close browser session after failed assertion with nightwatch - javascript

I implement a new testframework for automated tests in node.js with Nightwatch-Cucumber that based on Nightwatch.js. So, sometimes I use node.js Assertions to check some values. I work with the PageObject Pattern in my framework. My problem is that the browser session doen't close after a failed assertion and I don't know why and I don't know how to solve the problem.
Here is my StepDefinition:
const {
client
} = require('nightwatch-cucumber');
const {
defineSupportCode
} = require('cucumber');
const page = client.page.page();
defineSupportCode(({Given, When, Then}) => {
When(/^test$/, () => {
return page.test();
});
});
And that's my PageObject function:
module.exports = {
elements: {},
commands: [{
test() {
//here the assertion failed and the browser session still exist and doen't close
this.assert.equal(false, true);
return this.api;
}
}]
};
So, what can I do to realize it to close the browser and the session for ths test? It happened only if the node.js assertions fail.

Use an after or afterEach hook to always close the browser at the end of a test, regardless of outcome. See http://nightwatchjs.org/guide#using-before-each-and-after-each-hooks
after : function(browser) {
console.log('Closing down...');
browser.end();
},

To close your session after each test you have to add afterEach hook to your test file and use it like this:
afterEach : function(browser, done) {
browser.end(function(){
done();
});
}

Related

I need close and open new browser in protractor

I have a simple test:
beforeEach(function () {
lib.startApp(constants.ENVIRONMENT, browser);//get url
loginPageLoc.loginAs(constants.ADMIN_LOGIN,constants.ADMIN_PASSWORD,
browser);// log in
browser.driver.sleep(5000); //wait
});
afterEach(function() {
browser.restart(); //or browser.close()
});
it('Test1' , async() => {
lib.waitUntilClickable(adminManagersPage.ButtonManagers, browser);
adminManagersPage.ButtonManagers.click();
expect(element(by.css('.common-popup')).isPresent()).toBe(false);
});
it('Test2' , async() => {
lib.waitUntilClickable(adminManagersPage.ButtonManagers, browser);
adminManagersPage.ButtonManagers.click();
expect(element(by.css('.common-popup')).isPresent()).toBe(false);
});
The first iteration looks fine, but after .restart() I get:
Failed: This driver instance does not have a valid session ID (did you
call WebDriver.quit()?) and may no longer be used. NoSuchSessionError:
This driver instance does not have a valid session ID (did you call
WebDriver.quit()?) and may no longer be used.
If I use .close() I get:
Failed: invalid session id
But if I change Test2 on simple console.log('case 1'); it looks fine.
Please explain what am I doing wrong?
You are declaring your functions as async but are not awaiting the any actions within. If you are not setting your SELENIUM_PROMISE_MANAGER to false in your config then you will see unexpected behavior throughout your test when declaring async functions. This async behavior is likely the cause of your issue so I would ensure SELENIUM_PROMISE_MANAGER:false and ensure your awaiting your actions in each function.
The reason your test passes if you change the second test to just be console.log() is because you are not interacting with the browser and therefore the selenium session ID is not required. Every time the browser is closed the selenium session id will be destroyed and a new one created when a new browser window is launched.
Also you should be aware that there is a config setting you can enable so you do not need to do it manually in your test.
Update: Adding code examples of what I have described:
Note: If you have a lot of code already developed it will take serious effort to convert your framework to Async/await syntax. For a quicker solution you could try removing the async keywords from your it blocks
Add these to your config
SELENIUM_PROMISE_MANAGER:false,
restartBrowserBetweenTests:true
and change you spec to
beforeEach(async function () {
await lib.startApp(constants.ENVIRONMENT, browser);//get url
await loginPageLoc.loginAs(constants.ADMIN_LOGIN, constants.ADMIN_PASSWORD,
browser);// log in
await browser.driver.sleep(5000); //wait
});
it('Test1', async () => {
await lib.waitUntilClickable(adminManagersPage.ButtonManagers, browser);
await adminManagersPage.ButtonManagers.click();
expect(await element(by.css('.common-popup')).isPresent()).toBe(false);
});
it('Test2', async () => {
await lib.waitUntilClickable(adminManagersPage.ButtonManagers, browser);
await adminManagersPage.ButtonManagers.click();
expect(await element(by.css('.common-popup')).isPresent()).toBe(false);
});
There is a relevant configuration option:
// If true, protractor will restart the browser between each test.
restartBrowserBetweenTests: true,
Add the above in your config to restart browser between your tests.
Hope it helps you.

How to clean up after failed Intern functional tests?

I have some functional tests that run using Intern (3) and the last step in the test is to do some cleanup, including clearing localStorage on the remote browser, and switching back to the original window by storing the window handle right away and switching back to it before the tests end (so any subsequent tests don't fail because they're trying to run on a closed window if the previous test ended on a different one). However if some chaijs assertions fail in a .then() the cleanup code at the end gets skipped. Is there a better way to do cleanup for functional tests that will still get run even when some assertions fail?
this.leadfoot.remote
.get(require.toUrl(url))
.execute(function() { return document.querySelector('#welcome').textContent})
.then(welcome => {
assert.equal(welcome, 'hello world', 'properly greeted');
})
.execute(function() {
localStorage.clear();
});
If the assertion fails it'll never clear localStorage at the end and if the next test that runs expects localStorage to be empty it will fail too. Is there a better way to clean up after a functional test?
Use an afterEach method.
afterEach() {
return this.remote
.execute(function () {
localStorage.clear();
});
},
'my test'() {
return this.remote
.get(...)
.execute(...)
.then(welcome => {
assert.equal(welcome, ...);
});
}
in our project we do it like this:
afterEach: function() {
var command = this.remote;
if(intern.variables.currentCase && !intern.variables.currentCase.hasPassed) {
command = command.execute(function () {
localStorage.clear();
});
}
return command;
},
and local Storage.clear() will be executed only if the test has failed:

Making modular login function with selenium-webdriver and mocha

I am trying to create a login function that I can use with mocha testing and selenium-webdriver for unit testing, since I have a bunch of things to do that start with "user logs in and..."
Below is my best shot, but when I run it, the console just logs
Already registered user logs in and sees homepage
1) should work (it's red text)
Login an existing user
and then nothing happens and I have to control-C out of the process. My code was working before I went for this modular login implementation, so I'm sure that's where my problem is. Is there something I am missing here? I am also open to general critiques of how I went about making the login function, since I don't have that much experience with webdriver.
Below is my code. It's in one js file.
var test = require('selenium-webdriver/testing'),
chai = require('chai');
chai.use(require('chai-string'));
chai.use(require('chai-as-promised'));
var expect = chai.expect,
webdriver = require('selenium-webdriver'),
By = webdriver.By;
// I want this to be a modular login function I can use multiple places
function login(driver) {
test.describe('Login an existing user', function() {
test.it('should work', function() {
this.timeout(0);
var email = 'myemail#test.com';
var password = 'password';
driver.get('http://localhost:9000');
driver.getTitle().then(function(title) {
expect(title).to.equal('my title');
})
.then(function() {
// do login stuff and click login button
});
.then(function() {
return driver;
});
});
});
}
//an example of when I would use the login function
test.describe('Already registered user logs in and sees homepage', function() {
test.it('should work', function() {
this.timeout(0);
var driver = new webdriver.Builder().
withCapabilities(webdriver.Capabilities.chrome()).
build();
driver = login(driver)
.then(function() {
driver.findElement(By.xpath("relevant xpath")).click();
})
})
})
By calling login from inside test.it, you are effectively calling describe inside it, which is not allowed. Mocha simply does not support calling describe or it from inside it. Unfortunately, it does not try to detect such occurrence and scream about it. It just goes ahead and do whatever it does. I all cases it results in undefined behavior, like the behavior you observed. In most cases, the undefined behavior is erratic. I very rare cases, it happens to match the developer's expectations but that's due to chance, not design.
In your case, I'd just remove the calls to test.describe and test.it from inside login, and make sure to return a promise. You can keep the expect there. You won't be able to call this.timeout(0) inside login, however. Something like this:
function login(driver) {
var email = 'myemail#test.com';
var password = 'password';
driver.get('http://localhost:9000');
return driver.getTitle().then(function(title) {
expect(title).to.equal('my title');
})
.then(function() {
// do login stuff and click login button
})
.then(function () {
return driver;
});
}
And modify your calling code to:
var promise = login(driver)
.then(function(driver) {
driver.findElement(By.xpath("relevant xpath")).click();
});
You can do whatever you want to the promise, or nothing at all. However, driver is not itself a promise so setting driver to that value won't work.

How to stub a method that is called from an outer scope to the function under test?

I have a Redis client that is created thus using the node_redis library (https://github.com/NodeRedis/node_redis):
var client = require('redis').createClient(6379, 'localhost');
I have a method I want to test whose purpose is to set and publish a value to Redis, so I want to test to ensure the set and publish methods are called or not called according to my expectations. The tricky thing is I want this test to work without needing to fire up an instance of a Redis server, so I can't just create the client because it will throw errors if it cannot detect Redis. Therefore, I need to stub the createClient() method.
Example method:
// require('redis').createClient(port, ip) is called once and the 'client' object is used globally in my module.
module.exports.updateRedis = function (key, oldVal, newVal) {
if (oldVal != newVal) {
client.set(key, newVal);
client.publish(key + "/notify", newVal);
}
};
I've tried several ways of testing whether set and publish are called with the expected key and value, but have been unsuccessful. If I try to spy on the methods, I can tell my methods are getting called by running the debugger, but calledOnce is not getting flagged as true for me. If I stub the createClient method to return a fake client, such as:
{
set: function () { return 'OK'; },
publish: function () { return 1; }
}
The method under test doesn't appear to be using the fake client.
Right now, my test looks like this:
var key, newVal, oldVal, client, redis;
before(function () {
key = 'key';
newVal = 'value';
oldVal = 'different-value';
client = {
set: function () { return 'OK'; },
publish: function () { return 1; }
}
redis = require('redis');
sinon.stub(redis, 'createClient').returns(client);
sinon.spy(client, 'set');
sinon.spy(client, 'publish');
});
after(function () {
redis.createClient.restore();
});
it('sets and publishes the new value in Redis', function (done) {
myModule.updateRedis(key, oldVal, newVal);
expect(client.set.calledOnce).to.equal(true);
expect(client.publish.calledOnce).to.equal(true);
done();
});
The above code gives me an Assertion error (I'm using Chai)
AssertionError: expected false to equal true
I also get this error in the console logs, which indicates the client isn't getting stubbed out when the method actually runs.
Error connecting to redis [Error: Ready check failed: Redis connection gone from end event.]
UPDATE
I've since tried stubbing out the createClient method (using the before function so that it runs before my tests) in the outer-most describe block of my test suite with the same result - it appears it doesn't return the fake client when the test actually runs my function.
I've also tried putting my spies in the before of the top-level describe to no avail.
I noticed that when I kill my Redis server, I get connection error messages from Redis, even though this is the only test (at the moment) that touches any code that uses the Redis client. I am aware that this is because I create the client when this NodeJS server starts and Mocha will create an instance of the server app when it executes the tests. I'm supposing right now that the reason this isn't getting stubbed properly is because it's more than just a require, but the createClient() function is being called at app startup, not when I call my function which is under test. I feel there still ought to be a way to stub this dependency, even though it's global and the function being stubbed gets called before my test function.
Other potentially helpful information: I'm using the Gulp task runner - but I don't see how this should affect how the tests run.
I ended up using fakeredis(https://github.com/hdachev/fakeredis) to stub out the Redis client BEFORE creating the app in my test suite like so:
var redis = require('fakeredis'),
konfig = require('konfig'),
redisClient = redis.createClient(konfig.redis.port, konfig.redis.host);
sinon.stub(require('redis'), 'createClient').returns(redisClient);
var app = require('../../app.js'),
//... and so on
And then I was able to use sinon.spy in the normal way:
describe('some case I want to test' function () {
before(function () {
//...
sinon.spy(redisClient, 'set');
});
after(function () {
redisClient.set.restore();
});
it('should behave some way', function () {
expect(redisClient.set.called).to.equal(true);
});
});
It's also possible to mock and stub things on the client, which I found better than using the redisErrorClient they provide for testing Redis error handling in the callbacks.
It's quite apparent that I had to resort to a mocking library for Redis to do this because Sinon couldn't stub out the redisClient() method as long as it was being called in an outer scope to the function under test. It makes sense, but it's an annoying restriction.

Mocha test order file-wise and webdriverjs instance persistence

I'm testing my web-app with Mocha and WebDriver. I'm struggling with the best practices regarding Mocha test-order and persistent state of the driver.
I want to separate tests to different files, e.g.
test\
index.js
selenium\
login.js
search.js
So in execution wise, login.js has to be first because it logs in to the app and gets authenticated. Only after that search.js is possible to do. But how? In login.js I now have this:
webdriverjs = require('webdriverjs');
describe 'UI/Selenium', ->
client = {}
before ->
client = webdriverjs.remote
desiredCapabilities:
browserName: 'chrome'
client.init()
client.windowHandleSize({width: 1920, height: 1080})
it 'should let us login', (done) ->
client.url('http://127.0.0.1:1337/login')
.setValue('#username', 'username')
.setValue('#password', 'password')
.buttonClick('button[type="submit"]')
.waitFor '#search_results_user', 5000, (err) -> throw err if err
.call done
How can I persist the state of client to other tests without having to re-init it every time? And how do I define the execution order of files with Mocha?
How can I persist the state of client to other tests without having to re-init it every time?
You setup whatever you want to share among tests in a before hook (and tear it down in an after hook). This would mean moving the code in your test for logging in into your before hook. Supposing you are testing the "foo" view, you could do:
describe("foo view", function () {
before(function () { /* create selenium driver */ });
describe("when used by a logged in user", function () {
before(function () { /* log in */ });
it(...
it(...
after(function () { /* log out */ });
});
describe("when used by a logged out user", function () {
it(...
it(...
});
after(function () { /* shut down the driver */ });
});
And how do I define the execution order of files with Mocha?
Mocha tests should not depend on one another and consequently should not depend on the order in which they are executed.
If you are in a situation where you must break this cardinal rule, you could just invoke Mocha from the command line with the list of test files, in the order you want. Or you could launch Mocha programmatically and use addFile to add the files in order.

Categories

Resources