How to properly pass the this value when unit testing a Vuex action in TypeScript?

Currently, I am working with Vuex in a TypeScript configuration and facing challenges while attempting to unit test an action due to difficulty in setting the this parameter of the action method.

The action looks something like this:

export const login: ActionHandler<AuthState, RootState> = ({ commit },
  creds: { email: string, password: string }) => {
};

Here is how the test is structured:

  describe('actions', () => {
    test('login should call API with credentials', () => {
      (ApiService.post as jest.Mock).mockResolvedValue({
        data: { userId: 1234 }
      });
      const commit = jest.fn();
      login({ commit }, { email: '<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="13666076617d727e7653747e727a7a3a767077">[email protected]</a>', password: 'Pa$$w0rd123' });
    });
  });

The issue arises from the fact that the ActionHandler has a signature like:

login(this: Store<RootState>, injectee: ActionContext<AuthState, RootState>, payload?: any): any
.

As a result, TypeScript presents the following error message:

The 'this' context of type 'void' is not assignable to method's 'this' of type 'Store<RootState>'.ts(2684)

I am struggling to determine how to assign an instance of Store<RootState> as the this of the login action. Could it be possible that I am approaching this incorrectly? While I understand that this problem might not occur if I were using vanilla JS, my preference is to continue utilizing TypeScript if feasible.

Answer №1

I have discovered two approaches to tackle a similar issue. Feel free to give them a try.

1. Approach: Binding this (highly recommended)

To make it work, you need to bind your store to this and utilize the bound action:

// Bind
const loginBound = login.bind(store)

// Call
loginBound( /* ... */ );

In order for this solution to be effective, you may need to define additional mocks. Refer to "Appendix: Complete code solution" below for details.

2. Approach: Utilize Function casting

By opting for this method, you can satisfy Typescript requirements, but essentially you are just sidestepping the actual problem (essentially turning off type checks for the action).

// Bind
const loginCast = login as Function

// Call
loginCast( /* ... */ );

Appendix: Complete code solution

If utilizing the 1st approach (binding this), you will likely need to provide a comprehensive action context. Here is how the entire solution should look like:

describe('actions', () => {
  test('login should call api with credentials', () => {
    (ApiService.post as jest.Mock).mockResolvedValue({
      data: { userId: 1234 }
    });

    // Bind `this`
    const loginBound = login.bind(store)

    // Providing full action context
    const actionContext: ActionContext<AuthState, RootState> = {
      dispatch: jest.fn(),
      commit: jest.fn(),
      state: {}, // Your auth state mock goes here
      getters: {},
      rootState: {}, // Your root state mock goes here
      rootGetters: {}
    };

    // Call
    loginBound(actionContext, { email: '<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="691c1a0c1b0708040c290e04080005470a06">[email protected]</a>', password: 'Pa$$w0rd123' });
  });
});

Answer №2

This is the approach I took

(authenticate as Function)({ commit: jest.fn() }, { email: 'username.....', password: '..' });

In my specific case, I have a detailed state structure:

import { Module } from 'vuex';

const data: Module<any, any> = {
  state: { .... },
  mutations: { .... },
  getters: { ... },
  actions: {
    setData({ commit }, newData: string[]) {
      commit('SET_DATA_LIST', newData);
    }
  }
}

When testing with jest, I execute it like this:

(data.actions!.setData as Function)(context, ['1001', '1002']);

This particular line eliminates TypeScript validations for ActionContext<any, any>

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

Interpolating strings with Angular does not result in binding

My goal is to populate the template using string interpolation. However, when I attempt to reference the variable in the template, I receive the following error: core.js:1350 ERROR TypeError: Cannot read property 'status' of undefined. HTML ...

Jasmine: The Ultimate Tool for Monitoring Controller Methods

In my controller, I have a single method and a watcher that I want to ensure gets invoked when a model is updated. Is there a way to verify if the watcher listener is being called? var app = angular.module('app', []); app.controller('Main ...

Experiencing SyntaxError when utilizing rewire and mocha for Node.js testing. Unexpected token encountered

Trying to test a function exported from a nodejs file, I am utilizing q to manage promises. The function returns a promise that is either resolved or rejected internally through a callback. Within this callback, another function from a different location i ...

How do I inform Jest that spaces should be recognized as spaces?

Here is some code snippet for you to ponder: import { getLocale } from './locale'; export const euro = (priceData: number): string => { const priceFormatter = new Intl.NumberFormat(getLocale(), { style: 'currency', currenc ...

A versatile component loader that serves as a shared service for resolving components

Utilizing the ComponentFactoryResolver is necessary to dynamically incorporate components. In my scenario, I have a modal window that is loaded dynamically upon clicking a button on the view. The code for this operation appears as follows: let componentFa ...

What is the process for transforming a string literal type into the keys of a different type?

Imagine having a string literal type like this: type Letters = "a" | "b" | "c" | "d" | "e"; Is there a way to create the following type based on Letters? type LetterFlags = {a: boolean, b: boolean, c: bool ...

A guide on leveraging typeof within function parameters to ensure accurate variances

Let's create a simple class hierarchy and a list of instances. The goal is to filter items from the list of instances based on their class. There are a couple of challenges: We cannot use the typeof T syntax. How can this be written? We cannot decla ...

Tips for effectively hydrating Redux state in Next.js upon page refresh?

I'm experiencing an issue with hydrating user data from local storage upon app reload or page refresh. My project utilizes NextJS for the frontend, and for managing state across the application I rely on redux-toolkit and next-redux-wrapper. Upon us ...

Debouncing issue in Vuejs 2 when using watch option

After adding a debounce function in VueJs, I noticed that it works perfectly when passing the number of milliseconds directly. However, if I use a reference to a prop, it seems to be ignored. Here's a summary of the props: props : { debounce : { ...

An issue occurred in React 16.14.0 where the error "ReferenceError: exports is not defined" was not properly

As the creator of the next-translate library, I have encountered a perplexing issue with an experimental version involving an error specifically related to React 16.14.0. Interestingly, upgrading React to version 17 resolves the issue, but I am hesitant to ...

A guide to successfully transferring data array values from a Parent Component to a Child Component using APIs in Angular

To transfer values of this.bookingInfo = bookings.responseObj.txnValues; array from the parent component to the bookingInfo array in my child component and then insert that data into the chartNameChartTTV.data = []; array in the child component. Here, divN ...

Are you looking for a streamlined method to create styled components with MaterialUI?

I am exploring ways to easily define convenient components in my application utilizing Material-UI. After referring to an example from the Material-UI documentation for Appbar, I attempted to incorporate some React components for better readability. My c ...

Using the function goToPage() within the TabbedHeaderPager component

I am currently working on a project that involves using TabbedHeaderPager, and I need to change tabs programmatically. I have been attempting to use the function goToPage() but have run into difficulties accessing it. I have tried passing it as a prop an ...

How about incorporating webpack, three.js demos, and typescript all together in one project?

I'm encountering difficulties when trying to integrate certain elements from threejs's examples, such as EffectComposer or Detector, with webpack and typescript. Although the necessary *.d.ts files are installed using tsd, I am struggling to mak ...

How to stop form submission when Enter key is pressed in Angular

Despite scouring stackoverflow for answers, none of the solutions have worked for me. I have tried various approaches, which I will outline below: <form (keydown.enter)="$event.preventDefault()" ...> <button (keyup.enter)="skillsHandleEnter($eve ...

A guide on implementing react-pose alongside @reach/router in a React Typescript project

I'm facing a challenge in my React app as I attempt to convert a JS example using react-pose and @reach/router to TypeScript, but unfortunately, it's not functioning properly. Below are the relevant typings from react-pose: import { ComponentTy ...

Navigating through nested JSON Objects for dropdown functionality in Angular 6 - a step-by-step guide

Currently, I am facing a challenge in Angular 6.0 where I am trying to use HttpClient to iterate through JSON data retrieved from a local file within my assets folder. Below is the sample JSON Data: [{ "configKey": [{ "user1": [{ ...

The text color remains the same even after the method has returned a value

I have a vuex query that returns a list of books. I want to display each book with a specific color based on its status. I tried using a method to determine the color name for each book and bind it to the v-icon, but it's not working as expected - no ...

How to retrieve the type of an undefined class in Typescript

I am looking for a way to determine the type of a value before using it. For example, I have values "y" and "N" that I want to convert to boolean when the field is boolean. Instead of hardcoding the field, I am looping through the object to set the value ...

Using VueJS to dynamically hide or show elements based on the selection made in a form

I'm facing a challenge with displaying device information based on selection from a form select element in Vue.js. I have an array of devices that I render using a v-for loop, but I can't figure out how to filter and display them according to the ...