I'm working on a script and I need to pass some arguments, the way I'm passing the arguments is like this:
xvfb-run casperjs --ignore-ssl-errors=true --ssl-protocol=any casper/server.js --checks='["215","216"]'
Inside the server.js I assign it to a variable:
var checks = casper.cli.get('checks');
Then below in the code I use the code in a loop:
casper.each(checks, function(check) {
$('*[data-queue="'+ check+'"] input:checkbox').prop('checked', false);
});
My issue is that although I do console.log(checks) to confirm that the arguments are being received the script keeps complaining with:
[error] [phantom] each() only works with arrays
Not only that but the loop doesn't work either if I hardcode the array manually.
casper.each callback takes TWO arguments, the first is the Casper module itself, the second is the iterated variable.
casper.each(checks, function(self, check) {
// ...
});
If you want to manipulate the HTML page then it has to be done in the page's context, inside of page.evaluate:
casper.each(checks, function(self, check) {
self.evaluate(function(check){
$('*[data-queue="'+ check+'"] input:checkbox').prop('checked', false);
}, check);
});
UPDATE
Sorry, didn't notice the CLI argument of --checks='["215","216"]'
Of course it's not an array:
console.log( checks );
console.log( typeof(checks) );
(notice the quotation marks ' ')
'[215,216]'
string
But if you use it like this: --checks=["215","216"]
and in the script:
console.log( typeof(eval(checks)) );
console.log( eval(checks)[1] );
object
216
(of cource eval is evil and all, so it's better to change format of incoming IDs)
casperjs casper/server.js --checks=215,216,15942,5435
console.log( typeof(checks.split(",")) );
console.log( JSON.stringify(checks.split(",")) );
object
["215","216","15942","5435"]
Related
I've recently taken upon myself to add setter and getter methods to my class.
Since doing this, many parts of my code got broken and I'm unable to access getter methods.
Take the example below:
private loadInputs() : Input[] {
var inputs = <Input[]>this.get('inputs');
inputs.sort((a,b) => a.QuoteRef().localeCompare(b.QuoteRef()))
return( inputs || [] );
}
My input class has 2 variables,
_Project: string
_line: string
Which I access using a method QuoteRef()
public QuoteRef(): string {
return this._Project.concat('-' + this._Line.toString().padStart(3,'0'));
}
Whenever I try to access a method or a getter from my class on an item that is casted as an Input, I can see the variables (though not access them as they are private), but the prototype section doesn't contain any of the methods.
This triggers the following error in the website console:
TypeError: a.QuoteRef is not a function
What am I doing wrong?
Update
I got it to work by updating the code as follows:
inputs.sort((a,b) => {
let first = new Input(a);
let second = new Input(b);
return first.QuoteRef().localeCompare(second.QuoteRef());
});
Without seeing your complete class I can only guess, but I think that a and b in your sort are not of the type you expect. I can't see what this.get('inputs') does, but I suspect it is not returning an array with Input class objects. Hence the function cannot be found (is not a function). You could try:
inputs.sort((a,b) => {
console.log(typeof a);
console.log(typeof b);
a.QuoteRef().localeCompare(b.QuoteRef());
})
and check what the type is. Then check what your this.get actually returns.
Edit: forgot to mention that your IDE probably does not warn you because you cast the output of this.get to <Input[]>.
I’ll start with the code:
var s = ["hi"];
console.log(s);
s[0] = "bye";
console.log(s);
Simple, right? In response to this, the Firefox console says:
[ "hi" ]
[ "bye" ]
Wonderful, but Chrome’s JavaScript console (7.0.517.41 beta) says:
[ "bye" ]
[ "bye" ]
Have I done something wrong, or is Chrome’s JavaScript console being exceptionally lazy about evaluating my array?
Thanks for the comment, tec. I was able to find an existing unconfirmed Webkit bug that explains this issue: https://bugs.webkit.org/show_bug.cgi?id=35801 (EDIT: now fixed!)
There appears to be some debate regarding just how much of a bug it is and whether it's fixable. It does seem like bad behavior to me. It was especially troubling to me because, in Chrome at least, it occurs when the code resides in scripts that are executed immediately (before the page is loaded), even when the console is open, whenever the page is refreshed. Calling console.log when the console is not yet active only results in a reference to the object being queued, not the output the console will contain. Therefore, the array (or any object), will not be evaluated until the console is ready. It really is a case of lazy evaluation.
However, there is a simple way to avoid this in your code:
var s = ["hi"];
console.log(s.toString());
s[0] = "bye";
console.log(s.toString());
By calling toString, you create a representation in memory that will not be altered by following statements, which the console will read when it is ready. The console output is slightly different from passing the object directly, but it seems acceptable:
hi
bye
From Eric's explanation, it is due to console.log() being queued up, and it prints a later value of the array (or object).
There can be 5 solutions:
1. arr.toString() // not well for [1,[2,3]] as it shows 1,2,3
2. arr.join() // same as above
3. arr.slice(0) // a new array is created, but if arr is [1, 2, arr2, 3]
// and arr2 changes, then later value might be shown
4. arr.concat() // a new array is created, but same issue as slice(0)
5. JSON.stringify(arr) // works well as it takes a snapshot of the whole array
// or object, and the format shows the exact structure
You can clone an array with Array#slice:
console.log(s); // ["bye"], i.e. incorrect
console.log(s.slice()); // ["hi"], i.e. correct
A function that you can use instead of console.log that doesn't have this problem is as follows:
console.logShallowCopy = function () {
function slicedIfArray(arg) {
return Array.isArray(arg) ? arg.slice() : arg;
}
var argsSnapshot = Array.prototype.map.call(arguments, slicedIfArray);
return console.log.apply(console, argsSnapshot);
};
For the case of objects, unfortunately, the best method appears to be to debug first with a non-WebKit browser, or to write a complicated function to clone. If you are only working with simple objects, where order of keys doesn't matter and there are no functions, you could always do:
console.logSanitizedCopy = function () {
var args = Array.prototype.slice.call(arguments);
var sanitizedArgs = JSON.parse(JSON.stringify(args));
return console.log.apply(console, sanitizedArgs);
};
All of these methods are obviously very slow, so even more so than with normal console.logs, you have to strip them off after you're done debugging.
This has been patched in Webkit, however when using the React framework this happens for me in some circumstances, if you have such problems just use as others suggest:
console.log(JSON.stringify(the_array));
Looks like Chrome is replacing in its "pre compile" phase any instance of "s" with pointer to the actual array.
One way around is by cloning the array, logging fresh copy instead:
var s = ["hi"];
console.log(CloneArray(s));
s[0] = "bye";
console.log(CloneArray(s));
function CloneArray(array)
{
var clone = new Array();
for (var i = 0; i < array.length; i++)
clone[clone.length] = array[i];
return clone;
}
the shortest solution so far is to use array or object spread syntax to get a clone of values to be preserved as in time of logging, ie:
console.log({...myObject});
console.log([...myArray]);
however be warned as it does a shallow copy, so any deep nested non-primitive values will not be cloned and thus shown in their modified state in the console
This is already answered, but I'll drop my answer anyway. I implemented a simple console wrapper which doesn't suffer from this issue. Requires jQuery.
It implements only log, warn and error methods, you will have to add some more in order for it to be interchangeable with a regular console.
var fixedConsole;
(function($) {
var _freezeOne = function(arg) {
if (typeof arg === 'object') {
return $.extend(true, {}, arg);
} else {
return arg;
}
};
var _freezeAll = function(args) {
var frozen = [];
for (var i=0; i<args.length; i++) {
frozen.push(_freezeOne(args[i]));
}
return frozen;
};
fixedConsole = {
log: function() { console.log.apply(console, _freezeAll(arguments)); },
warn: function() { console.warn.apply(console, _freezeAll(arguments)); },
error: function() { console.error.apply(console, _freezeAll(arguments)); }
};
})(jQuery);
I am new to protractor and currently experimenting with an internal Angular JS application. In the below snippet of code, I am unable to understand on how Protractor is executing the statements and function calls. The behaviour seems weird OR I am doing something completely wrong. Appreciate all the help.
describe('Describe Function', function()
{
it('Should return a value',function(){
var IndexValue = ' ';
var col_model = element.all(by.repeater('col in renderedColumns'));
var row_model = element.all(by.repeater('row in renderedRows'));
browser.get('URL');
ptor = protractor.getInstance();
var user = element(by.model('login.user_id_1'));
user.sendKeys('demo');
element(by.id('txtusername')).getAttribute('value').then(function(text) {
console.log(text); // This line prints 'demo'
});
var pwd = element(by.model('login.password_1'));
pwd.sendKeys('demo');
var submit = element(by.className('login-submit'));
submit.click();
browser.driver.sleep(1000);
var colCount = element.all(by.repeater('col in renderedColumns')).count();
colCount.then(console.log) // This prints '80'
var items = [];
getArrayList();
function getArrayList() // Function to capture all the content of the table in an array
{
console.log('Array List function started');
colCount.then(function(Col_count){
for(var i=0; i < Col_count; ++i){
var grid = browser.findElement(by.repeater('col in renderedColumns').row(i));
grid.getText().then(function(gridValue){
items.push(gridValue.trim());
console.log('For loop')
});
}
});
console.log('Array List function completed');
}
console.log(items); // This prints []
getGridValue('Prospect');
function getGridValue(name)
{
console.log('Inside grid value');
}
});
});
When I execute the above code, even before the browser is invoked and the application is launched, first 4 lines (specified below) are printed on the console. What surprises me is the fact that point 1 and 3 are part of the function call which indeed is printing point 7. Looks like all the independent "console.log" are executed first and then the "console.log" associated with "element." statements are executed. Its really confusing. Plz let me know where am I going wrong. Thanks.
OUTPUT ON THE CONSOLE
1. Array List function started
2. []
3. Array List function completed
4. Inside grid value
5. demo
6. 80
7. For loop
For loop
For loop
For loop
.... so on until the loop ends
At first: well, protractor does, what you told it to do... You may read a bit about promises and async JavaScript.
I try to explain a bit, please understand, that explaining everything would take a lot of time.
Starting protractor calls your script (spec). The most lines you wrote before calling getArrayList() are function calls which return promises. This means, the function is called, and when ready, it's callback function gets called (e.g.: then(...))
An example:
var colCount = element.all(by.repeater('col in renderedColumns')).count();
colCount.then(console.log) // This prints '80'
In this two lines you are searching the DOM for every element which can be located by col in renderedColumns, then, if ready, count them and return the value to its callback then, afterwards print it via console.log.
But all of this takes time, and this is the reason why console.log('Array List function started'); gets printed before colCount.then(console.log).
Hope I could help a bit, as mentioned before, you may read a bit about promises.
I have an Autocomplete working with geonames.org cities in the source: option and want to manipulate some of the arrays in the results. I have a "hard coded" test version running, but am having trouble manipulating the array variables to turn it into something useful.
A short statement of the problem is I can't get the alert statements to output readable strings. I get [object, Object] types of output. I need readable strings in the arrays for other code (not shown) to work. But other problems are: the Firebug Console output does not occur, and the Console gives me the following error statement - Error: Permission denied to access property 'item.autocomplete' - from line 2 of jQuery. This does not happen with the hard coded test. This is my first time using .grep and I'm not comfortable with .map, so I'm pretty sure the problems are array manipulations in those 3 sections.
Here's some relevant code. All the variables are declared, but I don't show all the declarations below.
citiesWithBoroughs = //a global array variable, populated in a prior select: option
source: function (request, response){
$.ajax({
success: function ( data ){
var geonamesResponse=$.map(data.geonames, function (item){
return {
label: item.name //and others
}
}
alert(citiesWithBoroughs + "," + citiesWithBoroughs.length + "|cWB2" ); //displays correct info
var noBoroughs=$.grep( geonamesResponse, function ( item, i ) {
for (var i=0; i < citiesWithBoroughs.length; i++ )
if( item.label === citiesWithBoroughs[i] ){ //compare geonamesResponse to each citiesWithBoroughs
console.log(citiesWithBoroughs[i]); //nothing in Console
return false; //drop any matches out of the geonamesResponse array
}
noBoroughs = $.map( data.geonames, function (item){ return item.label; });
console.log(noBoroughs); //nothing appears in Console
return true;
});
alert(noBoroughs.length + "," + citiesWithBoroughs.length + "," + geonamesResponse.length + "|3lengths" ); //correct info
alert(noBoroughs + "|nB" ); //in test, shows correct number of [object,Object] but no data
if( noBoroughs.length != geonamesResponse.length ){
var dropdownsCityWithBoroughs = $.grep( geonamesResponse, function ( item, i ) {
for (var i=0; i<citiesWithBoroughs.length; i++ )
if(item.label === citiesWithBoroughs[i]){return false;}
return true;
}, true )//true inverts grep to return the has-Boroughs city in the dropdown
alert(dropdownsCityWithBoroughs + "|dCWB"); //contains object, Object, but shows no data
}
}
}
}
I'm a novice so please give specific comments and code. I don't follow general instructions well.
The short statement of my problem was I could not read the output of my alerts, which contain arrays. Turns out JSON.stringify() fixes that. Other problems, like the Console.log statements not showing continue, but I can deal with that mysterious problem when the alerts work.
PROBLEM
So, I have this function to retrieve and proceed data from $_REQUEST, $_POST, $_GET or $_COOKIE arrays. I know which array to use only from function call. Simplified ex:
function gg( $name, $type="_REQUEST" ) {
return isset( $GLOBALS[$type][$name] ) ? $GLOBALS[$type][$name] : false;
}
And it works perfectly for calls like:
gg('var', '_GET');
gg('var2', '_POST');
But fails dramatically for:
gg('var');
// or
gg('var', '_REQUEST');
I managed to simplify this problem thou to 2 lines:
print_r( $GLOBALS['_REQUEST'] ); // this line returns nothing...
print_r( $_REQUEST ); // ...UNLESS this line is present anywhere in the code
Now, my obvious question is: Is there any necessity to initialize this $_REQUEST array to be present in $GLOBALS?
additional info:
php: 5.3.3-7
apache: 2.2.16
also I'm running on CGI/FastCGI
EDIT & SOLUTION
1
As found here the easiest solution would be to edit php.ini and change there value of auto_globals_jit from On to Off.
auto_globals_jit Off
2
Instead of this you can use ini_set() inside of your source file, however it didn't work for me...
ini_set("auto_globals_jit", "Off");
3
Yet another solution is to use $GLOBALS array to everything except $_REQUEST and for $_REQUEST requests call directly to $_REQUEST array :D
if($type == "REQUEST") return $_REQUEST[$name];
else return ${"_".$type}[$name]; // or $GLOBALS["_".$type][$name] if previous won't work
Couldn't replicate this on my setup so it could possibly be CGI issue? As a workaround you could do something like this...
function gg( $name, $type="_REQUEST" ) {
return isset( ${$type}[$name] ) ? ${$type}[$name] : false;
}
Might be of interest:
As of PHP 5.4 $GLOBALS is now initialized just-in-time. This means
there now is an advantage to not use the $GLOBALS variable as you can
avoid the overhead of initializing it. http://www.php.net/manual/en/reserved.variables.globals.php
Update. See Post:
$_REQUEST not created when using variable variables?
Just a tip:
function gg( $name, $type="_REQUEST" ) {
if($type=="_REQUEST")return $GLOBALS[$name];
return isset( $GLOBALS[$type][$name] ) ? $GLOBALS[$type][$name] : false;
}
Once I have made a function like yours:
function get_data($name)
{
if(isset($_GET[$name]))return $_GET[$name];
if(isset($_POST[$name]))return $_POST[$name];
}
$_REQUEST is already a superglobal "which means they are available in all scopes throughout a script. There is no need to do global $variable; to access them within functions or methods."
function gg( $name, $type="_REQUEST" ) {
switch ($type) {
case '_REQUEST':
return $_REQUEST[$name];
break;
case 'GLOBALS':
return $_GLOBALS[$name];
break;
// etc...
default
return false;
}