Research
Security News
Malicious npm Packages Inject SSH Backdoors via Typosquatted Libraries
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
cypress-firebase
Advanced tools
Utilities and cli to help testing Firebase projects with Cypress
createTestEnvFile
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
npm i --save-dev cypress
cypress
folder containing cypress tests (or create one by calling cypress open
)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.
Install cypress-firebase and firebase-admin both: npm i cypress-firebase firebase-admin --save-dev
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 });
Setup plugin adding following your plugins file (cypress/plugins/index.js
):
const admin = require("firebase-admin");
const cypressFirebasePlugin = require("cypress-firebase").plugin;
module.exports = (on, config) => {
// Pass on function, config, and admin instance. Returns extended config
return cypressFirebasePlugin(on, config, admin);
};
Log into your Firebase console for the first time.
Go to Auth tab of Firebase and create a user for testing purpose
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
)
Add the following to your .gitignore
:
serviceAccount.json
cypress.env.json
Go to project setting on firebase console and generate new private key. See how to do here
Save the downloaded file as serviceAccount.json
in the root of your project (make sure that it is .gitignored)
Set the UID of the user you created earlier to the cypress environment. You can do this using a number of methods:
CYPRESS_TEST_UID
to a .env
file which is gitignoredTEST_UID
to cypress.env.json
cross-env
:"test": "cross-env CYPRESS_TEST_UID=your-uid cypress open"
CYPRESS_TEST_UID
environment variable in your CI settings if you are running tests in CIcy.login()
with the before
or beforeEach
sections of your testsNOTE: If you are running tests within your CI provider you will want to set the following environment variables:
CYPRESS_TEST_UID
- UID of your test userSERVICE_ACCOUNT
- service account object and thenpm start
) - for faster alternative checkout the test built version sectionnpm run test:open
in another terminal windowLogin to Firebase using custom auth token
Loading TEST_UID
automatically from Cypress env:
cy.login();
Passing a UID
const uid = "123SomeUid";
cy.login(uid);
Log out of Firebase instance
cy.logout();
Call Real Time Database path with some specified action. Authentication is through FIREBASE_TOKEN
since firebase-tools is used (instead of firebaseExtra).
action
String The action type to call with (set, push, update, remove)actionPath
String Path within RTDB that action should be appliedoptions
object Options
options.limitToFirst
number|boolean Limit to the first <num>
results. If true is passed than query is limited to last 1 item.options.limitToLast
number|boolean Limit to the last <num>
results. If true is passed than query is limited to last 1 item.options.orderByKey
boolean Order by key nameoptions.orderByValue
boolean Order by primitive valueoptions.orderByChild
string Select a child key by which to order resultsoptions.equalTo
string Restrict results to <val>
(based on specified ordering)options.startAt
string Start results at <val>
(based on specified ordering)options.endAt
string End results at <val>
(based on specified ordering)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);
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).
action
String The action type to call with (set, push, update, remove)actionPath
String Path within RTDB that action should be applieddataOrOptions
String Data for write actions or options for get actionoptions
Object Options
options.args
Array Command line args to be passedBasic
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("/");
});
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");
});
});
Install cross-env for cross system environment variable support: npm i --save-dev cross-env
Add the following to the scripts
section of your package.json
:
"emulators": "firebase emulators:start --only database,firestore",
"test": "cypress run",
"test:open": "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"
Add emulator ports to firebase.json
:
"emulators": {
"database": {
"port": 9000
},
"firestore": {
"port": 8080
}
}
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) {
fbConfig.databaseURL = `http://localhost:9000?ns=${fbConfig.projectId}`;
console.debug(`Using RTDB emulator: ${fbConfig.databaseURL}`);
}
// Initialize Firebase instance
firebase.initializeApp(fbConfig);
const firestoreSettings = {};
// 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;
}
// Emulate Firestore
if (shouldUseEmulator) {
firestoreSettings.host = "localhost:8080";
firestoreSettings.ssl = false;
console.debug(`Using Firestore emulator: ${firestoreSettings.host}`);
firebase.firestore().settings(firestoreSettings);
}
Make sure you also have 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 });
Start emulators: npm run emulators
In another terminal window, start the application: npm start
In another terminal window, open test runner with emulator settings: npm run test:emulate
NOTE: If you are using react-scripts
(from create-react-app) 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.debug(`Using RTDB emulator: ${fbConfig.databaseURL}`);
fbConfig.databaseURL = `http://${REACT_APP_FIREBASE_DATABASE_EMULATOR_HOST}?ns=${fbConfig.projectId}`;
}
// Initialize Firebase instance
firebase.initializeApp(fbConfig);
const firestoreSettings = {};
if (window.Cypress) {
// Needed for Firestore support in Cypress (see https://github.com/cypress-io/cypress/issues/6350)
firestoreSettings.experimentalForceLongPolling = true;
}
// Emulate RTDB if REACT_APP_FIRESTORE_EMULATOR_HOST exists in environment
if (REACT_APP_FIRESTORE_EMULATOR_HOST) {
firestoreSettings.host = REACT_APP_FIRESTORE_EMULATOR_HOST;
firestoreSettings.ssl = false;
console.debug(`Using Firestore emulator: ${firestoreSettings.host}`);
firebase.firestore().settings(firestoreSettings);
}
It is often required to run tests against the built version of your app instead of your dev version (with hot module reloading and other dev tools). You can do that by running a build script before spinning up the:
"start:dist": "npm run build && firebase emulators:start --only hosting",
firebase.json
:"emulators": {
"hosting": {
"port": 3000
}
}
npm run start:dist
to build your app and serve it with firebasenpm run test:open
NOTE: You can also use firebase serve
:
"start:dist": "npm run build && firebase serve --only hosting -p 3000",
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)FIREBASE_TOKEN
within CI environment variablesSeparate Install
name: Test Build
on: [pull_request]
jobs:
ui-tests:
name: UI Tests
runs-on: ubuntu-16.04
steps:
- name: Checkout Repo
uses: actions/checkout@v2
# Cypress action manages installing/caching npm dependencies and Cypress binary.
- name: Cypress Run
uses: cypress-io/github-action@v1
with:
group: "E2E Tests"
env:
# pass the Dashboard record key as an environment variable
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_KEY }}
# UID of User to login as during tests
CYPRESS_TEST_UID: ${{ secrets.TEST_UID }}
# Service Account (used for creating custom auth tokens)
SERVICE_ACCOUNT: ${{ secrets.SERVICE_ACCOUNT }}
# Branch settings
GITHUB_HEAD_REF: ${{ github.head_ref }}
GITHUB_REF: ${{ github.ref }}
Using Start For Local
name: Test
on: [pull_request]
jobs:
ui-tests:
name: UI Tests
runs-on: ubuntu-latest
steps:
- name: Checkout Repo
uses: actions/checkout@v2
# Cypress action manages installing/caching npm dependencies and Cypress binary
- name: Cypress Run
uses: cypress-io/github-action@v1
runs-on: ubuntu-16.04
with:
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 }}
# UID of User to login as during tests
CYPRESS_TEST_UID: ${{ secrets.TEST_UID }}
# Service Account (used for creating custom auth tokens)
SERVICE_ACCOUNT: ${{ secrets.SERVICE_ACCOUNT }}
# Branch settings
GITHUB_HEAD_REF: ${{ github.head_ref }}
GITHUB_REF: ${{ github.ref }}
When testing, tests should have admin read/write access to the database for seeding/verifying data. It isn't currently possible to use Firebase's firebase-admin
SDK directly within Cypress tests due to dependencies not being able to be loaded into the Browser environment. Since the admin SDK is nessesary to generate custom tokens and interact with Real Time Database and Firestore with admin privileges, this library provides convience methods (cy.callRtdb
, cy.callFirestore
, cy.login
, etc...) which call custom tasks which have access to the node environment.
fireadmin.io - A Firebase project management tool (here is the source)
FAQs
Utilities to help testing Firebase projects with Cypress.
The npm package cypress-firebase receives a total of 18,371 weekly downloads. As such, cypress-firebase popularity was classified as popular.
We found that cypress-firebase demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
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.
Research
Security News
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
Security News
MITRE's 2024 CWE Top 25 highlights critical software vulnerabilities like XSS, SQL Injection, and CSRF, reflecting shifts due to a refined ranking methodology.
Security News
In this segment of the Risky Business podcast, Feross Aboukhadijeh and Patrick Gray discuss the challenges of tracking malware discovered in open source softare.