| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196 |
- import {
- EffectScope,
- type Ref,
- WatchErrorCodes,
- type WatchOptions,
- type WatchScheduler,
- onWatcherCleanup,
- ref,
- watch,
- } from '../src'
- const queue: (() => void)[] = []
- // a simple scheduler for testing purposes
- let isFlushPending = false
- const resolvedPromise = /*@__PURE__*/ Promise.resolve() as Promise<any>
- const nextTick = (fn?: () => any) =>
- fn ? resolvedPromise.then(fn) : resolvedPromise
- const scheduler: WatchScheduler = (job, isFirstRun) => {
- if (isFirstRun) {
- job()
- } else {
- queue.push(job)
- flushJobs()
- }
- }
- const flushJobs = () => {
- if (isFlushPending) return
- isFlushPending = true
- resolvedPromise.then(() => {
- queue.forEach(job => job())
- queue.length = 0
- isFlushPending = false
- })
- }
- describe('watch', () => {
- test('effect', () => {
- let dummy: any
- const source = ref(0)
- watch(() => {
- dummy = source.value
- })
- expect(dummy).toBe(0)
- source.value++
- expect(dummy).toBe(1)
- })
- test('with callback', () => {
- let dummy: any
- const source = ref(0)
- watch(source, () => {
- dummy = source.value
- })
- expect(dummy).toBe(undefined)
- source.value++
- expect(dummy).toBe(1)
- })
- test('call option with error handling', () => {
- const onError = vi.fn()
- const call: WatchOptions['call'] = function call(fn, type, args) {
- if (Array.isArray(fn)) {
- fn.forEach(f => call(f, type, args))
- return
- }
- try {
- fn.apply(null, args)
- } catch (e) {
- onError(e, type)
- }
- }
- watch(
- () => {
- throw 'oops in effect'
- },
- null,
- { call },
- )
- const source = ref(0)
- const effect = watch(
- source,
- () => {
- onWatcherCleanup(() => {
- throw 'oops in cleanup'
- })
- throw 'oops in watch'
- },
- { call },
- )
- expect(onError.mock.calls.length).toBe(1)
- expect(onError.mock.calls[0]).toMatchObject([
- 'oops in effect',
- WatchErrorCodes.WATCH_CALLBACK,
- ])
- source.value++
- expect(onError.mock.calls.length).toBe(2)
- expect(onError.mock.calls[1]).toMatchObject([
- 'oops in watch',
- WatchErrorCodes.WATCH_CALLBACK,
- ])
- effect!.stop()
- source.value++
- expect(onError.mock.calls.length).toBe(3)
- expect(onError.mock.calls[2]).toMatchObject([
- 'oops in cleanup',
- WatchErrorCodes.WATCH_CLEANUP,
- ])
- })
- test('watch with onWatcherCleanup', async () => {
- let dummy = 0
- let source: Ref<number>
- const scope = new EffectScope()
- scope.run(() => {
- source = ref(0)
- watch(onCleanup => {
- source.value
- onCleanup(() => (dummy += 2))
- onWatcherCleanup(() => (dummy += 3))
- onWatcherCleanup(() => (dummy += 5))
- })
- })
- expect(dummy).toBe(0)
- scope.run(() => {
- source.value++
- })
- expect(dummy).toBe(10)
- scope.run(() => {
- source.value++
- })
- expect(dummy).toBe(20)
- scope.stop()
- expect(dummy).toBe(30)
- })
- test('nested calls to baseWatch and onWatcherCleanup', async () => {
- let calls: string[] = []
- let source: Ref<number>
- let copyist: Ref<number>
- const scope = new EffectScope()
- scope.run(() => {
- source = ref(0)
- copyist = ref(0)
- // sync by default
- watch(
- () => {
- const current = (copyist.value = source.value)
- onWatcherCleanup(() => calls.push(`sync ${current}`))
- },
- null,
- {},
- )
- // with scheduler
- watch(
- () => {
- const current = copyist.value
- onWatcherCleanup(() => calls.push(`post ${current}`))
- },
- null,
- { scheduler },
- )
- })
- await nextTick()
- expect(calls).toEqual([])
- scope.run(() => source.value++)
- expect(calls).toEqual(['sync 0'])
- await nextTick()
- expect(calls).toEqual(['sync 0', 'post 0'])
- calls.length = 0
- scope.run(() => source.value++)
- expect(calls).toEqual(['sync 1'])
- await nextTick()
- expect(calls).toEqual(['sync 1', 'post 1'])
- calls.length = 0
- scope.stop()
- expect(calls).toEqual(['sync 2', 'post 2'])
- })
- })
|