Skip to content

Q: How to setup @vue/test-utils? #25

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
viT-1 opened this issue Jan 17, 2020 · 27 comments
Closed

Q: How to setup @vue/test-utils? #25

viT-1 opened this issue Jan 17, 2020 · 27 comments
Labels
needs minimal reproducible example See https://stackoverflow.com/help/minimal-reproducible-example

Comments

@viT-1
Copy link

viT-1 commented Jan 17, 2020

I changed my code to use direct-vuex in actions, browser seems to work without errors, but when I run jest tests, test suite failed to run, cause of typing when I mount component with store

src/unique.blocks/SomeForm/SomeForm.spec.ts:30:19 - error TS2769: No overload matches this call.
      Overload 1 of 3, '(component: VueClass<SomeForm>, options?: ThisTypedMountOptions<SomeForm> | undefined): Wrapper<SomeForm>', gave the following error.
        Argument of type '{ store: { readonly state: { modSomeForm: { iamSelect: { value: IOption | null; data: IData; }; }; }; getters: { modSomeForm: { readonly someValue: IOption | null;
readonly someValues: IOption[]; }; }; commit: { ...; }; dispatch: { ...; }; original: { ...; }; }; localVue: VueConstructor<...>; }' is not assignable to parameter of type 'ThisTypedMountOptions<SomeForm>'.
          Type '{ store: { readonly state: { modSomeForm: { iamSelect: { value: IOption | null; data: IData; }; }; }; getters: { modSomeForm: { readonly someValue: IOption | null; readonly someValues: IOption[]; }; }; commit: { ...; }; dispatch: { ...; }; original: { ...; }; }; localVue: VueConstructor<...>; }' is not assignable to type 'MountOptions<SomeForm>'.
            Types of property 'store' are incompatible.
              Type '{ readonly state: { modSomeForm: { iamSelect: { value: IOption | null; data: IData; }; }; }; getters: { modSomeForm: { readonly someValue: IOption | null; readonly someValues: IOption[]; }; }; commit: { ...; }; dispatch: { ...; }; original: { ...; }; }' is missing the following properties from type 'Store<any>': replaceState, subscribe, subscribeAction, watch, and 3 more.
      Overload 2 of 3, '(component: ComponentOptions<Vue, DefaultData<Vue>, DefaultMethods<Vue>, DefaultComputed, PropsDefinition<Record<string, any>>, Record<...>>, options?: ThisTypedMountOptions<...> | undefined): Wrapper<...>', gave the following error.
        Value of type 'typeof SomeForm' has no properties in common with type 'ComponentOptions<Vue, DefaultData<Vue>, DefaultMethods<Vue>, DefaultComputed, PropsDefinition<Record<string, any>>, Record<...>>'. Did you mean to call it?
      Overload 3 of 3, '(component: FunctionalComponentOptions<Record<string, any>, PropsDefinition<Record<string, any>>>, options?: MountOptions<Vue> | undefined): Wrapper<...>', gave the following error.
        Argument of type 'typeof SomeForm' is not assignable to parameter of type 'FunctionalComponentOptions<Record<string, any>, PropsDefinition<Record<string, any>>>'.
          Property 'functional' is missing in type 'typeof SomeForm' but required in type 'FunctionalComponentOptions<Record<string, any>, PropsDefinition<Record<string, any>>>'.

    30          const wrapper = mount(SomeForm, { store, localVue });
                                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

      src/unique.blocks/SomeForm/SomeForm.spec.ts:30:25
        30      const wrapper = mount(SomeForm, { store, localVue });
                                      ~~~~~~~~
        Did you mean to use 'new' with this expression?
      node_modules/vue/types/options.d.ts:132:3
        132   functional: boolean;
              ~~~~~~~~~~
        'functional' is declared here.

What am I doing wrong?

@viT-1
Copy link
Author

viT-1 commented Jan 17, 2020

Cutted jest code:

import { createLocalVue, mount } from '@vue/test-utils';
import Vuex from 'vuex';
import { createDirectStore } from 'direct-vuex';

import { modSomeForm } from './store';
import { SomeForm } from './index'; // with vue-class-component decorated

const localVue = createLocalVue();
localVue.use(Vuex);

const storeConf = {
	modules: {
		modSomeForm,
	},
};

const { store } = createDirectStore(storeConf);

describe('@Component SomeForm', () => {
	it('all mock data are rendered', () => {
		expect.assertions(1);

		const wrapper = mount(SomeForm, { store, localVue });
		wrapper.vm.$nextTick();

		expect(...).toBe(true);
	});
});

@viT-1
Copy link
Author

viT-1 commented Jan 17, 2020

Just updated config to see problem directly:
npm run test

@paleo
Copy link
Collaborator

paleo commented Jan 17, 2020

Maybe:

		const wrapper = mount(SomeForm, { store: store.original, localVue });

@viT-1
Copy link
Author

viT-1 commented Jan 17, 2020

This fix is helped, now I got another error (test suite is still failed to run):

TypeError: Cannot read property 'getters' of undefined
...
at forEachValue (../node_modules/vuex/dist/vuex.common.js:80:20)
      at ModuleCollection.register (../node_modules/vuex/dist/vuex.common.js:210:5)
      at new ModuleCollection (../node_modules/vuex/dist/vuex.common.js:171:8)
      at new Store (../node_modules/vuex/dist/vuex.common.js:322:19)
      at createDirectStore (../node_modules/direct-vuex/dist/direct-vuex.umd.js:17:24)
      at Object.<anonymous> (VueApp/store/VueApp.store.ts:19:47)

Strange, but in VueApp.store.ts (which is seems not used in SomeForm.spec.ts)
export const { store, moduleActionContext } = createDirectStore(storeConf);

May be it is because of using standard Vuex getters, not createGetters.

Similar issue: championswimmer/vuex-module-decorators#63

@viT-1
Copy link
Author

viT-1 commented Jan 17, 2020

Also another test suite (SomeForm/store/actions.spec.ts) failed to run:

src/unique.blocks/SomeForm/store/actions.spec.ts:13:31 - error TS2345: Argument of type '{ dispatch: jest.Mock<any, any>; }' is not assignable to parameter of type 'ActionContext<IState, IState>'.
      Type '{ dispatch: Mock<any, any>; }' is missing the following properties from type 'ActionContext<IState, IState>': commit, state, getters, rootState, rootGetters

    13          await actions.getSomeValues({ dispatch }, { label: 'Рим' });

It seems context parameter in test (or actions) code should be changed but I don't understand how?
On classic Vuex In actions I am using destructuring input parameter, but now I am using ActionContext from 'vuex'.

@viT-1
Copy link
Author

viT-1 commented Jan 17, 2020

Can't find any example of using direct-vuex actions with tests =(

@viT-1
Copy link
Author

viT-1 commented Jan 20, 2020

About getters same error.
Fixed this in jest.config.js, for example:

moduleNameMapper: {
	// special fix (not included in tsconfig.paths.json) for Vue warning about version of Vue
	'vue$': 'vue/dist/vue.common.dev.js',
	// fix for 'getters' error https://github.com/vuejs/vuex/issues/264#issuecomment-23694847
	'vue': 'vue/dist/vue.js',
},

After fix I have another errors, investigating...

@viT-1
Copy link
Author

viT-1 commented Jan 20, 2020

vuejs/vue-test-utils#363

@viT-1
Copy link
Author

viT-1 commented Jan 20, 2020

I have located problem with direct-vuex:
Configured jest test with createDirectStore:

const storeConf = {
	modules: {
		modSomeForm,
	},
};
const { store: _store } = createDirectStore(storeConf);
const store = _store.original;
...
const wrapper = mount(SomeForm, { store, localVue });

If I use classic actions, all is ok: SomeForm.spec.ts suite is runned & actions.spec.ts is runned.
If I change actions.ts to using direct-vuex (just uncomment lines 3, 6, 12, 22, 23 & comment 24, 25)
I have a problem with getters error (SomeForm.spec.ts suite is failed to run), but actions.spec.ts is ok - test suite is runned & passed.

@paleo please check this by cloning project & npm run test

@paleo
Copy link
Collaborator

paleo commented Jan 20, 2020

When I execute npm run test:

> tsc --project ./src/tsconfig.dev.json --noEmit && eslint **/*.{ts,json,js} *.* .*.js


Oops! Something went wrong! :(

ESLint: 6.8.0.

No files matching the pattern "express.node" were found.
Please check for typing mistakes in the pattern.

npm ERR! code ELIFECYCLE
npm ERR! errno 2
npm ERR! [email protected] lint: `tsc --project ./src/tsconfig.dev.json --noEmit && eslint **/*.{ts,json,js} *.* .*.js`
npm ERR! Exit status 2

Is it the error I should see or another?
(On Node 12.)

@viT-1
Copy link
Author

viT-1 commented Jan 20, 2020

Strange, you can remove lint from pretest by renaming in package.json scripts "pretest" to "pre-something-test".

Is it the error I should see or another?

No, you should see

jest --no-cache --silent -c ./src/jest.config.js
PASS src/unique.blocks/SomeForm/store/mutations.spec.ts
...
PASS  src/unique.blocks/SomeForm/store/actions.spec.ts
...
PASS  src/unique.blocks/SomeForm/SomeForm.spec.ts

and after commening / commenting out in actions, you should see

jest --no-cache --silent -c ./src/jest.config.js
PASS src/unique.blocks/SomeForm/store/mutations.spec.ts
...
PASS  src/unique.blocks/SomeForm/store/actions.spec.ts
...
FAIL  src/unique.blocks/SomeForm/SomeForm.spec.ts

@viT-1
Copy link
Author

viT-1 commented Jan 21, 2020

@paleo, 'getters' problem looks like code trying to get getters from root store instead of module substore.

@viT-1
Copy link
Author

viT-1 commented Jan 21, 2020

I see only one workaround: using mocks with classic Vuex for actions in test suites & direct-vuex actions for browser & type checking.

@paleo
Copy link
Collaborator

paleo commented Jan 21, 2020

Fyi, in this code:

export const storeConf = {
	namespaced: true as true,
	actions: createActions(actions),
	// getters: createGetters<IState>()(getters),
	getters,
	mutations: createMutations<IState>()(mutations),
	state: defaultState,
	// syntax recommendation https://itnext.io/use-a-vuex-store-with-typing-in-typescript-without-decorators-or-boilerplate-57732d175ff3
	// but we declare type with const above
	// state: defaultState as IState,
} as const;

export const modSomeForm = createModule(storeConf);

createModule, createActions, createMutations are useless.

These functions provides autocompletion and typing in the implementation passed as a parameter. But if you declare first a variable then you pass it in these functions, they provide nothing.

Additionally, storeConf and modSomeForm are exactly the same object with the same typing.

@paleo
Copy link
Collaborator

paleo commented Jan 21, 2020

If I use classic actions, all is ok: SomeForm.spec.ts suite is runned & actions.spec.ts is runned.
If I change actions.ts to using direct-vuex (just uncomment lines 3, 6, 12, 22, 23 & comment 24, 25)
I have a problem with getters error (SomeForm.spec.ts suite is failed to run), but actions.spec.ts is ok - test suite is runned & passed.

I reproduced the error. But your code is complex and I haven't enough time to understand. Maybe the error is an effect of the cyclic dependency because of moduleActionContext. In your unit tests it is necessary to imports the files in the same order than during a regular execution of the project.

@viT-1
Copy link
Author

viT-1 commented Jan 21, 2020

I have some fixes (not committed yet, it will be soon).

@viT-1
Copy link
Author

viT-1 commented Jan 21, 2020

It is ready viT-1/systemjs-ts-es6-vue@7ce884a
For getting error about 'getters' now comment line 14 with mocking actions is enough.

@paleo paleo added the needs minimal reproducible example See https://stackoverflow.com/help/minimal-reproducible-example label Feb 2, 2020
@paleo
Copy link
Collaborator

paleo commented Feb 14, 2020

Hi. Maybe localGetterContext and localActionContext could help. If you don't need to access to rootXxx from action contexts, then localActionContext should help avoiding the circular dependency.

@viT-1
Copy link
Author

viT-1 commented Feb 14, 2020

@paleo Thank you, I'll try that. And If I can test actions without mocking, I'll closing the issue.

@viT-1
Copy link
Author

viT-1 commented Feb 14, 2020

@paleo Yes, it's working.

In actions.ts just changed to

import { localActionContext } from 'direct-vuex';
const getTypedContext = (ctx: any) => localActionContext(ctx, modSomeForm);

instead of

import { moduleActionContext } from '~/VueApp/store';
const getTypedContext = (ctx: any) => moduleActionContext(ctx, modSomeForm);

And I can remove standard actions mocking!

@viT-1 viT-1 closed this as completed Feb 14, 2020
@viT-1
Copy link
Author

viT-1 commented Feb 14, 2020

related: #29 (comment)

@viT-1
Copy link
Author

viT-1 commented Feb 14, 2020

With node (jest)
const getTypedContext = (ctx: any) => localActionContext(ctx, modSomeForm);
is ok,
but with browser I have error:

Uncaught (in promise) TypeError: localActionContext is not a function
    at getTypedContext (actions.ts:16)
    at actions.ts:27

@viT-1 viT-1 reopened this Feb 14, 2020
@paleo
Copy link
Collaborator

paleo commented Feb 14, 2020

Indeed, I forgot to export these new functions as properties of default.
Update and retry please.

@viT-1
Copy link
Author

viT-1 commented Feb 14, 2020

v 0.10.4 esm bundle is working, but ie11 (with SystemJS) is not:
Unhandled promise rejection TypeError: Object is not support property or method "localActionContext"

You can try my project on last commit npm run deploy && npm run www

@viT-1
Copy link
Author

viT-1 commented Feb 14, 2020

@paleo, default is good but i configured named imports too (in VueApp.store.ts)

@paleo
Copy link
Collaborator

paleo commented Feb 18, 2020

Just update in importmap.system.json. The tools you use are very weird.

@paleo paleo closed this as completed Feb 18, 2020
@viT-1
Copy link
Author

viT-1 commented Feb 25, 2020

@paleo Sorry for wasting your time, thank you very much!
Just changed importmap.system.json in my project & all working is ok.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
needs minimal reproducible example See https://stackoverflow.com/help/minimal-reproducible-example
Projects
None yet
Development

No branches or pull requests

2 participants