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

@getcircuit/firestore-test

Package Overview
Dependencies
Maintainers
19
Versions
186
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@getcircuit/firestore-test

<!-- @import "[TOC]" {cmd="toc" depthFrom=2 depthTo=6 orderedList=false} -->

  • 14.39.2-potato.1
  • unpublished
  • npm
  • Socket score

Version published
Weekly downloads
0
Maintainers
19
Weekly downloads
 
Created
Source

@getcircuit/firestore-test

This package is comprised of a set of utilities to help you write tests for your Firestore code. It mainly provides two significant features:

  • A set of utilities to help you seed Circuit's firestore data inside a jest test environment.
  • A tiny CLI to help you get the emulator going with the appropriate configuration for easy testing.

Usage

CLI

The CLI is a simple wrapper around the firebase CLI, but with the project set to test and pointing to this package's firebase.json configuration file.

# Start the emulator indefinitely
firestore-test emulator:start

# Start the emulator and run a command
firestore-test emulator:exec "<command>"

Example:

# Start the emulator and run jest
firestore-test emulator:exec "jest"

Utilities

Testing utilities
export {
  getTestFirestore,
  getTestRefSelectors,
  getUniqueTestRootRef,
  waitForSnapshotListenersInSync,
  FIREBASE_TEST_PROJECT_ID,
} from '@getcircuit/firestore-test'
  • getTestFirestore() returns a Firestore instance that is connected to the emulator.

  • getTestRefSelectors() returns a bag of ref selectors already attached to the test Firestore.

  • getUniqueTestRootRef() returns a DocumentReference that is unique to the current test. This is useful when you want to seed data that is specific to the current test without having to seed a whole team.

  • FIREBASE_TEST_PROJECT_ID is the id of the test project. It's _firebase_test_ currently.

import { getUniqueTestRootRef } from '@getcircuit/firestore-test'

const ref = getUniqueTestRootRef() // DocumentReference to /test-suite/{randomId}
  • waitForSnapshotListenersInSync() is a function that returns a promise that resolves when all active snapshot listeners are in sync with the remote firestore. This is useful when you want to guarantee that a test is waiting for a firestore document to be updated.
Seed utilities
export {
  seedDepots,
  seedMembers,
  seedPatches,
  seedPlan,
  seedPlanDrivers,
  seedRoute,
  seedStops,
  seedTeam,
  seedUsers,
} from '@getcircuit/firestore-test'

Each seeding utility accepts a DeepPartial version of the data being seeded. Properties that are not explicitly set will fallback to what has been defined inside the src/firebase/data-factories directory.

Each seeded model may or may not receive an id property. If no id is provided, a random one will be generated.

await seedStops({
  collection,
  stops: [
    { id: 'stop-1', name: 'Stop 1' }, // Stop 1 will have a known id
    { name: 'Stop 2' }, // Stop 2 will have a random id
  ],
})

While there are individual methods to seed users, members, depots, etc, you don't need to use them directly. For example, to seed a team with members and their users, you can use _only_ the seedTeam method:

import { seedTeam } from '@getcircuit/firestore-test'

const teamSelectors = await seedTeam({
  members: [
    { id: 'john', name: 'John Doe' },
    { id: 'jane', name: 'Jane Doe' },
  ],
})

This will automatically create the users for the specified members, and link their DocumentReferences accordingly.

We can then select these references with the help of the teamSelectors object:

teamSelectors.membersRef() // => CollectionReference<Member>
teamSelectors.memberRef('john') // => DocumentReference<Member>
teamSelectors.userRef('john') // => DocumentReference<User>
About seeding users via members

Since each test is expected to seed its own data, the user collection is shared between all tests. Creating an user via their member is done by using the same id given to the member. So if two different tests created a member with id john, both tests would try to write data to the same firestore document.

To prevent this, the seedTeam function prefixes the user id with the id of the team.

const teamSelectors = await seedTeam({
  id: 'team-1',
  members: [
    { id: 'john', name: 'John Doe' },
    { id: 'jane', name: 'Jane Doe' },
  ],
})

teamSelectors.userRef('john').id // => 'team-1-john'

In case one wants to verify the id of a user, the teamSelectors object provides a userId method that will return the prefixed id:

teamSelectors.userId('john') // => 'team-1-john'

This is one of the reasons the returned selectors are particularly useful, as this prefixing logic is encapsulated inside them.

About seeding documents with cross-references

As we saw, some of the seeding methods allow you to seed various firestore models at once. While this can speed up writing tests, it can also lead to some confusion when it comes to cross-references.

For example, imagine that we want to seed a team with one member that is assigned to a depot. At the moment of calling seedTeam, nothing exists, so there's no way to link the member-to-be to the depot-to-be.

const teamSelectors = await seedTeam({
  depots: [{ id: 'depot-1' }],
  members: [
    {
      id: 'john',
      name: 'John Doe',
      depot: ???, // How to link the member to the depot?
    },
  ],
})

There are two ways to solve this:

  • Use the deferred argument of the seeding methods

This is the most concise way, as it allows you to, instead of providing the seeding arguments directly, pass a function that will be called with the selectors. This function should return the data that will be used to seed the model.

const teamSelectors = await seedTeam((teamSelectors) => ({
  depots: [{ id: 'depot-1' }],
  members: [
    {
      id: 'john',
      name: 'John Doe',
      depot: teamSelectors.depotRef('depot-1'),
    },
  ],
}))
  • Use the seeding methods separately

This is the most verbose way, but it allows you to have full control over the references. However, you will need to manually link the references and deal with the "user id" conflict mentioned on the About seeding users via members section.

const teamSelectors = await seedTeam({
  depots: [{ id: 'depot-1' }],
})

const userSelectors = await seedUsers({
  teamRef: teamSelectors.teamRef,
  users: [
    {
      id: 'john',
      name: 'John Doe',
    },
  ],
  mapId: (id) => teamSelectors.userId(id),
})

const memberSelectors = await seedMembers({
  teamRef: teamSelectors.teamRef,
  members: [
    {
      id: 'john',
      name: 'John Doe',
      depot: teamSelectors.depotRef('depot-1'),
    },
  ],
})

Writing a test

Each test that connects to the firestore emulator is expected to be self-contained. This means that each test should seed its own data. Cleaning up is not necessary.

import { seedTeam, seedRoute } from '@getcircuit/firestore-test'

test('route is created', async () => {
  // Seeds a team with one member
  const { teamRef, memberRef, userRef } = await seedTeam({
    members: [{ id: 'chris' }],
  })

  // Seeds a route with linking it to the driver user and member documents
  const { routeRef } = await seedRoute(teamRef, {
    route: {
      driver: userRef('chris'),
      member: memberRef('chris'),
    },
  })

  expect('...').toEqual('...')
})
Configuring the test environment
Setup the test firebase app

The first thing to do is to create a setup file for jest and reference it in the jest.config setupFilesAfterEnv property. The first expression in this file must be an import expression for either:

  • @getcircuit/firestore-test/setup/browser if you want the test firebase app to use the browser SDK.
  • @getcircuit/firestore-test/setup/node if you want the test firebase app to use the node SDK.

The import will take care of creating a test firebase application and exposing it via getters. You can then use getTestFirestore, getTestFirebaseApp and getTestFirebaseAuth.

// client-project/tests/setup.js
import '@getcircuit/firestore-test/setup/browser'
import { getTestFirestore } from '@getcircuit/firestore-test'

getTestFirestore() // => Firestore

⚠️ If you want to use the node SDK, be sure to follow the firebase-types package's instructions on how to override the firebase types, as the default types for references, snapshots, etc, are associated with the browser SDK.

Jest config

@getcircuit/firestore-test provides a JestConfig file to serve as a base for your project's jest setup:

  • @getcircuit/firestore-test/jest/config.browser for a browser environment
  • @getcircuit/firestore-test/jest/config.node for a node environment
// client-project/jest.config.js
const shared = require('@getcircuit/firestore-test/jest/config.browser')

module.exports = {
  ...shared,
  // your custom config
}
Conflict-free firestore root

In case your Firestore-related test doesn't need a whole team, you can use the getUniqueTestRootRef method to get a unique document ref pointing to test-suite/{randomId}. This is useful to avoid having to create a unique collection in each test to prevent conflicts.

import { getUniqueTestRootRef } from '@getcircuit/firestore-test'

test('test 1', async () => {
  const rootRef = getUniqueTestRootRef()
  const patches = rootRef.collection('patches')

  // do stuff with this patches collection
})

test('test 2', async () => {
  const rootRef = getUniqueTestRootRef() // this is different from the previous test
  const patches = rootRef.collection('patches') // the collections in both tests have the same name, but live in different paths

  // do stuff with this patches collection
})

FAQs

Package last updated on 21 Aug 2023

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