问题
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?
回答1:
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 :/
回答2:
You can follow this article though for me it didn't work. I managed it work with the help of this article:
yarn add auth0-js --dev
Let's create a custom command called loginAsAdmin:
Cypress.Commands.add('loginAsAdmin', (overrides = {}) => {
Cypress.log({
name: 'loginAsAdminBySingleSignOn'
});
const webAuth = new auth0.WebAuth({
domain: 'my-super-duper-domain.eu.auth0.com', // Get this from https://manage.auth0.com/#/applications and your application
clientID: 'myclientid', // Get this from https://manage.auth0.com/#/applications and your application
responseType: 'token id_token'
});
webAuth.client.login(
{
realm: 'Username-Password-Authentication',
username: 'mytestemail@something.co.uk',
password: 'SoVeryVeryVery$ecure',
audience: 'myaudience', // Get this from https://manage.auth0.com/#/apis and your api, use the identifier property
scope: 'openid email profile'
},
function(err, authResult) {
// Auth tokens in the result or an error
if (authResult && authResult.accessToken && authResult.idToken) {
const token = {
accessToken: authResult.accessToken,
idToken: authResult.idToken,
// Set the time that the access token will expire at
expiresAt: authResult.expiresIn * 1000 + new Date().getTime()
};
window.sessionStorage.setItem('my-super-duper-app:storage_token', JSON.stringify(token));
} else {
console.error('Problem logging into Auth0', err);
throw err;
}
}
);
});
To use it:
describe('access secret admin functionality', () => {
it('should be able to navigate to', () => {
cy.visitHome()
.loginAsAdmin()
.get('[href="/secret-adminny-stuff"]') // This link should only be visible to admins
.click()
.url()
.should('contain', 'secret-adminny-stuff/'); // non-admins should be redirected away from this url
});
});
All credit goes to Johnny Reilly
来源:https://stackoverflow.com/questions/51208998/how-to-login-in-auth0-in-an-e2e-test-with-cypress