import { tmpl, domBackend, composedBackend, shadowBackend, execWithWarn } from '../base/env'
import { virtual as matchElementWithDom } from '../base/match'
import * as glassEasel from '../../src'
const domHtml = (elem: glassEasel.Element): string => {
const domElem = elem.getBackendElement() as unknown as Element
return domElem.innerHTML
}
const testCases = (testBackend: glassEasel.GeneralBackendContext) => {
const componentSpace = new glassEasel.ComponentSpace()
componentSpace.updateComponentOptions({
writeFieldsToNode: true,
writeIdToDOM: true,
})
componentSpace.defineComponent({
is: '',
})
describe('dynamic slot', () => {
describe('core', () => {
test('should support duplicate slot', () => {
const ops: Array<[number, string]> = []
const comp = componentSpace
.define()
.property('s', String)
.lifetime('attached', function () {
ops.push([-1, this.data.s.toString()])
})
.lifetime('detached', function () {
ops.push([-2, this.data.s.toString()])
})
.lifetime('moved', function () {
ops.push([-3, this.data.s.toString()])
})
.registerComponent()
const child = componentSpace
.define()
.options({ dynamicSlots: true })
.definition({
template: tmpl(''),
})
.registerComponent()
const parent = componentSpace
.define()
.usingComponents({ child, comp })
.definition({
template: tmpl(`
A
B
C
D
`),
})
.registerComponent()
const parentElem = glassEasel.Component.createWithContext(
'root',
parent.general(),
testBackend,
)
const childElem = parentElem.getShadowRoot()!.childNodes[0]!.asInstanceOf(child)!
const a = childElem.$.a as glassEasel.Element
const slot1 = childElem.getShadowRoot()!.createVirtualNode('slot')
glassEasel.Element.setSlotName(slot1, '')
const slotA1 = childElem.getShadowRoot()!.createVirtualNode('slot')
glassEasel.Element.setSlotName(slotA1, 'a')
const slotA2 = childElem.getShadowRoot()!.createVirtualNode('slot')
glassEasel.Element.setSlotName(slotA2, 'a')
const slotB = childElem.getShadowRoot()!.createVirtualNode('slot')
glassEasel.Element.setSlotName(slotB, 'b')
glassEasel.Element.pretendAttached(parentElem)
expect(childElem.childNodes.length).toBe(0)
expect(ops).toEqual([])
expect(domHtml(parentElem)).toBe('')
matchElementWithDom(parentElem)
a.appendChild(slotA1)
expect(childElem.childNodes.length).toBe(4)
expect(slotA1.getComposedChildren()).toEqual(childElem.childNodes)
expect(domHtml(parentElem)).toBe('A')
expect(ops).toEqual([[-1, 'A']])
matchElementWithDom(parentElem)
a.appendChild(slotA2)
expect(childElem.childNodes.length).toBe(8)
expect(slotA1.getComposedChildren()).toEqual(childElem.childNodes.slice(0, 4))
expect(slotA2.getComposedChildren()).toEqual(childElem.childNodes.slice(4, 8))
expect(domHtml(parentElem)).toBe(
'AA',
)
expect(ops).toEqual([
[-1, 'A'],
[-1, 'A'],
])
matchElementWithDom(parentElem)
a.insertBefore(slotB, slotA2)
expect(childElem.childNodes.length).toBe(12)
expect(slotA1.getComposedChildren()).toEqual(childElem.childNodes.slice(0, 4))
expect(slotA2.getComposedChildren()).toEqual(childElem.childNodes.slice(4, 8))
expect(slotB.getComposedChildren()).toEqual(childElem.childNodes.slice(8, 12))
expect(domHtml(parentElem)).toBe(
'ABA',
)
expect(ops).toEqual([
[-1, 'A'],
[-1, 'A'],
[-1, 'B'],
])
matchElementWithDom(parentElem)
a.removeChild(slotA1)
expect(childElem.childNodes.length).toBe(8)
expect(slotA2.getComposedChildren()).toEqual(childElem.childNodes.slice(0, 4))
expect(slotB.getComposedChildren()).toEqual(childElem.childNodes.slice(4, 8))
expect(domHtml(parentElem)).toBe(
'BA',
)
expect(ops).toEqual([
[-1, 'A'],
[-1, 'A'],
[-1, 'B'],
[-2, 'A'],
])
matchElementWithDom(parentElem)
a.appendChild(slotA1)
expect(childElem.childNodes.length).toBe(12)
expect(slotA2.getComposedChildren()).toEqual(childElem.childNodes.slice(0, 4))
expect(slotB.getComposedChildren()).toEqual(childElem.childNodes.slice(4, 8))
expect(slotA1.getComposedChildren()).toEqual(childElem.childNodes.slice(8, 12))
expect(domHtml(parentElem)).toBe(
'BAA',
)
expect(ops).toEqual([
[-1, 'A'],
[-1, 'A'],
[-1, 'B'],
[-2, 'A'],
[-1, 'A'],
])
matchElementWithDom(parentElem)
a.appendChild(slot1)
expect(childElem.childNodes.length).toBe(16)
expect(slotA2.getComposedChildren()).toEqual(childElem.childNodes.slice(0, 4))
expect(slotB.getComposedChildren()).toEqual(childElem.childNodes.slice(4, 8))
expect(slotA1.getComposedChildren()).toEqual(childElem.childNodes.slice(8, 12))
expect(slot1.getComposedChildren()).toEqual(childElem.childNodes.slice(12, 16))
expect(domHtml(parentElem)).toBe(
'BAAD',
)
expect(ops).toEqual([
[-1, 'A'],
[-1, 'A'],
[-1, 'B'],
[-2, 'A'],
[-1, 'A'],
[-1, 'D'],
])
matchElementWithDom(parentElem)
})
test('should support slot element modification', () => {
const child = componentSpace
.define()
.options({ dynamicSlots: true })
.definition({
template: tmpl(' '),
})
.registerComponent()
const parent = componentSpace
.define()
.usingComponents({ child })
.definition({
template: tmpl(''),
})
.registerComponent()
const parentElem = glassEasel.Component.createWithContext(
'root',
parent.general(),
testBackend,
)
const childElem = parentElem.getShadowRoot()!.childNodes[0]!.asInstanceOf(child)!
const slotA = childElem.getShadowRoot()!.childNodes[0]! as glassEasel.Element
const slotB = (childElem.getShadowRoot()!.childNodes[1]! as glassEasel.Element)
.childNodes[0]! as glassEasel.Element
const slotC = childElem.getShadowRoot()!.childNodes[2]! as glassEasel.Element
const contentA = parentElem.getShadowRoot()!.createVirtualNode('virtual')
contentA.appendChild(parentElem.getShadowRoot()!.createTextNode('A'))
const contentB = parentElem.getShadowRoot()!.createVirtualNode('virtual')
glassEasel.Element.setInheritSlots(contentB)
const textB = parentElem.getShadowRoot()!.createTextNode('B')
contentB.appendChild(textB)
glassEasel.Element.pretendAttached(parentElem)
expect(slotA.getComposedChildren()).toEqual([])
expect(slotB.getComposedChildren()).toEqual([])
expect(slotC.getComposedChildren()).toEqual([])
expect(domHtml(parentElem)).toBe('')
matchElementWithDom(parentElem)
childElem.appendChild(contentA)
expect(contentA.getComposedParent()).toBe(null)
expect(slotA.getComposedChildren()).toEqual([])
expect(slotB.getComposedChildren()).toEqual([])
expect(slotC.getComposedChildren()).toEqual([])
expect(domHtml(parentElem)).toBe('')
matchElementWithDom(parentElem)
glassEasel.Element.setSlotElement(contentA, slotA)
expect(contentA.getComposedParent()).toBe(slotA)
expect(slotA.getComposedChildren()).toEqual([contentA])
expect(slotB.getComposedChildren()).toEqual([])
expect(slotC.getComposedChildren()).toEqual([])
expect(domHtml(parentElem)).toBe('A')
matchElementWithDom(parentElem)
glassEasel.Element.setSlotElement(contentA, slotB)
expect(contentA.getComposedParent()).toBe(slotB)
expect(slotA.getComposedChildren()).toEqual([])
expect(slotB.getComposedChildren()).toEqual([contentA])
expect(slotC.getComposedChildren()).toEqual([])
expect(domHtml(parentElem)).toBe('A')
matchElementWithDom(parentElem)
childElem.removeChild(contentA)
expect(contentA.getComposedParent()).toBe(null)
expect(slotA.getComposedChildren()).toEqual([])
expect(slotB.getComposedChildren()).toEqual([])
expect(slotC.getComposedChildren()).toEqual([])
expect(domHtml(parentElem)).toBe('')
matchElementWithDom(parentElem)
glassEasel.Element.setSlotElement(contentA, slotC)
childElem.appendChild(contentA)
expect(contentA.getComposedParent()).toBe(slotC)
expect(slotA.getComposedChildren()).toEqual([])
expect(slotB.getComposedChildren()).toEqual([])
expect(slotC.getComposedChildren()).toEqual([contentA])
expect(domHtml(parentElem)).toBe('A')
matchElementWithDom(parentElem)
childElem.appendChild(contentB)
expect(contentA.getComposedParent()).toBe(slotC)
expect(contentB.getComposedParent()).toBe(null)
expect(textB.getComposedParent()).toBe(null)
expect(slotA.getComposedChildren()).toEqual([])
expect(slotB.getComposedChildren()).toEqual([])
expect(slotC.getComposedChildren()).toEqual([contentA])
expect(domHtml(parentElem)).toBe('A')
matchElementWithDom(parentElem)
glassEasel.Element.setSlotElement(contentB, slotC)
expect(contentA.getComposedParent()).toBe(slotC)
expect(contentB.getComposedParent()).toBe(slotC)
expect(textB.getComposedParent()).toBe(slotC)
expect(slotA.getComposedChildren()).toEqual([])
expect(slotB.getComposedChildren()).toEqual([])
expect(slotC.getComposedChildren()).toEqual([contentA, contentB, textB])
expect(domHtml(parentElem)).toBe('AB')
matchElementWithDom(parentElem)
childElem.appendChild(contentA)
expect(contentA.getComposedParent()).toBe(slotC)
expect(contentB.getComposedParent()).toBe(slotC)
expect(textB.getComposedParent()).toBe(slotC)
expect(slotA.getComposedChildren()).toEqual([])
expect(slotB.getComposedChildren()).toEqual([])
expect(slotC.getComposedChildren()).toEqual([contentB, textB, contentA])
expect(domHtml(parentElem)).toBe('BA')
matchElementWithDom(parentElem)
glassEasel.Element.setSlotElement(contentB, slotB)
expect(contentA.getComposedParent()).toBe(slotC)
expect(contentB.getComposedParent()).toBe(slotB)
expect(textB.getComposedParent()).toBe(slotB)
expect(slotA.getComposedChildren()).toEqual([])
expect(slotB.getComposedChildren()).toEqual([contentB, textB])
expect(slotC.getComposedChildren()).toEqual([contentA])
expect(domHtml(parentElem)).toBe('BA')
matchElementWithDom(parentElem)
childElem.removeChild(contentB)
expect(contentA.getComposedParent()).toBe(slotC)
expect(contentB.getComposedParent()).toBe(null)
expect(textB.getComposedParent()).toBe(null)
expect(slotA.getComposedChildren()).toEqual([])
expect(slotB.getComposedChildren()).toEqual([])
expect(slotC.getComposedChildren()).toEqual([contentA])
expect(domHtml(parentElem)).toBe('A')
matchElementWithDom(parentElem)
glassEasel.Element.setSlotElement(contentB, slotA)
childElem.appendChild(contentB)
expect(contentA.getComposedParent()).toBe(slotC)
expect(contentB.getComposedParent()).toBe(slotA)
expect(textB.getComposedParent()).toBe(slotA)
expect(slotA.getComposedChildren()).toEqual([contentB, textB])
expect(slotB.getComposedChildren()).toEqual([])
expect(slotC.getComposedChildren()).toEqual([contentA])
expect(domHtml(parentElem)).toBe('BA')
matchElementWithDom(parentElem)
childElem.removeChild(contentA)
expect(contentA.getComposedParent()).toBe(null)
expect(contentB.getComposedParent()).toBe(slotA)
expect(textB.getComposedParent()).toBe(slotA)
expect(slotA.getComposedChildren()).toEqual([contentB, textB])
expect(slotB.getComposedChildren()).toEqual([])
expect(slotC.getComposedChildren()).toEqual([])
expect(domHtml(parentElem)).toBe('B')
matchElementWithDom(parentElem)
childElem.removeChild(contentB)
expect(contentA.getComposedParent()).toBe(null)
expect(contentB.getComposedParent()).toBe(null)
expect(textB.getComposedParent()).toBe(null)
expect(slotA.getComposedChildren()).toEqual([])
expect(slotB.getComposedChildren()).toEqual([])
expect(slotC.getComposedChildren()).toEqual([])
expect(domHtml(parentElem)).toBe('')
matchElementWithDom(parentElem)
})
})
describe('tmpl', () => {
test('should support slot value usages', () => {
const child = componentSpace
.define()
.options({ dynamicSlots: true, virtualHost: true })
.definition({
data: {
v: 123,
},
template: tmpl(`
`),
})
.data(() => ({
sn: 'abc',
}))
.registerComponent()
const parent = componentSpace
.define()
.usingComponents({ child })
.definition({
template: tmpl(`
{{someText}}
`),
})
.registerComponent()
const parentElem = glassEasel.Component.createWithContext(
'root',
parent.general(),
testBackend,
)
const childElem = parentElem.getShadowRoot()!.childNodes[0]!.asInstanceOf(child)!
expect(domHtml(parentElem)).toBe('
123
')
matchElementWithDom(parentElem)
childElem.setData({ v: 456 })
expect(domHtml(parentElem)).toBe('456
')
matchElementWithDom(parentElem)
})
test('should support slot content modification', () => {
const child = componentSpace
.define()
.options({ dynamicSlots: true })
.definition({
template: tmpl(`
`),
})
.registerComponent()
const parent = componentSpace
.define()
.usingComponents({ child })
.data(() => ({
slotName1: 'a',
slotContent1: 'A',
slotName2: 'a',
slotContent2: 'B',
}))
.definition({
template: tmpl(`
{{slotContent1}}
{{slotContent2}}
`),
})
.registerComponent()
const parentElem = glassEasel.Component.createWithContext(
'root',
parent.general(),
testBackend,
)
const childElem = parentElem.getShadowRoot()!.childNodes[0]!.asInstanceOf(child)!
expect(childElem.childNodes.length).toBe(6)
expect(domHtml(parentElem)).toBe('ABABAB')
matchElementWithDom(parentElem)
parentElem.setData({ slotContent1: 'AA' })
expect(childElem.childNodes.length).toBe(6)
expect(domHtml(parentElem)).toBe('AABAABAAB')
matchElementWithDom(parentElem)
parentElem.setData({ slotName1: 'b' })
expect(childElem.childNodes.length).toBe(6)
expect(domHtml(parentElem)).toBe('BBB')
matchElementWithDom(parentElem)
parentElem.setData({ slotContent1: 'C', slotName1: 'a' })
expect(childElem.childNodes.length).toBe(6)
expect(domHtml(parentElem)).toBe('CBCBCB')
matchElementWithDom(parentElem)
parentElem.setData({ slotContent2: 'D' })
expect(childElem.childNodes.length).toBe(6)
expect(domHtml(parentElem)).toBe('CDCDCD')
matchElementWithDom(parentElem)
parentElem.setData({ slotName2: 'c' })
expect(childElem.childNodes.length).toBe(6)
expect(domHtml(parentElem)).toBe('CCC')
matchElementWithDom(parentElem)
parentElem.setData({ slotName1: 'b', slotName2: 'c' })
expect(childElem.childNodes.length).toBe(6)
expect(domHtml(parentElem)).toBe('')
matchElementWithDom(parentElem)
})
test('should support slot name modification', () => {
const child = componentSpace
.define()
.options({ dynamicSlots: true })
.definition({
template: tmpl(`
`),
})
.data(() => ({
sn: 'abc',
}))
.registerComponent()
const parent = componentSpace
.define()
.usingComponents({ child })
.definition({
template: tmpl(`
456
{{someText}}
`),
})
.registerComponent()
const parentElem = glassEasel.Component.createWithContext(
'root',
parent.general(),
testBackend,
)
const childElem = parentElem.getShadowRoot()!.childNodes[0]!.asInstanceOf(child)!
expect(domHtml(parentElem)).toBe('123
')
matchElementWithDom(parentElem)
childElem.setData({ sn: '' })
expect(domHtml(parentElem)).toBe('456')
matchElementWithDom(parentElem)
childElem.setData({ sn: 'def' })
expect(domHtml(parentElem)).toBe('')
matchElementWithDom(parentElem)
childElem.setData({ sn: 'abc' })
expect(domHtml(parentElem)).toBe('123
')
matchElementWithDom(parentElem)
})
test('nested inside dynamic slots', () => {
let ops: Array<[number, string]> = []
const x = componentSpace
.define()
.property('s', String)
.lifetime('attached', function () {
ops.push([-1, this.data.s.toString()])
})
.lifetime('detached', function () {
ops.push([-2, this.data.s.toString()])
})
.lifetime('moved', function () {
ops.push([-3, this.data.s.toString()])
})
.registerComponent()
const c1 = componentSpace
.define()
.property('enableA1', Boolean)
.property('enableA2', Boolean)
.definition({
template: tmpl(
`
`,
),
})
.registerComponent()
const c2 = componentSpace
.define()
.options({ multipleSlots: true })
.property('enableA1', Boolean)
.property('enableA2', Boolean)
.definition({
template: tmpl(
`
`,
),
})
.registerComponent()
const c3 = componentSpace
.define()
.options({ dynamicSlots: true })
.property('enableA1', Boolean)
.property('enableA2', Boolean)
.definition({
template: tmpl(
`
`,
),
})
.registerComponent()
const child = componentSpace
.define()
.options({ dynamicSlots: true })
.property('duplicateSlots', Boolean)
.definition({
template: tmpl(''),
})
.registerComponent()
const parent = componentSpace
.define()
.data(() => ({
slotName1: 'a1',
slotContent1: 'A',
slotName2: 'a1',
slotContent2: 'B',
enableA1: false,
enableA2: false,
duplicateSlots: false,
}))
.usingComponents({
child,
c1,
c2,
c3,
x,
})
.definition({
template: tmpl(
`
{{slotContent1}}
{{slotContent2}}
{{slotContent1}}
{{slotContent2}}
{{slotContent1}}
{{slotContent2}}
`,
),
})
.registerComponent()
const parentElem = glassEasel.Component.createWithContext(
'root',
parent.general(),
testBackend,
).asInstanceOf(parent)!
glassEasel.Element.pretendAttached(parentElem)
expect(domHtml(parentElem)).toBe(
'',
)
expect(ops).toEqual([
[-1, '1:A'],
[-1, '1:B'],
[-1, '2:A'],
[-1, '2:B'],
])
matchElementWithDom(parentElem)
ops = []
parentElem.setData({ enableA1: true })
expect(domHtml(parentElem)).toBe(
'ABABAB',
)
expect(ops).toEqual([
[-1, '3:A'],
[-1, '3:B'],
])
matchElementWithDom(parentElem)
ops = []
parentElem.setData({ slotName2: 'a2' })
expect(domHtml(parentElem)).toBe(
'ABAA',
)
expect(ops).toEqual([[-2, '3:B']])
matchElementWithDom(parentElem)
ops = []
parentElem.setData({ enableA2: true })
expect(domHtml(parentElem)).toBe(
'ABABABB',
)
expect(ops).toEqual([
[-1, '3:B'],
[-1, '3:B'],
])
matchElementWithDom(parentElem)
ops = []
parentElem.setData({ enableA1: false })
expect(domHtml(parentElem)).toBe(
'ABBBB',
)
expect(ops).toEqual([[-2, '3:A']])
matchElementWithDom(parentElem)
ops = []
parentElem.setData({ duplicateSlots: true })
expect(domHtml(parentElem)).toBe(
'ABBBBABBBB',
)
expect(ops).toEqual([
[-1, '1:A'],
[-1, '1:B'],
[-1, '2:A'],
[-1, '2:B'],
[-1, '3:B'],
[-1, '3:B'],
])
matchElementWithDom(parentElem)
ops = []
parentElem.setData({ slotName1: 'a2' })
expect(domHtml(parentElem)).toBe(
'ABABABABABABABAB',
)
expect(ops).toEqual([
[-1, '3:A'],
[-1, '3:A'],
[-1, '3:A'],
[-1, '3:A'],
])
matchElementWithDom(parentElem)
ops = []
parentElem.setData({ enableA2: false })
expect(domHtml(parentElem)).toBe(
'',
)
expect(ops).toEqual([
[-2, '3:A'],
[-2, '3:B'],
[-2, '3:A'],
[-2, '3:B'],
[-2, '3:A'],
[-2, '3:B'],
[-2, '3:A'],
[-2, '3:B'],
])
matchElementWithDom(parentElem)
ops = []
parentElem.setData({ enableA1: true })
expect(domHtml(parentElem)).toBe(
'ABAB',
)
expect(ops).toEqual([])
matchElementWithDom(parentElem)
ops = []
parentElem.setData({ slotName1: 'a1' })
expect(domHtml(parentElem)).toBe(
'ABAAABAA',
)
expect(ops).toEqual([
[-1, '3:A'],
[-1, '3:A'],
])
matchElementWithDom(parentElem)
ops = []
parentElem.setData({ slotContent1: 'C' })
expect(domHtml(parentElem)).toBe(
'CBCCCBCC',
)
expect(ops).toEqual([])
matchElementWithDom(parentElem)
ops = []
parentElem.setData({ duplicateSlots: false })
expect(domHtml(parentElem)).toBe(
'CBCC',
)
expect(ops).toEqual([
[-2, '1:C'],
[-2, '1:B'],
[-2, '2:C'],
[-2, '2:B'],
[-2, '3:C'],
])
matchElementWithDom(parentElem)
})
test('nested outside dynamic slots', () => {
let ops: Array<[number, string]> = []
const x = componentSpace
.define()
.property('s', String)
.lifetime('attached', function () {
ops.push([-1, this.data.s.toString()])
})
.lifetime('detached', function () {
ops.push([-2, this.data.s.toString()])
})
.lifetime('moved', function () {
ops.push([-3, this.data.s.toString()])
})
.registerComponent()
const c1 = componentSpace
.define()
.property('enableA1', Boolean)
.property('enableA2', Boolean)
.definition({
template: tmpl(
`
`,
),
})
.registerComponent()
const c2 = componentSpace
.define()
.options({ multipleSlots: true })
.property('enableA1', Boolean)
.property('enableA2', Boolean)
.definition({
template: tmpl(
`
`,
),
})
.registerComponent()
const c3 = componentSpace
.define()
.options({ dynamicSlots: true })
.property('enableA1', Boolean)
.property('enableA2', Boolean)
.definition({
template: tmpl(
`
`,
),
})
.registerComponent()
const child = componentSpace
.define()
.options({ dynamicSlots: true })
.property('duplicateSlots', Boolean)
.definition({
template: tmpl(''),
})
.registerComponent()
const parent = componentSpace
.define()
.data(() => ({
slotName1: 'b1',
slotContent1: 'A',
slotName2: 'b1',
slotContent2: 'B',
enableA1: false,
enableA2: false,
childSlotName: 'a1',
}))
.usingComponents({
child,
c1,
c2,
c3,
x,
})
.definition({
template: tmpl(
`
{{slotContent1}}
{{slotContent2}}
{{slotContent1}}
{{slotContent2}}
{{slotContent1}}
{{slotContent2}}
`,
),
})
.registerComponent()
const parentElem = glassEasel.Component.createWithContext(
'root',
parent.general(),
testBackend,
).asInstanceOf(parent)!
glassEasel.Element.pretendAttached(parentElem)
expect(domHtml(parentElem)).toBe('')
expect(ops).toEqual([
[-1, '1:A'],
[-1, '1:B'],
[-1, '2:A'],
[-1, '2:B'],
])
matchElementWithDom(parentElem)
ops = []
parentElem.setData({ enableA1: true })
expect(domHtml(parentElem)).toBe(
'ABABAB',
)
expect(ops).toEqual([
[-1, '3:A'],
[-1, '3:B'],
])
matchElementWithDom(parentElem)
ops = []
parentElem.setData({ slotName2: 'b2' })
expect(domHtml(parentElem)).toBe(
'ABBABBABB',
)
expect(ops).toEqual([
[-2, '1:B'],
[-1, '1:B'],
[-1, '1:B'],
[-2, '2:B'],
[-1, '2:B'],
[-1, '2:B'],
[-2, '3:B'],
[-1, '3:B'],
[-1, '3:B'],
])
matchElementWithDom(parentElem)
ops = []
parentElem.setData({ enableA2: true })
expect(domHtml(parentElem)).toBe(
'ABBABBABB',
)
expect(ops).toEqual([])
matchElementWithDom(parentElem)
ops = []
parentElem.setData({ childSlotName: 'a2' })
expect(domHtml(parentElem)).toBe(
'ABBABBABBABB',
)
expect(ops).toEqual([
[-2, '3:A'],
[-2, '3:B'],
[-2, '3:B'],
[-1, '3:A'],
[-1, '3:B'],
[-1, '3:B'],
[-1, '3:A'],
[-1, '3:B'],
[-1, '3:B'],
])
matchElementWithDom(parentElem)
ops = []
parentElem.setData({ slotName1: 'b2' })
expect(domHtml(parentElem)).toBe(
'ABABABABABABABAB',
)
expect(ops).toEqual([
[-2, '1:A'],
[-1, '1:A'],
[-1, '1:A'],
[-2, '2:A'],
[-1, '2:A'],
[-1, '2:A'],
[-2, '3:A'],
[-1, '3:A'],
[-1, '3:A'],
[-2, '3:A'],
[-1, '3:A'],
[-1, '3:A'],
])
matchElementWithDom(parentElem)
ops = []
parentElem.setData({ slotContent1: 'C' })
expect(domHtml(parentElem)).toBe(
'CBCBCBCBCBCBCBCB',
)
expect(ops).toEqual([])
matchElementWithDom(parentElem)
ops = []
parentElem.setData({ enableA2: false, childSlotName: 'a1' })
expect(domHtml(parentElem)).toBe(
'CBCBCBCBCBCB',
)
expect(ops).toEqual([
[-2, '3:C'],
[-2, '3:B'],
[-2, '3:C'],
[-2, '3:B'],
[-2, '3:C'],
[-2, '3:B'],
[-2, '3:C'],
[-2, '3:B'],
[-1, '3:C'],
[-1, '3:B'],
[-1, '3:C'],
[-1, '3:B'],
])
matchElementWithDom(parentElem)
ops = []
parentElem.setData({ enableA1: false })
expect(domHtml(parentElem)).toBe('')
expect(ops).toEqual([
[-2, '3:C'],
[-2, '3:B'],
[-2, '3:C'],
[-2, '3:B'],
])
matchElementWithDom(parentElem)
})
test('should support slot props', () => {
let updateCount = 0
const subComp = componentSpace
.define()
.property('propA', Number)
.observer('propA', () => {
updateCount += 1
})
.registerComponent()
const child = componentSpace
.define()
.options({ dynamicSlots: true })
.data(() => ({
a1: {
b: [10, 100],
},
a2: {
b: [20, 200],
},
c: 0,
}))
.definition({
template: tmpl(''),
})
.registerComponent()
const parent = componentSpace
.define()
.usingComponents({ child, 'x-c': subComp })
.definition({
template: tmpl(`
{{ foo.b[bar] }}
{{c}}
`),
})
.registerComponent()
const parentElem = glassEasel.Component.createWithContext(
'root',
parent.general(),
testBackend,
).asInstanceOf(parent)!
const childElem = parentElem.getShadowRoot()!.childNodes[0]!.asInstanceOf(child)!
expect(domHtml(parentElem)).toBe(
'100200',
)
expect(updateCount).toBe(2)
matchElementWithDom(parentElem)
childElem.replaceDataOnPath(['a1', 'b', 0], 11)
childElem.applyDataUpdates()
expect(domHtml(parentElem)).toBe(
'110200',
)
expect(updateCount).toBe(3)
matchElementWithDom(parentElem)
childElem.replaceDataOnPath(['a1', 'b', 0], 12)
childElem.replaceDataOnPath(['a2', 'b', 0], 21)
childElem.applyDataUpdates()
expect(domHtml(parentElem)).toBe(
'120210',
)
expect(updateCount).toBe(5)
matchElementWithDom(parentElem)
childElem.replaceDataOnPath(['a2', 'b', 1], 201)
childElem.applyDataUpdates()
expect(domHtml(parentElem)).toBe(
'120210',
)
expect(updateCount).toBe(6)
matchElementWithDom(parentElem)
childElem.setData({ c: 1 })
expect(domHtml(parentElem)).toBe(
'10012011',
)
expect(updateCount).toBe(8)
matchElementWithDom(parentElem)
})
test('should support slot props with wx:for', () => {
const child = componentSpace
.define()
.options({ dynamicSlots: true })
.data(() => ({
arr: [1, 2],
}))
.definition({
template: tmpl(''),
})
.registerComponent()
const parent = componentSpace
.define()
.usingComponents({ child })
.definition({
template: tmpl(`
{{c}}
`),
})
.registerComponent()
const parentElem = glassEasel.Component.createWithContext(
'root',
parent.general(),
testBackend,
).asInstanceOf(parent)!
const childElem = parentElem.getShadowRoot()!.childNodes[0]!.asInstanceOf(child)!
expect(domHtml(parentElem)).toBe('12')
matchElementWithDom(parentElem)
childElem.setData({ arr: [2, 1] })
expect(domHtml(parentElem)).toBe('21')
matchElementWithDom(parentElem)
childElem.spliceArrayDataOnPath(['arr'], 1, 0, [3, 4])
childElem.spliceArrayDataOnPath(['arr'], 4, 0, [5])
childElem.applyDataUpdates()
expect(domHtml(parentElem)).toBe('23415')
matchElementWithDom(parentElem)
childElem.spliceArrayDataOnPath(['arr'], 0, 1, [])
childElem.spliceArrayDataOnPath(['arr'], 2, 1, [])
childElem.applyDataUpdates()
expect(domHtml(parentElem)).toBe('345')
matchElementWithDom(parentElem)
})
test('should support slot props of array', () => {
const child = componentSpace
.define()
.options({ dynamicSlots: true })
.data(() => ({
c: [123, 456, 789],
}))
.definition({
template: tmpl(''),
})
.registerComponent()
const parent = componentSpace
.define()
.usingComponents({ child })
.definition({
template: tmpl(`
{{item}}
`),
})
.registerComponent()
const parentElem = glassEasel.Component.createWithContext(
'root',
parent.general(),
testBackend,
).asInstanceOf(parent)!
const childElem = parentElem.getShadowRoot()!.childNodes[0]!.asInstanceOf(child)!
expect(domHtml(parentElem)).toBe('123456789')
matchElementWithDom(parentElem)
childElem.setData({ c: [456] })
expect(domHtml(parentElem)).toBe('456')
matchElementWithDom(parentElem)
})
test('should support slot props inside wx:for and proper updates', () => {
let updateCount = 0
const itemChild = componentSpace
.define()
.property('p', Number)
.observer('p', () => {
updateCount += 1
})
.registerComponent()
const child = componentSpace
.define()
.options({ dynamicSlots: true })
.data(() => ({
c: 123,
}))
.definition({
template: tmpl(''),
})
.registerComponent()
const parent = componentSpace
.define()
.options({ writeIdToDOM: false })
.usingComponents({ child, item: itemChild })
.data(() => ({
list: [1],
}))
.definition({
template: tmpl(`
- {{ item }}
`),
})
.registerComponent()
const parentElem = glassEasel.Component.createWithContext(
'root',
parent.general(),
testBackend,
).asInstanceOf(parent)!
const childElem = parentElem.getShadowRoot()!.getElementById('a')!.asInstanceOf(child)!
expect(domHtml(parentElem)).toBe('- 1
')
matchElementWithDom(parentElem)
expect(updateCount).toBe(1)
childElem.setData({ c: 456 })
expect(domHtml(parentElem)).toBe('- 1
')
matchElementWithDom(parentElem)
expect(updateCount).toBe(1)
parentElem.setData({ list: [2] })
expect(domHtml(parentElem)).toBe('- 2
')
matchElementWithDom(parentElem)
expect(updateCount).toBe(2)
childElem.setData({ c: 789 })
expect(domHtml(parentElem)).toBe('- 2
')
matchElementWithDom(parentElem)
expect(updateCount).toBe(2)
})
test('should support slot props with let-var', () => {
const child = componentSpace
.define()
.options({ dynamicSlots: true })
.data(() => ({
c: 123,
}))
.definition({
template: tmpl(''),
})
.registerComponent()
const parent = componentSpace
.define()
.usingComponents({ child })
.definition({
template: tmpl(`
{{d}}
`),
})
.registerComponent()
const parentElem = glassEasel.Component.createWithContext(
'root',
parent.general(),
testBackend,
).asInstanceOf(parent)!
const childElem = parentElem.getShadowRoot()!.childNodes[0]!.asInstanceOf(child)!
expect(domHtml(parentElem)).toBe('123')
matchElementWithDom(parentElem)
childElem.setData({ c: 456 })
expect(domHtml(parentElem)).toBe('456')
matchElementWithDom(parentElem)
})
test('should support slot props inside let-var and proper updates', () => {
let updateCount = 0
const itemChild = componentSpace
.define()
.template(tmpl(`{{ p }}`))
.property('p', Number)
.observer('p', () => {
updateCount += 1
})
.registerComponent()
const child = componentSpace
.define()
.options({ dynamicSlots: true })
.data(() => ({
c: 123,
}))
.definition({
template: tmpl(''),
})
.registerComponent()
const parent = componentSpace
.define()
.options({ writeIdToDOM: false })
.usingComponents({ child, item: itemChild })
.data(() => ({
list: [1],
}))
.definition({
template: tmpl(`
`),
})
.registerComponent()
const parentElem = glassEasel.Component.createWithContext(
'root',
parent.general(),
testBackend,
).asInstanceOf(parent)!
const childElem = parentElem.getShadowRoot()!.getElementById('a')!.asInstanceOf(child)!
expect(domHtml(parentElem)).toBe('- 1
')
matchElementWithDom(parentElem)
expect(updateCount).toBe(1)
childElem.setData({ c: 456 })
expect(domHtml(parentElem)).toBe('- 1
')
matchElementWithDom(parentElem)
expect(updateCount).toBe(1)
parentElem.setData({ list: [2] })
expect(domHtml(parentElem)).toBe('- 2
')
matchElementWithDom(parentElem)
expect(updateCount).toBe(2)
childElem.setData({ c: 789 })
expect(domHtml(parentElem)).toBe('- 2
')
matchElementWithDom(parentElem)
expect(updateCount).toBe(2)
})
test('should support nested slot props and proper updates', () => {
let updateCount = 0
const itemChild = componentSpace
.define()
.template(tmpl(`{{ p }}`))
.property('p', Number)
.observer('p', () => {
updateCount += 1
})
.registerComponent()
const child = componentSpace
.define()
.options({ dynamicSlots: true, virtualHost: true })
.data(() => ({
c: 123,
}))
.definition({
template: tmpl(''),
})
.registerComponent()
const parent = componentSpace
.define()
.options({ writeIdToDOM: false })
.usingComponents({ child, item: itemChild })
.definition({
template: tmpl(`
`),
})
.registerComponent()
const parentElem = glassEasel.Component.createWithContext(
'root',
parent.general(),
testBackend,
).asInstanceOf(parent)!
const outer = parentElem.getShadowRoot()!.getElementById('a')!.asInstanceOf(child)!
const inner = parentElem.getShadowRoot()!.getElementById('b')!.asInstanceOf(child)!
expect(domHtml(parentElem)).toBe('- 123
')
matchElementWithDom(parentElem)
expect(updateCount).toBe(1)
inner.setData({ c: 456 })
expect(domHtml(parentElem)).toBe('- 123
')
matchElementWithDom(parentElem)
expect(updateCount).toBe(1)
outer.setData({ c: 654 })
expect(domHtml(parentElem)).toBe('- 654
')
matchElementWithDom(parentElem)
expect(updateCount).toBe(2)
inner.setData({ c: 789 })
expect(domHtml(parentElem)).toBe('- 654
')
matchElementWithDom(parentElem)
expect(updateCount).toBe(2)
})
test('should support duplicate slot props', () => {
let ops: Array<[number, string]> = []
const itemComp = componentSpace
.define()
.property('s', String)
.observer('s', (s: string) => {
ops.push([0, s.toString()])
})
.lifetime('attached', function () {
ops.push([-1, this.data.s.toString()])
})
.lifetime('detached', function () {
ops.push([-2, this.data.s.toString()])
})
.lifetime('moved', function () {
ops.push([-3, this.data.s.toString()])
})
.registerComponent()
const child = componentSpace
.define()
.options({ dynamicSlots: true })
.data(() => ({
items: [] as number[],
}))
.definition({
template: tmpl(
'',
),
})
.registerComponent()
const parent = componentSpace
.define()
.usingComponents({ child, 'x-c': itemComp })
.data(() => ({
a: {
foo: 'foo',
},
bar: undefined as string | undefined,
}))
.definition({
template: tmpl(`
{{index}}:{{item}}
`),
})
.registerComponent()
const parentElem = glassEasel.Component.createWithContext('root', parent, testBackend)
glassEasel.Element.pretendAttached(parentElem)
const childElem = parentElem.getShadowRoot()!.childNodes[1]!.asInstanceOf(child)!
parentElem.replaceDataOnPath(['a', 'foo'], 'oops')
expect(domHtml(parentElem)).toBe('')
expect(ops).toEqual([
[0, 'foo'],
[-1, 'foo'],
])
matchElementWithDom(parentElem)
ops = []
childElem.setData({ items: [1, 2, 3] })
expect(domHtml(parentElem)).toBe(
'0:11:22:3',
)
expect(ops).toEqual([
[0, '0:1'],
[0, '1:2'],
[0, '2:3'],
[-1, '0:1'],
[-1, '1:2'],
[-1, '2:3'],
])
matchElementWithDom(parentElem)
ops = []
childElem.setData({ items: [4, 5, 6] })
expect(ops).toEqual([
[-2, '0:1'],
[-2, '1:2'],
[-2, '2:3'],
[0, '0:4'],
[0, '1:5'],
[0, '2:6'],
[-1, '0:4'],
[-1, '1:5'],
[-1, '2:6'],
])
expect(domHtml(parentElem)).toBe(
'0:41:52:6',
)
matchElementWithDom(parentElem)
ops = []
childElem.setData({ items: [1, 4, 6, 3, 5, 2] })
expect(ops).toEqual([
[0, '0:1'],
[-1, '0:1'],
[0, '1:4'],
[0, '3:3'],
[-1, '3:3'],
[0, '4:5'],
[0, '5:2'],
[-1, '5:2'],
])
expect(domHtml(parentElem)).toBe(
'0:11:42:63:34:55:2',
)
matchElementWithDom(parentElem)
ops = []
childElem.setData({ items: [2, 5, 1, 4, 6, 3] })
expect(ops).toEqual([
[0, '0:2'],
[0, '1:5'],
[0, '2:1'],
[0, '3:4'],
[0, '4:6'],
[0, '5:3'],
])
expect(domHtml(parentElem)).toBe(
'0:21:52:13:44:65:3',
)
matchElementWithDom(parentElem)
ops = []
childElem.setData({ items: [1, 4, 6, 3, 5, 2] })
expect(ops).toEqual([
[0, '0:1'],
[0, '1:4'],
[0, '2:6'],
[0, '3:3'],
[0, '4:5'],
[0, '5:2'],
])
expect(domHtml(parentElem)).toBe(
'0:11:42:63:34:55:2',
)
matchElementWithDom(parentElem)
ops = []
childElem.spliceArrayDataOnPath(['items'], 2, 1, [])
childElem.applyDataUpdates()
expect(ops).toEqual([
[-2, '2:6'],
[0, '2:3'],
[0, '3:5'],
[0, '4:2'],
])
expect(domHtml(parentElem)).toBe(
'0:11:42:33:54:2',
)
matchElementWithDom(parentElem)
ops = []
childElem.spliceArrayDataOnPath(['items'], 2, 0, [6])
childElem.applyDataUpdates()
expect(ops).toEqual([
[0, '2:6'],
[-1, '2:6'],
[0, '3:3'],
[0, '4:5'],
[0, '5:2'],
])
expect(domHtml(parentElem)).toBe(
'0:11:42:63:34:55:2',
)
matchElementWithDom(parentElem)
ops = []
childElem.setData({ items: [] })
expect(ops).toEqual([
[-2, '1:4'],
[-2, '4:5'],
[-2, '0:1'],
[-2, '3:3'],
[-2, '5:2'],
[-2, '2:6'],
])
expect(domHtml(parentElem)).toBe('')
matchElementWithDom(parentElem)
ops = []
parentElem.setData({ bar: '1' })
expect(ops).toEqual([[0, 'oops']])
})
test('should support different deep copy strategies', () => {
const none = componentSpace
.define()
.options({
dynamicSlots: true,
dataDeepCopy: glassEasel.DeepCopyKind.None,
propertyPassingDeepCopy: glassEasel.DeepCopyKind.None,
})
.definition({
template: tmpl(`
`),
})
.data(() => ({
sp: {
text: 123,
},
}))
.registerComponent()
const rec = componentSpace
.define()
.options({
dynamicSlots: true,
dataDeepCopy: glassEasel.DeepCopyKind.SimpleWithRecursion,
propertyPassingDeepCopy: glassEasel.DeepCopyKind.SimpleWithRecursion,
})
.definition({
template: tmpl(`
`),
})
.data(() => ({
sp: {
text: 'abc',
},
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
recObj: {} as any,
}))
.registerComponent()
const parent = componentSpace
.define()
.usingComponents({ none, rec })
.definition({
template: tmpl(`
{{p.text}}
{{p.text}}
`),
})
.registerComponent()
const parentElem = glassEasel.Component.createWithContext(
'root',
parent.general(),
testBackend,
)
const noneElem = parentElem.getShadowRoot()!.childNodes[0]!.asInstanceOf(none)!
const recElem = parentElem.getShadowRoot()!.childNodes[1]!.asInstanceOf(rec)!
expect(domHtml(parentElem)).toBe('123
abc
')
matchElementWithDom(parentElem)
noneElem.setData({ 'sp.text': 456 })
expect(domHtml(parentElem)).toBe('123
abc
')
matchElementWithDom(parentElem)
noneElem.setData({ sp: { text: 789 } })
expect(domHtml(parentElem)).toBe('789
abc
')
matchElementWithDom(parentElem)
const recObj = {} as { r: any }
recObj.r = recObj
recElem.setData({ sp: { text: 'def' }, recObj })
expect(domHtml(parentElem)).toBe('789
def
')
matchElementWithDom(parentElem)
})
test('should support placeholders', () => {
const placeholder = componentSpace
.define()
.options({ dynamicSlots: true })
.definition({
template: tmpl(`
`),
})
.data(() => ({
sp: {
text: 123,
},
}))
.registerComponent()
const parent = componentSpace
.define()
.usingComponents({
impl: 'impl',
placeholder,
})
.placeholders({
impl: 'placeholder',
})
.definition({
template: tmpl(`
{{text}}
`),
})
.registerComponent()
const parentElem = glassEasel.Component.createWithContext(
'root',
parent.general(),
testBackend,
)
expect(domHtml(parentElem)).toBe('')
matchElementWithDom(parentElem)
componentSpace
.define('impl')
.options({ dynamicSlots: true })
.definition({
template: tmpl(`
`),
})
.data(() => ({
sp: {
text: 123,
},
}))
.registerComponent()
expect(domHtml(parentElem)).toBe('456
')
matchElementWithDom(parentElem)
})
test('should support slot as slot content', () => {
const comp = componentSpace
.define()
.options({
multipleSlots: true,
})
.template(
tmpl(`
comp
`),
)
.registerComponent()
const child = componentSpace
.define()
.options({
multipleSlots: true,
})
.usingComponents({ comp })
.template(
tmpl(`
child
`),
)
.property('s', String)
.registerComponent()
const parent = componentSpace
.define()
.usingComponents({ child })
.template(
tmpl(`
content
`),
)
.data(() => ({
s: '',
}))
.registerComponent()
const parentElem = glassEasel.Component.createWithContext(
'root',
parent.general(),
testBackend,
)
expect(domHtml(parentElem)).toBe('comp
')
parentElem.setData({
s: 'comp-slot',
})
expect(domHtml(parentElem)).toBe(
'comp
content
',
)
parentElem.setData({
s: 'invalid',
})
expect(domHtml(parentElem)).toBe('comp
')
})
test('should support slot as slot content with virtual host and placeholder', () => {
const placeholder = componentSpace
.define()
.options({
multipleSlots: true,
virtualHost: true,
})
.template(
tmpl(`
placeholder
`),
)
.registerComponent()
const child = componentSpace
.define()
.options({
multipleSlots: true,
virtualHost: true,
})
.usingComponents({ comp: 'comp', placeholder })
.placeholders({ comp: 'placeholder' })
.template(
tmpl(`
child
`),
)
.registerComponent()
const parent = componentSpace
.define()
.usingComponents({ child })
.template(
tmpl(`
content
`),
)
.registerComponent()
const parentElem = glassEasel.Component.createWithContext(
'root',
parent.general(),
testBackend,
)
expect(domHtml(parentElem)).toBe('placeholder
content
')
componentSpace
.define('comp')
.options({
multipleSlots: true,
})
.template(
tmpl(`
comp
`),
)
.registerComponent()
expect(domHtml(parentElem)).toBe('comp
content
')
})
test('should support splice update', () => {
const li = componentSpace.define().property('s', String).registerComponent()
const listDef = componentSpace
.define()
.options({
dynamicSlots: true,
})
.property('list', Array)
.data(() => ({
displayList: [] as number[],
}))
.template(tmpl(''))
.registerComponent()
const listArray = new Array(100).fill(0).map((_, i) => i)
const parent = componentSpace
.define()
.usingComponents({ list: listDef, li })
.data(() => ({
listArray,
}))
.template(
tmpl(`
{{item}}
`),
)
.registerComponent()
const root = glassEasel.Component.createWithContext('root', parent, testBackend)
const list = root.getShadowRoot()!.childNodes[0] as glassEasel.GeneralComponent
expect(domHtml(root)).toEqual('
')
matchElementWithDom(root)
list.setData({
displayList: [0, 1, 2],
})
expect(domHtml(root)).toEqual('012
')
matchElementWithDom(root)
list.spliceArrayDataOnPath(['displayList'], undefined, undefined, [3, 4])
list.applyDataUpdates()
expect(list.data.displayList).toEqual([0, 1, 2, 3, 4])
expect(domHtml(root)).toEqual(
'01234
',
)
matchElementWithDom(root)
list.spliceArrayDataOnPath(['displayList'], 0, 2, [])
list.applyDataUpdates()
expect(list.data.displayList).toEqual([2, 3, 4])
expect(domHtml(root)).toEqual('234
')
matchElementWithDom(root)
})
})
})
describe('slot utilities', () => {
test('get slot element in shadow root', () => {
const single = componentSpace
.define()
.definition({
template: tmpl('
'),
})
.registerComponent()
const multi = componentSpace
.define()
.options({ multipleSlots: true })
.definition({
template: tmpl('
'),
})
.registerComponent()
const dynamic = componentSpace
.define()
.options({ dynamicSlots: true })
.definition({
template: tmpl('
'),
})
.registerComponent()
const parent = componentSpace
.define()
.usingComponents({ single, multi, dynamic })
.definition({
template: tmpl(`
S
M
D
`),
})
.registerComponent()
const parentElem = glassEasel.Component.createWithContext('root', parent, testBackend)
const singleElem = (parentElem.$.s as glassEasel.Element).asInstanceOf(single)!
const multiElem = (parentElem.$.m as glassEasel.Element).asInstanceOf(multi)!
const dynamicElem = (parentElem.$.d as glassEasel.Element).asInstanceOf(dynamic)!
const singleSlot = (singleElem.$.a as glassEasel.Element).childNodes[0]!.asElement()!
const multiSlot = (multiElem.$.a as glassEasel.Element).childNodes[0]!.asElement()!
const dynamicSlot = (dynamicElem.$.a as glassEasel.Element).childNodes[0]!.asElement()!
expect(parentElem.getShadowRoot()!.getSlotElementFromName('')).toBe(null)
expect(singleElem.getShadowRoot()!.getSlotElementFromName('')).toBe(singleSlot)
expect(multiElem.getShadowRoot()!.getSlotElementFromName('')).toBe(null)
expect(dynamicElem.getShadowRoot()!.getSlotElementFromName('')).toStrictEqual([])
expect(parentElem.getShadowRoot()!.getSlotElementFromName('a')).toBe(null)
expect(singleElem.getShadowRoot()!.getSlotElementFromName('a')).toBe(singleSlot)
expect(multiElem.getShadowRoot()!.getSlotElementFromName('a')).toBe(multiSlot)
expect(dynamicElem.getShadowRoot()!.getSlotElementFromName('a')).toStrictEqual([dynamicSlot])
parentElem.getShadowRoot()!.forEachSlot(() => {
throw new Error()
})
singleElem.getShadowRoot()!.forEachSlot((slot) => {
expect(slot).toBe(singleSlot)
})
multiElem.getShadowRoot()!.forEachSlot((slot) => {
expect(slot).toBe(multiSlot)
})
dynamicElem.getShadowRoot()!.forEachSlot((slot) => {
expect(slot).toBe(dynamicSlot)
})
const checkSlotContentMethods = (
sr: glassEasel.ShadowRoot,
slot: glassEasel.Element,
expectedContent: glassEasel.Node[],
expectedNode: glassEasel.Node[],
) => {
const contentArr = sr.getSlotContentArray(slot)!
expect(contentArr?.length).toBe(expectedContent.length)
for (let i = 0; i < contentArr?.length; i += 1) {
expect(contentArr[i]).toBe(expectedContent[i])
}
let cur = 0
sr.forEachNodeInSlot((node, s) => {
if (!s) {
if (node.asTextNode()) {
expect(node.asTextNode()!.textContent).toBe('M')
expect(node.asVirtualNode()).toBe(null)
} else {
expect(node.asVirtualNode()).toBeTruthy()
}
return
}
expect(s).toBe(slot)
expect(node).toBe(expectedContent[cur])
cur += 1
})
expect(cur).toBe(expectedContent.length)
cur = 0
sr.forEachNodeInSpecifiedSlot(slot, (node) => {
expect(node).toBe(expectedContent[cur])
cur += 1
})
expect(cur).toBe(expectedContent.length)
cur = 0
sr.forEachSlotContentInSlot((node, s) => {
if (!s) {
if (node.asTextNode()) {
expect(node.asTextNode()!.textContent).toBe('M')
expect(node.asVirtualNode()).toBe(null)
} else {
expect(node.asVirtualNode()).toBeTruthy()
}
return
}
expect(s).toBe(slot)
expect(node).toBe(expectedNode[cur])
cur += 1
})
expect(cur).toBe(expectedNode.length)
cur = 0
sr.forEachSlotContentInSpecifiedSlot(slot, (node) => {
expect(node).toBe(expectedNode[cur])
cur += 1
})
expect(cur).toBe(expectedNode.length)
}
checkSlotContentMethods(
singleElem.getShadowRoot()!,
singleSlot,
[
singleElem.childNodes[0]!,
(singleElem.childNodes[0] as glassEasel.Element).childNodes[0]!,
singleElem.childNodes[1]!,
],
[
(singleElem.childNodes[0] as glassEasel.Element).childNodes[0]!,
singleElem.childNodes[1]!,
],
)
checkSlotContentMethods(
multiElem.getShadowRoot()!,
multiSlot,
[(multiElem.childNodes[0] as glassEasel.Element).childNodes[0]!],
[(multiElem.childNodes[0] as glassEasel.Element).childNodes[0]!],
)
checkSlotContentMethods(
dynamicElem.getShadowRoot()!,
dynamicSlot,
[
dynamicElem.childNodes[0]!,
(dynamicElem.childNodes[0] as glassEasel.Element).childNodes[0]!,
dynamicElem.childNodes[1]!,
],
[
(dynamicElem.childNodes[0] as glassEasel.Element).childNodes[0]!,
dynamicElem.childNodes[1]!,
],
)
})
test('fallback to possible event bindings', () => {
const callOrder: number[] = []
const single = componentSpace
.define()
.definition({
template: tmpl('
'),
methods: {
fAbc() {
callOrder.push(1)
},
},
})
.registerComponent()
const parent = componentSpace
.define()
.usingComponents({ single })
.definition({
template: tmpl(`
`),
})
.registerComponent()
const parentElem = execWithWarn(1, () =>
glassEasel.Component.createWithContext('root', parent, testBackend),
)
const a = parentElem.getShadowRoot()!.getElementById('a')!
a.triggerEvent('aBc', null, { bubbles: true, composed: true })
expect(callOrder).toStrictEqual([1])
})
test('fallback to possible event bindings (template script)', () => {
const single = componentSpace
.define()
.definition({
template: tmpl(`
exports.fA = function (ev) {
ev.target._test = 123
}
`),
})
.registerComponent()
const parent = componentSpace
.define()
.usingComponents({ single })
.definition({
template: tmpl(`
`),
})
.registerComponent()
const parentElem = glassEasel.Component.createWithContext('root', parent, testBackend)
const a = parentElem.getShadowRoot()!.getElementById('a')!
a.triggerEvent('aBc', null, { bubbles: true, composed: true })
expect((a.getComposedParent() as unknown as { _test: number })._test).toStrictEqual(123)
})
})
}
describe('slot (DOM backend)', () => testCases(domBackend))
describe('slot (shadow backend)', () => testCases(shadowBackend))
describe('slot (composed backend)', () => testCases(composedBackend))