How can I mimic a globally referenced object like `gapi` within a Vue "Composition" + Pinia "Setup Function" single page application?

I have a Vue single-page application (SPA) that utilizes Pinia as a store. The application also establishes a global namespace gapi by including a Google API script tag: https://developers.google.com/drive/api/quickstart/js

This means that in my store, I will have code similar to:

function gapiLoaded() {
    gapi.load('client', initializeGapiClient);
}

During test functions, components make use of the store using the useStore paradigm. Consequently, when running my test suite, I encounter errors such as gapi is not defined.

Here is an example test file:

import { describe, it, expect } from 'vitest';
import { mount } from '@vue/test-utils';
import Files from '../Files.vue';
import type { ReadFile } from '../Files.vue';
import { createTestingPinia } from '@pinia/testing';
// Import any store you want to interact with in tests
import { useDriveStore } from '@/stores/drive';
import { fn } from '@vitest/spy';

describe('Files Component', () => {

  it('renders properly', () => {
    const wrapper = mount(Files, {
      global: {
        plugins: [
          createTestingPinia({
            createSpy: fn,
            initialState: {
              drive: {
                files: [
                  {
                    name: 'test file 1',
                    id: '1'
                  },
                  {
                    name: 'test file 2',
                    id: '2'
                  }
                ]
              }
            }
          })
        ],
        mocks: {
          gapi: {
            load: function() {},
            client: {
              init: function() {},
            }
          }
        }
      }
    });
    const store = useDriveStore(); // Uses the testing pinia!
    const filesList = '[data-testid=files-list]';
    expect(store.fetchFiles).toHaveBeenCalledTimes(1);
    expect(wrapper.find(filesList).text()).toContain('test file 1');
    expect(wrapper.findAll('[data-testid=files-item]')).toHaveLength(2);
  });
});

Although I have tried to mock the gapi according to the vue-test-util documentation, it seems that the documentation may only cover globals referenced by components and not those referenced by a store file.

I attempted to find if createTestingPinia offers a global mock option, but there is no mention of it in the docs: . Unfortunately, I was unable to locate comprehensive documentation on this function.

In a @pinia/testing + vue-test-utils test file, how can I mock globals referenced within a Pinia store file?

The entire codebase is open source if more context is needed:

Answer №1

This has nothing to do with Vue or Pinia-specific testing utilities. The gapi variable or import is missing and should be mocked.

In general, any dependencies that need to be replaced for testing purposes can be made injectable. Dependency injection for testability reasons is not necessary with Jest/Vitest because mocking at the module level is available. It may be more challenging to conditionally mock an import based on a specific test scenario, but third-party libraries should usually be fully mocked in unit tests, especially those that heavily depend on the DOM like gapi, as there is no real DOM in the testing environment.

The global gapi can be mocked at the beginning of the test suite:

beforeEach(() => 
  globalThis.gapi = {
    load: vi.fn()
  }
});

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

Steps to dynamically update a button's href based on the active status of a class

I am facing a challenge in using Js and Jquery to dynamically change the href of a button based on the presence of a specific class. I am relatively new to Javascript and unsure if I am approaching this problem in the best way. Any suggestions on how to re ...

Exploring the power of Vue.js: implementing radio buttons within a v-for iteration

I am currently working on implementing radio buttons to allow users to select one of the photos as their profile picture: <ul v-for="p in myPhotos"> <li> <div> <div> <div> photo id: {{p.imgId}} ...

Creating a dynamic process for assigning unique identifiers to objects in JavaScript

Struggling with JavaScript while working on a project in .Net. I am looking for a way to trigger a click on the 'generate' button that extracts data from the dropdown menu after the click event. I have a button named 'btn', which, whe ...

IE page refresh causing jQuery blur to malfunction

Our website features a textbox with a watermark that appears and disappears based on focus and blur events in jQuery. However, we have encountered a unique issue with Internet Explorer browsers. Whenever a user clicks on the textbox, the watermark disapp ...

Looking for a substitute for a promise within Array.map or a loop?

Seeking guidance on resolving a specific issue. The upload component I'm working with allows for multiple file uploads, resulting in the onDrop function returning both accepted and rejected files (based on extension and size). Among the accepted file ...

Exploring the combination of v-for and v-if in Vue framework

I am in possession of an array of photos and I require, prior to the page loading, to change the SRC of the image. Here is an example: images: [ { name: 'Car', image: require('../assets/car.png') }, { name: 'Book', ima ...

Setting a preloaded model as a property value in Three.js object

I'm fairly new to Three.js and I have a question. Is it possible to load a 3D model into Three.js and maintain that loaded model as a value in my object? The issue is, when I try to do this, I encounter a problem where I can't access this.instanc ...

The Vue.js v-on:mouseover function is not functioning properly in displaying the menu

When I hover over a LI tag, I want to display a menu. I have successfully implemented it using a simple variable with the following code: @mouseover="hoverFormsControls=true" @mouseleave="hoverFormsControls=false" However, when I attempted to use an arr ...

Unable to load new page using Selenium and Chromedriver

Currently, I am in the process of learning how to utilize Selenium with Python. As a beginner exercise, I have been attempting to click a button on a webpage. Although I have been able to successfully locate the button and click on it, an unusual issue ari ...

Tips for resolving the error message "TypeError: Converting circular structure to JSON"

I had a straightforward query where I needed to select all from the aliases table. Everything was working fine until I ran npm update. TypeError: Converting circular structure to JSON public async fetchAliases(req: Request, res: Response): Promise< ...

The output of new Date() varies between app.js and ejs

app.get("/test",function(req,res){ var d = new Date(); res.send(d); }); When I access mydomain/test, it displays the output "2019-03-19T04:50:47.710Z" which is in UTC. app.get("/testejs",function(req,res){ res.render("testejs");}); Below is the content ...

Using jQuery/Javascript to create a dynamic content slider

I am currently working on developing a straightforward content slider. When the page loads, my goal is to display only one item (including an image and some content) and provide a navigation system for users to move through the items. Below is the basic H ...

What is the mechanism behind the functionality of the input type=number spinner in browsers?

I want to recreate the spinner feature found in input type=number. <input type=number step=0.3/> When clicking on the up arrow of the spinner, the value increases by 0.3 (0.3, 0.6 , 0.9 , 1.2 , 1.5 ...etc). However, if the current value is 1.4 and ...

Updating Data with Ajax in Laravel

Hey there, could you walk me through the process of updating with Ajax? I'm working with Laravel I prefer using HTML and Ajax only Here are my routes: Route::post('/post/homepage', 'AdminController@HomePage'); ...

Maintaining Vue Router's functionality by utilizing Vue router links within Vue3

Is there a way to maintain the state of first view components when routing between views, without resetting them? Thank you for any help in advance. <div> <div id="titleControl"> <router-link :to="{ name: 'controls& ...

Running a React application on a single server with the ability to serve multiple domains

Is it feasible to host a React App on an IP (X.XXX.XXX.XX) server and render different applications based on the URL pointing to this server? For instance, keeping the application on one server while multiple companies point their DNS's to the server ...

What is the best way to include additional text in my scroll-to-top button that already features an icon?

I'm completely new to this industry, so I could really use some assistance. Despite trying every possible solution, nothing seems to be working for me. Currently, I am looking to add some text to my scroll-to-top button, which already exists with an ...

Enhance your online shopping experience with the dynamic feature of adding product bundles to your

I am facing an issue where my code is not adding the product bundles to the cart using AJAX, however, it works perfectly fine with simple and variable products. If I disable the AJAX call function, the code works but unfortunately, it results in the page ...

Puppeteer encountered an error when trying to evaluate the script: ReferenceError: TABLE_ROW_SELECTOR was not defined

https://i.stack.imgur.com/PJYUf.jpg Recently, I started exploring pupeteer and node while using vscode. My goal is to log into a website and scrape a table. So far, this is what I have: (async () => { const browser = await puppeteer.launch({ headle ...

Tips for passing a variable containing an image source from Node.js to a Jade file

Below is the code snippet from my index.js file, where I'm using express for routing: var express = require('express'); var router = express.Router(); /* GET home page. */ router.get('/', function(req, res){ var db = req.db; ...