scopeId.spec.ts 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508
  1. import { createApp, h } from '@vue/runtime-dom'
  2. import {
  3. createComponent,
  4. createDynamicComponent,
  5. createSlot,
  6. defineVaporComponent,
  7. setInsertionState,
  8. template,
  9. vaporInteropPlugin,
  10. } from '../src'
  11. import { makeRender } from './_utils'
  12. const define = makeRender()
  13. describe('scopeId', () => {
  14. test('should attach scopeId to child component', () => {
  15. const Child = defineVaporComponent({
  16. __scopeId: 'child',
  17. setup() {
  18. return template('<div child></div>', true)()
  19. },
  20. })
  21. const { html } = define({
  22. __scopeId: 'parent',
  23. setup() {
  24. return createComponent(Child)
  25. },
  26. }).render()
  27. expect(html()).toBe(`<div child="" parent=""></div>`)
  28. })
  29. test('should attach scopeId to child component with insertion state', () => {
  30. const Child = defineVaporComponent({
  31. __scopeId: 'child',
  32. setup() {
  33. return template('<div child></div>', true)()
  34. },
  35. })
  36. const { html } = define({
  37. __scopeId: 'parent',
  38. setup() {
  39. const t0 = template('<div parent></div>', true)
  40. const n1 = t0() as any
  41. setInsertionState(n1)
  42. createComponent(Child)
  43. return n1
  44. },
  45. }).render()
  46. expect(html()).toBe(`<div parent=""><div child="" parent=""></div></div>`)
  47. })
  48. test('should attach scopeId to nested child component', () => {
  49. const Child = defineVaporComponent({
  50. __scopeId: 'child',
  51. setup() {
  52. return template('<div child></div>', true)()
  53. },
  54. })
  55. const Parent = defineVaporComponent({
  56. __scopeId: 'parent',
  57. setup() {
  58. return createComponent(Child)
  59. },
  60. })
  61. const { html } = define({
  62. __scopeId: 'app',
  63. setup() {
  64. return createComponent(Parent)
  65. },
  66. }).render()
  67. expect(html()).toBe(`<div child="" parent="" app=""></div>`)
  68. })
  69. test('should not attach scopeId to nested multiple root components', () => {
  70. const Child = defineVaporComponent({
  71. __scopeId: 'child',
  72. setup() {
  73. return template('<div child></div>', true)()
  74. },
  75. })
  76. const Parent = defineVaporComponent({
  77. __scopeId: 'parent',
  78. setup() {
  79. const n0 = template('<div parent></div>')()
  80. const n1 = createComponent(Child)
  81. return [n0, n1]
  82. },
  83. })
  84. const { html } = define({
  85. __scopeId: 'app',
  86. setup() {
  87. return createComponent(Parent)
  88. },
  89. }).render()
  90. expect(html()).toBe(`<div parent=""></div><div child="" parent=""></div>`)
  91. })
  92. test('should attach scopeId to nested child component with insertion state', () => {
  93. const Child = defineVaporComponent({
  94. __scopeId: 'child',
  95. setup() {
  96. return template('<div child></div>', true)()
  97. },
  98. })
  99. const Parent = defineVaporComponent({
  100. __scopeId: 'parent',
  101. setup() {
  102. return createComponent(Child)
  103. },
  104. })
  105. const { html } = define({
  106. __scopeId: 'app',
  107. setup() {
  108. const t0 = template('<div app></div>', true)
  109. const n1 = t0() as any
  110. setInsertionState(n1)
  111. createComponent(Parent)
  112. return n1
  113. },
  114. }).render()
  115. expect(html()).toBe(
  116. `<div app=""><div child="" parent="" app=""></div></div>`,
  117. )
  118. })
  119. test('should attach scopeId to dynamic component', () => {
  120. const { html } = define({
  121. __scopeId: 'parent',
  122. setup() {
  123. return createDynamicComponent(() => 'button')
  124. },
  125. }).render()
  126. expect(html()).toBe(`<button parent=""></button><!--dynamic-component-->`)
  127. })
  128. test('should attach scopeId to dynamic component with insertion state', () => {
  129. const { html } = define({
  130. __scopeId: 'parent',
  131. setup() {
  132. const t0 = template('<div parent></div>', true)
  133. const n1 = t0() as any
  134. setInsertionState(n1)
  135. createDynamicComponent(() => 'button')
  136. return n1
  137. },
  138. }).render()
  139. expect(html()).toBe(
  140. `<div parent=""><button parent=""></button><!--dynamic-component--></div>`,
  141. )
  142. })
  143. test('should attach scopeId to nested dynamic component', () => {
  144. const Comp = defineVaporComponent({
  145. __scopeId: 'child',
  146. setup() {
  147. return createDynamicComponent(() => 'button', null, null, true)
  148. },
  149. })
  150. const { html } = define({
  151. __scopeId: 'parent',
  152. setup() {
  153. return createComponent(Comp, null, null, true)
  154. },
  155. }).render()
  156. expect(html()).toBe(
  157. `<button child="" parent=""></button><!--dynamic-component-->`,
  158. )
  159. })
  160. test('should attach scopeId to nested dynamic component with insertion state', () => {
  161. const Comp = defineVaporComponent({
  162. __scopeId: 'child',
  163. setup() {
  164. return createDynamicComponent(() => 'button', null, null, true)
  165. },
  166. })
  167. const { html } = define({
  168. __scopeId: 'parent',
  169. setup() {
  170. const t0 = template('<div parent></div>', true)
  171. const n1 = t0() as any
  172. setInsertionState(n1)
  173. createComponent(Comp, null, null, true)
  174. return n1
  175. },
  176. }).render()
  177. expect(html()).toBe(
  178. `<div parent=""><button child="" parent=""></button><!--dynamic-component--></div>`,
  179. )
  180. })
  181. test.todo('should attach scopeId to suspense content', async () => {})
  182. // :slotted basic
  183. test.todo('should work on slots', () => {
  184. const Child = defineVaporComponent({
  185. __scopeId: 'child',
  186. setup() {
  187. const n1 = template('<div child></div>', true)() as any
  188. setInsertionState(n1)
  189. createSlot('default', null)
  190. return n1
  191. },
  192. })
  193. const Child2 = defineVaporComponent({
  194. __scopeId: 'child2',
  195. setup() {
  196. return template('<span child2></span>', true)()
  197. },
  198. })
  199. const { html } = define({
  200. __scopeId: 'parent',
  201. setup() {
  202. const n2 = createComponent(
  203. Child,
  204. null,
  205. {
  206. default: () => {
  207. const n0 = template('<div parent></div>')()
  208. const n1 = createComponent(Child2)
  209. return [n0, n1]
  210. },
  211. },
  212. true,
  213. )
  214. return n2
  215. },
  216. }).render()
  217. expect(html()).toBe(
  218. `<div child="" parent="">` +
  219. `<div parent="" child-s=""></div>` +
  220. // component inside slot should have:
  221. // - scopeId from template context
  222. // - slotted scopeId from slot owner
  223. // - its own scopeId
  224. `<span child2="" child="" parent="" child-s=""></span>` +
  225. `<!--slot-->` +
  226. `</div>`,
  227. )
  228. })
  229. test.todo(':slotted on forwarded slots', async () => {})
  230. })
  231. describe('vdom interop', () => {
  232. test('vdom parent > vapor child', () => {
  233. const VaporChild = defineVaporComponent({
  234. __scopeId: 'vapor-child',
  235. setup() {
  236. return template('<button vapor-child></button>', true)()
  237. },
  238. })
  239. const VdomChild = {
  240. __scopeId: 'vdom-child',
  241. setup() {
  242. return () => h(VaporChild as any)
  243. },
  244. }
  245. const App = {
  246. __scopeId: 'parent',
  247. setup() {
  248. return () => h(VdomChild)
  249. },
  250. }
  251. const root = document.createElement('div')
  252. createApp(App).use(vaporInteropPlugin).mount(root)
  253. expect(root.innerHTML).toBe(
  254. `<button vapor-child="" vdom-child="" parent=""></button>`,
  255. )
  256. })
  257. test('vdom parent > vapor > vdom child', () => {
  258. const InnerVdomChild = {
  259. __scopeId: 'inner-vdom-child',
  260. setup() {
  261. return () => h('button')
  262. },
  263. }
  264. const VaporChild = defineVaporComponent({
  265. __scopeId: 'vapor-child',
  266. setup() {
  267. return createComponent(InnerVdomChild as any, null, null, true)
  268. },
  269. })
  270. const VdomChild = {
  271. __scopeId: 'vdom-child',
  272. setup() {
  273. return () => h(VaporChild as any)
  274. },
  275. }
  276. const App = {
  277. __scopeId: 'parent',
  278. setup() {
  279. return () => h(VdomChild)
  280. },
  281. }
  282. const root = document.createElement('div')
  283. createApp(App).use(vaporInteropPlugin).mount(root)
  284. expect(root.innerHTML).toBe(
  285. `<button inner-vdom-child="" vapor-child="" vdom-child="" parent=""></button>`,
  286. )
  287. })
  288. test('vdom parent > vapor > vapor > vdom child', () => {
  289. const InnerVdomChild = {
  290. __scopeId: 'inner-vdom-child',
  291. setup() {
  292. return () => h('button')
  293. },
  294. }
  295. const VaporChild = defineVaporComponent({
  296. __scopeId: 'vapor-child',
  297. setup() {
  298. return createComponent(InnerVdomChild as any, null, null, true)
  299. },
  300. })
  301. const VaporChild2 = defineVaporComponent({
  302. __scopeId: 'vapor-child2',
  303. setup() {
  304. return createComponent(VaporChild as any, null, null, true)
  305. },
  306. })
  307. const VdomChild = {
  308. __scopeId: 'vdom-child',
  309. setup() {
  310. return () => h(VaporChild2 as any)
  311. },
  312. }
  313. const App = {
  314. __scopeId: 'parent',
  315. setup() {
  316. return () => h(VdomChild)
  317. },
  318. }
  319. const root = document.createElement('div')
  320. createApp(App).use(vaporInteropPlugin).mount(root)
  321. expect(root.innerHTML).toBe(
  322. `<button inner-vdom-child="" vapor-child="" vapor-child2="" vdom-child="" parent=""></button>`,
  323. )
  324. })
  325. test('vdom parent > vapor dynamic child', () => {
  326. const VaporChild = defineVaporComponent({
  327. __scopeId: 'vapor-child',
  328. setup() {
  329. return createDynamicComponent(() => 'button', null, null, true)
  330. },
  331. })
  332. const VdomChild = {
  333. __scopeId: 'vdom-child',
  334. setup() {
  335. return () => h(VaporChild as any)
  336. },
  337. }
  338. const App = {
  339. __scopeId: 'parent',
  340. setup() {
  341. return () => h(VdomChild)
  342. },
  343. }
  344. const root = document.createElement('div')
  345. createApp(App).use(vaporInteropPlugin).mount(root)
  346. expect(root.innerHTML).toBe(
  347. `<button vapor-child="" vdom-child="" parent=""></button><!--dynamic-component-->`,
  348. )
  349. })
  350. test('vapor parent > vdom child', () => {
  351. const VdomChild = {
  352. __scopeId: 'vdom-child',
  353. setup() {
  354. return () => h('button')
  355. },
  356. }
  357. const VaporChild = defineVaporComponent({
  358. __scopeId: 'vapor-child',
  359. setup() {
  360. return createComponent(VdomChild as any, null, null, true)
  361. },
  362. })
  363. const App = {
  364. __scopeId: 'parent',
  365. setup() {
  366. return () => h(VaporChild as any)
  367. },
  368. }
  369. const root = document.createElement('div')
  370. createApp(App).use(vaporInteropPlugin).mount(root)
  371. expect(root.innerHTML).toBe(
  372. `<button vdom-child="" vapor-child="" parent=""></button>`,
  373. )
  374. })
  375. test('vapor parent > vdom > vapor child', () => {
  376. const InnerVaporChild = defineVaporComponent({
  377. __scopeId: 'inner-vapor-child',
  378. setup() {
  379. return template('<button inner-vapor-child></button>', true)()
  380. },
  381. })
  382. const VdomChild = {
  383. __scopeId: 'vdom-child',
  384. setup() {
  385. return () => h(InnerVaporChild as any)
  386. },
  387. }
  388. const VaporChild = defineVaporComponent({
  389. __scopeId: 'vapor-child',
  390. setup() {
  391. return createComponent(VdomChild as any, null, null, true)
  392. },
  393. })
  394. const App = {
  395. __scopeId: 'parent',
  396. setup() {
  397. return () => h(VaporChild as any)
  398. },
  399. }
  400. const root = document.createElement('div')
  401. createApp(App).use(vaporInteropPlugin).mount(root)
  402. expect(root.innerHTML).toBe(
  403. `<button inner-vapor-child="" vdom-child="" vapor-child="" parent=""></button>`,
  404. )
  405. })
  406. test('vapor parent > vdom > vdom > vapor child', () => {
  407. const InnerVaporChild = defineVaporComponent({
  408. __scopeId: 'inner-vapor-child',
  409. setup() {
  410. return template('<button inner-vapor-child></button>', true)()
  411. },
  412. })
  413. const VdomChild = {
  414. __scopeId: 'vdom-child',
  415. setup() {
  416. return () => h(InnerVaporChild as any)
  417. },
  418. }
  419. const VdomChild2 = {
  420. __scopeId: 'vdom-child2',
  421. setup() {
  422. return () => h(VdomChild as any)
  423. },
  424. }
  425. const VaporChild = defineVaporComponent({
  426. __scopeId: 'vapor-child',
  427. setup() {
  428. return createComponent(VdomChild2 as any, null, null, true)
  429. },
  430. })
  431. const App = {
  432. __scopeId: 'parent',
  433. setup() {
  434. return () => h(VaporChild as any)
  435. },
  436. }
  437. const root = document.createElement('div')
  438. createApp(App).use(vaporInteropPlugin).mount(root)
  439. expect(root.innerHTML).toBe(
  440. `<button inner-vapor-child="" vdom-child="" vdom-child2="" vapor-child="" parent=""></button>`,
  441. )
  442. })
  443. })