diff --git a/packages/reactivity/__tests__/reactiveArray.spec.ts b/packages/reactivity/__tests__/reactiveArray.spec.ts index ebb4a926acd..2760eb8a1a8 100644 --- a/packages/reactivity/__tests__/reactiveArray.spec.ts +++ b/packages/reactivity/__tests__/reactiveArray.spec.ts @@ -89,6 +89,80 @@ describe('reactivity/reactive/Array', () => { expect(index).toBe(1) }) + // only non-existent reactive will try to search by using its raw value + describe('Array identity methods have been called times', () => { + const identityMethods = ['includes', 'indexOf', 'lastIndexOf'] as const + function instrumentArr(rawTarget: any[]) { + identityMethods.forEach(key => { + const spy = vi.fn(rawTarget[key] as any) + rawTarget[key] = spy + }) + } + function searchValue(target: any[], ...args: unknown[]) { + return identityMethods.map(key => (target[key] as any)(...args)) + } + function unInstrumentArr(rawTarget: any[]) { + identityMethods.forEach(key => { + ;(rawTarget[key] as any).mockClear() + // relink to prototype method + rawTarget[key] = Array.prototype[key] as any + }) + } + function expectHaveBeenCalledTimes(rawTarget: any[], times: number) { + identityMethods.forEach(key => { + expect(rawTarget[key]).toHaveBeenCalledTimes(times) + }) + } + + test('should be called once with a non-existent raw value', () => { + const reactiveArr = reactive([]) + instrumentArr(toRaw(reactiveArr)) + const searchResult = searchValue(reactiveArr, {}) + + expectHaveBeenCalledTimes(toRaw(reactiveArr), 1) + expect(searchResult).toStrictEqual([false, -1, -1]) + + unInstrumentArr(toRaw(reactiveArr)) + }) + + test('should be called once with an existent reactive value', () => { + const existReactiveValue = reactive({}) + const reactiveArr = reactive([existReactiveValue, existReactiveValue]) + + instrumentArr(toRaw(reactiveArr)) + const searchResult = searchValue(reactiveArr, existReactiveValue) + + expectHaveBeenCalledTimes(toRaw(reactiveArr), 1) + expect(searchResult).toStrictEqual([true, 0, 1]) + + unInstrumentArr(toRaw(reactiveArr)) + }) + + test('should be called twice with a non-existent reactive value', () => { + const reactiveArr = reactive([]) + instrumentArr(toRaw(reactiveArr)) + const searchResult = searchValue(reactiveArr, reactive({})) + + expectHaveBeenCalledTimes(toRaw(reactiveArr), 2) + expect(searchResult).toStrictEqual([false, -1, -1]) + + unInstrumentArr(toRaw(reactiveArr)) + }) + + test('should be called twice with a non-existent reactive value, but the raw value exists', () => { + const existRaw = {} + const reactiveArr = reactive([existRaw, existRaw]) + + instrumentArr(toRaw(reactiveArr)) + const searchResult = searchValue(reactiveArr, reactive(existRaw)) + + expectHaveBeenCalledTimes(toRaw(reactiveArr), 2) + expect(searchResult).toStrictEqual([true, 0, 1]) + + unInstrumentArr(toRaw(reactiveArr)) + }) + }) + test('delete on Array should not trigger length dependency', () => { const arr = reactive([1, 2, 3]) const fn = vi.fn()