Cypress Test timeout on then() not respecting timeout option - cypress

I've been trying to figure out when the .then() function in Cypress is not respecting the timeout option. The then() function is asserting a few times and ultimately fails because my call is still waiting for results. What I was hoping for is setting it to 90000 so that it just spends more time asserting.
commonPage
.getSearchInput()
.clear({ force: true })
.type('My Search Term', { force: true })
.type('{enter}')
.then({ timeout: 90000, }, () => {
accountRoles.getLabel().should('have.contain.text', 'My Search Term');
});
getLabel(): Cypress.Chainable<JQuery<HTMLElement>> {
return cy.get('div[col-id="label"]');
}

If you want to increase the timeout for .should('contain.text', 'My Search Term'), putting it on the .then() wrapping is incorrect.
The .should() will retry only the .getLabel(), so whatever is in there needs the timeout option.
One of the down-sides of using page objects in Cypress.
For example, this is the usual pattern
cy.get('div[col-id="label"]', {timeout:90000})
.should('contain.text', 'My Search Term')
This will repeat cy.get('div[col-id="label"]' until .should('contain.text', 'My Search Term') becomes true, or times out.

If your app is sending a request/call, intercept that and wait on it so you don't have to continuously play a guessing game with a timeout. Also, 90 seconds is a lot of time to complete for what looks to be search suggestions.
cy.intercept('GET','/your-call').as('yourCall')
commonPage
.getSearchInput()
.clear({ force: true })
.type('My Search Term', { force: true })
.type('{enter}')
cy.wait('#yourCall')
accountRoles.getLabel().should('have.contain.text', 'My Search Term')

Related

Cypress - Unable to run multiple tests

I am very new to cypress automation and have been following though some examples and have ran into an issue that does not appear to be addressed in any video I have seen, where multiple tests in the same 'describe' do not run as expected.
If I create the following code & run it, it all works perfectly:-
describe('My First Test', () => {
it('Open Google', () => {
cy.visit('https://google.co.uk')
cy.get('#L2AGLb > .QS5gu').click()
cy.get('.gLFyf').type('Automation Step by Step{Enter}')
})
})
I have then attempted to split up the test into individual tests, as follows:-
describe('My First Test', () => {
it('Open Google', () => {
cy.visit('https://google.co.uk')
})
it('Confirm warning', () => {
cy.get('#L2AGLb > .QS5gu').click()
cy.get('.gLFyf').type('Automation Step by Step{Enter}')
})
it('Confirm warning', () => {
cy.get('.gLFyf').type('Automation Step by Step{Enter}')
})
})
The problem now is that after opening Chrome and then going into the next test, which should allow me to type the text, a 'default blank page' is displayed and the tests then fail.
What am I missing here to be able to run these three tests fully?
Code in VS Code
Error after opening Chrome & attempting to type in box
As above really, I was hoping to be able to run all three simple tests together.
EDIT - I rolled back my version of Cypress to 10.10.0 and it works perfectly, so no idea what has changed on the latest version released yesterday.
Try it with Test Isolation turned to false.
Best Practice: Tests should always be able to be run independently from one another and still pass
This was added in Cypress 12.0.0.
But if you want to play without it,
Test Isolation Disabled
testIsolation
beforeEach test
true
- clears page by visiting about:blank
- clears cookies in all domains
- local storage in all domains
- session storage in all domains
false
does not alter the current browser context
cypress.config.js
const { defineConfig } = require('cypress')
module.exports = defineConfig({
e2e: {
testIsolation: false,
},
})
You should visit your page for every test. You can put the cy.visit in a beforeEach function.

cy.url().should() failing with type error

We have the following piece of code that fails intermittently.
cy.url().then((url) => {
if (url.includes('https://app') || url.includes('https://auth')) {
cy.url().should('match', /\/agent|\/worker/, { timeout: 30000 }) ^
}
})
The failure happens in the second cy.url() command where the should match condition fails with the error "[object Object]: expected undefined to match". We only see this error every once or so in 10 time.
Instead of again using cy.url(), use the previous url you extracted with cy.wrap(), something like this:
cy.url().then((url) => {
if (url.includes('https://app') || url.includes('https://auth')) {
cy.wrap(url).should('match', /\/agent|\/worker/, {timeout: 30000})
}
})
I'm not sure of the reason for covering multiple scenarios like this would be.
Also, you don't give an example of the url, I'm going to assume your regex is to check the pathname of the url.
cy.location('pathname').should('match', /\/agent|\/worker/i)

Retries in Cypress and Before hooks

Morning all. I have a slightly unusual design to my tests. A typical example might be
describe('1', () => {
describe('2', () => {
before()
describe('3', () => {
it('1')
// ...
it('n')
});
});
});
If there is a failure in one of the individual tests (it 1..n), I want to re-run ALL of those tests, and run the "before" code first too - ie from "describe 2". If I use a before hook then re-runs don't trigger that again. If I change to a beforeEach, then it gets called before each and every "it" block, which I don't want.
Effectively, each it block is a test check, describe 3 is a test step, describe 2 a test spec, and describe 1 a test "group"
Can anyone suggest a way I can re-run a test spec (describe 2) when one test check fails, including re-running the before code for that spec?
(I know this is probably anti-pattern etc, but....)
You can externalise the before() callback function, and use the test:after:run event to trigger it on a retry.
I haven't tested this extensively, but the gist is
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

In my Cypress.io tests why do I need to treat a cy.task like it is asyncronous when its not

I have Cypress.io tests that use a simple Cypress task to log some table information to the terminal. For example I have a test like so:
it("Writes some console logs and checks a val", () => {
cy.task("rowLog", { id: 1, name: "foo", type: "bar" });
let one = 1;
expect(one).to.equal(2);
});
And the task, "rowLog" like so:
module.exports = (on, config) => {
on("task", {
rowLog(data) {
// use node.js console.table to pretty print table data:
console.table(data);
return null;
},
}
But the result of rowLog will not display in my terminal if I run Cypress headlessly via Cypress run. This is because the test will fail. If we switch the test so that it passes, then it will show.
However I just realized that if I treat rowLog like it's async like below. It will print the results to the terminal:
it("Writes some console logs and checks a val", () => {
// add a .then to task:
cy.task("rowLog", { id: 1, name: "foo", type: "bar" }).then(() => {
let one = 1;
expect(one).to.equal(2);
});
});
This is not what I would expect from the docs. They say that:
cy.task() yields the value returned or resolved by the task event in the pluginsFile.
(source)
And that a task can yield either a promise or a value.
I'm new to Cypress here-- is there something I'm missing or doing wrong? I'd like to be able to not have to chain my tasks with .then statements if it is just synchronous stuff like writing output to ensure everything is emitted to my terminal.
If you look into the type definition of cy.task command, you will see that it returns a Chainable (that is a promise-like entity). So it behaves like any other cy command (ansynchrounously).
As for the yield either a promise or a **value** - this statement refers to the handler of the task, not the task itself. As for the other command, Cypress will wrap a returned value into a promise if it was not done by the handler.

How do I wait for the callback passed to a Cypress custom command to finish before proceeding the test?

I have this code:
cy.visit(Cypress.env('frontendUrl'))
.pathShould('be', '/login')
.log('Reached')
My custom command pathShould looks like this:
Cypress.Commands.add('pathShould', (chain, path) => {
cy.location('pathname', { timeout: 20000 }).should(chain, path);
});
In some cases the should assertion is executed, in others it isn't, like here:
How do I ensure that the callback of my custom command is executed completely before continuing the test?
The main issue is that there is not an assertion named be.
cy.should('be', '') won't do anything - it's not a valid assertion. You're probably looking for cy.should('eq'...)
This works for me:
Cypress.Commands.add('pathShould', (chain, path) => {
return cy.location('pathname', { timeout: 20000 }).should(chain, path);
});
it('', () => {
cy.visit('http://example.com')
.pathShould('eq', '/')
.log('Reached')
})
On a side note: it is probably an overkill to use a custom command like this, as you don't really gain anything by wrapping a one liner. Take a look at: https://docs.cypress.io/api/cypress-api/custom-commands.html#Best-Practices

Resources