From: Eduardo San Martin Morote Date: Fri, 9 Jul 2021 16:45:42 +0000 (+0200) Subject: test: fix tests X-Git-Tag: v2.0.0-rc.0~85 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b9ca0cda9186e6bdf989ae40e81b3495516882d4;p=thirdparty%2Fvuejs%2Fpinia.git test: fix tests --- diff --git a/__tests__/mapHelpers.spec.ts b/__tests__/mapHelpers.spec.ts index d917e446..a0b74be9 100644 --- a/__tests__/mapHelpers.spec.ts +++ b/__tests__/mapHelpers.spec.ts @@ -13,7 +13,6 @@ import { nextTick, defineComponent } from 'vue' import { mockWarn } from 'jest-mock-warn' describe('Map Helpers', () => { - const useCartStore = defineStore({ id: 'cart' }) const useStore = defineStore({ id: 'main', state: () => ({ @@ -41,62 +40,6 @@ describe('Map Helpers', () => { describe('mapStores', () => { mockWarn() - it('mapStores computes only once when mapping one store', async () => { - const pinia = createPinia() - const fromStore = jest.fn(function () { - // @ts-ignore - return this.mainStore - }) - const Component = defineComponent({ - template: `

{{ fromStore.n }}

`, - computed: { - ...mapStores(useStore), - fromStore, - }, - }) - - const wrapper = mount(Component, { global: { plugins: [pinia] } }) - // const store = useStore() - // const other = useCartStore() - expect(wrapper.vm.mainStore).toBeDefined() - expect(wrapper.text()).toBe('0') - await nextTick() - expect(fromStore).toHaveBeenCalledTimes(1) - - await wrapper.trigger('click') - expect(wrapper.text()).toBe('1') - expect(fromStore).toHaveBeenCalledTimes(1) - await wrapper.trigger('click') - expect(wrapper.text()).toBe('2') - expect(fromStore).toHaveBeenCalledTimes(1) - }) - - it('mapStores computes only once when mapping multiple stores', async () => { - const pinia = createPinia() - const fromStore = jest.fn(function () { - // @ts-ignore - return this.mainStore - }) - const Component = defineComponent({ - template: `

{{ mainStore.n }} {{ fromStore.n }} {{ cartStore.$id }}

`, - computed: { - ...mapStores(useStore, useCartStore), - fromStore, - }, - }) - - const wrapper = mount(Component, { global: { plugins: [pinia] } }) - expect(wrapper.text()).toBe('0 0 cart') - await nextTick() - expect(fromStore).toHaveBeenCalledTimes(1) - - await wrapper.trigger('click') - expect(wrapper.text()).toBe('1 1 cart') - expect(fromStore).toHaveBeenCalledTimes(1) - await wrapper.trigger('click') - expect(wrapper.text()).toBe('2 2 cart') - expect(fromStore).toHaveBeenCalledTimes(1) - }) it('can set custom suffix', async () => { const pinia = createPinia() @@ -110,7 +53,6 @@ describe('Map Helpers', () => { const wrapper = mount(Component, { global: { plugins: [pinia] } }) // const store = useStore() - // const other = useCartStore() // @ts-expect-error: by default this shouldn't exist expect(wrapper.vm.main).toBeDefined() expect(wrapper.vm.mainStore).not.toBeDefined() diff --git a/__tests__/state.spec.ts b/__tests__/state.spec.ts index a69460a4..3f3e6e0e 100644 --- a/__tests__/state.spec.ts +++ b/__tests__/state.spec.ts @@ -1,15 +1,16 @@ -import { createPinia, defineStore, setActivePinia } from '../src' +import { createPinia, defineStore, Pinia, setActivePinia } from '../src' import { computed, nextTick, ref, watch } from 'vue' describe('State', () => { - const useStore = () => { + const useStore = (pinia?: Pinia) => { // create a new store - setActivePinia(createPinia()) + setActivePinia(pinia || createPinia()) return defineStore({ id: 'main', state: () => ({ name: 'Eduardo', counter: 0, + nested: { n: 0 }, }), })() } @@ -29,6 +30,72 @@ describe('State', () => { expect(upperCased.value).toBe('ED') }) + it('can be set with patch', () => { + const pinia = createPinia() + const store = useStore(pinia) + + store.$patch({ name: 'a' }) + + expect(store.name).toBe('a') + expect(store.$state.name).toBe('a') + expect(pinia.state.value.main.name).toBe('a') + }) + + it('can be set on store', () => { + const pinia = createPinia() + const store = useStore(pinia) + + store.name = 'a' + + expect(store.name).toBe('a') + expect(store.$state.name).toBe('a') + expect(pinia.state.value.main.name).toBe('a') + }) + + it('can be set on store.$state', () => { + const pinia = createPinia() + const store = useStore(pinia) + + store.$state.name = 'a' + + expect(store.name).toBe('a') + expect(store.$state.name).toBe('a') + expect(pinia.state.value.main.name).toBe('a') + }) + + it('can be nested set with patch', () => { + const pinia = createPinia() + const store = useStore(pinia) + + store.$patch({ nested: { n: 3 } }) + + expect(store.nested.n).toBe(3) + expect(store.$state.nested.n).toBe(3) + expect(pinia.state.value.main.nested.n).toBe(3) + }) + + it('can be nested set on store', () => { + const pinia = createPinia() + const store = useStore(pinia) + + store.nested.n = 3 + + expect(store.nested.n).toBe(3) + expect(store.$state.nested.n).toBe(3) + expect(pinia.state.value.main.nested.n).toBe(3) + }) + + it('can be nested set on store.$state', () => { + const pinia = createPinia() + const store = useStore(pinia) + + store.$state.nested.n = 3 + + expect(store.nested.n).toBe(3) + expect(store.$state.nested.n).toBe(3) + expect(pinia.state.value.main.nested.n).toBe(3) + }) + // it('watch', () => { // setActivePinia(createPinia()) // defineStore({ @@ -50,6 +117,27 @@ describe('State', () => { expect(spy).toHaveBeenCalledTimes(1) }) + it('can be given a ref', () => { + const pinia = createPinia() + const store = useStore(pinia) + + // If the ref is directly set to the store, it won't work, + // it must be set into the `store.$state` so it connects to pinia + // store.name = ref('Ed') + + // @ts-expect-error + store.$state.name = ref('Ed') + + expect(store.name).toBe('Ed') + expect(store.$state.name).toBe('Ed') + expect(pinia.state.value.main.name).toBe('Ed') + + store.name = 'Other' + expect(store.name).toBe('Other') + expect(store.$state.name).toBe('Other') + expect(pinia.state.value.main.name).toBe('Other') + }) + it('unwraps refs', () => { const name = ref('Eduardo') const counter = ref(0) diff --git a/__tests__/store.patch.spec.ts b/__tests__/store.patch.spec.ts index 8e6c1579..f6c80377 100644 --- a/__tests__/store.patch.spec.ts +++ b/__tests__/store.patch.spec.ts @@ -1,10 +1,5 @@ import { reactive, ref } from 'vue' -import { - createPinia, - defineSetupStore, - defineStore, - setActivePinia, -} from '../src' +import { createPinia, defineStore, Pinia, setActivePinia } from '../src' describe('store.$patch', () => { const useStore = () => { @@ -89,26 +84,57 @@ describe('store.$patch', () => { }) describe('skipping nested objects', () => { - const useStore = () => { + const useStore = (pinia?: Pinia) => { // create a new store - setActivePinia(createPinia()) + setActivePinia(pinia || createPinia()) return defineStore({ id: 'main', state: () => ({ arr: [] as any[], + name: 'Eduardo', item: { a: 0, b: 0 } as null | { a: number; b?: number }, }), })() } + // const useStore = (pinia?: Pinia) => { + // // create a new store + // setActivePinia(pinia || createPinia()) + // return defineSetupStore('main', () => { + // const arr = ref([] as any[]) + // const item = ref({ a: 0, b: 0 } as null | { a: number; b?: number }) - it('ref', () => { - const store = useStore() + // return { arr, item } + // })() + // } + + it('ref of primitive', () => { + const pinia = createPinia() + const store = useStore(pinia) + const name = ref('Edu') + // @ts-expect-error: because it's a ref + store.$patch({ name }) + expect(pinia.state.value.main.name).toEqual('Edu') + expect(store.$state.name).toEqual('Edu') + expect(store.name).toEqual('Edu') + }) + + it('ref of object', () => { + const pinia = createPinia() + const store = useStore(pinia) const item = ref({ a: 1, b: 1 }) const oldItem = store.item // @ts-expect-error: because it's a ref - store.$patch({ item }) + store.$state.item = item expect(oldItem).toEqual({ a: 0, b: 0 }) + expect(pinia.state.value.main.item).toEqual({ a: 1, b: 1 }) + expect(store.$state.item).toEqual({ a: 1, b: 1 }) expect(store.item).toEqual({ a: 1, b: 1 }) + + // @ts-expect-error: because it's a ref + store.$patch({ item: ref({ a: 2, b: 2 }) }) + expect(pinia.state.value.main.item).toEqual({ a: 2, b: 2 }) + expect(store.$state.item).toEqual({ a: 2, b: 2 }) + expect(store.item).toEqual({ a: 2, b: 2 }) }) it('nested ref', () => { diff --git a/__tests__/storePlugins.spec.ts b/__tests__/storePlugins.spec.ts index 74751875..89621e49 100644 --- a/__tests__/storePlugins.spec.ts +++ b/__tests__/storePlugins.spec.ts @@ -10,7 +10,6 @@ declare module '../src' { idFromPlugin: Id globalA: string globalB: string - notShared: number shared: number } @@ -134,8 +133,6 @@ describe('store plugins', () => { store.$state.shared = ref(20) } // @ts-expect-error: TODO: allow setting refs - store.notShared = ref(10) - // @ts-expect-error: TODO: allow setting refs store.shared = toRef(store.$state, 'shared') }) @@ -158,11 +155,5 @@ describe('store plugins', () => { expect(store.shared).toBe(1) expect(store2.$state.shared).toBe(1) expect(store2.shared).toBe(1) - - store.notShared = 5 - expect(store.$state).not.toHaveProperty('notShared') - expect(store.notShared).toBe(5) - expect(store2.$state).not.toHaveProperty('notShared') - expect(store2.notShared).toBe(10) }) }) diff --git a/src/mapHelpers.ts b/src/mapHelpers.ts index 47f97677..5f04f0e8 100644 --- a/src/mapHelpers.ts +++ b/src/mapHelpers.ts @@ -5,7 +5,6 @@ import { StateTree, Store, StoreDefinition, - ActionsTree, } from './types' /** @@ -55,26 +54,6 @@ export type _Spread = A extends [infer L, ...infer R] ? _StoreObject & _Spread : unknown -function getCachedStore< - Id extends string = string, - S extends StateTree = StateTree, - G extends GettersTree = GettersTree, - A /* extends ActionsTree */ = ActionsTree ->( - vm: ComponentPublicInstance, - useStore: StoreDefinition -): Store { - const cache = '_pStores' in vm ? vm._pStores! : (vm._pStores = {}) - const id = useStore.$id - return (cache[id] || - (cache[id] = useStore(vm.$pinia) as unknown as Store)) as unknown as Store< - Id, - S, - G, - A - > -} - export let mapStoreSuffix = 'Store' /** @@ -131,8 +110,10 @@ export function mapStores( return stores.reduce((reduced, useStore) => { // @ts-ignore: $id is added by defineStore - reduced[useStore.$id + mapStoreSuffix] = function (this: Vue) { - return getCachedStore(this, useStore) + reduced[useStore.$id + mapStoreSuffix] = function ( + this: ComponentPublicInstance + ) { + return useStore(this.$pinia) } return reduced }, {} as _Spread) @@ -280,13 +261,13 @@ export function mapState< ? keysOrMapper.reduce((reduced, key) => { reduced[key] = function (this: ComponentPublicInstance) { // @ts-expect-error - return getCachedStore(this, useStore)[key] + return useStore(this.$pinia)[key] } as () => any return reduced }, {} as _MapStateReturn) : Object.keys(keysOrMapper).reduce((reduced, key: keyof KeyMapper) => { reduced[key] = function (this: ComponentPublicInstance) { - const store = getCachedStore(this, useStore) + const store = useStore(this.$pinia) const storeKey = keysOrMapper[key] // for some reason TS is unable to infer the type of storeKey to be a // function @@ -411,7 +392,7 @@ export function mapActions< ...args: any[] ) { // @ts-expect-error - return (getCachedStore(this, useStore)[key] as _Method)(...args) + return (useStore(this.$pinia)[key] as _Method)(...args) } return reduced }, {} as _MapActionsReturn) @@ -422,7 +403,7 @@ export function mapActions< ...args: any[] ) { // @ts-expect-error - return getCachedStore(this, useStore)[keysOrMapper[key]](...args) + return useStore(this.$pinia)[keysOrMapper[key]](...args) } return reduced }, {} as _MapActionsObjectReturn) @@ -510,12 +491,12 @@ export function mapWritableState< reduced[key] = { get(this: ComponentPublicInstance) { // @ts-expect-error - return getCachedStore(this, useStore)[key] + return useStore(this.$pinia)[key] }, set(this: ComponentPublicInstance, value) { // it's easier to type it here as any // @ts-expect-error - return (getCachedStore(this, useStore)[key] = value as any) + return (useStore(this.$pinia)[key] = value as any) }, } return reduced @@ -525,13 +506,12 @@ export function mapWritableState< reduced[key] = { get(this: ComponentPublicInstance) { // @ts-expect-error - return getCachedStore(this, useStore)[keysOrMapper[key]] + return useStore(this.$pinia)[keysOrMapper[key]] }, set(this: ComponentPublicInstance, value) { // it's easier to type it here as any // @ts-expect-error - return (getCachedStore(this, useStore)[keysOrMapper[key]] = - value as any) + return (useStore(this.$pinia)[keysOrMapper[key]] = value as any) }, } return reduced diff --git a/src/rootStore.ts b/src/rootStore.ts index cd6b0835..a5d276f9 100644 --- a/src/rootStore.ts +++ b/src/rootStore.ts @@ -122,8 +122,8 @@ declare module '@vue/runtime-core' { $pinia: Pinia /** - * Cache of stores instantiated by the current instance. Used by map - * helpers. + * Cache of stores instantiated by the current instance. Used by devtools to + * list currently used stores. * * @internal */ diff --git a/src/store.ts b/src/store.ts index 72a95f9a..4a21dfcb 100644 --- a/src/store.ts +++ b/src/store.ts @@ -43,34 +43,10 @@ import { } from './rootStore' import { IS_CLIENT } from './env' -function innerPatch( - target: T, - patchToApply: DeepPartial -): T { - // TODO: get all keys like symbols as well - for (const key in patchToApply) { - const subPatch = patchToApply[key] - const targetValue = target[key] - if ( - isPlainObject(targetValue) && - isPlainObject(subPatch) && - !isRef(subPatch) && - !isReactive(subPatch) - ) { - target[key] = innerPatch(targetValue, subPatch) - } else { - // @ts-ignore - target[key] = subPatch - } - } - - return target -} - -const { assign } = Object - /** - * Create an object of computed properties referring to + * Create an object of computed properties referring to the root state. This + * allows direct modification of `store.state` while still changing the root + * state. * * @param rootStateRef - pinia.state * @param id - unique name @@ -95,6 +71,32 @@ function computedFromState( return reactiveObject } +function innerPatch( + target: T, + patchToApply: DeepPartial +): T { + // no need to go through symbols because they cannot be serialized anyway + for (const key in patchToApply) { + const subPatch = patchToApply[key] + const targetValue = target[key] + if ( + isPlainObject(targetValue) && + isPlainObject(subPatch) && + !isRef(subPatch) && + !isReactive(subPatch) + ) { + target[key] = innerPatch(targetValue, subPatch) + } else { + // @ts-ignore + target[key] = subPatch + } + } + + return target +} + +const { assign } = Object + export interface DefineSetupStoreOptions< Id extends string, S extends StateTree, diff --git a/src/types.ts b/src/types.ts index 3598bdac..d1eff336 100644 --- a/src/types.ts +++ b/src/types.ts @@ -286,14 +286,6 @@ export interface StoreWithState< */ $subscribe(callback: SubscriptionCallback): () => void - /** - * Array of registered action subscriptions.Set without the generics to avoid - * errors between the generic version of Store and specific stores. - * - * @internal - */ - _as: StoreOnActionListener[] - /** * @alpha Please send feedback at https://github.com/posva/pinia/issues/240 * Setups a callback to be called every time an action is about to get