How to stop jest describe without throw all the test - jestjs

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.

Related

Selenium Test Case not getting executed with Jest

I am working on setting up an automation test suite for an application using selenium and jest and it turns out that the console never reaches the inner body of the minimalist test case written below.
describe('When a user Opens Launchpad', () => {
test('It should be able to Navigate to Tasks Application without errors', async () => {
driver.get('http://localhost:4004/fiori.html').then(function () {
const temp = driver.findElement(By.xpath("li[#id='__tile11']"));
temp.then(function (element){
element.getAttribute("innerHTML");
expect(element.getText()).toBe("VT Dashboard");
})
});
}, 200000);
});
I looked online and tried multiple fixes like putting the driver.get() method above all these functions, making the test cases synchronous and using getText() instead of getAttribute() but none of them worked.
I either get an element not found error (The element actually exists when I check it on the chromium browser) or the test case executes successfully without reaching the expect statement in debug mode.
Bottomline is that driver.findElement() returns a promise instead of an element and would be great if I could get an element instead of promise.
Any help or correction here would be greatly appreciated.
If the function is async you should return the promise chain from that function or just use await keyword. So try following:
test('It should be able to Navigate to Tasks Application without errors', async () => {
await driver.get('http://localhost:4004/fiori.html');
const temp = await driver.findElement(By.xpath("li[#id='__tile11']"));
element.getAttribute("innerHTML");
expect(element.getText()).toBe("VT Dashboard");
}, 200000);
or
test('It should be able to Navigate to Tasks Application without errors', async () => {
return driver.get('http://localhost:4004/fiori.html').then(function () {
const temp = driver.findElement(By.xpath("li[#id='__tile11']"));
temp.then(function (element){
element.getAttribute("innerHTML");
expect(element.getText()).toBe("VT Dashboard");
})
});
}, 200000);

Variable value doesn't change in request intercept

I am trying to intercept some requests in cypress following the click of a button, and I am trying to check if requests were or not made to a list of urls. I used a variable ( isRequestCalled ) as you can see, to update it if the requests were made, but the updates don't seem to be reach the test. When a request is intercepted, the value of isRequestCalled is updated in that scope but it doesn't seem to be visible in the scope of the test. Has anyone encoutered this type of issue, I appreciate all suggestions. Thanks!
describe('E2E test', () => {
let isRequestCalled
beforeEach(() => {
isRequestCalled=false
cy.origin(Cypress.env('ORIGIN_URL'), () => {
localStorage.clear();
})
cy.wait(1000);
cy.visit(Cypress.env('BASE_URL'));
cy.intercept('*', (req) => {
isRequestCalled=Cypress.env("REQUEST_URLS").some((url)=>{
return req.url.includes(url)
}) || isRequestCalled
// cy.wrap(isRequestCalled).as('isRequestCalled')
if (isRequestCalled) {
req.alias = 'apiRequests'
}
}
)
})
it('Consent test: Deny', () => {
cy.get(`[data-testid='${Cypress.env('BANNER_TEST_ID')}']`).should('be.visible').log("Banner visible");
cy.get(`[data-testid='${Cypress.env('BUTTON1_TESTID')}']`).should('be.visible').log("Button 1 visible");
cy.get(`[data-testid='${Cypress.env('BUTTON2_TESTID')}']`).should('be.visible').log("Button 2 visible");
cy.get(`[data-testid='${Cypress.env('BUTTON1_TESTID')}']`).click();
cy.reload();
// cy.get('#isRequestCalled').then(isRequestCalled => {
// console.log(isRequestCalled)
// expect(isRequestCalled).to.eq(false)
// })
cy.wrap(isRequestCalled).should('eq',false)
});
it('Consent test: Allow', () => {
cy.get(`[data-testid='${Cypress.env('BANNER_TEST_ID')}']`).should('be.visible').log("Banner visible");
cy.get(`[data-testid='${Cypress.env('BUTTON1_TESTID')}']`).should('be.visible').log("Button 1 visible");
cy.get(`[data-testid='${Cypress.env('BUTTON2_TESTID')}']`).should('be.visible').log("Button 2 visible");
cy.get(`[data-testid='${Cypress.env('BUTTON2_TESTID')}']`).click();
cy.wait('#apiRequests')
});
});
When using cy.intercept() you actually have no need for any variable counters if you want to ensure that the intercepted call has been made. I would recommend aliasing all of the requests you want to intercept and simply waiting (cy.wait()) for them after you perform the action that should trigger the call
describe('E2E test', () => {
beforeEach(() => {
cy.intercept('firstRequestUrl').as('firstCall');
cy.intercept('secondRequestUrl').as('secondCall')
cy.intercept('thirdRequestUrl').as('thirdCall')
cy.origin(Cypress.env('ORIGIN_URL'), () => {
localStorage.clear();
})
cy.wait(1000); // look into removing that as it looks kinda redundant
cy.visit(Cypress.env('BASE_URL'));
})
it('Does smth', () => {
cy.wait('#firstCall')
cy.get('#secondCallTrigger').click()
cy.wait('#secondCall')
})
it('Does smth else', () => {
cy.get('#thirdCallTrigger').click()
cy.wait('#thirdCall')
})
})
P.S.Keep in mind that js/ts variables in Cypress are very tricky to use between contexts, when you declare something in the describe, hooks, or a different it, you can't easily reach it from another it block.
People use env variables or hacks like this for that, although as you can see there is a native functionality for it.
Be sure to check the rest of intercept documentation and aliases documentation for more information.
It could be that the cy.reload() is resetting isRequestCalled.
If any commands such as visit or reload causes the browser to reset, you will notice the browser clearing and it's a sure sign that variables set up globally such as isRequestCalled are also cleared.
I can't really tell the intention of some of the commands, but perhaps you could test the value before the reload.
cy.wrap(isRequestCalled).should('eq',false)
cy.reload()
Also, what is the trigger for requests intercepted by Cypress.env("REQUEST_URLS")?
If it's cy.visit(Cypress.env('BASE_URL')) then the intercept needs to come before the visit.
If it's cy.get(```[data-testid='${Cypress.env("BUTTON1_TESTID")}']```).click() then the ordering is ok.

using values in describe blocks in a hierarchical manner

I'm using jest for e2e testing, and I want to be able to use the value/response in one of the describe block in the next, even to create dynamic titles, say e.g.
describe('top most', async ()=>{
let topVar = -1;
describe('first nested', async ()=> {
it ('get first response', async ()=> {
const res = await request('/call');
topVar = res.body.user_id; // returns 1001
}
describe(`get values for user with user_id ${topVar}`, async () => { // topVar prints -1
it('get the values for the user with id ${topVar}', async ()=> { // topVar prints -1
const res = await request(`/user?user_id=${topVar}`);
expect(res.body.user_id).toBe(1001);
}
}
The value seems to be replaced inside the it block but, not in the titles. Is there any way to do that, also are the it and describe blocks always run in a sequential manner from top to bottom? I did find out about --runInBand but, am not sure if they meant test in different files or tests in the same file.
Every describe should be independent, whe you need consecutive steps for a test then it's considered the same test and they should be part of the same describe, you can have multiple 'it' in the same describe, that way they should be consecutive.

Can Jest's beforeAll wait until its describe scope to execute?

I've been baffled for the last few days by how Jest deals with beforeAll inside describe blocks. I want each describe's beforeAll to only execute when it's time to run the tests inside that describe.
I've written this test file (test test file?):
async function wait1sec() {
return new Promise(resolve => setTimeout(resolve, 1000));
}
async function logWait(i, isDescribe = false) {
console.log(`before wait inside ${ isDescribe ? 'describe' : 'it' } #` + i);
await wait1sec();
console.log(`after wait inside ${ isDescribe ? 'describe' : 'it' } #` + i);
expect(true).toEqual(true);
}
function logSync(i, isBefore = false) {
console.log(`synchronous function ${ isBefore ? 'before' : 'it' } #${ i }`);
}
describe('async behavior', () => {
describe('block 1', () => {
const i = 1;
beforeAll(() => logSync(i, true));
it('sync test in block 1', () => logSync(i));
});
describe('block 2', () => {
const i = 2;
beforeAll(() => logSync(i, true));
it('sync test in block 1', () => logSync(i));
});
});
You can see the remnants of the fact that I was testing this in the context of async behavior but it turns out that it's confusing even without async.
What I hope get is:
synchronous function before #1
synchronous function it #1
synchronous function before #2
synchronous function it #2
What I actually get is
synchronous function before #1
synchronous function before #2
synchronous function it #1
synchronous function it #2
What's even worse is that if I skip the second block (with xdescribe), the second beforeAll still runs!
So I guess it's not that confusing, I guess it's just pretty clear how it works, it's just not the way I want it to work.
Is there any way, short of simply putting my "beforeAll" code inside my first test of the block, to get the result I want?
Note: For a bit of context, I'm using these tests to interact with an external REST API to test how I've set up a workflow. In other words, I'm not testing my own code, and all the interactions with the server are real and result in real CRUD transactions. They're not significant, and they're easy to clean up, so it's a safe and controlled thing to be doing. And my tests build on each other, from one describe to the next (i.e., the page I'm working with is created once, before the very first test, and modified with the REST API throughout the remainder) which means that a beforeAll in describe #2 running before the tests in describe #1 can mess up the whole thing. (Especially if I'm trying to skip blocks with xdescribe!)
You can use -i flag for run your test sequentially;
Also You can wrap your describe blocks into IIFE (or add function for that):
describe('async behavior', () => {
(() =>
describe('block 1', () => {
...
}))();
(() =>
describe('block 2', () => {
...
}))();
});
and it will work as You expect, but I'm not sure is it a good practice. Maybe this way will create some other logical errors for some other async behaviour;

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