Your One-Stop Guide to cy.visit() in Cypress: Usage, Examples, and Best Practices
Cypress and visit Command Overview:
Cypress: A JavaScript-based end-to-end (E2E) testing framework for modern web applications. It facilitates creating and running automated tests that interact with the browser UI, ensuring the application behaves as expected.
cy.visit() Command: This crucial Cypress command serves as the entry point for your tests. It navigates the browser to a specified URL, loading the corresponding web page. This action lays the groundwork for subsequent interactions with the page's elements.
Optional Parameters:
- timeout (Number): Sets the maximum wait time (in milliseconds) for the page to load. The default is 30000 (30 seconds).
- onBeforeLoad (Function): Executes a callback function before the page finishes loading. Useful for setting up test data or environment configurations.
- onLoad (Function): Executes a callback function after the page finishes loading. Typically used for assertions or initial interactions.
Example Code:
- Reloading Window: Visiting a new domain or subdomain necessitates the Cypress window to reload. To test across different domains, use the cy.origin() command.
- Automatic baseUrl Prefixing: If you've set a baseUrl configuration in Cypress, cy.visit() will automatically prepend it to the provided URL.
Potential Errors:
- Timeout Error: If the page loading exceeds the specified timeout, Cypress throws a timeout error.
- Network Errors: Connectivity or server issues can lead to network errors, preventing successful page loading.
- JavaScript Errors: Issues within the website's JavaScript code might interfere with the Cypress test execution.
Error Handling:
- Use cy.on('uncaught:exception', (err) => { ... }) to handle expected or unexpected JavaScript errors.
- Implement retries with cy.reload() or cy.wait() in case of temporary network failures.
Best Practices:
- Employ descriptive URLs for clarity and maintainability.
- Set appropriate timeouts based on page loading expectations.
- Handle errors gracefully to prevent test failures due to external factors.
By effectively incorporating the cy.visit() command and considering the provided best practices, you can streamline your Cypress test writing experience and ensure your web applications function correctly across different scenarios.
Visit a remote URL.
Best Practice We recommend setting a baseUrl when using cy.visit() . Read about best practices here.
Correct Usage
url (String)
The URL to visit.
Cypress will prefix the URL with the baseUrl configured in your global configuration if set.
If the baseUrl has not been set, you will need to specify a fully qualified URL or Cypress will attempt to act as your web server. See the prefixes notes for more details.
Note: visiting a new domain requires the Test Runner window to reload. You cannot visit different super domains in a single test.
options (Object)
Pass in an options object to control the behavior of cy.visit() .
By default, the cy.visit() commands' will use the pageLoadTimeout and baseUrl set globally in your configuration .
You can also set all cy.visit() commands' pageLoadTimeout and baseUrl globally in configuration .
- cy.visit() 'yields the window object after the page finishes loading'
Let's confirm the window.navigator.language after visiting the site:
Visit a local server running on http://localhost:8000
cy.visit() resolves when the remote page fires its load event.
Change the default timeout
Overrides the pageLoadTimeout set globally in your configuration for this page load.
Add basic auth headers
Cypress will automatically apply the right authorization headers if you're attempting to visit an application that requires Basic Authentication .
Provide the username and password in the auth object. Then all subsequent requests matching the origin you're testing will have these attached at the network level.
You can also provide the username and password directly in the URL.
Cypress will automatically attach this header at the network proxy level, outside of the browser. Therefore you will not see this header in the Dev Tools.
Provide an onBeforeLoad callback function
onBeforeLoad is called as soon as possible, before your page has loaded all of its resources. Your scripts will not be ready at this point, but it's a great hook to potentially manipulate the page.
Check out our example recipes using cy.visit() 's onBeforeLoad option to: Bootstrapping your App Set a token to localStorage for login during Single Sign On Stub window.fetch
Provide an onLoad callback function
onLoad is called once your page has fired its load event. All of the scripts, stylesheets, html and other resources are guaranteed to be available at this point.
Add query parameters
You can provide query parameters as an object to cy.visit() by passing qs to options .
The parameters passed to qs will be merged into existing query parameters on the url .
Submit a form
To send a request that looks like a user submitting an HTML form, use a POST method with a body containing the form values:
Visit is automatically prefixed with baseUrl
Cypress will prefix the URL with the baseUrl if it has been set. We recommending configuring the baseUrl in the your configuration file ( cypress.json by default) to prevent repeating yourself in every cy.visit() command.
If you would like to visit a different host when the baseUrl has been set, provide the fully qualified URL you would like to go to.
Visit local files
Cypress will automatically attempt to serve your files if you don't provide a host and the baseUrl is not defined . The path should be relative to your project's root folder (where the cypress.json file is generated by default).
Having Cypress serve your files is useful in smaller projects and example apps, but isn't recommended for production apps. It is always better to run your own server and provide the URL to Cypress.
Visit local file when baseUrl is set
If you have baseUrl set, but need to visit a local file in a single test or a group of tests, set the baseUrl to null by using a test configuration . Given our cypress.json file:
The first test visits the baseUrl , while the second test visits the local file.
Visit will automatically follow redirects
Protocol can be omitted from common hosts.
Cypress automatically prepends the http:// protocol to common hosts. If you're not using one of these 3 hosts, then make sure to provide the protocol.
Visit will always yield the remote page's window object when it resolves
Trying to change the User-Agent ? You can set the userAgent as a configuration value in your configuration file.
Prevent requests before a remote page initially loads
One common scenario Cypress supports is visiting a remote page and also preventing any Ajax requests from immediately going out.
You may think this works:
But if your app makes a request upon being initialized, the above code will not work . cy.visit() will resolve once its load event fires. The cy.intercept() command is not processed until after cy.visit() resolves.
Many applications will have already begun routing, initialization, and requests by the time the cy.visit() in the above code resolves. Therefore creating a cy.intercept() route will happen too late, and Cypress will not process the requests.
Luckily Cypress supports this use case. Reverse the order of the commands:
Cypress will automatically apply the routes to the very next cy.visit() and does so immediately before any of your application code runs.
Requirements
- cy.visit() requires being chained off of cy .
- cy.visit() requires the response to be content-type: text/html .
- cy.visit() requires the response code to be 2xx after following redirects.
- cy.visit() requires the load load event to eventually fire.
- cy.visit() will automatically wait for assertions you have chained to pass
- cy.visit() can time out waiting for the page to fire its load event.
- cy.visit() can time out waiting for assertions you've added to pass.
Command Log
Visit example application in a beforeEach
The commands above will display in the Command Log as:
When clicking on visit within the command log, the console outputs the following:
- cy.reload()
- cy.request()
- Recipe: Bootstrapping your App
- Recipe: Logging In - Single Sign on
- Recipe: Stubbing window.fetch
© 2017 Cypress.io Licensed under the MIT License. https://docs.cypress.io/api/commands/visit
Cypress vs. Other Testing Frameworks: Why Cypress Should Be Your Choice
What it is: Cypress. $ is a custom jQuery-like syntax used within Cypress test scripts to interact with web elements. It provides a familiar and concise way to locate and manipulate elements in the DOM
- Cypress 101: A Beginner's Guide to Automated Testing for JavaScript Apps
- Cypress: A Comprehensive Guide for End-to-End Web Application Testing
- Unlocking the Power of Cypress: JavaScript-Based Testing for Modern Web Apps
Leveraging Cypress Fixtures: Enhance Code Reusability and Maintainability
Leveraging Cypress Pause for Debugging and Manual Interaction in Web Testing
Cypress scrollintoview : your toolkit for effective element scrolling.
Visit a remote URL.
Best Practice We recommend setting a baseUrl when using cy.visit() . Read about best practices here.
Correct Usage
url (String)
The URL to visit.
Cypress will prefix the URL with the baseUrl configured in your network options if you’ve set one.
You may also specify the relative path of an html file. The path is relative to the root directory of the Cypress installation. Note that the file:// prefix is not needed.
options (Object)
Pass in an options object to control the behavior of cy.visit() .
You can also set all cy.visit() commands’ pageLoadTimeout and baseUrl globally in configuration .
cy.visit() yields the window object after the page finishes loading.
Visit a local server running on http://localhost:8000
cy.visit() resolves when the remote page fires its load event.
Change the default timeout
Add basic auth headers.
Cypress will automatically apply the right authorization headers if you’re attempting to visit an application that requires Basic Authentication .
Provide the username and password in the auth object. Then all subsequent requests matching the origin you’re testing will have these attached at the network level.
You can also provide the username and password directly in the URL.
Cypress will automatically attach this header at the network proxy level, outside of the browser. Therefore you will not see this header in the Dev Tools.
Provide an onBeforeLoad callback function
onBeforeLoad is called as soon as possible, before your page has loaded all of its resources. Your scripts will not be ready at this point, but it’s a great hook to potentially manipulate the page.
Check out our example recipes using cy.visit() ‘s onBeforeLoad option to: Bootstraping your App Set a token to localStorage for login during Single Sign On Stub window.fetch
Provide an onLoad callback function
onLoad is called once your page has fired its load event. All of the scripts, stylesheets, html and other resources are guaranteed to be available at this point.
Add query paramaters
You can provide query parameters as an object to cy.visit() by passing qs to options .
The parameters passed to qs will be merged into existing query parameters on the url .
Submit a form
To send a request that looks like a user submitting an HTML form, use a POST method with a body containing the form values:
Visit will automatically follow redirects
Protocol can be omitted from common hosts.
Cypress automatically prepends the http:// protocol to common hosts. If you’re not using one of these 3 hosts, then make sure to provide the protocol.
Cypress can optionally act as your web server
Cypress will automatically attempt to serve your files if you don’t provide a host and baseUrl is not defined . The path should be relative to your project’s root folder (where the cypress.json file is generated by default).
Having Cypress serve your files is useful in smaller projects and example apps, but isn’t recommended for production apps. It is always better to run your own server and provide the url to Cypress.
Visit is automatically prefixed with baseUrl
Configure baseUrl in the your configuration file ( cypress.json by default) to prevent repeating yourself in every cy.visit() command.
Visit will always yield the remote page’s window object when it resolves
Trying to change the User-Agent ? You can set the userAgent as a configuration value in your configuration file.
Prevent XHR / Ajax requests before a remote page initially loads
One common scenario Cypress supports is visiting a remote page and also preventing any Ajax requests from immediately going out.
You may think this works:
But if your app makes a request upon being initialized, the above code will not work . cy.visit() will resolve once its load event fires. The cy.server() and cy.route() commands are not processed until after cy.visit() resolves.
Many applications will have already begun routing, initialization, and requests by the time the cy.visit() in the above code resolves. Therefore creating a cy.server() will happen too late, and Cypress will not process the requests.
Luckily Cypress supports this use case. Reverse the order of the commands:
Cypress will automatically apply the server and routes to the very next cy.visit() and does so immediately before any of your application code runs.
Requirements
cy.visit() requires being chained off of cy .
cy.visit() requires the response to be content-type: text/html .
cy.visit() requires the response code to be 2xx after following redirects.
cy.visit() requires the load load event to eventually fire.
cy.visit() will automatically wait for assertions you've chained to pass.
cy.visit() can time out waiting for the page to fire its load event.
cy.visit() can time out waiting for assertions you've added to pass.
Command Log
Visit example application in a beforeEach
The commands above will display in the Command Log as:
When clicking on visit within the command log, the console outputs the following:
- cy.reload()
- cy.request()
- Recipe: Bootstrapping your App
- Recipe: Logging In - Single Sign on
- Recipe: Stubbing window.fetch
© 2020 Cypress.io Licensed under the MIT License. https://docs.cypress.io/api/commands/visit.html
Cache and restore cookies , localStorage , and sessionStorage (i.e. session data) in order to recreate a consistent browser context between tests.
The cy.session() command will inherit the testIsolation value to determine whether or not the page is cleared when caching and restoring the browser context.
Correct Usage
Incorrect Usage
Arguments
id (String, Array, Object)
A unique identifier that will be used to cache and restore a given session. In simple cases, a String value is sufficient. In order to simplify generation of more complex ids, if you pass an Array or Object , Cypress will generate an id for you by deterministically stringifying the value you pass in. For example, if you pass ['Jane', '123', 'admin'] , an id of ["Jane","123","admin"] will be generated for you.
See the choosing the correct id to cache a session section for a more thorough explanation with examples.
Note that large or cyclical data structures may be slow or difficult to serialize into an identifier, so exercise care with the data you specify.
setup (Function)
This function is called whenever a session for the given id hasn't yet been cached, or if it's no longer valid (see the validate option). After setup and validate runs for the first time, Cypress will preserve all cookies, sessionStorage , and localStorage , so that subsequent calls to cy.session() with the same id will bypass setup and just restore and validate the cached session data.
The page is cleared before setup when testIsolation is enabled and is not cleared when testIsolation is disabled.
Cookies, local storage and session storage in all domains are always cleared before setup runs, regardless of the testIsolation configuration.
options (Object)
- cy.session() yields null .
Updating an existing login custom command
You can add session caching to your login custom command . Wrap the inside of the command with a call to cy.session() .
With session validation
Updating an existing login helper function
You can add session caching to a login helper function by wrapping the inside of the function with a call to cy.session() .
Switching sessions inside tests
Because cy.session() clears the page and all session data before running setup , you can use it to easily switch between sessions without first needing to log the previous user out. This allows tests to more accurately represent real-world scenarios and helps keep test run times short.
Validating the session
The validate function is used to ensure the session has been correctly established. This is especially helpful when a cached session is being restored, because if the session is not valid, cy.session() will recreate the session by re-running setup .
The following scenarios will mark the session as invalid:
- the validate function throws an exception
- the validate function returns a Promise that resolves to false or rejects
- the validate function contains failing Cypress command
- the last Cypress command in the validate function yielded false
Here are a few validate examples:
Modifying session data before caching
If you want to change which session data is cached, you can modify cookies, localStorage , sessionStorage as-necessary in setup .
Caching session data across specs
If you want to use the same session across multiple specs in the same Cypress run on the same machine, add cacheAcrossSpecs=true to the session options to leverage the session through the run.
Multiple login commands
A more complex app may require multiple login commands, which may require multiple uses of cy.session() . However, because the id value is used as a unique identifier to save and restore sessions, it's very important that it's actually unique per session.
In the following example, if the resulting session data that loginByForm and loginByApi create is different in any way , it would be a mistake to specify [name, password] as the id for both, because there would be no way to distinguish between the sessions created by loginByForm("user", "p4ssw0rd") and loginByApi("user", "p4ssw0rd") . Instead, you can modify the id to differentiate its value between both login functions, so that each will always be cached uniquely.
Where to call cy.visit()
Intuitively it seems that you should call cy.visit() immediately after cy.session() in your login function or custom command, so it behaves (from the point of view of the subsequent test) exactly the same as a login function without cy.session() .
However, if you want to test something on a different page, you will need to call cy.visit() at the beginning of that test, which means you will be calling cy.visit() a second time in your test. Since cy.visit() waits for the visited page to become active before continuing, this could add up to an unacceptable waste of time.
Tests will obviously be faster if you call cy.visit() only when necessary. This can be easily realised by organizing tests into suites and calling cy.visit() after logging in, inside a beforeEach hook.
Updating a login function that returns a value
If your custom login command returns a value that you use to assert in a test, wrapping it with cy.session() will break that test. However, it's usually easy to solve this by refactoring the login code to assert directly inside setup .
Cross-domain sessions
It's possible to switch domains while caching sessions, just be sure to explicitly visit the domain in your login command before calling cy.session() .
When the page and session data are cleared
Test isolation enabled .
The page is cleared and cookies, local storage and session storage (session data) in all domains are cleared automatically when cy.session() runs and test isolation is enabled with testIsolation=true (default in Cypress 12), This guarantees consistent behavior whether a session is being created or restored and allows you to switch sessions without first having to explicitly log out.
Note: cy.visit() must be explicitly called afterwards to ensure the page to test is loaded.
Test Isolation Disabled
When test isolation is disabled with testIsolation=false , the page will not clear, however, the session data will clear when cy.session() runs.
cy.visit() does not need to be called afterwards to ensure the page to test is loaded.
NOTE: Disabling test isolation may improve performance of end-to-end tests, however, previous tests could impact the browser state of the next test and cause inconsistency when using .only(). Be mindful to write isolated tests when test isolation is disabled.
When test isolation is disabled, it is encouraged to setup your session in a before hook or in the first test to ensure a clean setup.
Session caching
Once created, a session for a given id is cached for the duration of the spec file. You can't modify a stored session after it has been cached, but you can always create a new session with a different id .
In order to reduce development time, when running Cypress in "open" mode, sessions will be cached for spec file reruns .
To persist a session across multiple specs, use the option cacheAcrossSpecs=true .
Explicitly clearing sessions
When running Cypress in "open" mode, you can explicitly clear all spec and global sessions and re-run the spec file by clicking the "Clear All Sessions" button in the Instrument Panel .
For debugging purposes, all spec and global sessions can be cleared with the Cypress.session.clearAllSavedSessions() method.
Where to call cy.session()
While it is possible to call cy.session() explicitly inside a test or beforeEach , it is considered a best practice to call cy.session() inside a login custom command or reusable wrapper function. See the Updating an existing login custom command and Updating an existing login helper function examples for more details.
Choosing the correct id to cache a session
In order for sessions to be cached uniquely, the id argument must be unique for each new session created. The id provided to cy.session() will display in the reporter, thus we do not recommend using sensitive data like passwords or tokens as unique identifiers.
If you have custom login code that uses multiple parameters (in this example, a name, a token, and a password), in order to be able to log in many different users, but the id only included one of them (in this example, name ):
If you ran this, user1 would be logged in with token1 and p4ssw0rd , and a session would be created and cached using "user1" as the id .
Now let's say you wanted to try to log in the same user, but with a different token and/or password, and expect a different session to be created and cached. You run this, but because cy.session() is only being passed name as its id , it won't create a new session, but will instead load the saved session for "user1" .
In summary, you need to ensure that the id is unique. Create it from all the parameters used inside the setup function that may change, otherwise id values may collide and create unexpected results.
In this example, setting the id to [name, uniqueKey] guarantees that calling login() with different name , token and password values will create and cache unique sessions.
The uuid npm package can be used to generate random unique ids if an arbitrary name-space does not meet your needs.
Common Questions
Why are all my cypress commands failing after calling cy.session() .
When testIsolation is enabled, ensure that you're calling cy.visit() after calling cy.session() , otherwise your tests will be running on a blank page.
Why am I seeing 401 errors after calling cy.session() ?
It's possible that your session is not valid or was not fully established before the session was saved and the command ended. Be sure to specify a validate function so that cy.session() can validate and recreate the session if necessary.
Command Log
The instrument panel .
Whenever a session is created or restored inside a test, an extra instrument panel is displayed at the top of the test to give more information about the state of your sessions.
Clicking any session id in the panel will print that session's details to the console, and clicking the "Clear All Sessions" button will clear all saved spec and global sessions and re-run the spec file (see Session caching for more details).
The command log
Whenever cy.session() is called, the command log will show one of the following lines, which includes the status of the session call along with the session id value:
No saved session was found, so a new session was created and saved:
A saved session was found, and used:
A saved session was found, but the validate function failed, so the session was recreated and saved:
Note that in cases where the validate function fails immediately after setup creates the session, the test will fail with an error.
Expanding the session group in the command log will show all of the commands that were run when creating and/or validating the session.
In this image, a saved session is restored, but when /personal is visited in the validate function, the app redirects to /signin , which invalidates the session. A new session is created by visiting /signin where the user is logged in, after which, validation succeeds, and the session is available for the remainder of the test.
Printing to the console
Clicking a session id in the Instrument Panel or clicking the first line under an expanded session group in the command log will print that session's details to the console. This information contains the id along with any cached session data, including cookies, localStorage and sessionStorage .
- Authenticate faster in tests with the cy.session command
- Custom Commands
- Cypress.session
- Updating an existing login custom command
- Updating an existing login helper function
- Switching sessions inside tests
- Validating the session
- Modifying session data before caching
- Caching session data across specs
- Multiple login commands
- Where to call cy.visit()
- Updating a login function that returns a value
- Cross-domain sessions
- When the page and session data are cleared
- Test Isolation Enabled
- Test Isolation Disabled
- Session caching
- Explicitly clearing sessions
- Where to call cy.session()
- Choosing the correct id to cache a session
- Common Questions
- The Instrument Panel
- The command log
- Printing to the console
Better world by better software
Gleb bahmutov phd , our planet 🌏 is in danger, act today: what you can do, cypress cy.intercept problems, a few common cy.intercept gotchas and how to avoid them.
Note: I am using code from testing-workshop-cypress to demonstrate these cy.intercept gotchas.
📚 Since this post has been published in 2020, Cypress team has resolved many of these issues. To learn how to effectively spy and stub network requests in your Cypress tests, enroll in my online course Cypress Network Testing Exercises . The course covers a lot of topis in its main 30 lessons plus multiple bonus lessons.
The intercept was registered too late
Cy.wait uses the intercept, cached response, multiple matchers, no overwriting interceptors, avoid cypress commands inside the interceptor, sending different responses, no overwriting interceptors (again), single use intercept.
- Get the number of times an intercept was matched
Count intercepts (again)
Set an alias dynamically, simulate network error, network idle.
- Stub SSE resource
- new 🌟 Disable network caching
The problem
Let's say the application is making GET /todos call to load its data. We might write a test like this to spy on this call:
It looks reasonable, it even shows the call in the Command Log - but does NOT pass.
You can see the XHR call in the command log - and it looks like it should work, right? But if you open the "Routes" section, notice that our intercept was never counted.
The root cause
The application is making the call to load the data right on startup :
Thus, it makes the call right at the end of cy.visit command. By the time cy.intercept runs, the call is already in progress , and thus not intercepted. Cypress shows XHR calls by default in its Command Log, thus it has nothing to do with our intercept. I always thought NOT showing when cy.intercept happens in the Command Log was a user experience failure.
The solution
Make sure the network intercept is registered before the application makes the call.
In our case, we need to register the intercept before visiting the page. Once the page is loaded, the application fetches the todo items, and everything is working as expected.
The bonus solution
You can overwrite Cypress commands and log a message to the Command Log. Unfortunately overwriting cy.intercept has a bug with aliases #9580 , and thus we cannot always show when the intercept is registered. We can still do it case by case.
The above test clearly shows that the intercept is registered too late.
Update: the alias bug #9580 has been fixed and released in Cypress v6.2.0
If you first wait on the intercept and then separately try to cy.get it to validate - well, the cy.get always resolves with null .
This is different behavior from cy.route where we could wait on the interception, and the get it separately.
The workaround
Honestly, I feel this is a bug #9306 that we should fix shortly. But for now you can validate the interception immediately using the value yielded by cy.wait
If you want to use multiple assertions over the interception, use the .should(cb) or .then(cb) assertions. The .then(cb) makes more sense here, since the interception value would never change after completing.
Update: the bug #9306 has been fixed and released in Cypress v6.2.0
Let's inspect the interception object yielded by the cy.wait command.
We cannot validate the data sent by the server, because there is no data in the response. Instead the server tells the browser that the data loaded previously is still valid and has not been modified. Hmm, but we are not the browser - the cy.intercept runs in the proxy outside the browser. Thus we have nothing to test.
We can check the caching by using the following test:
This is where it gets a little tricky - since this caching depends on the DevTools setting. If for example, you set the browser DevTools Network tab to disable caching you get the test that passes when the DevTools is closed, and fails when the DevTools is open.
The server will always return the actual data if the server cannot determine what the client has already. The server determines the data "cache key" in this case by looking at the if-none-match request header sent by the web application.
We need to delete this header on the outgoing request. Let's do it using our cy.intercept . Now we can extend the test to validate the response.
The command cy.intercept can match requests using a substring, a minimatch, or a regular expression. By default, it intercepts requests matching any HTTP method. Thus when you define several intercepts, it is easy to get into the situation when multiple intercepts apply. In that case the first cy.wait(alias) "uses up" the intercept's response.
Consider the following test that adds a new todo.
We probably want to make sure the new todo sent to the server has the title "Write a test". Let's set up an intercept and validate the sent data.
If we inspect the value yielded by cy.wait('@post') it shows the object sent in the request
Great, let's validate the new item. Since we do not control the random ID generation (of course we can control it, see the Cypress testing workshop how), we can just validate the title and completed properties.
Great, everything works, what's the problem?
Imagine someone else comes along and changes the cy.visit and adds a wait for todo items to load - just like we did before. They do this to guarantee the application has loaded before adding new items.
So far so good. The test passes.
Notice a curious thing in the Command Log though - the XHR request to post the new item to the server has todos(2) badge.
The POST /todos network call the application has made matches two intercepts, but the Command Log only prints the first intercept's alias.
In our test both intercepts only spied on the request. Now someone comes along and asks why do we need to reset the database before each test using resetDatabase utility method. It would be so simple to stub the initial GET /todos call and return an empty list of items.
The changed test is below - it simply uses cy.intercept('/todos', []).as('todos') .
The test fails.
If there are multiple matching interceptors, the first intercept that stubs the request stops any further processing. Thus our POST /todos network request gets intercepted by overly broad first * /todos intercept.
Use specific intercepts and make sure the stubs are not "breaking" any listeners defined afterwards. Change the first intercept to only work with GET /todos call.
Update: I have opened issue #9588 to provide better labels to the intercepted requests.
Let's imagine the most common scenario - every test starts with zero items. We stab the GET /todos request and visit the site in a beforeEach hook.
Now you want to confirm the application displays the initial list of todos correctly. Hmm, you would like to stub the initial GET /todos call in the new test:
Unfortunately the test fails. The first intercept cy.intercept('GET', '/todos', []) still executes, our the second intercept never has a chance.
We have a tracker issue #9302 to solve this problem somehow. It is complex - changing an intercept from stub to spy, or from spy to stub is tricky.
Separate the suites of tests to avoid using the beforeEach hook that sets up the unwanted intercept.
We have completely separated the suite of tests that should all start with zero items from the suite of tests that should start with two items. Now the intercepts never "compete". After all - this is just JavaScript and you can compose the test commands and the hooks in any way you want.
You cannot use Cypress commands inside the interceptor callback, since it breaks the deterministic command chaining. The following test fails
The interceptor is triggered by the application's code, while other Cypress commands are already running. Cypress has no idea how to chain the cy.writeFile command - should it be after the currently running command? Or later? This leads to non-deterministic and unpredictable tests. Thus this is disallowed.
Instead save the body of the request in a variable, wait for the network call to happen, and then write the data to a file.
Watch the video below for details
Imagine we stub the loading items from the server. Initially the server returns two items. Then the user adds another item (we stub the POST request too), and when the page reloads, we should receive three items. How would we return different responses for the same GET /todos call? At first we could try to overwrite the intercept
But of course this does not work - because as I have shown in the previous section, you cannot overwrite an intercept (yet).
The first interceptor responds to both GET /todos calls. The cy.intercept('GET', '/todos', threeItems) intercept was never called, as I highlighted in the screenshot.
Use JavaScript code to return different responses from a single GET /todos intercept. For example, you can place all prepared responses into an array, and then use Array.prototype.shift to return and remove the first item.
Let's get back to overwriting the interceptors again. We want to overwrite the GET /todos response from a particular test.
The last test "shows the initial todos" fails, because its intercept is never executed. See the previous section No overwriting interceptors for the details and a solution.
We cannot replace the intercept again, but we can extend the logic in setting the intercept to use JavaScript code to implement the overwrite. Let's create a new Cypress command cy.http that will stub the network call with a response, but that response will come from an object of intercepts. Any test that wants to return something else should just replace the value in that object.
Tip: I will store the object with intercepts in Cypress.config() object and will reset it before every test.
The main logic is inside the cy.intercept call
Notice the value is not hard-coded. Here is how we use cy.http from our tests
The tests work
Notice we still have to call the cy.http before the cy.visit , thus cy.visit cannot be placed in the beforeEach hook. Still, overwriting is very convenient for tests that expect different responses, like adding an item.
Tip: add a log message to our cy.http command for better experience.
This is similar to overwriting interceptors. Imagine you want to stub a network call once, but then allow other calls to go through to the server. Just intercepting with a static response would not work, as that intercept would work forever.
Here is the test that does not work.
Notice how the new item appears in the UI, we also see it being sent to the server using POST /todos call. Yet, the intercept "todos" again returns the empty list of todos on page reload.
We want to intercept only once. We do not need to even write a custom command , a simple function would do:
Let's see the test
Nice! And since the intercept continues spying on the requests, even if it does not stub them, we can get the last call and confirm other details.
Get number of times an intercept was matched
Sometimes you want to know / assert the number of times an intercept was matched. You can see this information in the "Routes" table in the Command Log, how can you confirm it from the test?
Note: for now, using cy.state('routes') is an implementation detail, and might change in the future, but you could do the following:
We can count the number of times the intercept was called
If you are stubbing the network calls, use req.reply
There is an alternative approach to getting the number of times a network intercept was invoked - by using cy.spy . If we pass a created spy as route handler, it allows the network call to continue unchanged, yet we can get the count from that spy later
Here is an application that fetches a list of fruits every 30 seconds. We can use a synthetic clock to speed up the test, as described in Testing periodic network requests with cy.intercept and cy.clock combination blog post. We can count the number of times the spy was called.
The Command Log shows the result - there were 4 times the intercept was matched, and 4 times the spy was called.
For stubbing, you can use the Sinon's callsFake method and invoke the req.reply from there
In case of GraphQL, all requests go through a single endpoint. Thus it is important for the intercept to inspect the request first, before deciding what to do. A convenient way to deal with such requests is to set the alias dynamically. For example, let's inspect all GET /users requests, and set an alias for the request that limits the number of users to 5:
The test runs, and an alias is created. The test can wait on that alias:
Imagine you are trying to check how your application handles a network communication error. Internet is unreliable, servers go down and come back up all the time. You want to make sure your web application does not blow up. You might try stubbing network request with forceNetworkError: true response.
Hmm, a curious thing happens - you see multiple intercept messages in the Command Log!
Sometimes you see the intercept matched twice, some times even three times - but it was supposed to be a single Ajax call!
This is the browser trying to be robust and retrying a failed network request. The browser sees a network error coming back from the Cypress proxy and retries the same request 1-2 times before giving up.
I prefer replying with a specific HTTP network status code rather than forceNetworkError: true in most situations. Thus I test how the application handles 404 or 500 errors rather than a generic "Server is not responding" network error.
The browser will not retry such call, since technically the server has responded. It is up to the application to retry on 404 status code.
The deprecated cy.server had an option to block all non-stubbed routes, returning 404 status code instead. How do we recreate the same behavior using cy.intercept ? If we do cy.intercept('*', { statusCode: 404 }) we will break all network requests.
The original cy.server and cy.route only dealt with XMLHttpRequest objects, thus we could limit ourselves to recreating the same behavior of JSON Ajax requests. Assuming your application's code is well-behaved and sets the correct accept header, we can define the last stub to catch all Ajax calls.
If you really need some calls to go through, write the route matching logic yourself
With the full access to the request inside the route matcher function you can decide how to proceed.
To wait for multiple network requests to finish and for the network to be idle, try using the cypress-network-idle plugin. Watch the videos introduction to Cypress-network-idle plugin and Prepare Intercept And Wait Using cypress-network-idle Plugin .
Stub SSE Resource
If you want to stub a server-side events resource, you can respond to the request with a text body that simply lists the events. Unfortunately, the events are not going to be spaced in time, but it still works. See the Cypress Network Testing Exercises bonus lesson 22 for details.
Disable network caching
Cypress network interception happens outside the browser. But the browsers love to save bandwidth, so they cache the data internally. Often you see response headers like ETag: W/"960-cdSYN2pW398HtVmxLUpnD9XjLME" and request headers like If-None-Match: W/"960-cdSYN2pW398HtVmxLUpnD9XjLME" - these control the data caching. The server will look at the If-None-Match header value and respond with 304 Not Modified if the data hash is still the same as what the browser already has.
This creates problems during Cypress testing if you want to get the actual data, but the server sends an empty body with 304 response.
To stop the browser from sending request header If-None-Match you can:
- in the interactive mode open DevTools and check "Disable cache" checkbox.
While the DevTools stay open, the browser will NOT use the cached data and will receive the full data from the server.
- you can delete the request header If-None-Match inside the intcept
The request automatically continues to the server and without it, the server has no idea what data the browser has, thus it is forced to send the full response.
- disable network cache using Chrome Debugger Protocol and built-in Cypress Automation
Tip: I wrote cypress-cdp plugin that makes the above commands much simpler
Cypress Network Testing Exercises Course
I show solutions to many problems shown in this blog post in my hands-on course Cypress Network Testing Exercises .
- blog post Migrating .route() to .intercept() in Cypress
- blog post Difference between cy.route and cy.route2
- spying on requests
- stubbing any request
- changing the response from the server
- intercepting static resources like HTML and CSS
- redirecting requests
- replying with different responses
- blog post Smart GraphQL Stubbing in Cypress
- cy.intercept documentation page
- The blog post Testing loading spinner states with Cypress shows another way to control the network to assert the loading element.
- see how to implement "wait for network idle", "how to test if the network call has not been made" in Cypress Tips and Tricks
A common mistake when using cy.session() (and how to fix it)
February 8, 2023
This is a guest post from Ambassador Filip Hric! Hello, I’m Filip. I teach testers about web development and developers about testing. I have spoken at conferences around the world, created multiple online courses, and host live workshops on e2e testing in Cypress.io. Find everything I do at https://filiphric.com.
In Cypress v12, the cy.session() command was released as generally available. Many teams have already added it to their projects to save minutes from their test runs.
I recently wrote a blogpost on how cy.session() can be used instead of abstracting login logic into a page object. As community members on my Discord implement cy.session() into their projects, I often see one problem pop up repeatedly. Let me give you an example.
Consider this login flow:
By applying the cy.session() API, we can cache the cookies, localStorage and sessionStorage state of our browser for this login flow like this:
There is one small problem with this type of login. As soon as Cypress completes the cy.type() function, it caches our browser state. However, our frontend might still be working on the login flow and some of the authentication data we need has not been set.
Let’s say our application login will send an http request to the server to authenticate the user, and then redirect to a home page, before saving cookies into the browser.
Given the example above, our session will not get properly cached because we didn’t not explicitly tell cy.session() authentication flow is still in progress. The easiest way we can fix that is to add a guarding assertion to our session.
This way we will ensure that Cypress will cache our browser at the right moment. Depending on our application, this might be right after we navigate to a new page or some time later. The exact point will be determined by the application we are testing.
Usually, when making a login action, a server responds with an authorization in the form of a token that gets saved as cookies or some other browser storage. Instead of checking for the location, we can check that storage item. The session might then look something like this:
There is a slight problem with this approach though. The cy.getCookie() command will execute immediately and will not retry. Instead of using cy.getCookie() command we will choose a slightly different approach.
This way we can ensure that we wait the proper time for the application to store our cookies in the browser. After our cookies are saved, we can safely cache our session and use it in our tests.
The validation of a successful login is important for the cy.session() command to work properly. In fact, when using cy.session() you can add the validation as a separate step. This is what the validate() function is for. Let’s see it in action:
In this simple example, cy.session() will work pretty much the same way as before. But there is one upside to this approach. Our validate() function can do more than check the mere presence of the token. Instead, we could check for the validity of the token and make sure it does not expire during, e.g., a longer test run. The validate function can look something like this:
If the cy.request() returns an error code, it will mean that our authorization is no longer valid and we need to log in again. The validate() function will take care of this, and re-run our login flow.
This way we still keep login attempts to minimum, while making sure that the authorization does not expire during the test run.
Remember to always ensure that your cy.session() flow contains a “confirming assertion” that will guard the creation of the session to proper time.
About the Ambassador Program
The Cypress Ambassador program supports the top Cypress advocates around the world. Through this program, Ambassadors are offered speaking opportunities, a personalized hub, and visibility within our extensive network.To learn more about these wonderful ambassadors visit our Official Ambassador webpage.
Navigation Menu
Search code, repositories, users, issues, pull requests..., provide feedback.
We read every piece of feedback, and take your input very seriously.
Saved searches
Use saved searches to filter your results more quickly.
To see all available qualifiers, see our documentation .
- Notifications
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement . We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Cy.visit does not work on this certain websites and I dunno why #26454
justme6666 commented Apr 7, 2023
nagash77 commented Apr 10, 2023
Sorry, something went wrong.
No branches or pull requests
IMAGES
VIDEO
COMMENTS
But if your app makes a request upon being initialized, the above code will not work.cy.visit() will resolve once its load event fires. The cy.intercept() command is not processed until after cy.visit() resolves. Many applications will have already begun routing, initialization, and requests by the time the cy.visit() in the above code resolves. Therefore creating a cy.intercept() route will ...
My webserver was available, I have not had any weird configurations and I was simply trying to visit a page in my cypress tests. When I looked into the details of the error, I saw the following: From Node.js Internals: Error: connect ECONNREFUSED 127.0.0.1:4200 at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1187:16)
I did find a solution. As it turns out, cy.visit() does not work in my app's current test suite configuration. Our command runs yarn run test:unit:cy which opens cypress in unit testing mode?. To sidestep this, I set up a new make command to run yarn run cypress open, where none of the unit test will run apparently, but cy.visit() runs quite happily. ...
No milestone. Development. No branches or pull requests. 7 participants. Current behavior: Upon running cy.visity () and inputing a URL, the request times out. Desired behavior: Trying to run a simple cy.visit () command but unable to Page does not load and hangs until it times out.
@MikeMcC399 I tried above work-around https: ... .6.3 to 13.0.0 (the last one I tried) everything worked fine, with no more problems on the second test, and during the cy.visit() of the second test it looked like fewer assets were being fetched. I don't know what the actual problem is but for now, I'm going to stick with version 13.6.3.
Simulate different network conditions using Cypress commands like cy.route() if necessary. Page Load Timeout: If the page takes a while to load, consider increasing the timeout for cy.visit(). You can do this in your Cypress configuration or the test using cy.visit(url, { timeout: 10000 }), where 10000 is the timeout in milliseconds.
Use cy.visit() to Reset: If you need to switch back to the original domain after visiting a different one, make sure to use cy.visit() ... This is partly why Cypress doesn't often top the charts as a contemporary automation tool among newer web frameworks. On the other hand, modern tools, such as testRigor, leverage AI-driven techniques to ...
Cypress: A JavaScript-based end-to-end (E2E) testing framework for modern web applications. It facilitates creating and running automated tests that interact with the browser UI, ensuring the application behaves as expected. cy.visit () Command: This crucial Cypress command serves as the entry point for your tests.
We also send cookies that the browser would normally send. Headers like Accept, Connection, Host, and User-Agent are normally sent with the browser request, so we also send them with the cy.visit(), because cy.visit() is intended to be used to test visiting a web page. I just tried your original example with the headers you shared:
All Tests are working fine. I just found that this URL actually getting failed to load by calling cy.visit('xxx') in gitlab ci/cd job. But the same URL is working fine with my local machine cypress run.
cy.visit() requires the load load event to eventually fire. Assertions cy.visit() will automatically wait for assertions you've chained to pass. Timeouts cy.visit() can time out waiting for the page to fire its load event. cy.visit() can time out waiting for assertions you've added to pass. Command Log Visit example application in a beforeEach
Because cy.session() clears the page and all session data before running setup, you can use it to easily switch between sessions without first needing to log the previous user out. This allows tests to more accurately represent real-world scenarios and helps keep test run times short. const login = (name) => {.
@chrisbreiding apologies for insisting too much on this. I really don't have any choice here. The latest issue which I am facing connected to this, I have chromeWebSecurity: false, on my cypress.config file and it is working fine in the OPEN mode. However, when I execute the test in RUN mode on chrome browser, I am unable to bi-pass the security. Is this expected?
Ask questions, find answers and collaborate at work with Stack Overflow for Teams. Explore Teams. Collectives™ on Stack Overflow. Find centralized, trusted content and collaborate around the technologies you use most. ... After a few it block, cy.visit() stop attempting to load the url and the test fails due to assertions regarding page ...
Multiple matchers The problem. The command cy.intercept can match requests using a substring, a minimatch, or a regular expression. By default, it intercepts requests matching any HTTP method. Thus when you define several intercepts, it is easy to get into the situation when multiple intercepts apply. In that case the first cy.wait(alias) "uses up" the intercept's response.
Current behavior. cy visit fails to load . the same is working some times without changing anything. We attempted to make an http request to this URL but the request failed without a response. > Error: socket hang up. - you don't have internet access. - you forgot to run / boot your web server.
In Cypress v12, the cy.session() command was released as generally available. Many teams have already added it to their projects to save minutes from their test runs. I recently wrote a blogpost on how cy.session() can be used instead of abstracting login logic into a page object. As community members on my Discord implement cy.session() into their projects, I often see one problem pop up ...
Teams. Q&A for work. Connect and share knowledge within a single location that is structured and easy to search. Learn more about Teams
Your help would be greatly appreciated, if you ever manage to use cy.visit to work on this website, please tell me how... I tried the standard cy.visit command and unline other websites this one doesn't load... Thank you. Desired behavior. In this situation cy.visit should load the specified page.... Test code to reproduce
The intercept must always be set up before the trigger (page visit or button click). But the intercept varies between tests, so instead of before() I would try setting up a helper function that is called at the top of each test. const loadAndIntercept = (apiResult) => {.