glass-easel 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (237) hide show
  1. package/README.md +40 -0
  2. package/dist/glass_easel.all.d.ts +1 -0
  3. package/dist/glass_easel.all.js +2 -0
  4. package/dist/glass_easel.all.js.map +1 -0
  5. package/dist/glass_easel.domlike.global.d.ts +1 -0
  6. package/dist/glass_easel.domlike.global.js +2 -0
  7. package/dist/glass_easel.domlike.global.js.map +1 -0
  8. package/dist/index.d.ts +1 -0
  9. package/dist/index.js +1 -0
  10. package/dist/types/src/backend/backend_protocol.d.ts +119 -0
  11. package/dist/types/src/backend/backend_protocol.d.ts.map +1 -0
  12. package/dist/types/src/backend/composed_backend_protocol.d.ts +90 -0
  13. package/dist/types/src/backend/composed_backend_protocol.d.ts.map +1 -0
  14. package/dist/types/src/backend/domlike_backend_protocol.d.ts +76 -0
  15. package/dist/types/src/backend/domlike_backend_protocol.d.ts.map +1 -0
  16. package/dist/types/src/backend/mode.d.ts +46 -0
  17. package/dist/types/src/backend/mode.d.ts.map +1 -0
  18. package/dist/types/src/backend/suggested_backend_protocol.d.ts +30 -0
  19. package/dist/types/src/backend/suggested_backend_protocol.d.ts.map +1 -0
  20. package/dist/types/src/behavior.d.ts +428 -0
  21. package/dist/types/src/behavior.d.ts.map +1 -0
  22. package/dist/types/src/class_list.d.ts +79 -0
  23. package/dist/types/src/class_list.d.ts.map +1 -0
  24. package/dist/types/src/component.d.ts +291 -0
  25. package/dist/types/src/component.d.ts.map +1 -0
  26. package/dist/types/src/component_params.d.ts +239 -0
  27. package/dist/types/src/component_params.d.ts.map +1 -0
  28. package/dist/types/src/component_space.d.ts +164 -0
  29. package/dist/types/src/component_space.d.ts.map +1 -0
  30. package/dist/types/src/data_path.d.ts +5 -0
  31. package/dist/types/src/data_path.d.ts.map +1 -0
  32. package/dist/types/src/data_proxy.d.ts +107 -0
  33. package/dist/types/src/data_proxy.d.ts.map +1 -0
  34. package/dist/types/src/data_utils.d.ts +3 -0
  35. package/dist/types/src/data_utils.d.ts.map +1 -0
  36. package/dist/types/src/element.d.ts +275 -0
  37. package/dist/types/src/element.d.ts.map +1 -0
  38. package/dist/types/src/element_iterator.d.ts +43 -0
  39. package/dist/types/src/element_iterator.d.ts.map +1 -0
  40. package/dist/types/src/event.d.ts +104 -0
  41. package/dist/types/src/event.d.ts.map +1 -0
  42. package/dist/types/src/external_shadow_tree.d.ts +20 -0
  43. package/dist/types/src/external_shadow_tree.d.ts.map +1 -0
  44. package/dist/types/src/func_arr.d.ts +39 -0
  45. package/dist/types/src/func_arr.d.ts.map +1 -0
  46. package/dist/types/src/global_options.d.ts +111 -0
  47. package/dist/types/src/global_options.d.ts.map +1 -0
  48. package/dist/types/src/index.d.ts +43 -0
  49. package/dist/types/src/index.d.ts.map +1 -0
  50. package/dist/types/src/mutation_observer.d.ts +79 -0
  51. package/dist/types/src/mutation_observer.d.ts.map +1 -0
  52. package/dist/types/src/native_node.d.ts +8 -0
  53. package/dist/types/src/native_node.d.ts.map +1 -0
  54. package/dist/types/src/node.d.ts +49 -0
  55. package/dist/types/src/node.d.ts.map +1 -0
  56. package/dist/types/src/relation.d.ts +47 -0
  57. package/dist/types/src/relation.d.ts.map +1 -0
  58. package/dist/types/src/render.d.ts +3 -0
  59. package/dist/types/src/render.d.ts.map +1 -0
  60. package/dist/types/src/selector.d.ts +32 -0
  61. package/dist/types/src/selector.d.ts.map +1 -0
  62. package/dist/types/src/shadow_root.d.ts +136 -0
  63. package/dist/types/src/shadow_root.d.ts.map +1 -0
  64. package/dist/types/src/template_engine.d.ts +18 -0
  65. package/dist/types/src/template_engine.d.ts.map +1 -0
  66. package/dist/types/src/text_node.d.ts +32 -0
  67. package/dist/types/src/text_node.d.ts.map +1 -0
  68. package/dist/types/src/tmpl/index.d.ts +18 -0
  69. package/dist/types/src/tmpl/index.d.ts.map +1 -0
  70. package/dist/types/src/tmpl/native_rendering.d.ts +45 -0
  71. package/dist/types/src/tmpl/native_rendering.d.ts.map +1 -0
  72. package/dist/types/src/tmpl/proc_gen_wrapper.d.ts +80 -0
  73. package/dist/types/src/tmpl/proc_gen_wrapper.d.ts.map +1 -0
  74. package/dist/types/src/tmpl/proc_gen_wrapper_dom.d.ts +50 -0
  75. package/dist/types/src/tmpl/proc_gen_wrapper_dom.d.ts.map +1 -0
  76. package/dist/types/src/tmpl/range_list_diff.d.ts +19 -0
  77. package/dist/types/src/tmpl/range_list_diff.d.ts.map +1 -0
  78. package/dist/types/src/trait_behaviors.d.ts +38 -0
  79. package/dist/types/src/trait_behaviors.d.ts.map +1 -0
  80. package/dist/types/src/virtual_node.d.ts +10 -0
  81. package/dist/types/src/virtual_node.d.ts.map +1 -0
  82. package/dist/types/tests/backend/domlike.test.d.ts +2 -0
  83. package/dist/types/tests/backend/domlike.test.d.ts.map +1 -0
  84. package/dist/types/tests/base/env.d.ts +29 -0
  85. package/dist/types/tests/base/env.d.ts.map +1 -0
  86. package/dist/types/tests/base/match.d.ts +9 -0
  87. package/dist/types/tests/base/match.d.ts.map +1 -0
  88. package/dist/types/tests/core/backend.test.d.ts +2 -0
  89. package/dist/types/tests/core/backend.test.d.ts.map +1 -0
  90. package/dist/types/tests/core/behavior.test.d.ts +2 -0
  91. package/dist/types/tests/core/behavior.test.d.ts.map +1 -0
  92. package/dist/types/tests/core/component_space.test.d.ts +2 -0
  93. package/dist/types/tests/core/component_space.test.d.ts.map +1 -0
  94. package/dist/types/tests/core/data_update.test.d.ts +2 -0
  95. package/dist/types/tests/core/data_update.test.d.ts.map +1 -0
  96. package/dist/types/tests/core/misc.test.d.ts +2 -0
  97. package/dist/types/tests/core/misc.test.d.ts.map +1 -0
  98. package/dist/types/tests/core/placeholder.test.d.ts +2 -0
  99. package/dist/types/tests/core/placeholder.test.d.ts.map +1 -0
  100. package/dist/types/tests/core/slot.test.d.ts +2 -0
  101. package/dist/types/tests/core/slot.test.d.ts.map +1 -0
  102. package/dist/types/tests/core/trait_behaviors.test.d.ts +2 -0
  103. package/dist/types/tests/core/trait_behaviors.test.d.ts.map +1 -0
  104. package/dist/types/tests/tmpl/binding_map.test.d.ts +2 -0
  105. package/dist/types/tests/tmpl/binding_map.test.d.ts.map +1 -0
  106. package/dist/types/tests/tmpl/event.test.d.ts +2 -0
  107. package/dist/types/tests/tmpl/event.test.d.ts.map +1 -0
  108. package/dist/types/tests/tmpl/expression.test.d.ts +2 -0
  109. package/dist/types/tests/tmpl/expression.test.d.ts.map +1 -0
  110. package/dist/types/tests/tmpl/lvalue.test.d.ts +2 -0
  111. package/dist/types/tests/tmpl/lvalue.test.d.ts.map +1 -0
  112. package/dist/types/tests/tmpl/native_rendering.test.d.ts +2 -0
  113. package/dist/types/tests/tmpl/native_rendering.test.d.ts.map +1 -0
  114. package/dist/types/tests/tmpl/structure.test.d.ts +2 -0
  115. package/dist/types/tests/tmpl/structure.test.d.ts.map +1 -0
  116. package/dist/types/tests/types/chaining.test.d.ts +2 -0
  117. package/dist/types/tests/types/chaining.test.d.ts.map +1 -0
  118. package/dist/types/tests/types/createElement.test.d.ts +2 -0
  119. package/dist/types/tests/types/createElement.test.d.ts.map +1 -0
  120. package/dist/types/tests/types/definition.test.d.ts +2 -0
  121. package/dist/types/tests/types/definition.test.d.ts.map +1 -0
  122. package/guide/zh_CN/advanced/binding_map_update.md +32 -0
  123. package/guide/zh_CN/advanced/build_args.md +28 -0
  124. package/guide/zh_CN/advanced/component_filter.md +70 -0
  125. package/guide/zh_CN/advanced/component_space.md +124 -0
  126. package/guide/zh_CN/advanced/custom_backend.md +53 -0
  127. package/guide/zh_CN/advanced/error_listener.md +32 -0
  128. package/guide/zh_CN/advanced/external_component.md +73 -0
  129. package/guide/zh_CN/advanced/template_engine.md +61 -0
  130. package/guide/zh_CN/appendix/backend_protocol.md +501 -0
  131. package/guide/zh_CN/appendix/list_diff_algorithm.md +406 -0
  132. package/guide/zh_CN/basic/beginning.md +94 -0
  133. package/guide/zh_CN/basic/component.md +156 -0
  134. package/guide/zh_CN/basic/event.md +169 -0
  135. package/guide/zh_CN/basic/lifetime.md +66 -0
  136. package/guide/zh_CN/basic/method.md +62 -0
  137. package/guide/zh_CN/basic/template.md +135 -0
  138. package/guide/zh_CN/data_management/advanced_update.md +170 -0
  139. package/guide/zh_CN/data_management/data_deep_copy.md +157 -0
  140. package/guide/zh_CN/data_management/data_observer.md +154 -0
  141. package/guide/zh_CN/data_management/property_early_init.md +31 -0
  142. package/guide/zh_CN/data_management/pure_data_pattern.md +21 -0
  143. package/guide/zh_CN/index.md +93 -0
  144. package/guide/zh_CN/interaction/behavior.md +52 -0
  145. package/guide/zh_CN/interaction/component_path.md +37 -0
  146. package/guide/zh_CN/interaction/generic.md +73 -0
  147. package/guide/zh_CN/interaction/placeholder.md +40 -0
  148. package/guide/zh_CN/interaction/relation.md +151 -0
  149. package/guide/zh_CN/interaction/slot.md +137 -0
  150. package/guide/zh_CN/interaction/template_import.md +94 -0
  151. package/guide/zh_CN/interaction/trait_behavior.md +117 -0
  152. package/guide/zh_CN/styling/external_class.md +46 -0
  153. package/guide/zh_CN/styling/style_isolation.md +54 -0
  154. package/guide/zh_CN/styling/virtual_host.md +52 -0
  155. package/guide/zh_CN/tree/element_iterator.md +54 -0
  156. package/guide/zh_CN/tree/mutation_observer.md +52 -0
  157. package/guide/zh_CN/tree/node_tree.md +142 -0
  158. package/guide/zh_CN/tree/node_tree_modification.md +78 -0
  159. package/guide/zh_CN/tree/selector.md +66 -0
  160. package/jest.config.js +6 -0
  161. package/jest.dts.config.js +9 -0
  162. package/jest.unit.config.js +14 -0
  163. package/package.json +28 -0
  164. package/src/backend/backend_protocol.ts +313 -0
  165. package/src/backend/composed_backend_protocol.ts +252 -0
  166. package/src/backend/domlike_backend_protocol.ts +370 -0
  167. package/src/backend/mode.ts +51 -0
  168. package/src/backend/suggested_backend_protocol.ts +83 -0
  169. package/src/behavior.ts +1655 -0
  170. package/src/bootstrap_dom_dev.js +22 -0
  171. package/src/class_list.ts +376 -0
  172. package/src/component.ts +1309 -0
  173. package/src/component_params.ts +461 -0
  174. package/src/component_space.ts +547 -0
  175. package/src/data_path.ts +225 -0
  176. package/src/data_proxy.ts +670 -0
  177. package/src/data_utils.ts +50 -0
  178. package/src/element.ts +1966 -0
  179. package/src/element_iterator.ts +158 -0
  180. package/src/event.ts +401 -0
  181. package/src/external_shadow_tree.ts +27 -0
  182. package/src/func_arr.ts +198 -0
  183. package/src/global_options.ts +242 -0
  184. package/src/index.ts +187 -0
  185. package/src/mutation_observer.ts +252 -0
  186. package/src/native_node.ts +74 -0
  187. package/src/node.ts +174 -0
  188. package/src/relation.ts +380 -0
  189. package/src/render.ts +25 -0
  190. package/src/selector.ts +218 -0
  191. package/src/shadow_root.ts +766 -0
  192. package/src/template_engine.ts +45 -0
  193. package/src/text_node.ts +149 -0
  194. package/src/tmpl/index.ts +199 -0
  195. package/src/tmpl/native_rendering.ts +175 -0
  196. package/src/tmpl/proc_gen_wrapper.ts +954 -0
  197. package/src/tmpl/proc_gen_wrapper_dom.ts +230 -0
  198. package/src/tmpl/range_list_diff.ts +443 -0
  199. package/src/trait_behaviors.ts +51 -0
  200. package/src/virtual_node.ts +51 -0
  201. package/tests/backend/domlike.test.ts +254 -0
  202. package/tests/base/env.ts +78 -0
  203. package/tests/base/match.ts +185 -0
  204. package/tests/core/backend.test.ts +144 -0
  205. package/tests/core/behavior.test.ts +546 -0
  206. package/tests/core/component_space.test.ts +212 -0
  207. package/tests/core/data_update.test.ts +461 -0
  208. package/tests/core/misc.test.ts +339 -0
  209. package/tests/core/placeholder.test.ts +180 -0
  210. package/tests/core/slot.test.ts +1495 -0
  211. package/tests/core/trait_behaviors.test.ts +153 -0
  212. package/tests/legacy/README.md +3 -0
  213. package/tests/legacy/behavior.test.js +293 -0
  214. package/tests/legacy/component.test.js +1247 -0
  215. package/tests/legacy/data_path.test.js +149 -0
  216. package/tests/legacy/data_proxy.test.js +759 -0
  217. package/tests/legacy/element_iterator.test.js +148 -0
  218. package/tests/legacy/event.test.js +849 -0
  219. package/tests/legacy/external.test.js +510 -0
  220. package/tests/legacy/extra_info.test.js +109 -0
  221. package/tests/legacy/generics.test.js +176 -0
  222. package/tests/legacy/mutation_observer.test.js +210 -0
  223. package/tests/legacy/relation.test.js +517 -0
  224. package/tests/legacy/selector.test.js +263 -0
  225. package/tests/legacy/slot.test.js +915 -0
  226. package/tests/legacy/virtual.test.js +394 -0
  227. package/tests/tmpl/binding_map.test.ts +208 -0
  228. package/tests/tmpl/event.test.ts +206 -0
  229. package/tests/tmpl/expression.test.ts +429 -0
  230. package/tests/tmpl/lvalue.test.ts +160 -0
  231. package/tests/tmpl/native_rendering.test.ts +155 -0
  232. package/tests/tmpl/structure.test.ts +998 -0
  233. package/tests/types/chaining.test.ts +614 -0
  234. package/tests/types/createElement.test.ts +82 -0
  235. package/tests/types/definition.test.ts +442 -0
  236. package/tsconfig.json +11 -0
  237. package/webpack.config.js +270 -0
@@ -0,0 +1,1495 @@
1
+ import { tmpl, domBackend } from '../base/env'
2
+ import { virtual as matchElementWithDom } from '../base/match'
3
+ import * as glassEasel from '../../src'
4
+
5
+ const componentSpace = new glassEasel.ComponentSpace()
6
+ componentSpace.updateComponentOptions({
7
+ writeFieldsToNode: true,
8
+ writeIdToDOM: true,
9
+ })
10
+ componentSpace.defineComponent({
11
+ is: '',
12
+ })
13
+
14
+ const domHtml = (elem: glassEasel.Element): string => {
15
+ const domElem = elem.getBackendElement() as unknown as Element
16
+ return domElem.innerHTML
17
+ }
18
+
19
+ describe('slot', () => {
20
+ describe('dynamic slot', () => {
21
+ describe('core', () => {
22
+ test('should support duplicate slot', () => {
23
+ const ops: Array<[number, string]> = []
24
+
25
+ const comp = componentSpace
26
+ .define()
27
+ .property('s', String)
28
+ .lifetime('attached', function () {
29
+ ops.push([-1, this.data.s.toString()])
30
+ })
31
+ .lifetime('detached', function () {
32
+ ops.push([-2, this.data.s.toString()])
33
+ })
34
+ .lifetime('moved', function () {
35
+ ops.push([-3, this.data.s.toString()])
36
+ })
37
+ .registerComponent()
38
+
39
+ const child = componentSpace
40
+ .define()
41
+ .options({ dynamicSlots: true })
42
+ .definition({
43
+ template: tmpl('<span id="a"></span>'),
44
+ })
45
+ .registerComponent()
46
+
47
+ const parent = componentSpace
48
+ .define()
49
+ .usingComponents({ child, comp })
50
+ .definition({
51
+ template: tmpl(`
52
+ <child>
53
+ <comp slot="a" s="A">A</comp>
54
+ <comp slot="b" s="B">B</comp>
55
+ <comp slot="c" s="C">C</comp>
56
+ <comp s="D">D</comp>
57
+ </child>
58
+ `),
59
+ })
60
+ .registerComponent()
61
+
62
+ const parentElem = glassEasel.Component.createWithContext(
63
+ 'root',
64
+ parent.general(),
65
+ domBackend,
66
+ )
67
+ const childElem = parentElem
68
+ .getShadowRoot()!
69
+ .childNodes[0]!.asInstanceOf(child)!
70
+
71
+ const slot1 = childElem.getShadowRoot()!.createVirtualNode('slot')
72
+ glassEasel.Element.setSlotName(slot1, '')
73
+
74
+ const slotA1 = childElem.getShadowRoot()!.createVirtualNode('slot')
75
+ glassEasel.Element.setSlotName(slotA1, 'a')
76
+
77
+ const slotA2 = childElem.getShadowRoot()!.createVirtualNode('slot')
78
+ glassEasel.Element.setSlotName(slotA2, 'a')
79
+
80
+ const slotB = childElem.getShadowRoot()!.createVirtualNode('slot')
81
+ glassEasel.Element.setSlotName(slotB, 'b')
82
+
83
+ glassEasel.Element.pretendAttached(parentElem)
84
+ expect(childElem.childNodes.length).toBe(0)
85
+ expect(ops).toEqual([])
86
+ expect(domHtml(parentElem)).toBe('<child><span id="a"></span></child>')
87
+ matchElementWithDom(parentElem)
88
+
89
+ childElem.$.a?.appendChild(slotA1 as any)
90
+ expect(childElem.childNodes.length).toBe(4)
91
+ expect(domHtml(parentElem)).toBe(
92
+ '<child><span id="a"><comp>A</comp></span></child>',
93
+ )
94
+ expect(ops).toEqual([[-1, 'A']])
95
+ matchElementWithDom(parentElem)
96
+
97
+ childElem.$.a?.appendChild(slotA2 as any)
98
+ expect(childElem.childNodes.length).toBe(8)
99
+ expect(domHtml(parentElem)).toBe(
100
+ '<child><span id="a"><comp>A</comp><comp>A</comp></span></child>',
101
+ )
102
+ expect(ops).toEqual([
103
+ [-1, 'A'],
104
+ [-1, 'A'],
105
+ ])
106
+ matchElementWithDom(parentElem)
107
+
108
+ childElem.$.a?.insertBefore(slotB as any, slotA2 as any)
109
+ expect(childElem.childNodes.length).toBe(12)
110
+ expect(domHtml(parentElem)).toBe(
111
+ '<child><span id="a"><comp>A</comp><comp>B</comp><comp>A</comp></span></child>',
112
+ )
113
+ expect(ops).toEqual([
114
+ [-1, 'A'],
115
+ [-1, 'A'],
116
+ [-1, 'B'],
117
+ ])
118
+ matchElementWithDom(parentElem)
119
+
120
+ childElem.$.a?.removeChild(slotA1 as any)
121
+ expect(childElem.childNodes.length).toBe(8)
122
+ expect(domHtml(parentElem)).toBe(
123
+ '<child><span id="a"><comp>B</comp><comp>A</comp></span></child>',
124
+ )
125
+ expect(ops).toEqual([
126
+ [-1, 'A'],
127
+ [-1, 'A'],
128
+ [-1, 'B'],
129
+ [-2, 'A'],
130
+ ])
131
+ matchElementWithDom(parentElem)
132
+
133
+ childElem.$.a?.appendChild(slotA1 as any)
134
+ expect(childElem.childNodes.length).toBe(12)
135
+ expect(domHtml(parentElem)).toBe(
136
+ '<child><span id="a"><comp>B</comp><comp>A</comp><comp>A</comp></span></child>',
137
+ )
138
+ expect(ops).toEqual([
139
+ [-1, 'A'],
140
+ [-1, 'A'],
141
+ [-1, 'B'],
142
+ [-2, 'A'],
143
+ [-1, 'A'],
144
+ ])
145
+ matchElementWithDom(parentElem)
146
+
147
+ childElem.$.a?.appendChild(slot1 as any)
148
+ expect(childElem.childNodes.length).toBe(16)
149
+ expect(domHtml(parentElem)).toBe(
150
+ '<child><span id="a"><comp>B</comp><comp>A</comp><comp>A</comp><comp>D</comp></span></child>',
151
+ )
152
+ expect(ops).toEqual([
153
+ [-1, 'A'],
154
+ [-1, 'A'],
155
+ [-1, 'B'],
156
+ [-2, 'A'],
157
+ [-1, 'A'],
158
+ [-1, 'D'],
159
+ ])
160
+ matchElementWithDom(parentElem)
161
+ })
162
+
163
+ test('should support slot element modification', () => {
164
+ const child = componentSpace
165
+ .define()
166
+ .options({ dynamicSlots: true })
167
+ .definition({
168
+ template: tmpl(
169
+ '<slot name="a" /> <span><slot name="b" /></span> <slot name="a" />',
170
+ ),
171
+ })
172
+ .registerComponent()
173
+
174
+ const parent = componentSpace
175
+ .define()
176
+ .usingComponents({ child })
177
+ .definition({
178
+ template: tmpl('<child></child>'),
179
+ })
180
+ .registerComponent()
181
+
182
+ const parentElem = glassEasel.Component.createWithContext(
183
+ 'root',
184
+ parent.general(),
185
+ domBackend,
186
+ )
187
+ const childElem = parentElem
188
+ .getShadowRoot()!
189
+ .childNodes[0]!.asInstanceOf(child)!
190
+
191
+ const slotA = childElem.getShadowRoot()!
192
+ .childNodes[0]! as glassEasel.Element
193
+ const slotB = (
194
+ childElem.getShadowRoot()!.childNodes[1]! as glassEasel.Element
195
+ ).childNodes[0]! as glassEasel.Element
196
+ const slotC = childElem.getShadowRoot()!
197
+ .childNodes[2]! as glassEasel.Element
198
+
199
+ const contentA = parentElem
200
+ .getShadowRoot()!
201
+ .createVirtualNode('virtual')
202
+ contentA.appendChild(parentElem.getShadowRoot()!.createTextNode('A'))
203
+
204
+ const contentB = parentElem
205
+ .getShadowRoot()!
206
+ .createVirtualNode('virtual')
207
+ glassEasel.Element.setInheritSlots(contentB)
208
+ contentB.appendChild(parentElem.getShadowRoot()!.createTextNode('B'))
209
+
210
+ glassEasel.Element.pretendAttached(parentElem)
211
+ expect(domHtml(parentElem)).toBe('<child><span></span></child>')
212
+ matchElementWithDom(parentElem)
213
+
214
+ childElem.appendChild(contentA)
215
+ expect(domHtml(parentElem)).toBe('<child><span></span></child>')
216
+ matchElementWithDom(parentElem)
217
+
218
+ glassEasel.Element.setSlotElement(contentA, slotA)
219
+ expect(domHtml(parentElem)).toBe('<child>A<span></span></child>')
220
+ matchElementWithDom(parentElem)
221
+
222
+ glassEasel.Element.setSlotElement(contentA, slotB)
223
+ expect(domHtml(parentElem)).toBe('<child><span>A</span></child>')
224
+ matchElementWithDom(parentElem)
225
+
226
+ childElem.removeChild(contentA)
227
+ expect(domHtml(parentElem)).toBe('<child><span></span></child>')
228
+ matchElementWithDom(parentElem)
229
+
230
+ glassEasel.Element.setSlotElement(contentA, slotC)
231
+ childElem.appendChild(contentA)
232
+ expect(domHtml(parentElem)).toBe('<child><span></span>A</child>')
233
+ matchElementWithDom(parentElem)
234
+
235
+ childElem.appendChild(contentB)
236
+ expect(domHtml(parentElem)).toBe('<child><span></span>A</child>')
237
+ matchElementWithDom(parentElem)
238
+
239
+ glassEasel.Element.setSlotElement(contentB, slotC)
240
+ expect(domHtml(parentElem)).toBe('<child><span></span>AB</child>')
241
+ matchElementWithDom(parentElem)
242
+
243
+ childElem.appendChild(contentA)
244
+ expect(domHtml(parentElem)).toBe('<child><span></span>BA</child>')
245
+ matchElementWithDom(parentElem)
246
+
247
+ glassEasel.Element.setSlotElement(contentB, slotB)
248
+ expect(domHtml(parentElem)).toBe('<child><span>B</span>A</child>')
249
+ matchElementWithDom(parentElem)
250
+
251
+ childElem.removeChild(contentB)
252
+ expect(domHtml(parentElem)).toBe('<child><span></span>A</child>')
253
+ matchElementWithDom(parentElem)
254
+
255
+ glassEasel.Element.setSlotElement(contentB, slotA)
256
+ childElem.appendChild(contentB)
257
+ expect(domHtml(parentElem)).toBe('<child>B<span></span>A</child>')
258
+
259
+ childElem.removeChild(contentA)
260
+ expect(domHtml(parentElem)).toBe('<child>B<span></span></child>')
261
+ matchElementWithDom(parentElem)
262
+
263
+ childElem.removeChild(contentB)
264
+ expect(domHtml(parentElem)).toBe('<child><span></span></child>')
265
+ matchElementWithDom(parentElem)
266
+ })
267
+ })
268
+
269
+ describe('tmpl', () => {
270
+ test('should support slot content modification', () => {
271
+ const child = componentSpace
272
+ .define()
273
+ .options({ dynamicSlots: true })
274
+ .definition({
275
+ template: tmpl(`
276
+ <slot name="a" />
277
+ <span>
278
+ <slot name="a" />
279
+ </span>
280
+ <slot name="a" />
281
+ `),
282
+ })
283
+ .registerComponent()
284
+
285
+ const parent = componentSpace
286
+ .define()
287
+ .usingComponents({ child })
288
+ .data(() => ({
289
+ slotName1: 'a',
290
+ slotContent1: 'A',
291
+ slotName2: 'a',
292
+ slotContent2: 'B',
293
+ }))
294
+ .definition({
295
+ template: tmpl(`
296
+ <child>
297
+ <block slot="{{slotName1}}">{{slotContent1}}</block>
298
+ <block slot="{{slotName2}}">{{slotContent2}}</block>
299
+ </child>
300
+ `),
301
+ })
302
+ .registerComponent()
303
+
304
+ const parentElem = glassEasel.Component.createWithContext(
305
+ 'root',
306
+ parent.general(),
307
+ domBackend,
308
+ )
309
+ const childElem = parentElem
310
+ .getShadowRoot()!
311
+ .childNodes[0]!.asInstanceOf(child)!
312
+
313
+ expect(childElem.childNodes.length).toBe(6)
314
+ expect(domHtml(parentElem)).toBe('<child>AB<span>AB</span>AB</child>')
315
+ matchElementWithDom(parentElem)
316
+
317
+ parentElem.setData({ slotContent1: 'AA' })
318
+ expect(childElem.childNodes.length).toBe(6)
319
+ expect(domHtml(parentElem)).toBe(
320
+ '<child>AAB<span>AAB</span>AAB</child>',
321
+ )
322
+ matchElementWithDom(parentElem)
323
+
324
+ parentElem.setData({ slotName1: 'b' })
325
+ expect(childElem.childNodes.length).toBe(6)
326
+ expect(domHtml(parentElem)).toBe('<child>B<span>B</span>B</child>')
327
+ matchElementWithDom(parentElem)
328
+
329
+ parentElem.setData({ slotContent1: 'C', slotName1: 'a' })
330
+ expect(childElem.childNodes.length).toBe(6)
331
+ expect(domHtml(parentElem)).toBe('<child>CB<span>CB</span>CB</child>')
332
+ matchElementWithDom(parentElem)
333
+
334
+ parentElem.setData({ slotContent2: 'D' })
335
+ expect(childElem.childNodes.length).toBe(6)
336
+ expect(domHtml(parentElem)).toBe('<child>CD<span>CD</span>CD</child>')
337
+ matchElementWithDom(parentElem)
338
+
339
+ parentElem.setData({ slotName2: 'c' })
340
+ expect(childElem.childNodes.length).toBe(6)
341
+ expect(domHtml(parentElem)).toBe('<child>C<span>C</span>C</child>')
342
+ matchElementWithDom(parentElem)
343
+
344
+ parentElem.setData({ slotName1: 'b', slotName2: 'c' })
345
+ expect(childElem.childNodes.length).toBe(6)
346
+ expect(domHtml(parentElem)).toBe('<child><span></span></child>')
347
+ matchElementWithDom(parentElem)
348
+ })
349
+
350
+ test('should support slot name modification', () => {
351
+ const child = componentSpace
352
+ .define()
353
+ .options({ dynamicSlots: true })
354
+ .definition({
355
+ template: tmpl(`
356
+ <slot name="{{sn}}" some-text="123" />
357
+ `),
358
+ })
359
+ .data(() => ({
360
+ sn: 'abc',
361
+ }))
362
+ .registerComponent()
363
+
364
+ const parent = componentSpace
365
+ .define()
366
+ .usingComponents({ child })
367
+ .definition({
368
+ template: tmpl(`
369
+ <child>
370
+ <span>456</span>
371
+ <div slot="abc" slot:some-text>{{someText}}</div>
372
+ </child>
373
+ `),
374
+ })
375
+ .registerComponent()
376
+
377
+ const parentElem = glassEasel.Component.createWithContext(
378
+ 'root',
379
+ parent.general(),
380
+ domBackend,
381
+ )
382
+ const childElem = parentElem
383
+ .getShadowRoot()!
384
+ .childNodes[0]!.asInstanceOf(child)!
385
+
386
+ expect(domHtml(parentElem)).toBe('<child><div>123</div></child>')
387
+ matchElementWithDom(parentElem)
388
+
389
+ childElem.setData({ sn: '' })
390
+ expect(domHtml(parentElem)).toBe('<child><span>456</span></child>')
391
+ matchElementWithDom(parentElem)
392
+
393
+ childElem.setData({ sn: 'def' })
394
+ expect(domHtml(parentElem)).toBe('<child></child>')
395
+ matchElementWithDom(parentElem)
396
+
397
+ childElem.setData({ sn: 'abc' })
398
+ expect(domHtml(parentElem)).toBe('<child><div>123</div></child>')
399
+ matchElementWithDom(parentElem)
400
+ })
401
+
402
+ test('nested inside dynamic slots', () => {
403
+ let ops: Array<[number, string]> = []
404
+
405
+ const x = componentSpace
406
+ .define()
407
+ .property('s', String)
408
+ .lifetime('attached', function () {
409
+ ops.push([-1, this.data.s.toString()])
410
+ })
411
+ .lifetime('detached', function () {
412
+ ops.push([-2, this.data.s.toString()])
413
+ })
414
+ .lifetime('moved', function () {
415
+ ops.push([-3, this.data.s.toString()])
416
+ })
417
+ .registerComponent()
418
+
419
+ const c1 = componentSpace
420
+ .define()
421
+ .property('enableA1', Boolean)
422
+ .property('enableA2', Boolean)
423
+ .definition({
424
+ template: tmpl(
425
+ `
426
+ <slot wx:if="{{enableA1}}" name="a1" />
427
+ <a><slot wx:if="{{enableA2}}" name="a2"/></a>
428
+ `,
429
+ ),
430
+ })
431
+ .registerComponent()
432
+
433
+ const c2 = componentSpace
434
+ .define()
435
+ .options({ multipleSlots: true })
436
+ .property('enableA1', Boolean)
437
+ .property('enableA2', Boolean)
438
+ .definition({
439
+ template: tmpl(
440
+ `
441
+ <slot wx:if="{{enableA1}}" name="a1" />
442
+ <a><slot wx:if="{{enableA2}}" name="a2"/></a>
443
+ `,
444
+ ),
445
+ })
446
+ .registerComponent()
447
+
448
+ const c3 = componentSpace
449
+ .define()
450
+ .options({ dynamicSlots: true })
451
+ .property('enableA1', Boolean)
452
+ .property('enableA2', Boolean)
453
+ .definition({
454
+ template: tmpl(
455
+ `
456
+ <slot wx:if="{{enableA1}}" name="a1" />
457
+ <a>
458
+ <slot wx:if="{{enableA2}}" name="a2"/>
459
+ <slot wx:if="{{enableA2}}" name="a2"/>
460
+ </a>
461
+ `,
462
+ ),
463
+ })
464
+ .registerComponent()
465
+
466
+ const child = componentSpace
467
+ .define()
468
+ .options({ dynamicSlots: true })
469
+ .property('duplicateSlots', Boolean)
470
+ .definition({
471
+ template: tmpl('<slot /><slot wx:if="{{duplicateSlots}}"/>'),
472
+ })
473
+ .registerComponent()
474
+
475
+ const parent = componentSpace
476
+ .define()
477
+ .data(() => ({
478
+ slotName1: 'a1',
479
+ slotContent1: 'A',
480
+ slotName2: 'a1',
481
+ slotContent2: 'B',
482
+ enableA1: false,
483
+ enableA2: false,
484
+ duplicateSlots: false,
485
+ }))
486
+ .usingComponents({
487
+ child,
488
+ c1,
489
+ c2,
490
+ c3,
491
+ x,
492
+ })
493
+ .definition({
494
+ template: tmpl(
495
+ `
496
+ <child duplicate-slots="{{duplicateSlots}}">
497
+ <c1 enable-a1="{{enableA1}}" enable-a2="{{enableA2}}">
498
+ <x slot="{{slotName1}}" s="1:{{slotContent1}}">{{slotContent1}}</x>
499
+ <x slot="{{slotName2}}" s="1:{{slotContent2}}">{{slotContent2}}</x>
500
+ </c1>
501
+ <c2 enable-a1="{{enableA1}}" enable-a2="{{enableA2}}">
502
+ <x slot="{{slotName1}}" s="2:{{slotContent1}}">{{slotContent1}}</x>
503
+ <x slot="{{slotName2}}" s="2:{{slotContent2}}">{{slotContent2}}</x>
504
+ </c2>
505
+ <c3 enable-a1="{{enableA1}}" enable-a2="{{enableA2}}">
506
+ <x slot="{{slotName1}}" s="3:{{slotContent1}}">{{slotContent1}}</x>
507
+ <x slot="{{slotName2}}" s="3:{{slotContent2}}">{{slotContent2}}</x>
508
+ </c3>
509
+ </child>
510
+ `,
511
+ ),
512
+ })
513
+ .registerComponent()
514
+
515
+ const parentElem = glassEasel.Component.createWithContext(
516
+ 'root',
517
+ parent.general(),
518
+ domBackend,
519
+ ).asInstanceOf(parent)!
520
+
521
+ glassEasel.Element.pretendAttached(parentElem)
522
+ expect(domHtml(parentElem)).toBe(
523
+ '<child><c1><a></a></c1><c2><a></a></c2><c3><a></a></c3></child>',
524
+ )
525
+ expect(ops).toEqual([
526
+ [-1, '1:A'],
527
+ [-1, '1:B'],
528
+ [-1, '2:A'],
529
+ [-1, '2:B'],
530
+ ])
531
+ matchElementWithDom(parentElem)
532
+
533
+ ops = []
534
+ parentElem.setData({ enableA1: true })
535
+ expect(domHtml(parentElem)).toBe(
536
+ '<child><c1><x>A</x><x>B</x><a></a></c1><c2><x>A</x><x>B</x><a></a></c2><c3><x>A</x><x>B</x><a></a></c3></child>',
537
+ )
538
+ expect(ops).toEqual([
539
+ [-1, '3:A'],
540
+ [-1, '3:B'],
541
+ ])
542
+ matchElementWithDom(parentElem)
543
+
544
+ ops = []
545
+ parentElem.setData({ slotName2: 'a2' })
546
+ expect(domHtml(parentElem)).toBe(
547
+ '<child><c1><x>A</x><x>B</x><a></a></c1><c2><x>A</x><a></a></c2><c3><x>A</x><a></a></c3></child>',
548
+ )
549
+ expect(ops).toEqual([
550
+ [-2, '3:B'],
551
+ ])
552
+ matchElementWithDom(parentElem)
553
+
554
+ ops = []
555
+ parentElem.setData({ enableA2: true })
556
+ expect(domHtml(parentElem)).toBe(
557
+ '<child><c1><x>A</x><x>B</x><a></a></c1><c2><x>A</x><a><x>B</x></a></c2><c3><x>A</x><a><x>B</x><x>B</x></a></c3></child>',
558
+ )
559
+ expect(ops).toEqual([
560
+ [-1, '3:B'],
561
+ [-1, '3:B'],
562
+ ])
563
+ matchElementWithDom(parentElem)
564
+
565
+ ops = []
566
+ parentElem.setData({ enableA1: false })
567
+ expect(domHtml(parentElem)).toBe(
568
+ '<child><c1><a><x>A</x><x>B</x></a></c1><c2><a><x>B</x></a></c2><c3><a><x>B</x><x>B</x></a></c3></child>',
569
+ )
570
+ expect(ops).toEqual([
571
+ [-2, '3:A'],
572
+ ])
573
+ matchElementWithDom(parentElem)
574
+
575
+ ops = []
576
+ parentElem.setData({ duplicateSlots: true })
577
+ expect(domHtml(parentElem)).toBe(
578
+ '<child><c1><a><x>A</x><x>B</x></a></c1><c2><a><x>B</x></a></c2><c3><a><x>B</x><x>B</x></a></c3><c1><a><x>A</x><x>B</x></a></c1><c2><a><x>B</x></a></c2><c3><a><x>B</x><x>B</x></a></c3></child>',
579
+ )
580
+ expect(ops).toEqual([
581
+ [-1, '1:A'],
582
+ [-1, '1:B'],
583
+ [-1, '2:A'],
584
+ [-1, '2:B'],
585
+ [-1, '3:B'],
586
+ [-1, '3:B'],
587
+ ])
588
+ matchElementWithDom(parentElem)
589
+
590
+ ops = []
591
+ parentElem.setData({ slotName1: 'a2' })
592
+ expect(domHtml(parentElem)).toBe(
593
+ '<child><c1><a><x>A</x><x>B</x></a></c1><c2><a><x>A</x><x>B</x></a></c2><c3><a><x>A</x><x>B</x><x>A</x><x>B</x></a></c3><c1><a><x>A</x><x>B</x></a></c1><c2><a><x>A</x><x>B</x></a></c2><c3><a><x>A</x><x>B</x><x>A</x><x>B</x></a></c3></child>',
594
+ )
595
+ expect(ops).toEqual([
596
+ [-1, '3:A'],
597
+ [-1, '3:A'],
598
+ [-1, '3:A'],
599
+ [-1, '3:A'],
600
+ ])
601
+ matchElementWithDom(parentElem)
602
+
603
+ ops = []
604
+ parentElem.setData({ enableA2: false })
605
+ expect(domHtml(parentElem)).toBe(
606
+ '<child><c1><a></a></c1><c2><a></a></c2><c3><a></a></c3><c1><a></a></c1><c2><a></a></c2><c3><a></a></c3></child>',
607
+ )
608
+ expect(ops).toEqual([
609
+ [-2, '3:A'],
610
+ [-2, '3:B'],
611
+ [-2, '3:A'],
612
+ [-2, '3:B'],
613
+ [-2, '3:A'],
614
+ [-2, '3:B'],
615
+ [-2, '3:A'],
616
+ [-2, '3:B'],
617
+ ])
618
+ matchElementWithDom(parentElem)
619
+
620
+ ops = []
621
+ parentElem.setData({ enableA1: true })
622
+ expect(domHtml(parentElem)).toBe(
623
+ '<child><c1><x>A</x><x>B</x><a></a></c1><c2><a></a></c2><c3><a></a></c3><c1><x>A</x><x>B</x><a></a></c1><c2><a></a></c2><c3><a></a></c3></child>',
624
+ )
625
+ expect(ops).toEqual([])
626
+ matchElementWithDom(parentElem)
627
+
628
+ ops = []
629
+ parentElem.setData({ slotName1: 'a1' })
630
+ expect(domHtml(parentElem)).toBe(
631
+ '<child><c1><x>A</x><x>B</x><a></a></c1><c2><x>A</x><a></a></c2><c3><x>A</x><a></a></c3><c1><x>A</x><x>B</x><a></a></c1><c2><x>A</x><a></a></c2><c3><x>A</x><a></a></c3></child>',
632
+ )
633
+ expect(ops).toEqual([
634
+ [-1, '3:A'],
635
+ [-1, '3:A'],
636
+ ])
637
+ matchElementWithDom(parentElem)
638
+
639
+ ops = []
640
+ parentElem.setData({ slotContent1: 'C' })
641
+ expect(domHtml(parentElem)).toBe(
642
+ '<child><c1><x>C</x><x>B</x><a></a></c1><c2><x>C</x><a></a></c2><c3><x>C</x><a></a></c3><c1><x>C</x><x>B</x><a></a></c1><c2><x>C</x><a></a></c2><c3><x>C</x><a></a></c3></child>',
643
+ )
644
+ expect(ops).toEqual([])
645
+ matchElementWithDom(parentElem)
646
+
647
+ ops = []
648
+ parentElem.setData({ duplicateSlots: false })
649
+ expect(domHtml(parentElem)).toBe(
650
+ '<child><c1><x>C</x><x>B</x><a></a></c1><c2><x>C</x><a></a></c2><c3><x>C</x><a></a></c3></child>',
651
+ )
652
+ expect(ops).toEqual([
653
+ [-2, '1:C'],
654
+ [-2, '1:B'],
655
+ [-2, '2:C'],
656
+ [-2, '2:B'],
657
+ [-2, '3:C'],
658
+ ])
659
+ matchElementWithDom(parentElem)
660
+ })
661
+
662
+ test('nested outside dynamic slots', () => {
663
+ let ops: Array<[number, string]> = []
664
+
665
+ const x = componentSpace
666
+ .define()
667
+ .property('s', String)
668
+ .lifetime('attached', function () {
669
+ ops.push([-1, this.data.s.toString()])
670
+ })
671
+ .lifetime('detached', function () {
672
+ ops.push([-2, this.data.s.toString()])
673
+ })
674
+ .lifetime('moved', function () {
675
+ ops.push([-3, this.data.s.toString()])
676
+ })
677
+ .registerComponent()
678
+
679
+ const c1 = componentSpace
680
+ .define()
681
+ .property('enableA1', Boolean)
682
+ .property('enableA2', Boolean)
683
+ .definition({
684
+ template: tmpl(
685
+ `
686
+ <slot wx:if="{{enableA1}}" name="a1" />
687
+ <a><slot wx:if="{{enableA2}}" name="a2"/></a>
688
+ `,
689
+ ),
690
+ })
691
+ .registerComponent()
692
+
693
+ const c2 = componentSpace
694
+ .define()
695
+ .options({ multipleSlots: true })
696
+ .property('enableA1', Boolean)
697
+ .property('enableA2', Boolean)
698
+ .definition({
699
+ template: tmpl(
700
+ `
701
+ <slot wx:if="{{enableA1}}" name="a1" />
702
+ <a><slot wx:if="{{enableA2}}" name="a2"/></a>
703
+ `,
704
+ ),
705
+ })
706
+ .registerComponent()
707
+
708
+ const c3 = componentSpace
709
+ .define()
710
+ .options({ dynamicSlots: true })
711
+ .property('enableA1', Boolean)
712
+ .property('enableA2', Boolean)
713
+ .definition({
714
+ template: tmpl(
715
+ `
716
+ <slot wx:if="{{enableA1}}" name="a1" />
717
+ <a>
718
+ <slot wx:if="{{enableA2}}" name="a2"/>
719
+ <slot wx:if="{{enableA2}}" name="a2"/>
720
+ </a>
721
+ `,
722
+ ),
723
+ })
724
+ .registerComponent()
725
+
726
+ const child = componentSpace
727
+ .define()
728
+ .options({ dynamicSlots: true })
729
+ .property('duplicateSlots', Boolean)
730
+ .definition({
731
+ template: tmpl('<slot name="b1" /><slot name="b2" /><slot name="b2" />'),
732
+ })
733
+ .registerComponent()
734
+
735
+ const parent = componentSpace
736
+ .define()
737
+ .data(() => ({
738
+ slotName1: 'b1',
739
+ slotContent1: 'A',
740
+ slotName2: 'b1',
741
+ slotContent2: 'B',
742
+ enableA1: false,
743
+ enableA2: false,
744
+ childSlotName: 'a1',
745
+ }))
746
+ .usingComponents({
747
+ child,
748
+ c1,
749
+ c2,
750
+ c3,
751
+ x,
752
+ })
753
+ .definition({
754
+ template: tmpl(
755
+ `
756
+ <c1 enable-a1="{{enableA1}}" enable-a2="{{enableA2}}">
757
+ <child slot="{{childSlotName}}">
758
+ <x slot="{{slotName1}}" s="1:{{slotContent1}}">{{slotContent1}}</x>
759
+ <x slot="{{slotName2}}" s="1:{{slotContent2}}">{{slotContent2}}</x>
760
+ </child>
761
+ </c1>
762
+ <c2 enable-a1="{{enableA1}}" enable-a2="{{enableA2}}">
763
+ <child slot="{{childSlotName}}">
764
+ <x slot="{{slotName1}}" s="2:{{slotContent1}}">{{slotContent1}}</x>
765
+ <x slot="{{slotName2}}" s="2:{{slotContent2}}">{{slotContent2}}</x>
766
+ </child>
767
+ </c2>
768
+ <c3 enable-a1="{{enableA1}}" enable-a2="{{enableA2}}">
769
+ <child slot="{{childSlotName}}">
770
+ <x slot="{{slotName1}}" s="3:{{slotContent1}}">{{slotContent1}}</x>
771
+ <x slot="{{slotName2}}" s="3:{{slotContent2}}">{{slotContent2}}</x>
772
+ </child>
773
+ </c3>
774
+ `,
775
+ ),
776
+ })
777
+ .registerComponent()
778
+
779
+ const parentElem = glassEasel.Component.createWithContext(
780
+ 'root',
781
+ parent.general(),
782
+ domBackend,
783
+ ).asInstanceOf(parent)!
784
+
785
+ glassEasel.Element.pretendAttached(parentElem)
786
+ expect(domHtml(parentElem)).toBe(
787
+ '<c1><a></a></c1><c2><a></a></c2><c3><a></a></c3>',
788
+ )
789
+ expect(ops).toEqual([
790
+ [-1, '1:A'],
791
+ [-1, '1:B'],
792
+ [-1, '2:A'],
793
+ [-1, '2:B'],
794
+ ])
795
+ matchElementWithDom(parentElem)
796
+
797
+ ops = []
798
+ parentElem.setData({ enableA1: true })
799
+ expect(domHtml(parentElem)).toBe(
800
+ '<c1><child><x>A</x><x>B</x></child><a></a></c1><c2><child><x>A</x><x>B</x></child><a></a></c2><c3><child><x>A</x><x>B</x></child><a></a></c3>',
801
+ )
802
+ expect(ops).toEqual([
803
+ [-1, '3:A'],
804
+ [-1, '3:B'],
805
+ ])
806
+ matchElementWithDom(parentElem)
807
+
808
+ ops = []
809
+ parentElem.setData({ slotName2: 'b2' })
810
+ expect(domHtml(parentElem)).toBe(
811
+ '<c1><child><x>A</x><x>B</x><x>B</x></child><a></a></c1><c2><child><x>A</x><x>B</x><x>B</x></child><a></a></c2><c3><child><x>A</x><x>B</x><x>B</x></child><a></a></c3>',
812
+ )
813
+ expect(ops).toEqual([
814
+ [-2, '1:B'],
815
+ [-1, '1:B'],
816
+ [-1, '1:B'],
817
+ [-2, '2:B'],
818
+ [-1, '2:B'],
819
+ [-1, '2:B'],
820
+ [-2, '3:B'],
821
+ [-1, '3:B'],
822
+ [-1, '3:B'],
823
+ ])
824
+ matchElementWithDom(parentElem)
825
+
826
+ ops = []
827
+ parentElem.setData({ enableA2: true })
828
+ expect(domHtml(parentElem)).toBe(
829
+ '<c1><child><x>A</x><x>B</x><x>B</x></child><a></a></c1><c2><child><x>A</x><x>B</x><x>B</x></child><a></a></c2><c3><child><x>A</x><x>B</x><x>B</x></child><a></a></c3>',
830
+ )
831
+ expect(ops).toEqual([])
832
+ matchElementWithDom(parentElem)
833
+
834
+ ops = []
835
+ parentElem.setData({ childSlotName: 'a2' })
836
+ expect(domHtml(parentElem)).toBe(
837
+ '<c1><child><x>A</x><x>B</x><x>B</x></child><a></a></c1><c2><a><child><x>A</x><x>B</x><x>B</x></child></a></c2><c3><a><child><x>A</x><x>B</x><x>B</x></child><child><x>A</x><x>B</x><x>B</x></child></a></c3>',
838
+ )
839
+ expect(ops).toEqual([
840
+ [-2, '3:A'],
841
+ [-2, '3:B'],
842
+ [-2, '3:B'],
843
+ [-1, '3:A'],
844
+ [-1, '3:B'],
845
+ [-1, '3:B'],
846
+ [-1, '3:A'],
847
+ [-1, '3:B'],
848
+ [-1, '3:B'],
849
+ ])
850
+ matchElementWithDom(parentElem)
851
+
852
+ ops = []
853
+ parentElem.setData({ slotName1: 'b2' })
854
+ expect(domHtml(parentElem)).toBe(
855
+ '<c1><child><x>A</x><x>B</x><x>A</x><x>B</x></child><a></a></c1><c2><a><child><x>A</x><x>B</x><x>A</x><x>B</x></child></a></c2><c3><a><child><x>A</x><x>B</x><x>A</x><x>B</x></child><child><x>A</x><x>B</x><x>A</x><x>B</x></child></a></c3>',
856
+ )
857
+ expect(ops).toEqual([
858
+ [-2, '1:A'],
859
+ [-1, '1:A'],
860
+ [-1, '1:A'],
861
+ [-2, '2:A'],
862
+ [-1, '2:A'],
863
+ [-1, '2:A'],
864
+ [-2, '3:A'],
865
+ [-1, '3:A'],
866
+ [-1, '3:A'],
867
+ [-2, '3:A'],
868
+ [-1, '3:A'],
869
+ [-1, '3:A'],
870
+ ])
871
+ matchElementWithDom(parentElem)
872
+
873
+ ops = []
874
+ parentElem.setData({ slotContent1: 'C' })
875
+ expect(domHtml(parentElem)).toBe(
876
+ '<c1><child><x>C</x><x>B</x><x>C</x><x>B</x></child><a></a></c1><c2><a><child><x>C</x><x>B</x><x>C</x><x>B</x></child></a></c2><c3><a><child><x>C</x><x>B</x><x>C</x><x>B</x></child><child><x>C</x><x>B</x><x>C</x><x>B</x></child></a></c3>',
877
+ )
878
+ expect(ops).toEqual([])
879
+ matchElementWithDom(parentElem)
880
+
881
+ ops = []
882
+ parentElem.setData({ enableA2: false, childSlotName: 'a1' })
883
+ expect(domHtml(parentElem)).toBe(
884
+ '<c1><child><x>C</x><x>B</x><x>C</x><x>B</x></child><a></a></c1><c2><child><x>C</x><x>B</x><x>C</x><x>B</x></child><a></a></c2><c3><child><x>C</x><x>B</x><x>C</x><x>B</x></child><a></a></c3>',
885
+ )
886
+ expect(ops).toEqual([
887
+ [-1, '3:C'],
888
+ [-1, '3:B'],
889
+ [-1, '3:C'],
890
+ [-1, '3:B'],
891
+ [-2, '3:C'],
892
+ [-2, '3:B'],
893
+ [-2, '3:C'],
894
+ [-2, '3:B'],
895
+ [-2, '3:C'],
896
+ [-2, '3:B'],
897
+ [-2, '3:C'],
898
+ [-2, '3:B'],
899
+ ])
900
+ matchElementWithDom(parentElem)
901
+
902
+ ops = []
903
+ parentElem.setData({ enableA1: false })
904
+ expect(domHtml(parentElem)).toBe(
905
+ '<c1><a></a></c1><c2><a></a></c2><c3><a></a></c3>',
906
+ )
907
+ expect(ops).toEqual([
908
+ [-2, '3:C'],
909
+ [-2, '3:B'],
910
+ [-2, '3:C'],
911
+ [-2, '3:B'],
912
+ ])
913
+ matchElementWithDom(parentElem)
914
+ })
915
+
916
+ test('should support slot props', () => {
917
+ let updateCount = 0
918
+ const subComp = componentSpace
919
+ .define()
920
+ .property('propA', Number)
921
+ .observer('propA', () => {
922
+ updateCount += 1
923
+ })
924
+ .registerComponent()
925
+
926
+ const child = componentSpace
927
+ .define()
928
+ .options({ dynamicSlots: true })
929
+ .data(() => ({
930
+ a1: {
931
+ b: [10, 100],
932
+ },
933
+ a2: {
934
+ b: [20, 200],
935
+ },
936
+ c: 0,
937
+ }))
938
+ .definition({
939
+ template: tmpl(
940
+ '<slot a="{{a1}}" c="{{c}}" /><slot a="{{a2}}" c="{{c}}" />',
941
+ ),
942
+ })
943
+ .registerComponent()
944
+
945
+ const parent = componentSpace
946
+ .define()
947
+ .usingComponents({ child, 'x-c': subComp })
948
+ .definition({
949
+ template: tmpl(`
950
+ <child>
951
+ <block slot:a="foo" slot:c="bar">
952
+ <x-c prop-a="{{ foo.b[bar] }}">{{ foo.b[bar] }}</x-c>
953
+ </block>
954
+ <span slot:c>{{c}}</span>
955
+ </child>
956
+ `),
957
+ })
958
+ .registerComponent()
959
+
960
+ const parentElem = glassEasel.Component.createWithContext(
961
+ 'root',
962
+ parent.general(),
963
+ domBackend,
964
+ ).asInstanceOf(parent)!
965
+ const childElem = parentElem
966
+ .getShadowRoot()!
967
+ .childNodes[0]!.asInstanceOf(child)!
968
+
969
+ expect(domHtml(parentElem)).toBe(
970
+ '<child><x-c>10</x-c><span>0</span><x-c>20</x-c><span>0</span></child>',
971
+ )
972
+ expect(updateCount).toBe(2)
973
+ matchElementWithDom(parentElem)
974
+
975
+ childElem.replaceDataOnPath(['a1', 'b', 0], 11)
976
+ childElem.applyDataUpdates()
977
+ expect(domHtml(parentElem)).toBe(
978
+ '<child><x-c>11</x-c><span>0</span><x-c>20</x-c><span>0</span></child>',
979
+ )
980
+ expect(updateCount).toBe(3)
981
+ matchElementWithDom(parentElem)
982
+
983
+ childElem.replaceDataOnPath(['a1', 'b', 0], 12)
984
+ childElem.replaceDataOnPath(['a2', 'b', 0], 21)
985
+ childElem.applyDataUpdates()
986
+ expect(domHtml(parentElem)).toBe(
987
+ '<child><x-c>12</x-c><span>0</span><x-c>21</x-c><span>0</span></child>',
988
+ )
989
+ expect(updateCount).toBe(5)
990
+ matchElementWithDom(parentElem)
991
+
992
+ childElem.replaceDataOnPath(['a2', 'b', 1], 201)
993
+ childElem.applyDataUpdates()
994
+ expect(domHtml(parentElem)).toBe(
995
+ '<child><x-c>12</x-c><span>0</span><x-c>21</x-c><span>0</span></child>',
996
+ )
997
+ expect(updateCount).toBe(6)
998
+ matchElementWithDom(parentElem)
999
+
1000
+ childElem.setData({ c: 1 })
1001
+ expect(domHtml(parentElem)).toBe(
1002
+ '<child><x-c>100</x-c><span>1</span><x-c>201</x-c><span>1</span></child>',
1003
+ )
1004
+ expect(updateCount).toBe(8)
1005
+ matchElementWithDom(parentElem)
1006
+ })
1007
+
1008
+ test('should support duplicate slot props', () => {
1009
+ let ops: Array<[number, string]> = []
1010
+
1011
+ const itemComp = componentSpace
1012
+ .define()
1013
+ .property('s', String)
1014
+ .observer('s', (s: string) => {
1015
+ ops.push([0, s.toString()])
1016
+ })
1017
+ .lifetime('attached', function () {
1018
+ ops.push([-1, this.data.s.toString()])
1019
+ })
1020
+ .lifetime('detached', function () {
1021
+ ops.push([-2, this.data.s.toString()])
1022
+ })
1023
+ .lifetime('moved', function () {
1024
+ ops.push([-3, this.data.s.toString()])
1025
+ })
1026
+ .registerComponent()
1027
+
1028
+ const child = componentSpace
1029
+ .define()
1030
+ .options({ dynamicSlots: true })
1031
+ .data(() => ({
1032
+ items: [] as number[],
1033
+ }))
1034
+ .definition({
1035
+ template: tmpl(
1036
+ '<slot wx:for="{{items}}" item="{{item}}" index="{{index}}" wx:key="*this" />',
1037
+ ),
1038
+ })
1039
+ .registerComponent()
1040
+
1041
+ const parent = componentSpace
1042
+ .define()
1043
+ .usingComponents({ child, 'x-c': itemComp })
1044
+ .data(() => ({
1045
+ a: {
1046
+ foo: 'foo',
1047
+ },
1048
+ bar: undefined as string | undefined,
1049
+ }))
1050
+ .definition({
1051
+ template: tmpl(`
1052
+ <x-c s="{{a.foo}}" />
1053
+ <child>
1054
+ <x-c slot:item slot:index s="{{index}}:{{item}}">{{index}}:{{item}}</x-c>
1055
+ </child>
1056
+ `),
1057
+ })
1058
+ .registerComponent()
1059
+
1060
+ const parentElem = glassEasel.Component.createWithContext(
1061
+ 'root',
1062
+ parent,
1063
+ domBackend,
1064
+ )
1065
+ glassEasel.Element.pretendAttached(parentElem)
1066
+ const childElem = parentElem
1067
+ .getShadowRoot()!
1068
+ .childNodes[1]!.asInstanceOf(child)!
1069
+
1070
+ parentElem.replaceDataOnPath(['a', 'foo'], 'oops')
1071
+
1072
+ expect(domHtml(parentElem)).toBe(
1073
+ '<x-c></x-c><child></child>',
1074
+ )
1075
+ expect(ops).toEqual([
1076
+ [0, 'foo'],
1077
+ [-1, 'foo'],
1078
+ ])
1079
+ matchElementWithDom(parentElem)
1080
+
1081
+ ops = []
1082
+ childElem.setData({ items: [1, 2, 3] })
1083
+ expect(domHtml(parentElem)).toBe(
1084
+ '<x-c></x-c><child><x-c>0:1</x-c><x-c>1:2</x-c><x-c>2:3</x-c></child>',
1085
+ )
1086
+ expect(ops).toEqual([
1087
+ [0, '0:1'],
1088
+ [-1, '0:1'],
1089
+ [0, '1:2'],
1090
+ [-1, '1:2'],
1091
+ [0, '2:3'],
1092
+ [-1, '2:3'],
1093
+ ])
1094
+ matchElementWithDom(parentElem)
1095
+
1096
+ ops = []
1097
+ childElem.setData({ items: [4, 5, 6] })
1098
+ expect(ops).toEqual([
1099
+ [-2, '0:1'],
1100
+ [-2, '1:2'],
1101
+ [-2, '2:3'],
1102
+ [0, '0:4'],
1103
+ [-1, '0:4'],
1104
+ [0, '1:5'],
1105
+ [-1, '1:5'],
1106
+ [0, '2:6'],
1107
+ [-1, '2:6'],
1108
+ ])
1109
+ expect(domHtml(parentElem)).toBe(
1110
+ '<x-c></x-c><child><x-c>0:4</x-c><x-c>1:5</x-c><x-c>2:6</x-c></child>',
1111
+ )
1112
+ matchElementWithDom(parentElem)
1113
+
1114
+ ops = []
1115
+ childElem.setData({ items: [1, 4, 6, 3, 5, 2] })
1116
+ expect(ops).toEqual([
1117
+ [0, '0:1'],
1118
+ [-1, '0:1'],
1119
+ [0, '1:4'],
1120
+ [0, '3:3'],
1121
+ [-1, '3:3'],
1122
+ [0, '4:5'],
1123
+ [0, '5:2'],
1124
+ [-1, '5:2'],
1125
+ ])
1126
+ expect(domHtml(parentElem)).toBe(
1127
+ '<x-c></x-c><child><x-c>0:1</x-c><x-c>1:4</x-c><x-c>2:6</x-c><x-c>3:3</x-c><x-c>4:5</x-c><x-c>5:2</x-c></child>',
1128
+ )
1129
+ matchElementWithDom(parentElem)
1130
+
1131
+ ops = []
1132
+ childElem.setData({ items: [2, 5, 1, 4, 6, 3] })
1133
+ expect(ops).toEqual([
1134
+ [0, '0:2'],
1135
+ [0, '1:5'],
1136
+ [0, '2:1'],
1137
+ [0, '3:4'],
1138
+ [0, '4:6'],
1139
+ [0, '5:3'],
1140
+ ])
1141
+ expect(domHtml(parentElem)).toBe(
1142
+ '<x-c></x-c><child><x-c>0:2</x-c><x-c>1:5</x-c><x-c>2:1</x-c><x-c>3:4</x-c><x-c>4:6</x-c><x-c>5:3</x-c></child>',
1143
+ )
1144
+ matchElementWithDom(parentElem)
1145
+
1146
+ ops = []
1147
+ childElem.setData({ items: [1, 4, 6, 3, 5, 2] })
1148
+ expect(ops).toEqual([
1149
+ [0, '0:1'],
1150
+ [0, '1:4'],
1151
+ [0, '2:6'],
1152
+ [0, '3:3'],
1153
+ [0, '4:5'],
1154
+ [0, '5:2'],
1155
+ ])
1156
+ expect(domHtml(parentElem)).toBe(
1157
+ '<x-c></x-c><child><x-c>0:1</x-c><x-c>1:4</x-c><x-c>2:6</x-c><x-c>3:3</x-c><x-c>4:5</x-c><x-c>5:2</x-c></child>',
1158
+ )
1159
+ matchElementWithDom(parentElem)
1160
+
1161
+ ops = []
1162
+ childElem.spliceArrayDataOnPath(['items'], 2, 1, [])
1163
+ childElem.applyDataUpdates()
1164
+ expect(ops).toEqual([
1165
+ [-2, '2:6'],
1166
+ [0, '2:3'],
1167
+ [0, '3:5'],
1168
+ [0, '4:2'],
1169
+ ])
1170
+ expect(domHtml(parentElem)).toBe(
1171
+ '<x-c></x-c><child><x-c>0:1</x-c><x-c>1:4</x-c><x-c>2:3</x-c><x-c>3:5</x-c><x-c>4:2</x-c></child>',
1172
+ )
1173
+ matchElementWithDom(parentElem)
1174
+
1175
+ ops = []
1176
+ childElem.spliceArrayDataOnPath(['items'], 2, 0, [6])
1177
+ childElem.applyDataUpdates()
1178
+ expect(ops).toEqual([
1179
+ [0, '2:6'],
1180
+ [-1, '2:6'],
1181
+ [0, '3:3'],
1182
+ [0, '4:5'],
1183
+ [0, '5:2'],
1184
+ ])
1185
+ expect(domHtml(parentElem)).toBe(
1186
+ '<x-c></x-c><child><x-c>0:1</x-c><x-c>1:4</x-c><x-c>2:6</x-c><x-c>3:3</x-c><x-c>4:5</x-c><x-c>5:2</x-c></child>',
1187
+ )
1188
+ matchElementWithDom(parentElem)
1189
+
1190
+ ops = []
1191
+ childElem.setData({ items: [] })
1192
+ expect(ops).toEqual([
1193
+ [-2, '1:4'],
1194
+ [-2, '4:5'],
1195
+ [-2, '0:1'],
1196
+ [-2, '3:3'],
1197
+ [-2, '5:2'],
1198
+ [-2, '2:6'],
1199
+ ])
1200
+ expect(domHtml(parentElem)).toBe('<x-c></x-c><child></child>')
1201
+ matchElementWithDom(parentElem)
1202
+
1203
+ ops = []
1204
+ parentElem.setData({ bar: '1' })
1205
+ expect(ops).toEqual([
1206
+ [0, 'oops'],
1207
+ ])
1208
+ })
1209
+
1210
+ test('should support different deep copy strategies', () => {
1211
+ const none = componentSpace
1212
+ .define()
1213
+ .options({
1214
+ dynamicSlots: true,
1215
+ dataDeepCopy: glassEasel.DeepCopyKind.None,
1216
+ propertyPassingDeepCopy: glassEasel.DeepCopyKind.None,
1217
+ })
1218
+ .definition({
1219
+ template: tmpl(`
1220
+ <slot p="{{ sp }}" />
1221
+ `),
1222
+ })
1223
+ .data(() => ({
1224
+ sp: {
1225
+ text: 123,
1226
+ },
1227
+ }))
1228
+ .registerComponent()
1229
+
1230
+ const rec = componentSpace
1231
+ .define()
1232
+ .options({
1233
+ dynamicSlots: true,
1234
+ dataDeepCopy: glassEasel.DeepCopyKind.SimpleWithRecursion,
1235
+ propertyPassingDeepCopy: glassEasel.DeepCopyKind.SimpleWithRecursion,
1236
+ })
1237
+ .definition({
1238
+ template: tmpl(`
1239
+ <slot p="{{ sp }}" />
1240
+ `),
1241
+ })
1242
+ .data(() => ({
1243
+ sp: {
1244
+ text: 'abc',
1245
+ },
1246
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
1247
+ recObj: {} as any,
1248
+ }))
1249
+ .registerComponent()
1250
+
1251
+ const parent = componentSpace
1252
+ .define()
1253
+ .usingComponents({ none, rec })
1254
+ .definition({
1255
+ template: tmpl(`
1256
+ <none><div slot:p>{{p.text}}</div></none>
1257
+ <rec><div slot:p>{{p.text}}</div></rec>
1258
+ `),
1259
+ })
1260
+ .registerComponent()
1261
+
1262
+ const parentElem = glassEasel.Component.createWithContext(
1263
+ 'root',
1264
+ parent.general(),
1265
+ domBackend,
1266
+ )
1267
+ const noneElem = parentElem
1268
+ .getShadowRoot()!
1269
+ .childNodes[0]!.asInstanceOf(none)!
1270
+ const recElem = parentElem
1271
+ .getShadowRoot()!
1272
+ .childNodes[1]!.asInstanceOf(rec)!
1273
+
1274
+ expect(domHtml(parentElem)).toBe('<none><div>123</div></none><rec><div>abc</div></rec>')
1275
+ matchElementWithDom(parentElem)
1276
+
1277
+ noneElem.setData({ 'sp.text': 456 })
1278
+ expect(domHtml(parentElem)).toBe('<none><div>123</div></none><rec><div>abc</div></rec>')
1279
+ matchElementWithDom(parentElem)
1280
+
1281
+ noneElem.setData({ sp: { text: 789 } })
1282
+ expect(domHtml(parentElem)).toBe('<none><div>789</div></none><rec><div>abc</div></rec>')
1283
+ matchElementWithDom(parentElem)
1284
+
1285
+ const recObj = {} as { r: any }
1286
+ recObj.r = recObj
1287
+ recElem.setData({ sp: { text: 'def' }, recObj })
1288
+ expect(domHtml(parentElem)).toBe('<none><div>789</div></none><rec><div>def</div></rec>')
1289
+ matchElementWithDom(parentElem)
1290
+ })
1291
+
1292
+ test('should support placeholders', () => {
1293
+ const placeholder = componentSpace
1294
+ .define()
1295
+ .options({ dynamicSlots: true })
1296
+ .definition({
1297
+ template: tmpl(`
1298
+ <div>
1299
+ <slot text="123" />
1300
+ </div>
1301
+ `),
1302
+ })
1303
+ .data(() => ({
1304
+ sp: {
1305
+ text: 123,
1306
+ },
1307
+ }))
1308
+ .registerComponent()
1309
+
1310
+ const parent = componentSpace
1311
+ .define()
1312
+ .usingComponents({
1313
+ impl: 'impl',
1314
+ placeholder,
1315
+ })
1316
+ .placeholders({
1317
+ impl: 'placeholder',
1318
+ })
1319
+ .definition({
1320
+ template: tmpl(`
1321
+ <impl><div slot:text>{{text}}</div></impl>
1322
+ `),
1323
+ })
1324
+ .registerComponent()
1325
+
1326
+ const parentElem = glassEasel.Component.createWithContext(
1327
+ 'root',
1328
+ parent.general(),
1329
+ domBackend,
1330
+ )
1331
+
1332
+ expect(domHtml(parentElem)).toBe('<impl><div><div>123</div></div></impl>')
1333
+ matchElementWithDom(parentElem)
1334
+
1335
+ componentSpace
1336
+ .define('impl')
1337
+ .options({ dynamicSlots: true })
1338
+ .definition({
1339
+ template: tmpl(`
1340
+ <span>
1341
+ <slot text="456" />
1342
+ </span>
1343
+ `),
1344
+ })
1345
+ .data(() => ({
1346
+ sp: {
1347
+ text: 123,
1348
+ },
1349
+ }))
1350
+ .registerComponent()
1351
+
1352
+ expect(domHtml(parentElem)).toBe('<impl><span><div>456</div></span></impl>')
1353
+ matchElementWithDom(parentElem)
1354
+ })
1355
+ })
1356
+ })
1357
+
1358
+ describe('slot utilities', () => {
1359
+ test('get slot element in shadow root', () => {
1360
+ const single = componentSpace
1361
+ .define()
1362
+ .definition({
1363
+ template: tmpl('<div id="a"><slot name="a" /></div>'),
1364
+ })
1365
+ .registerComponent()
1366
+
1367
+ const multi = componentSpace
1368
+ .define()
1369
+ .options({ multipleSlots: true })
1370
+ .definition({
1371
+ template: tmpl('<div id="a"><slot name="a" /></div>'),
1372
+ })
1373
+ .registerComponent()
1374
+
1375
+ const dynamic = componentSpace
1376
+ .define()
1377
+ .options({ dynamicSlots: true })
1378
+ .definition({
1379
+ template: tmpl('<div id="a"><slot name="a" /></div>'),
1380
+ })
1381
+ .registerComponent()
1382
+
1383
+ const parent = componentSpace
1384
+ .define()
1385
+ .usingComponents({ single, multi, dynamic })
1386
+ .definition({
1387
+ template: tmpl(`
1388
+ <single id="s"><div wx:if="1" slot="a" />S</single>
1389
+ <multi id="m"><div wx:if="1" slot="a" />M</multi>
1390
+ <dynamic id="d"><div wx:if="1" slot="a" />D</dynamic>
1391
+ `),
1392
+ })
1393
+ .registerComponent()
1394
+
1395
+ const parentElem = glassEasel.Component.createWithContext(
1396
+ 'root',
1397
+ parent,
1398
+ domBackend,
1399
+ )
1400
+ const singleElem = (parentElem.$.s as glassEasel.Element).asInstanceOf(single)!
1401
+ const multiElem = (parentElem.$.m as glassEasel.Element).asInstanceOf(multi)!
1402
+ const dynamicElem = (parentElem.$.d as glassEasel.Element).asInstanceOf(dynamic)!
1403
+ const singleSlot = (singleElem.$.a as glassEasel.Element).childNodes[0]!.asElement()!
1404
+ const multiSlot = (multiElem.$.a as glassEasel.Element).childNodes[0]!.asElement()!
1405
+ const dynamicSlot = (dynamicElem.$.a as glassEasel.Element).childNodes[0]!.asElement()!
1406
+
1407
+ expect(parentElem.getShadowRoot()!.getSingleSlotElement()).toBe(null)
1408
+ expect(singleElem.getShadowRoot()!.getSingleSlotElement()).toBe(singleSlot)
1409
+ expect(multiElem.getShadowRoot()!.getSingleSlotElement()).toBe(undefined)
1410
+ expect(dynamicElem.getShadowRoot()!.getSingleSlotElement()).toBe(undefined)
1411
+
1412
+ expect(parentElem.getShadowRoot()!.getSlotElementFromName('')).toBe(null)
1413
+ expect(singleElem.getShadowRoot()!.getSlotElementFromName('')).toBe(singleSlot)
1414
+ expect(multiElem.getShadowRoot()!.getSlotElementFromName('')).toBe(null)
1415
+ expect(dynamicElem.getShadowRoot()!.getSlotElementFromName('')).toStrictEqual([])
1416
+
1417
+ expect(parentElem.getShadowRoot()!.getSlotElementFromName('a')).toBe(null)
1418
+ expect(singleElem.getShadowRoot()!.getSlotElementFromName('a')).toBe(singleSlot)
1419
+ expect(multiElem.getShadowRoot()!.getSlotElementFromName('a')).toBe(multiSlot)
1420
+ expect(dynamicElem.getShadowRoot()!.getSlotElementFromName('a')).toStrictEqual([dynamicSlot])
1421
+
1422
+ parentElem.getShadowRoot()!.forEachSlot(() => {
1423
+ throw new Error()
1424
+ })
1425
+ singleElem.getShadowRoot()!.forEachSlot((slot) => {
1426
+ expect(slot).toBe(singleSlot)
1427
+ })
1428
+ multiElem.getShadowRoot()!.forEachSlot((slot) => {
1429
+ expect(slot).toBe(multiSlot)
1430
+ })
1431
+ dynamicElem.getShadowRoot()!.forEachSlot((slot) => {
1432
+ expect(slot).toBe(dynamicSlot)
1433
+ })
1434
+
1435
+ const checkSlotContentMethods = (
1436
+ sr: glassEasel.ShadowRoot,
1437
+ slot: glassEasel.Element,
1438
+ expectedContent: glassEasel.Node[],
1439
+ ) => {
1440
+ const contentArr = sr.getSlotContentArray(slot)!
1441
+ expect(contentArr?.length).toBe(expectedContent.length)
1442
+ for (let i = 0; i < contentArr?.length; i += 1) {
1443
+ expect(contentArr[i]).toBe(expectedContent[i])
1444
+ }
1445
+ let cur = 0
1446
+ sr.forEachNodeInSlot((node, s) => {
1447
+ if (!s) {
1448
+ if (node.asTextNode()) {
1449
+ expect(node.asTextNode()!.textContent).toBe('M')
1450
+ expect(node.asVirtualNode()).toBe(null)
1451
+ } else {
1452
+ expect(node.asVirtualNode()).toBeTruthy()
1453
+ }
1454
+ return
1455
+ }
1456
+ expect(s).toBe(slot)
1457
+ expect(node).toBe(expectedContent?.[cur])
1458
+ cur += 1
1459
+ })
1460
+ expect(cur).toBe(expectedContent?.length)
1461
+ cur = 0
1462
+ sr.forEachNodeInSpecifiedSlot(slot, (node) => {
1463
+ expect(node).toBe(expectedContent?.[cur])
1464
+ cur += 1
1465
+ })
1466
+ expect(cur).toBe(expectedContent?.length)
1467
+ }
1468
+ checkSlotContentMethods(
1469
+ singleElem.getShadowRoot()!,
1470
+ singleSlot,
1471
+ [
1472
+ singleElem.childNodes[0]!,
1473
+ (singleElem.childNodes[0] as glassEasel.Element).childNodes[0]!,
1474
+ singleElem.childNodes[1]!,
1475
+ ],
1476
+ )
1477
+ checkSlotContentMethods(
1478
+ multiElem.getShadowRoot()!,
1479
+ multiSlot,
1480
+ [
1481
+ (multiElem.childNodes[0] as glassEasel.Element).childNodes[0]!,
1482
+ ],
1483
+ )
1484
+ checkSlotContentMethods(
1485
+ dynamicElem.getShadowRoot()!,
1486
+ dynamicSlot,
1487
+ [
1488
+ dynamicElem.childNodes[0]!,
1489
+ (dynamicElem.childNodes[0] as glassEasel.Element).childNodes[0]!,
1490
+ dynamicElem.childNodes[1]!,
1491
+ ],
1492
+ )
1493
+ })
1494
+ })
1495
+ })