I want to retrieve a list of elements on a page, and for each one, create a test spec. My (pseudo) code is :-
fetchElements().then(element_list) {
foreach element {
it("should have some property", function() {
expect("foo")
})
}
}
When I run this, I get "No specs found", which I guess makes sense since they are being defined off the main path.
What's the best way to achieve dynamically created specs?
There are major problems preventing it to be easily achieved:
the specs you are creating are based on the result of asynchronous code - on the elements Protractor should first find
you can only have the Protractor/WebDriverJS specific code inside the it, beforeEach, beforeAll, afterEach, afterAll for it to work properly and have the promises put on the Control Flow etc.
you cannot have nested it blocks - jasmine would not execute them: Cannot perform a 'it' inside another 'it'
If it were not the elements you want to generate test cases from, but a static variable with a defined value, it would be as simple as:
describe("Check something", function () {
var arr = [
{name: "Status Reason", inclusion: true},
{name: "Status Reason", inclusion: false}
];
arr.map(function(item) {
it("should look good with item " + item, function () {
// test smth
});
});
});
But, if arr would be a promise, the test would fail at the very beginning since the code inside describe (which is not inside it) would be executed when the tests would be loaded by jasmine.
To conclude, have a single it() block and work inside it:
it("should have elements with a desired property", function() {
fetchElements().then(element_list) {
foreach element {
expect("foo")
})
}
}
If you are worried about distinguishing test failures from an element to element, you can, for instance, provide readable error messages so that, if a test fails, you can easily say, which of the elements has not passed the test (did not have a specific property in your pseudo-test case). For example, you can provide custom messages to expect():
expect(1).toEqual(2, 'because of stuff')
We can generate dynamic tests using jasmin data provider but it is working with only static data.
If we want to generate tests from the asynchronous call in protractor, then we need to use onprepare function in the protractor config js.
Create a bootloader and read the test cases from excel or server and import the data loader in the onprepare function. It is bit difficult to explain because I have faced
many issues like import is not supported in this javascript version and expected 2 args but got only 1. finally I have used babel to fix the issues and able to generate tests.
Below is the sample implementation that I have done in the on prepare method
var automationModule = require('./src/startup/bootloader.ts');
var defer = protractor.promise.defer();
automationModule.tests.then(function(res) {
defer.fulfill(res);
});
bootloader.ts contains the code to read the test suites and tests from excel sheet and sets the tests to the single to class.
Here res is the instance of singleton class which is returning from bootloader.ts
hard to explain everything here but you can take a look at my full implementation in my github https://github.com/mannejkumar/protractor-keyword-driven-framework
Related
The application i am working on has 3 different environments. so is there a way to use a specific test data files for each environment( QA/STG/PRD) when running tests. I know we can use cypress.env file to specify environment related data but i couldn't figure out how to specify different file when running in different environments.
I believe it is not possible to choose which files your cypress will run according to the .env, but you can put your test content inside an IF, so even if cypress runs the test file, everything will be inside one IF that if not satisfied will not perform actions.
describe('Group of tests', () => {
it('Test 1', () => {
if(env == 'dev') {
// Your test here
} else {
/* put something here so that cypress doesn't
throw an error claiming the test suite is empty. */
}
});
});
There's a package cypress-tags that might suit your needs.
Select tests by passing a comma separated list of tags to the Cypress environment variable CYPRESS_INCLUDE_TAGS
describe(['QA'], 'Will tag every test inside the describe with the "QA" tag',
function () { ... });
it(['QA'], 'This is a work-in-progress test', function () { ... });
I'm setting up a CI-chain and decided to use Cypress for the UI testing. I need to get the result for each individual testcase in my suite. Preferably from within Node in for example a afterEach statement.
Has anyone done this before? Is there any built-in support for this?
I do not want to parse the end result for testcases preferably.
It was possible by using Mocha's this.currentState in conjunction with Cypress plugins.
This is how I solved it:
cypress/plugins/index.js
on("task", {
testFinished(event) {
console.log(event.title, event.result);
return null;
}
});
in my testsuite
afterEach(function() {
cy.task("testFinished", { title: this.currentTest.title, result: this.currentTest.state });
});
The console.log in plugins can now easily be switched for a POST request to wherever you want to store the results.
I am trying to evaluate the tagged features in the this.BeforeFeature hook in world file but I am getting the error 'TypeError: handler is not a function' . What I interpret from the error message is that this.BeforeFeature() takes function as parameter and I am using the below code.
there are other ways to expedite this problem - like reading the names of feature but it will totally defeat the purpose of tags it that case so I don't want to employ that approach.
this.registerHandler('BeforeFeature', {tags: ["#foo,#bar"]} ,function (event, callback) {
console.log("before feature")
global.browser.driver.manage().window().setSize(500, 800);
callback();
});
Any help is appreciated.
Since scenario's inherit the hooks from feature evaluating the hooks on scenario should do the job-
Inheritance works as below-
Feature(hooks)->Scenario(hooks)/Scenario Outlines(hooks -> Examples
this.Before("#foo", function (scenario) {
// This hook will be executed before scenarios tagged with #foo // ...
});
Hope it helps.Thanks
How does a BDD-style (mocha, jasmine, etc.) large wrapped single describe() get broken down into multiple files. I have a 5,000-line unit test file, all of it wrapped as follows:
describe('services',function(){
describe('serviceA',function(){...});
describe('serviceB',function(){...});
describe('serviceC',function(){...});
describe('serviceD',function(){...});
// .....
describe('serviceN',function(){...});
});`
For sanity's sake I would like to break each one ('serviceA','serviceB',...,'serviceN') into its own file. But I want each to still be inside describe("services").
I see 2 methods:
use require in the parent: describe('serviceA',require('./services/serviceA')); (repeat lots of times)
have some wrapper include them all
I don't like the second method, since it requires a lot of keeping of file name, require() and describe() in sync (and thus violates DRY).
I prefer each file know what it is testing, and not have to explicitly add the path, rather just have the testrunner intelligently include everything in './services', and thus have inside serviceA.js:
describe('serviceA', function() {
// ....
});
but then I need some way to "wrap" 'serviceA' inside 'services'. Is there any way to do that?
In this case, I am testing an angular app via karma, but the concept is the same.
Jasmine's describe has no special meaning for loading of files. It's a descriptor. What's stopping you from just doing this?
Karma config:
files: [
'src/services/**/*.js',
'tests/services/**/*.js'
]
serviceA.spec.js
describe('services', function () {
describe('serviceA', function () {
});
});
serviceB.spec.js
describe('services', function () {
describe('serviceB', function () {
});
});
So I've been playing around with Lift in Scala, and I've been enjoying it a lot. I might just be missing something that exists in the lift javascript library, but I haven't been able to find any way of using a scoped javascript callback. It seems that the lift way of handling callbacks is to pass the callback as function name and have lift return a JsCmd that Call()s the function.
My lift code is heavily based on this example http://demo.liftweb.net/json_more
And my javascript looks kinda like
function operation(config) {
var actions = config.actions,
action = actions.shift(),
name = config.name;
function chainAction(response) {
if (actions.length > 0) {
action = actions.shift();
action.action(name, chainAction);
}
}
action.action(name, chainAction);
}
operation({
name: "ajax",
actions: [
{ action: ajaxCall1 },
{ action: ajaxCall2 }
]
});
Where I'd want ajaxCall1 and ajaxCall2, to be AJAX calls to lift. i.e. callNoParam() in the lift example, and chainAction to be the scoped callback. Is there a way to do this in lift that I'm missing? For clarity, I have been able to get this code to call the lift function, but not to handle the callback correctly.
Thanks.
Edit
Upon reading through the lift-generated javascript code, it looks like there are indeed placeholders for success/failure callbacks. In particular, it looks like this line of lift
AllJsonHandler.is.jsCmd
is generating this line of javascript
function F86737576748N5SY25(obj) {liftAjax.lift_ajaxHandler('F86737576748N5SY25='+ encodeURIComponent(JSON.stringify(obj)), null,null);}
which references this method
lift_ajaxHandler: function(theData, theSuccess, theFailure, responseType)
But not allowing me to pass theSuccess or theFailure which look like they are being passed along into jQuery.ajax() calls. My investigation continues. If anyone has any good resources on is.jsCmd it would be appreciated.
Below is a piece of code that adds a Javascript function doCallback to the page (in #placeholder). This function will print a line to the console and then do an ajaxCall back to the server to the function commandCallback.
def addExecuteCallback(ns: NodeSeq):NodeSeq = {
val log = JsRaw("console.log('[doCallback] Generated from Lift.');").cmd &
SHtml.ajaxCall(JsRaw("commandString"), commandCallback _)._2.cmd
val f = JsCmds.Function("doCallback", List[String](), log)
("#placeholder" #> JsCmds.Script(f)).apply(ns)
}
At the end of commandCallback, you can return:
JsCmds.Run("chainAction('" + valueOfResponse + "');")