firebase / firebase-js-sdk

Firebase Javascript SDK
https://firebase.google.com/docs/web/setup
Other
4.75k stars 872 forks source link

Firestore emulator fails with "FIRESTORE (10.9.0) INTERNAL ASSERTION FAILED: Unexpected state" when run in jsdom environment #8137

Closed jeadorf closed 1 month ago

jeadorf commented 1 month ago

Operating System

Arch Linux 6.8.2-arch2-1

Browser Version

Google Chrome 122.0.6261.94 (Official Build) (64-bit)

Firebase SDK Version

10.9.0

Firebase SDK Product:

Firestore

Describe your project's tooling

Two setups:

  1. npm, firebase, mocha, chai
  2. npm, firebase, jsdom, vitest

Describe the problem

I wanted to write a simple unit test, using the Firestore emulator. The simplest, meaningful test I could come up with was to (1) write a Firestore document, then (2) read the Firestore document, and (3) compare expectations. Unfortunately, Firestore failed with FIRESTORE (10.10.0) INTERNAL ASSERTION FAILED: Unexpected state:

Error: FIRESTORE (10.10.0) INTERNAL ASSERTION FAILED: Unexpected state
 ❯ fail node_modules/@firebase/firestore/src/util/assert.ts:40:9
 ❯ hardAssert node_modules/@firebase/firestore/src/util/assert.ts:54:5
 ❯ fromBytes node_modules/@firebase/firestore/src/remote/serializer.ts:264:5
 ❯ fromWatchChange node_modules/@firebase/firestore/src/remote/serializer.ts:507:25
 ❯ PersistentListenStream.onMessage node_modules/@firebase/firestore/src/remote/persistent_stream.ts:642:25
 ❯ node_modules/@firebase/firestore/src/remote/persistent_stream.ts:517:21
 ❯ node_modules/@firebase/firestore/src/remote/persistent_stream.ts:570:18
 ❯ node_modules/@firebase/firestore/src/util/async_queue_impl.ts:135:7
 ❯ node_modules/@firebase/firestore/src/util/async_queue_impl.ts:186:14
 ❯ processTicksAndRejections node:internal/process/task_queues:95:5

This error appears when using vitest --environment=jsdom (setup 1): using jsdom does not work. This error does not appear when using vitest --environment=node (setup 1). This error does not appear when using mocha (setup 2) as the test runner.

Using --environment=node would strip me of the opportunity to run unit tests that depend on jsdom, as far as I understand. The error message ("internal assertion failed") is also rather cryptic, and does not make it clear whether this message indicates "a bug" or "working as intended; what you're doing is not supported".

There are a few similar bug reports that I stumbled upon, as well as reports on stackoverflow. None of them were giving me an answer to: is this a bug, or a feature. I also hope that a step-by-step recipe to reproduce this issue is helpful for Firestore developers and Firestore users alike.

Steps and code to reproduce issue

I used the following steps:

  1. Create new Firebase project and Firestore database (to make firebase init work)
  2. Set up Firebase: firebase init with feature Firestore and Emulators
  3. Set up module: npm init
  4. Install mocha: npm install --save-dev mocha
  5. Install chai: npm install --save-dev chai
  6. Install vitest: npm install --save-dev vitest
  7. Install firebase: npm install firebase
  8. Create simple unit test test.mjs (we'll run this with mocha later; baseline)
  9. Create similar unit test vitest.test.mjs (we'll run this with vitest later)
  10. Configure shorthands for running tests in package.json: test:mocha, test:vitest-node, test:vitest-jsdom
  11. Run tests with mocha: npm run test:mocha: good
  12. Run tests with vitest (node): npm run test:vitest-node: good
  13. Run tests with vitest (jsdom): npm run test:vitest-jsdom: bad

The key files are:

package.json:

{
  "name": "experimental",
  "version": "1.0.0",
  "main": "test.mjs",
  "scripts": {
    "test:mocha": "mocha --exit test.mjs",
    "test:vitest-node": "vitest --environment=node",
    "test:vitest-jsdom": "vitest --environment=jsdom"
  },
  "author": "",
  "license": "ISC",
  "description": "",
  "devDependencies": {
    "chai": "^5.1.0",
    "jsdom": "^24.0.0",
    "mocha": "^10.4.0",
    "vitest": "^1.4.0"
  },
  "dependencies": {
    "firebase": "^10.10.0"
  }
}

test.mjs:

import {
  doc,
  setDoc,
  getDoc,
  connectFirestoreEmulator,
  getFirestore,
} from "@firebase/firestore";
import { initializeApp } from "firebase/app";

import { it } from "mocha";
import { expect } from "chai";

it("should work", async () => {
  const app = initializeApp({
    projectId: "demo-experimental",
  });
  const db = getFirestore(app);
  connectFirestoreEmulator(db, "127.0.0.1", 8080);
  const docRef = doc(db, "items", "foo");
  await setDoc(docRef, {
    message: "hello, world",
  });

  const snapshot = await getDoc(docRef);
  const data = snapshot.data();
  expect(data).to.eql({
    message: "hello, world",
  });
})

vitest.test.mjs:

import {
  doc,
  setDoc,
  getDoc,
  connectFirestoreEmulator,
  getFirestore,
} from "@firebase/firestore";
import { initializeApp } from "firebase/app";

import { expect, it } from "vitest";

it("should work", async () => {
  const app = initializeApp({
    projectId: "demo-experimental",
  });
  const db = getFirestore(app);
  connectFirestoreEmulator(db, "127.0.0.1", 8080);
  const docRef = doc(db, "items", "foo");
  await setDoc(docRef, {
    message: "hello, world",
  });

  const snapshot = await getDoc(docRef);
  const data = snapshot.data();
  expect(data).to.eql({
    message: "hello, world",
  });
})
MarkDuckworth commented 1 month ago

Thanks for the detailed steps for reproduction. I have a PR with a fix.

jeadorf commented 1 month ago

Thank you! I ran the vitest.test.mjs example with vitest --environment=jsdom and the test is now passing. I did not explicitly check whether the whether the fix made it into the version, but I assume so:

npm view firebase

firebase@10.11.0 | Apache-2.0 | deps: 26 | versions: 3591