scopeId.spec.ts 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. import {
  2. h,
  3. render,
  4. nodeOps,
  5. serializeInner,
  6. renderSlot
  7. } from '@vue/runtime-test'
  8. import { setScopeId, withCtx } from '../src/componentRenderContext'
  9. describe('scopeId runtime support', () => {
  10. test('should attach scopeId', () => {
  11. const App = {
  12. __scopeId: 'parent',
  13. render: () => h('div', [h('div')])
  14. }
  15. const root = nodeOps.createElement('div')
  16. render(h(App), root)
  17. expect(serializeInner(root)).toBe(`<div parent><div parent></div></div>`)
  18. })
  19. test('should attach scopeId to components in parent component', () => {
  20. const Child = {
  21. __scopeId: 'child',
  22. render: () => h('div')
  23. }
  24. const App = {
  25. __scopeId: 'parent',
  26. render: () => h('div', [h(Child)])
  27. }
  28. const root = nodeOps.createElement('div')
  29. render(h(App), root)
  30. expect(serializeInner(root)).toBe(
  31. `<div parent><div child parent></div></div>`
  32. )
  33. })
  34. // :slotted basic
  35. test('should work on slots', () => {
  36. const Child = {
  37. __scopeId: 'child',
  38. render(this: any) {
  39. return h('div', renderSlot(this.$slots, 'default', {}, undefined, true))
  40. }
  41. }
  42. const Child2 = {
  43. __scopeId: 'child2',
  44. render: () => h('span')
  45. }
  46. const App = {
  47. __scopeId: 'parent',
  48. render: () => {
  49. return h(
  50. Child,
  51. withCtx(() => {
  52. return [h('div'), h(Child2)]
  53. })
  54. )
  55. }
  56. }
  57. const root = nodeOps.createElement('div')
  58. render(h(App), root)
  59. // slot content should have:
  60. // - scopeId from parent
  61. // - slotted scopeId (with `-s` postfix) from child (the tree owner)
  62. expect(serializeInner(root)).toBe(
  63. `<div child parent>` +
  64. `<div parent child-s></div>` +
  65. // component inside slot should have:
  66. // - scopeId from template context
  67. // - slotted scopeId from slot owner
  68. // - its own scopeId
  69. `<span child2 parent child-s></span>` +
  70. `</div>`
  71. )
  72. })
  73. // #2892
  74. test(':slotted on forwarded slots', async () => {
  75. const Wrapper = {
  76. __scopeId: 'wrapper',
  77. render(this: any) {
  78. // <div class="wrapper"><slot/></div>
  79. return h('div', { class: 'wrapper' }, [
  80. renderSlot(this.$slots, 'default')
  81. ])
  82. }
  83. }
  84. const Slotted = {
  85. __scopeId: 'slotted',
  86. render(this: any) {
  87. // <Wrapper><slot/></Wrapper>
  88. return h(Wrapper, null, {
  89. default: withCtx(() => [
  90. renderSlot(this.$slots, 'default', {}, undefined, true)
  91. ])
  92. })
  93. }
  94. }
  95. // simulate hoisted node
  96. setScopeId('root')
  97. const hoisted = h('div', 'hoisted')
  98. setScopeId(null)
  99. const Root = {
  100. __scopeId: 'root',
  101. render(this: any) {
  102. // <Slotted><div>hoisted</div><div>{{ dynamic }}</div></Slotted>
  103. return h(Slotted, null, {
  104. default: withCtx(() => {
  105. return [hoisted, h('div', 'dynamic')]
  106. })
  107. })
  108. }
  109. }
  110. const root = nodeOps.createElement('div')
  111. render(h(Root), root)
  112. expect(serializeInner(root)).toBe(
  113. `<div class="wrapper" wrapper slotted root>` +
  114. `<div root slotted-s>hoisted</div>` +
  115. `<div root slotted-s>dynamic</div>` +
  116. `</div>`
  117. )
  118. const Root2 = {
  119. __scopeId: 'root',
  120. render(this: any) {
  121. // <Slotted>
  122. // <Wrapper>
  123. // <div>hoisted</div><div>{{ dynamic }}</div>
  124. // </Wrapper>
  125. // </Slotted>
  126. return h(Slotted, null, {
  127. default: withCtx(() => [
  128. h(Wrapper, null, {
  129. default: withCtx(() => [hoisted, h('div', 'dynamic')])
  130. })
  131. ])
  132. })
  133. }
  134. }
  135. const root2 = nodeOps.createElement('div')
  136. render(h(Root2), root2)
  137. expect(serializeInner(root2)).toBe(
  138. `<div class="wrapper" wrapper slotted root>` +
  139. `<div class="wrapper" wrapper root slotted-s>` +
  140. `<div root>hoisted</div>` +
  141. `<div root>dynamic</div>` +
  142. `</div>` +
  143. `</div>`
  144. )
  145. })
  146. // #1988
  147. test('should inherit scopeId through nested HOCs with inheritAttrs: false', () => {
  148. const App = {
  149. __scopeId: 'parent',
  150. render: () => {
  151. return h(Child)
  152. }
  153. }
  154. function Child() {
  155. return h(Child2, { class: 'foo' })
  156. }
  157. function Child2() {
  158. return h('div')
  159. }
  160. Child2.inheritAttrs = false
  161. const root = nodeOps.createElement('div')
  162. render(h(App), root)
  163. expect(serializeInner(root)).toBe(`<div parent></div>`)
  164. })
  165. })