import {
tmpl,
multiTmpl,
domBackend,
execWithWarn,
composedBackend,
shadowBackend,
} 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) => {
test('basic tree building', () => {
const def = glassEasel
.registerElement({
template: tmpl(`
Hello world!
`),
})
.general()
const elem = glassEasel.Component.createWithContext('root', def, testBackend)
expect(domHtml(elem)).toBe('Hello world!
')
matchElementWithDom(elem)
})
test('comments inside template', () => {
const def = glassEasel
.registerElement({
template: tmpl(`
Hello world!
`),
})
.general()
const elem = glassEasel.Component.createWithContext('root', def, testBackend)
expect(domHtml(elem)).toBe('Hello world!')
matchElementWithDom(elem)
})
test('meta tags inside template', () => {
const def = glassEasel
.registerElement({
template: tmpl(`
Hello world!
`),
})
.general()
const elem = glassEasel.Component.createWithContext('root', def, testBackend)
expect(domHtml(elem)).toBe('Hello world!')
matchElementWithDom(elem)
})
test('basic data binding', () => {
const def = glassEasel
.registerElement({
template: tmpl(`
{{c}}
`),
data: {
a: 123,
c: 'abc',
},
})
.general()
const elem = glassEasel.Component.createWithContext('root', def, testBackend).general()
expect(domHtml(elem)).toBe('abc
')
matchElementWithDom(elem)
elem.setData({
a: true,
c: false,
})
expect(domHtml(elem)).toBe('false
')
matchElementWithDom(elem)
})
test('if blocks', () => {
const def = glassEasel
.registerElement({
template: tmpl(`
a
b
c
d
e
f
`),
data: {
cond1: false,
cond2: '',
},
})
.general()
const elem = glassEasel.Component.createWithContext('root', def, testBackend)
expect(domHtml(elem)).toBe('c
f
')
matchElementWithDom(elem)
elem.setData({
cond1: true,
})
expect(domHtml(elem)).toBe('a
c
d
')
matchElementWithDom(elem)
elem.setData({
cond2: 1,
})
expect(domHtml(elem)).toBe('a
b
d
')
matchElementWithDom(elem)
elem.setData({
cond1: null,
})
expect(domHtml(elem)).toBe('b
e
')
matchElementWithDom(elem)
})
test('if blocks in template-is', () => {
const def = glassEasel
.registerElement({
template: tmpl(`
{{ aa }}
{{ aa }}
`),
data: {
cond: false,
b: 123,
c: 456,
},
})
.general()
const elem = glassEasel.Component.createWithContext('root', def, testBackend)
expect(domHtml(elem)).toBe('456
')
matchElementWithDom(elem)
elem.setData({
cond: true,
})
expect(domHtml(elem)).toBe('123
')
matchElementWithDom(elem)
elem.setData({
cond: false,
c: 789,
})
expect(domHtml(elem)).toBe('789
')
matchElementWithDom(elem)
})
test('if blocks in include', () => {
const def = glassEasel
.registerElement({
template: multiTmpl({
'': '',
a: '{{a}}
',
}),
data: {
cond: false,
a: 123,
},
})
.general()
const elem = glassEasel.Component.createWithContext('root', def, testBackend)
expect(domHtml(elem)).toBe('123
')
matchElementWithDom(elem)
elem.setData({
cond: true,
})
expect(domHtml(elem)).toBe('')
matchElementWithDom(elem)
elem.setData({
cond: false,
a: 456,
})
expect(domHtml(elem)).toBe('456
')
matchElementWithDom(elem)
})
test('if blocks in slot', () => {
const childComp = glassEasel
.registerElement({
options: {
multipleSlots: true,
},
template: tmpl(`
`),
data: {
cond1: true,
cond2: false,
},
})
.general()
const def = glassEasel
.registerElement({
using: {
child: childComp.general(),
},
template: tmpl(`
A
C
`),
data: {
cond: false,
b: 123,
c: 456,
},
})
.general()
const elem = glassEasel.Component.createWithContext('root', def, testBackend)
const child = elem.getShadowRoot()!.getElementById('c')!.asInstanceOf(childComp)!
expect(domHtml(elem)).toBe('A
')
matchElementWithDom(elem)
child.setData({
cond1: false,
cond2: true,
})
expect(domHtml(elem)).toBe('C
')
matchElementWithDom(elem)
child.setData({
cond1: false,
cond2: false,
})
expect(domHtml(elem)).toBe('')
matchElementWithDom(elem)
})
test('for blocks without key', () => {
const ops: Array<[number, string]> = []
const itemComp = glassEasel
.registerElement({
properties: { s: { type: String } },
lifetimes: {
attached() {
ops.push([-1, this.data.s.toString()])
},
detached() {
ops.push([-2, this.data.s.toString()])
},
},
})
.general()
const def = glassEasel
.registerElement({
using: {
'x-c': itemComp,
},
template: tmpl(`
{{k}}
`),
data: {
list: [10, 20],
},
})
.general()
const elem = glassEasel.Component.createWithContext('root', def, testBackend)
glassEasel.Element.pretendAttached(elem)
expect(domHtml(elem)).toBe('0
1
')
matchElementWithDom(elem)
expect(ops).toEqual([
[-1, '10'],
[-1, '20'],
])
ops.length = 0
elem.setData({
list: [20, 10],
})
expect(domHtml(elem)).toBe('0
1
')
matchElementWithDom(elem)
expect(ops).toEqual([])
ops.length = 0
elem.setData({
list: [30, 40, 50, 60],
})
expect(domHtml(elem)).toBe(
'0
1
2
3
',
)
matchElementWithDom(elem)
expect(ops).toEqual([
[-1, '50'],
[-1, '60'],
])
ops.length = 0
elem.setData({
list: [50],
})
expect(domHtml(elem)).toBe('0
')
matchElementWithDom(elem)
expect(ops).toEqual([
[-2, '40'],
[-2, '50'],
[-2, '60'],
])
})
test('for blocks with key', () => {
const ops: Array<[number, string]> = []
const itemComp = glassEasel
.registerElement({
properties: { s: String },
lifetimes: {
attached() {
ops.push([-1, this.data.s.toString()])
},
detached() {
ops.push([-2, this.data.s.toString()])
},
moved() {
ops.push([-3, this.data.s.toString()])
},
},
})
.general()
const def = glassEasel
.registerElement({
using: {
'x-c': itemComp,
},
template: tmpl(`
{{item.v}}
`),
data: {
list: [
{ k: 'a', v: 10 },
{ k: 'b', v: 20 },
],
},
})
.general()
const elem = glassEasel.Component.createWithContext('root', def, testBackend)
const listBlock = elem.getShadowRoot()!.childNodes[0] as glassEasel.VirtualNode
const checkIndex = () => {
for (let i = 0; i < listBlock.childNodes.length; i += 1) {
const itemBlock = listBlock.childNodes[i] as glassEasel.Element
expect((itemBlock.childNodes[0] as glassEasel.Element).dataset.i).toBe(i)
}
}
glassEasel.Element.pretendAttached(elem)
expect(domHtml(elem)).toBe('1020')
matchElementWithDom(elem)
checkIndex()
expect(ops).toEqual([
[-1, 'a:10'],
[-1, 'b:20'],
])
ops.length = 0
elem.setData({
list: [
{ k: 'c', v: 30 },
{ k: 'a', v: 40 },
{ k: 'd', v: 50 },
{ k: 'b', v: 60 },
{ k: 'e', v: 70 },
],
})
expect(domHtml(elem)).toBe('3040506070')
matchElementWithDom(elem)
checkIndex()
expect(ops).toEqual([
[-1, 'c:30'],
[-1, 'd:50'],
[-1, 'e:70'],
])
ops.length = 0
elem.setData({
list: [
{ k: 'c', v: 30 },
{ k: 'b', v: 60 },
{ k: 'a', v: 40 },
{ k: 'd', v: 50 },
{ k: 'e', v: 70 },
],
})
expect(domHtml(elem)).toBe('3060405070')
matchElementWithDom(elem)
checkIndex()
expect(ops).toEqual([[-3, 'b:60']])
ops.length = 0
elem.setData({
list: [
{ k: 'c', v: 30 },
{ k: 'b', v: 40 },
{ k: 'd', v: 50 },
{ k: 'e', v: 60 },
{ k: 'a', v: 70 },
],
})
expect(domHtml(elem)).toBe('3040506070')
matchElementWithDom(elem)
checkIndex()
expect(ops).toEqual([[-3, 'a:40']])
ops.length = 0
elem.setData({
list: [
{ k: 'c', v: 30 },
{ k: 'b', v: 40 },
{ k: 'd', v: 50 },
{ k: 'e', v: 60 },
{ k: 'a', v: 70 },
],
})
expect(domHtml(elem)).toBe('3040506070')
matchElementWithDom(elem)
checkIndex()
expect(ops).toEqual([])
ops.length = 0
elem.setData({
list: [
{ k: 'b', v: 80 },
{ k: 'e', v: 90 },
],
})
expect(domHtml(elem)).toBe('8090')
matchElementWithDom(elem)
checkIndex()
expect(ops).toEqual([
[-2, 'c:30'],
[-2, 'd:50'],
[-2, 'a:70'],
])
})
test('nested for blocks', () => {
const def = glassEasel
.registerElement({
template: tmpl(`
{{item}}
`),
data: {
list: [
{ a: '1', b: '2' },
{ c: '3', d: '4' },
],
},
})
.general()
const elem = glassEasel.Component.createWithContext('root', def, testBackend)
expect(
(elem.getShadowRoot()!.getElementById('a0')!.childNodes[0] as glassEasel.TextNode)
.textContent,
).toBe('1')
expect(
(elem.getShadowRoot()!.getElementById('b0')!.childNodes[0] as glassEasel.TextNode)
.textContent,
).toBe('2')
expect(
(elem.getShadowRoot()!.getElementById('c1')!.childNodes[0] as glassEasel.TextNode)
.textContent,
).toBe('3')
expect(
(elem.getShadowRoot()!.getElementById('d1')!.childNodes[0] as glassEasel.TextNode)
.textContent,
).toBe('4')
elem.setData({
list: [{ a: 'A', b: 'B' }],
})
expect(
(elem.getShadowRoot()!.getElementById('a0')!.childNodes[0] as glassEasel.TextNode)
.textContent,
).toBe('A')
expect(
(elem.getShadowRoot()!.getElementById('b0')!.childNodes[0] as glassEasel.TextNode)
.textContent,
).toBe('B')
elem.setData({
list: [{ e: 'A', f: 'B' }],
})
expect(
(elem.getShadowRoot()!.getElementById('e0')!.childNodes[0] as glassEasel.TextNode)
.textContent,
).toBe('A')
expect(
(elem.getShadowRoot()!.getElementById('f0')!.childNodes[0] as glassEasel.TextNode)
.textContent,
).toBe('B')
})
test('for blocks expanding object without key', () => {
const ops: Array<[number, string]> = []
const itemComp = glassEasel
.registerElement({
properties: { s: { type: String } },
lifetimes: {
attached() {
ops.push([-1, this.data.s.toString()])
},
detached() {
ops.push([-2, this.data.s.toString()])
},
},
})
.general()
const def = glassEasel
.registerElement({
using: {
'x-c': itemComp,
},
template: tmpl(`
{{k}}
`),
data: {
list: { a: 10, b: 20 },
},
})
.general()
const elem = glassEasel.Component.createWithContext('root', def, testBackend)
glassEasel.Element.pretendAttached(elem)
expect(domHtml(elem)).toBe('a
b
')
matchElementWithDom(elem)
expect(ops).toEqual([
[-1, 'a:10'],
[-1, 'b:20'],
])
ops.length = 0
elem.setData({
list: { a: 20, b: 10 },
})
expect(domHtml(elem)).toBe('a
b
')
matchElementWithDom(elem)
expect(ops).toEqual([])
ops.length = 0
elem.setData({
list: {
a: 30,
b: 40,
c: 50,
d: 60,
},
})
expect(domHtml(elem)).toBe(
'a
b
c
d
',
)
matchElementWithDom(elem)
expect(ops).toEqual([
[-1, 'c:50'],
[-1, 'd:60'],
])
ops.length = 0
elem.setData({
list: { c: 50 },
})
expect(domHtml(elem)).toBe('c
')
matchElementWithDom(elem)
expect(ops).toEqual([
[-2, 'b:40'],
[-2, 'c:50'],
[-2, 'd:60'],
])
})
test('for blocks expanding object with key', () => {
const ops: Array<[number, string]> = []
const itemComp = glassEasel.registerElement({
properties: { s: String },
lifetimes: {
attached() {
ops.push([-1, this.data.s.toString()])
},
detached() {
ops.push([-2, this.data.s.toString()])
},
moved() {
ops.push([-3, this.data.s.toString()])
},
},
})
const def = glassEasel.registerElement({
using: {
'x-c': itemComp.general(),
},
template: tmpl(`
{{item.v}}
`),
data: {
list: { f1: { k: 'a', v: 10 }, f2: { k: 'b', v: 20 } } as Record<
string,
{ k: string; v: number }
>,
},
})
const elem = glassEasel.Component.createWithContext('root', def, testBackend)
const listBlock = elem.getShadowRoot()!.childNodes[0] as glassEasel.VirtualNode
const checkIndex = () => {
const keys = Object.keys(elem.data.list)
for (let i = 0; i < listBlock.childNodes.length; i += 1) {
const itemBlock = listBlock.childNodes[i] as glassEasel.Element
expect((itemBlock.childNodes[0] as glassEasel.Element).dataset.i).toBe(keys[i])
}
}
glassEasel.Element.pretendAttached(elem)
expect(domHtml(elem)).toBe('1020')
matchElementWithDom(elem)
checkIndex()
expect(ops).toEqual([
[-1, 'a:10'],
[-1, 'b:20'],
])
ops.length = 0
elem.setData({
list: {
f3: { k: 'c', v: 30 },
f4: { k: 'd', v: 40 },
f1: { k: 'a', v: 50 },
f5: { k: 'e', v: 60 },
f6: { k: 'f', v: 70 },
f2: { k: 'b', v: 80 },
},
})
expect(domHtml(elem)).toBe(
'304050607080',
)
matchElementWithDom(elem)
checkIndex()
expect(ops).toEqual([
[-1, 'c:30'],
[-1, 'd:40'],
[-1, 'e:60'],
[-1, 'f:70'],
])
ops.length = 0
elem.setData({
list: {
f1: { k: 'a', v: 50 },
f5: { k: 'e', v: 60 },
f6: { k: 'f', v: 70 },
f2: { k: 'b', v: 80 },
f3: { k: 'g', v: 30 },
f4: { k: 'h', v: 40 },
},
})
expect(domHtml(elem)).toBe(
'506070803040',
)
matchElementWithDom(elem)
checkIndex()
expect(ops).toEqual([
[-2, 'c:30'],
[-2, 'd:40'],
[-1, 'g:30'],
[-1, 'h:40'],
])
ops.length = 0
elem.setData({
list: {
f5: { k: 'e', v: 10 },
f3: { k: 'g', v: 20 },
f6: { k: 'f', v: 30 },
f1: { k: 'a', v: 40 },
f2: { k: 'b', v: 50 },
},
})
expect(domHtml(elem)).toBe('1020304050')
matchElementWithDom(elem)
checkIndex()
expect(ops).toEqual([
[-3, 'g:30'],
[-3, 'a:50'],
[-2, 'h:40'],
])
ops.length = 0
elem.setData({
list: {
f1: { k: 'a', v: 10 },
f2: { k: 'c', v: 20 },
f3: { k: 'e', v: 30 },
f4: { k: 'f', v: 40 },
f5: { k: 'b', v: 50 },
f6: { k: 'g', v: 60 },
f7: { k: 'h', v: 70 },
},
})
expect(domHtml(elem)).toBe(
'10203040506070',
)
matchElementWithDom(elem)
checkIndex()
expect(ops).toEqual([
[-3, 'a:40'],
[-1, 'c:20'],
[-3, 'g:20'],
[-1, 'h:70'],
])
ops.length = 0
elem.setData({
list: {
f7: { k: 'h', v: 60 },
f3: { k: 'e', v: 50 },
f4: { k: 'f', v: 40 },
f2: { k: 'c', v: 30 },
f5: { k: 'b', v: 20 },
f1: { k: 'a', v: 10 },
},
})
expect(domHtml(elem)).toBe(
'605040302010',
)
matchElementWithDom(elem)
checkIndex()
expect(ops).toEqual([
[-3, 'h:70'],
[-3, 'c:20'],
[-2, 'g:60'],
[-3, 'a:10'],
])
ops.length = 0
elem.setData({
list: {
f8: { k: 'f', v: 80 },
f7: { k: 'e', v: 70 },
f6: { k: 'd', v: 60 },
f5: { k: 'a', v: 50 },
f4: { k: 'c', v: 40 },
f3: { k: 'b', v: 30 },
f2: { k: 'g', v: 20 },
f1: { k: 'h', v: 10 },
},
})
expect(domHtml(elem)).toBe(
'8070605040302010',
)
matchElementWithDom(elem)
checkIndex()
expect(ops).toEqual([
[-3, 'f:40'],
[-1, 'd:60'],
[-3, 'a:10'],
[-1, 'g:20'],
[-3, 'h:60'],
])
})
test('for blocks expending string', () => {
const ops: Array<[number, string]> = []
const itemComp = glassEasel
.registerElement({
properties: { s: String },
lifetimes: {
attached() {
ops.push([-1, this.data.s.toString()])
},
detached() {
ops.push([-2, this.data.s.toString()])
},
moved() {
ops.push([-3, this.data.s.toString()])
},
},
})
.general()
const def = glassEasel
.registerElement({
using: {
'x-c': itemComp,
},
template: tmpl(`
{{item}}
`),
data: {
s: 'x',
},
})
.general()
const elem = execWithWarn(1, () =>
glassEasel.Component.createWithContext('root', def, testBackend),
)
glassEasel.Element.pretendAttached(elem)
expect(domHtml(elem)).toBe('x')
matchElementWithDom(elem)
expect(ops).toEqual([[-1, 'x']])
ops.length = 0
execWithWarn(1, () => {
elem.setData({
s: 'abc',
})
})
expect(domHtml(elem)).toBe('abc')
matchElementWithDom(elem)
expect(ops).toEqual([
[-2, 'x'],
[-1, 'a'],
[-1, 'b'],
[-1, 'c'],
])
ops.length = 0
execWithWarn(1, () => {
elem.setData({
s: 'cab',
})
})
expect(domHtml(elem)).toBe('cab')
matchElementWithDom(elem)
ops.length = 0
execWithWarn(1, () => {
elem.setData({
s: '',
})
})
expect(domHtml(elem)).toBe('')
matchElementWithDom(elem)
expect(ops).toEqual([
[-2, 'c'],
[-2, 'a'],
[-2, 'b'],
])
})
test('for blocks expending number', () => {
const def = glassEasel
.registerElement({
template: tmpl(`
{{item}}
`),
data: { n: 3 },
})
.general()
const elem = execWithWarn(1, () =>
glassEasel.Component.createWithContext('root', def, testBackend),
)
glassEasel.Element.pretendAttached(elem)
expect(domHtml(elem)).toBe('012')
matchElementWithDom(elem)
execWithWarn(1, () => elem.setData({ n: 2 }))
expect(domHtml(elem)).toBe('01')
matchElementWithDom(elem)
})
test('slot inside for blocks', () => {
const x = glassEasel.registerElement({})
const def = glassEasel.registerElement({
using: { x },
template: tmpl(`
{{item}}
`),
data: { n: [0, 1, 2] },
})
const elem = glassEasel.Component.createWithContext('root', def, testBackend)
glassEasel.Element.pretendAttached(elem)
expect(domHtml(elem)).toBe('012')
matchElementWithDom(elem)
elem.spliceArrayDataOnPath(['n'], 3, 0, [3, 4])
elem.applyDataUpdates()
expect(domHtml(elem)).toBe('01234')
matchElementWithDom(elem)
elem.spliceArrayDataOnPath(['n'], 0, 0, [5, 6])
elem.applyDataUpdates()
expect(domHtml(elem)).toBe('5601234')
matchElementWithDom(elem)
elem.spliceArrayDataOnPath(['n'], 2, 3, [7, 8])
elem.applyDataUpdates()
expect(domHtml(elem)).toBe('567834')
matchElementWithDom(elem)
elem.spliceArrayDataOnPath(['n'], 0, 3, [])
elem.applyDataUpdates()
expect(domHtml(elem)).toBe('834')
matchElementWithDom(elem)
elem.spliceArrayDataOnPath(['n'], 1, 2, [])
elem.applyDataUpdates()
expect(domHtml(elem)).toBe('8')
matchElementWithDom(elem)
})
test('slot inside for blocks', () => {
const x = glassEasel.registerElement({})
const def = glassEasel.registerElement({
using: { x },
template: tmpl(`
{{item}}
`),
data: { n: [0, 1, 2] },
})
const elem = glassEasel.Component.createWithContext('root', def, testBackend)
glassEasel.Element.pretendAttached(elem)
expect(domHtml(elem)).toBe('012')
matchElementWithDom(elem)
elem.spliceArrayDataOnPath(['n'], 3, 0, [3, 4])
elem.applyDataUpdates()
expect(domHtml(elem)).toBe('01234')
matchElementWithDom(elem)
elem.spliceArrayDataOnPath(['n'], 0, 0, [5, 6])
elem.applyDataUpdates()
expect(domHtml(elem)).toBe('5601234')
matchElementWithDom(elem)
elem.spliceArrayDataOnPath(['n'], 2, 3, [7, 8])
elem.applyDataUpdates()
expect(domHtml(elem)).toBe('567834')
matchElementWithDom(elem)
elem.spliceArrayDataOnPath(['n'], 0, 3, [])
elem.applyDataUpdates()
expect(domHtml(elem)).toBe('834')
matchElementWithDom(elem)
})
test('template include', () => {
const def = glassEasel
.registerElement({
template: multiTmpl({
'': '{{a}}',
a: '{{a}}{{b}}
',
}),
data: {
a: 123,
b: 'abc',
},
})
.general()
const elem = glassEasel.Component.createWithContext('root', def, testBackend)
expect(domHtml(elem)).toBe('123abc
123')
matchElementWithDom(elem)
elem.setData({ a: 456 })
expect(domHtml(elem)).toBe('456abc
456')
matchElementWithDom(elem)
elem.setData({ b: 'def' })
expect(domHtml(elem)).toBe('456def
456')
matchElementWithDom(elem)
})
test('template undefined include', () => {
const def = glassEasel
.registerElement({
template: multiTmpl({
'': '
',
}),
data: {
a: 123,
},
})
.general()
const elem = glassEasel.Component.createWithContext('root', def, testBackend)
expect(domHtml(elem)).toBe('')
matchElementWithDom(elem)
})
test('template-name data', () => {
const def = glassEasel
.registerElement({
template: multiTmpl({
child: `
{{index}}:{{item.data}}
`,
'': `
`,
}),
data: {
arr: [
{ shown: true, data: 123 },
{ shown: false, data: 456 },
{ shown: true, data: 789 },
],
},
})
.general()
const elem = glassEasel.Component.createWithContext('root', def, testBackend)
expect(domHtml(elem)).toBe('0:1232:789
')
matchElementWithDom(elem)
elem.setData({ 'arr[0].shown': false })
expect(domHtml(elem)).toBe('2:789
')
matchElementWithDom(elem)
elem.setData({
arr: [
{ shown: false, data: 789 },
{ shown: true, data: 456 },
],
})
expect(domHtml(elem)).toBe('1:456
')
matchElementWithDom(elem)
})
test('template-name data shortcut', () => {
const def = glassEasel
.registerElement({
template: multiTmpl({
child: `
{{ obj.a }}
{{ a }}
`,
'': `
`,
}),
data: {
obj: { a: 123 },
},
})
.general()
const elem = glassEasel.Component.createWithContext('root', def, testBackend)
expect(domHtml(elem)).toBe('123
123
')
matchElementWithDom(elem)
elem.setData({ obj: { a: 456 } })
expect(domHtml(elem)).toBe('456
456
')
matchElementWithDom(elem)
elem.setData({ 'obj.a': 789 })
expect(domHtml(elem)).toBe('789
789
')
matchElementWithDom(elem)
})
test('template-name data cascaded passing', () => {
const def = glassEasel
.registerElement({
template: tmpl(`
{{ a }}
`),
data: {
obj: { a: 123 },
},
})
.general()
const elem = glassEasel.Component.createWithContext('root', def, testBackend)
expect(domHtml(elem)).toBe('123
')
matchElementWithDom(elem)
elem.setData({ obj: { a: 456 } })
expect(domHtml(elem)).toBe('456
')
matchElementWithDom(elem)
elem.setData({ 'obj.a': 789 })
expect(domHtml(elem)).toBe('789
')
matchElementWithDom(elem)
})
test('static template-is', () => {
const def = glassEasel
.registerElement({
template: tmpl(`
{{a + b.c + d}}
`),
data: {
b: {
a: 300,
},
},
})
.general()
const elem = glassEasel.Component.createWithContext('root', def, testBackend)
expect(domHtml(elem)).toBe('345
')
matchElementWithDom(elem)
elem.setData({ b: { a: 200, d: 7 } })
expect(domHtml(elem)).toBe('247
')
matchElementWithDom(elem)
elem.setData({ 'b.a': 100 })
expect(domHtml(elem)).toBe('147
')
matchElementWithDom(elem)
elem.setData({ 'b.hidden': true })
expect(domHtml(elem)).toBe('')
matchElementWithDom(elem)
})
test('dynamic template-is', () => {
const def = glassEasel
.registerElement({
template: tmpl(`
{{a}}
{{b}}
`),
data: {
childType: '',
a: 123,
b: 456,
},
properties: {},
methods: {},
})
.general()
const elem = glassEasel.Component.createWithContext('root', def, testBackend)
expect(domHtml(elem)).toBe('')
matchElementWithDom(elem)
elem.setData({ childType: 'A' })
expect(domHtml(elem)).toBe('123
')
matchElementWithDom(elem)
elem.setData({ childType: 'B' })
expect(domHtml(elem)).toBe('456
')
matchElementWithDom(elem)
elem.setData({ a: 0 })
expect(domHtml(elem)).toBe('456
')
matchElementWithDom(elem)
elem.setData({ b: 789 })
expect(domHtml(elem)).toBe('789
')
matchElementWithDom(elem)
})
test('template-is inside for block', () => {
const def = glassEasel
.registerElement({
template: tmpl(`
{{index}}:{{a}}
{{index}}:{{b}}
`),
data: {
list: [],
a: 123,
b: 456,
},
properties: {},
methods: {},
})
.general()
const elem = glassEasel.Component.createWithContext('root', def, testBackend)
expect(domHtml(elem)).toBe('')
matchElementWithDom(elem)
elem.setData({ list: [1, 2] })
expect(domHtml(elem)).toBe('0:1231:456
')
matchElementWithDom(elem)
elem.setData({ list: [2, 1, 1] })
expect(domHtml(elem)).toBe('0:4561:1232:123
')
matchElementWithDom(elem)
elem.setData({ a: 0 })
expect(domHtml(elem)).toBe('0:4561:02:0
')
matchElementWithDom(elem)
elem.setData({ b: 789 })
expect(domHtml(elem)).toBe('0:7891:02:0
')
matchElementWithDom(elem)
})
test('undefined template-is', () => {
const def = glassEasel
.registerElement({
template: tmpl(`
`),
data: {
b: 123,
},
})
.general()
const elem = glassEasel.Component.createWithContext('root', def, testBackend)
expect(domHtml(elem)).toBe('')
matchElementWithDom(elem)
})
test('undefined template-data', () => {
const def = glassEasel
.registerElement({
template: tmpl(`
{{ a }}
`),
data: {
a: 123,
b: 789,
},
})
.general()
const elem = glassEasel.Component.createWithContext('root', def, testBackend)
expect(domHtml(elem)).toBe('
')
matchElementWithDom(elem)
elem.setData({ a: 456 })
expect(domHtml(elem)).toBe('
')
matchElementWithDom(elem)
})
test('cascaded template', () => {
const def = glassEasel
.registerElement({
template: multiTmpl({
'': `
`,
'template/s': `
key: {{ c.i }}
`,
'template/i': `
value: a
value: b
`,
}),
data: {
c: {
i: 'A',
},
},
})
.general()
const elem = glassEasel.Component.createWithContext('root', def, testBackend).asInstanceOf(def)!
expect(domHtml(elem)).toBe('key: A
value: a
')
matchElementWithDom(elem)
elem.setData({ 'c.i': 'B' })
expect(domHtml(elem)).toBe('key: B
value: b
')
matchElementWithDom(elem)
})
test('custom scripts', () => {
const def = glassEasel
.registerElement({
template: multiTmpl({
'': `
{{ a.test(1, 3) + 2 }}
`,
'scripts/abc.wxs': `
module.exports = require('def')
`,
'scripts/def.wxs': `
exports.test = function (a, b) {
return a + b
}
`,
}),
data: {},
})
.general()
const elem = glassEasel.Component.createWithContext('root', def, testBackend).asInstanceOf(def)!
expect(domHtml(elem)).toBe('6
')
matchElementWithDom(elem)
})
test('custom inline scripts', () => {
const def = glassEasel
.registerElement({
template: multiTmpl({
'': `
exports.test = require("./scripts/def").test
{{ a.test(1, 3) + 2 }}
`,
'scripts/def.wxs': `
exports.test = function (a, b) {
return a + b
}
`,
}),
data: {},
})
.general()
const elem = glassEasel.Component.createWithContext('root', def, testBackend).asInstanceOf(def)!
expect(domHtml(elem)).toBe('6
')
matchElementWithDom(elem)
})
test('custom scripts across files', () => {
const def = glassEasel
.registerElement({
template: multiTmpl({
'': `
`,
a: `
exports.test = function (a, b) { return a - b }
{{ a.test(1, 2) }} {{ b.test(1, 2) }}
`,
'scripts/def.wxs': `
exports.test = function (a, b) {
return a + b
}
`,
}),
data: {},
})
.general()
const elem = glassEasel.Component.createWithContext('root', def, testBackend).asInstanceOf(def)!
expect(domHtml(elem)).toBe('-1 3
')
matchElementWithDom(elem)
})
test('custom scripts with template', () => {
const def = glassEasel
.registerElement({
template: tmpl(`
module.exports={ foo: 'foo' }
{{a.foo}}
{{a.foo}}
`),
data: {},
})
.general()
const elem = glassEasel.Component.createWithContext('root', def, testBackend).asInstanceOf(def)!
expect(domHtml(elem)).toBe('foo
foo
')
matchElementWithDom(elem)
})
test('block slot', () => {
const childComp = glassEasel.registerElement({
options: {
multipleSlots: true,
},
template: tmpl(`
`),
})
const def = glassEasel
.registerElement({
using: {
'child-comp': childComp,
},
template: tmpl(`
123
`),
data: {
d: 'child-slot',
},
})
.general()
const elem = glassEasel.Component.createWithContext('root', def, testBackend)
glassEasel.Element.pretendAttached(elem)
expect(domHtml(elem)).toBe('123
')
matchElementWithDom(elem)
elem.setData({
d: '',
})
expect(domHtml(elem)).toBe('')
matchElementWithDom(elem)
})
test('let variable', () => {
const def = glassEasel
.registerElement({
template: tmpl(`
{{c}}
`),
data: {
d: 123,
},
})
.general()
const elem = glassEasel.Component.createWithContext('root', def, testBackend)
glassEasel.Element.pretendAttached(elem)
const div = elem.getShadowRoot()!.childNodes[0]!.asElement()!
expect(domHtml(elem)).toBe('1230
')
expect(div.dataset.a).toBe('')
expect(div.dataset.b).toBe(123)
expect(div.dataset.c).toBe(1230)
matchElementWithDom(elem)
elem.setData({
d: 456,
})
expect(domHtml(elem)).toBe('4560
')
expect(div.dataset.a).toBe('')
expect(div.dataset.b).toBe(456)
expect(div.dataset.c).toBe(4560)
matchElementWithDom(elem)
})
test('let variable on block', () => {
const def = glassEasel
.registerElement({
template: tmpl(`
{{c}}
`),
data: {
d: 123,
},
})
.general()
const elem = glassEasel.Component.createWithContext('root', def, testBackend)
glassEasel.Element.pretendAttached(elem)
expect(domHtml(elem)).toBe('1230')
matchElementWithDom(elem)
elem.setData({
d: 456,
})
expect(domHtml(elem)).toBe('4560')
matchElementWithDom(elem)
})
test('tag name cases', () => {
const def = glassEasel
.registerElement({
template: tmpl(`
`),
})
.general()
const elem = glassEasel.Component.createWithContext('root', def, testBackend)
glassEasel.Element.pretendAttached(elem)
expect(domHtml(elem)).toBe('')
matchElementWithDom(elem)
})
test('dataset name cases', () => {
const def = glassEasel
.registerElement({
template: tmpl(`
`),
})
.general()
const elem = glassEasel.Component.createWithContext('root', def, testBackend)
glassEasel.Element.pretendAttached(elem)
expect(domHtml(elem)).toBe('')
matchElementWithDom(elem)
expect(elem.getShadowRoot()!.childNodes[0]!.asElement()!.dataset.camelcase).toBe(123)
})
test('attribute name cases', () => {
const def = glassEasel
.registerElement({
template: tmpl(`
`),
})
.general()
const elem = glassEasel.Component.createWithContext('root', def, testBackend)
glassEasel.Element.pretendAttached(elem)
expect(domHtml(elem)).toBe('')
matchElementWithDom(elem)
})
test('attribute types', () => {
const def = glassEasel
.registerElement({
template: tmpl(`
`),
data: {
arr: [],
obj: {},
NaN,
infinity: Infinity,
},
})
.general()
const elem = glassEasel.Component.createWithContext('root', def, testBackend)
glassEasel.Element.pretendAttached(elem)
expect(domHtml(elem)).toBe(
'',
)
matchElementWithDom(elem)
})
test('property name cases', () => {
const subComp = glassEasel.registerElement({
properties: {
propName: String,
},
})
const def = glassEasel
.registerElement({
using: {
child: subComp.general(),
},
template: tmpl(`
`),
})
.general()
const elem = glassEasel.Component.createWithContext('root', def, testBackend)
glassEasel.Element.pretendAttached(elem)
expect(elem.getShadowRoot()!.childNodes[0]!.asInstanceOf(subComp)!.data.propName).toBe('abc')
})
test('setting native node attr', () => {
const def = glassEasel
.registerElement({
template: tmpl(`
`),
data: {
hidden: false,
url: 'abc',
},
})
.general()
const elem = glassEasel.Component.createWithContext('root', def, testBackend)
glassEasel.Element.pretendAttached(elem)
expect(domHtml(elem)).toBe('')
matchElementWithDom(elem)
elem.setData({
hidden: true,
url: '',
})
expect(domHtml(elem)).toBe('')
matchElementWithDom(elem)
})
test('setting element id', () => {
const def = glassEasel
.registerElement({
template: tmpl(`
123
`),
data: {
d: 'abc',
},
})
.general()
const elem = glassEasel.Component.createWithContext('root', def, testBackend)
glassEasel.Element.pretendAttached(elem)
expect(domHtml(elem)).toBe('123
')
matchElementWithDom(elem)
expect(domHtml(elem.getShadowRoot()!.getElementById('abc')!)).toBe('123')
elem.setData({
d: 'def',
})
expect(elem.getShadowRoot()!.getElementById('abc')).toBe(undefined)
expect(domHtml(elem.getShadowRoot()!.getElementById('def')!)).toBe('123')
})
test('setting element slot', () => {
const subComp = glassEasel.registerElement({
options: {
multipleSlots: true,
},
template: tmpl(`
`),
})
const def = glassEasel
.registerElement({
using: {
'sub-comp': subComp,
},
template: tmpl(`
`),
})
.general()
const elem = glassEasel.Component.createWithContext('root', def, testBackend)
glassEasel.Element.pretendAttached(elem)
expect(domHtml(elem)).toBe('')
matchElementWithDom(elem)
elem.setData({
s: 'a',
})
expect(domHtml(elem)).toBe('')
matchElementWithDom(elem)
elem.setData({
s: 'b',
})
expect(domHtml(elem)).toBe('')
matchElementWithDom(elem)
})
test('setting element dataset', () => {
const subComp = glassEasel.registerElement({
template: tmpl(`
`),
})
const def = glassEasel
.registerElement({
using: {
'sub-comp': subComp,
},
template: tmpl(`
`),
})
.general()
const elem = glassEasel.Component.createWithContext('root', def, testBackend)
glassEasel.Element.pretendAttached(elem)
expect(domHtml(elem)).toBe('')
matchElementWithDom(elem)
expect(elem.getShadowRoot()!.getElementById('a')!.dataset['a-b']).toBe('123')
const sub = elem.getShadowRoot()!.getElementById('sub')!.asGeneralComponent()!
expect(sub.dataset['a-b']).toBe('456')
expect(sub.getShadowRoot()!.getElementById('a')!.dataset['a-b']).toBe('789')
})
test('setting element dataset (legacy syntax)', () => {
const subComp = glassEasel.registerElement({
template: tmpl(`
`),
})
const def = glassEasel
.registerElement({
using: {
'sub-comp': subComp,
},
template: tmpl(`
`),
})
.general()
const elem = glassEasel.Component.createWithContext('root', def, testBackend)
glassEasel.Element.pretendAttached(elem)
expect(domHtml(elem)).toBe('')
matchElementWithDom(elem)
expect(elem.getShadowRoot()!.getElementById('a')!.dataset.aB).toBe('123')
const sub = elem.getShadowRoot()!.getElementById('sub')!.asGeneralComponent()!
expect(sub.dataset.aB).toBe('456')
expect(sub.getShadowRoot()!.getElementById('a')!.dataset.aB).toBe('789')
})
test('setting slot name', () => {
const subComp = glassEasel.registerElement({
options: {
multipleSlots: true,
},
template: tmpl(`
`),
data: {
a: '',
b: '',
c: 's',
},
})
const def = glassEasel
.registerElement({
using: {
'sub-comp': subComp.general(),
},
template: tmpl(`
`),
})
.general()
const elem = glassEasel.Component.createWithContext('root', def, testBackend)
const subElem = (elem.$.sub as glassEasel.GeneralComponent).asInstanceOf(subComp)!
glassEasel.Element.pretendAttached(elem)
expect(domHtml(elem)).toBe('')
matchElementWithDom(elem)
subElem.setData({
b: 's',
})
expect(domHtml(elem)).toBe('')
matchElementWithDom(elem)
subElem.setData({
a: 's',
})
expect(domHtml(elem)).toBe('')
matchElementWithDom(elem)
subElem.setData({
b: '',
c: '',
})
expect(domHtml(elem)).toBe('')
matchElementWithDom(elem)
subElem.setData({
c: 's',
})
expect(domHtml(elem)).toBe('')
matchElementWithDom(elem)
subElem.setData({
b: 's',
})
expect(domHtml(elem)).toBe('')
matchElementWithDom(elem)
subElem.setData({
a: 'a',
b: 'b',
})
expect(domHtml(elem)).toBe('')
matchElementWithDom(elem)
subElem.setData({
c: '',
})
expect(domHtml(elem)).toBe('')
matchElementWithDom(elem)
})
test('binding event listeners', () => {
const ops: any[] = []
const def = glassEasel
.registerElement({
template: tmpl(`
`),
methods: {
ev(ev: any) {
// eslint-disable-next-line no-use-before-define
expect(this).toBe(elem)
ops.push(ev)
},
},
})
.general()
const elem = glassEasel.Component.createWithContext('root', def, testBackend)
glassEasel.Element.pretendAttached(elem)
expect(domHtml(elem)).toBe('
')
matchElementWithDom(elem)
const child = elem.getShadowRoot()!.getElementById('child')!
child.triggerEvent('customEv')
const ev = ops.shift() as glassEasel.ShadowedEvent
expect(ev.mark).toEqual({ a: '123', b: undefined })
expect(ev.target.dataset).toEqual({ dA: 'a', 'd-b': 'b', dC: undefined })
elem.setData({ d: true })
child.triggerEvent('customEv')
const ev2 = ops.shift() as glassEasel.ShadowedEvent
expect(ev2.mark).toEqual({ a: '123', b: true })
expect(ev2.target.dataset).toEqual({ dA: 'a', 'd-b': 'b', dC: true })
})
test('binding dynamic event listeners', () => {
let ops: number[] = []
const def = glassEasel
.registerElement({
template: tmpl(`
`),
methods: {
a() {
ops.push(1)
},
b() {
ops.push(2)
},
},
})
.general()
const elem = glassEasel.Component.createWithContext('root', def, testBackend)
glassEasel.Element.pretendAttached(elem)
const child = elem.getShadowRoot()!.getElementById('child')!
child.triggerEvent('customEv')
expect(ops).toStrictEqual([])
ops = []
elem.setData({
handlerName: 'a',
})
child.triggerEvent('customEv')
expect(ops).toStrictEqual([1])
ops = []
elem.setData({
handlerName: 'b',
})
child.triggerEvent('customEv')
expect(ops).toStrictEqual([2])
ops = []
})
test('binding event on slot', () => {
const ops: any[] = []
const def = glassEasel
.registerElement({
template: tmpl(`
`),
methods: {
ev(ev: any) {
// eslint-disable-next-line no-use-before-define
expect(this).toBe(elem)
ops.push(ev)
},
},
})
.general()
const elem = glassEasel.Component.createWithContext('root', def, testBackend)
glassEasel.Element.pretendAttached(elem)
expect(domHtml(elem)).toBe('')
matchElementWithDom(elem)
const child = elem.getShadowRoot()!.getElementById('child')!
child.triggerEvent('customEv', null, { bubbles: true })
const ev = ops.shift() as glassEasel.ShadowedEvent
expect(ev.mark).toStrictEqual({ b: 123 })
expect(ev.target.dataset).toStrictEqual({ a: 'abc' })
})
test('setting properties', () => {
const cs = new glassEasel.ComponentSpace()
const subComp = cs.defineComponent({
template: tmpl(`
`),
properties: {
a: Boolean,
style: String,
propA: Number,
dataB: String,
},
})
const def = cs
.defineComponent({
using: {
'sub-comp': subComp.general(),
},
template: tmpl(`
`),
})
.general()
const elem = glassEasel.Component.createWithContext('root', def, testBackend)
glassEasel.Element.pretendAttached(elem)
expect(domHtml(elem)).toBe('')
matchElementWithDom(elem)
})
test('setting array as classNames', () => {
const cs = new glassEasel.ComponentSpace()
const ssm = cs.styleScopeManager
const subComp = cs.defineComponent({
externalClasses: ['ext-class'],
template: tmpl(`
`),
})
const def = cs
.defineComponent({
options: {
styleScope: ssm.register('p'),
},
using: {
'sub-comp': subComp.general(),
},
data: () => ({
classes: 'static',
extClass: 'a-class',
}),
template: tmpl(`
`),
})
.general()
const elem = glassEasel.Component.createWithContext('root', def, testBackend)
glassEasel.Element.pretendAttached(elem)
expect(domHtml(elem)).toBe(
'',
)
matchElementWithDom(elem)
elem.setData({
classes: ['static', 'dynamic'],
})
expect(domHtml(elem)).toBe(
'',
)
matchElementWithDom(elem)
elem.setData({
classes: '',
extClass: ['a-class', 'dynamic'],
})
expect(domHtml(elem)).toBe(
'',
)
matchElementWithDom(elem)
elem.setData({
extClass: 'static',
})
expect(domHtml(elem)).toBe('')
matchElementWithDom(elem)
})
test('setting external classes', () => {
const cs = new glassEasel.ComponentSpace(
undefined,
undefined,
glassEasel.getDefaultComponentSpace().styleScopeManager,
)
const ssm = cs.styleScopeManager
const subComp = cs.defineComponent({
externalClasses: ['class', 'data-class'],
template: tmpl(`
`),
data: {
a: 's',
},
})
const def = cs
.defineComponent({
options: {
styleScope: ssm.register('p'),
},
using: {
'sub-comp': subComp.general(),
},
template: tmpl(`
`),
})
.general()
const elem = glassEasel.Component.createWithContext('root', def, testBackend)
glassEasel.Element.pretendAttached(elem)
expect(domHtml(elem)).toBe(
'',
)
matchElementWithDom(elem)
elem.setData({
dynamic: 'dynamic',
})
expect(domHtml(elem)).toBe(
'',
)
matchElementWithDom(elem)
elem.setData({
dynamic: '',
})
expect(domHtml(elem)).toBe(
'',
)
matchElementWithDom(elem)
})
test('setting nested external classes', () => {
const cs = new glassEasel.ComponentSpace(
undefined,
undefined,
glassEasel.getDefaultComponentSpace().styleScopeManager,
)
const ssm = cs.styleScopeManager
const subComp = cs.defineComponent({
externalClasses: ['class', 'ext-class'],
template: tmpl(`
`),
})
const def = cs
.defineComponent({
options: {
styleScope: ssm.register('p'),
},
using: {
sub: subComp.general(),
},
properties: {
dynamic: String,
},
externalClasses: ['a-class'],
template: tmpl(`
`),
})
.general()
const parent = cs.defineComponent({
options: {
styleScope: ssm.register('pp'),
extraStyleScope: glassEasel.StyleScopeManager.globalScope(),
},
using: {
def,
},
data: {
dynamic1: '',
dynamic2: '',
},
template: tmpl(``),
})
const elem = glassEasel.Component.createWithContext('root', parent, testBackend)
glassEasel.Element.pretendAttached(elem)
expect(domHtml(elem)).toBe(
'',
)
matchElementWithDom(elem)
elem.setData({
dynamic1: 'dynamic',
})
expect(domHtml(elem)).toBe(
'',
)
matchElementWithDom(elem)
elem.setData({
dynamic1: '',
dynamic2: 'dynamic',
})
expect(domHtml(elem)).toBe(
'',
)
matchElementWithDom(elem)
})
test('class list syntax (virtual-tree update)', () => {
const cs = new glassEasel.ComponentSpace()
const def = cs
.defineComponent({
data: () => ({
classA: false,
}),
template: tmpl(
`
`,
{ updateMode: 'virtualTree' },
),
})
.general()
const elem = glassEasel.Component.createWithContext('root', def, testBackend)
let changeCount = 0
glassEasel.MutationObserver.create(() => {
changeCount += 1
}).observe(elem.getShadowRoot()!.childNodes[1]!, { properties: true })
glassEasel.Element.pretendAttached(elem)
expect(domHtml(elem)).toBe('')
matchElementWithDom(elem)
elem.setData({
classA: true,
})
expect(domHtml(elem)).toBe('')
matchElementWithDom(elem)
expect(changeCount).toBe(1)
elem.setData({
classA: false,
})
expect(domHtml(elem)).toBe('')
matchElementWithDom(elem)
expect(changeCount).toBe(2)
})
test('class list syntax (binding-map update)', () => {
const cs = new glassEasel.ComponentSpace()
const def = cs
.defineComponent({
data: () => ({
classA: false,
}),
template: tmpl(
`
`,
{ updateMode: 'bindingMap' },
),
})
.general()
const elem = glassEasel.Component.createWithContext('root', def, testBackend)
let changeCount = 0
glassEasel.MutationObserver.create(() => {
changeCount += 1
}).observe(elem.getShadowRoot()!.childNodes[1]!, { properties: true })
glassEasel.Element.pretendAttached(elem)
expect(domHtml(elem)).toBe('')
matchElementWithDom(elem)
elem.setData({
classA: true,
})
expect(domHtml(elem)).toBe('')
matchElementWithDom(elem)
expect(changeCount).toBe(1)
elem.setData({
classA: false,
})
expect(domHtml(elem)).toBe('')
matchElementWithDom(elem)
expect(changeCount).toBe(2)
})
test('style list syntax (virtual-tree update)', () => {
const cs = new glassEasel.ComponentSpace()
const def = cs
.defineComponent({
data: () => ({
fontSize: 16,
c: null as string | null,
}),
template: tmpl(
`
`,
{ updateMode: 'virtualTree' },
),
})
.general()
const elem = glassEasel.Component.createWithContext('root', def, testBackend)
glassEasel.Element.pretendAttached(elem)
expect(domHtml(elem)).toBe(
'',
)
matchElementWithDom(elem)
elem.setData({
fontSize: 20,
c: 'blue',
})
expect(domHtml(elem)).toBe(
'',
)
matchElementWithDom(elem)
elem.setData({
c: null,
})
expect(domHtml(elem)).toBe(
'',
)
matchElementWithDom(elem)
})
test('style list syntax (binding-map update)', () => {
const cs = new glassEasel.ComponentSpace()
const def = cs
.defineComponent({
data: () => ({
fontSize: 16,
c: null as string | null,
}),
template: tmpl(
`
`,
{ updateMode: 'bindingMap' },
),
})
.general()
const elem = glassEasel.Component.createWithContext('root', def, testBackend)
glassEasel.Element.pretendAttached(elem)
expect(domHtml(elem)).toBe(
'',
)
matchElementWithDom(elem)
elem.setData({
fontSize: 20,
c: 'blue',
})
expect(domHtml(elem)).toBe(
'',
)
matchElementWithDom(elem)
elem.setData({
c: null,
})
expect(domHtml(elem)).toBe(
'',
)
matchElementWithDom(elem)
})
test('pass object to child components', () => {
const cs = new glassEasel.ComponentSpace()
let ops = 0
const subComp = cs.defineComponent({
template: tmpl(`
`),
properties: {
prop: Object,
},
observers: {
prop() {
ops += 1
},
},
})
const def = cs
.defineComponent({
using: {
'sub-comp': subComp.general(),
},
template: tmpl(`
`),
data: {
obj: {
a: 'a1',
},
num: 0,
},
})
.general()
const elem = glassEasel.Component.createWithContext('root', def, testBackend)
glassEasel.Element.pretendAttached(elem)
expect(domHtml(elem)).toBe('')
matchElementWithDom(elem)
expect(ops).toBe(1)
elem.setData({
'obj.b': 'b1',
})
expect(ops).toBe(2)
elem.setData({
num: 1,
})
expect(ops).toBe(2)
elem.setData({
obj: { a: 'a2' },
})
expect(domHtml(elem)).toBe('')
matchElementWithDom(elem)
expect(ops).toBe(3)
})
test('recursive component', () => {
const cs = new glassEasel.ComponentSpace()
cs.updateComponentOptions({
writeFieldsToNode: false,
})
type INode = {
className: string
childNodes: INode[]
before?: string
after?: string
}
const Node = cs
.define('node')
.options({
virtualHost: true,
dataDeepCopy: glassEasel.DeepCopyKind.None,
propertyPassingDeepCopy: glassEasel.DeepCopyKind.None,
})
.property('childNodes', {
type: Array,
default: () => [] as INode[],
})
.externalClasses(['class'])
.template(
tmpl(`
{{item.before}}
{{item.after}}
`),
)
.registerComponent()
cs.setGlobalUsingComponent('node', Node.general())
const Parent = cs
.define()
.usingComponents({ node: Node })
.options({
dataDeepCopy: glassEasel.DeepCopyKind.None,
propertyPassingDeepCopy: glassEasel.DeepCopyKind.None,
})
.template(
tmpl(`
`),
)
.data(() => ({
node: {
className: 'root',
childNodes: [
{
className: 'c1',
childNodes: [
{
className: 'c11',
childNodes: [],
before: '[',
after: ']',
},
],
},
{
className: 'c2',
childNodes: [],
},
],
} satisfies INode,
}))
.registerComponent()
const elem = glassEasel.Component.createWithContext('root', Parent, testBackend)
glassEasel.Element.pretendAttached(elem)
expect(domHtml(elem)).toBe(
'',
)
matchElementWithDom(elem)
})
}
describe('node tree structure (DOM backend)', () => testCases(domBackend))
describe('node tree structure (shadow backend)', () => testCases(shadowBackend))
describe('node tree structure (composed backend)', () => testCases(composedBackend))