]> git.ipfire.org Git - thirdparty/vuejs/pinia.git/commitdiff
fix: consistent computation of computed in tests with storeToRefs
authorEduardo San Martin Morote <posva13@gmail.com>
Mon, 3 Mar 2025 10:59:46 +0000 (11:59 +0100)
committerEduardo San Martin Morote <posva13@gmail.com>
Mon, 3 Mar 2025 10:59:50 +0000 (11:59 +0100)
Close #2913

packages/testing/src/testing.spec.ts
packages/testing/src/testing.ts

index 1ddd1153722b3c3967b50581acfbfdc99a045832..75b40687b9f172b994c0cde8b34085b9b7fe3346 100644 (file)
@@ -1,14 +1,18 @@
 import { describe, expect, it, vi } from 'vitest'
 import { createTestingPinia, TestingOptions } from './testing'
-import { createPinia, defineStore, setActivePinia } from 'pinia'
+import { createPinia, defineStore, setActivePinia, storeToRefs } from 'pinia'
 import { mount } from '@vue/test-utils'
 import { defineComponent, ref, computed } from 'vue'
 
 describe('Testing', () => {
   const useCounter = defineStore('counter', {
-    state: () => ({ n: 0 }),
+    state: () => ({ n: 0, doubleComputedCallCount: 0 }),
     getters: {
-      double: (state) => state.n * 2,
+      double: (state) => {
+        // NOTE: not supposed to be done in a getter...
+        state.doubleComputedCallCount++
+        return state.n * 2
+      },
       doublePlusOne(): number {
         return this.double + 1
       },
@@ -22,7 +26,11 @@ describe('Testing', () => {
 
   const useCounterSetup = defineStore('counter-setup', () => {
     const n = ref(0)
-    const double = computed(() => n.value * 2)
+    const doubleComputedCallCount = ref(0)
+    const double = computed(() => {
+      doubleComputedCallCount.value++
+      return n.value * 2
+    })
     const doublePlusOne = computed(() => double.value + 1)
     function increment(amount = 1) {
       n.value += amount
@@ -31,7 +39,14 @@ describe('Testing', () => {
       n.value = 0
     }
 
-    return { n, double, doublePlusOne, increment, $reset }
+    return {
+      n,
+      doubleComputedCallCount,
+      double,
+      doublePlusOne,
+      increment,
+      $reset,
+    }
   })
 
   type CounterStore =
@@ -301,6 +316,16 @@ describe('Testing', () => {
         expect(store.doublePlusOne).toBe(7)
       })
     })
+
+    // https://github.com/vuejs/pinia/issues/2913
+    it('does not compute getters immediately with storeToRefs', () => {
+      const pinia = createTestingPinia()
+      const store = useStore(pinia)
+
+      expect(store.doubleComputedCallCount).toBe(0)
+      storeToRefs(store)
+      expect(store.doubleComputedCallCount).toBe(0)
+    })
   }
 
   it('works with no actions', () => {
index 964a07c8fa32f36d3e225630b2720bbe35c7b233..8be334b7e007d71db01845cf06677566a07a65fd 100644 (file)
@@ -1,4 +1,4 @@
-import { createApp, customRef, isReactive, isRef, toRaw, triggerRef } from 'vue'
+import { computed, createApp, isReactive, isRef, toRaw, triggerRef } from 'vue'
 import type { App, ComputedRef, WritableComputedRef } from 'vue'
 import {
   Pinia,
@@ -9,6 +9,9 @@ import {
   _DeepPartial,
   PiniaPluginContext,
 } from 'pinia'
+// NOTE: the implementation type is correct and contains up to date types
+// while the other types hide internal properties
+import type { ComputedRefImpl } from '@vue/reactivity'
 
 export interface TestingOptions {
   /**
@@ -206,7 +209,7 @@ function isPlainObject(
 
 function isComputed<T>(
   v: ComputedRef<T> | WritableComputedRef<T> | unknown
-): v is ComputedRef<T> | WritableComputedRef<T> {
+): v is (ComputedRef<T> | WritableComputedRef<T>) & ComputedRefImpl<T> {
   return !!v && isRef(v) && 'effect' in v
 }
 
@@ -215,36 +218,33 @@ function WritableComputed({ store }: PiniaPluginContext) {
   for (const key in rawStore) {
     const originalComputed = rawStore[key]
     if (isComputed(originalComputed)) {
-      const originalFn = originalComputed.effect.fn
-      rawStore[key] = customRef((track, trigger) => {
-        // override the computed with a new one
-        const overriddenFn = () =>
-          // @ts-expect-error: internal value
-          originalComputed._value
-        // originalComputed.effect.fn = overriddenFn
-        return {
-          get: () => {
-            track()
-            return originalComputed.value
-          },
-          set: (newValue) => {
-            // reset the computed to its original value by setting it to its initial state
-            if (newValue === undefined) {
-              originalComputed.effect.fn = originalFn
-              // @ts-expect-error: private api to remove the current cached value
-              delete originalComputed._value
-              // @ts-expect-error: private api to force the recomputation
-              originalComputed._dirty = true
-            } else {
-              originalComputed.effect.fn = overriddenFn
-              // @ts-expect-error: private api
-              originalComputed._value = newValue
-            }
-            // this allows to trigger the original computed in setup stores
-            triggerRef(originalComputed)
-            trigger()
-          },
-        }
+      const originalFn = originalComputed.fn
+      // override the computed with a new one
+      const overriddenFn = () =>
+        // @ts-expect-error: internal cached value
+        originalComputed._value
+      // originalComputed.fn = overriddenFn
+
+      rawStore[key] = computed<unknown>({
+        get() {
+          return originalComputed.value
+        },
+        set(newValue) {
+          // reset the computed to its original value by setting it to its initial state
+          if (newValue === undefined) {
+            originalComputed.fn = originalFn
+            // @ts-expect-error: private api to remove the current cached value
+            delete originalComputed._value
+            // @ts-expect-error: private api to force the recomputation
+            originalComputed._dirty = true
+          } else {
+            originalComputed.fn = overriddenFn
+            // @ts-expect-error: private api
+            originalComputed._value = newValue
+          }
+          // this allows to trigger the original computed in setup stores
+          triggerRef(originalComputed)
+        },
       })
     }
   }