Timing assertion in testcafe - node.js

I have several testcases in a suite for testcafe. And when I run this in QA environment say I get a report like this
TC1 -- 5 secs
TC2- 4 secs
TC3- 10 secs
So each test case is taking different time to complete( best case scenario) Now in production also I want the test case to run in the same amount of time say TC1 in Production should not take more than 5 secs, how do I code that in testcafe?
Is there an assertion which will allow me to monitor the test case completion within a particular time? I want full testcase completion, not selector or assertion completion.

I suggest you try the TestCafe Programming Interface. In this case you can separate all your tests into "lists" and add your own "run-done" logic. See the following "one-run" example:
const createTestCafe = require('testcafe');
let testcafe = null;
createTestCafe('localhost', 1337, 1338)
.then(tc => {
testcafe = tc;
const runner = testcafe.createRunner();
const testList = ['tests/fixture1.js', 'tests/func/fixture3.js'];
const runPromise = runner
.src(testList)
.browsers(['chrome'])
.run();
const deadlinePromise = new Promise(resolve => setTimeout(resolve, 10000))
.then(throw new Error('fail'));
return Promise.race(runPromise, deadlinePromise).
})
.then(() => {
// success
})
.catch(() => {
// fail
});

Related

How to get status of executed tests using afterEach in Jest?

When running tests in jest, I want to get the status of the 'it' whether they pass or fail, so far I was able to get the name of the running 'it' but I don't see a way to get its status.
// Expected value
let expectValue = 1;
// Test run
describe('Sample test', () => {
let statusTest = false;
afterEach(() => {
statusTest = false;
})
// This test passes
it('Value must be 1', () => {
expect(expectValue).toBe(1)
statusTest = true;
})
// This test fails
it('El valor debe ser 2', () => {
expect(expectValue).toBe(2)
statusTest = true;
})
beforeEach(() => {
/**
* At the moment it is being handled with a global variable to
* obtain the status of the test.
*
* Only the name and path are available in getState()
*/
if (!statusTest)
console.log(expect.getState().currentTestName)
})
})
At the time of passing the test I want to get its status either "passed" or "failed".
I tried to get the data inside expect or expect.getState(), but I don't see the value I require.
Similar questions:
Get the current test epec name in jest
Jest html reporters

Run background task inside Jest

I was writing an automatic test framework with Jest. For this, I need to run some background tasks under certain intervals. In summary, this is like polling. Let's give an example with a pseudo code.
test('Some potato poteto test', () => {
jest.setTimeout(12000000); // 20 min
const intervalPeriod = 5 * 60 * 1000; // 5 min
let retry = 0;
const intervalId = setInterval(() => {
console.log("I am doing my 5 minutes check");
// my custom logic goes here
retry++;
if(retry === MAX_RETRY) { // this will be always hit
clearInterval(intervalId)
}
}, intervalPeriod);
});
So, in every 5 mins, I'll make a network call, do some of my custom logic. Now, the problem is while running this, the test finished but jest can't terminate.
By 'test-finished' I meant, test suite ran, but the code inside the setInterval does not get executed right then.
My question is, does this happen because setInterval does not run immediately in the event-loop and is jest not configured to run a setInterval?
Please note that I don't want to mock the setInterval and also I don't want to use any fake timer.
Therefore, is it possible to achieve this with Jest?
I also tried the same thing with the cron library for node but it is the same issue. Jest does not execute the code and finish executing then halted because those setInterval/cron are still running in the background.
Is there a way to run those tasks inside Jest? I mean is there a way to run those jobs and when they are done then terminate Jest?
That's intended, Jest execute all the test instructions and quit the test.
For jest to wait for a computation, you need to use Promises and its async mode.
Doc for Jest async mode: https://jestjs.io/docs/asynchronous
Learn more about Promises here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
And async/await syntax here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
This is a naive way for doing it:
const MAX_RETRY = 3;
const INTERVAL_PERIOD = 5 * 60 * 1000; // 5 min
const SAFETY_TIME = INTERVAL_PERIOD;
const TEST_TIMEOUT = (1 + MAX_RETRY) * INTERVAL_PERIOD + SAFETY_TIME;
test('Some potato poteto test', async () => {
let retry = 0;
let success = false;
function yourLogicFunction(): boolean {
const resultOfYourLogicFunction = retry === MAX_RETRY; // because retry is used in this condition I store the result before changing it
console.log(`this is my attempt number ${retry}`);
retry++;
return resultOfYourLogicFunction;
}
function mockRequest(resolve: (value: unknown) => void): void {
setTimeout(() => resolve(yourLogicFunction()), INTERVAL_PERIOD);
}
while (!success && retry <= MAX_RETRY) {
success = await new Promise(mockRequest) as boolean;
}
}, TEST_TIMEOUT);
The third parameter of test is its timeout.

How to stop jest describe without throw all the test

I want to stop a whole describe of JEST without throwing an error or stoping the other describes.
Im writing e2e test for my app with JEST and PUPPETEER, I write the test in a way every DESCRIBE its a flow of the path and every IT its a step, inside a IT I want to stop the flow if the pages dont match some conditions.
describe('Book a Room', ()=> {
it ('enter on main page' async() => await mainPage.navigateToMainPage())
it('go to book room page', async() => await bookRoomPage.navigateToBookRoomPage())
// The function its inside the "bookRoomPage"
it('check if the user can book room', () => {
if (!page.userCanOpenARoom()) {
// DONT EXECUTE THE NEXT IT BUT CONTINUE WITH THE OTHER DESCRIBE
}
})
it('go to book preview...', async() => bookRoomPreviewPage.navigateToBookRoomPreviewPage());
// REMAINING FLOW
})
I already try with process.exit(0) but exit the whole process
You can try out what this blog says here its for sharing specs in your test suites which is pretty handy. But for your case specifically you could extract your page cases in separate suites and then dynamically include the test case on runtime if a condition is met.
Something like:
Include Spec function shared_specs/index.js
const includeSpec = (sharedExampleName, args) => {
require(`./${sharedExampleName}`)(args);
};
exports.includeSpec = includeSpec;
Test A shared_specs/test_a.js
describe('some_page', () => {
it...
})
Test B shared_specs/test_b.js
describe('some_other_page', () => {
it...
})
and then in your test case
// Something like this would be your path I guess
import {includeSpec} from '../shared_specs/includeSpec.js'
describe('Book a Room', async ()=> {
if (page.userCanOpenARoom()) {
includeSpec('test_a', page);
} else {
includeSpec('test_b', page); // Or dont do anything
}
});
Just make sure that you check the paths since
require(`./${sharedExampleName}`)(args);
will load it dynamically at runtime, and use includeSpec in your describe blocks not it blocks. You should be able to split up your test suites pretty nicely with this.

node.js async/await or generic-pool causes infinite loop?

I was trying to create an automation script for work, it is supposed to use multiple puppeteer instances to process input strings simultaneously.
the task queue and number of puppeteer instances are controlled by the package generic-pool,
strangely, when i run the script on ubuntu or debian, it seems that it fells into an infinite loop. tries to run infinite number of puppeteer instances. while when run on windows, the output was normal.
const puppeteer = require('puppeteer');
const genericPool = require('generic-pool');
const faker = require('faker');
let options = require('./options');
let i = 0;
let proxies = [...options.proxy];
const pool = genericPool.createPool({
create: async () => {
i++;
console.log(`create instance ${i}`);
if (!proxies.length) {
proxies = [...options.proxy];
}
let {control = null, proxy} = proxies.pop();
let instance = await puppeteer.launch({
headless: true,
args: [
`--proxy-server=${proxy}`,
]
});
instance._own = {
proxy,
tor: control,
numInstance: i,
};
return instance;
},
destroy: async instance => {
console.log('destroy instance', instance._own.numInstance);
await instance.close()
},
}, {
max: 3,
min: 1,
});
async function run(emails = []) {
console.log('Processing', emails.length);
const promises = emails.map(email => {
console.log('Processing', email)
pool.acquire()
.then(browser => {
console.log(`${email} handled`)
pool.destroy(browser);})
})
await Promise.all(promises)
await pool.drain();
await pool.clear();
}
let emails = [a,b,c,d,e,];
run(emails)
Output
create instance 1
Processing 10
Processing Stacey_Haley52
Processing Polly.Block
create instance 2
Processing Shanny_Hudson59
Processing Vivianne36
Processing Jayda_Ullrich
Processing Cheyenne_Quitzon
Processing Katheryn20
Processing Jamarcus74
Processing Lenore.Osinski
Processing Hobart75
create instance 3
create instance 4
create instance 5
create instance 6
create instance 7
create instance 8
create instance 9
is it because of my async functions? How can I fix it?
Appreciate your help!
Edit 1. modified according to #James suggested
The main problem you are trying to solve,
It is supposed to use multiple puppeteer instances to process input strings simultaneously.
Promise Queue
You can use a rather simple solution that involves a simple promise queue. We can use p-queue package to limit the concurrency as we wish. I used this on multiple scraping projects to always test things out.
Here is how you can use it.
// emails to handle
let emails = [a, b, c, d, e, ];
// create a promise queue
const PQueue = require('p-queue');
// create queue with concurrency, ie: how many instances we want to run at once
const queue = new PQueue({
concurrency: 1
});
// single task processor
const createInstance = async (email) => {
let instance = await puppeteer.launch({
headless: true,
args: [
`--proxy-server=${proxy}`,
]
});
instance._own = {
proxy,
tor: control,
numInstance: i,
};
console.log('email:', email)
return instance;
}
// add tasks to queue
for (let email of emails) {
queue.add(async () => createInstance(email))
}
Generic Pool Infinite Loop Problem
I removed all kind of puppeteer related code from your sample code and saw how it was still producing the infinite output to console.
create instance 70326
create instance 70327
create instance 70328
create instance 70329
create instance 70330
create instance 70331
...
Now, if you test few times, you will see it will throw the loop only if you something on your code is crashing. The culprit is this pool.acquire() promise, which is just re queuing on error.
To find what is causing the crash, use the following events,
pool.on("factoryCreateError", function(err) {
console.log('factoryCreateError',err);
});
pool.on("factoryDestroyError", function(err) {
console.log('factoryDestroyError',err);
});
There are some issues related to this:
acquire() never resolves/rejects if factory always rejects, here.
About the acquire function in pool.js, here.
.acquire() doesn't reject when resource creation fails, here.
Good luck!
You want to return from your map rather than await, also don't await inside the destroy call, return the result and you can chain these e.g.
const promises = emails.map(e => pool.acquire().then(pool.destroy));
Or alternatively, you could just get rid of destroy completely e.g.
pool.acquire().then(b => b.close())

How can I build my test suite asynchronously?

I'm trying to create mocha tests for my controllers using a config that has to be loaded async. Below is my code. However, when the mocha test is run, it doesn't run any tests, displaying 0 passing. The console.logs are never even called. I tried doing before(next => config.build().then(next)) inside of the describe, but even though the tests run, before is never called. Is there a way to have the config be loaded one time before any tests are run?
'use strict';
const common = require('./common');
const config = require('../config');
config
.build()
.then(test);
function test() {
console.log(1);
describe('Unit Testing', () => {
console.log(2);
require('./auth');
});
}
You should run Mocha with the --delay option, and then use run() once you are done building your test suite. Here is an example derived from the code you show in the question:
'use strict';
function test() {
console.log(1);
describe('Unit Testing', () => {
console.log(2);
it("test", () => {
console.log(3);
});
});
// You must use --delay for `run()` to be available to you.
run();
}
setTimeout(test, 1000);
I'm using setTimeout to simulate an asynchronous operation. Using --delay and run() allows you to build a suite that is the result of an asynchronous computation. Note, however, that the suite must be built in one shot. (You cannot have an asynchronous process inside describe that will make calls to it. This won't work.)
One thing you should definitely not do is what rob3c suggests: calling describe or it (or both) from inside a hook. This is a mistake that every now and then people make so it is worth addressing in details. The problem is that it is just not supported by Mocha, and therefore there are no established semantics associated with calling describe or it from inside a hook. Oh, it is possible to write simple examples that work as one might expect but:
When the suite becomes more complex, the suite's behavior no longer corresponds to anything sensible.
Since there are no semantics associated with this approach, newer Mocha releases may handle the erroneous usage differently and break your suite.
Consider this simple example:
const assert = require("assert");
const p = Promise.resolve(["foo", "bar", "baz"]);
describe("top", () => {
let flag;
before(() => {
flag = true;
return p.then((names) => {
describe("embedded", () => {
for (const name of names) {
it(name, () => {
assert(flag);
});
}
});
});
});
after(() => {
flag = false;
});
it("regular test", () => {
assert(flag);
});
});
When we run it, we get:
top
✓ regular test
embedded
1) foo
2) bar
3) baz
1 passing (32ms)
3 failing
// [stack traces omitted for brevity]
What's going on here? Shouldn't all the tests pass? We set flag to true in the before hook for the top describe. All tests we create in it should see flag as true, no? The clue is in the output above: when we create tests inside a hook, Mocha will put the tests somewhere but it may not be in a location that reflects the structure of the describe blocks in the code. What happens in this case is that Mocha just appends the tests created in the hook the the very end of the suite, outside the top describe, so the after hook runs before the dynamically created tests, and we get a counter-intuitive result.
Using --delay and run(), we can write a suite that behaves in a way concordant with intuition:
const assert = require("assert");
const p = Promise.resolve(["foo", "bar", "baz"]).then((names) => {
describe("top", () => {
let flag;
before(() => {
flag = true;
});
after(() => {
flag = false;
});
describe("embedded", () => {
for (const name of names) {
it(name, () => {
assert(flag);
});
}
});
it("regular test", () => {
assert(flag);
});
});
run();
});
Output:
top
✓ regular test
embedded
✓ foo
✓ bar
✓ baz
4 passing (19ms)
In modern environments, you can use top-level await to fetch your data up front. This is a documented approach for mocha: https://mochajs.org/#dynamically-generating-tests
Slightly adapting the example from the mocha docs to show the general idea:
function fetchData() {
return new Promise((resolve) => setTimeout(resolve, 5000, [1, 2, 3]));
}
// top-level await: Node >= v14.8.0 with ESM test file
const data = await fetchData();
describe("dynamic tests", function () {
data.forEach((value) => {
it(`can use async data: ${value}`, function () {
// do something with data here
});
});
});
This is nice as it is on a per-file basis, and doesn't involve you taking on management responsibility of the test runner as you do with --delay.
The problem with using the --delay command line flag and run() callback that #Louis mentioned in his accepted answer, is that run() is a single global hook that delays the root test suite. Therefore, you have to build them all at once (as he mentioned), which can make organizing tests a hassle (to say the least).
However, I prefer to avoid magic flags whenever possible, and I certainly don't want to have to manage my entire test suite in a single global run() callback. Fortunately, there's a way to dynamically create the tests on a per-file basis, and it doesn't require any special flags, either :-)
To dynamically create It() tests in any test source file using data obtained asynchronously, you can (ab)use the before() hook with a placeholder It() test to ensure mocha waits until before() is run. Here's the example from my answer to a related question, for convenience:
before(function () {
console.log('Let the abuse begin...');
return promiseFn().
then(function (testSuite) {
describe('here are some dynamic It() tests', function () {
testSuite.specs.forEach(function (spec) {
it(spec.description, function () {
var actualResult = runMyTest(spec);
assert.equal(actualResult, spec.expectedResult);
});
});
});
});
});
it('This is a required placeholder to allow before() to work', function () {
console.log('Mocha should not require this hack IMHO');
});

Resources