How to override the preconfigured casper instance in a test environment - phantomjs

i have installed the phantomjs 1.9.7 & casperjs 1.1.0 and now i see that all my scripts need to be updated and to remove the first line since it makes the below fatal error:
Fatal: you can't override the preconfigured casper instance in a test environment.
Docs: http://docs.casperjs.org/en/latest/testing.html#test-command-args-and-options
Reading also in another question i understand that i have to remove now this line from my scripts:
var casper = require('casper').create();
Which is a lot of work since i have meanwhile more then 200 script that needs to be updated!!!
So my question is how can i overcome this issue without updating all the scripts (mentioned in Can't run the testing framework in CasperJS)
var casper = require('casper').create();
if (casper.cli.has("ip") === false) {
casper.echo("Usage: casper.js test get_info.js --ip=<x.x.x.x>").exit();
}
casper.test.begin('get info', 1, function suite(test) {
casper.start("http://" + casper.cli.get("ip") + ":xxxx/man/", function() {
test.assertTitle("TEST GUI", "GUI has the correct title");
test.assertExists('form[name="loginForm"]', "login form is found");
this.fill('form[name="loginForm"]', {
'userId': 'xxxxx',
'password': 'yyyyy'
}, true);
});
casper.then(function() {
test.assertTextExists("Welcome", "Login into TEST GUI");
this.click('a#test_menu.menu1itemUnSel[tabindex="4"]');
});
casper.then(function() {
casper.wait(5000, function() {
this.echo('should appear after 5s');
});
});
casper.then(function() {
test.assertTextExists("TEST Data", "Login into Data");
this.click('a#test_menu_data.menu2itemUnSel[tabindex="4"]');
});
casper.then(function() {
casper.wait(5000, function() {
this.echo('should appear after 5s');
});
});
casper.then(function() {
test.assertTextExists("Logout", "Loggin out from TEST GUI");
this.click('a.minorLinkshighlight');
});
casper.run(function() {
test.done();
this.exit();
});
});
The above script was working before i installed the phantomjs 1.9.7 & casperjs 1.1.0, the only thing that i can't recall is which version i had before my server had to be reinstalled!
Thanks in adv.

Yes this is my way of avoiding updating old casperjs scripts due to the overriding of the preconfigured casper instance in a test environment!
go to the casperjs path and edit in path modules:
/../casperjs/modules/casper.js
and remark out the below lines:
exports.create = function create(options) {
"use strict";
// This is a bit of a hack to check if one is trying to override the preconfigured
// casper instance from within a test environment.
// if (phantom.casperTest && window.casper) {
// console.error("Fatal: you can't override the preconfigured casper instance in a test environment.");
// console.error("Docs: http://docs.casperjs.org/en/latest/testing.html#test-command-args-and-options");
// phantom.exit(1);
// }
return new Casper(options);
};
This worked for me. You should use it only on your own risk!

Related

How to configure Detox lifecycle hooks in the latest version?

I run detox in version 17.13.2 with jest-circus as the test runner. My main problem is the app is not reset either after or before I run the tests which leads to an inconsistent state of the app.
My test file:
import { by, device, element, expect, waitFor } from "detox"
describe("Login", () => {
beforeEach(async () => {
await device.reloadReactNative()
})
it("should login with correct data", async () => {
await element(by.id("login_email_input")).typeText("ch.tietz#gmail.com")
await element(by.id("login_password_input")).typeText("12345678")
await element(by.id("login_submit_button")).tap()
await waitFor(element(by.id("workout_screen"))).toBeVisible()
// additional test steps
})
})
Now if the login actually works but the test fails at one of the subsequent steps, the state of the app user will still be "logged in".
From what I understand, it's not possible to actually alter the app state, e.g. by clearing the AsyncStorage or interacting directly with the state mgmt tool. Instead, it's recommended to just reinstall the app - but this is exactly where I am struggling.
I have tried numerous approaches and none of them worked. What makes it really hard to understand configuration is that detox completely changed how the configuration works and switched to jest-circus as the main test runner.
My setup is basically the one created by jest init -r jest. From what I understand, this already includes some defaults for detox.init() and detox.cleanup():
{
"detox": {
"behavior": {
"init": {
"reinstallApp": true,
"launchApp": true,
"exposeGlobals": true
},
"cleanup": {
"shutdownDevice": false
}
}
}
}
However, this does not seem to be sufficient to actually wipe the app state after running the tests.
I tried working with an init script as setupFilesAfterEnv, which would call cleanup() after the test suite is run. Actually that works in an older project which still uses jasmine 2 as test runner.
import { cleanup, init } from 'detox';
const adapter = require('detox/runners/jest/adapter');
const specReporter = require('detox/runners/jest/specReporter');
const config = require('../package.json').detox;
// Set the default timeout
jest.setTimeout(120000);
jasmine.getEnv().addReporter(adapter);
// This takes care of generating status logs on a per-spec basis. By default, jest only reports at file-level.
// This is strictly optional.
jasmine.getEnv().addReporter(specReporter);
beforeAll(async () => {
await init(config, { launchApp: false });
}, 300000);
beforeEach(async () => {
await adapter.beforeEach();
});
afterAll(async () => {
await adapter.afterAll();
await cleanup();
});
First off, it complains that jasmine is not defined. I guess that's because actually the adapter in this case should be a DetoxAdapterCircus which it is not, even though in my config file I specify:
{
"preset": "react-native",
"testEnvironment": "./environment.ts",
"testRunner": "jest-circus/runner",
"testTimeout": 120000,
"testRegex": "\\.e2e\\.ts$",
"reporters": ["detox/runners/jest/streamlineReporter"],
"verbose": true
}
"testRunner": "jest-circus/runner"
Another idea would be to alter the CustomDetoxEnvironment but I do not understand how I can get access to the detox lifecycle hooks.
const {
DetoxCircusEnvironment,
SpecReporter,
WorkerAssignReporter,
} = require("detox/runners/jest-circus")
class CustomDetoxEnvironment extends DetoxCircusEnvironment {
constructor(config) {
super(config)
// should I access the hooks here now?
// This takes care of generating status logs on a per-spec basis. By default, Jest only reports at file-level.
// This is strictly optional.
this.registerListeners({
SpecReporter,
WorkerAssignReporter,
})
}
}
module.exports = CustomDetoxEnvironment
Tl;dr: I don't know where to put my reusable lifecycle hooks in the latest version of Detox. Also, I wonder if these custom configurations are even needed to reinstall the app and wipe the app data before each test suite.
You can still use setupFilesAfterEnv, but you're not supposed to call the same hooks/cleanup as before (see https://github.com/wix/Detox/issues/2410#issuecomment-744387707).
here's an example of our init script:
// init.ts
// with "setupFilesAfterEnv": ["./init.ts"] in the conf
beforeAll(async () => {
// to reset the state
await device.clearKeychain();
// we are launching the app manually
await device.launchApp({
permissions: { notifications: 'YES', location: 'inuse' },
});
await device.setURLBlacklist([
// ...
]);
});

Protractor: Error: ReferenceError: describle is not defined

I tried to run protractor test but there is error shows up on the command prompt. I put the spec file and config file at the same directory. Below is the content of the file.
spec.js
describle("Freelance Website", function () {
it("Sign Up", function (){
browser.get("https://www.freelancer.com/");
});
});
config.js
exports.config = {
framework: 'jasmine',
seleniumAddress: 'http://localhost:4444/wd/hub',
specs: ['spec.js']
}
I run the protractor test with following command protractor conf.js. What is the problem? How to solve it?
please correct the spelling of describe. Refer the below code
describe("A suite is just a function", function() {
var a;
it("and so is a spec", function() {
a = true;
expect(a).toBe(true);
});
});

Intermittent Selenium ECONNREFUSED errors on CircleCI

Our CircleCI tests use selenium, via the selenium-webdriver to run UI tests through PhantomJS. The tests work 100% of the time locally in our vagrant env, but fail about 1 out of 3 times on CircleCI with ECONNREFUSED errors like:
Error: ECONNREFUSED connect ECONNREFUSED 10.0.4.1:59525
at ClientRequest.<anonymous> (node_modules/selenium-webdriver/http/index.js:238:15)
at Socket.socketErrorListener (_http_client.js:310:9)
at emitErrorNT (net.js:1278:8)
at _combinedTickCallback (internal/process/next_tick.js:74:11)
at process._tickCallback (internal/process/next_tick.js:98:9)
From: Task: WebDriver.navigate().to(http://127.0.0.1:8080/src/public/login.php?t=ur&ign=1&ut=ad)
at thenableWebDriverProxy.schedule (node_modules/selenium-webdriver/lib/webdriver.js:816:17)
at Navigation.to (node_modules/selenium-webdriver/lib/webdriver.js:1140:25)
at thenableWebDriverProxy.get (node_modules/selenium-webdriver/lib/webdriver.js:997:28)
at User.logIn (testJs/ui/utils/user.js:9:16)
at Host.logInAsHost (testJs/ui/utils/host.js:13:14)
at Preferences.disableRevenueGeneration (testJs/ui/utils/preferences.js:57:14)
at Context.<anonymous> (testJs/ui/tests/preferences.js:13:22)
These errors happen at random times throughout our tests and they are not being triggered by any particular place in our tests. As far as I can tell, this issue is occurring on the selenium/selenium-webdriver end as the web server remains up and working and isn't generating any errors.
I’ve tried all of the following and none of them have worked:
Upgraded to the latest selenium-webdriver (3.4.0)
Upgraded to a more recent version of nodejs (6.9.2)
Used a different web server
Upgraded to a recent version of PhantomJS (1.9.7-15)
Tried using export DBUS_SESSION_BUS_ADDRESS as per
https://github.com/SeleniumHQ/docker-selenium/issues/87
In node_modules/selenium-webdriver/http/index.js, I modified the code to retry for ECONNREFUSED errors by reusing the retry already there for ECONNRESET, i.e. if (e.code === 'ECONNRESET') { becomes if (e.code === 'ECONNRESET' || e.code === 'ECONNREFUSED') {. This doesn’t work as then selenium-webdriver just retries indefinitely and from this I realized that the issue appears to be that once an ECONNREFUSED error is encountered, selenium/selenium-webdriver are unrecoverable.
Has anyone found a solution?
WARNING: This is merely a hack and is by no means a proper solution, but it is the only one that appears to reliably work for us on CircleCI. Hopefully, someone else will find a better solution.
Our test framework, mocha, appears to have a retry mechanism developed specifically for selenium (go figure): http://mochajs.org/#retry-tests. The first gotcha is that code in after and before is not retried. Therefore, you need to move code to beforeEach and afterEach. The second gotcha is that you need some special logic in beforeEach and afterEach to handle driver errors.
So, in order to utilize this mechanism, we had to refactor our tests so that each test has a beforeEach that sets up a fresh webdriver instance and then an afterEach that quits that driver instance. This way, a crashed driver instance doesn’t stop all subsequent tests. We developed a helper function that we call at the beginning of all describe blocks so that we don’t have to add too much code to our tests.
Then, we added this.retries(10) to our topmost describe block.
Here is the helper class:
var webdriver = require('selenium-webdriver');
// We default to using phantomjs, but in the future, when we run tests in other browsers, e.g. via
// SauceLabs, we'll want to change the browser.
var customPhantom = webdriver.Capabilities.phantomjs();
customPhantom.set('ssl-protocol', 'any');
// For convenience in tests
global.By = webdriver.By;
global.until = webdriver.until;
var SeleniumStabilizer = function () {};
SeleniumStabilizer.prototype.MAX_RETRIES = 10;
SeleniumStabilizer.prototype.createDriver = function () {
global.driver = new webdriver.Builder()
// .forBrowser('phantomjs')
.withCapabilities(customPhantom)
.build();
// Resolves when driver is ready
return global.driver;
};
SeleniumStabilizer.prototype.init = function (opts) {
var self = this,
n = 0;
var beforeEachWithRetries = function () {
// Create a fresh selenium driver for the next test
return self.createDriver().then(function () {
if (opts.onBeforeEach) {
// Execute the test-specific defined onBeforeEach
return opts.onBeforeEach();
}
}).catch(function (err) {
// ECONNREFUSED error and we should retry?
if (err.message.indexOf('ECONNREFUSED') !== -1 && n++ < self.MAX_RETRIES) {
return beforeEachWithRetries();
} else {
throw err;
}
});
};
opts.beforeEach(function () {
n = 0;
return beforeEachWithRetries();
});
var afterEachWithRetries = function () {
return Promise.resolve().then(function () {
if (opts.onAfterEach) {
// Execute the test-specific defined onAfterEach
return opts.onAfterEach();
}
}).then(function () {
// Quit the selenium driver before starting the next test
return driver.quit();
}).catch(function (err) {
// ECONNREFUSED error and we should retry?
if (err.message.indexOf('ECONNREFUSED') !== -1 && n++ < self.MAX_RETRIES) {
// Create a new driver
return self.createDriver().then(function () {
return afterEachWithRetries();
});
} else {
throw err;
}
});
};
opts.afterEach(function () {
n = 0;
return afterEachWithRetries();
});
};
Then, your tests look like:
var seleniumStabilizer = new SeleniumStabilizer();
describe('my tests', function () {
this.retries(seleniumStabilizer.MAX_RETRIES);
describe('search engines', function () {
seleniumStabilizer.init({
beforeEach: beforeEach,
afterEach: afterEach,
onBeforeEach: function () {
// e.g. return doPromiseAfterDriverSetUp();
},
onAfterEach: function () {
// e.g. return doPromiseBeforeDriverQuits();
}
});
it('should get google', function () {
return driver.get('https://www.google.com');
});
it('should get amazon', function () {
return driver.get('https://www.amazon.com');
});
});
});

Functional testing with grunt-webdriver, mocha and chai-as-promised

I'm trying to build a functional testing system to verify our web site is behaving correctly for our users. I have cobbled together a bunch of Node.js modules and helpers in an attempt to get a framework that provides simple, concise tests without a heap of nested function callbacks and I believe promises can provide that, so my package.json file looks like this:
"dependencies": {
"chai-as-promised": "^4.3.0",
"grunt": "^0.4.5",
"grunt-webdriver": "^0.4.8"
}
My Gruntfile.js looks like this:
module.exports = function(grunt) {
grunt.initConfig({
webdriver: { // for use with webdriver.io
options: {
desiredCapabilities: {
browserName: 'phantomjs' // No Xvfb required
}
},
chrome: {
tests: ['chrome/*.js'],
options: {
desiredCapabilities: {
browserName: 'chrome'
}
}
},
},
});
grunt.loadNpmTasks('grunt-webdriver');
grunt.registerTask('default', ['webdriver']);
};
And finally my test case in chrome/login.js looks like this:
'use strict';
var chai = require('chai'),
chaiAsPromised = require('chai-as-promised'),
assert;
chaiAsPromised.transferPromiseness = browser.transferPromiseness;
chai.use(chaiAsPromised);
assert = chai.assert;
describe('login test', function () {
it('verifies user can log in', function(done) {
browser
.url('https://localhost/')
.setValue('#userauth_username','foo')
.setValue('#userauth_password',"password")
.submitForm('#form_users_login')
.then(function(){
browser.getText('#auth-user-id', function(err, value){
console.log(value);
});
assert.becomes(browser.getText('#auth-user-id'), 'foo');
})//.call(done);
});
});
When I run grunt webdriver:chrome on the command line, I see it start up Chrome and log into the website. The 'auth-user-id' span is correctly displaying the user's id after logging in but for some reason browser.getText() is not returning it and the test is therefore failing. I have tried adding a .pause(100) after the .submitForm() to give me time to interact with the page in Chrome so I know it is a problem in the test case.
What am I doing wrong?
This seems to be the best and most succinct way of doing what I want. I'm not sure I need chai-as-promised yet but maybe I'll move the login function to an included file and use chai-as-promised to assert that the promised login has occurred before entering the tests.
'use strict';
var chai = require('chai'),
chaiAsPromised = require('chai-as-promised'),
assert,
expect;
chaiAsPromised.transferPromiseness = browser.transferPromiseness;
chai.use(chaiAsPromised);
assert = chai.assert;
expect = chai.expect;
describe('login test', function () {
it('verifies user can log in', function(done) {
browser
.url('https://localhost/')
.setValue('#userauth_username','foo')
.setValue('#userauth_password',"password")
.submitForm('#form_users_login')
.waitForExist('#auth-user-id')
.getText('#auth-user-id')
.then(function(text){
//console.log('Username: ' + text);
assert.equal(text, 'foo');
})
.saveScreenshot('out.png')
.call(done)
});
it('should not display logincontrols after login', function(done){
browser
.isVisible('#logincontrols')
.then(function(bool){
expect(bool).to.be.false;
})
.call(done)
});
it('should display loggedin section after login', function(done){
browser
.isVisible('#loggedin')
.then(function(bool){
expect(bool).to.be.true;
})
.call(done)
});
});
and for completeness, this is what I see on the output:
# grunt webdriver:chrome
Running "webdriver:chrome" (webdriver) task
login test
✓ verifies user can log in (7691ms)
✓ should not display logincontrols after login (70ms)
✓ should display loggedin section after login (58ms)
3 passing (8s)
Done, without errors.

How do I take a screenshot when a test in internjs fails?

I am having issues figuring out how to take a screenshot ONLY when a test fails in InternJs. I have this simple test in my registerSuite;
'verify google homepage': function () {
var url = 'https://www.google.com/';
return this.remote
.get(url)
.getCurrentUrl()
.then(function (data) {
assert.strictEqual(data, url, 'Incorrect URL');
})
.findByName('q')
.click()
}
I can simply create a screenshot using the following code;
.takeScreenshot
.then(function (data) {
fs.writeFileSync('/path/to/some/file', data, 'base64');
)}
I want to only take a screenshot, if the above test fails the assertion or is unable to find the locator.
I looked into the afterEach method, but I can't figure out how to get the status of the last test to apply a conditional.
So my question is, has anyone setup their internjs test to only take screenshots on failures and how was it accomplished?
It is not currently possible to interact with the currently executing test from beforeEach or afterEach methods; this capability is coming in the next version of Intern.
Selenium server, by default, provides a screenshot on every Selenium command failure, which is a Buffer object on the error.detail.screen property. If a Selenium command fails, just use this property which already has the screenshot waiting for you.
For assertion failures, you can create a simple promise helper to take a screenshot for you:
function screenshotOnError(callback) {
return function () {
try {
return callback.apply(this, arguments);
}
catch (error) {
return this.remote.takeScreenshot().then(function (buffer) {
fs.writeFileSync('/path/to/some/file', buffer);
throw error;
});
}
};
}
// ...
'verify google homepage': function () {
return this.remote.get(url).getCurrentUrl().then(screenshotOnError(function (actualUrl) {
assert.strictEqual(actualUrl, url);
}));
}
If it’s too inconvenient to wrap all your callbacks manually like this, you can also create and use a custom interface for registering your tests that wraps the test functions automatically for you in a similar manner. I’ll leave that as an exercise for the reader.
You can use catch method at the end of your chain and use error.detail.screen as suggested by C Snover.
'verify google homepage': function () {
return this.remote
.get(require.toUrl('./fixture.html'))
.findById('operation')
.click()
.type('hello, world')
.end()
.findById('submit')
.click()
.end()
.catch(function(error){
fs.writeFileSync('/tmp/screenshot.png', error.detail.screen);
})
}
I've been playing with this today and have managed to get it for an entire suite rather than needing to add the code to every single test which seems quite needless.
var counter = -1,
suite = {
beforeEach: function () {
counter++;
},
afterEach: function () {
var currentTest = this.tests[counter];
if (!currentTest.error) {
return;
}
this.remote
.takeScreenshot().then(function (buffer) {
if (!fs.existsSync(path)) {
fs.mkdirSync(path);
}
fs.writeFileSync('/tmp/' + currentTest.name + '.png', buffer);
});
}
};
The annoying thing you will need to do is do this for every test suite rather than "globally" but is much better than doing it for every test.
Building on the answer by Hugo Oshiro,
// tests/support/CleanScreenshots.js
define([
'intern/dojo/node!path',
'intern/dojo/node!del',
], function(path, del) {
return new Promise((resolve, reject) => {
let directory = 'tests/screenshots';
del(path.join(directory, '**/*'))
.then(resolve)
.catch(reject);
});
});
Then in your intern config:
/* global define */
define([
'tests/support/CleanScreenshots'
], function (CleanScreenshots) {
return {
...
setup: function () {
return CleanScreenshots();
},
...
};
});
According to this issue, starting with the Intern 3.0 you can do a custom reporter that take an Screenshots when test fail. So you can centralize it in a simple way, just referencing the custom reporter in your config.js. In my case, what can I just add a reporter array in the config.js with the path to my custom array:
reporters: [
{ id: 'tests/support/ScreenShot' }
],
than I made an custom reporter overriding testFail:
'use strict';
define([
'intern/dojo/node!fs',
], function(fs) {
function ScreenShot(config) {
config = config || {};
}
ScreenShot.prototype.testFail = function(test) {
test.remote.takeScreenshot().then(function(buffer) {
try {
fs.writeFileSync('./screenshots/' + test.parent.name.replace(/ /g, '') + '-' +
test.name.replace(/ /g, '') + '.png', buffer);
} catch (err) {
console.log('Failed to take a screenshot: ' + err);
}
});
};
return ScreenShot;
});
Pay attention to the relative paths both to reference the custom reporter and the place for screenshots. They all seems to be taken considering where you run intern-runner, not the place the source files are located.
For more info about custom reporters go to this page.