I'm curious why I need to add an extra await in my Vue.js unit test whenever multiple calls are made to my Firebase auth mock

In my vue.js application with firebase authentication, I encountered an issue while testing the Login.vue component. To mock firebase auth, I had to simulate the configuration file used for initializing firebase (named firebase.config). It's worth mentioning that in my config file, firebase.auth() is exported as auth, hence why I'm using fb.auth instead of fb.auth() in my code.

One of the test scenarios involves checking if a user is authenticated but not emailVerified, which should trigger displaying an email dialog. The focus of the test is on verifying whether the data property dialogEmail is being set to true.

During the sign-in process, I configured the firebase authentication state persistence to 'session'. While this setup poses no problems in the app itself, it caused issues during testing.

Snippet from Login.vue (abbreviated component code):

<template>
  <v-btn @click="signInUser(email, password)">
    Sign in
  <v/btn>
</template>

<script>
import * as fb from '@/firebase.config';

export default {
  data () {
    return {
      email: '',
      password: '',
    };
  },
  methods: {
    async signInUser (email, password) {
      try {
        await fb.auth.setPersistence('session');
        const { user } = await fb.auth.signInWithEmailAndPassword(email, password);

        if (user.emailVerified) {
          this.$router.push('/home');
        } else {
          this.dialogEmail = true;
        }
      } catch (err) {
        // ...error handling
      }
    },
  },
};
</script>

Snippet from login.spec.js:

import Vuetify from 'vuetify';
import { mount, createLocalVue } from '@vue/test-utils';
import Login from '@/views/Login';

jest.mock('@/firebase.config', () => ({
  auth: {
    setPersistence: () => Promise.resolve(),
    signInWithEmailAndPassword: () => Promise.resolve({ user: { emailVerified: false } }),
  },
}));

const localVue = createLocalVue();
const $router = { push: jest.fn() };

describe('login.vue', () => {
  let vuetify;

  beforeEach(() => {
    vuetify = new Vuetify();
  });

  const mountFunction = () => mount(Login, {
    localVue,
    vuetify,
    mocks: { $router },
  });

  it('shows dialog if user email not verified on sign in', async () => {
    expect.assertions(1);
    const wrapper = mountFunction();
    const signInButton = wrapper.find('[data-test="signIn"]');
    await wrapper.setData({ email: '<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="42242d2d022023306c2d3025">[email protected]</a>', password: 'FooBarBaz' });
    await signInButton.trigger('click');
    expect(wrapper.vm.dialogEmail).toBe(true);
  });
});

The main functionality of my app works perfectly fine. However, my test scenario was failing with an unexpected output:

expect(received).toBe(expected) // Object.is equality    

    Expected: true
    Received: false

Surprisingly, adding a short delay before the assertion allowed the test to pass successfully. This raised questions about why the extra timeout was needed and what exactly was happening behind the scenes. Seeking clarification on this matter prompted me to seek insights here.

Answer №1

When implementing the sign-in user function, there is a need to wait for the resolution of two promises:

await fb.auth.setPersistence('session');
const { user } = await fb.auth.signInWithEmailAndPassword(email, password);

In your test scenario, you included

await signInButton.trigger('click');
, however, this action only triggers the function call without waiting for its completion.

Hence, it is important to introduce a mocked promise resolution within the test after calling the sign-in function.

By utilizing setTimeout with a delay of 0, the promise will be added to the end of the tasks queue, ensuring that the test waits until the function's completion before proceeding.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Unable to manipulate JQuery lightSlider slides using element index

I've been working on a new page design at this link: The code is still a work in progress, so bear with me as I test out some functions and scripts. At the end of the first section, there are 4 logos that, when clicked, will trigger a modal to pop u ...

Obtaining the data value from a style applied to a div element in an E-commerce platform like MyShop

I have recently started using MyShop for my online store at www.myshop.com. The programming language they use is quite different from what I am used to. For example, the total price in the basket.html script is displayed using the following code: <spa ...

Learn the process of updating a nested document within an array in MongoDB

I have a data structure as shown below: { "name":"xxxxxx", "list":[ { "listname":"XXXXX1", "card":[ { "title":"xxxxxx", "descip":"xxxxxxx ...

Using VueJS to determine if a certain string includes a specific substring within an if-statement embedded within a v

I am aiming to verify if the link in a json object contains 'http'. If it does, I want to assign a target='_blank' attribute to the v-btn. The link could also be something like #test. Currently, this is how I am attempting to achieve i ...

Working with HTML5 Canvas to Clip Images

Is there a way to implement a tileset image in canvas like this one? I am trying to figure out how to make it so that clicking on the first tile returns 0, clicking on the tenth tile returns 9, and so on... Any suggestions on how to clip a tileset on an ...

Guide for adding an image directory to a React application for deployment/testing purposes

I am currently working on a personal project and there are two aspects that have been causing me some frustration. The project is coded in React, with an express node application serving as the backend. In the frontend, I am able to upload and send images ...

Error: Unable to modify a property that is marked as read-only on object '#<Object>' in Redux Toolkit slice for Firebase Storage in React Native

Hey there! I've been working on setting my downloadUrl after uploading to firebase storage using Redux Toolkit, but I'm facing some challenges. While I have a workaround, I'd prefer to do it the right way. Unfortunately, I can't seem to ...

Using SCSS to apply a class in Next.js

Currently, I am working on a project using Next.js and implementing the SCSS module procedure for styling. An example component directory structure is as follows: SampleComponent -> index.tsx and SampleComponent.module.scss The code in SampleComponent ...

Utilizing TypeScript to enhance method proxying

I'm currently in the process of converting my JavaScript project to TypeScript, but I've hit a roadblock with an unresolved TypeScript error (TS2339). Within my code base, I have a class defined like this: export const devtoolsBackgroundScriptCl ...

The choices in the second dropdown menu will change based on the selection made in the first dropdown menu

Currently utilizing reactJS, I have the choices for two dropdown lists named categories and items. constructor(props) { super(props) } this.state = { categories: [ { "id": 1, "category_name": ...

Using regular expressions, you can conveniently extract text that is contained within paragraph tags

I attempted to use RegExp in JavaScript to extract text between paragraph tags, but unfortunately it isn't working... Here is my pattern: <p>(.*?)</p> The text I am trying to extract from is: <p> My content. </p> <img sr ...

Best practices for authenticating methods with Google signin in Angular projects

I have made significant progress towards getting the Google signin feature to work, reaching 95% completion. However, I am currently facing two issues with the code. Here is a simplified version of my current implementation: loginWithGoogle(): void { //t ...

Strange behavior observed in the Datepicker, possibly linked to the blur event

I'm facing a challenge with the Datepicker feature. When I blur, the calendar disappears if the Datepicker was active, but the focus remains on the input field. As a result, I am unable to trigger the Datepicker again by clicking on the input field. T ...

The onmouseup event is not triggering in Vue.js 2

Show me the full code example here: https://github.com/kenpeter/test_vue_simple_audio_1 Attaching @onmouseup to the input range appears to have an issue. When I drag the slider, the function progressChange does not seem to be triggered. <input type ...

Manipulating and Parsing HTML files locally using Node.js

Hey there! I have a bit of an odd question that might be on the basic side. I'm diving into web development and front-end work, so I'm still fairly new to it all. The Scenario To give you some background, my experience with JS is mainly from usi ...

The select2 does not show the selected information

My select2 is not selecting the value when in edit mode. You can view my data here. Upon clicking the edit data button, it should select "settings" as the parent's value, but unfortunately, it's not working. You can see the issue here. Modal Sc ...

I'm having trouble pinpointing the cause of the never-ending loop in this React code that is using Material UI grid

There seems to be an issue with infinite looping in this code, and I can't figure out the cause, import React, { useEffect, useState } from 'react'; import VideoCardComponent from './VideoCard'; import Grid from '@mui/material ...

Enhance the background property in createMuiTheme of Material-UI by incorporating additional properties using Typescript

I've been attempting to include a new property within createMuiTheme, but Typescript is not allowing me to do so. I followed the instructions provided here: https://next.material-ui.com/guides/typescript/#customization-of-theme I created a .ts file ...

Encountering the error message "Uncaught TypeError: $.ajax is undefined"

Recently, I encountered an issue with my form that utilizes ajax to send user information to a php file. The form is embedded within a bootstrap modal and was functioning perfectly until I attempted to add an extra field for enhanced functionality. However ...

How can I show or hide all child elements of a DOM node using JavaScript?

Assume I have a situation where the HTML below is present and I aim to dynamically conceal all the descendants of the 'overlay' div <div id="overlay" class="foo"> <h2 class="title">title</h2> ...