]> 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>
Wed, 12 May 2021 10:06:57 +0000 (12:06 +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 31064a00531b7baeac4412fc980b2ea14ef39746..ffd31de11e8e6ec6b8aa3d8192bd2196bcbd2812 100644 (file)
@@ -120,6 +120,7 @@ function initStore<Id extends string, S extends StateTree>(
     let type: MutationType
     isListening = false
     // reset the debugger events since patches are sync
+    /* istanbul ignore else */
     if (__DEV__) {
       debuggerEvents = []
     }
@@ -147,12 +148,14 @@ function initStore<Id extends string, S extends StateTree>(
     // watch here to link the subscription to the current active instance
     // e.g. inside the setup of a component
     const options: WatchOptions = { deep: true, flush: 'sync' }
+    /* istanbul ignore else */
     if (__DEV__) {
       options.onTrigger = (event) => {
         if (isListening) {
           debuggerEvents = event
         } else {
           // let patch send all the events together later
+          /* istanbul ignore else */
           if (Array.isArray(debuggerEvents)) {
             debuggerEvents.push(event)
           } else {