0
votes

I'm working on a project in which a user can log in using whether a phone number, using Google, or using Github. I use Firebase for authentication. I'm currently adding integration tests using Cypress. My problem is I can't properly test that the user can link or unlink a provider. To link a provider I use linkWithRedirect() firebase method.
At first, I tried to just simulate a button click. But this didn't work because Cypress doesn't allow visiting multiple domains in one test.
My next attempt was to use linkWithCredential() method. I get cookies and the provider's auth URL from the developer tools to get authorization code and use this code to get access token.
My Cypress command to get Google token:

Cypress.Commands.add(
  'getGoogleToken',
  () => {
    cy.request({
      url: Cypress.env("GOOGLE_AUTH_URL"),
      headers: {
        cookie: Cypress.env("GOOGLE_AUTH_COOKIE")
      }, 
      followRedirect: false
    })
    .its("redirectedToUrl")
    .then(redirectedToUrl => {
      const url = new URL(redirectedToUrl);
      const code = url.searchParams.get("code");
      cy.request({
        url: "https://www.googleapis.com/oauth2/v4/token",
        method: "POST",
        form: true,
        body: {
          code,
          redirect_uri: Cypress.env("GOOGLE_REDIRECT_URI"),
          client_id: Cypress.env("GOOGLE_CLIENT_ID"),
          client_secret: Cypress.env("GOOGLE_CLIENT_SECRET"),
          grant_type: "authorization_code"
        }
      })
      .its("body")
      .then(body => body.access_token);
    });
});

Using this token I call my own linkWithGoogleWithToken method which calls linkWithCredential method:

Cypress.Commands.add(
  'linkWithGoogle', 
  () => {
    cy.getGoogleToken()
      .then(token => {
        cy.get("@auth")
          .invoke("linkWithGoogleWithToken", token);
      });
});

In my tests, I call several functions to link one provider, then link another provider, then unlink provider, and so on. Sometimes this command works as expected, but sometimes it remains in the pending state forever. I can't find why. Searching doesn't help, it seems like no one has encountered this problem.
So, I am looking for ways to link OAuth provider programmatically, not using UI. I'm considering Firebase Admin SDK, but I can't find the way to link providers in its documentation. I would appreciate any help from anyone who was encountering this issue.

1

1 Answers

0
votes

I found out why I was having this problem. The point is that after visiting the page, I add an alias to refer to my firebase functions:

Cypress.Commands.add(
  'init',
  () => {
    cy.visit("/");
    cy.window()
      .its("firebase")
      .its("auth").as("auth");
    cy.nullifyUsername();
});

The first time when I called the invoke method, everything worked fine. Then I reloaded the page because I need to update the page with this information.

cy.linkGoogle();
    cy.reload();
    cy.get("@googleButton")
      .contains(/^unlink$/i);

And after that, the next time I tried to link another provider the invoke method remained in the pending state. That happened because after reloading the page, my alias was referred to a function that was no longer exists. My solution is to add alias after reloading the page. So, I created custom command for this:

Cypress.Commands.add(
  'refresh',
  () => {
    cy.reload();
    cy.window()
      .its("firebase")
      .its("auth").as("auth");
});