web-dev-qa-db-fra.com

Comment se connecter en Auth0 dans un test E2E avec Cypress?

J'ai commencé à tester une webapp React mais je ne suis pas allé loin car j'ai eu des problèmes de connexion. J'utilise cypress outil de test e2e.

Une page d'accueil s'affiche avec un bouton pour vous connecter, qui vous redirigera vers le service auth0 . L'utilisateur est connecté avec e-mail et mot de passe, puis est redirigé vers la webapp avec un jeton.

J'ai essayé de nombreuses approches différentes, chacune d'elles entraînant un problème différent.

Remarque: je ne veux pas tester Auth0, je veux juste entrer dans ma webapp.

Tentative 1. Cliquer sur le bouton de connexion

Testé: Cypress devrait faire la même chose que ce que fait l'utilisateur, par conséquent, le test cliquera sur le bouton de connexion et ira à Auth0 et remplira les informations d'identification. Problème: Cypress ne vous permet pas de naviguer vers un autre domaine pendant le test.

Parce que Cypress modifie sa propre URL d'hôte pour correspondre à celle de vos applications, il nécessite que votre application reste sur le même superdomaine pendant l'intégralité d'un seul test.

Vous êtes censé pouvoir désactiver ce paramètre "chromeWebSecurity": false Dans cypress.json Mais cela ne fonctionnera pas encore parce que vous ne pouvez visiter qu'un seul domaine avec cy.visit()

Tentative 2. Connectez-vous par programme à partir du test

Testé: connectez-vous à partir du test de cyprès avec la bibliothèque auth0-js donc il n'est pas nécessaire de cliquer sur le bouton de connexion et donc aucun changement de domaine ne se produit.

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

Problèmes: cy.route()n'attend pas la demande de récupération , une solution de contournement consiste à utiliser cy.stub(win, 'fetch') . Cela n'attendra pas:

enter image description here

Tentative 3. Connectez-vous par programme à partir de la webapp

Essayé: J'ai commencé à penser que la demande d'espionnage de cyprès n'était faite qu'à partir de l'application et non à partir du test lui-même (comme j'ai essayé dans le point ci-dessus).

J'ai ajouté un bouton fake-login dans la page d'accueil qui appellera auth0-js (Donc pas de changement de domaine) avec des informations d'identification codées en dur et cliquez dessus depuis le test

cy.get('#fake-login').click()

Problèmes: cette stratégie a fonctionné, mais bien sûr, je ne veux pas ajouter un bouton avec des informations d'identification dans la page d'accueil. J'ai donc essayé d'ajouter l'élément bouton à la webapp pendant le 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
})

Et pour une raison quelconque, cela ne fonctionne pas, l'élément est ajouté mais yt n'attendra pas que la demande soit faite.

Je ne sais donc pas quoi essayer d'autre. Peut-être que tout est une mauvaise compréhension de la façon dont la connexion doit être effectuée dans E2E, dois-je travailler avec des données fictives pour que la connexion ne soit pas nécessaire?

15
Mikel

Ce n'est pas actuellement pris en charge dans Cypress. J'ai construit une solution de contournement qui pourrait aider, cependant.

J'ai mis en place un serveur simple qui fonctionne en parallèle de cyprès. Le point de terminaison ouvre une instance sans tête de Puppeteer et termine le flux de connexion, répondant à l'appel avec tous les 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);

Ensuite, je viens d'étendre Cypress pour ajouter la commande 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);
    });
  });
});

Chaque appel prend environ 5 à 10 secondes, ce qui est nul, mais mieux que de ne pas avoir d'authentification: /

2
Brandon

Vous pouvez suivre cela article mais pour moi cela n'a pas fonctionné. J'ai réussi à le faire fonctionner avec l'aide de cet article :

fil ajouter auth0-js --dev

Créons une commande personnalisée appelée 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: '[email protected]',
        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;
        }
    }
);
  });

Pour l'utiliser:

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

Tout crédit revient à Johnny Reilly

0
Ruhul Amin