Intermittent Selenium ECONNREFUSED errors on CircleCI - selenium

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');
});
});
});

Related

Cypress E2E: Before hook not working on retries

Our tests have a reset state cypress command on the before hook that clears local storage and cookies so that before each run this function makes sure no session was stored.
But we are somehow getting test fail upon retry because it looks like its ignoring the reset state function and doesn't start the test on the signup page.
Is there a way to force the before hook when a retry happens?
The before-hook is excluded from retry as described here: https://docs.cypress.io/guides/guides/test-retries#How-It-Works
Also notable, if something went wrong within before-hook there will be no retry.
There is also an issue that requests this feature: https://github.com/cypress-io/cypress/issues/19458
As a workaround you can use a beforeEach:
let isError = false;
beforeEach(() => {
cy.once('fail', (err) => {
isError = true;
throw err;
});
if (isError) {
cy.resetAll(); // or whatever you have to do before retry
isError = false;
}
});
Explanation: If there is an assertion error then the 'fail'-event is triggered an is catched. In the catch-block the 'isError'-flag is set. Then on the first retry we make our reset-work and also resets the 'isError'-flag.
If you have some initial work that you do in before-hook normally, you have to do a little modification:
let isError = false;
let firstTime = true;
beforeEach(() => {
cy.once('fail', (err) => {
isError = true;
throw err;
});
if (isError) {
cy.resetAll();
}
if (firstTime || isError) {
firstTime = false;
isError = false;
// setupMyBackend()
}
});
Slightly simpler, make the before() callback callable elsewhere (declare and name it).
Use test:after:run event to call it depending on test results.
const beforeCallback = () => {...}
before(beforeCallback)
Cypress.on('test:after:run', (result) => {
if (result.currentRetry < result.retries && result.state === 'failed') {
beforeCallback()
}
})
it('fails', {retries:3}, () => expect(false).to.eq(true)) // failing test to check it out

Nuxt end to end testing with jest

Hello im searching for a way to use component testing as well as end to end testing with nuxt.
we want to be able to test components (which already works) and also check if pages parse their url parameters correctly or sitemaps are correctly created and other page level features and router functions
i tried ava but we already implemented the component testing with jest which works fine now and in the nuxt docs the server rendering for testing was described with ava and i adapted that to jest now but i get timeout errors so i increased the time out to 40 seconds but still get a timeout.
did anybody get the testing to work with the nuxt builder like in the example (https://nuxtjs.org/guide/development-tools)?
this is my end to end test example file
// test.spec.js:
const { resolve } = require('path')
const { Nuxt, Builder } = require('nuxt')
// We keep the nuxt and server instance
// So we can close them at the end of the test
let nuxt = null
// Init Nuxt.js and create a server listening on localhost:4000
beforeAll(async (done) => {
jest.setTimeout(40000)
const config = {
dev: false,
rootDir: resolve(__dirname, '../..'),
telemetry: false,
}
nuxt = new Nuxt(config)
try {
await new Builder(nuxt).build()
nuxt.server.listen(4000, 'localhost')
} catch (e) {
console.log(e)
}
done()
}, 30000)
describe('testing nuxt', () => {
// Example of testing only generated html
test('Route / exits and render HTML', async (t, done) => {
const context = {}
const { html } = await nuxt.server.renderRoute('/', context)
t.true(html.includes('<h1 class="red">Hello world!</h1>'))
jest.setTimeout(30000)
done()
})
})
// Close server and ask nuxt to stop listening to file changes
afterAll((t) => {
nuxt.close()
})
my current error is :
● Test suite failed to run
Timeout - Async callback was not invoked within the 40000ms timeout specified by jest.setTimeout.Error: Timeout - Async callback was not invoked within the 40000ms timeout specified by jest.setTimeout.
any info is very appreciated as i could not resolve this issue myself

Conditional Login in the beforeAll - Protractor

Looking for a repeatable pattern for putting conditional logic in the beforeAll statement for a Protractor spec. This is what I've got so far:
beforeAll(function () {
login.devTestLogin();
//Check to see if depedent service is up. If its down, log
customerManagement.resultCount.isPresent().then(function (result) {
if (result) {
console.log("Dependent Service - up");
customerManagement.clickManageCustomer(0);
} else {
console.log('Dependent Service - down');
process.exit(0);
}
});
});
This is the error message that I keep getting:
[13:25:10] E/launcher - BUG: launcher exited with 1 tasks remaining
>>
Warning: Tests failed, protractor exited with code: 100 Use --force to continue.
Aborted due to warnings.
Process finished with exit code 100
Does anyone have a good repeatable pattern for executing test/don't test logic?
You don't use beforeAll(). You wrap your it() calls in if statements.
describe('App', () => {
customerManagement.resultCount.isPresent().then((present: boolean) => {
if(present) {
it('should do some stuff', () => {
browser.get('/whatever')
// add some more thenes here
.then(result => expect(result).toContain('Some stuff'));
});
}
});
});

How to test an Electron app with selenium webdriver

I have read the documentation and I have followed the tutorial step by step and I only have managed to run the app.
Documentation: http://electron.atom.io/docs/tutorial/using-selenium-and-webdriver/
The connection with chromedriver I cannot make it work, when I launch the test and try click a simple button I get this:
Error: ChromeDriver did not start within 5000ms at Error (native)
at node_modules/spectron/lib/chrome-driver.js:58:25 at
Request._callback (node_modules/spectron/lib/chrome-driver.js:116:45)
at Request.self.callback
(node_modules/spectron/node_modules/request/request.js:200:22) at
Request.
(node_modules/spectron/node_modules/request/request.js:1067:10) at
IncomingMessage.
(node_modules/spectron/node_modules/request/request.js:988:12) at
endReadableNT (_stream_readable.js:913:12) at _combinedTickCallback
(internal/process/next_tick.js:74:11) at process._tickCallback
(internal/process/next_tick.js:98:9)
My code:
"use strict";
require("co-mocha");
var Application = require('spectron').Application;
var assert = require('assert');
const webdriver = require('selenium-webdriver');
const driver = new webdriver.Builder()
.usingServer('http://127.0.0.1:9515')
.withCapabilities({
chromeOptions: {
binary: "./appPath/app"
}
})
.forBrowser('electron')
.build();
describe('Application launch', function () {
this.timeout(100000);
var app;
beforeEach(function () {
app = new Application({
path: "./appPath/app"
});
return app.start();
});
afterEach(function () {
if (app && app.isRunning()) {
return app.stop();
}
});
it('click a button', function* () {
yield driver.sleep(5000);
yield driver.findElement(webdriver.By.css(".classSelector")).click();
});
});
Thanks and sorry for my English.
I recommend you to use Spectron. which is a less painful way of testing your electron app. in my opinion perfect combination is using it with Ava test framework, which allows the concurrently test.
async & await is also another big win. which allows you to have so clean test cases.
and also if you have a test which needs to happen serial, you can use test.serial
test.serial('login as new user', async t => {
let app = t.context.app
app = await loginNewUser(app)
await util.screenshotCreateOrCompare(app, t, 'new-user-mission-view-empty')
})
test.serial('Can Navigate to Preference Page', async t => {
let app = t.context.app
await app.client.click('[data-test="preference-button"]')
await util.screenshotCreateOrCompare(app, t, 'new-user-preference-page-empty')
})
and just for reference; my helper test cases.
test.before(async t => {
app = util.createApp()
app = await util.waitForLoad(app, t)
})
test.beforeEach(async t => {
t.context.app = app
})
test.afterEach(async t => {
console.log('test complete')
})
// CleanUp
test.after.always(async t => {
// This runs after each test and other test hooks, even if they
failed
await app.client.localStorage('DELETE', 'user')
console.log('delete all files')
const clean = await exec('rm -rf /tmp/DesktopTest')
await clean.stdout.on('data', data => {
console.log(util.format('clean', data))
})
await app.client.close()
await app.stop()
})
util function,
// Returns a promise that resolves to a Spectron Application once the app has loaded.
// Takes a Ava test. Makes some basic assertions to verify that the app loaded correctly.
function createApp (t) {
return new Application({
path: path.join(__dirname, '..', 'node_modules', '.bin',
'electron' + (process.platform === 'win32' ? '.cmd' : '')),
// args: ['-r', path.join(__dirname, 'mocks.js'), path.join(__dirname, '..')],
env: {NODE_ENV: 'test'},
waitTimeout: 10e3
})
}
First off, Spectron (which is a wrapper for WebdriverIO) and WebdriverJS (which is part of Selenium-Webdriver) are two different frameworks, you only need to use one of them for your tests.
If you are using WebdriverJS, then you need to run ./node_modules/.bin/chromedriver in this step: http://electron.atom.io/docs/tutorial/using-selenium-and-webdriver/#start-chromedriver
I could get ChromeDriver working by adding a proxy exception in my terminal.
export {no_proxy,NO_PROXY}="127.0.0.1"

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.