diff --git a/packages/runtime-core/__tests__/apiOptions.spec.ts b/packages/runtime-core/__tests__/apiOptions.spec.ts index 7d915eb0..c55c197e 100644 --- a/packages/runtime-core/__tests__/apiOptions.spec.ts +++ b/packages/runtime-core/__tests__/apiOptions.spec.ts @@ -149,30 +149,24 @@ describe('api: options', () => { function assertCall(spy: jest.Mock, callIndex: number, args: any[]) { expect(spy.mock.calls[callIndex].slice(0, 2)).toMatchObject(args) + expect(spy).toHaveReturnedWith(ctx) } - assertCall(spyA, 0, [1, undefined]) - assertCall(spyB, 0, [2, undefined]) - assertCall(spyC, 0, [{ qux: 3 }, undefined]) - expect(spyA).toHaveReturnedWith(ctx) - expect(spyB).toHaveReturnedWith(ctx) - expect(spyC).toHaveReturnedWith(ctx) - ctx.foo++ await nextTick() - expect(spyA).toHaveBeenCalledTimes(2) - assertCall(spyA, 1, [2, 1]) + expect(spyA).toHaveBeenCalledTimes(1) + assertCall(spyA, 0, [2, 1]) ctx.bar++ await nextTick() - expect(spyB).toHaveBeenCalledTimes(2) - assertCall(spyB, 1, [3, 2]) + expect(spyB).toHaveBeenCalledTimes(1) + assertCall(spyB, 0, [3, 2]) ctx.baz.qux++ await nextTick() - expect(spyC).toHaveBeenCalledTimes(2) + expect(spyC).toHaveBeenCalledTimes(1) // new and old objects have same identity - assertCall(spyC, 1, [{ qux: 4 }, { qux: 4 }]) + assertCall(spyC, 0, [{ qux: 4 }, { qux: 4 }]) }) test('watch array', async () => { @@ -218,30 +212,24 @@ describe('api: options', () => { function assertCall(spy: jest.Mock, callIndex: number, args: any[]) { expect(spy.mock.calls[callIndex].slice(0, 2)).toMatchObject(args) + expect(spy).toHaveReturnedWith(ctx) } - assertCall(spyA, 0, [1, undefined]) - assertCall(spyB, 0, [2, undefined]) - assertCall(spyC, 0, [{ qux: 3 }, undefined]) - expect(spyA).toHaveReturnedWith(ctx) - expect(spyB).toHaveReturnedWith(ctx) - expect(spyC).toHaveReturnedWith(ctx) - ctx.foo++ await nextTick() - expect(spyA).toHaveBeenCalledTimes(2) - assertCall(spyA, 1, [2, 1]) + expect(spyA).toHaveBeenCalledTimes(1) + assertCall(spyA, 0, [2, 1]) ctx.bar++ await nextTick() - expect(spyB).toHaveBeenCalledTimes(2) - assertCall(spyB, 1, [3, 2]) + expect(spyB).toHaveBeenCalledTimes(1) + assertCall(spyB, 0, [3, 2]) ctx.baz.qux++ await nextTick() - expect(spyC).toHaveBeenCalledTimes(2) + expect(spyC).toHaveBeenCalledTimes(1) // new and old objects have same identity - assertCall(spyC, 1, [{ qux: 4 }, { qux: 4 }]) + assertCall(spyC, 0, [{ qux: 4 }, { qux: 4 }]) }) test('provide/inject', () => { diff --git a/packages/runtime-core/__tests__/apiWatch.spec.ts b/packages/runtime-core/__tests__/apiWatch.spec.ts index 9b1d35a2..ef05452f 100644 --- a/packages/runtime-core/__tests__/apiWatch.spec.ts +++ b/packages/runtime-core/__tests__/apiWatch.spec.ts @@ -13,13 +13,12 @@ import { mockWarn } from '@vue/shared' describe('api: watch', () => { mockWarn() - it('basic usage', async () => { + it('watch(effect)', async () => { const state = reactive({ count: 0 }) let dummy watch(() => { dummy = state.count }) - await nextTick() expect(dummy).toBe(0) state.count++ @@ -27,33 +26,6 @@ describe('api: watch', () => { expect(dummy).toBe(1) }) - it('triggers when initial value is null', async () => { - const state = ref(null) - const spy = jest.fn() - watch(() => state.value, spy) - await nextTick() - expect(spy).toHaveBeenCalled() - }) - - it('triggers when initial value is undefined', async () => { - const state = ref() - const spy = jest.fn() - watch(() => state.value, spy) - await nextTick() - expect(spy).toHaveBeenCalled() - state.value = 3 - await nextTick() - expect(spy).toHaveBeenCalledTimes(2) - // testing if undefined can trigger the watcher - state.value = undefined - await nextTick() - expect(spy).toHaveBeenCalledTimes(3) - // it shouldn't trigger if the same value is set - state.value = undefined - await nextTick() - expect(spy).toHaveBeenCalledTimes(3) - }) - it('watching single source: getter', async () => { const state = reactive({ count: 0 }) let dummy @@ -68,9 +40,6 @@ describe('api: watch', () => { } } ) - await nextTick() - expect(dummy).toMatchObject([0, undefined]) - state.count++ await nextTick() expect(dummy).toMatchObject([1, 0]) @@ -87,9 +56,6 @@ describe('api: watch', () => { prevCount + 1 } }) - await nextTick() - expect(dummy).toMatchObject([0, undefined]) - count.value++ await nextTick() expect(dummy).toMatchObject([1, 0]) @@ -107,9 +73,6 @@ describe('api: watch', () => { prevCount + 1 } }) - await nextTick() - expect(dummy).toMatchObject([1, undefined]) - count.value++ await nextTick() expect(dummy).toMatchObject([2, 1]) @@ -127,8 +90,6 @@ describe('api: watch', () => { vals.concat(1) oldVals.concat(1) }) - await nextTick() - expect(dummy).toMatchObject([[1, 1, 2], []]) state.count++ count.value++ @@ -149,8 +110,6 @@ describe('api: watch', () => { count + 1 oldStatus === true }) - await nextTick() - expect(dummy).toMatchObject([[1, false], []]) state.count++ status.value = true @@ -164,7 +123,6 @@ describe('api: watch', () => { const stop = watch(() => { dummy = state.count }) - await nextTick() expect(dummy).toBe(0) stop() @@ -174,7 +132,7 @@ describe('api: watch', () => { expect(dummy).toBe(0) }) - it('cleanup registration (basic)', async () => { + it('cleanup registration (effect)', async () => { const state = reactive({ count: 0 }) const cleanup = jest.fn() let dummy @@ -182,7 +140,6 @@ describe('api: watch', () => { onCleanup(cleanup) dummy = state.count }) - await nextTick() expect(dummy).toBe(0) state.count++ @@ -202,22 +159,30 @@ describe('api: watch', () => { onCleanup(cleanup) dummy = count }) + + count.value++ await nextTick() - expect(dummy).toBe(0) + expect(cleanup).toHaveBeenCalledTimes(0) + expect(dummy).toBe(1) count.value++ await nextTick() expect(cleanup).toHaveBeenCalledTimes(1) - expect(dummy).toBe(1) + expect(dummy).toBe(2) stop() expect(cleanup).toHaveBeenCalledTimes(2) }) - it('flush timing: post', async () => { + it('flush timing: post (default)', async () => { const count = ref(0) + let callCount = 0 const assertion = jest.fn(count => { - expect(serializeInner(root)).toBe(`${count}`) + callCount++ + // on mount, the watcher callback should be called before DOM render + // on update, should be called after the count is updated + const expectedDOM = callCount === 1 ? `` : `${count}` + expect(serializeInner(root)).toBe(expectedDOM) }) const Comp = { @@ -230,7 +195,6 @@ describe('api: watch', () => { } const root = nodeOps.createElement('div') render(h(Comp), root) - await nextTick() expect(assertion).toHaveBeenCalledTimes(1) count.value++ @@ -270,7 +234,6 @@ describe('api: watch', () => { } const root = nodeOps.createElement('div') render(h(Comp), root) - await nextTick() expect(assertion).toHaveBeenCalledTimes(1) count.value++ @@ -313,7 +276,6 @@ describe('api: watch', () => { } const root = nodeOps.createElement('div') render(h(Comp), root) - await nextTick() expect(assertion).toHaveBeenCalledTimes(1) count.value++ @@ -346,9 +308,6 @@ describe('api: watch', () => { { deep: true } ) - await nextTick() - expect(dummy).toEqual([0, 1, 1, true]) - state.nested.count++ await nextTick() expect(dummy).toEqual([1, 1, 1, true]) @@ -369,18 +328,42 @@ describe('api: watch', () => { expect(dummy).toEqual([1, 2, 2, false]) }) - it('lazy', async () => { + it('immediate', async () => { const count = ref(0) const cb = jest.fn() - watch(count, cb, { lazy: true }) - await nextTick() - expect(cb).not.toHaveBeenCalled() + watch(count, cb, { immediate: true }) + expect(cb).toHaveBeenCalledTimes(1) count.value++ await nextTick() - expect(cb).toHaveBeenCalled() + expect(cb).toHaveBeenCalledTimes(2) }) - it('ignore lazy option when using simple callback', async () => { + it('immediate: triggers when initial value is null', async () => { + const state = ref(null) + const spy = jest.fn() + watch(() => state.value, spy, { immediate: true }) + expect(spy).toHaveBeenCalled() + }) + + it('immediate: triggers when initial value is undefined', async () => { + const state = ref() + const spy = jest.fn() + watch(() => state.value, spy, { immediate: true }) + expect(spy).toHaveBeenCalled() + state.value = 3 + await nextTick() + expect(spy).toHaveBeenCalledTimes(2) + // testing if undefined can trigger the watcher + state.value = undefined + await nextTick() + expect(spy).toHaveBeenCalledTimes(3) + // it shouldn't trigger if the same value is set + state.value = undefined + await nextTick() + expect(spy).toHaveBeenCalledTimes(3) + }) + + it('warn immediate option when using effect signature', async () => { const count = ref(0) let dummy // @ts-ignore @@ -388,13 +371,10 @@ describe('api: watch', () => { () => { dummy = count.value }, - { lazy: true } + { immediate: false } ) - expect(dummy).toBeUndefined() - expect(`lazy option is only respected`).toHaveBeenWarned() - - await nextTick() expect(dummy).toBe(0) + expect(`"immediate" option is only respected`).toHaveBeenWarned() count.value++ await nextTick() diff --git a/packages/runtime-core/__tests__/components/Suspense.spec.ts b/packages/runtime-core/__tests__/components/Suspense.spec.ts index 264bd7ca..a530532f 100644 --- a/packages/runtime-core/__tests__/components/Suspense.spec.ts +++ b/packages/runtime-core/__tests__/components/Suspense.spec.ts @@ -164,8 +164,14 @@ describe('Suspense', () => { deps.push(p.then(() => Promise.resolve())) watch(() => { + calls.push('immediate effect') + }) + + const count = ref(0) + watch(count, v => { calls.push('watch callback') }) + count.value++ // trigger the watcher now onMounted(() => { calls.push('mounted') @@ -193,18 +199,24 @@ describe('Suspense', () => { const root = nodeOps.createElement('div') render(h(Comp), root) expect(serializeInner(root)).toBe(`