diff --git a/README.md b/README.md index 333d851d..f53c991e 100644 --- a/README.md +++ b/README.md @@ -24,47 +24,15 @@ If you are interested in what drove the need for this checkout [the why section] **Note**: Skip cypress install if it already exists within your project -1. Log into your Firebase console for the first time. -1. Go to Auth tab of Firebase and create a user for testing purpose -1. 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`) -1. Go to project setting on firebase console and generate new private key. See how to do [here](https://sites.google.com/site/scriptsexamples/new-connectors-to-google-services/firebase/tutorials/authenticate-with-a-service-account) -1. Save the downloaded file as `serviceAccount.json` in the root of your project (for local dev) -1. Set service account as the `SERVICE_ACCOUNT` environment variable within your CI 1. Install Cypress and add it to your package file: `npm i --save-dev cypress` -1. Add cypress folder by calling `cypress open` -1. Add the following to your `.gitignore`: - ``` - serviceAccount.json - cypress.env.json - ``` +1. 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](#folders) for more info about other supported folders. -1. Make sure you have `firebase-tools` installed (globally and within project). It is used to call to database when using `cy.callRtdb` and `cy.callFirestore`. 1. Install using `npm i cypress-firebase --save-dev` -1. Add the following to the `scripts` section of your `package.json`: - - ```json - "build:testConfig": "cypress-firebase createTestEnvFile", - "test": "npm run build:testConfig && cypress run", - "test:open": "npm run build:testConfig && cypress open", - "test:stage": "npm run test -- --env envName=stage", - "test:open:stage": "npm run test:open -- --env envName=stage" - ``` - - Environment variables can be passed through `--env`. `envName` points to the firebase project within the projects section of `.firebaserc`. - -1. Add your config info to your environment variables (for CI) or `cypress.env.json` when running locally (make sure this is in you `.gitignore`) - - ```js - { - "TEST_UID": "<- uid of the user you want to test as ->", - "FIREBASE_API_KEY": "<- browser apiKey of your project ->" - } - ``` - +1. Make sure you have `firebase-tools` installed (globally and within project). It is used to call to database when using `cy.callRtdb` and `cy.callFirestore`. 1. Add the following your custom commands file (`cypress/support/commands.js`): ```js @@ -78,7 +46,7 @@ If you are interested in what drove the need for this checkout [the why section] // Your config from Firebase Console }; - window.fbInstance = firebase.initializeApp(fbConfig); + firebase.initializeApp(fbConfig); attachCustomCommands({ Cypress, cy, firebase }) ``` @@ -98,166 +66,39 @@ If you are interested in what drove the need for this checkout [the why section] ``` The plugin sets `baseUrl` and loads config from `.firebaserc` -1. Add support for loading the authed Firebase instance (passed through `window.fbInstance`) within your application: +1. If you plan to authenticate the user in your tests, continue to the next section which covers Auth - ```js - // Import parts of Firebase you are using - import firebase from 'firebase/app' - import 'firebase/auth' - import 'firebase/firestore' - import 'firebase/database' +#### Auth - const fbConfig = { - // Your config from Firebase Console - }; - - // Initialize Firebase only if an fbInstance was not passed to the window (tests) - if (!window.fbInstance) { - firebase.initializeApp(config.firebase) +1. Log into your Firebase console for the first time. +1. Go to Auth tab of Firebase and create a user for testing purpose +1. 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`) +1. Add the following to your `.gitignore`: + ``` + serviceAccount.json + cypress.env.json + ``` +1. Go to project setting on firebase console and generate new private key. See how to do [here](https://sites.google.com/site/scriptsexamples/new-connectors-to-google-services/firebase/tutorials/authenticate-with-a-service-account) +1. Save the downloaded file as `serviceAccount.json` in the root of your project (make sure that it is .gitignored) +1. 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`): + + ```js + { + "TEST_UID": "<- uid of the user you want to test as ->" } - - // Make sure to use the instance from the window if it exists - const fbInstance = window.fbInstance || firebase ``` + + 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](#test-built-version) 1. Open cypress test running by running `npm run test:open` in another terminal window -#### 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: - - ```json - "start:dist": "npm run build && firebase serve --only hosting -p 3000", - ``` - -1. Run `npm run start:dist` to build your app and serve it with firebase -1. 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) -1. Set `FIREBASE_TOKEN` within CI environment variables - -#### Github Actions Examples - -**Separate Install** - -```yml -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** - -```yml -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 }} -``` - -### Folders - -`cypress` is the default folder where config is loaded from, but you can use another folder by specifiying a different setting for the `integrationFolder` parameter in `cypress.json`: - -```json -{ - "projectId": "<- your project id ->", - "fixturesFolder": "test/e2e/fixtures", - "integrationFolder": "test/e2e/integration", - "pluginsFile": "test/e2e/plugins/index.js", - "screenshotsFolder": "test/e2e/screenshots", - "videosFolder": "test/e2e/videos", - "supportFile": "test/e2e/support/index.js" -} -``` - ## Docs ### CLI Commands @@ -436,6 +277,149 @@ describe('Test firestore', () => { }); ``` +## Recipes + +### Generate JWT Before Run + +1. Add the following to the `scripts` section of your `package.json`: + + ```json + "build:testConfig": "cypress-firebase createTestEnvFile", + "test": "npm run build:testConfig && cypress run", + "test:open": "npm run build:testConfig && cypress open", + ``` +1. Add your config info to your environment variables (for CI) or `cypress.env.json` when running locally (make sure this is in you `.gitignore`) + + ```js + { + "TEST_UID": "<- uid of the user you want to test as ->", + "FIREBASE_API_KEY": "<- browser apiKey of your project ->" + } + ``` + +### 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: + + ```json + "start:dist": "npm run build && firebase serve --only hosting -p 3000", + ``` + +1. Run `npm run start:dist` to build your app and serve it with firebase +1. 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) +1. Set `FIREBASE_TOKEN` within CI environment variables + +## Examples + +### Github Actions + +**Separate Install** + +```yml +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** + +```yml +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. diff --git a/examples/basic/cypress/integration/Projects.spec.js b/examples/basic/cypress/integration/Projects.spec.js index 2ed742c2..18c2ad61 100644 --- a/examples/basic/cypress/integration/Projects.spec.js +++ b/examples/basic/cypress/integration/Projects.spec.js @@ -1,24 +1,16 @@ describe('Projects View', () => { describe('when authenticated', () => { before(() => { - // Login using custom token - cy.log('Calling login') + // Use cy.setRtdb() to set projects created by authed user + cy.callRtdb('push', 'projects', { name: 'project 1' }, { withMeta: true }) + cy.callRtdb('push', 'projects', { name: 'project 3' }, { withMeta: true }) cy.login(Cypress.env('TEST_UID')); - // TODO: Use cy.setRtdb() to set projects created by authed user - // cy.callFirestore('add', 'projects', { name: 'project 1'}) - // cy.callRtdb('set', 'projects/asdf123', { name: 'project 1'}) // Go to home page cy.visit('/'); }); - after(() => { - // TODO: Use cy.setRtdb() to set projects created by authed user - // cy.callRtdb('remove') - }) - it('Shows projects if logged in', () => { cy.get('[data-test=projects]').should('exist') }); - }) - + }); }); diff --git a/examples/basic/cypress/support/commands.js b/examples/basic/cypress/support/commands.js index 1a3141e6..26d8cbc0 100644 --- a/examples/basic/cypress/support/commands.js +++ b/examples/basic/cypress/support/commands.js @@ -4,21 +4,31 @@ import 'firebase/database'; import 'firebase/firestore'; import { attachCustomCommands } from 'cypress-firebase'; + const fbConfig = { apiKey: "AIzaSyCTUERDM-Pchn_UDTsfhVPiwM4TtNIxots", authDomain: "redux-firebasev3.firebaseapp.com", - // databaseURL: "https://redux-firebasev3.firebaseio.com", - datbaseURL: `http://localhost:9000?ns=redux-firebasev3`, + databaseURL: "https://redux-firebasev3.firebaseio.com", projectId: "redux-firebasev3", storageBucket: "redux-firebasev3.appspot.com", messagingSenderId: "823357791673" } -window.fbInstance = firebase.initializeApp(fbConfig); +// Emulate RTDB if Env variable is passed +const rtdbEmulatorHost = Cypress.env('FIREBASE_DATABASE_EMULATOR_HOST') +if (rtdbEmulatorHost) { + fbConfig.databaseURL = `http://${rtdbEmulatorHost}?ns=redux-firebasev3` +} + +firebase.initializeApp(fbConfig); -firebase.firestore().settings({ - host: 'localhost:8080', - ssl: false -}) +// 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 }) \ No newline at end of file +attachCustomCommands({ Cypress, cy, firebase }) diff --git a/examples/basic/firebase.json b/examples/basic/firebase.json new file mode 100644 index 00000000..809d652f --- /dev/null +++ b/examples/basic/firebase.json @@ -0,0 +1,20 @@ +{ + "hosting": { + "public": "dist", + "ignore": ["firebase.json", "**/.*", "**/node_modules/**"], + "rewrites": [ + { + "source": "**", + "destination": "/index.html" + } + ] + }, + "database": { + "rules": "database.rules.json" + }, + "emulators": { + "database": { + "port": 9000 + } + } +} diff --git a/examples/basic/package.json b/examples/basic/package.json index 3cce77ee..698e78e2 100644 --- a/examples/basic/package.json +++ b/examples/basic/package.json @@ -3,13 +3,14 @@ "version": "0.1.0", "scripts": { "start": "react-scripts start", - "emulate": "cross-env REACT_APP_USE_DB_EMULATORS=true yarn start", + "dev": "cross-env REACT_APP_USE_DB_EMULATORS=true yarn start", + "emulators": "firebase emulators:start --only database", "build": "react-scripts build", "eject": "react-scripts eject", "build:testConfig": "cypress-firebase createTestEnvFile", - "test": "npm run build:testConfig && cross-env CYPRESS_baseUrl=http://localhost:3000 cypress run", - "test:ui": "npm run build:testConfig && cross-env CYPRESS_baseUrl=http://localhost:3000 cypress open", - "test:emulate": "npm run build:testConfig && cross-env CYPRESS_baseUrl=http://localhost:3000 cypress open" + "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)\" yarn test:open" }, "dependencies": { "firebase": "^7.8.0", @@ -18,7 +19,7 @@ "react-dom": "^16.12.0" }, "devDependencies": { - "cross-env": "^5.2.0", + "cross-env": "^7.0.0", "cypress": "^4.0.1", "cypress-firebase": "*", "eslint-plugin-cypress": "^2.0.1", diff --git a/examples/basic/src/App.js b/examples/basic/src/App.js index c9e5813e..c506de99 100644 --- a/examples/basic/src/App.js +++ b/examples/basic/src/App.js @@ -1,7 +1,7 @@ import React from 'react'; import initFirebase from './initFirebase' import RTDBProjects from './RTDBProjects' -import FirestoreProjects from './FirestoreProjects' +// import FirestoreProjects from './FirestoreProjects' import './App.css'; initFirebase() @@ -12,8 +12,12 @@ function App() {

Data From RTDB

-

Data From Firestore

- + {/* Skipped since emulated Firestore does not + currently work with Cypress. + See: https://github.com/cypress-io/cypress/issues/6350 + */} + {/*

Data From Firestore

+ */}
) diff --git a/examples/basic/yarn.lock b/examples/basic/yarn.lock index b824097f..846c4616 100644 --- a/examples/basic/yarn.lock +++ b/examples/basic/yarn.lock @@ -4446,7 +4446,7 @@ create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4: safe-buffer "^5.0.1" sha.js "^2.4.8" -cross-env@^5.1.3, cross-env@^5.2.0: +cross-env@^5.1.3: version "5.2.0" resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-5.2.0.tgz#6ecd4c015d5773e614039ee529076669b9d126f2" integrity sha512-jtdNFfFW1hB7sMhr/H6rW1Z45LFqyI431m3qU6bFXcQ3Eh7LtBuG3h74o7ohHZ3crrRkkqHlo4jYHFPcjroANg== @@ -4454,7 +4454,14 @@ cross-env@^5.1.3, cross-env@^5.2.0: cross-spawn "^6.0.5" is-windows "^1.0.0" -cross-spawn@7.0.1, cross-spawn@^7.0.0: +cross-env@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-7.0.0.tgz#5a3b2ddce51ec713ea58f2fb79ce22e65b4f5479" + integrity sha512-rV6M9ldNgmwP7bx5u6rZsTbYidzwvrwIYZnT08hSGLcQCcggofgFW+sNe7IhA1SRauPS0QuLbbX+wdNtpqE5CQ== + dependencies: + cross-spawn "^7.0.1" + +cross-spawn@7.0.1, cross-spawn@^7.0.0, cross-spawn@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.1.tgz#0ab56286e0f7c24e153d04cc2aa027e43a9a5d14" integrity sha512-u7v4o84SwFpD32Z8IIcPZ6z1/ie24O6RU3RbtL5Y316l3KuHVPx9ItBgWQ6VlfAFnRnTtMUrsQ9MUUTuEZjogg== diff --git a/package.json b/package.json index caf488c6..7a44b89a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cypress-firebase", - "version": "0.10.0", + "version": "0.10.1", "description": "Utilities to help testing Firebase projects with Cypress.", "main": "lib/index.js", "module": "lib/index.js", @@ -33,7 +33,7 @@ "commander": "^4.1.0", "figures": "^3.1.0", "firebase-admin": "^8.9.2", - "firebase-tools-extra": "0.5.0", + "firebase-tools-extra": "0.5.1", "lodash": "^4.17.15" }, "devDependencies": { diff --git a/yarn.lock b/yarn.lock index ecd7d1a5..c7d3da1c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2489,10 +2489,10 @@ firebase-admin@^8.9.2: "@google-cloud/firestore" "^3.0.0" "@google-cloud/storage" "^4.1.2" -firebase-tools-extra@0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/firebase-tools-extra/-/firebase-tools-extra-0.5.0.tgz#160cbb59d30eec14c2b2bfd5d83990b8cf3023f2" - integrity sha512-DxIIkYRmRiADP1HT+ZrstDZ0MwOWg5jHnDpntyHuPoPxTukgG5ZzRA+ZWIlNO8X0MqXkoCNhBdr5Sle/9KGC4A== +firebase-tools-extra@0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/firebase-tools-extra/-/firebase-tools-extra-0.5.1.tgz#ba3e3c6e79c113caba25b0b0993fde954477ec24" + integrity sha512-C7z7Z1YXOJ0Iv577LpLGktaLSdx+UC4gmXa3N1Ej/WxMmhgpEFVCIw0hKSq+tOeUNWvejUa2ZE6XxSkvtPKtkQ== dependencies: firebase-admin "^8.9.2" lodash "^4.17.15"