How to pass an object to a CasperJS function - javascript

I'm writing some test code for work and am trying to figure out why it throws an error. I'm using an object oriented approach to this so to keep my main script as clean as possible. I have one script that contains all my element paths called elements.js. I'm using phantom to inject that script into another file that contains click methods called click.js. Example:
function Click() {
phantom.page.includeJs('/my/path/to/elements.js');
var element = new Elements();
this.clickElement = function() {
casper.then( function() {
casper.click(element.nameOfElement);
});
};
}
Running my script this way throws undefined errors, however if I directly declare the element's path in my click.js script, my test runs fine:
function Click() {
var nameOfElement = ('css > path > to > element');
this.clickElement = function() {
casper.then( function() {
casper.click(nameOfElement);
});
};
}
I'm wanting to call all element paths from one source just to keep my scripts clean, as you can imagine they can get long depending on how many elements we're testing. I also have other files that require the use of an element's path for this test, but they follow the same principle as the piece of my click.js script above.
UPDATE
Here is what my element.js script looks like:
function Elements() {
var nameOfElement = ("css path");
var anotherElement = ("css path");
}

PhantomJS (and CasperJS) has two contexts. page.includeJs() and page.injectJs() are two PhantomJS functions that include a JavaScript file into the DOM which is then evaluated in the page context, but you want to access your elements paths outside of the page context.
I assume your elements.js looks like this:
function Elements(){
this.someElement = "css path";
...
}
then you need to read the file and evaluate it:
var fs = require("fs");
var el = fs.read("/path/tp/elements.js");
eval(el);
var elements = new Elements();
console.log(elements.someElement);
You probably should define your elements.js differently to leverage PhantomJS's require capability:
As module
elements.js
module.exports = function(){
this.someElement = "css path";
...
}
script.js
var elements = new require("/path/to/elements.js");
As object
elements.js
exports.someElement = "css path";
exports.someOtherElement = "css path";
script.js
var elements = require("/path/to/elements.js");
You probably don't want to read the file on every click, so you can
var elements = require("/path/to/elements.js");
casper.clickElement = function(nameOfElement) {
this.thenClick(elements[nameOfElement]);
};
and use it later like this
casper.clickElement("someOtherElement");

I solved this by getting through a rookie brain fart I was in. In my element.js script, I added a simple get method and it works now.
function Elements() {
var someElement = "css path";
this.getSomeElementPath = function() {
return someElement;
};
}
Called it in my click.js script as:
function Click() {
phantom.page.injectJs('path/to/element.js');
var element = new Elements();
this.clickSomeElement = function() {
casper.then( function() {
casper.click(element.getSomeElementPath());
});
};
}
And, in my main script:
'use strict':
phantom.page.injectJs('/path/to/element.js');
phantom.page.injectJs('/path/to/click.js');
var element = new Element();
var click = new Click();
casper.test.begin("Test", function (test) {
var url = www.url.com;
console.log(url);
casper.start(url);
casper.then(function() {
click.clickSomeElement();
});
....//rest of code
casper.run(function() {
casper.test.done();
});
});
Now, the test runs as it should.

Related

Accessing an Immediately Invoked Function Expression variable in Node.js in another file using require

File 1 - Monitor.js
var MONITOR = (function () {
// File Content
return {
doThing: function() {
doThing();
}
};
})();
File 2 - Test.js
var monitor = require('../public/js/monitor.js');
I want to access doThing() in File 2. I have tried various syntax and no luck so far.
From the frontend HTML I can simply include Monitor.js in a script tag, and call MONITOR.doThing(); without trouble but in Test.js this is proving difficult.
Any advice on how?
You have to export MONITOR so that someone else can access it with require().
Add this:
module.exports = MONITOR;
at the bottom of Monitor.js.
And, if you want the monitor.doThing() method to return some value, then you have to add a return statement to the function as in:
var MONITOR = (function () {
// File Content
return {
doThing: function() {
return "hello";
}
};
})();

Swapping hardcoded arguments for variables

I've got a functional script on my site that allows me to open a link in a new window when a specific class is added to the link. I need a lot of those on my site though so I figured I'd make the script a bit easier to edit by working with variables.
In the process of changing out hardcoded strings for variables my script stopped working though. The only one that works is the var where I set the url.
I'm learning that ${} doesn't work everywhere. Hope that someone can point out where my thinking is wrong. Also hope that I got the terminology right, trying to learn though! :-)
var function1Name = "test_function";
var function1Url = "https://www.google.com";
var function1Class = ".test_function_class";
function ${function1Name}() {
window.open(function1Url, "_blank", "height=200");
}
jQuery("${function1Class}").click(function(){
${function1Name}()
});
None of your uses of ${} are valid JavaScript syntax.
Your function declaration van be replaced with:
window[function1Name] = function () {
window.open(function1Url, "_blank", "height=200");
}
Please note that the function will no longer be hoisted when declared this way, so order of operation matters.
The click handler can be written as follows:
jQuery(function1Class).click(function() { // Note that I just used the variable there.
window[function1Name]();
});
There is a ${} concept in JavaScript, but that is only in template literals:
const myVariable = "Foo";
const message = `myVariable contains: "${myVariable}"!`;
console.log(message);
There's several syntax issues here.
Firstly, function ${function1Name}() is not valid syntax. Function names must be defined before runtime. If you want to dynamically access a function, place it in an object and set the key with the variable reference.
Secondly, ${function1Name}() is again not valid syntax. You cannot invoke a function like that dynamically. Referring to the suggestion above, you can access an object dynamically so the first point fixes this problem.
Thirdly, string interpolation only works within template literals, so you need to delimit the string with backticks: ``. However it's completely redundant here as you can just use $(function1Class)
With those issues in mind, here's an updated example:
var function1Name = "test_function";
var function1Url = "https://www.google.com";
var function1Class = ".test_function_class";
var funcObj = {
[function1Name]: function() {
console.log(`function called, window would open here containing ${function1Url}...`);
// window.open(function1Url, "_blank", "height=200");
}
}
$(function1Class).click(function() {
funcObj[function1Name]()
});
/*
alternative using a template literal, although note that it's redundant here
$(`${function1Class}`).click(function() {
funcObj[function1Name]()
});
*/
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
Click me
One last thing to note is that no version of IE supports template literals, so be sure of your browser support requirements before using them.
So cool, I got it to work!
var function1Name = "test_function_1";
var function1Url = "https://www.google.com";
var function1Class = ".test_function_class1";
var function2Name = "test_function_2";
var function2Url = "https://www.cnn.com";
var function2Class = ".test_function_class2";
// Function 1
window[function1Name] = function () {
window.open(function1Url, "_blank", "toolbar=no,status=no,scrollbars=yes,resizable=yes,top=500,left=500,width=600,height=745");
}
jQuery(function1Class).click(function() {
window[function1Name]();
});
// Function 2
window[function2Name] = function () {
window.open(function2Url, "_blank", "toolbar=no,status=no,scrollbars=yes,resizable=yes,top=500,left=500,width=600,height=745");
}
jQuery(function2Class).click(function() {
window[function2Name]();
});
I can now add a bunch of url's and corresponding classes as was my intention. Super happy about that.
A follow up question though, as I'll have to experiment with what the ideal window parameters will be I'm trying to make those arguments variables as well. I've tried the examples of how to insert a variables output from the functional code but those methods don't work there. Here's that code:
var windowWidth = 250
var function1Name = "test_function_1";
var function1Url = "https://www.google.com";
var function1Class = ".test_function_class1";
var function2Name = "test_function_2";
var function2Url = "https://www.cnn.com";
var function2Class = ".test_function_class2";
// Function 1
window[function1Name] = function () {
window.open(function1Url, "_blank", "toolbar=no,status=no,scrollbars=yes,resizable=yes,top=500,left=500,width=[windowWidth],height=745");
}
jQuery(function1Class).click(function() {
window[function1Name]();
});
// Function 2
window[function2Name] = function () {
window.open(function2Url, "_blank", "toolbar=no,status=no,scrollbars=yes,resizable=yes,top=500,left=500,width=600,height=745");
}
jQuery(function2Class).click(function() {
window[function2Name]();
});
How would I insert the variables value (2nd line of Function1) there ?

How can I keep same function names meant for different things in their own scope (It may not be the right word)?

I have created two different independent scripts to update two different canvas. The problem is that some of the function names in both the scripts are same. This is going to result in conflicts.
I could combine them together to form a big script but that will soon become messy if I try to add more functionality. Similarly, naming every function differently will be tedious and error prone.
Is there some way, I can keep the scripts with same function names on a single page without any conflict?
Here is my code:
var DropEffect = {
var dropSize, speed;
var createDrops = function () {
var canvas = document.getElementById("drop");
....
};
}
This is the error I get:
Unexpected token name «dropSize», expected punc «:»
Try to do it like this:
File 1:
var nm1 = {
myFunction: function(){
}
}
File 2:
var nm2 = {
myFunction: function(){
}
}
Now, whenever you want to access, use nm1.myFunction() or nm2.myFunction();
In general such kind of objects are called namespaces.
After you modified question,
var DropEffect = {
var dropSize, speed;
var createDrops = function () {
var canvas = document.getElementById("drop");
....
};
}
This is not correct. You are creating variables inside an object (namespace).Objects just understand key-value pairs. Better do it like this,
var DropEffect = {
dropSize : "",
speed : "",
createDrops : function () {
var canvas = document.getElementById("drop");
....
};
}
and now use it as: DropEffect.dropSize etc.
You can wrap code in IIFE's like this:
(function(){
var theFunction = function(){
console.log('foo');
};
theFunction();
}());
(function(){
var theFunction = function(){
console.log('bar');
};
theFunction();
}());

Testing multiple browsers with protractor backed by page objects

I'm writing a test where two browsers need to interact. The problem with simply forking the browser is that my page objects still reference the old browser. I didn't want to rewrite all of my PO's to take the browser as a parameter so I tried the first solution found in the link below where they overwrite the global variables with the new browser's version :
Multiple browsers and the Page Object pattern
However, changing the global variables doesn't seem to work as all the subsequent page object functions that I call are performed against the original browser instance. I have tried logging the window handler before and after the switch and they are indeed different which only baffles me further. Here's some of the code.
spec:
var MultiBrowserFunctions = require('../common/multiBrowserFunctions.js');
var HomePage = require('../home/home.po.js');
describe('blah', function(){
it('blah', function(){
MultiBrowserFunctions.openNewBrowser(true);
HomePage.initializePage();
});
});
MultiBrowserFunctions:
(function() {
var browserRegistry = [];
module.exports = {
openNewBrowser: function(isSameUrl){
if(typeof browserRegistry[0] == 'undefined'){
browserRegistry[0] = {
browser: browser,
element: element,
$: $,
$$: $$,
}
}
var tmp = browser.forkNewDriverInstance(isSameUrl);
var id = browserRegistry.length;
browserRegistry[id] = {
browser: tmp,
element: tmp.element,
$: tmp.$,
$$: tmp.$$,
}
switchToBrowserContext(id);
return id;
},
resetBrowserInstance : function(){
browserRegistry.splice(1,browserRegistry.length);
switchToBrowserContext(0);
}
}
function switchToBrowserContext(id){
console.log('---------------------------switching to browser: ' + id);
browser=browserRegistry[id].browser;
element=browserRegistry[id].element;
$=browserRegistry[id].$;
$$=browserRegistry[id].$$;
}
}());
My questions are:
(1) why doesn't this work?
(2) Is there some other solution that doesn't involve rewriting all of my po's?
What you can do is, save the browsers in different variables and then switch between them by overriding the globals via a utility or something.
describe('Switching browsers back and forth', function () {
var browserA, browserB;
it('Browser Switch', function () {
var browsers = {
a : browser,
b : browser.forkNewDriverInstance(true)
};
browserA = browsers.a;
browserB = browsers.b;
var browserAndElement = switchBrowser(browserB);
browser = browserAndElement.browser;
element = browserAndElement.element;
//do your stuff
var browserAndElement = switchBrowser(browserA);
browser = browserAndElement.browser;
element = browserAndElement.element;
//do your stuff
});
});
The switchBrowser() can look like following:
this.switchBrowser = function (currentBrowser) {
browser = currentBrowser;
element = currentBrowser.element;
return {
browser : browser,
element : element
}
}
In this way you don't have to rewrite your POs to take in the new globals.
Hope it helps!
Cheers

How to access functions in injected .js files from evaluate()?

Is it possible to reference functions in an embedded JavaScript file, when calling page.evaluate() using PhantomJS?
e.g. I would like to call a function someFunctionInMyJs included in the file my.js:
var page = require('webpage').create();
page.injectJs('my.js')
page.open('http://...', function() {
var x = page.evaluate(function() {
var y = someFunctionInMyJs();
return y;
});
phantom.exit();
});
Is that possible/are there alternative approaches?
my.js looks like this:
function someFunctionInMyJs() {
return 'Hi there!';
}
and the error I'm getting is:
ReferenceError: Can't find variable: someFunctionInMyJs
my.js lies in the folder where I'm invoking PhantomJS.
The problem is that you inject a script into the page which is about:blank at this point and then open your intended page. You need to inject your script inside of the page.open callback. You will have to do this every time you navigate to another page.
page.open('http://...', function() {
page.injectJs('my.js')
var x = page.evaluate(function() {
var y = someFunctionInMyJs();
return y;
});
phantom.exit();
});
You can also try to inject (injectJs) your script from through the page.onInitialized handler, if your script for example exchanges the implementation of XMLHttpRequest.
If this still doesn't work, it is possible that your function isn't global in which case you have to make it so. Edit my.js to define your function as
window.someFunctionInMyJs = function() {
return 'Hi there!';
};

Categories

Resources