With cypress.io get-Method we can define a timeout only valid for this specific get-method call:
cy.get('.mobile-nav', { timeout: 10000 })
Is there a way to define a timeout for a specific its-method call like:
cy.window().its('MyClass')
Or do I need to increase defaultCommandTimeout in cypress.json?
Place this in the its block before you want it used:
Cypress.config('defaultCommandTimeout', 10000);
Cypress.config() documentation
I'd prefer to set timeout for a specific test instead of modifying global config.
it('should do something', {
defaultCommandTimeout: 10000
}, () => {
// ...
})
https://docs.cypress.io/guides/core-concepts/writing-and-organizing-tests.html#Allowed-config-values
For me, I just do this wherever I wanted to wait
cy.get('ELEMENT', {timeout:50000})
Based on Joel's answer, here's what I did to restore to the default timeout right after:
const DEFAULT_COMMAND_TIMEOUT = Cypress.config().defaultCommandTimeout;
// there's no easy way to increase the timeout when using
// `its` command therefore we need to save the current
// timeout, change it globally, and restore it after
Cypress.config('defaultCommandTimeout', 15000);
return cy
.window()
.its('something')
.should('exist')
.then(() => {
Cypress.config('defaultCommandTimeout', DEFAULT_COMMAND_TIMEOUT);
});
My web app was launching over 6 XHR (which is over the default limit of chrome) requests upon login, so request I am intercepting occasionally takes more than default 5000
Cypress.config('requestTimeout', 10000);
It seems that this should work (not tested it though):
cy.window().its({ timeout: 10000 }, 'MyClass')
Related
We recently installed datadogRUM in our application and now so many DD events kick off in my cypress test that they cause a timeout and failure
I have tried cy.intercept in multiple ways:
cy.intercept('POST', 'https://rum.browser-intake-datadoghq.com/*', {
statusCode: 202,
body: {
},
}).as('datadogRUM');
cy.intercept('POST', 'https://rum-http-intake.logs.datadoghq.com/*', {});
cy.intercept(/\.*datadog.*$/, (req) => {
console.log('DATADOG INTERCEPTED');
req.reply("console.log('datadog intercept');");
});
cy.intercept({
method: 'POST',
url: '/\.*datadog.*$/'
}, req => {
req.destroy();
});
cy.intercept('POST', 'https://rum-http-intake.logs.datadoghq.com/*', { forceNetworkError: true });
just to start. I feel like I've tried every possible variation. I also created a cypress.json file in my /cypress folder
{
"blockHosts": "*datadoghq.com/*"
}
I get hundreds of calls back in my network tab to https://rum.browser-intake-datadoghq.com/api/v2/rum with the preview of console.log('datadog intercept') as I've intercepted them. They all display the solid blue line as if they are being intercepted and blocked. When I set the intercept to an alias I see the alias in my cypress runner window. But there are no 503s or 404s anywhere. The page still fills up with events, cypress gets overloaded, and my test times out.
I even tried copying the data-dog-rum.ts from the src/utils folder to cypress/utils and either commenting out everything or setting the sampleRate to 0, no dice.
EDIT: I am able to get the test passing by adding
Cypress.on('uncaught:exception', () => {
// returning false here prevents Cypress from
// failing the test
return false;
});
to my support/index.js but now whether I add a cy.intercept in my test makes absolutely no difference. The page still fills up with datadog requests regardless, and whether they come back as 200/pending/cancelled, they still delay a single it block in a spec to where it takes 60 seconds to run instead of approx 10 seconds
You can use javascript to perform the stub inside the routeHandler
cy.intercept('*', (req) => { // look at everything
if (req.url.includes('datadoghq')) { // add more conditions if needed
req.reply({}) // prevent request reaching the server
}
})
blockhosts should work with
Pass only the host
{
"blockHosts": "*datadoghq.com"
}
I am trying to end to end test a file upload page in Cypress, which includes testing if a file upload progress bar works.
Unfortunately, in the local environment the upload happens instantly so the progress bar is not shown.
I am aware that you can throttle the response speed using cy.intercept() to simulate slow networks. However, this isn't slowing the request upload speed:
cy.intercept('post', `/route`, (req) => {
req.on('response', (res) => {
res.setThrottle(1000)
})
}).as('post')
Is there any way that I can apply a throttle to the outgoing request?
I think Cypress itself can only throttle or delay responses using cy.intercept() as mentioned in the question and described here: Throttle or delay response all incoming responses
However, you can probably use the Chrome Debugger Protocol from Chrome DevTools for that. Here you have the function Network.emulateNetworkConditions that allows you to throttle both download and upload throughput. This approach then assumes that your tests run in Chrome.
If you're unsure how to use this in your Cypress tests, here is a blog post about testing an application in offline network mode that uses a similar approach with Network.enable: Testing an Application in Offline Network Mode
In the routeHandler, use setTimeout() to delay the call to req.continue().
To get the command queue to wait for setTimeout, return a Promise wrapper. See Request phase
If the handler returned a Promise, wait for the Promise to resolve.
If you want a delay longer than the default command timeout of 4 seconds, you will need to increase the config since cy.intercept does not take a timeout option.
Cypress.config('defaultCommandTimeout', 6000) // timeout in 6 seconds
cy.intercept('POST', '/route', (req) => {
return new Promise(resolve => {
setTimeout(() => resolve(req.continue()), 5000) // delay by 5 seconds
})
}).as('delayedRequest')
// trigger POST
cy.wait('#delayedRequest')
Cypress.config('defaultCommandTimeout', 4000) // revert to normal timeout
Middleware
If you already have a complex intercept, you can set the delay in a middleware intercept.
Middleware is always executed first, but if does not handle the request the call is passed to the next intercept.
// request is delayed here
cy.intercept('POST', '/route', {
middleware: true // middleware always fires first
},
(req) => new Promise(resolve =>
setTimeout(() => resolve(), 5000) // no handler, just delay
)
)
// then passed to here
cy.intercept('POST', '/route',
(req) => {
req.continue() // handler for request
}
).as('delayedRequest')
// trigger POST
cy.wait('#delayedRequest')
In my app after login in from a clean state, there are a series of sync queries that are being fired to make sure that the local data is updated. There is a loading screen while this is happening. I just need to cypress to wait for all these calls to finish before performing the test.
cy.intercept() is identifying the call, but cy.wait() only waits for the first one to be finished.
Is there a way to create the alias dynamilcally or have the application wait for the spinner to disapper?
describe('Navigation', function () {
beforeEach(function () {
// Programmatically login via Amazon Cognito API
cy.intercept('POST', '**/graphql').as('dataStore');
cy.loginByCognitoApi(Cypress.env('cognito_username'), Cypress.env('cognito_password'));
cy.wait(['#dataStore']);
});
it('shows logged in', function () {
cy.get('[data-test=logo]').should('be.visible');
});
You can repeat wait on a single intercept, so count up the number of orange dataStore tags (looks like 11) and wait that amount of times
cy.intercept('POST', '**/graphql').as('dataStore');
cy.loginByCognitoApi(Cypress.env('cognito_username'), Cypress.env('cognito_password'));
Cypress._.times(11, () => {
cy.wait('#dataStore')
})
Or it might be 10 - looking at the route defn. In any case, experiment. The app should be consistent in the calls it makes.
I had a similar case. What I do is store an array of objects in a different file and each object represents a specific test scenario. That way you can iterate through your test cases an assign an alias dynamically.
So you could do something like this:
beforeEach(function () {
yourArray.forEach((testcase) => {
cy.intercept('POST', '**/graphql').as(`${testcase.testname}datastore`);
cy.loginByCognitoApi(Cypress.env('cognito_username'),
Cypress.env('cognito_password'));
cy.wait(`#${testcase.testname}datastore`);
}
});
If the number of requests aren't consistent, something I've done is the following (I've since put this in a command to use in multiple places):
cy.intercept('POST', '**/graphql').as('dataStore');
cy.loginByCognitoApi(Cypress.env('cognito_username'),Cypress.env('cognito_password'));
cy.get('#dataStore.all').then(xhrs => cy.wait(Array(xhrs.length).fill('#dataStore')));
Doing a wait on the alias with "all" returns all of the calls made to aliased route that Cypress has seen since the alias was made.
#user16695029 is a great solution.
If you run into the issue of API calls not being predictable (kicked off by a timer async etc), then keeping track of API call count might be useful:
at the start of your test code
let responseCounter = 0;
cy.intercept({ method: 'POST', url: '/save', middleware: true }, req => {
req.on('response', (res) => {
responseCounter++;
})
}).as('save')
then later
let expectedSaveCount = 12;
Cypress._.times(expectedSaveCount - responseCounter, () => {
cy.wait('#save')
})
cy.get('#save.all').should('have.length', expectedSaveCount)
To avoid adding cy.wait, am trying to user the explicit wait, but seems it is broken. What I missed here
const APIAccountPage= {
APIAccount(name) {
//cy.wait(14000);
cy.server();
cy.route('GET', '/api/accounts').as('Accounts');
cy.wait('#Accounts', { timeout: 10000 })
.its('status').should('eq', 200);
cy.get('.vbutton__baseline___3XNit.fonts__letterSpacing___3l5GB.styles__searchIconWrapper___19oVL.styles__iconButton___LLzft')
.should('be.visible')
.should('not.be.disabled');
cy.get('.vbutton__baseline___3XNit.fonts__letterSpacing___3l5GB.styles__searchIconWrapper___19oVL.styles__iconButton___LLzft').click();
cy.wait(4000);
cy.get('.styles__patientSearchClass___2iGOV').type(name);
cy.wait(4000);
cy.get('.styles__patientSearchClass___2iGOV').contains(name).click();
}
}
module.exports = APIAccountPage;
Error Details
Cypress Window
Try this following, if the /api/account is not working, there may be multiple reason, one of which I am thinking is a backend api (which happened in my case) for which you need to handle the authentication.
If it is same then try to provide the complete URL like,
cy.server()
cy.route(
'GET',
'https://<URL>/api/accounts',
'{"errors":[]}',
).as('account');
and call cy.wait('#account') whichever place is appropriate.
Just a sidenote cy.server() and cy.route has been deprecated since 6.0.0, you can read about it from here. Instead, a new method has been introduced called cy.intercept().
cy.intercept('GET', 'https://some-url/api/accounts').as('Accounts')
cy.wait('#Account') //Wait
Is it possible to wait until an element is visible?
cy.get('[data-test=submitIsVisible]').should('be.visible');
This should error if the submit button is not visible. I want to wait until the submit button is visible.
The primary use case is visual testing, i.e. taking a screenshot of the page.
You can wait for the element to be visible like so:
// Give this element 10 seconds to appear
cy.get('[data-test=submitIsVisible]', { timeout: 10000 }).should('be.visible');
According to Cypress's Documentation:
DOM based commands will automatically retry and wait for their corresponding elements to exist before failing.
Cypress offers you many robust ways to query the DOM, all wrapped with retry-and-timeout logic.
Another way to wait for an element’s presence in the DOM is through timeouts. Cypress commands have a default timeout of 4 seconds, however, most Cypress commands have customizable timeout options. Timeouts can be configured globally or on a per-command basis. Check the customizable timeout options list here.
In some cases, your DOM element will not be actionable. Cypress gives you a powerful {force:true} option you can pass to most action commands.
Caveat:
As Anthony Cregan pointed out, the .should('be.visible') assertion checks whether an element is visible on the page, not necessarily in the viewport. This means that this assertion will return true even if the element is not within the visible area of the screen when the test is run.
Further recommended readings:
Retry-ability.
Interacting with elements.
Updated for Cypress v12
If you want to see exactly how Cypress waits for something to become visible, follow this example.
Using this code, you can check out how the delay and the timeout can affect the passing or failing of the .should('be.visible') assertion.
Steps
Add a simple page to a VSCode project containing Cypress v12.1.0
Call it index.html
<html>
<body>
<h2>API fetched data</h2>
<span>will become visible here</span>
</body>
<script>
fetch('https://jsonplaceholder.typicode.com/posts/1')
.then(response => response.json())
.then(data => document.querySelector('span').innerText = data.title )
</script>
</html>
Right-click index.html and choose "Open with Live Server" to activate the page.
Add this test to see how Cypress waits for the API data
describe('test the waiting of API data', () => {
const timings = [
{ delay: 0, timeout: 4000 }, // default, passes
{ delay: 2000, timeout: 4000 }, // passes
{ delay: 4000, timeout: 4000 }, // flaky
{ delay: 5000, timeout: 4000 }, // fails
{ delay: 5000, timeout: 10000 }, // passes
]
timings.forEach(timing => {
const {delay, timeout} = timing;
it(`delayed API by ${delay} ms, command timout is ${timeout} ms`, () => {
cy.intercept('https://jsonplaceholder.typicode.com/posts/1', (req) => {
req.continue((res) => res.setDelay(delay))
})
cy.visit('http://127.0.0.1:5500/index.html')
cy.contains('sunt aut facere', {timeout})
.should('be.visible')
})
})
})
Result
This shows that the longer the delay in receiving the data, the bigger the timeout needed on the visibility assertion.
you can also do it by passing below script into your cypress.config.js files
e2e: {
defaultCommandTimeout: 25000,
}
pass defaultCommandTimeout as per your requirement.
Try element.should('have.length.greaterThan', 0).and('be.visible')