| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266 |
- import { nextTick, watch, watchEffect } from '@vue/runtime-core'
- import {
- reactive,
- effect,
- EffectScope,
- onScopeDispose,
- computed,
- ref,
- ComputedRef
- } from '../src'
- describe('reactivity/effect/scope', () => {
- it('should run', () => {
- const fnSpy = jest.fn(() => {})
- new EffectScope().run(fnSpy)
- expect(fnSpy).toHaveBeenCalledTimes(1)
- })
- it('should accept zero argument', () => {
- const scope = new EffectScope()
- expect(scope.effects.length).toBe(0)
- })
- it('should return run value', () => {
- expect(new EffectScope().run(() => 1)).toBe(1)
- })
- it('should collect the effects', () => {
- const scope = new EffectScope()
- scope.run(() => {
- let dummy
- const counter = reactive({ num: 0 })
- effect(() => (dummy = counter.num))
- expect(dummy).toBe(0)
- counter.num = 7
- expect(dummy).toBe(7)
- })
- expect(scope.effects.length).toBe(1)
- })
- it('stop', () => {
- let dummy, doubled
- const counter = reactive({ num: 0 })
- const scope = new EffectScope()
- scope.run(() => {
- effect(() => (dummy = counter.num))
- effect(() => (doubled = counter.num * 2))
- })
- expect(scope.effects.length).toBe(2)
- expect(dummy).toBe(0)
- counter.num = 7
- expect(dummy).toBe(7)
- expect(doubled).toBe(14)
- scope.stop()
- counter.num = 6
- expect(dummy).toBe(7)
- expect(doubled).toBe(14)
- })
- it('should collect nested scope', () => {
- let dummy, doubled
- const counter = reactive({ num: 0 })
- const scope = new EffectScope()
- scope.run(() => {
- effect(() => (dummy = counter.num))
- // nested scope
- new EffectScope().run(() => {
- effect(() => (doubled = counter.num * 2))
- })
- })
- expect(scope.effects.length).toBe(1)
- expect(scope.scopes!.length).toBe(1)
- expect(scope.scopes![0]).toBeInstanceOf(EffectScope)
- expect(dummy).toBe(0)
- counter.num = 7
- expect(dummy).toBe(7)
- expect(doubled).toBe(14)
- // stop the nested scope as well
- scope.stop()
- counter.num = 6
- expect(dummy).toBe(7)
- expect(doubled).toBe(14)
- })
- it('nested scope can be escaped', () => {
- let dummy, doubled
- const counter = reactive({ num: 0 })
- const scope = new EffectScope()
- scope.run(() => {
- effect(() => (dummy = counter.num))
- // nested scope
- new EffectScope(true).run(() => {
- effect(() => (doubled = counter.num * 2))
- })
- })
- expect(scope.effects.length).toBe(1)
- expect(dummy).toBe(0)
- counter.num = 7
- expect(dummy).toBe(7)
- expect(doubled).toBe(14)
- scope.stop()
- counter.num = 6
- expect(dummy).toBe(7)
- // nested scope should not be stoped
- expect(doubled).toBe(12)
- })
- it('able to run the scope', () => {
- let dummy, doubled
- const counter = reactive({ num: 0 })
- const scope = new EffectScope()
- scope.run(() => {
- effect(() => (dummy = counter.num))
- })
- expect(scope.effects.length).toBe(1)
- scope.run(() => {
- effect(() => (doubled = counter.num * 2))
- })
- expect(scope.effects.length).toBe(2)
- counter.num = 7
- expect(dummy).toBe(7)
- expect(doubled).toBe(14)
- scope.stop()
- })
- it('can not run an inactive scope', () => {
- let dummy, doubled
- const counter = reactive({ num: 0 })
- const scope = new EffectScope()
- scope.run(() => {
- effect(() => (dummy = counter.num))
- })
- expect(scope.effects.length).toBe(1)
- scope.stop()
- scope.run(() => {
- effect(() => (doubled = counter.num * 2))
- })
- expect('[Vue warn] cannot run an inactive effect scope.').toHaveBeenWarned()
- expect(scope.effects.length).toBe(1)
- counter.num = 7
- expect(dummy).toBe(0)
- expect(doubled).toBe(undefined)
- })
- it('should fire onScopeDispose hook', () => {
- let dummy = 0
- const scope = new EffectScope()
- scope.run(() => {
- onScopeDispose(() => (dummy += 1))
- onScopeDispose(() => (dummy += 2))
- })
- scope.run(() => {
- onScopeDispose(() => (dummy += 4))
- })
- expect(dummy).toBe(0)
- scope.stop()
- expect(dummy).toBe(7)
- })
- it('should warn onScopeDispose() is called when there is no active effect scope', () => {
- const spy = jest.fn()
- const scope = new EffectScope()
- scope.run(() => {
- onScopeDispose(spy)
- })
- expect(spy).toHaveBeenCalledTimes(0)
- onScopeDispose(spy)
- expect(
- '[Vue warn] onScopeDispose() is called when there is no active effect scope to be associated with.'
- ).toHaveBeenWarned()
- scope.stop()
- expect(spy).toHaveBeenCalledTimes(1)
- })
- it('should derefence child scope from parent scope after stopping child scope (no memleaks)', () => {
- const parent = new EffectScope()
- const child = parent.run(() => new EffectScope())!
- expect(parent.scopes!.includes(child)).toBe(true)
- child.stop()
- expect(parent.scopes!.includes(child)).toBe(false)
- })
- it('test with higher level APIs', async () => {
- const r = ref(1)
- const computedSpy = jest.fn()
- const watchSpy = jest.fn()
- const watchEffectSpy = jest.fn()
- let c: ComputedRef
- const scope = new EffectScope()
- scope.run(() => {
- c = computed(() => {
- computedSpy()
- return r.value + 1
- })
- watch(r, watchSpy)
- watchEffect(() => {
- watchEffectSpy()
- r.value
- })
- })
- c!.value // computed is lazy so trigger collection
- expect(computedSpy).toHaveBeenCalledTimes(1)
- expect(watchSpy).toHaveBeenCalledTimes(0)
- expect(watchEffectSpy).toHaveBeenCalledTimes(1)
- r.value++
- c!.value
- await nextTick()
- expect(computedSpy).toHaveBeenCalledTimes(2)
- expect(watchSpy).toHaveBeenCalledTimes(1)
- expect(watchEffectSpy).toHaveBeenCalledTimes(2)
- scope.stop()
- r.value++
- c!.value
- await nextTick()
- // should not trigger anymore
- expect(computedSpy).toHaveBeenCalledTimes(2)
- expect(watchSpy).toHaveBeenCalledTimes(1)
- expect(watchEffectSpy).toHaveBeenCalledTimes(2)
- })
- })
|