-
Notifications
You must be signed in to change notification settings - Fork 3.2k
New issue
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
max 1 site... support visiting multiple superdomains in one test #944
Comments
Yeah, it can be done. Quick question - is there any reason you need a browser to load up 500 different domains and click a button? Wouldn't it be much much easier to just use For instance, what does clicking the button do? Send an HTTP request is my guess. So instead of using the UI, just send the HTTP request directly using Any additional information about your use case would be helpful. |
Just jumping in to say that I have a use-case where I need to load several sites in the same test (not 500, 2 will do for a test). I'm testing a browser extension that will show a modal (via a content script) on several sites that you can whitelist in its settings. The extension uses a global timer (via its background tab) to synchronise the extension's behaviour across different sites/tabs/link clicks/refreshes (it persists a countdown as you browse those various whitelisted sites, among other things). Because of this restriction, I can't test that the synchronisation works when the sites I visit are on different domains. I can't just make a |
To me the issue is that it is more work to simulate the requests than it is to have Cypress fill in a form. That and it deviates too far from the User flow that my users would be experiencing. |
This is an applicable issue for my organization's test code. We recently implemented OKTA, which requires you to go to a super domain to authenticate then route to the super domain that is going to be tested. When I use "cy.visit()" after authenticating, all authentication data will be wiped out and Cypress will attempt to authenticate the same exact way again, causing either a sid or cross domain error. Due to this issue, we are about to drop Cypress all together and move back to Selenium. |
@alovato88 if you switched to using @MaxwellGBrown we've been down this rabbit hole many times with many different user feedback and the answer is always the same - you can test your login page in isolation away from the main app once, and then use Just visit the OTHER domain in the test and log in. You could even stub the network request if you wanted to prevent the 3rd party server from redirecting you. Once you do that, then use There are many other issues in here in which I've commented providing different approaches and work arounds you all may find useful. Our best practices cover this pretty in depth, and I've even given a talk about this subject and provide real world examples of how to approach this problem.
@ejoubaud You can do this in Cypress - simply visit the domains in different tests, not the same test. As long as you don't visit two different super domains in one test it will all just work. Visit one super domain, test your extension, and then in a separate test visit a different one and test your extension in there. |
@brian-mann Where can I find any of these showcased recipes? |
I think I have a good use case for this. We're migrating from a monolithic Ruby on Rails app, to micro-services on the back-ends and user-type differentiated front-ends. Our new front-ends are React SPAs, and one of them is way too big to replace at once, so for some app routes we just display the original page within an iframe whereas for others we're using React components to render those routes. At present I can't write tests that exercise new and old at the same time. I'm presently working around this by putting every test in its own file, but this is far from ideal. |
We also require this functionality to test a third party integration |
I would love to be able to visit multiple domains. My use case is testing the integration between a front end site and a backend admin. There are certainly ways around not visiting multiple domains, however locally they are only running on different ports (3000, and 3001). There certainly are work arounds:
|
This is an absolute blocker for my current client. They have built a solution that integrates several SaaS systems including Salesforce, and need to be able to test side-effects in integrated systems. For example, that registering in the Web front-end causes a lead to be created in Salesforce. We too will have to abandon Cypress for Selenium, despite significant enthusiasm for Cypress, if this use case can't be addressed. Update: maybe not ... we are able to subsequently test state on the SaaS system in a second context, and reset it in a third. Something of a hack though. |
This comment has been minimized.
This comment has been minimized.
This limitation is a blocker for us as well. I thought I might be able to overcome this limitation by being a bit creative. I tried the following solutions without success: Workaround attempt 1 - Use a custom proxy to remove security headers To solve this I created a proxy which would remove these headers, and passed this proxy to Cypress using environment variables: const fs = require('fs');
const hoxy = require('hoxy');
const hostname = 'localhost';
const port = process.argv[2];
createProxy(hostname, port);
console.log(`Started proxy on ${hostname}:${port}`);
function createProxy(hostname, port) {
const proxy = hoxy
.createServer({
certAuthority: {
key: fs.readFileSync(`${__dirname}/ca/selfsigned-ca.key.pem`),
cert: fs.readFileSync(`${__dirname}/ca/selfsigned-ca.crt.pem`)
}
})
.listen(port, hostname);
proxy.intercept({ phase: 'response' }, removeSecurityHeaders);
}
function removeSecurityHeaders(request, response) {
console.log(request.fullUrl());
delete response.headers['x-frame-options'];
} Passing it to Cypress: Requests where passing through my proxy, but it still didn't work. After a while I found out that only the requests for the AUT where passing though the proxy. Workaround attempt 2 - Load Chrome extension to remove security headers {
"scripts": {
"download-extension": "ced gleekbfjekiniecknbkamfmkohkpodhe extensions/ignore-x-frame-headers"
},
"dependencies": {
"chrome-ext-downloader": "^1.0.4",
}
} And loaded the extension via const path = require('path');
module.exports = (on, config) => {
on('before:browser:launch', (browser = {}, args) => {
console.log(config, browser, args);
if (browser.name === 'chrome') {
const ignoreXFrameHeadersExtension = path.join(__dirname, '../extensions/ignore-x-frame-headers');
args.push(args.push(`--load-extension=${ignoreXFrameHeadersExtension}`));
}
return args;
});
}; With this the external page did load. However, Cypress didn't work on that page. Apparently Cypress uses the proxy to inject itself into the page. Conclusion Now let's discuss this. Things are different when you want to test the integration between different applications. If testing such integrations is the main focus of your tests, you need support for multiple super domains. Often such integrations include more complicated flows such as: Now one can argue that Cypress just isn't meant for use cases like this. Especially if there is a technical limitation which is nearly impossible to overcome. What I am currently missing in this discussion is an explanation on what this technical limitation is. Why does Cypress currently support only one super domain? What would be needed to support multiple? Would implementing that make Cypress a lot more complex? Or would it be, just a lot of work? Related: |
Here's a hacky workaround: Cypress.Commands.add('forceVisit', url => {
cy.get('body').then(body$ => {
const appWindow = body$[0].ownerDocument.defaultView;
const appIframe = appWindow.parent.document.querySelector('iframe');
// We return a promise here because we don't want to
// continue from this command until the new page is
// loaded.
return new Promise(resolve => {
appIframe.onload = () => resolve();
appWindow.location = url;
});
});
}); |
Hi @suchipi this looked like a promising workaround! But unfortunately the x-frame-options issue still remains for us...
|
Tested this with hope it will work, it is already an improvement sa it seems to load the page in the promise but then: Refused to display 'https://****'' in a frame because it set 'X-Frame-Options' to 'sameorigin'. Will watch this post for updates. |
Let me present the use-case I need to visit 2 domains for, to bring in my 2 cents on this issue.
This is the TL;DR version of our onboarding workflow. It is very collaborative between our users and our back-office, and it doesn't make sense to test one without the other. Programming this via Maybe things running on For the record, we tried to use subdomains to address this. It worked fine on developers' environment but it turned out to be very difficult to build in a CI pipeline, in terms of complexity and pipeline time. |
I have this in my cypress.json {
"baseUrl": "https://my-website.com",
"chromeWebSecurity": false
} but I'm still getting this error: CypressError: Cypress detected a cross origin error happened on page load:
Before the page load, you were bound to the origin policy: A cross origin error happens when your application navigates to a new superdomain which does not match the origin policy above. This typically happens in one of three ways:
Cypress does not allow you to change superdomains within a single test. You may need to restructure some of your test code to avoid this problem. Alternatively you can also disable Chrome Web Security which will turn off this restriction by setting { chromeWebSecurity: false } in your 'cypress.json' file. https://on.cypress.io/cross-origin-violation Any ideas as to why explicitly disabling chromeWebSecurity doesn't work? |
Is there a reason cypress suggests changing this config property but it doesn't work? My current workaround is to have separate tests visit the different domains. It works because during dev I have a dummy server keeping up with requests made from the different origins. describe("Do stuff on one site then visit another and check it worked", () => {
it("Can open the first site and do some things", () => {
cy.visit("localhost:8080");
// do stuff that sends data to a dev server running on another port on localhost
});
it("Can see the results in the other place", () => {
cy.visit("localhost:8888");
// validate my things went good
});
}); Test names and descriptions are vague on purpose. It's not best practices since my tests have to be run sequentially and depend on the previous, but it's helped me test my workflow better. |
@jdborneman-terminus have you tried this workaround? It's pretty buried in this thread. You could do something similar to rehydrate your session storage if needed. |
I'm inheriting some Cypress code that I'm trying to get up to snuff and implemented well and see bits and pieces (like save/restoreLocalStorage) already in there, but not in the exact same way. I'll definitely check that out once my meetings for the day are done! Thanks! |
This is an old issue that was blocker to me many times and I had two main situations to act:
The solution I used to work is open a new tab and perform javascript calls directly to this new tab and then close it, keeping cookies that came from there. Feel free to improve this solution. support/index.js Cypress.Commands.add('newTabAction', url => {
cy.wrap(null).then({ timeout: 40000 }, async () => { // timeout enough to perform all steps in the worst (slowest) case
const result = await new Cypress.Promise((resolve, reject) => {
performNewTabAction(resolve, reject, url);
});
expect(result).to.not.eql('error');
});
});
var performNewTabAction = function (resolve, reject, url) {
const newWindow = window.open(url, '_blank');
waitForEl(newWindow, '//input[@data-id="anExample"]', function(element) {
element.click(); // just an example
newWindow.close();
}, function() {
console.error("unable to find element");
resolve('error');
}, 30); // retries
}
var waitForEl = function (win, selectorDispatch, callback, errorCallback, maxTimes) { //implements wait for and call callback or errorCallback}; |
any timeline on when this will be available. This is a turning to be a major blocker for e2e tests for us where user workflow navigated between different domains. E2e tests cannot be isolated in to seperate tests it restricts us from adding retry and using previous context previous steps |
I think this merged PR should provide us the feature of multidomain |
Not working for me. Not sure if its in prod yet. |
It's not released yet. I wonder if it will come with the next minor or major version. |
Either way we won't be able to use it because we can't upgrade cypress before #17759 is fixed. 😭 |
Looks like #20467 is merged, but not released yet https://github.com/cypress-io/cypress/releases? |
Is it going to be included in the next release? 🙏 |
Hey guys, cypress dev here. 👋 This feature will be released at first as an experimental feature so we can gather feedback from you all before it becomes generally available in a future major release. As for timelines, take this with a grain of salt, but we're expecting to release the experimental version within the next couple of months. |
Keenly waiting for this fix for redirection testing in Firefox as we cannot turn off web security on it. |
Hello @mjhenkes, thanks a lot for this great coming feature. cy.origin(validateEmailLink, { args: { validateEmailLink } }, ({ validateEmailLink }) => {
cy.visit(validateEmailLink); // visit cross domain and redirection back to original domain OK but test stuck here with page load timeout error (the page on the original domain is loaded)
cy.get('[data-cy="emailValidated"]').should('be.visible');
}); Is there a solution/workaround to go through this error? |
@AdrienDong, thanks for bringing up this use case! It actually helped us find a bug that we're working on fixing in #21144. Depending on how your redirect is setup, there are two ways to accomplish what you're attempting (once we finish that PR).
// Assume baseUrl is origin A
it('follows the server side redirect', () => {
// This visits origin B but is redirected to origin A in the server so an origin B site
// never loads in the browser. No cy.origin needed!
cy.visit('originB/email/link');
// This validation takes place on an origin A site.
cy.get('[data-cy="emailValidated"]').should('be.visible');
})
// Assume baseUrl is origin A
it('follows the server side redirect', () => {
cy.origin('originB', () => {
// This visits an origin B page that uses javascript to redirect to origin A
cy.visit('originB/email/link');
})
// This validation takes place on an origin A site.
cy.get('[data-cy="emailValidated"]').should('be.visible');
}) Thanks for tying out the new feature! |
The code for this is done in cypress-io/cypress#18075, but has yet to be released. |
Released in This comment thread has been locked. If you are still experiencing this issue after upgrading to |
Hey All, The There is also have a blog post(with video!) that you can read through. |
It feels good to finally close this issue! Great job to everyone who helped close this Issue |
@nbcarey I am taking a look into describe("nordstrom", () => {
it("goes to nordstrom rack", () => {
cy.visit("https://www.nordstrom.com/");
cy.visit("https://www.nordstromrack.com/");
cy.origin("https://www.nordstromrack.com", () => {
// Seem to be running into cookie issues where page reloads? shopping cart is returning a 401 and reloading the page
// which blows away state. Requires further investigation
// automation tests repeated eventually get flagged for bot behavior.
// Eventually the page does become stable
cy.get("#keyword-search-input").clear().type("nordstrom rack shoes");
});
});
}); But seem to be running into possible request/cookie issues with the shopping cart. Have you given |
You could also do an example that is less contrived. Same issues are present. I created #24756 to track this issue separately from the closed meta issue describe("nordstrom", () => {
it("goes to nordstrom rack", () => {
cy.visit("https://www.nordstrom.com/")
// give some time for items on the page to initialize in the SPA
.wait(4000);
cy.get("#keyword-search-input").click().type("nordstrom rack{enter}");
cy.origin("https://www.nordstromrack.com", () => {
// Seem to be running into cookie issues where page reloads? Requires further investigation
// automation tests repeated eventually get flagged for bot behavior
cy.get("#keyword-search-input").clear().type("nordstrom rack shoes");
});
});
}); |
Maybe you could document a way for us to remove this limit without having to open an issue to have the core functioning of the app changed ? Maybe allow passing an argument when running the app.
The text was updated successfully, but these errors were encountered: