skip to Main Content

I am trying to test firestore security rules that I have wrote, but every time I am trying to run my tests I am getting timeout error.

Before I run tests, I start my firestore emulators with such command:

❯ npm run firebase:run:emulator

> [email protected] firebase:run:emulator
> cd firebase && firebase emulators:start --project=warzywniax --import=./seed --export-on-exit

i  emulators: Starting emulators: auth, firestore, storage
i  firestore: Importing data from /firebase/seed/firestore_export/firestore_export.overall_export_metadata
i  firestore: Firestore Emulator logging to firestore-debug.log
✔  firestore: Firestore Emulator UI websocket is running on 9150.
i  auth: Importing config from /firebase/seed/auth_export/config.json
i  auth: Importing accounts from /firebase/seed/auth_export/accounts.json
i  ui: Emulator UI logging to ui-debug.log

┌─────────────────────────────────────────────────────────────┐
│ ✔  All emulators ready! It is now safe to connect your app. │
│ i  View Emulator UI at http://127.0.0.1:4000/               │
└─────────────────────────────────────────────────────────────┘

┌────────────────┬────────────────┬─────────────────────────────────┐
│ Emulator       │ Host:Port      │ View in Emulator UI             │
├────────────────┼────────────────┼─────────────────────────────────┤
│ Authentication │ 127.0.0.1:9099 │ http://127.0.0.1:4000/auth      │
├────────────────┼────────────────┼─────────────────────────────────┤
│ Firestore      │ 127.0.0.1:8080 │ http://127.0.0.1:4000/firestore │
├────────────────┼────────────────┼─────────────────────────────────┤
│ Storage        │ 127.0.0.1:9199 │ http://127.0.0.1:4000/storage   │
└────────────────┴────────────────┴─────────────────────────────────┘
  Emulator Hub running at 127.0.0.1:4400
  Other reserved ports: 4500, 9150

It looks like everything is okay. My firestore emulator is hosted under 127.0.0.1:8080 and there is no any problem accessing this emulator via my web-browser.

Before I run any test, I am running some setup script on setupFilesAfterEnv jest hook. The whole setup script looks like this:

import { initializeTestEnvironment, RulesTestEnvironment } from '@firebase/rules-unit-testing';
import firebase from 'firebase/compat/';

export let testEnvironment: RulesTestEnvironment;
export let guestUserContext: firebase.firestore.Firestore;
beforeAll(async () => {
  testEnvironment = await initializeTestEnvironment({
    firestore: {
      host: '127.0.0.1',
      port: 8080,
    },
    projectId: 'warzywniax',
  });
  guestUserContext = testEnvironment.unauthenticatedContext().firestore();
  console.info('TEST_ENV is ready:n', testEnvironment);
  console.info('GUEST_USER is ready:n', guestUserContext);
});

Then if attempt to run some simple test like this one:

import { assertSucceeds } from '@firebase/rules-unit-testing';
import { guestUserContext } from '../integration-setup';

describe('Offers collection tests', () => {
  it('should allow to read offers for any user', async () => {
    const testQuery = guestUserContext.collection('offers');

    expect(await assertSucceeds(testQuery.get()));
  });
});

it results with this error:

 FAIL  test/integration/firebase/firestore.rules.spec.ts (5.234 s)
  Offers collection tests
    ✕ should allow to read offers for any user (5001 ms)

  ● Offers collection tests › should allow to read offers for any user

    thrown: "Exceeded timeout of 5000 ms for a test.
    Add a timeout value to this test to increase the timeout, if this is a long-running test. See https://jestjs.io/docs/api#testname-fn-timeout.

I have checked was there any problem getting collection, but if I log testQuery it looks alright.

I have also followed advice from error above error, and I extended the timeout to 30s (--testTimeout=30000). What gives strange result. My test passed by I got such error anyway:

  console.error
    [2023-03-30T15:45:44.552Z]  @firebase/firestore: Firestore (9.17.1): Could not reach Cloud Firestore backend. Backend didn't respond within 10 seconds.
    This typically indicates that your device does not have a healthy Internet connection at the moment. The client will operate in offline mode until it is able to successfully connect to the backend.

      at Logger.defaultLogHandler [as _logHandler] (node_modules/@firebase/logger/src/logger.ts:115:57)
      at Logger.Object.<anonymous>.Logger.error (node_modules/@firebase/logger/src/logger.ts:210:5)
      at x (node_modules/@firebase/firestore/dist/index.esm2017.js:124:11)
      at Pu.au (node_modules/@firebase/firestore/dist/index.esm2017.js:13778:20)
      at nc.op (node_modules/@firebase/firestore/dist/index.esm2017.js:13748:14)
      at node_modules/@firebase/firestore/dist/index.esm2017.js:14307:14
      at node_modules/@firebase/firestore/dist/index.esm2017.js:17414:73
      at node_modules/@firebase/firestore/dist/index.esm2017.js:17447:54

 PASS  test/integration/firebase/firestore.rules.spec.ts (10.335 s)
  Offers collection tests
    ✓ should allow to read offers for any user (10168 ms)

What, in my opinion, should make this test fall. Do you have any ideas what can be wrong, and how can I fix this issue?

2

Answers


  1. I ran your setup and was able to get a passing test. Check the version numbers here to see if it is the cause of any issues. Also the outputs may be different in a meaningful way:

    import { initializeTestEnvironment } from '@firebase/rules-unit-testing';
    
    export let testEnvironment;
    export let guestUserContext;
    beforeEach(async () => {
      testEnvironment = await initializeTestEnvironment({
        firestore: {
          host: '127.0.0.1',
          port: 8080,
        },
        projectId: 'warzywniax',
      });
      guestUserContext = testEnvironment.unauthenticatedContext().firestore();
      console.info('TEST_ENV is ready:n', testEnvironment);
      console.info('GUEST_USER is ready:n', guestUserContext);
    });
    
    import { initializeTestEnvironment, assertSucceeds } from '@firebase/rules-unit-testing';
    import { guestUserContext } from './lib.js';
    
    describe('Offers collection tests', () => {
      it('should allow to read offers for any user', async () => {
        const testQuery = guestUserContext.collection('offers');
    
        await assertSucceeds(testQuery.get());
      });
    });
    

    node.js package.json:

    {
      "name": "rulestestingso",
      "version": "1.0.0",
      "description": "Testing this",
      "main": "main.js",
      "type": "module",
      "scripts": {
        "test": "echo "Error: no test specified" && exit 1"
      },
      "author": "chris",
      "license": "ISC",
      "dependencies": {
        "@firebase/rules-unit-testing": "^2.0.7",
        "firebase": "^9.19.0",
        "jest": "^29.5.0"
      }
    }
    
    
    $ mocha main.js
    
    
      Offers collection tests
    TEST_ENV is ready:
     RulesTestEnvironmentImpl {
      projectId: 'warzywniax',
      emulators: { firestore: { host: '127.0.0.1', port: 8080 } },
      contexts: Set(1) {
        RulesTestContextImpl {
          projectId: 'warzywniax',
          emulators: [Object],
          authToken: undefined,
          destroyed: false,
          envDestroyed: false,
          app: [FirebaseAppImpl]
        }
      },
      destroyed: false
    }
    GUEST_USER is ready:
     Firestore {
      _delegate: Firestore {
        _authCredentials: FirebaseAuthCredentialsProvider {
          authProvider: [Provider],
          currentUser: [User],
          tokenCounter: 0,
          forceRefresh: false,
          auth: null
        },
        _appCheckCredentials: FirebaseAppCheckTokenProvider {
          appCheckProvider: [Provider],
          forceRefresh: false,
          appCheck: null,
          latestAppCheckToken: null
        },
        _databaseId: DatabaseId { projectId: 'warzywniax', database: '(default)' },
        _app: FirebaseAppImpl {
          _isDeleted: false,
          _options: [Object],
          _config: [Object],
          _name: '_Firebase_RulesUnitTesting_1680224130695_0.0042143144476949335',
          _automaticDataCollectionEnabled: false,
          _container: [ComponentContainer]
        },
        type: 'firestore',
        _persistenceKey: '_Firebase_RulesUnitTesting_1680224130695_0.0042143144476949335',
        _settings: FirestoreSettingsImpl {
          host: '127.0.0.1:8080',
          ssl: false,
          credentials: undefined,
          ignoreUndefinedProperties: false,
          cache: undefined,
          cacheSizeBytes: 41943040,
          experimentalForceLongPolling: false,
          experimentalAutoDetectLongPolling: false,
          useFetchStreams: true
        },
        _settingsFrozen: false,
        _queue: AsyncQueueImpl {
          tail: [Promise],
          retryableOps: [],
          _isShuttingDown: false,
          delayedOperations: [],
          failure: null,
          operationInProgress: false,
          skipNonRestrictedTasks: false,
          timerIdsToSkip: [],
          backoff: [ExponentialBackoff],
          visibilityHandler: [Function (anonymous)]
        }
      },
      _persistenceProvider: IndexedDbPersistenceProvider {},
      INTERNAL: { delete: [Function: delete] },
      _appCompat: FirebaseAppImpl {
        _delegate: FirebaseAppImpl {
          _isDeleted: false,
          _options: [Object],
          _config: [Object],
          _name: '_Firebase_RulesUnitTesting_1680224130695_0.0042143144476949335',
          _automaticDataCollectionEnabled: false,
          _container: [ComponentContainer]
        },
        firebase: <ref *1> {
          __esModule: true,
          initializeApp: [Function: initializeAppCompat],
          app: [Function],
          registerVersion: [Function: registerVersion],
          setLogLevel: [Function: setLogLevel],
          onLog: [Function: onLog],
          apps: [Getter],
          SDK_VERSION: '9.19.0',
          INTERNAL: [Object],
          default: [Circular *1],
          database: [Function],
          firestore: [Function],
          storage: [Function]
        },
        container: ComponentContainer {
          name: '_Firebase_RulesUnitTesting_1680224130695_0.0042143144476949335',
          providers: [Map]
        }
      }
    }
        ✔ should allow to read offers for any user (1049ms)
    
    
      1 passing (1s)
    
    
    Login or Signup to reply.
  2. I ran into this issue. I have several Jest tests related to Firestore rules, all of them failing due to Timeout while all my Firebase Database rules tests were passing.

    What I learned after a lot of research was that, at least on my side, the problem was related to the test environment using jsdom. When I switched to the Jest default test environment, which is node, the tests start passing again.

    The main help I found was this thread on (sic) React native repo.
    https://github.com/facebook/react-native/issues/36342

    And the most important paragraph from the whole thread is this:
    "The root issue here is that RN’s Jest environment now only exports react-native as an export condition (not node), which means packages which export a node target might now resolve differently. In the case of firebase, it defaults back to ESM. This should’ve been labelled as a breaking change to the Jest setup to be honest – sorry about that."

    I hope this helps someone.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search