Let's say the application changes the URL using the assignment:
1 | location.href = 'https://acme.com' |
Can we prevent the URL change? For example, we might want to limit our test to the current origin. Let's do it using the following example page:
1 | <body> |
1 | setTimeout(() => { |
The initial Cypress test does not prevent the navigation to "acme.com"
1 | it('sets the location HREF', () => { |

🎁 The source code for this blog post is located in the repo bahmutov/with-window.
Video
I have recorded a short video going through this example and my solution
Cannot stub Location object
We need to prevent location.href = ... assignment by making the property read-only or better: using a custom object property definition with a setter stub function. Unfortunately, we cannot overwrite the location.href property:

Ok, maybe we can mock the entire location object? It is a property of the global window object:

Unfortunately, the location property itself cannot be overwritten either.

We need another way. In the blog post Stub The Unstubbable I have shown one possible solution that modifies the application's source code to create an intermediate proxy "Location" instance. The E2E test can control that object. It is imperfect solution, as it requires source code modifications, which might be unavailable.
Let's find another way.
JavaScript with keyword
JavaScript has a pretty obscure operator with that you should definitely NOT use in production, but can help us with our testing needs.
1 | with (expression) { |
The above syntax adds the expression result into the variable lookup chain. For example:
1 | let a, x, y; |
In the example above, the PI, cos, and sin are properties of the Math object. By using with (Math) we are forcing the browser to look up these identifiers in the Math object (before going up to the window object).
Tip: the with (expression) syntax is hard to read and understand. A simple spread operator would be much more preferable way of coding the above example:
1 | let a, x, y; |
The E2E test
The application's code accessing the location object is loaded by the <script src="app.js"></script> resource. Let's wrap this code using a fake window object just so we can "sneak" in a fake "location" object of our creation.
1 | it('sets the location HREF', () => { |
When the test runs, the application loads app.js and receives the following (modified) script
1 | const fakeWindowObject = { |
The test stays on the same page, but we do see the application printing "changing window location to acme.com".

We need to verify the location.href property really changes to acme.com string. We can put the fake window object on the real window object. Then we can get its value using the cy.window command.
One last tip: the browser caches the app.js resource, thus we need to remove the caching headers in order to receive the full JavaScript source code:
1 | cy.intercept('GET', 'app.js', (req) => { |
Proxy to the real location
We don't want to create a completely fake location object, since we want to be able to use the real properties and methods in Location.prototype. Instead of plain object, our fake location can proxy to the real thing:
1 | it('sets the location HREF', () => { |
To see the proxy in action, I will modify the console.log message the application prints:
1 | setTimeout(() => { |
The test runs and we see the real host name, yet href = ... assignment is trapped.

Tip: if you do not know the URL value, simply request it to make sure it is valid using cy.request command
1 | cy.window() |
If the URL is invalid, or the server responds with an error, cy.request automatically fails.

Nice.