]> git.ipfire.org Git - thirdparty/vuejs/pinia.git/commitdiff
test(onAction): add tests
authorEduardo San Martin Morote <posva13@gmail.com>
Wed, 12 May 2021 10:06:57 +0000 (12:06 +0200)
committerEduardo San Martin Morote <posva13@gmail.com>
Thu, 13 May 2021 11:10:20 +0000 (13:10 +0200)
__tests__/onAction.spec.ts [new file with mode: 0644]
src/store.ts

diff --git a/__tests__/onAction.spec.ts b/__tests__/onAction.spec.ts
new file mode 100644 (file)
index 0000000..5c57466
--- /dev/null
@@ -0,0 +1,200 @@
+import { createPinia, defineStore, setActivePinia } from '../src'
+import { mount } from '@vue/test-utils'
+import { nextTick } from 'vue'
+
+describe('Subscriptions', () => {
+  const useStore = () => {
+    // create a new store
+    setActivePinia(createPinia())
+    return defineStore({
+      id: 'main',
+      state: () => ({
+        user: 'Eduardo',
+      }),
+      actions: {
+        direct(name: string) {
+          this.user = name
+        },
+        patchObject(user: string) {
+          this.$patch({ user })
+        },
+        patchFn(name: string) {
+          this.$patch((state) => {
+            state.user = name
+          })
+        },
+        async asyncUpperName() {
+          return this.user.toUpperCase()
+        },
+        upperName() {
+          return this.user.toUpperCase()
+        },
+        throws(e: any) {
+          throw e
+        },
+        async rejects(e: any) {
+          throw e
+        },
+      },
+    })()
+  }
+
+  let store: ReturnType<typeof useStore>
+  beforeEach(() => {
+    store = useStore()
+  })
+
+  it('fires callback when action is called', () => {
+    const spy = jest.fn()
+    store.$onAction(spy)
+    store.$onAction(({}) => {})
+    store.direct('Cleiton')
+    expect(spy).toHaveBeenCalledTimes(1)
+    expect(spy).toHaveBeenCalledWith(
+      expect.objectContaining({
+        name: 'direct',
+        args: ['Cleiton'],
+        store,
+      })
+    )
+  })
+
+  it('removes the callback when unsubscribe is called', () => {
+    const spy = jest.fn()
+    const unsubscribe = store.$onAction(spy)
+    unsubscribe()
+    store.direct('Cleiton')
+    expect(spy).not.toHaveBeenCalled()
+  })
+
+  it('calls after with the returned value', async () => {
+    const spy = jest.fn()
+    store.$onAction(({ after }) => {
+      after(spy)
+    })
+    expect(store.upperName()).toBe('EDUARDO')
+    await nextTick()
+    expect(spy).toHaveBeenCalledTimes(1)
+    expect(spy).toHaveBeenCalledWith('EDUARDO')
+  })
+
+  it('calls after with the resolved value', async () => {
+    const spy = jest.fn()
+    store.$onAction(({ after }) => {
+      after(spy)
+    })
+    await expect(store.asyncUpperName()).resolves.toBe('EDUARDO')
+    expect(spy).toHaveBeenCalledTimes(1)
+    expect(spy).toHaveBeenCalledWith('EDUARDO')
+  })
+
+  it('calls onError when it throws', () => {
+    const spy = jest.fn()
+    store.$onAction(({ onError }) => {
+      onError(spy)
+    })
+    expect(() => store.throws('fail')).toThrow()
+    expect(spy).toHaveBeenCalledTimes(1)
+    expect(spy).toHaveBeenCalledWith('fail')
+  })
+
+  it('calls onError when it rejects', async () => {
+    const spy = jest.fn()
+    expect.assertions(3)
+    store.$onAction(({ onError }) => {
+      onError(spy)
+    })
+    await expect(store.rejects('fail')).rejects.toBe('fail')
+    expect(spy).toHaveBeenCalledTimes(1)
+    expect(spy).toHaveBeenCalledWith('fail')
+  })
+
+  it('listeners are not affected when unsubscribe is called multiple times', () => {
+    const func1 = jest.fn()
+    const func2 = jest.fn()
+    const unsubscribe1 = store.$onAction(func1)
+    store.$onAction(func2)
+    unsubscribe1()
+    unsubscribe1()
+    store.direct('Cleiton')
+    expect(func1).not.toHaveBeenCalled()
+    expect(func2).toHaveBeenCalledTimes(1)
+  })
+
+  describe('multiple store instances', () => {
+    const useStore = defineStore({
+      id: 'main',
+      state: () => ({
+        name: 'Eduardo',
+      }),
+
+      actions: {
+        changeName(name: string) {
+          this.name = name
+        },
+      },
+    })
+
+    it('triggers subscribe only once', async () => {
+      setActivePinia(createPinia())
+      const s1 = useStore()
+      const s2 = useStore()
+
+      expect(s2).not.toBe(s1)
+
+      const spy1 = jest.fn()
+      const spy2 = jest.fn()
+
+      s1.$onAction(spy1)
+      s2.$onAction(spy2)
+
+      expect(spy1).toHaveBeenCalledTimes(0)
+      expect(spy2).toHaveBeenCalledTimes(0)
+
+      s1.changeName('Edu')
+
+      expect(spy1).toHaveBeenCalledTimes(1)
+      expect(spy2).toHaveBeenCalledTimes(1)
+    })
+
+    it('removes on unmount', async () => {
+      const pinia = createPinia()
+      const spy1 = jest.fn()
+      const spy2 = jest.fn()
+
+      const wrapper = mount(
+        {
+          setup() {
+            const s1 = useStore()
+            s1.$onAction(spy1)
+          },
+          template: `<p/>`,
+        },
+        { global: { plugins: [pinia] } }
+      )
+
+      const s1 = useStore()
+      const s2 = useStore()
+
+      s2.$onAction(spy2)
+
+      expect(spy1).toHaveBeenCalledTimes(0)
+      expect(spy2).toHaveBeenCalledTimes(0)
+
+      s1.changeName('Cleiton')
+
+      expect(spy2).toHaveBeenCalledTimes(1)
+      expect(spy1).toHaveBeenCalledTimes(1)
+
+      s1.changeName('other')
+      expect(spy1).toHaveBeenCalledTimes(2)
+      expect(spy2).toHaveBeenCalledTimes(2)
+
+      await wrapper.unmount()
+
+      s1.changeName('again')
+      expect(spy1).toHaveBeenCalledTimes(2)
+      expect(spy2).toHaveBeenCalledTimes(3)
+    })
+  })
+})
index a3726ad2f117616dda7b0876b1c257d1066baf49..bb7fc6838dc13249d0fb0d066e261d7c7881fac3 100644 (file)
@@ -28,6 +28,7 @@ import {
   DefineStoreOptions,
   GenericStore,
   StoreOnActionListener,
+  MutationType,
 } from './types'
 import { useStoreDevtools } from './devtools'
 import {
@@ -120,11 +121,11 @@ function initStore<Id extends string, S extends StateTree>(
     isListening = false
     if (typeof partialStateOrMutator === 'function') {
       partialStateOrMutator(pinia.state.value[$id])
-      type = '🧩 patch'
+      type = MutationType.patchFunction
     } else {
       innerPatch(pinia.state.value[$id], partialStateOrMutator)
       partialState = partialStateOrMutator
-      type = '⤵️ patch'
+      type = MutationType.patchObject
     }
     isListening = true
     // because we paused the watcher, we need to manually call the subscriptions
@@ -145,7 +146,10 @@ function initStore<Id extends string, S extends StateTree>(
       () => pinia.state.value[$id] as UnwrapRef<S>,
       (state) => {
         if (isListening) {
-          callback({ storeName: $id, type: '🧩 in place', payload: {} }, state)
+          callback(
+            { storeName: $id, type: MutationType.direct, payload: {} },
+            state
+          )
         }
       },
       {