I am doing a POC on switching to Protractor and Jasmine to perform our automated scripting. I'm trying to build a preliminary framework, but I'm having issues trying to translate my concept to reality.
I've set up three files: conf.js, spec.js, and cf.js. Conf.js is testplan-specific configuration file, spec.js contains the actual tests, and cf.js contains the common functions that I will be using through all test plans. I am trying to include a variable in cf.js to contain the starting URL to be used in the browser.get call. So far, I have not been able to get that to work. I've tried declaring it in cf.js before the //commonfunctions// function declaration, as well as within the function itself. What is the proper way to do this?
cf.js
var commonfunctions = function () {
global.StartPage = 'http://google.com/';
this.ccClick = function (clickElement) {
browser.wait(protractor.ExpectedConditions.visibilityOf(clickElement),
this.defaultWait);
browser.wait(protractor.ExpectedConditions.elementToBeClickable(clickElement),
this.defaultWait);
clickElement.click();
};
// Common text search
this.ConfirmText = function(testElement, compareString) {
browser.wait(protractor.ExpectedConditions.visibilityOf(testElement),
10000);
expect(testElement.getText()).toEqual(compareString);
};
};
module.exports = new commonfunctions();
spec.js
beforeEach(function() {
browser.waitForAngularEnabled(false);
browser.get(commonfunctions.StartPage);
});
Right now, it does not navigate to the webpage.
This should be possible, I have posted a similar answer before but let me know if you have further concerns about the approach. The approach I took was to require the common function file in the onPrepare as a global variable. This way anything exported from the file is accessible throughout all the tests.
Storing global variable in a separate file for Protractor Tests
Your made a mistake at following code:
// cf.js
var commonfunctions = function () {
global.StartPage = 'http://google.com/';
// spec.js
beforeEach(function() {
browser.waitForAngularEnabled(false);
browser.get(commonfunctions.StartPage);
});
// you define `StartPage` to a global variable, not a property of `commonfunctions`,
thus you shouldn't refer it from `commonfunctions`, but from `global` as following:
browser.get(global.StartPage)
or you define StartPage to be a property of commonfunctions
// cf.js
var commonfunctions = function () {
this.StartPage = 'http://google.com/';
// use `this` at here, rather than `global`
// spec.js
beforeEach(function() {
browser.waitForAngularEnabled(false);
browser.get(commonfunctions.StartPage);
});
Add the below one to your config.js
baseUrl: 'http://google.com/',
To use it in your test as below
browser.get(browser.baseUrl);
Hope this helps you
Related
I created a function which return the new Browser object from the JS function browser.forkNewDriverInstance() and i created a global variable in my config file and i'm calling a function from that file by using this global variable. but here when i'm calling that function it is throwing error like utility.openNewBrowser is not a function error.
Config File:
onPrepare: function () {
global.utility=require("../src/test/resources/com.learnFramework.utility/timeOutConfig.js");
}
Cucumber Opts functions
cucumberOpts: {
//i'm using the same file for setting up the timeout.. is this creating the issue??
require:['../src/test/resources/com.learnFramework.utility/timeOutConfig.js'],
tags: false,
profile: false,
format:'json:../Reports/jsonResult/results.json',
'no-source': true
}
Function
var configure = function () {
this.setDefaultTimeout(100 * 1000);
this.openNewBrowser=function(){
return browser.forkNewDriverInstance(true);
}
};
module.exports = configure;
Error Log
TypeError: utility.openNewBrowser is not a function
When i called the forkNewBrowserInstance method directly i'm getting the below error.
both angularJS testability and angular testability are undefined. This could be either because this is a non-angular page or because your test involves client-side navigation, which can interfere with Protractor's bootstrapping. See http://git.io/v4gXM for details
can some help me to resolve this issue.. i got this error because the first browser ignoring synchronization but second browser how can i ignore the synchronization?
What you did above is the correct way of defining a global variable. But I think the global variable name should be configure instead of utility that is why it is throwing the TypeError.
And wherever you want to call it, use that variable name as is. This is actually how protractor, browser and other built-in global variables were made available globally. The following posting was helpful and also the protractor doc where it's explaining the property: params?: any;
Hope this helps, please let me know.
I faced the same issue but with jasmine.
so i had done the following workaround and issue was resolved.
class abc{
constructor() {
var configure = function () {
this.setDefaultTimeout(100 * 1000);
this.openNewBrowser=function(){
return browser.forkNewDriverInstance(true);
}
};
}
}
module.exports = new abc();
I want to achieve the following:
Create a dynamic id for a customer in my createPlan.js
Store the ID in a global variable
Use this globalVariable in a separate file, sendforreview.js
Currently I'm using jasmine data provider but dynamically I'm not able to
do it every time after i run the first js file then i update the jasmine data
provider and then I run the next js file so that the plan number is fed as
input in the search textbox .
This is a simple logic but it'll be applicable across any application I guess.
createplan.js file:
var plannumberis, using = require('jasmine-data-provider');
describe("Plan Creation", () => {
using(leadEnters.leadinputs, (data, description) => {
it("Plan is generated and stored globally", () => {
jasmine.DEFAULT_TIMEOUT_INTERVAL = 100000;
browser.wait(EC.urlContains('WorkFlow/#/app/impPlan'));
browser.wait(EC.presenceOf(element(by.xpath("//div[#class='sweModal-body']"))), 160 * 10000);
var dialog = element(by.className("sweModal-body"));
expect(dialog.isDisplayed).toBeTruthy();
var dialog-text = element(by.className("sweModal-content"));
dialogtext.getText().then(function (text) {
console.log(text);
plannumberis=text.slice(20, 28);// --here i store the plan number in var
// plannumberis **
console.log("implementation plan id is :" + plan);
browser.wait(EC.presenceOf(element(by.css("button[class='btn btn-primary okBtn']"))));
element(by.css("button[class='btn btn-primary okBtn']")).click();
})
})
})
})
sendforreview.js
describe("STAGE 2:Developer Activities : Segment Checkout", () => {
using(DeveloperInputs.Devinputs, (data, description) => {
it("Developer checksout the required segments", () => {
searchPlanId.clickSearch();
searchPlanId.selectSearchTypeWithIndex(2);
searchPlanId.enterImplementationNo(data.PlanNumber);// --here i want to
// call theplannumber generated in the first js file.How to do this
browser.wait(EC.presenceOf(element(by.css("i[class='fa fa-search']"))), 10000);
searchPlanId.clickSearchButton();
developerActions.editFirstImplementation(plannumberis);
// Here I want to call the id generated in createplan.js
// So in jasmine data provider I'm manually updating
// this after I run the 1stjs file then pass it in second jsfile
})
})
})
You should be able to achieve this by using NodeJS's global variables keyword which make variables accessible to all files in the project.
file1.js
//delcare var
global.newGlobalVar = 'value we want in another file'
file2.js
//use the var
console.log(newGlobalVar)
However as it appears you are using loops in both your test files I cannot see how this approach will work for you
The issue you're having is that var plannumberis is on the module (file) scope, not the global scope. Your variable is only accessible within that spec file.
Some more from the docs:
In browsers, the top-level scope is the global scope. This means that within the browser var something will define a new global variable. In Node.js this is different. The top-level scope is not the global scope; var something inside a Node.js module will be local to that module.
The way to put something on the global scope is to put it on the global object.
global.plannumberis=text.slice(20, 28);
After that you can reference it from any module using plannumberis.
However, I cannot encourage your design decision here. Having interdepencencies between specs like this is highly discouraged. With the way you've designed it, if anyone ever tries to run sendforreview.js by itself, the test will fail in a confusing and seemingly-arbitrary manner. You should write your specs so that they are all independent of each other and can be run in any order.
In this particular case, you could accomplish that by combining the two files into a single spec.
I am using PhantomJS for page automation. For my script, I need to load another script which is also used on the actual client-side of my webpage. This script contains some parts where global variables - which are assumed to be set before the script is loaded - are used.
Now my problem is that I can't figure out how to set these variables before I require them within PhantomJS.
This (obviously) didn't do the trick:
variableX = 1024;
var moduleX = require('myScripts.js');
Now what's the proper and intended way ( if there is one) to do this?
If you prepare the testing script as a CommonJS module, you can require it and use its methods and variables in PhantomJS.
From the docs: http://phantomjs.org/release-1.7.html
As an example, supposed there is a script universe.js which contains the following code:
exports.answer = 42;
exports.start = function () {
console.log('Starting the universe....');
}
This module can be used in another script like the following:
var universe = require('./universe');
universe.start();
console.log('The answer is', universe.answer);
And if you want to assign global variables from such a module you can use the global object as in node.js:
exports.start = function () {
global.success = true;
console.log('Starting the universe....');
}
I'm building a midly size app using backbone and its friends jquery and underscore. My plan is to use QunitJS to create unittests.
I already have a Proof-Of-Concept of the app, so I basicly have a good grasp of how the code should look like without having to test it. It looks like that:
(function() {
// prepare some model
var Query = BackboneModel.extend({});
query = new Query();
// register some events
$('body.something').click(function() {
query.set('key', 'value');
# ...
});
// create some backbone view
var QueryView = Backbone.View.extend({...})
// init backbone view
query.view = new QueryView();
// add some plumbing here ...
// and so on...
})();
Otherwise said:
I wrap the module inside a function to avoid pollution
Class declaration and class use is interleaved
event registration is interleaved with the rest of the code
variables global to the module are reused inside the module
Now I need to test that. The problem is, I think, mainly about event registration and plumbing.
My plan is to wrap the code in functions and export every function and objects I want to test. The code will look like this:
var app = (function() {
var app = {}
// prepare some model
var Query = BackboneModel.extend({});
app.query = new Query();
// register some events
app.registerEvent = function() {
$('body.something').click(function() {
query.set('key', 'value');
# ...
});
};
app.registerEvent(); // XXX: call immediatly
// create some backbone view
app.QueryView = Backbone.View.extend({...})
// init backbone view
app.query.view = new QueryView();
// add some plumbing here ...
// wrapped in function with correct arguments and call it immediatly
// and so on...
// ...
return app;
})();
This is the first time I need to write tests in javascript for this kind of application so I'm wondering whether my approach to make the code testable is correct or can be improved. For instance, It seems silly to me to wrap the registration of events in function without arguments and call them immediatly.
Is there a javascript way to do this?
So I found an excellent way to test private functions while keeping my production code clean. I am not sure if you are using any build system like Grunt or Gulp, but if you are open to it you could do something like this:
//has a dependency of 'test' causing it to run testing suite first
gulp.task('js', ['test'], function() {
return gulp.src(source)
.pipe(plumber())
//create sourcemaps so console errors point to original file
.pipe(sourcemaps.init())
//strip out any code between comments
.pipe(stripCode({
start_comment: 'start-test',
end_comment: 'end-test'
}))
//combine all separatefiles into one
.pipe(concatenate('mimic.min.js'))
//minify and mangle
.pipe(uglify())
.pipe(sourcemaps.write('maps'))
.pipe(gulp.dest('dist/js'));
});
And the file could look like:
var app = (function () {
function somePrivateFunction () {}
function someotherPrivateFunction () {}
var app = {
publicFunction: function(){}
publicVar: publicVar
}
/* start-test */
app.test = {
testableFunction: somePrivateFunction
}
/* end-test */
}());
Everything between the test comments gets stripped out after the tests are run so the production code is clean. Grunt has a version of this and I assume any automated build system can do the same. You can even set a watch task so that it runs the tests on every save. Otherwise you'll have to manually remove the exported test object before deployment.
In the case of Backbone just attach a test object to the module and reference that object in the tests. Or if you really want to separate it, set the object in the global scope. window.testObject = { //list of objects and methods to test... }; and strip that code out before deployment.
Basically what I do is avoid any calls in the library I want to test. So everything is wrapped in a function and exported.
Then I have two other files one is main.js where I do the plumbing and should be tested using integration tests with selenium and tests.js which does unit testing.
I am trying to set a global variable on protractor to use in all describe blocks.
var glob = 'test';
describe('glob test', function () {
it('should set glob', function () {
browser.get('http://example.com/test');
browser.executeScript(function () {
window.glob = glob;
});
});
});
But this returns me the following error:
Message:
[firefox #2] UnknownError: glob is not defined
I also looked at this question: protractor angularJS global variables
so i tried to set the variable glob in conf.js in this way:
exports.config = {
...,
onPrepare: function () {
global.glob = 'test';
}
};
Still, having the same error.
How can i add properly global variables in protractor tests?
It is possible to set globals from the Protractor config file with the help of params property:
exports.config = {
// ...
params: {
glob: 'test'
}
// ...
};
And you can access it in the specs using browser.params.glob.
See the reference config file.
The params object will be passed directly to the Protractor instance, and can be accessed from your test as browser.params. It is an arbitrary object and can contain anything you may need in your test. This can be changed via the command line as:
protractor conf.js --params.glob 'other test'
Update:
From the docs for browser.executeScript:
If the script is provided as a function object, that function will be converted to a string for injection into the target window. Any arguments provided in addition to the script will be included as script arguments and may be referenced using the arguments object.
So, JavaScript scope in this case does not work, you function that is passed to browser.executeScript won't have any closure variables from spec like browser. But you can pass these variables explicitly:
browser.executeScript(function (glob) {
// use passed variables on the page
console.log(glob);
}, browser.params.glob);
You can also set global variables in onPrepare() using global:
onPrepare: function () {
global.myVariable = "test";
},
Then, you would just use myVariable throughout the tests as is.
This is actually how protractor, browser and other built-in global variables were made available globally:
Runner.prototype.setupGlobals_ = function(browser_) {
// Export protractor to the global namespace to be used in tests.
global.protractor = protractor;
global.browser = browser_;
global.$ = browser_.$;
global.$$ = browser_.$$;
global.element = browser_.element;
global.by = global.By = protractor.By;
// ...
}
Note that with this approach you are polluting your global scope/namespace, be careful.
Another option is to use a process variable
Protractor is a node process. Any node process can be started with custom node variables. Not sure how it's done in windows (please comment if you know how) but for mac and any linux/unix OS you can
Start protractor with environment variable like this
MY_VAR=Dev protractor tmp/config.js
And then it will be available anywhere within your process and even in your config
console.log(process.env.MY_VAR)
I know I'm a bit late to the answer but here's another way of setting global variables that can be used throughout the file
describe("Some Global most outer describe", function(){
var glob = "some global variable";
var blob = "some other global variable";
it('should test glob', function(){
expecte(glob).toEqual("some global variable");
});
it('should test blob', function(){
expecte(glob).toEqual("some other global variable");
});
describe('Some inner describe', function(){
//Some other inner tests can also see glob and blob
});
});