import { domBackend, execWithWarn, tmpl } from '../base/env' import * as glassEasel from '../../src' const componentSpace = new glassEasel.ComponentSpace() componentSpace.updateComponentOptions({ writeFieldsToNode: true, }) componentSpace.defineComponent({ is: '', }) const domHtml = (elem: glassEasel.Element): string => { const domElem = elem.getBackendElement() as unknown as Element return domElem.innerHTML } describe('chaining-form interface', () => { test('chaining options and template', () => { const beh = componentSpace .define() .options({ writeIdToDOM: false, }) .template( tmpl(`
`), ) .registerBehavior() const compDef = componentSpace .define() .options({ writeIdToDOM: true, }) .template( tmpl(`
`), ) .behavior(beh) .data(() => ({ a: 'abc', })) .registerComponent() const elem = glassEasel.Component.createWithContext('root', compDef, domBackend) expect(domHtml(elem)).toBe('
') }) test('chaining using and generics', () => { const child = componentSpace .define() .options({ virtualHost: true, }) .externalClasses(['a-class', 'b-class']) .template( tmpl(` `), ) .registerComponent() const beh = componentSpace .define() .usingComponents({ 'invalid-comp': child, }) .generics({ 'invalid-gen': true, }) .registerBehavior() const compDef = componentSpace .define() .behavior(beh) .usingComponents({ 'comp-a': child, }) .usingComponents({ 'comp-b': child, }) .generics({ 'gen-a': true, }) .generics({ 'gen-b': { default: 'comp-b', }, }) .template( tmpl(` `), ) .registerComponent() const elem = glassEasel.Component.createWithGenericsAndContext( 'root', compDef, { 'gen-a': child, 'gen-b': child, 'invalid-gen': child, }, domBackend, ) expect(domHtml(elem)).toBe( '', ) }) test('chaining external classes', () => { const beh = componentSpace.define().externalClasses(['invalid']).registerBehavior() const child = componentSpace .define() .behavior(beh) .externalClasses(['a-class', 'b-class']) .template( tmpl(`
`), ) .registerComponent() const compDef = componentSpace .define() .usingComponents({ child, }) .template( tmpl(` `), ) .registerComponent() const elem = execWithWarn(1, () => glassEasel.Component.createWithContext('root', compDef, domBackend), ) expect(domHtml(elem)).toBe('
') }) test('chaining data and observers', () => { const callOrder: number[] = [] const beh = componentSpace .define() .data(() => ({ a: 123, a2: 456, })) .observer('a', function () { callOrder.push(1) this.setData({ a2: this.data.a * 2, }) }) .registerBehavior() const compDef = componentSpace .define() .behavior(beh) .data(() => ({ b: 'abc', b2: '', b3: '', c: 'ccc', })) .init(({ data, setData, observer }) => { observer('b', () => { callOrder.push(3) setData({ b3: `${data.b}3`, }) let catched = false try { observer('b', () => undefined) } catch { catched = true } expect(catched).toBe(true) }) observer(['b', 'c'], () => { callOrder.push(4) }) }) .definition({ observers: { b() { callOrder.push(2) this.setData({ b2: `${this.data.b}2`, }) }, }, }) .registerComponent() const elem = glassEasel.Component.createWithContext('root', compDef, domBackend) expect(elem.data).toStrictEqual({ a: 123, a2: 456, b: 'abc', b2: '', b3: '', c: 'ccc', }) elem.setData({ a: 234, b: 'def', c: '', }) expect(callOrder).toStrictEqual([1, 2, 3, 4]) expect(elem.data).toStrictEqual({ a: 234, a2: 468, b: 'def', b2: 'def2', b3: 'def3', c: '', }) }) test('chaining properties', () => { const callOrder: number[] = [] const beh = componentSpace .define() .property('propA', { type: Number, default: () => 456, }) .observer('propA', () => { callOrder.push(1) }) .registerBehavior() const compDef = componentSpace .define() .behavior(beh) .property('propB', String) .definition({ properties: { propC: { default: () => 789, }, }, observers: { propA() { callOrder.push(2) }, }, }) .init(({ observer }) => { observer('propB', () => { callOrder.push(3) }) }) .registerComponent() const elem = glassEasel.Component.createWithContext('root', compDef, domBackend) expect(elem.data).toStrictEqual({ propA: 456, propB: '', propC: 789 }) execWithWarn(1, () => { elem.setData({ propA: 'abc' as unknown as number, propB: 'def', propC: undefined, }) }) expect(callOrder).toStrictEqual([1, 2, 3]) expect(elem.data).toStrictEqual({ propA: 456, propB: 'def', propC: 789 }) }) test('chaining methods', () => { const beh = componentSpace .define() .data(() => ({ a1: '', })) .methods({ a1(s: string) { this.setData({ a1: s, }) }, }) .registerBehavior() const compDef = componentSpace .define() .template( tmpl(`
{{a1}}-{{a2}}-{{a3}}
`), ) .behavior(beh) .definition({ data: { a2: '', }, methods: { a2(s: string) { this.setData({ a2: s, }) }, }, }) .data(() => ({ a3: '', })) .init(({ setData, method, lifetime, listener }) => { const cev = method((s: glassEasel.Event<{ detailStr: string }>) => { setData({ a3: s.detail.detailStr }) }) const cev2 = listener<{ detailStr: string }>((s) => { setData({ a3: s.detail.detailStr }) }) lifetime('created', () => { let catched = false try { method(() => undefined) } catch { catched = true } try { listener(() => undefined) } catch { catched = true } expect(catched).toBe(false) }) return { cev, cev2, } }) .registerComponent() const elem = glassEasel.Component.createWithContext('root', compDef, domBackend) expect(domHtml(elem)).toBe('
--
') elem.a1('b1') expect(domHtml(elem)).toBe('
b1--
') elem.a2('b2') expect(domHtml(elem)).toBe('
b1-b2-
') elem .getShadowRoot()! .getElementById('a')! .asNativeNode() ?.triggerEvent('customEv', { detailStr: 'b3' }) expect(domHtml(elem)).toBe('
b1-b2-b3
') elem .getShadowRoot()! .getElementById('a')! .asNativeNode() ?.triggerEvent('customEv2', { detailStr: 'b33' }) expect(domHtml(elem)).toBe('
b1-b2-b33
') }) test('chaining lifetimes', () => { const callOrder: number[] = [] const beh = componentSpace .define() .data(() => ({ a: 0, })) .lifetime('created', function () { callOrder.push(1) this.setData({ a: 1 }) }) .registerBehavior() const compDef = componentSpace .define() .template( tmpl(`
{{a}}-{{b}}-{{c}}
`), ) .behavior(beh) .data(() => ({ b: 0, })) .definition({ lifetimes: { created() { callOrder.push(2) this.setData({ b: 2 }) }, }, }) .data(() => ({ c: 0, })) .init(({ data, setData, lifetime }) => { lifetime('created', () => { callOrder.push(3) setData({ c: 3 }) expect(data.c).toBe(3) }) lifetime('attached', () => { callOrder.push(-1) }) expect(data.c).toBe(0) lifetime('created', () => { let catched = false try { lifetime('attached', () => { callOrder.push(-100) }) } catch { catched = true } expect(catched).toBe(true) }) }) .registerComponent() const elem = glassEasel.Component.createWithContext('root', compDef, domBackend) expect(callOrder).toStrictEqual([1, 2, 3]) expect(elem.data).toStrictEqual({ a: 1, b: 2, c: 3 }) glassEasel.Element.pretendAttached(elem) expect(callOrder).toStrictEqual([1, 2, 3, -1]) expect(domHtml(elem)).toBe('
1-2-3
') }) test('chaining page-lifetimes', () => { const callOrder: number[] = [] const beh = componentSpace .define() .data(() => ({ a: 0, })) .pageLifetime('show', function (opt: { inc: number }) { callOrder.push(opt.inc) this.setData({ a: opt.inc }) const r = opt r.inc += 1 }) .registerBehavior() const compDef = componentSpace .define() .template( tmpl(`
{{a}}-{{b}}-{{c}}
`), ) .behavior(beh) .data(() => ({ b: 0, })) .definition({ pageLifetimes: { show(opt: { inc: number }) { callOrder.push(opt.inc) this.setData({ b: opt.inc * 10 }) const r = opt r.inc += 1 }, }, }) .data(() => ({ c: 0, })) .init(({ data, setData, pageLifetime, lifetime }) => { pageLifetime('show', (opt: { inc: number }) => { callOrder.push(opt.inc) setData({ c: opt.inc * 100 }) const r = opt r.inc += 1 expect(data.c).toBe(300) }) pageLifetime('hide', () => { callOrder.push(-1) }) expect(data.c).toBe(0) lifetime('created', () => { let catched = false try { pageLifetime('hide', () => { callOrder.push(-100) }) } catch { catched = true } expect(catched).toBe(true) }) }) .registerComponent() const elem = glassEasel.Component.createWithContext('root', compDef, domBackend) expect(domHtml(elem)).toBe('
0-0-0
') elem.triggerPageLifetime('show', [{ inc: 1 }]) expect(callOrder).toStrictEqual([1, 2, 3]) expect(elem.data).toStrictEqual({ a: 1, b: 20, c: 300 }) expect(domHtml(elem)).toBe('
1-20-300
') elem.triggerPageLifetime('hide', []) expect(callOrder).toStrictEqual([1, 2, 3, -1]) }) test('chaining init and methodCallerInit', () => { const callOrder: number[] = [] const beh = componentSpace .define() .init(() => { callOrder.push(1) }) .registerBehavior() const compDef = componentSpace .define() .init(() => { callOrder.push(2) }) .behavior(beh) .methodCallerInit(function () { callOrder.push(3) return this }) .registerComponent() glassEasel.Component.createWithContext('root', compDef, domBackend) expect(callOrder).toStrictEqual([3, 1, 2]) }) test('chaining relations', () => { const eventArr: number[] = [] const parentDef = componentSpace .define('parent-comp') .relation('child', { type: 'child', target: 'child-comp', linked() { eventArr.push(1) this.getRelationNodes('child').forEach((c, index) => { // eslint-disable-next-line no-use-before-define const cc = c.asInstanceOf(childDef)! cc.setIndex(index) }) }, }) .registerComponent() const childDef = componentSpace .define('child-comp') .template(tmpl('{{index}}')) .data(() => ({ index: '', })) .methods({ setIndex(index: number) { this.setData({ index: String.fromCharCode(index + 'A'.charCodeAt(0)), }) }, }) .init(({ relation, lifetime }) => { relation({ type: 'parent', target: 'parent-comp', linked() { eventArr.push(2) }, }) lifetime('created', () => { let catched = false try { relation({ type: 'child' }) } catch { catched = true } expect(catched).toBe(true) }) }) .registerComponent() const compDef = componentSpace .define() .usingComponents({ parent: parentDef, child: childDef, }) .template( tmpl(` `), ) .data(() => ({ list: [1, 2, 3], })) .registerComponent() const comp = glassEasel.Component.createWithContext('root', compDef, domBackend) glassEasel.Element.pretendAttached(comp) expect(eventArr).toStrictEqual([1, 2, 1, 2, 1, 2]) expect(domHtml(comp)).toBe('ABC') }) test('chaining filter', () => { const beh = componentSpace .define() .chainingFilter<{ myData(this: T): T }, never>((chain) => // eslint-disable-next-line @typescript-eslint/no-unsafe-return Object.create(chain, { myData: { value() { return chain.data(() => ({ a: 123, })) }, }, }), ) .registerBehavior() const compDef = componentSpace .define() .behavior(beh) .data(() => ({ b: '233' })) .myData() .template( tmpl(`
{{a}}
`), ) .registerComponent() const elem = glassEasel.Component.createWithContext('root', compDef, domBackend) expect(domHtml(elem)).toBe('
123
') }) test('chaining extraThisFieldsType', () => { const beh = componentSpace .define() .extraThisFieldsType<{ behDelayedData: string }>() .lifetime('created', function () { this.behDelayedData = ' World!' }) .registerBehavior() const compDef = componentSpace .define() .behavior(beh) .extraThisFieldsType<{ delayedData: string }>() .data(() => ({ hello: '', })) .lifetime('created', function () { this.delayedData = 'Hello' this.setData({ hello: this.delayedData + this.behDelayedData, }) }) .template( tmpl(`
{{hello}}
`), ) .registerComponent() const elem = glassEasel.Component.createWithContext('root', compDef, domBackend) expect(domHtml(elem)).toBe('
Hello World!
') }) })