I have started testing a react webapp but I didn't go far because I had issues with the login. I am using cypress e2e testing tool.
A welcome page is shown with a button to login, which will redirect you to auth0
service. User is login with email and password , then is redirected back to the webapp with a token.
I tried many different approach each of them resulting in a different problem.
Note: I don't want to test Auth0, I just want to enter in my webapp.
Attempt 1. Clicking on login button
Tried: Cypress should do the same as what the user does, therefore the test will click login button and go to Auth0 and fill in credentials. Problem: Cypress doesn't allow you to navigate to another domain during the test.
Because Cypress changes its own host URL to match that of your applications, it requires that your application remain on the same superdomain for the entirety of a single test.
You are supposed to be able to disable that setting setting "chromeWebSecurity": false
in cypress.json
but it will not work yet because you can only visit a single domain with cy.visit()
Attempt 2. Login programmatically from the test
Tried: login from the cypress test with auth0-js
library so it is not needed to click in login button and thus no domain change occurs.
describe('Waiting to fetch', () => { beforeEach(() => { this.fetchAuthDeferred = getDeferred() cy.visit('http://localhost:3000', { onBeforeLoad(win) { cy.stub(win, 'fetch') .withArgs($url) .as('fetchAuth') .returns(this.fetchAuthDeferred.promise) } }) }) it('login', () => { cy.visit('http://localhost:3000') const auth = new auth0.WebAuth(authOptions) auth.login(loginOptions) cy.get('@fetchAuth', { timeout: 10000 }).should('haveOwnProperty', 'token') cy.visit('http://localhost:3000') cy.get('[class*="hamburger"]').click() }) })
Problems: cy.route()
doesn't wait for fetch request, a workaround is to use cy.stub(win, 'fetch')
. It won't wait:
Attempt 3. Login programmatically from the webapp
Tried: I started to think that cypress only spy request made from the app and not from the test itself (as I tried in the point above).
I added a fake-login button in the welcome page which will call auth0-js
(so no domain change) with hardcoded credentials and click it from the test
cy.get('#fake-login').click()
Problems: that strategy worked, but of course I don't want to add a button with credential in the welcome page. So I tried adding the button element to the webapp during the test:
it('Login adding element', () => { cy.visit('http://localhost:3000') const = document.createElement('div') fakeLogin.innerHTML = 'Fake login' fakeLogin.onclick = function() { const auth = new auth0.WebAuth(authOptions) auth.login(loginOptions) } fakeLogin.style.position = 'absolute' fakeLogin.style.zIndex = 1000 fakeLogin.id = 'fake-login' cy.get('#root').invoke('prepend', fakeLogin) cy.get('#fake-login').click() cy.get('[class*="hamburger"]').click() // Visible when logged in })
And for some reason this doesn't work, the element is added but yt will not wait until the request are made.
So I don't know what else to try. Maybe everything is a misunderstanding of how login should be done in E2E, should I work with mock data so login is not needed?
Cypress can handle prompt pop-up windows, where users can input values. A prompt has a text field, where the input is taken. To handle a prompt pop-up, cy. window() method is used.
This is not currently supported in Cypress. I built a workaround that might help, though.
I set up a simple server that runs in parallel to cypress. The endpoint opens a headless instance of Puppeteer and completes the login flow, responding to the call with all the cookies:
const micro = require("micro"); const puppeteer = require("puppeteer"); const url = require("url"); const login = async (email, password) => { const browser = await puppeteer.launch({ headless: true }); const page = await browser.newPage(); await page.goto("https://my-login-page.com"); // do whatever you have to do to get to your auth0 lock screen, then: await page.waitFor(".auth0-lock-input-email"); await page.waitFor("span.auth0-label-submit"); await page.type(".auth0-lock-input-email input", email); await page.type(".auth0-lock-input-password input", password); await page.click("span.auth0-label-submit"); await page.waitFor("some-selector-on-your-post-auth-page"); return page.cookies(); }; const server = micro(async (req, res) => { // expect request Url of form `http://localhost:3005?email=blahblah&password=blahblah const data = url.parse(req.url, true); const { email, password} = data.query; console.log(`Logging ${email} in.`); return login(email, password); }); server.listen(3005);
Then I just extend Cypress to add the login
command:
Cypress.Commands.add("login", (email, password) => { const reqUrl = `http://localhost:3005?email=${encodeURIComponent( email )}&password=${encodeURIComponent(password)}`; console.log("Beginning login.", reqUrl); cy.request(reqUrl).then(res => { const cookies = res.body; cookies.forEach((c) => { cy.setCookie(c.name, c.value, c); }); }); });
Each call takes ~5-10s, which sucks, but better than not having any auth at all :/
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With