Skip to content

Error: INTERNAL ASSERTION FAILED when unit testing frontend code with jest #6931

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
CharlieMcVicker opened this issue Jan 6, 2023 · 26 comments

Comments

@CharlieMcVicker
Copy link

CharlieMcVicker commented Jan 6, 2023

[REQUIRED] Describe your environment

  • Operating System version: macOS Monterey 12.1 (M1)
  • Browser version: N/A
  • Firebase SDK version: npm package version 3.8.0 (Firestore 9.15.0)
  • Firebase Product: firestore, auth, emulators

[REQUIRED] Describe the problem

I am attempting to test front-end React code using jest and testing-library/react via react-scripts / create-react-app. These tests require the jest jsdom env. However, I am encountering an error for which I only found solutions where users call jest with --env=node. Here is the error and stacktrace:

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

Details:

  • I am trying to write automated tests for React components that interact with Firebase via the WebSDK (eg. a custom AuthProvider component)
  • I am using the Firestore and Auth emulators via the Firebase CLI
  • I am using jest and testing-library/react (via react-scripts test/CRA) for testing

I hope that writing frontend unit tests against the emulator suite is a supported use case! (as the non-interactive testing sections of the auth emulator guide would suggest!!)

Related issues/posts

Steps to reproduce:

To cause error:

  1. Create a new (typescript) react app via create-react-app
  2. Install firebase SDK
  3. Create a firebase.test.ts file with the code below
  4. Run file as a jest test npm test -- firebase.test.ts (uses react-scripts test internally)

To resolve error (but remove ability to test frontend code):

  1. Install ts-jest npm i -d ts-jest
  2. run npx ts-jest config:init
  3. run npx jest --node=env -- firebase.test.ts

Relevant Code:

// firebase.test.ts
import {
  doc,
  setDoc,
  getDoc,
  connectFirestoreEmulator,
  getFirestore,
} from "@firebase/firestore";
import {
  getAuth,
  connectAuthEmulator,
  GoogleAuthProvider,
  signInWithCredential,
} from "firebase/auth";
import firebase from "firebase/compat";

const firebaseConfig = {
    // I've stripped my key, but a demo-prefixed project for the local emulators should work
};

// Initialize Firebase
const app = firebase.initializeApp(firebaseConfig);
const db = getFirestore(app);
const auth = getAuth(app);

// Connect emulators
connectFirestoreEmulator(db, "localhost", 8080);
connectAuthEmulator(auth, "http://localhost:9099");

describe("firestore", () => {
  it("test-case", async () => {
    await signInWithCredential(
      auth,
      GoogleAuthProvider.credential(
        JSON.stringify({
          sub: "abc",
          email: "[email protected]",
          email_verified: true,
        })
      )
    );

    expect(auth.currentUser).not.toBeNull();

    // if you aren't using typescript you can just get rid of the ! below
    const docRef = doc(db, "users", auth.currentUser!.uid);

    await setDoc(docRef, {
      foo: "bar",
    });

    // Comment below this point and there is no error

    const snapshot = await getDoc(docRef);
    const data = snapshot.data();
    expect(data).toStrictEqual({
      foo: "bar",
    });
  });
});

Desired behavior

Jest tests which mount React components via jsdom are able to read and write to local Firestore emulators so that I can have automated tests running on developer machines.

I am happy to provide longer code examples, eg. a code sample that demonstrates React code interacting with Firestore and how testing-library/react calls will fail with jest --env=node. I could also produce a repository that reproduces the error if that is easier than reproducing it from scratch.

Thanks,
Charlie

@wu-hui
Copy link
Contributor

wu-hui commented Jan 6, 2023

Thanks for your report.

This is likely a duplicate of: #6509

We are working to provide a fix for Jest users, but no ETA is known at this point.

@CharlieMcVicker
Copy link
Author

Thanks for the response. I will try to work around this then.

@CharlieMcVicker
Copy link
Author

Question: is mocha or any other testing environment with frontend support compatible with the firebase websdk?

I am happy to change frontend testing frameworks if it means I can test my firebase code.

@dconeybe
Copy link
Contributor

dconeybe commented Jan 9, 2023

(drive-by comment)

The problem is not so much with jest but rather with jsdom.

At runtime the Firestore SDK tries to determine if it's running in a browser or in node, and chooses some platform APIs accordingly. However, jsdom is this weird hybrid where it's running in node but is emulating a browser environment. The "fix" we are planning is to provide a way to override the node/browser auto-detection. Then you will be able to specify "browser" even though you're running in node with jsdom.

We use mocha internally for our integration tests, so mocha definitely works. I don't personally know much about jest; however, I understand that it does some custom module loading that can sometimes be problematic with Firestore. That being said, many Firestore users use jest.

@CharlieMcVicker
Copy link
Author

CharlieMcVicker commented Jan 9, 2023

This makes me wonder if it would be possible to create a firestore jsdom environment that will load firestore before jsdom so firestore has the needed node resources (which I assume is what is desired, since --env=node works) to run in the testing environment while jsdom is still available down the road for testing-libary/react.

@dconeybe
Copy link
Contributor

Update: A new feature to allow users to specify their environment as "node" or "browser" to override Firebase's runtime environment detection and force the SDK to act as if it were in the requested environment was released in v9.16.0 (January 19, 2023): https://firebase.google.com/support/release-notes/js#version_9160_-_january_19_2023. Could you test this out to see if specifying the desired environment fixes your tests?

@CharlieMcVicker
Copy link
Author

I'm very excited to see this released! I do not know when I will have time to test this because of how I rearranged the roadmap for my project to avoid this issue. I think a lot of other folks have had this issue and you may be able to track down someone working more closely with the tool at this moment by looking through some of the linked issues.

@dconeybe
Copy link
Contributor

Ok sounds good. I'm going to add a "needs-info" label to this issue and a bot will automatically close it in about 2 weeks. If you have a chance to test it out before then please write back. Otherwise, hopefully other jsdom users stumble across this thread, try out the solution, and report back.

@google-oss-bot
Copy link
Contributor

Hey @CharlieMcVicker. We need more information to resolve this issue but there hasn't been an update in 5 weekdays. I'm marking the issue as stale and if there are no new updates in the next 5 days I will close it automatically.

If you have more information that will help us get to the bottom of this, just add a comment!

@michaelAtCoalesce
Copy link

hi, im trying to run our jest tests in jsdom mode, and getting the following
`

FIRESTORE (9.17.1) INTERNAL ASSERTION FAILED: Unexpected state

  at fail (../../node_modules/@firebase/firestore/src/util/assert.ts:40:9)
  at hardAssert (../../node_modules/@firebase/firestore/src/util/assert.ts:54:5)
  at fromBytes (../../node_modules/@firebase/firestore/src/remote/serializer.ts:262:5)
  at fromWatchChange (../../node_modules/@firebase/firestore/src/remote/serializer.ts:500:25)
  at PersistentListenStream.onMessage (../../node_modules/@firebase/firestore/src/remote/persistent_stream.ts:642:25)
  at ../../node_modules/@firebase/firestore/src/remote/persistent_stream.ts:517:21
  at ../../node_modules/@firebase/firestore/src/remote/persistent_stream.ts:570:18
  at ../../node_modules/@firebase/firestore/src/util/async_queue_impl.ts:135:7
  at ../../node_modules/@firebase/firestore/src/util/async_queue_impl.ts:186:14

`

regardless of whether or not i set the export __FIREBASE_DEFAULTS__='{"forceEnvironment":"browser"}' to browser or node, - still getting the same behavior. @dconeybe

@dconeybe
Copy link
Contributor

dconeybe commented Mar 1, 2023

@michaelAtCoalesce Could you provide a github repo that I could clone to reproduce for myself? I think some debugging is needed to get to the bottom of this.

@michaelAtCoalesce
Copy link

Hey @dconeybe i cannot do that right now as our app is pretty huge - but i'd be happy to jump on a google hangout with you and do some pair debugging. sent you an email

@dconeybe
Copy link
Contributor

dconeybe commented Mar 1, 2023

Thank you for the offer, @michaelAtCoalesce, to jump onto a hangout. I'd like to try debugging asynchronously for now though. I understand you probably can't expose your entire app but could you create a minimal repro app that demonstrates the issue? Like maybe create a node project with 1 jsdom test that fails when you run it?

@michaelAtCoalesce
Copy link

after some more experimentation, it looks like changing some usages of firebase.firestore() to admin.firestore() appears to have made the assert go away.

@Zeusmist
Copy link

Zeusmist commented Mar 1, 2023

Thank you for the offer, @michaelAtCoalesce, to jump onto a hangout. I'd like to try debugging asynchronously for now though. I understand you probably can't expose your entire app but could you create a minimal repro app that demonstrates the issue? Like maybe create a node project with 1 jsdom test that fails when you run it?

Hi @dconeybe I face this same error in my React Native application.
I recently setup a bunch of onSnapshot listeners. And I also get this error

Firestore (9.15.0): Firestore (9.15.0) INTERNAL ASSERTION FAILED: Unexpected state

It happens multiple times which makes me believe it's related to either the listeners or the document elements they return.
The error occurs at random times during development.

Does this information help your debug?

@dconeybe
Copy link
Contributor

dconeybe commented Mar 2, 2023

Thank you for the information. Honestly, the most helpful thing would be a reproduction app. Could you put one together for me?

@michaelAtCoalesce
Copy link

michaelAtCoalesce commented Mar 2, 2023

image
im in meetings right now, but i noticed that things were breaking before due to some 'Class expected' assert, and this diff made the Class expected assert go away.

i have a branch that reproduces the error, but it takes awhile to break the code out.

@google-oss-bot
Copy link
Contributor

Hey @CharlieMcVicker. We need more information to resolve this issue but there hasn't been an update in 5 weekdays. I'm marking the issue as stale and if there are no new updates in the next 5 days I will close it automatically.

If you have more information that will help us get to the bottom of this, just add a comment!

@michaelAtCoalesce
Copy link

hi @dconeybe im sending you a repro over email

@google-oss-bot
Copy link
Contributor

Hey @CharlieMcVicker. We need more information to resolve this issue but there hasn't been an update in 5 weekdays. I'm marking the issue as stale and if there are no new updates in the next 5 days I will close it automatically.

If you have more information that will help us get to the bottom of this, just add a comment!

@dconeybe dconeybe self-assigned this Mar 23, 2023
@dconeybe
Copy link
Contributor

@michaelAtCoalesce You mentioned in email that you fixed your issue by exclusively using the "admin" sdk. This is a correct fix. The "admin" SDK and "normal client sdk" are completely different, independent projects that, although they are both written in TypeScript and have some similarly-named classes, are completely independent and cannot inter-operate. Specifically, you cannot use Firestore or DocumentReference objects from the "admin" sdk in the "client" sdk.

@CharlieMcVicker Are you also attempting to intermix "admin" sdk and "client" sdk objects in your code? If you see both "firebase" and "firebase-admin" in your package.json then it is a distinct possibility that this is happening.

@dconeybe
Copy link
Contributor

For context, the Firebase Admin SDK (https://github.com/firebase/firebase-admin-node) is intended to be used by "servers", such as in Cloud Functions for Firebase or in one of your backend servers. The Admin SDK has unfettered access to your database, does not check things like security rules, and has no client-side features like offline caching.

In contrast, the Firebase JavaScript Client SDK (this GitHub repository) is intended for use in apps that you provide to your customers. It provides features like enforcing security rules and offline caching.

If the app that Jest is testing uses the Firebase Admin SDK then the Jest tests probably should too. Similarly, if the app that Jest is testing uses the Firebase JavaScript client SDK then the Jest tests should also use the client SDK. The exception would be if you use the admin sdk to configure the test environment, like set up users and security rules or seed a database.

@michaelAtCoalesce
Copy link

@dconeybe what we ended up doing is switching over our firestore wrapper to use the admin SDK when jest testing.

so we have code that uses the firebase javascript client SDK, but when we jest test with JSDOM we switch over everything to the admin sdk.

@dconeybe
Copy link
Contributor

For anyone experiencing this error, or an error like it, in react-native, please see the fix documented here: #7115 (comment) (and the full explanation by @hsubox76 a few comments later).

tl;dr If you're customizing the metro bundler config you may be clobbering its logic to choose the "react-native" bundle from dependencies in node_modules. In particular, Firestore has some special logic when running in react-native (e.g. using a different base64 codec: https://github.com/firebase/firebase-js-sdk/blob/master/packages/firestore/src/platform/rn/base64.ts) that gets lost and causes problems if the "browser" bundle is used.

@CharlieMcVicker
Copy link
Author

Hi @dconeybe -- I was not attempting to mix admin functionality. As adding Cloud persistence came back on the roadmap for our project, we decided to build against Realtime Database instead of CloudFirestore, which resulted in much less new code, and so we were able to move forward without circling back on this. (Moving from LocalStorage to Realtime Database is horrifyingly easy).

I am meeting with a volunteer team on the project today and will see if circling back on automated testing against a local Realtime DB could be feasible in the near future.

@dconeybe
Copy link
Contributor

dconeybe commented Apr 3, 2023

@CharlieMcVicker Thank you for the update. I'll mark this ticket as "closed" since there is no immediate action to take right now. If you'd like to continue discussion please add a new comment. If comments get closed then simply create a new issue and provide a link back to this one as part of the description. If you find issues with Realtime Database then please open a new issue since the devlopers for Firestore are completely different than those for RTDB. Thank you!

@dconeybe dconeybe closed this as completed Apr 3, 2023
@firebase firebase locked and limited conversation to collaborators May 4, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

7 participants