How to ensure webdriverio is really getting page's elements - node.js

I've been facing an issue testing an Vue/Nuxt app with WebdriverIO. Mainly, it my tests are always passing. The following code will have an success execution:
it('should open correct app', () => {
Page.open()
browser.pause(1000)
expect(browser).toHaveTitle('Checkout')
browser.pause(1000)
})
As does this one:
it('should open correct app', () => {
Page.open()
browser.pause(1000)
expect(browser).toHaveTitle('Something Completely Different')
browser.pause(1000)
})
My first guess is that Page.open() is loading asynchronous, and the expect condition were being verified before the element existed. But if I try make the function asynchronous, to use await:
it('should open correct app', async () => {
await Page.open()
await browser.pause(1000)
await expect(browser).toHaveTitle('Checkout')
await browser.pause(1000)
})
it gives me an error... of connection.
[Chrome 100.0.4896.60 linux #0-0] 1) Yourapp should open correct app
[Chrome 100.0.4896.60 linux #0-0] net::ERR_CONNECTION_REFUSED at http://localhost:3000/
[Chrome 100.0.4896.60 linux #0-0] net::ERR_CONNECTION_REFUSED at http://localhost:3000/
[Chrome 100.0.4896.60 linux #0-0] Error: net::ERR_CONNECTION_REFUSED at http://localhost:3000/
It happens because my tests are configured with an initialization of Nuxt server on the webdriver config hooks, that is basically inspired in that example:
wdio.conf.ts:
...
async onPrepare () {
if (process.env.NUXT_ENV_CI !== 'true') {
return
}
const rootDir = path.resolve(__dirname, '.')
const nuxtConfig = {
head: {
title: 'Yourapp'
},
dev: false,
rootDir,
modules: ['bootstrap-vue/nuxt']
}
this.nuxtInstance = new nuxt.Nuxt(nuxtConfig)
await new nuxt.Builder(this.nuxtInstance).build()
// its based on Express ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
await this.nuxtInstance.server.listen(3000, 'localhost')
},
onComplete () {
if (process.env.NUXT_ENV_CI !== 'true') {
return
}
this.nuxtInstance.close()
}
...
and, despite it has been issued and corrected here https://github.com/webdriverio/webdriverio/issues/4042 looks like the asynchronous function is running after the onComplete hook is triggered. That's an issue I need the most to fix, since I'm using Browserstack Automate to test in multiple browsers in CI/CD and it only works when those hooks are correctly executed .
Anyway, I handled that locally running npm run dev on another terminal, what makes the tests run again, but they keep always passing successfully even when they shouldn't .
I'm using
Node 16.14.2
NPM 8.5.0
WebdriverIO 7.16.14
Nuxt 2.15.8
Ubuntu 20.04.4
Example code:
https://github.com/kaabsimas/issue-example

Related

Redis Connections closing in Heroku CI using heroku-redis:in-dyno

Tests run (and Redis works) perfectly fine in a local environment
Redis works normally even when deployed to production
However Redis connections immediately close when run in Heroku's CI test environment
Technologies used:
Node.js, Express, Mocha, Chai, Supertest
Heroku, Heroku CI, (Heroku) Redis
I'm having the tests run through the Pipeline feature. And then I get the following error output:
-----> Running test command `make test-ci`...
NODE_ENV=test ./node_modules/.bin/db-migrate up --log-level=error --env=test --config=config/database.json --migrations-dir ./db/migrations
NODE_ENV=test ./node_modules/.bin/mocha --exit --ui bdd --reporter spec --timeout 15000 --recursive ./tests/setup.js ./tests/
Redis successfully connected
POST: API /account/phone
Redis set error The client is closed
1) accepts a phone number and generates a PIN
0 passing (15s)
1 failing
1) POST: API /account/phone
accepts a phone number and generates a PIN:
Error: Timeout of 15000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves. (/app/tests/api/account.js)
make: *** [Makefile:66: test-ci] Error 1
-----> test command `make test-ci` failed with exit status 2
My app.json file looks like this
{
"environments": {
"test": {
"scripts": {
"test": "make test-ci"
},
"addons": [
"heroku-redis:in-dyno",
"heroku-postgresql:in-dyno"
]
}
}
}
My Redis connection file looks like this:
let redis = require('redis')
let client = redis.createClient({
url: process.env.REDIS_URL
})
client.on('error', err => console.log('Redis Client Error', err))
client.on('connect', () => {
console.log('Redis successfully connected')
})
The route that's being tested looks like this:
redis.set('phone:keys:' + phone, code, { EX: 60 * 5 }).then(val => {
console.log('did it get set in redis?')
twilio.send(phone, message).then(response => {
res.status(201).json({
ok: true,
data: { body: response.body },
})
})
})
.catch((err) => {
console.log('Redis set error', err.message)
})
In local env (and production), it all works normally. Even when running tests locally. But only in Heroku CI it fails, and I see the "Redis set error The client is closed "

Run and install X server to make puppeteer works

on my linux server I running my nodejs project which should crawl single page app by puppeteer npm module.
Here is an example of the code I use:
const puppeteer = require('puppeteer');
(async () => {
try {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://bvopen.abrickis.me/#/standings');
await page.waitForSelector('.category', { timeout: 1000 });
const body = await page.evaluate(() => {
return document.querySelector('body').innerHTML;
});
console.log(body);
await browser.close();
} catch (error) {
console.log(error);
}
})();
But I've got the next error:
0|www | Error: Failed to launch the browser process!
0|www | [5642:5642:0511/154701.856738:ERROR:browser_main_loop.cc(1485)] Unable to open X display.
0|www | [0511/154701.863486:ERROR:nacl_helper_linux.cc(308)] NaCl helper process running without a sandbox!
0|www | Most likely you need to configure your SUID sandbox correctly
0|www | TROUBLESHOOTING: https://github.com/puppeteer/puppeteer/blob/master/docs/troubleshooting.md
I searched a lot how to install X server and tried a lot of things like sudo apt-get install xorg openbox but it doesn't helps.
Looks like puppeteer wants to start the browser in a non-headless mode but as you don't have xorg installed it failed. But I would say that's not what you want when it's running on a server anyways. So there is no need to install xorg or anything.
Maybe try to launch the puppeteer browser with following options:
await puppeteer.launch({
headless: true,
args: [
"--disable-gpu",
"--disable-dev-shm-usage",
"--no-sandbox",
"--disable-setuid-sandbox"
]
});

JEST with Express does not finish

I started to write tests with Jest of (nano)express application. The test starts the server at beforeAll() and closes it at afterAll(). I can see that the code is executed, but the JEST process does not end.
test.js
test('end to end test', async () => {
const polls = await axios.get(`http://localhost:3000/bff/polls/last`);
console.log(polls.data);
expect(polls.data).toBeDefined();
});
beforeAll(() => {
app.listen(3000, '0.0.0.0')
.then(r => logger.info("Server started"));
});
afterAll(() => {
if (app.close())
logger.info("Server stopped");
});
Output from npm run test
Test Suites: 1 failed, 1 total
Tests: 1 failed, 1 total
Snapshots: 0 total
Time: 5.625s
Ran all test suites.
Jest did not exit one second after the test run has completed.
This usually means that there are asynchronous operations that weren't stopped in your tests. Consider running Jest with `--detectOpenHandles` to troubleshoot this issue.
When I run with jest --config jest.config.js --detectOpenHandles the test does not finish as well but there is no error and I need to kill it anyway.
The complete source code is there: https://github.com/literakl/mezinamiridici/blob/master/infrastructure/test/api.int.test.js
I have tested separatelly outside of the tests that nanoexpress will terminate the process with app.close() call. So it is JEST related.
Update: the same behaviour with promises
test('end to end test', () => {
const polls = axios.get(`http://localhost:3000/bff/polls/last`);
return expect(polls).resolves.toBeDefined();
});
Update:
Here you can find minimum reproducible repository: https://github.com/literakl/nano-options.git
I have switched from Axios to GotJS and the trouble is still there. When I run the test with npm run test from command line now, it fails with:
Timeout - Async callback was not invoked within the 20000ms timeout specified by jest.setTimeout.Timeout - Async callback was not invoked within the 20000ms timeout specified by jest.setTimeout.Error
When I start the test from WebStorm there is no error but the process keeps running.
UPDATE
My initial thought was that this is a winston related issue but it appears that jest testEnvironment has to be set to node in order for Axios to run propertly using the axios/lib/adapters/http adapter. You can check a related issue here "detect jest and use http adapter instead of XMLhttpRequest".
Set testEnvironment: 'node' inside jest.config.js.
Update create user test to run the done callback function at the end:
describe("user accounts", () => {
test('create user', async (done) => {
// let response = await axios.get(`${API}/users/1234`);
let response = await axios.get(`${API}/users/1234`, getAuthHeader()); // TODO error with Authorization header
expect(response.data.success).toBeTruthy();
expect(response.data.data).toBeDefined();
let profile = response.data.data;
expect(profile.bio.nickname).toMatch("leos");
expect(profile.auth.email).toMatch("leos#email.bud");
// Call done here to let Jest know we re done with the async test call
done();
});
});
The root cause was an open handle of mongodb client. How did I find it?
1) install leakes-handle library
npm install leaked-handles --save-dev
2) import it to your test
require("leaked-handles");
3) the output
tcp handle leaked at one of:
at makeConnection (infrastructure\node_modules\mongodb\lib\core\connection\connect.js:274:20)
tcp stream {
fd: -1,
readable: true,
writable: true,
address: { address: '127.0.0.1', family: 'IPv4', port: 54963 },
serverAddr: null
}
If you cannot find the root cause, you can kill the JEST explicitelly with
jest --config jest.config.js --runInBand --forceExit
Here is another reason for me. I was using Puppeteer and because my target element was hidden the screenshot method threw error:
const browser = await puppeteer.launch({
headless: true,
executablePath: chromiumPath
});
const page = await browser.newPage();
await page.goto(`file://${__dirname}\\my.html`);
const element = await page.$("#my-element");
// This may throw errors
await element.screenshot({path: snapshotFileName});
await browser.close();
So, I made sure that the browser.close() was called no matter what:
try {
await element.screenshot({path: snapshotFileName});
} finally {
await browser.close();
}
Following works for my integration testing with Express, Nodejs, and Jest. Nothing special in package.json either: "test": "jest --verbose". Cheers
afterAll( async () => {
await mongoose.connection.close();
jest.setTimeout(3000);
});
Tests: 6 passed, 6 total
Snapshots: 0 total
Time: 4.818 s, estimated 5 s
Ran all test suites matching /users.test.js/i.

Protractor generates an error when disabling flow control to test an Angular app

I've been struggling with this error for a while and I'm running out of mana. I'm currently trying to test an Angular app with protractor and async/await. According to the doc, I have to disable the control flow by adding the following to my config file:
SELENIUM_PROMISE_MANAGER: false but doing so produces the following error:
UnhandledPromiseRejectionWarning: Error: Error while waiting for Protractor to sync with the page: "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 https://github.com/angular/protractor/issues/2643 for details" I visited the url (https://github.com/angular/protractor/issues/2643) but it didn't turn out very helpful.
At this point I'm not sure if I'm doing something wrong or if it's a bug with protractor itself. For this reason I also opened an issue on GitHub.
Here is my test:
import {
browser,
ExpectedConditions,
$
} from 'protractor';
describe('When user click \"Test\" button', async () => {
beforeAll(async () => {
expect(browser.getCurrentUrl()).toContain('myawesomewebsite');
});
it ("should click the button", async () => {
var button = $(".button");
button.click();
});
});
And here is my full configuration:
exports.config = {
capabilities: {
'browserName': 'chrome'
},
seleniumAddress: 'http://localhost:4444/wd/hub',
framework: 'jasmine',
specs: ['test.spec.ts'],
SELENIUM_PROMISE_MANAGER: false,
jasmineNodeOpts: {
defaultTimeoutInterval: 30000
},
beforeLaunch: function () {
require('ts-node/register')
}
};
You missed await before each protractor api invoking.
describe('When user click \"Test\" button', async () => {
beforeAll(async () => {
expect(await browser.getCurrentUrl()).toContain('myawesomewebsite');
});
it ("should click the button", async () => {
var button = $(".button");
await button.click();
});
});
So, thanks to #CrispusDH on GitHub, I figured out that I could use waitForAngularEnabled in the configuration file and not just in the spec file. Using it in the spec file was not working, but if used in the onPrepare hook of the configuration file, the error goes away.
A lot of resources online were saying to set it to false, but this wasn't working for me as Protractor couldn't find element without waiting for Angular, so I did set it to false in the configuration and file but called browser.waitForAngularEnabled(true); in my specs file (beforeAll hook). Now the error is gone, allowing me to use async/await.
Here is the proper configuration to use:
SELENIUM_PROMISE_MANAGER: false,
onPrepare: async () => {
await browser.waitForAngularEnabled(false);
}
And here is the code to call in spec file:
beforeAll(async () => {
browser.waitForAngularEnabled(true);
});

Cannot read property '_host' of null when running supertest in node-inspector

I'm fairly new to the Angular world and have been using the angular-fullstack generator + Yeoman to build out a project. I'm using Sublime (not Webstorm) and have been trying to figure out how to set up the project so that I can debug the mocha tests from the terminal, but am hitting a wall.
Here's the default things.spec.js that's generated with 'yo angular-fullstack' with a debugger statement added in.
var should = require('should');
var app = require('../../app');
var request = require('supertest');
describe('GET /api/things', function() {
it('should respond with JSON array', function(done) {
request(app)
.get('/api/things')
.expect(200)
.expect('Content-Type', /json/)
.end(function(err, res) {
debugger;
if (err) return done(err);
res.body.should.be.instanceof(Array);
done();
});
});
});
By combining advice from the grunt-mocha-test documentation and this SO answer, I've updated the test script in package.json as follows:
"scripts": {
"test": "node-debug <PATH TO GRUNT-CLI>\\grunt test"
}
When I run 'npm test', node-inspector successfully boots up a browser instance and attaches the debug session to my test process. However, when I press "continue", the debugger breakpoint is NOT hit and the test fails with the following stack. Anyone have any clues as to what's causing this?
TypeError: Cannot read property '_host' of null
at ClientRequest.http.ClientRequest.onSocket (eval at WRAPPED_BY_NODE_INSPECTOR (\node_modules\node-inspector\node_modules\v8-debug\v8-debug.js:95:15), <anonymous>:381:28)
at new ClientRequest (http.js:1432:10)
at Object.exports.request (http.js:1843:10)
at Test.Request.request (\node_modules\supertest\node_modules\superagent\lib\node\index.js:615:28)
at Test.Request.end (\node_modules\supertest\node_modules\superagent\lib\node\index.js:677:18)
at Test.end (\node_modules\supertest\lib\test.js:123:7)
at Context.<anonymous> (\server\api\thing\thing.spec.js:14:8)
at Test.Runnable.run (\node_modules\grunt-mocha-test\node_modules\mocha\lib\runnable.js:196:15)
at Runner.runTest (\node_modules\grunt-mocha-test\node_modules\mocha\lib\runner.js:374:10)
at Runner.runTests.next (\node_modules\grunt-mocha-test\node_modules\mocha\lib\runner.js:452:12)
This looks like a bug in Node Inspector.
https://github.com/node-inspector/node-inspector/issues/698

Resources