|
|
@@ -1,7 +1,9 @@
|
|
|
import { defineComponent } from 'vue'
|
|
|
import {
|
|
|
nextTick,
|
|
|
+ onBeforeUpdate,
|
|
|
onEffectCleanup,
|
|
|
+ onUpdated,
|
|
|
ref,
|
|
|
render,
|
|
|
renderEffect,
|
|
|
@@ -25,6 +27,27 @@ beforeEach(() => {
|
|
|
afterEach(() => {
|
|
|
host.remove()
|
|
|
})
|
|
|
+const createDemo = (
|
|
|
+ setupFn: (porps: any, ctx: any) => any,
|
|
|
+ renderFn: (ctx: any) => any,
|
|
|
+) => {
|
|
|
+ const demo = defineComponent({
|
|
|
+ setup(...args) {
|
|
|
+ const returned = setupFn(...args)
|
|
|
+ Object.defineProperty(returned, '__isScriptSetup', {
|
|
|
+ enumerable: false,
|
|
|
+ value: true,
|
|
|
+ })
|
|
|
+ return returned
|
|
|
+ },
|
|
|
+ })
|
|
|
+ demo.render = (ctx: any) => {
|
|
|
+ const t0 = template('<div></div>')
|
|
|
+ renderFn(ctx)
|
|
|
+ return t0()
|
|
|
+ }
|
|
|
+ return () => render(demo as any, {}, '#host')
|
|
|
+}
|
|
|
|
|
|
describe('renderWatch', () => {
|
|
|
test('effect', async () => {
|
|
|
@@ -53,16 +76,26 @@ describe('renderWatch', () => {
|
|
|
expect(dummy).toBe(1)
|
|
|
})
|
|
|
|
|
|
- test('scheduling order', async () => {
|
|
|
+ test('should run with the scheduling order', async () => {
|
|
|
const calls: string[] = []
|
|
|
|
|
|
- const demo = defineComponent({
|
|
|
- setup() {
|
|
|
+ const mount = createDemo(
|
|
|
+ () => {
|
|
|
+ // setup
|
|
|
const source = ref(0)
|
|
|
const renderSource = ref(0)
|
|
|
const change = () => source.value++
|
|
|
const changeRender = () => renderSource.value++
|
|
|
|
|
|
+ // Life Cycle Hooks
|
|
|
+ onUpdated(() => {
|
|
|
+ calls.push(`updated ${source.value}`)
|
|
|
+ })
|
|
|
+ onBeforeUpdate(() => {
|
|
|
+ calls.push(`beforeUpdate ${source.value}`)
|
|
|
+ })
|
|
|
+
|
|
|
+ // Watch API
|
|
|
watchPostEffect(() => {
|
|
|
const current = source.value
|
|
|
calls.push(`post ${current}`)
|
|
|
@@ -78,33 +111,28 @@ describe('renderWatch', () => {
|
|
|
calls.push(`sync ${current}`)
|
|
|
onEffectCleanup(() => calls.push(`sync cleanup ${current}`))
|
|
|
})
|
|
|
- const __returned__ = { source, change, renderSource, changeRender }
|
|
|
- Object.defineProperty(__returned__, '__isScriptSetup', {
|
|
|
- enumerable: false,
|
|
|
- value: true,
|
|
|
+ return { source, change, renderSource, changeRender }
|
|
|
+ },
|
|
|
+ // render
|
|
|
+ (_ctx) => {
|
|
|
+ // Render Watch API
|
|
|
+ renderEffect(() => {
|
|
|
+ const current = _ctx.renderSource
|
|
|
+ calls.push(`renderEffect ${current}`)
|
|
|
+ onEffectCleanup(() => calls.push(`renderEffect cleanup ${current}`))
|
|
|
})
|
|
|
- return __returned__
|
|
|
+ renderWatch(
|
|
|
+ () => _ctx.renderSource,
|
|
|
+ (value) => {
|
|
|
+ calls.push(`renderWatch ${value}`)
|
|
|
+ onEffectCleanup(() => calls.push(`renderWatch cleanup ${value}`))
|
|
|
+ },
|
|
|
+ )
|
|
|
},
|
|
|
- })
|
|
|
+ )
|
|
|
|
|
|
- demo.render = (_ctx: any) => {
|
|
|
- const t0 = template('<div></div>')
|
|
|
- renderEffect(() => {
|
|
|
- const current = _ctx.renderSource
|
|
|
- calls.push(`renderEffect ${current}`)
|
|
|
- onEffectCleanup(() => calls.push(`renderEffect cleanup ${current}`))
|
|
|
- })
|
|
|
- renderWatch(
|
|
|
- () => _ctx.renderSource,
|
|
|
- (value) => {
|
|
|
- calls.push(`renderWatch ${value}`)
|
|
|
- onEffectCleanup(() => calls.push(`renderWatch cleanup ${value}`))
|
|
|
- },
|
|
|
- )
|
|
|
- return t0()
|
|
|
- }
|
|
|
-
|
|
|
- const instance = render(demo as any, {}, '#host')
|
|
|
+ // Mount
|
|
|
+ const instance = mount()
|
|
|
const { change, changeRender } = instance.setupState as any
|
|
|
|
|
|
expect(calls).toEqual(['pre 0', 'sync 0', 'renderEffect 0'])
|
|
|
@@ -114,8 +142,10 @@ describe('renderWatch', () => {
|
|
|
expect(calls).toEqual(['post 0'])
|
|
|
calls.length = 0
|
|
|
|
|
|
+ // Update
|
|
|
changeRender()
|
|
|
change()
|
|
|
+
|
|
|
expect(calls).toEqual(['sync cleanup 0', 'sync 1'])
|
|
|
calls.length = 0
|
|
|
|
|
|
@@ -123,11 +153,75 @@ describe('renderWatch', () => {
|
|
|
expect(calls).toEqual([
|
|
|
'pre cleanup 0',
|
|
|
'pre 1',
|
|
|
+ 'beforeUpdate 1',
|
|
|
'renderEffect cleanup 0',
|
|
|
'renderEffect 1',
|
|
|
'renderWatch 1',
|
|
|
'post cleanup 0',
|
|
|
'post 1',
|
|
|
+ 'updated 1',
|
|
|
])
|
|
|
})
|
|
|
+
|
|
|
+ test('errors should include the execution location with beforeUpdate hook', async () => {
|
|
|
+ const mount = createDemo(
|
|
|
+ // setup
|
|
|
+ () => {
|
|
|
+ const source = ref()
|
|
|
+ const update = () => source.value++
|
|
|
+ onBeforeUpdate(() => {
|
|
|
+ throw 'error in beforeUpdate'
|
|
|
+ })
|
|
|
+ return { source, update }
|
|
|
+ },
|
|
|
+ // render
|
|
|
+ (ctx) => {
|
|
|
+ renderEffect(() => {
|
|
|
+ ctx.source
|
|
|
+ })
|
|
|
+ },
|
|
|
+ )
|
|
|
+
|
|
|
+ const instance = mount()
|
|
|
+ const { update } = instance.setupState as any
|
|
|
+ await expect(async () => {
|
|
|
+ update()
|
|
|
+ await nextTick()
|
|
|
+ }).rejects.toThrow('error in beforeUpdate')
|
|
|
+
|
|
|
+ expect(
|
|
|
+ '[Vue warn] Unhandled error during execution of beforeUpdate hook',
|
|
|
+ ).toHaveBeenWarned()
|
|
|
+ })
|
|
|
+
|
|
|
+ test('errors should include the execution location with updated hook', async () => {
|
|
|
+ const mount = createDemo(
|
|
|
+ // setup
|
|
|
+ () => {
|
|
|
+ const source = ref(0)
|
|
|
+ const update = () => source.value++
|
|
|
+ onUpdated(() => {
|
|
|
+ throw 'error in updated'
|
|
|
+ })
|
|
|
+ return { source, update }
|
|
|
+ },
|
|
|
+ // render
|
|
|
+ (ctx) => {
|
|
|
+ renderEffect(() => {
|
|
|
+ ctx.source
|
|
|
+ })
|
|
|
+ },
|
|
|
+ )
|
|
|
+
|
|
|
+ const instance = mount()
|
|
|
+ const { update } = instance.setupState as any
|
|
|
+ await expect(async () => {
|
|
|
+ update()
|
|
|
+ await nextTick()
|
|
|
+ }).rejects.toThrow('error in updated')
|
|
|
+
|
|
|
+ expect(
|
|
|
+ '[Vue warn] Unhandled error during execution of updated',
|
|
|
+ ).toHaveBeenWarned()
|
|
|
+ })
|
|
|
})
|