Skip to content

Commit 417db7a

Browse files
committed
fix: consistent computation of computed in tests with storeToRefs
Close #2913
1 parent 7ed360b commit 417db7a

File tree

2 files changed

+62
-37
lines changed

2 files changed

+62
-37
lines changed

packages/testing/src/testing.spec.ts

+30-5
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
11
import { describe, expect, it, vi } from 'vitest'
22
import { createTestingPinia, TestingOptions } from './testing'
3-
import { createPinia, defineStore, setActivePinia } from 'pinia'
3+
import { createPinia, defineStore, setActivePinia, storeToRefs } from 'pinia'
44
import { mount } from '@vue/test-utils'
55
import { defineComponent, ref, computed } from 'vue'
66

77
describe('Testing', () => {
88
const useCounter = defineStore('counter', {
9-
state: () => ({ n: 0 }),
9+
state: () => ({ n: 0, doubleComputedCallCount: 0 }),
1010
getters: {
11-
double: (state) => state.n * 2,
11+
double: (state) => {
12+
// NOTE: not supposed to be done in a getter...
13+
state.doubleComputedCallCount++
14+
return state.n * 2
15+
},
1216
doublePlusOne(): number {
1317
return this.double + 1
1418
},
@@ -22,7 +26,11 @@ describe('Testing', () => {
2226

2327
const useCounterSetup = defineStore('counter-setup', () => {
2428
const n = ref(0)
25-
const double = computed(() => n.value * 2)
29+
const doubleComputedCallCount = ref(0)
30+
const double = computed(() => {
31+
doubleComputedCallCount.value++
32+
return n.value * 2
33+
})
2634
const doublePlusOne = computed(() => double.value + 1)
2735
function increment(amount = 1) {
2836
n.value += amount
@@ -31,7 +39,14 @@ describe('Testing', () => {
3139
n.value = 0
3240
}
3341

34-
return { n, double, doublePlusOne, increment, $reset }
42+
return {
43+
n,
44+
doubleComputedCallCount,
45+
double,
46+
doublePlusOne,
47+
increment,
48+
$reset,
49+
}
3550
})
3651

3752
type CounterStore =
@@ -301,6 +316,16 @@ describe('Testing', () => {
301316
expect(store.doublePlusOne).toBe(7)
302317
})
303318
})
319+
320+
// https://github.com/vuejs/pinia/issues/2913
321+
it('does not compute getters immediately with storeToRefs', () => {
322+
const pinia = createTestingPinia()
323+
const store = useStore(pinia)
324+
325+
expect(store.doubleComputedCallCount).toBe(0)
326+
storeToRefs(store)
327+
expect(store.doubleComputedCallCount).toBe(0)
328+
})
304329
}
305330

306331
it('works with no actions', () => {

packages/testing/src/testing.ts

+32-32
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { createApp, customRef, isReactive, isRef, toRaw, triggerRef } from 'vue'
1+
import { computed, createApp, isReactive, isRef, toRaw, triggerRef } from 'vue'
22
import type { App, ComputedRef, WritableComputedRef } from 'vue'
33
import {
44
Pinia,
@@ -9,6 +9,9 @@ import {
99
_DeepPartial,
1010
PiniaPluginContext,
1111
} from 'pinia'
12+
// NOTE: the implementation type is correct and contains up to date types
13+
// while the other types hide internal properties
14+
import type { ComputedRefImpl } from '@vue/reactivity'
1215

1316
export interface TestingOptions {
1417
/**
@@ -206,7 +209,7 @@ function isPlainObject(
206209

207210
function isComputed<T>(
208211
v: ComputedRef<T> | WritableComputedRef<T> | unknown
209-
): v is ComputedRef<T> | WritableComputedRef<T> {
212+
): v is (ComputedRef<T> | WritableComputedRef<T>) & ComputedRefImpl<T> {
210213
return !!v && isRef(v) && 'effect' in v
211214
}
212215

@@ -215,36 +218,33 @@ function WritableComputed({ store }: PiniaPluginContext) {
215218
for (const key in rawStore) {
216219
const originalComputed = rawStore[key]
217220
if (isComputed(originalComputed)) {
218-
const originalFn = originalComputed.effect.fn
219-
rawStore[key] = customRef((track, trigger) => {
220-
// override the computed with a new one
221-
const overriddenFn = () =>
222-
// @ts-expect-error: internal value
223-
originalComputed._value
224-
// originalComputed.effect.fn = overriddenFn
225-
return {
226-
get: () => {
227-
track()
228-
return originalComputed.value
229-
},
230-
set: (newValue) => {
231-
// reset the computed to its original value by setting it to its initial state
232-
if (newValue === undefined) {
233-
originalComputed.effect.fn = originalFn
234-
// @ts-expect-error: private api to remove the current cached value
235-
delete originalComputed._value
236-
// @ts-expect-error: private api to force the recomputation
237-
originalComputed._dirty = true
238-
} else {
239-
originalComputed.effect.fn = overriddenFn
240-
// @ts-expect-error: private api
241-
originalComputed._value = newValue
242-
}
243-
// this allows to trigger the original computed in setup stores
244-
triggerRef(originalComputed)
245-
trigger()
246-
},
247-
}
221+
const originalFn = originalComputed.fn
222+
// override the computed with a new one
223+
const overriddenFn = () =>
224+
// @ts-expect-error: internal cached value
225+
originalComputed._value
226+
// originalComputed.fn = overriddenFn
227+
228+
rawStore[key] = computed<unknown>({
229+
get() {
230+
return originalComputed.value
231+
},
232+
set(newValue) {
233+
// reset the computed to its original value by setting it to its initial state
234+
if (newValue === undefined) {
235+
originalComputed.fn = originalFn
236+
// @ts-expect-error: private api to remove the current cached value
237+
delete originalComputed._value
238+
// @ts-expect-error: private api to force the recomputation
239+
originalComputed._dirty = true
240+
} else {
241+
originalComputed.fn = overriddenFn
242+
// @ts-expect-error: private api
243+
originalComputed._value = newValue
244+
}
245+
// this allows to trigger the original computed in setup stores
246+
triggerRef(originalComputed)
247+
},
248248
})
249249
}
250250
}

0 commit comments

Comments
 (0)