apiSetupHelpers.spec.ts 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. import { createComponent, defineVaporComponent, template } from '../src'
  2. import {
  3. currentInstance,
  4. onMounted,
  5. ref,
  6. useAttrs,
  7. useSlots,
  8. withAsyncContext,
  9. } from '@vue/runtime-dom'
  10. import { makeRender } from './_utils'
  11. import type { VaporComponentInstance } from '../src/component'
  12. const define = makeRender<any>()
  13. describe('SFC <script setup> helpers', () => {
  14. test('useSlots / useAttrs (no args)', () => {
  15. let slots: VaporComponentInstance['slots'] | undefined
  16. let attrs: VaporComponentInstance['attrs'] | undefined
  17. const Comp = defineVaporComponent({
  18. setup() {
  19. // @ts-expect-error
  20. slots = useSlots()
  21. attrs = useAttrs()
  22. return []
  23. },
  24. })
  25. const count = ref(0)
  26. const passedAttrs = { id: () => count.value }
  27. const passedSlots = {
  28. default: () => template('')(),
  29. x: () => template('')(),
  30. }
  31. const { render } = define({
  32. render: () => createComponent(Comp, passedAttrs, passedSlots),
  33. })
  34. render()
  35. expect(typeof slots!.default).toBe('function')
  36. expect(typeof slots!.x).toBe('function')
  37. expect(attrs).toMatchObject({ id: 0 })
  38. count.value++
  39. expect(attrs).toMatchObject({ id: 1 })
  40. })
  41. test('useSlots / useAttrs (with args)', () => {
  42. let slots: VaporComponentInstance['slots'] | undefined
  43. let attrs: VaporComponentInstance['attrs'] | undefined
  44. let ctx: VaporComponentInstance | undefined
  45. const Comp = defineVaporComponent({
  46. setup(_, _ctx) {
  47. // @ts-expect-error
  48. slots = useSlots()
  49. attrs = useAttrs()
  50. ctx = _ctx as VaporComponentInstance
  51. return []
  52. },
  53. })
  54. const { render } = define({ render: () => createComponent(Comp) })
  55. render()
  56. expect(slots).toBe(ctx!.slots)
  57. expect(attrs).toBe(ctx!.attrs)
  58. })
  59. describe.todo('withAsyncContext', () => {
  60. test('basic', async () => {
  61. const spy = vi.fn()
  62. let beforeInstance: VaporComponentInstance | null = null
  63. let afterInstance: VaporComponentInstance | null = null
  64. let resolve: (msg: string) => void
  65. const Comp = defineVaporComponent({
  66. async setup() {
  67. let __temp: any, __restore: any
  68. beforeInstance = currentInstance as VaporComponentInstance
  69. const msg =
  70. (([__temp, __restore] = withAsyncContext(
  71. () =>
  72. new Promise(r => {
  73. resolve = r
  74. }),
  75. )),
  76. (__temp = await __temp),
  77. __restore(),
  78. __temp)
  79. // register the lifecycle after an await statement
  80. onMounted(spy)
  81. afterInstance = currentInstance as VaporComponentInstance
  82. return document.createTextNode(msg)
  83. },
  84. })
  85. const { html } = define(Comp).render()
  86. expect(spy).not.toHaveBeenCalled()
  87. resolve!('hello')
  88. // wait a macro task tick for all micro ticks to resolve
  89. await new Promise(r => setTimeout(r))
  90. // mount hook should have been called
  91. expect(spy).toHaveBeenCalled()
  92. // should retain same instance before/after the await call
  93. expect(beforeInstance).toBe(afterInstance)
  94. expect(html()).toBe('hello')
  95. })
  96. test.todo('error handling', async () => {})
  97. test.todo('should not leak instance on multiple awaits', async () => {})
  98. test.todo('should not leak on multiple awaits + error', async () => {})
  99. test.todo('race conditions', async () => {})
  100. test.todo('should teardown in-scope effects', async () => {})
  101. })
  102. })