dappsnation / akita-ng-fire

Akita ❤️ Angular 🔥 Firebase
MIT License
131 stars 27 forks source link

Firestore Internal Assertion Failed When Testing CollectionService (Jest) #245

Closed Everduin94 closed 2 years ago

Everduin94 commented 2 years ago

I'm trying to test CollectionService, similar to the tests found here: https://github.com/dappsnation/akita-ng-fire/blob/v7/projects/akita-ng-fire/src/lib/collection/collection.service.spec.ts

When invoking the following functions in a Jest unit test

I get the following exception:

    Unhandled Promise rejection: FIRESTORE (9.8.1) INTERNAL ASSERTION FAILED: Unexpected state ; Zone: <root> ; Task: Promise.then
 ; Value: Error: FIRESTORE (9.8.1) INTERNAL ASSERTION FAILED: Unexpected state
        at fail (/home/erik/dev/monobased/node_modules/@firebase/firestore/src/util/assert.ts:40:9)
        at hardAssert (/home/erik/dev/monobased/node_modules/@firebase/firestore/src/util/assert.ts:54:5)
        at fromBytes (/home/erik/dev/monobased/node_modules/@firebase/firestore/src/remote/serializer.ts:252:5)
        at fromWatchChange (/home/erik/dev/monobased/node_modules/@firebase/firestore/src/remote/serializer.ts:475:25)
        at PersistentListenStream.onMessage (/home/erik/dev/monobased/node_modules/@firebase/firestore/src/remote/persistent_strea
m.ts:642:25)

An Example -- TagService can be any CollectionService in this context. (For context, I'm running the Firebase Emulator)

import { getApp, initializeApp, provideFirebaseApp } from "@angular/fire/app";
import { connectFirestoreEmulator, getFirestore, provideFirestore } from "@angular/fire/firestore";
import {createServiceFactory, SpectatorService} from '@ngneat/spectator';
import { TagService } from "./tag.service";
import { TagQuery } from "./tag.query";
import { TagStore } from "./tag.store";

describe('Tags Test', () => {

  const createService = createServiceFactory({
    service: TagService,
    imports: [
      provideFirebaseApp(() => initializeApp({
        apiKey: 'AIzaSyD8fRfGLDsh8u8pXoKwzxiDHMqg-b1IpN0',
        authDomain: 'akita-ng-fire-f93f0.firebaseapp.com',
        databaseURL: 'https://akita-ng-fire-f93f0.firebaseio.com',
        projectId: 'akita-ng-fire-f93f0',
        storageBucket: 'akita-ng-fire-f93f0.appspot.com',
        messagingSenderId: '561612331472',
        appId: '1:561612331472:web:307acb3b5d26ec0cb8c1d5'
      }, 'collection service app')),
      provideFirestore(() => {
        const firestore = getFirestore(getApp('collection service app'));

        if (!firestore['_initialized'])
        {
          connectFirestoreEmulator(firestore, 'localhost', 8080);
          // enableIndexedDbPersistence(firestore);
        }

        return firestore;
      })
    ],
    providers: [
      TagStore,
      TagQuery
    ]
  });

  let spectator: SpectatorService<TagService>;
  let service: TagService;
  beforeEach(async () => {
    spectator = createService();
    service = spectator.service;
  });

  it('should create', () => {
    expect(spectator.service).toBeTruthy();
  });

  it('SyncCollection', async (done) => {
    service.syncCollection().subscribe(v => {
      expect(true).toBeTruthy();
      done();
    });
  });
});

After doing some research. It seems that the solution may be to run jest in a node environment. But this causes a cascading set of problems since we're using the Angular TestBed to provide all of the dependencies. -- I also don't see this project's tests doing that explicitly, but maybe that's because it's using karma/jasmine?


Dependencies:

    "@angular/fire": "^7.3.0",
    "@datorama/akita": "^7.1.1",
    "akita-ng-fire": "^7.0.0",
    "firebase": "^9.8.1",
    "@angular/core": "13.3.5",
hakimio commented 2 years ago

Most likely this is an issue with jest described in the following bug report: https://github.com/firebase/firebase-js-sdk/issues/3653 Possible workaround here: https://github.com/facebook/jest/issues/7780#issuecomment-615890410

EDIT: workaround when using jsdom instead of node environment: https://github.com/firebase/firebase-js-sdk/issues/3096#issuecomment-827741103

hakimio commented 2 years ago

Basically the issue here is with jest where the following assertion fails:

const foo = Buffer.alloc(0);
const isInstance = foo instanceof Uint8Array;
expect(isInstance).toBe(true);

And it effects this firestore code. This has nothing to do with akita-ng-fire.

Everduin94 commented 2 years ago

@hakimio -- Thank you for the input

Also sorry, I was trying to test CollectionService, but was initially guessing this was not specific to akita-ng-fire, as you mentioned. So I should have posted my question somewhere more applicable.

I tried the workaround you mentioned, which led me to upgrade to jest 28, since the above wasn't working for me.

This issue can be closed, I'm getting the following when trying to test CollectionService with Jest 28. Which to your point has nothing to do with akita-ng-fire itself.

    Jest encountered an unexpected token
   ...
    /home/erik/dev/monobased/node_modules/@firebase/firestore/dist/index.esm2017.js:12860
                    function (t, e) {
                    ^^^^^^^^

    SyntaxError: Function statements require a function name

      at Runtime.createScriptFromCode (../../node_modules/@jest/core/node_modules/jest-runtime/build/index.js:1773:14)
      at Object.<anonymous> (../../node_modules/firebase/firestore/dist/index.esm.js:1:1)
Everduin94 commented 2 years ago

I mistakenly mentioned that node could not be used as the jest environment due to Angular TestBed. This is not true. My test had nested dependencies on classes that used document/router which caused those problems.

I was able to use node as my jest environment once I removed those dependencies and then was able to successfully test CollectionService - syncCollection() and signin().