Testing 如何使用Cypress在E2E测试中登录Auth0?

Testing 如何使用Cypress在E2E测试中登录Auth0?,testing,mocking,auth0,e2e-testing,cypress,Testing,Mocking,Auth0,E2e Testing,Cypress,我已经开始测试react webapp,但我没有做太多,因为我的登录有问题。我正在使用e2e测试工具 将显示一个欢迎页面,其中包含一个登录按钮,该按钮会将您重定向到服务。用户使用电子邮件和密码登录,然后使用令牌重定向回webapp 我尝试了许多不同的方法,每种方法都导致了不同的问题 注意:我不想测试Auth0,我只想在我的webapp中输入 尝试1。单击登录按钮 已尝试:Cypress应执行与用户相同的操作,因此测试将单击登录按钮并转到Auth0并填写凭据。 问题:Cypress不允许您在测试期

我已经开始测试react webapp,但我没有做太多,因为我的登录有问题。我正在使用e2e测试工具

将显示一个欢迎页面,其中包含一个登录按钮,该按钮会将您重定向到服务。用户使用电子邮件和密码登录,然后使用令牌重定向回webapp

我尝试了许多不同的方法,每种方法都导致了不同的问题

注意:我不想测试Auth0,我只想在我的webapp中输入

尝试1。单击登录按钮

已尝试:Cypress应执行与用户相同的操作,因此测试将单击登录按钮并转到Auth0并填写凭据。 问题:Cypress不允许您在测试期间导航到另一个域

您应该能够在
cypress.json
中禁用该设置
“chromeWebSecurity”:false
,但由于

尝试2。以编程方式从测试登录

尝试:使用库从cypress测试登录,因此不需要单击登录按钮,因此不会发生域更改

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()
  })
})
问题:
cy.route()
,需要使用一种解决方法。它不会等待:

尝试3。从webapp以编程方式登录

尝试:我开始认为cypress只是从应用程序发出的间谍请求,而不是从测试本身发出的间谍请求(正如我在上文中尝试的那样)

我在欢迎页面中添加了一个假登录按钮,它将使用硬编码凭据调用
auth0 js
(因此没有域更改),并从测试中单击它

cy.get('#fake-login').click()
问题:这一策略奏效了,但我当然不想在欢迎页面中添加带有凭证的按钮。因此,我在测试期间尝试将button元素添加到webapp:

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
})
由于某些原因,这不起作用,添加了元素,但yt不会等待请求发出

所以我不知道还能尝试什么。
也许一切都是对E2E中应该如何登录的误解,我是否应该使用模拟数据以便不需要登录?

这在Cypress中目前不受支持。不过,我建立了一个可能会有所帮助的变通方法

我设置了一个与cypress并行运行的简单服务器。端点打开Puppeter的无头实例并完成登录流,使用所有cookie响应调用:

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);
然后我只需扩展Cypress以添加
login
命令:

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);
    });
  });
});
每次通话需要5-10秒,这很糟糕,但总比根本没有任何身份验证要好:///p>你可以按照这个来做,但对我来说,它不起作用。我在以下人员的帮助下完成了这项工作:

添加auth0js--dev

让我们创建一个名为
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;
        }
    }
);
  });
要使用它:

    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
    });
});

所有的功劳都归于

我以前曾经在React应用程序中遇到过同样的问题,现在又面临着同样的问题。上次我被迫从高级库迁移到更通用的库,以便为“Cypress方式”(密码授予)和“普通方式”(授权代码授予)获得一个有效的解决方案

当时,我对
auth0 spa js
库的问题是无法将其配置为用作令牌缓存。然而,现在情况已经改变了;同样的库也得到了这样的支持,同样的支持在更高级别的库中提供给了我们(在引擎盖下使用
auth0 spa js
库),这就是我这次使用的库

我的解决方案是将
auth0 react
库配置为在测试或开发模式下使用
localstorage
缓存,同时仍在生产中使用推荐的
内存
缓存:

const{
REACT\u APP\u AUTH0\u域,
REACT\u APP\u AUTH0\u CLIENT\u ID,
对观众作出反应,
节点环境
}=process.env;
ReactDOM.render(
使用localStorage作为缓存位置

这使我们能够有效地模拟
auth0 spa js
库在成功身份验证后将登录信息的JSON对象存储在localStorage中的行为。在“login”Cypress命令中,我们将身份验证请求发送给auth0,并使用响应中的令牌生成“false”放在localStorage中的身份验证对象:

import*作为jwt从“jsonwebtoken”导入;
const env=Cypress.env;
Commands.add(“login”,()=>{
const username=env(“验证用户名”);
cy.log(`Login(${username})`);
常量观众=环境(“授权观众”);
const client_id=env(“auth client id”);
const scope=“openid配置文件电子邮件”;
赛义德请求({
方法:“张贴”,
url:env(“验证url”),
正文:{
授权类型:“密码”,
用户名,
密码:env(“验证密码”),
观众
范围
客户id,
客户机密:env(“验证客户机密”)
}
})。然后({body})=>{
const itemName=`@@auth0spajs@:${client_id}::${viewer}::${scope}`;
const claims=jwt.decode(body.id\u令牌);
常数{
昵称
名称
图片,
更新地址:,
电子邮件,
已验证的电子邮件,
附属的,
经验
}=索赔;
常数项={
正文:{
…身体,
decodedToken:{
声称,
用户:{
昵称
名称
图片,
更新地址:,
电子邮件,
已验证的电子邮件,
附属的
},
观众
客户识别码
}
},
到期日期:exp
};
setItem(itemName,JSON.stringify(item));
});
});

您可能已经看到了这一点,但如果您没有看到,OAuth交互是很难实现的:-这项工作正在积极进行中。您可以按照他们的建议尝试gitter,尽管在撰写本文时,gitter聊天中提问的人比给出ans的人多得多