Jest & Firebase Emulator - wait for trigger function to be called

Viewed 463

My package.json file contains the following:

...
    "jest": "jest --runInBand",
    "test": "firebase emulators:exec 'npm run jest'"
...

which allows me to run tests based on jest with a running Firebase Emulator.

Consider the following piece of code:

describe("MyTests", () => {
  const req = supertest(app);
  let user: admin.auth.UserRecord;
  let idToken: string | undefined;

  beforeAll(async () => {
    const email = "test@gmail.com";
    const password = "password";
    user = await admin.auth().createUser({ email: email, password: password });

    await auth.signInWithEmailAndPassword(email, password);
    idToken = await auth.currentUser?.getIdToken();
  });

// actual tests

Where I'm creating a Firebase user, which in turn trigger a function (functions.auth.user().onCreate(foo)).

I do notice that this foo function is being executed after the jest test suite is running:

Test Suites: 1 passed, 1 total
Tests:       3 passed, 3 total
Snapshots:   0 total
Time:        14.841 s, estimated 16 s
Ran all test suites.
āœ”  Script exited successfully (code 0)
i  functions: Beginning execution of "us-central1-foo"
i  emulators: Shutting down emulators.
i  functions: Stopping Functions Emulator
i  functions: Waiting for all functions to finish...
i  functions: Finished "us-central1-foo" in ~1s

How can I wait for it to be executed from inside the beforeAll() function?

1 Answers

I found a pretty hacky solution to make this work, but it works.

When i was researching this question my self i realized that the logs from the Emulator UI is available on a websocket on ws://localhost:4500

So what i did was that i created a function that checks when functions start and when functions finishes bases on the firebase loggs Beginning execution of " and Finished "

I also created a function that adds a listener that gets triggered when no functions has been triggered within 100ms from the last Finished "-message

The Emulator UI is not included by default in the firebase emulators:exec-command so you need to run it with firebase emulators:exec --ui "npm test" for the logs service to start.

I use it like this

beforeAll(async () => {
  await initFirebaseListener()
})
afterAll(() => {
  closeSocket()
})

describe('wait for firebase', () => {
  test('should wait for firebase', async () => {
    // do stuff that triggers some auth, firestore, RTDB or whatever function
    await waitForFirebaseToFinish()
    // Get something that i can validate
    // expect(theThingIWasWaitingFor).toEqual('the expected value that the trigged did')
  })
})

Key takeaways:

  • You need to close the socket for the process to terminate after test execution
  • This is super hacky, can break in the next update of firebase-admin
  • You need to add --ui to the emulators:exec command for the logs service to start.
  • It works, for me at least.

Created a gist with the code, link down below. I used the websocket npm library (npm install -D websocket) for subscribing to the logs, but you could use another library if you like.

https://gist.github.com/Gurey/8301022f8357b3a7d74cefca4b598646

Related