Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

cypress-firebase

Package Overview
Dependencies
Maintainers
1
Versions
103
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

cypress-firebase

Utilities to help testing Firebase projects with Cypress.

  • 0.11.0-beta
  • Source
  • npm
  • Socket score

Version published
Weekly downloads
19K
increased by3.66%
Maintainers
1
Weekly downloads
 
Created
Source

cypress-firebase

NPM version Build Status License Code Style

Utilities and cli to help testing Firebase projects with Cypress

What?

If you are interested in what drove the need for this checkout the why section

Usage

Pre-Setup

Note: Skip cypress install if it already exists within your project

  1. Install Cypress and add it to your package file: npm i --save-dev cypress
  2. Make sure you have a cypress folder containing cypress tests (or create one by calling cypress open)

Setup

Note: These instructions assume your tests are in the cypress folder (cypress' default). See the folders section below for more info about other supported folders.

  1. Install cypress-firebase and firebase-admin both: npm i cypress-firebase firebase-admin --save-dev

  2. Add the following your custom commands file (cypress/support/commands.js):

    import firebase from 'firebase/app';
    import 'firebase/auth';
    import 'firebase/database';
    import 'firebase/firestore';
    import { attachCustomCommands } from 'cypress-firebase';
    
    const fbConfig = {
      // Your config from Firebase Console
    };
    
    firebase.initializeApp(fbConfig);
    
    attachCustomCommands({ Cypress, cy, firebase })
    
  3. Setup plugin adding following your plugins file (cypress/plugins/index.js):

    const admin = require('firebase-admin')
    const cypressFirebasePlugin = require('cypress-firebase').pluginWithTasks
    
    module.exports = (on, config) => {
      // Pass on function, config, and admin instance. Returns extended config
      return cypressFirebasePlugin(on, config, admin)
    }
    
Old Method (using firebase-tools-extra cli)
  1. Same steps as above but make the following change of import in plugins file (cypress/plugins/index.js):
+ const cypressFirebasePlugin = require('cypress-firebase').plugin
- const cypressFirebasePlugin = require('cypress-firebase').pluginWithTasks
Auth
  1. Log into your Firebase console for the first time.

  2. Go to Auth tab of Firebase and create a user for testing purpose

  3. Get the UID of created account. This will be the account which you use to login while running tests (we will call this UID TEST_UID)

  4. Add the following to your .gitignore:

    serviceAccount.json
    cypress.env.json
    
  5. Go to project setting on firebase console and generate new private key. See how to do here

  6. Save the downloaded file as serviceAccount.json in the root of your project (make sure that it is .gitignored)

  7. Add the UID of the user you created earlier to your cypress environment file (cypress.env.json) when running locally (make sure this is in you .gitignore):

    {
      "TEST_UID": "<- uid of the user you want to test as ->"
    }
    

In CI this will instead be loaded from the TEST_UID environment variable

  1. Pass the UID when logging in: cy.login(Cypress.env('TEST_UID'))

NOTE: If you are running tests within your CI provider you will want to set the SERVICE_ACCOUNT environment variable as the service account object and the TEST_UID environment variable as the UID of your test user

Running

  1. Start your local dev server (usually npm start) - for faster alternative checkout the test built version section
  2. Open cypress test running by running npm run test:open in another terminal window

Docs

CLI Commands

createTestEnvFile {#createTestEnvFile}

Create test environment file (cypress.env.json) which contains custom auth token generated using firebase-admin SDK and serviceAccount.json.

Requirements

A service account must be provided. This can be done by setting serviceAccount.json in the root of the project (often used locally since service accounts should be in gitignore), or by setting the SERVICE_ACCOUNT enviroment variable. For different environmets you can prefix with the environment name such as STAGE_SERVICE_ACCOUNT.

Examples
cypress-firebase createTestEnvFile

Custom Cypress Commands

Table of Contents
cy.login

Login to Firebase auth using FIREBASE_AUTH_JWT environment variable which is generated using firebase-admin authenticated with serviceAccount during build:testConfig phase.

Examples
cy.login()
cy.logout

Log out of Firebase instance

Examples
cy.logout()
cy.callRtdb

Call Real Time Database path with some specified action. Authentication is through FIREBASE_TOKEN since firebase-tools is used (instead of firebaseExtra).

Parameters
  • action String The action type to call with (set, push, update, remove)
  • actionPath String Path within RTDB that action should be applied
  • opts object Options
    • opts.limitToFirst number|boolean Limit to the first <num> results. If true is passed than query is limited to last 1 item.
    • opts.limitToLast number|boolean Limit to the last <num> results. If true is passed than query is limited to last 1 item.
    • opts.orderByKey boolean Order by key name
    • opts.orderByValue boolean Order by primitive value
    • opts.orderByChild string Select a child key by which to order results
    • opts.equalTo string Restrict results to <val> (based on specified ordering)
    • opts.startAt string Start results at <val> (based on specified ordering)
    • opts.endAt string End results at <val> (based on specified ordering)
    • opts.instance string Use the database <instance>.firebaseio.com (if omitted, use default database instance)
    • opts.args Array Command line args to be passed
Examples

Set data

const fakeProject = { some: 'data' }
cy.callRtdb('set', 'projects/ABC123', fakeProject)

Set Data With Meta

const fakeProject = { some: 'data' }
// Adds createdAt and createdBy (current user's uid) on data
cy.callRtdb('set', 'projects/ABC123', fakeProject, { withMeta: true })

Get/Verify Data

cy.callRtdb('get', 'projects/ABC123')
  .then((project) => {
    // Confirm new data has users uid
    cy.wrap(project)
      .its('createdBy')
      .should('equal', Cypress.env('TEST_UID'))
  })

Other Args

const opts = { args: ['-d'] }
const fakeProject = { some: 'data' }
cy.callRtdb('update', 'project/test-project', fakeProject, opts)
cy.callFirestore

Call Firestore instance with some specified action. Authentication is through serviceAccount.json since it is at the base level. If using delete, auth is through FIREBASE_TOKEN since firebase-tools is used (instead of firebaseExtra).

Parameters
  • action String The action type to call with (set, push, update, remove)
  • actionPath String Path within RTDB that action should be applied
  • opts Object Options
    • opts.args Array Command line args to be passed
Examples

Basic

cy.callFirestore('set', 'project/test-project', 'fakeProject.json')

Recursive Delete

const opts = { recursive: true }
cy.callFirestore('delete', 'project/test-project', opts)

Other Args

const opts = { args: ['-r'] }
cy.callFirestore('delete', 'project/test-project', opts)

Full


describe('Test firestore', () => {
  const TEST_UID = Cypress.env('TEST_UID');
  const mockAge = 8;

  beforeEach(() => {
    cy.visit('http://localhost:4200');
  });

  it('read/write test', () => {
    cy.log('Starting test');

    cy.callFirestore('set', `testCollection/${TEST_UID}`, {
      name: 'axa',
      age: 8,
    });
    cy.callFirestore('get', `testCollection/${TEST_UID}`).then(r => {
      cy.wrap(r[0])
        .its('id')
        .should('equal', TEST_UID);
      cy.wrap(r[0])
        .its('data.age')
        .should('equal', mockAge);
    });
    cy.log('Ended test');
  });
});

Recipes

Using Database Emulators

  1. Install cross-env for cross system environment variable support: npm i --save-dev cross-env

  2. Add the following to the scripts section of your package.json:

    "emulators": "firebase emulators:start --only database,firestore",
    "test": "cross-env CYPRESS_baseUrl=http://localhost:3000 cypress run",
    "test:open": "cross-env CYPRESS_baseUrl=http://localhost:3000 cypress open",
    "test:emulate": "cross-env FIREBASE_DATABASE_EMULATOR_HOST=\"localhost:$(cat firebase.json | jq .emulators.database.port)\" FIRESTORE_EMULATOR_HOST=\"localhost:$(cat firebase.json | jq .emulators.firestore.port)\" yarn test:open"
    
  3. Add support in your application for connecting to the emulators:

    const shouldUseEmulator = window.location.hostname === 'localhost' // or other logic to determine when to use
    // Emulate RTDB
    if (shouldUseEmulator) {
      console.log('Using RTDB emulator')
      fbConfig.databaseURL = `http://localhost:9000?ns=${fbConfig.projectId}`
    }
    
    // Initialize Firebase instance
    firebase.initializeApp(fbConfig)
    
    // Emulate Firestore
    if (shouldUseEmulator) {
      console.log('Using Firestore emulator')
      const firestoreSettings = {
        host: 'localhost:8080',
        ssl: false,
      };
    
      // Pass long polling setting to Firestore when running in Cypress
      if (window.Cypress) {
        // Needed for Firestore support in Cypress (see https://github.com/cypress-io/cypress/issues/6350)
        firestoreSettings.experimentalForceLongPolling = true;
      }
    
      firebase.firestore().settings(firestoreSettings)
    }
    
  4. Make sure you also have matching init logic in cypress/support/commands.js or cypress/support/index.js:

    import firebase from 'firebase/app';
    import 'firebase/auth';
    import 'firebase/database';
    import 'firebase/firestore';
    import { attachCustomCommands } from 'cypress-firebase';
    
    const fbConfig = {
      // Your Firebase Config
    }
    
    // Emulate RTDB if Env variable is passed
    const rtdbEmulatorHost = Cypress.env('FIREBASE_DATABASE_EMULATOR_HOST')
    if (rtdbEmulatorHost) {
      fbConfig.databaseURL = `http://${rtdbEmulatorHost}?ns=${fbConfig.projectId}`
    }
    
    firebase.initializeApp(fbConfig);
    
    // Emulate Firestore if Env variable is passed
    const firestoreEmulatorHost = Cypress.env('FIRESTORE_EMULATOR_HOST')
    if (firestoreEmulatorHost) {
      firebase.firestore().settings({
        host: firestoreEmulatorHost,
        ssl: false
      })
    }
    
    attachCustomCommands({ Cypress, cy, firebase })
    
  5. Start emulators: npm run emulators

  6. In another terminal window, start the application: npm start

  7. In another terminal window, open test runner with emulator settings: npm run test:emulate

NOTE: If you are using react-scripts or other environment management, you can use environment variables to pass settings into your app:

const { REACT_APP_FIREBASE_DATABASE_EMULATOR_HOST, REACT_APP_FIRESTORE_EMULATOR_HOST } = process.env
// Emulate RTDB if REACT_APP_FIREBASE_DATABASE_EMULATOR_HOST exists in environment
if (REACT_APP_FIREBASE_DATABASE_EMULATOR_HOST) {
  console.log('Using RTDB emulator')
  fbConfig.databaseURL = `http://${REACT_APP_FIREBASE_DATABASE_EMULATOR_HOST}?ns=${fbConfig.projectId}`
}

// Initialize Firebase instance
firebase.initializeApp(fbConfig)

// Emulate RTDB if REACT_APP_FIRESTORE_EMULATOR_HOST exists in environment
if (REACT_APP_FIRESTORE_EMULATOR_HOST) {
  console.log('Using Firestore emulator')
  const firestoreSettings = {
    host: REACT_APP_FIRESTORE_EMULATOR_HOST,
    ssl: false,
  };

  if (window.Cypress) {
    // Needed for Firestore support in Cypress (see https://github.com/cypress-io/cypress/issues/6350)
    firestoreSettings.experimentalForceLongPolling = true;
  }

  firebase.firestore().settings(firestoreSettings)
}

Generate JWT Before Run

  1. Add the following to the scripts section of your package.json:

    "build:testConfig": "cypress-firebase createTestEnvFile",
    "test": "npm run build:testConfig && cypress run",
    "test:open": "npm run build:testConfig && cypress open",
    
  2. Add your config info to your environment variables (for CI) or cypress.env.json when running locally (make sure this is in you .gitignore)

    {
      "TEST_UID": "<- uid of the user you want to test as ->"
    }
    

Testing Different Environments

Environment variables can be passed through --env. envName points to the firebase project within the projects section of .firebaserc.

Test Built Version

Tests will run faster locally if you tests against the build version of your app instead of your dev version (with hot module reloading and other dev tools). You can do that by:

  1. Adding the following npm script:

    "start:dist": "npm run build && firebase serve --only hosting -p 3000",
    
  2. Run npm run start:dist to build your app and serve it with firebase

  3. In another terminal window, run a test command such as npm run test:open

CI

  1. Run firebase login:ci to generate a CI token for firebase-tools (this will give your cy.callRtdb and cy.callFirestore commands admin access to the DB)
  2. Set FIREBASE_TOKEN within CI environment variables

Examples

Github Actions

Separate Install

name: Test Build

on: [pull_request]

jobs:
  ui-tests:
    name: UI Tests
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Repo
        uses: actions/checkout@v1

      # Install is run separatley from test so that dependencies are available
      # for other steps
      - name: Install Dependencies
        uses: cypress-io/github-action@v1
        with:
          # just perform install
          runTests: false

      - name: Build Test Environment Config
        env:
          FIREBASE_TOKEN: ${{ secrets.FIREBASE_TOKEN }}
          TEST_UID: ${{ secrets.TEST_UID }}
          SERVICE_ACCOUNT: ${{ secrets.SERVICE_ACCOUNT }}
          GITHUB_HEAD_REF: ${{ github.head_ref }}
          GITHUB_REF: ${{ github.ref }}
        run: |
          $(npm bin)/cypress-firebase createTestEnvFile $TEST_ENV

      # Cypress action manages installing/caching npm dependencies and Cypress binary.
      - name: Cypress Run
        uses: cypress-io/github-action@v1
        with:
          # we have already installed all dependencies above
          install: false
          group: 'E2E Tests'
        env:
          # pass the Dashboard record key as an environment variable
          CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_KEY }}
          FIREBASE_TOKEN: ${{ secrets.FIREBASE_TOKEN }}
          GITHUB_HEAD_REF: ${{ github.head_ref }}
          GITHUB_REF: ${{ github.ref }}

Using Start For Local

name: Test Hosted

on: [pull_request]

jobs:
  ui-tests:
    name: UI Tests
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Repo
        uses: actions/checkout@v1

      # Install is run separatley from test so that dependencies are available
      # for other steps
      - name: Install Dependencies
        uses: cypress-io/github-action@v1
        with:
          # just perform install
          runTests: false

      - name: Build Test Environment Config
        env:
          FIREBASE_TOKEN: ${{ secrets.FIREBASE_TOKEN }}
          TEST_UID: ${{ secrets.TEST_UID }}
          SERVICE_ACCOUNT: ${{ secrets.SERVICE_ACCOUNT }}
          GITHUB_HEAD_REF: ${{ github.head_ref }}
          GITHUB_REF: ${{ github.ref }}
        run: |
          $(npm bin)/cypress-firebase createTestEnvFile $TEST_ENV

      # Cypress action manages installing/caching npm dependencies and Cypress binary.
      - name: Cypress Run
        uses: cypress-io/github-action@v1
        with:
          # we have already installed all dependencies above
          install: false
          group: 'E2E Tests'
          start: npm start
          wait-on: http://localhost:3000
        env:
          # pass the Dashboard record key as an environment variable
          CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_KEY }}
          FIREBASE_TOKEN: ${{ secrets.FIREBASE_TOKEN }}
          GITHUB_REF: ${{ github.head_ref }}

Why?

It isn't currently possible to use Firebase's firebase-admin SDK directly within Cypress due to dependencies not being able to be loaded into the Browser environment. Since firebase-admin is nessesary to generate custom token needed to login to Firebase, the usage of it happens outside of Cypress (through cypress-firebase createTestEnvFile) before booting up.

Instead of a cli tool, the plugin that is included could maybe use firebase-admin (since cypress plugins is a node environment) - when investigating this, I found it frustrating to get the values back into the test. That said, always open to better ways of solving this, so please reach out with your ideas!

Projects Using It

fireadmin.io - A Firebase project management tool (here is the source)

Roadmap

  • Fix issue where auth token goes bad after test suite has been open a long time

Keywords

FAQs

Package last updated on 09 Mar 2020

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc