Nostradamus Prophecy Machine
Miss any of our Open RFC calls?Watch the recordings here! »

cypress-social-logins

1.5.5 • Public • Published

cypress-social-logins

cypress authentication flows using social network providers

npm version license downloads build Known Vulnerabilities Security Responsible Disclosure

About

This Cypress library makes it possible to perform third-party logins (think oauth) for services such as GitHub, Google or Facebook.

It does so by delegating the login process to a puppeteer flow that performs the login and returns the cookies for the application under test so they can be set by the calling Cypress flow for the duration of the test.

Support

Supported identity providers:

Provider Plugin name
Google GoogleSocialLogin
GitHub GitHubSocialLogin
Microsoft MicrosoftSocialLogin
Facebook TBD
Twitter TBD
LinkedIn TBD

Usage

  1. Call the declared task with a set of options for the social login flow interaction
  2. Set the cookies for the test flow with the help of Cypress.Cookies.defaults
  3. Copy over all or some (or none) of the local & session storage objects from puppeteer to local instance. Note: If you want to persist localStorage through all tests, see localStorage Troubleshooting below.
cy.clearCookies()
 
return cy.task('GoogleSocialLogin', socialLoginOptions).then(({cookies, lsd, ssd}) => {
  const cookie = cookies.filter(cookie => cookie.name === cookieName).pop()
  if (cookie) {
    cy.setCookie(cookie.name, cookie.value, {
      domain: cookie.domain,
      expiry: cookie.expires,
      httpOnly: cookie.httpOnly,
      path: cookie.path,
      secure: cookie.secure
    })
 
    Cypress.Cookies.defaults({
      preserve: cookieName
    })
  }
 
  // ssd contains session storage data (window.sessionStorage)
  // lsd contains local storage data (window.localStorage)
 
  cy.window().then(window => {
    Object.keys(ssd).forEach(key => window.sessionStorage.setItem(key, ssd[key]))
    Object.keys(lsd).forEach(key => window.localStorage.setItem(key, lsd[key]))
  })
})

Options passed to the task include:

Option name Description Example
username
password
loginUrl The URL for the login page that includes the social network buttons https://www.example.com/login
args string array which allows providing further arguments to puppeteer ['--no-sandbox', '--disable-setuid-sandbox']
headless Whether to run puppeteer in headless mode or not true
logs Whether to log interaction with the loginUrl website & cookie data false
loginSelector A selector on the page that defines the specific social network to use and can be clicked, such as a button or a link 'a[href="/auth/auth0/google-oauth2"]'
postLoginSelector A selector on the post-login page that can be asserted upon to confirm a successful login '.account-panel'
preLoginSelector a selector to find and click on before clicking on the login button (useful for accepting cookies) '.ind-cbar-right button'
loginSelectorDelay delay a specific amount of time before clicking on the login button, defaults to 250ms. Pass a boolean false to avoid completely. 100
getAllBrowserCookies Whether to get all browser cookies instead of just ones with the domain of loginUrl true
isPopup boolean, is your google auth displayed like a popup true
popupDelay number, delay a specific milliseconds before popup is shown. Pass a falsy (false, 0, null, undefined, '') to avoid completely 2000
cookieDelay number, delay a specific milliseconds before get a cookies. Pass a falsy (false, 0, null,undefined,'') to avoid completely 100

Install

Install the plugin as a dependency

npm install --save-dev cypress-social-logins

Import the plugin

Import the cypress-social-logins plugin definition for the specific social network login you are interested of, and declare a task that performs the login.

Example:

const {GoogleSocialLogin} = require('cypress-social-logins').plugins
 
module.exports = (on, config) => {
  on('task', {
    GoogleSocialLogin: GoogleSocialLogin
  })
}

Using the social login

Once the Cypress task is defined we can expose a test case that makes use of it. The task will accept an options object with the username, password and other configurations that need to be specified so that the task can navigate through the page properly.

Once the task has completed it will return the list of cookies from the new page. Most likely that these cookies need to be set for the rest of the sessions in the test flow, hence the example code showing the case for Cypress.Cookies.defaults.

describe('Login', () => {
  it('Login through Google', () => {
    const username = Cypress.env('googleSocialLoginUsername')
    const password = Cypress.env('googleSocialLoginPassword')
    const loginUrl = Cypress.env('loginUrl')
    const cookieName = Cypress.env('cookieName')
    const socialLoginOptions = {
      username,
      password,
      loginUrl,
      headless: true,
      logs: false,
      loginSelector: 'a[href="/auth/auth0/google-oauth2"]',
      postLoginSelector: '.account-panel'
    }
 
    return cy.task('GoogleSocialLogin', socialLoginOptions).then(({cookies}) => {
      cy.clearCookies()
 
      const cookie = cookies.filter(cookie => cookie.name === cookieName).pop()
      if (cookie) {
        cy.setCookie(cookie.name, cookie.value, {
          domain: cookie.domain,
          expiry: cookie.expires,
          httpOnly: cookie.httpOnly,
          path: cookie.path,
          secure: cookie.secure
        })
 
        Cypress.Cookies.defaults({
          preserve: cookieName
        })
      }
    })
  })
})

Defining custom login

When you need to use social logins which aren't supported by this plugin you can make use of the baseLoginConnect() function that is exported as part of the plugin like so:

const { baseLoginConnect } = require('cypress-social-logins').plugins
 
module.exports = (on, config) => {
    on('task', {
        customLogin(options) {
            async function typeUsername({ page, options } = {
            }) {
                await page.waitForSelector('input[id="username"')
                await page.type('input[id="username"', options.username)
            }
 
            async function typePassword({ page, options } = {
            }) {
                await page.waitForSelector('input[id="password"]')
                await page.type('input[id="password"]', options.password)
                await page.click('button[id="_submit"]')
            }
 
            return baseLoginConnect(typeUsername, typePassword, null, options);
        }
    })
}

Troubleshooting

Timeout while trying to enter username

Make sure you are providing the plugin with the username or password in the options when instantiating it. If you're passing it via environment variables then the plugin will look for these two: CYPRESS_googleSocialLoginUsername and CYPRESS_googleSocialLoginPassword

If your application uses popup auth, make sure you are providing isPopup: true configuration parameter.

Failed to launch the browser process

If you're getting an error on a Linux server such as:

Error: Failed to launch the browser process!
[768:768:0423/165641.025850:ERROR:zygote_host_impl_linux.cc(89)] Running as root without --no-sandbox is not supported. See https://crbug.com/638180.
TROUBLESHOOTING:

You should pass the argument --no-sandbox to the plugin as extra arguments.

localStorage isn't persisting through all tests

If you find that lsd is not persisting through tests (useful if you need a JWT from SSO in order to login before each test) using the default implementation above, then you can utilize the package cypress-localstorage-commands (https://www.npmjs.com/package/cypress-localstorage-commands).

To use:

npm install --save-dev cypress-localstorage-commands

import 'cypress-localstorage-commands'
 
before(() => {
  describe('Login through Google', () => {
    const username = Cypress.env('googleSocialLoginUsername')
    const password = Cypress.env('googleSocialLoginPassword')
    const loginUrl = Cypress.env('loginUrl')
    const localStorageItem = Cypress.env('lsdItemName')
    const socialLoginOptions = {
      username,
      password,
      loginUrl,
      headless: true,
      logs: false,
      loginSelector: 'a[href="/auth/auth0/google-oauth2"]',
      postLoginSelector: '.account-panel'
    }
 
    // Clears localStorage prior to getting any new localStorage items
    cy.clearLocalStorageSnapshot()
 
    return cy.task('GoogleSocialLogin', socialLoginOptions).then(({lsd}) => {
      // Check for localStorage item, such as a JWT or similar
      const hasLsd = Object.keys(lsd)
        .filter(item => item === localStorageItem)
        .pop()
 
      if (hasLsd) {
        cy.window().then(() => {
          Object.keys(lsd).forEach(key => {
            cy.setLocalStorage(key, lsd[key])
          })
        })
 
        // Saves a snapshot of localStorage
        cy.saveLocalStorage()
      }
    })
  })
})
 
// Restore the saved localStorage snapshot prior to each test
beforeEach(() => {
  cy.restoreLocalStorage()
})
 
// Save the localStorage snapshot after each test
afterEach(() => {
  cy.saveLocalStorage()
})

Error: module not found: "ws" from file

If you're getting an error message such as:

Error: module not found: "ws" from file ..... node_modules/puppeteer/lib/WebSocketTransport.js #17

It may be due to the fact that you're requiring one of the exported plugin functions, such as GoogleSocialLogin in your spec file in addition to requiring it in cypress/plugins/index.js. Remove it from your spec file.

See discussion about in this issue.

Author

Liran Tal liran.tal@gmail.com

Install

npm i cypress-social-logins

DownloadsWeekly Downloads

2,177

Version

1.5.5

License

Apache-2.0

Unpacked Size

46.7 kB

Total Files

14

Last publish

Collaborators

  • avatar
  • avatar