rendererSuspense.spec.ts 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. import {
  2. h,
  3. ref,
  4. Suspense,
  5. ComponentOptions,
  6. render,
  7. nodeOps,
  8. serializeInner,
  9. nextTick
  10. } from '@vue/runtime-test'
  11. describe('renderer: suspense', () => {
  12. it('basic usage (nested + multiple deps)', async () => {
  13. const msg = ref('hello')
  14. const deps: Promise<any>[] = []
  15. const createAsyncComponent = (loader: () => Promise<ComponentOptions>) => ({
  16. async setup(props: any, { slots }: any) {
  17. const p = loader()
  18. deps.push(p)
  19. const Inner = await p
  20. return () => h(Inner, props, slots)
  21. }
  22. })
  23. const AsyncChild = createAsyncComponent(
  24. () =>
  25. new Promise(resolve => {
  26. setTimeout(() => {
  27. resolve({
  28. setup(props: { msg: string }) {
  29. return () => h('div', props.msg)
  30. }
  31. })
  32. }, 0)
  33. })
  34. )
  35. const AsyncChild2 = createAsyncComponent(
  36. () =>
  37. new Promise(resolve => {
  38. setTimeout(() => {
  39. resolve({
  40. setup(props: { msg: string }) {
  41. return () => h('div', props.msg)
  42. }
  43. })
  44. }, 10)
  45. })
  46. )
  47. const Mid = {
  48. setup() {
  49. return () =>
  50. h(AsyncChild, {
  51. msg: msg.value
  52. })
  53. }
  54. }
  55. const Comp = {
  56. setup() {
  57. return () =>
  58. h(Suspense, [msg.value, h(Mid), h(AsyncChild2, { msg: 'child 2' })])
  59. }
  60. }
  61. const root = nodeOps.createElement('div')
  62. render(h(Comp), root)
  63. expect(serializeInner(root)).toBe(`<!---->`)
  64. await Promise.all(deps)
  65. await nextTick()
  66. expect(serializeInner(root)).toBe(
  67. `<!---->hello<div>hello</div><div>child 2</div><!---->`
  68. )
  69. })
  70. test('fallback content', async () => {
  71. const deps: Promise<any>[] = []
  72. const Async = {
  73. async setup() {
  74. const p = new Promise(r => setTimeout(r, 1))
  75. deps.push(p)
  76. await p
  77. // test resume for returning bindings
  78. return {
  79. msg: 'async'
  80. }
  81. },
  82. render(this: any) {
  83. return h('div', this.msg)
  84. }
  85. }
  86. const Comp = {
  87. setup() {
  88. return () =>
  89. h(Suspense, null, {
  90. default: h(Async),
  91. fallback: h('div', 'fallback')
  92. })
  93. }
  94. }
  95. const root = nodeOps.createElement('div')
  96. render(h(Comp), root)
  97. expect(serializeInner(root)).toBe(`<div>fallback</div>`)
  98. await Promise.all(deps)
  99. await nextTick()
  100. expect(serializeInner(root)).toBe(`<div>async</div>`)
  101. })
  102. test.todo('buffer mounted/updated hooks & watch callbacks')
  103. test.todo('onResolve')
  104. test.todo('content update before suspense resolve')
  105. test.todo('unmount before suspense resolve')
  106. test.todo('nested suspense')
  107. test.todo('error handling')
  108. test.todo('portal inside suspense')
  109. })