Higher order functions not working in casperjs - coffeescript

I have function defined like this
casper.executeRemote = (fn,params)->
->
try
fn.apply(this,params)
catch e
console.log(e)
getLinks = ->
elems = document.querySelectorAll('#pagination-flickr a')
and i am calling the function like below
casper.then ->
all_links = #evaluate #executeRemote getLinks
casper.log all_links,'debug'
However I get the below error when i try to run this in casperjs
ReferenceError: Can't find variable: fn
The same code works fine, if i try in a browser console(compiled js). What is that I am doing wrong?

Of course they work, but not at the border to the page context:
Note: The arguments and the return value to the evaluate function must be a simple primitive object. The rule of thumb: if it can be serialized via JSON, then it is fine.
Closures, functions, DOM nodes, etc. will not work!
Thankfully there is the toString function on Functions. So, you can change your code a little to account for passing the function as a string and evaling inside of the page context.
(Untested) example:
casper.executeRemote = (fn,params)->
fn = eval('('+fn+')')
try
fn.apply(this,params)
catch e
console.log(e)
getLinks = ->
elems = document.querySelectorAll('#pagination-flickr a')
casper.then ->
all_links = #evaluate(#executeRemote, getLinks.toString())
casper.log all_links,'debug'

Based on #Artjom's answer. I modified the code as shown below, then it works
executeRemote = (fn,params)->
->
try
fn.apply(this,params)
catch e
console.log(e)
getLinks = ->
elems = document.querySelectorAll('#pagination-flickr a')
links = (link.href for link in elems)
remoteWrapper = (excRm,gL)->
excRm=eval('('+excRm+')')
gL=eval('('+gL+')')
fn=excRm(gL)
fn()
casper.on 'remote.message',(message)->
#echo message
casper.then ->
all_links = #evaluate remoteWrapper, executeRemote.toString(), getLinks.toString()
casper.log all_links,'debug'

Related

Dealing with recursion/sync-loops in coffeescript

Right now I'm trying to build some code to handle lists over 100 items, as returned by the Amazon API endpoints. This requires building page-support into our data-gathering routines. This is my first time doing much with coffeescript, so I'm running into some conceptual walls here.
In a less async language, what I'm trying to do would be handleable using an until loop:
puts "Fetching launch configs"
next_token = ''
do
if next_token.length > 0
page_list = get_autoscale_configs(next_token)
else
page_list = get_autoscale_configs
if page_list.NextToken is undefined
next_token = ''
else
next_token = page_list.NextToken
until(next_token.length == 0)
The method of doing this in coffeescript is eluding me. What I have now...
populate_configs = ( ) ->
process_results = ( err data ) ->
if err
return err
# do some logic
if data.NextToken
saved.next_token = data.NextToken
else
saved.next_token = ''
return console.log "Finished parsing #{data.LaunchConfigurations.length} items."
if saved.next_token = ''
autoscaling.describeLaunchConfigurations {
MaxRecords: 100, StartToken: next_token
}, ( err, data ) -> process_results( err, data )
else
autoscaling.describeLaunchConfigurations {
MaxRecords: 100
}, ( err, data ) -> process_results( err, data )
And then in the body of the code, this function is invoked:
saved = {}
async.series [
( series_cb ) ->
saved.next_token = ''
async.doWhilst populate_configs,
saved.next_token.length > 4,
( err ) ->
if err
# complain about it.
# else, log success
return series_cb()
# more callbacks
]
The idea here being that populate_configs is called by doWhilst, which then fetches a list of launch_configs out of amazon. The data is then passed into another function called process_results, which persists things that should be persisted and sets variables for next_token. It returns, and doWhilst tests to see if the test is passing (the string-length of saved.next_token is long enough to be data); if it passes, it runs through populate_configs again, if it fails, it runs the third callback.
What I'm getting right now is that the first iteration of that populate_configs block is executed, but then the entire execution stops dead at that point. None of the calls in the error-handler of doWhilst are being executed.
Clearly, I'm misunderstanding how callbacks work and how to get myself out of this hole. This part needs to be synchronous. Once I have this list built, I can do all sorts of async fun with the list I'm building. But I need the list first.
I think the issue is here: if saved.next_token = ''. You set next_token to '' so populate_configs runs only once. The comparaison is done with == or is in CoffeeScript.
Also, ( err, data ) -> process_results( err, data ) can be replaced by process_results.

Casper require() with CoffeeScript not importing external files

I followed CasperJS's documentation about including .coffee files from the main Casper test file. My code looks like this:
home/tests/my_test_file.coffee:
parameters = require('../parameters')
casper.test.begin "Test ", (test) ->
home_page = parameters.root_path
page_to_test = home_page + "my_page_to_test"
casper.start page_to_test, ->
test.assertEquals #getCurrentUrl(), page_to_test
casper.run ->
test.done()
home/parameters.coffee:
require = patchRequire global.require
root_path = "http://localhost:1080/"
my_page = "foo"
other_param = "bar"
exports = ->
{
'root_path': root_path,
'my_page': my_page,
'other_param': other_param
}
However, Casper keeps telling me that page_to_test is undefined in my_test_file.coffee.
That's not a proper use of exports. First you don't need a function here, because you directly access the properties of the returned object. And second you cannot assign something directly to exports. That's what module.exports is for.
module.exports = {
'root_path': root_path,
'my_page': my_page,
'other_param': other_param
}
or
exports.root_path = root_path
exports.my_page = my_page
exports.other_param = other_param
By assigning an object to exports (exports = obj), you overwrite the object that does the actual exporting and nothing is exported.

How to stop Coffeescript from escaping keywords?

I am trying to write a indexeddb function "delete". It should read like this in JS:
var transaction = db.transaction('objectStore','readwrite');
var objectStore = transaction.objectStore('objectStore');
objectStore.delete(id);
However, when I write it in CS:
transaction = db.transaction 'objectStore','readWrite'
objectStore = transaction.objectStore 'objectStore'
objectStore.delete(id)
Of course it outputs:
...
objectStore["delete"](id);
I didn't write a method for IDBTransaction called "delete", but I have to use it. How can I keep CS from escaping the "delete" method and turning it into a "delete" key in an object?
Use backticks to pass through bare Javascript:
`objectStore.delete(id)`
will be compiled through verbatim. Try it here at my favorite site for interpreting between CS and JS: http://js2coffee.org/#coffee2js
transaction = db.transaction 'objectStore','readWrite'
objectStore = transaction.objectStore 'objectStore'
`objectStore.delete(id)`
becomes
var objectStore, transaction;
transaction = db.transaction('objectStore', 'readWrite');
objectStore = transaction.objectStore('objectStore');
objectStore.delete(id);
Why do you care that the JavaScript version is objectStore["delete"](id)? That's the same as objectStore.delete(id).
For example, if you say this in CoffeeScript:
class B
m: (x) -> console.log("B.m(#{x})")
class C extends B
c = new C
c.m('a')
c['m']('b')
The last two lines come out as this JavaScript:
c.m('a');
c['m']('b');
but they both call the same method.
Demo: http://jsfiddle.net/ambiguous/XvNzB/
Similarly, if you say this in JavaScript:
var o = {
m: function(x) { console.log('m', x) }
};
o.m('a');
o['m']('b');
The last two lines call the same method.
Demo: http://jsfiddle.net/ambiguous/Y3eUW/

using object as parameter for find in meteor bears no result

I got the following code:
Clientes = new Meteor.Collection 'clientes'
Template.listaClientes.clientes = ->
query = {}
query = Session.get "searchData"
console.log "get: " + query.nombre
clientes = Clientes.find(query)
Template.listaClientes.events
'click .listItem' : ->
console.log "item clicked"
Template.searchPane.events
'click #btnToList' : ->
query = {}
#asigna los campos a query si no son blancos
if (x = $("#qNombre").val())? then query.nombre = x
if (x = $("#qCiudad").val())? then query.ciudad = x
console.log "q: " + query.nombre + query.ciudad
Session.set "searchData", query
it runs perfect on startup, query is {} so template shows every document in the collection. When I click on #btnToList I get no results back from the Find clausule. If I set "searchData" to {} manually from browser console it fires the trigger and all documents are drawn again.
any ideas about what I'm missing?
edit:
Doing the following works fine which makes me wonder whats going on even more :/
q={}
q.nombre = query.nombre if query.nombre?
Clientes.find q
well, looks like the second IF (#qCiudad) was always adding a value even when empty.
Changing the checks to if (x = $("#qCiudad).val()) != "" then ... made it and the query objects is passed correctly.

Sinon.JS stub for window.location.search

I am trying to test a simple function that makes a call to window.location.search. I'm trying to understand how to stub this call so that I can return a url of my choosing.
function:
getParameterByName: (name) =>
name = name.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]")
regexS = "[\\?&]" + name + "=([^&#]*)"
regex = new RegExp(regexS)
results = regex.exec(window.location.search) //Stub call to window.location.search
if(results == null)
return ""
else
return decodeURIComponent(results[1].replace(/\+/g, " "))
Test case:
describe "Data tests", () ->
it "Should parse parameter from url", () ->
data = new Data()
console.log("search string: " + window.location.search) //prints "search string:"
window.location.search = "myUrl"
console.log("search string: " + window.location.search) //prints "search string:"
console.log(data.getParameterByName('varName'))
expect(true).toBe(true)
My original attempt was to return a value directly like so:
sinon.stub(window.location.search).returns("myUrl")
This, of course, doesn't work. I don't think I'm specifying the stub correctly, but it shows my intent.
Any ideas on how to solve this would be greatly appreciated.
So, as mentioned before, you can't mock window.location directly. Nor did the mylib.search wrapper idea work with my situation. So, what I did was break out my call to window.location.search into its own function. My new class looks like so:
getParameterByName: (name) =>
console.log("name: #{name}")
name = name.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]")
regexS = "[\\?&]" + name + "=([^&#]*)"
regex = new RegExp(regexS)
results = regex.exec(#getWindowLocationSearch())
if(results == null)
return ""
else
return decodeURIComponent(results[1].replace(/\+/g, " "))
getWindowLocationSearch:() =>
window.location.search
Then in my test case, I replace the function with my test code like so:
describe "Data tests", () ->
it "Should parse parameter from localhost url", () ->
goodUrl = "http://localhost:3333/?token=val1"
Data::getWindowLocationSearch = () -> return goodUrl
unit = new Data()
result = unit.getParameterByName("token")
expect(result).toBe("val1")
For those who don't read Coffeescript, the equivalent javascript code is listed below:
it("Should parse parameter from localhost url", function() {
var goodUrl, result, unit;
goodUrl = "http://localhost:3333/?token=val1";
Data.prototype.getWindowLocationSearch = function() {
return goodUrl;
};
unit = new Data();
result = unit.getParameterByName("token");
expect(result).toBe("val1");
return expect(true).toBe(true);
});
As is my usual experience with Javascript. The working solution was not nearly as painful as the journey to get there. Thank you very much for your comments and contributions.
UPDATE: window.location, it seems, is a bit of a special case, see this discussion: https://groups.google.com/forum/?fromgroups#!topic/sinonjs/MMYrwKIZNUU%5B1-25%5D
The easiest way to solve this problem is to write a wrapper function around window.location, and stub that:
mylib.search = function (url) {
window.location.search = url;
};
And in your test:
sinon.stub(mylib, 'search').returns("myUrl")
ORIGINAL ANSWER:
Try this:
sinon.stub(window.location, 'search').returns("myUrl")