Search code examples
vue.jsvuejs2jestjsvuexnuxt.js

Testing a vue app with nuxt, vuex and jest gettig not supported error


I want to Unit test my component which mutates an object in a store module when a button is clicked.

I followed the article by brandon aaskov on how to unit test nuxt plus vuex, but I'm not able to reference the nuxt store Object. I always get an output like this:


    Error: Not supported

        at Object.<anonymous> (...\tests\unit\plannerObjectSelector.spec.js:67:5) 

    > (the line NuxtStore = await import(storePath);)

        at Object.asyncJestLifecycle (...\node_modules\jest-jasmine2\build\jasmineAsyncInstall.js:53:37)
        at ...\node_modules\jest-jasmine2\build\queueRunner.js:43:12
        at new Promise (<anonymous>)
        at mapper (...\node_modules\jest-jasmine2\build\queueRunner.js:26:19)
        at ...\node_modules\jest-jasmine2\build\queueRunner.js:73:41

This is the Component i want to test:


     <template>
   <v-container>
     <v-row v-for="object in plannerObjects" :key="object.id">
       <v-btn
         v-if="object.id == activeObjectId"
         color="primary"
         class="button"
         block
         large
         tile
         @click="objectSelected(object)"
       >
         {{ object.name }}
       </v-btn>
       <v-btn
         v-else
         color="primary"
         block
         large
         text
         @click="objectSelected(object)"
         :ref="'unSelectedBtn-' + object.id"
       >
         {{ object.name }}
       </v-btn>

     </v-row>
   </v-container>
 </template>

 <script>
 export default {
   name: "spaceSelector",
   props: {
     plannerObjects: {
       type: Array,
       required: true
     }
   },
   data: () => ({
     activeObjectId: -1
   }),
   methods: {
     objectSelected(object) {
       console.log("Sroe object", object);
       this.$store.commit("planner/setActivePlannerObject", object);
       this.activeObjectId = object.id;
       console.log(this.$store.getters["planner/activePlannerObject"]);
     }
   }
 };
 </script>

 <style scoped>
 .button {
   width: 190px;
 }
 </style>
 ```



this is my jest.config.js file: 

module.exports = {
  preset: "@vue/cli-plugin-unit-jest/presets/no-babel",
  setupFiles: ["<rootDir>/tests/unit/index.js"],
  globalSetup: "<rootDir>/jest.setup.js"
};```

jest.setup.js:


    import { Nuxt, Builder } from "nuxt";
    import nuxtConfig from "./nuxt.config";

    // these boolean switches turn off the build for all but the store
    const resetConfig = {
      loading: false,
      loadingIndicator: false,
      fetch: {
        client: false,
        server: false
      },
      features: {
        store: true,
        layouts: false,
        meta: false,
        middleware: false,
        transitions: false,
        deprecations: false,
        validate: false,
        asyncData: false,
        fetch: false,
        clientOnline: false,
        clientPrefetch: false,
        clientUseUrl: false,
        componentAliases: false,
        componentClientOnly: false
      },
      build: {
        indicator: false,
        terser: false
      }
    };

    // we take our nuxt config, lay the resets on top of it,
    // and lastly we apply the non-boolean overrides
    const config = Object.assign({}, nuxtConfig, resetConfig, {
      mode: "spa",
      srcDir: nuxtConfig.srcDir,
      ignore: ["**/components/**/*", "**/layouts/**/*", "**/pages/**/*"]
    });

    const buildNuxt = async () => {
      const nuxt = new Nuxt(config);
      await new Builder(nuxt).build();
      return nuxt;
    };

    module.exports = async () => {
      const nuxt = await buildNuxt();

      // we surface this path as an env var now
      // so we can import the store dynamically later on
      process.env.buildDir = nuxt.options.buildDir;
    };

unit/index.js


    import Vue from "vue";
    import Vuetify from "vuetify";
    Vue.config.productionTip = false;
    Vue.use(Vuetify);

and finally my test class:


    import { shallowMount } from "@vue/test-utils";
    import plannerObjectSelector from "../../components/core/bars/planner/plannerObjectSelector";
    import { __createMocks as createStoreMocks } from "../../store";
    import _ from "lodash";

    import { createLocalVue } from "@vue/test-utils";

    import Vuex from "vuex";
    import Vuetify from "vuetify";

    var plannerObjects = [
    {
    id:0
    }
    ];
    const factory = () => {
      return shallowMount(plannerObjectSelector, {
        propsData: {
          plannerObjects: plannerObjects
        }
      });
    };

    describe("plannerObjectSelector.vue", () => {
      const localVue = createLocalVue();
      localVue.use(Vuex);
      localVue.use(Vuetify);

      // to use Store
      let NuxtStore;
      let store;

      beforeAll(async () => {
        // note the store will mutate across tests
        const storePath = `${process.env.buildDir}/store/index.js`;
        NuxtStore = await import(storePath);
      });

      beforeEach(async () => {
        store = await NuxtStore.createStore();
      });  

      it("renders", () => {
        const wrapper = factory();
        expect(wrapper.exists()).toBe(true);
      });

      it("buttonClickedStoresObjectInStore", () => {
        const wrapper = factory();
        var btnref = "unSelectedBtn-0";
        const btn = wrapper.find({ ref: btnref });
        btn.trigger("click");
        // look whats in our Store
        let plannerObject = store.getters["planner/activePlannerObject"];
        console.log(plannerObject);
        expect(plannerObject).toBe(plannerObjects[0]);
      });

      test("mounts properly", () => {
        const wrapper = factory();
        expect(wrapper.isVueInstance()).toBeTruthy();
      });

      test("renders properly", () => {
        const wrapper = factory();
        expect(wrapper.html()).toMatchSnapshot();
      });
    });

And this is my folder structure: folder Structure I would be thankful for any advice.


Solution

  • It turns out pretty simple, just test the function directly, even though I had banged my head for some hours by going through the documents just like you.

    // ~/store/getters.ts
    
    import { GetterTree } from 'vuex'
    import { RootState } from '~/store/state'
    
    export default {
      [Getters.KEY.IS_SIGN_IN]: (state: RootState): boolean => {
        return Boolean(state.currentUser)
      }
    } as GetterTree<RootState, RootState>
    
    // ~/store/__tests__/getters.test.js (do not use .ts)
    
    import state from '~/store/state'
    import getters, { Getters } from '~/store/getters'
    
    describe('getters', () => {
      it(Getters.KEY.IS_SIGN_IN, () => {
        // currentUser: undefined
        expect(getters[Getters.KEY.IS_SIGN_IN](state)).toBe(false)
    
        // SignIn: true
        state.currentUser = { ...$mockData.currentUser }
        expect(getters[Getters.KEY.IS_SIGN_IN](state)).toBe(true)
      })