]> git.ipfire.org Git - thirdparty/vuejs/pinia.git/commitdiff
test: mapStores
authorEduardo San Martin Morote <posva13@gmail.com>
Tue, 6 Apr 2021 17:52:56 +0000 (19:52 +0200)
committerEduardo San Martin Morote <posva@users.noreply.github.com>
Fri, 9 Apr 2021 11:08:56 +0000 (13:08 +0200)
__tests__/mapHelpers.spec.ts [new file with mode: 0644]
src/mapHelpers.ts

diff --git a/__tests__/mapHelpers.spec.ts b/__tests__/mapHelpers.spec.ts
new file mode 100644 (file)
index 0000000..0b9ac83
--- /dev/null
@@ -0,0 +1,113 @@
+import { createPinia, defineStore, mapStores, PiniaPlugin } from '../src'
+import { createLocalVue, mount } from '@vue/test-utils'
+import VueCompositionAPI, {
+  nextTick,
+  defineComponent,
+} from '@vue/composition-api'
+
+describe('Map Helpers', () => {
+  const useCartStore = defineStore({ id: 'cart' })
+  const useStore = defineStore({
+    id: 'main',
+    state: () => ({
+      a: true,
+      n: 0,
+      nested: {
+        foo: 'foo',
+        a: { b: 'string' },
+      },
+    }),
+    getters: {
+      double() {
+        return this.n * 2
+      },
+      notA() {
+        return !this.a
+      },
+    },
+    actions: {
+      doubleCount() {
+        this.n = this.n * 2
+      },
+    },
+  })
+
+  const localVue = createLocalVue()
+  localVue.use(VueCompositionAPI)
+  localVue.use(PiniaPlugin)
+
+  describe('mapStores', () => {
+    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: `<p @click="fromStore.n++">{{ fromStore.n }}</p>`,
+        computed: {
+          ...mapStores(useStore),
+          fromStore,
+        },
+      })
+
+      const wrapper = mount(Component, { localVue, 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: `<p @click="fromStore.n++">{{ mainStore.n }} {{ fromStore.n }} {{ cartStore.$id }}</p>`,
+        computed: {
+          ...mapStores(useStore, useCartStore),
+          fromStore,
+        },
+      })
+
+      const wrapper = mount(Component, { localVue, pinia })
+      expect(wrapper.text()).toBe('0 0 cart')
+      await nextTick()
+      // NOTE: it seems to be the same as the number of stores, probably because
+      // we use Vue.set
+      expect(fromStore).toHaveBeenCalledTimes(2)
+
+      await wrapper.trigger('click')
+      expect(wrapper.text()).toBe('1 1 cart')
+      expect(fromStore).toHaveBeenCalledTimes(2)
+      await wrapper.trigger('click')
+      expect(wrapper.text()).toBe('2 2 cart')
+      expect(fromStore).toHaveBeenCalledTimes(2)
+    })
+  })
+
+    // mapStores(useStore).main().$patch({ n: 20 })
+
+    const wrapper = mount(Component, { localVue, pinia })
+    expect(wrapper.vm.main).toBeDefined()
+    const store = useStore()
+    expect(wrapper.text()).toBe('0 0')
+    expect(fromStore).toHaveBeenCalledTimes(1)
+
+    store.n++
+    await nextTick()
+    expect(wrapper.text()).toBe('1 1')
+    expect(fromStore).toHaveBeenCalledTimes(1)
+  })
+})
index 6eff181bcb09e1600d4506885f4c2d62376e5eda..cdca90ea49fba01f4427027c1c60bdaa2900b64a 100644 (file)
@@ -7,7 +7,7 @@ type StoreObject<S> = S extends StoreDefinition<
   infer Actions
 >
   ? {
-      [Id in Ids]: () => Store<Ids, State, Getters, Actions>
+      [Id in `${Ids}Store`]: () => Store<Id, State, Getters, Actions>
     }
   : {}
 
@@ -15,12 +15,32 @@ type Spread<A extends readonly any[]> = A extends [infer L, ...infer R]
   ? StoreObject<L> & Spread<R>
   : unknown
 
+/**
+ * Allows using stores without the composition API (`setup()`) by generating an object to be spread in the `computed` field of a component.
+ *
+ * @example
+ * ```js
+ * export default {
+ *   computed: {
+ *     // other computed properties
+ *     ...mapStores(useUserStore, useCartStore)
+ *   },
+
+ *   created() {
+ *     this.userStore // store with id "user"
+ *     this.cartStore // store with id "cart"
+ *   }
+ * }
+ * ```
+ *
+ * @param stores - list of stores to map to an object
+ */
 export function mapStores<Stores extends unknown[]>(
   ...stores: [...Stores]
 ): Spread<Stores> {
   return stores.reduce((reduced, useStore) => {
-    // @ts-ignore
-    reduced[useStore.$id] = function () {
+    // @ts-ignore: $id is added by defineStore
+    reduced[useStore.$id + 'Store'] = function () {
       return (useStore as GenericStoreDefinition)((this as any).$pinia)
     }
     return reduced