import { Component, markup, mount, useState, xml } from "../../src"; import { useAttachedEl } from "../../src/runtime/hooks"; import { makeTestFixture, nextTick, steps, useLogLifecycle } from "../helpers"; let fixture: HTMLElement; beforeEach(() => { fixture = makeTestFixture(); }); describe("basics", () => { test("can attach an empty component", async () => { fixture.innerHTML = "
hello
"; class Test extends Component { setup() { useLogLifecycle(); } } await mount(Test, fixture, { position: "attach" }); expect(steps.splice(0)).toMatchInlineSnapshot(` Array [ "Test:setup", "Test:willStart", "Test:willRender", "Test:rendered", "Test:mounted", ] `); expect(fixture.innerHTML).toBe("
hello
"); }); test("attaching a component with a template throws", async () => { fixture.innerHTML = "
hello
"; class Test extends Component { static template = xml`hello`; } let error: Error | null = null; try { await mount(Test, fixture, { position: "attach" }); } catch (e: any) { error = e; } expect(error!.message).toBe("Cannot attach a component with a template"); expect(fixture.innerHTML).toBe("
hello
"); }); test("can attach a component with simple dynamic content", async () => { fixture.innerHTML = "

hello

"; class Test extends Component { static dynamicContent = { "p:t-att-a": "value", }; value: string = ""; setup() { this.value = "b"; } } await mount(Test, fixture, { position: "attach" }); expect(fixture.innerHTML).toBe('

hello

'); }); test("useAttachedEl returns the attached element", async () => { fixture.innerHTML = "

hello

"; let el: any = null; class Test extends Component { setup() { el = useAttachedEl(); } } await mount(Test, fixture, { position: "attach" }); expect(el).toBe(fixture); }); test("useAttachedEl throws if component is not attached", async () => { class Test extends Component { static template = xml`hello`; setup() { useAttachedEl(); } } let error: any = null; try { await mount(Test, fixture); } catch (_e: any) { error = _e; } expect(error.message).toBe("useAttachedEl can only be called with component that are attached"); }); test("multiple dynamic attribute", async () => { fixture.innerHTML = "

hello

"; class Test extends Component { static dynamicContent = { "p:t-att-a": "value", "p:t-att-b": "value + 'coucou'", }; value: string = ""; setup() { this.value = "b"; } } await mount(Test, fixture, { position: "attach" }); expect(fixture.innerHTML).toBe('

hello

'); }); test("attrs can target root", async () => { fixture.innerHTML = "

hello

"; class Test extends Component { static dynamicContent = { "root:t-att-a": "value", }; value: string = ""; setup() { this.value = "b"; } } await mount(Test, fixture, { position: "attach" }); expect(fixture.outerHTML).toBe('

hello

'); }); test("dynamic attribute is updated on rerender", async () => { fixture.innerHTML = "

hello

"; class Test extends Component { static dynamicContent = { "p:t-att-a": "state.value", }; state: any; setup() { this.state = useState({ value: 1 }); } } const test = await mount(Test, fixture, { position: "attach" }); expect(fixture.innerHTML).toBe('

hello

'); test.state.value = 2; await nextTick(); expect(fixture.innerHTML).toBe('

hello

'); }); test("t-on-click, basic", async () => { fixture.innerHTML = "

hello

"; let ev: Event | null = null; class Test extends Component { static dynamicContent = { "p:t-att-a": "state.value", "p:t-on-click": "onClick", }; state: any; setup() { this.state = useState({ value: 1 }); } onClick(_ev: any) { ev = _ev; this.state.value = 2; } } await mount(Test, fixture, { position: "attach" }); expect(fixture.innerHTML).toBe('

hello

'); fixture.querySelector("p")!.click(); expect(ev).toBeInstanceOf(Event); await nextTick(); expect(fixture.innerHTML).toBe('

hello

'); }); test("t-on-click, target root element", async () => { fixture.innerHTML = "hello"; let click = false; class Test extends Component { static dynamicContent = { "root:t-on-click": "onClick", }; onClick() { click = true; } } await mount(Test, fixture, { position: "attach" }); fixture.click(); expect(click).toBe(true); }); test("t-out, basic", async () => { fixture.innerHTML = "

hello

"; class Test extends Component { static dynamicContent = { "p:t-out": "state.value", }; state: any; setup() { this.state = useState({ value: 1 }); } } await mount(Test, fixture, { position: "attach" }); expect(fixture.innerHTML).toBe("

1

"); }); test("t-out, on root", async () => { fixture.innerHTML = "hello"; class Test extends Component { static dynamicContent = { "root:t-out": "state.value", }; state: any; setup() { this.state = useState({ value: 1 }); } } expect(fixture.outerHTML).toBe("
hello
"); await mount(Test, fixture, { position: "attach" }); expect(fixture.outerHTML).toBe("
1
"); }); test("t-out, with markup", async () => { fixture.innerHTML = `

hello

hello

`; class Test extends Component { static dynamicContent = { "p.p1:t-out": "value1", "p.p2:t-out": "value2", }; value1: any; value2: any; setup() { this.value1 = "
value1
"; this.value2 = markup("
value2
"); } } await mount(Test, fixture, { position: "attach" }); expect(fixture.innerHTML).toBe( `

<div>value1</div>

value2

` ); }); });