import { App, Component, mount, onMounted, useState, xml } from "../../src/index"; import { children, makeTestFixture, nextAppError, nextTick, snapshotEverything } from "../helpers"; snapshotEverything(); let originalconsoleWarn = console.warn; let mockConsoleWarn: any; let fixture: HTMLElement; beforeEach(() => { fixture = makeTestFixture(); mockConsoleWarn = jest.fn(() => {}); console.warn = mockConsoleWarn; }); afterEach(() => { console.warn = originalconsoleWarn; }); describe("slots", () => { test("simple default slot", async () => { class Child extends Component { static template = xml``; } class Parent extends Component { static template = xml`some text`; static components = { Child }; } await mount(Parent, fixture); expect(fixture.innerHTML).toBe("some text"); }); test("simple default slot, variation", async () => { class Child extends Component { static template = xml``; } class Parent extends Component { static template = xml`some text`; static components = { Child }; } await mount(Parent, fixture); expect(fixture.innerHTML).toBe("some text"); }); test("t-set-slot=default has priority over rest of the content", async () => { class Child extends Component { static template = xml``; } class Parent extends Component { static template = xml` some text some other text `; static components = { Child }; } await mount(Parent, fixture); expect(fixture.innerHTML).toBe("some other text"); }); test("simple slot with slot scope", async () => { let child: any; class Child extends Component { static template = xml``; state = useState({ bool: true }); setup() { child = this; } } class Parent extends Component { static template = xml` some text other text `; static components = { Child }; } await mount(Parent, fixture); expect(fixture.innerHTML).toBe("some text"); child.state.bool = false; await nextTick(); expect(fixture.innerHTML).toBe("other text"); }); test("slot with slot scope and t-props", async () => { class Child extends Component { static template = xml` `; info = { a: 1, b: 2 }; } class Parent extends Component { static template = xml`

`; static components = { Child }; } await mount(Parent, fixture); expect(fixture.innerHTML).toBe("

1

2

"); }); test("simple dynamic slot with slot scope", async () => { let child: any; class Child extends Component { static template = xml``; state = useState({ bool: true }); setup() { child = this; } } class Parent extends Component { static template = xml` some text other text `; static components = { Child }; } await mount(Parent, fixture); expect(fixture.innerHTML).toBe("some text"); child.state.bool = false; await nextTick(); expect(fixture.innerHTML).toBe("other text"); }); test("simple named and empty slot", async () => { class Child extends Component { static template = xml``; setup() { expect(this.props.slots["myEmptySlot"]).toBeTruthy(); } } class Parent extends Component { static template = xml`some text`; static components = { Child }; } await mount(Parent, fixture); expect(fixture.innerHTML).toBe("some text"); }); test("simple named and empty slot -- 2", async () => { class Child extends Component { static template = xml`default empty`; setup() { expect(this.props.slots["myEmptySlot"]).toBeTruthy(); expect(this.props.slots["myEmptySlot"].myProp).toBe("myProp text"); } } class Parent extends Component { static template = xml``; static components = { Child }; } await mount(Parent, fixture); expect(fixture.innerHTML).toBe("default empty"); }); test("can use .translate suffix on slot props", async () => { class Child extends Component { static template = xml``; } class Parent extends Component { static template = xml``; static components = { Child }; } await mount(Parent, fixture); expect(fixture.innerHTML).toBe("some message"); }); test(".translate slot props are translated", async () => { class Child extends Component { static template = xml``; } class Parent extends Component { static template = xml``; static components = { Child }; } await mount(Parent, fixture, { translateFn: () => "translated message" }); expect(fixture.innerHTML).toBe("translated message"); }); test("default slot with slot scope: shorthand syntax", async () => { let child: any; class Child extends Component { static template = xml``; state = useState({ bool: true }); setup() { child = this; } } class Parent extends Component { static template = xml` some text other text `; static components = { Child }; } await mount(Parent, fixture); expect(fixture.innerHTML).toBe("some text"); child.state.bool = false; await nextTick(); expect(fixture.innerHTML).toBe("other text"); }); test("simple default slot with params", async () => { class Child extends Component { static template = xml``; state = useState({ bool: true }); } class Parent extends Component { static template = xml` some text other text `; static components = { Child }; } let error: Error; const app = new App(Parent); const mountProm = app.mount(fixture).catch((e: Error) => (error = e)); await expect(nextAppError(app)).resolves.toThrow("error occured in the owl lifecycle"); await mountProm; expect(error!).not.toBeNull(); expect(mockConsoleWarn).toBeCalledTimes(1); }); test("simple default slot with params and bound function", async () => { class Child extends Component { static template = xml``; state = useState({ value: 123 }); getValue() { return this.state.value; } } class Parent extends Component { static template = xml` `; static components = { Child }; } await mount(Parent, fixture); expect(fixture.innerHTML).toBe("123"); }); test("default slot with params with - in it", async () => { class Child extends Component { static template = xml``; state = useState({ value: 123 }); } class Parent extends Component { static template = xml` `; static components = { Child }; } await mount(Parent, fixture); expect(fixture.innerHTML).toBe("123"); }); test("fun: two calls to the same slot", async () => { class Child extends Component { static template = xml``; } class Parent extends Component { static template = xml`some text`; static components = { Child }; } await mount(Parent, fixture); expect(fixture.innerHTML).toBe("some textsome text"); }); test("slot content is bound to caller", async () => { class Child extends Component { static template = xml``; } class Parent extends Component { static template = xml``; static components = { Child }; state = useState({ value: 0 }); inc() { this.state.value++; } } const parent = await mount(Parent, fixture); expect(parent.state.value).toBe(0); fixture.querySelector("button")!.click(); expect(parent.state.value).toBe(1); }); test("slot content is bound to caller (variation)", async () => { class Child extends Component { static template = xml``; } class Parent extends Component { // t-set t-value in template is to force compiler to protect the scope // which in turns means that the ctx propagated to the slot is a sub object static template = xml` `; static components = { Child }; state = useState({ value: 0 }); inc() { expect(this).toBe(parent); this.state.value++; } } const parent = await mount(Parent, fixture); expect(parent.state.value).toBe(0); fixture.querySelector("button")!.click(); expect(parent.state.value).toBe(1); }); test("can define and call slots", async () => { class Dialog extends Component { static template = xml`
`; } class Parent extends Component { static components = { Dialog }; static template = xml`
header footer
`; } await mount(Parent, fixture); expect(fixture.innerHTML).toBe( "
header
footer
" ); }); test("can define and call slots with params", async () => { class Dialog extends Component { static template = xml`
`; } class Parent extends Component { static components = { Dialog }; static template = xml`
header footer
`; var = 3; } await mount(Parent, fixture); expect(fixture.innerHTML).toBe( "
3
header
5
footer
" ); }); test("can define and call slots with bound params", async () => { class Child extends Component { static template = xml` `; } class Parent extends Component { static components = { Child }; static template = xml` abc `; state = useState({ value: 444 }); getValue() { return this.state.value; } } await mount(Parent, fixture); expect(fixture.innerHTML).toBe("abc444"); }); test("no named slot content => just no children", async () => { class Dialog extends Component { static template = xml``; } class Parent extends Component { static template = xml``; static components = { Dialog }; } await mount(Parent, fixture); expect(fixture.innerHTML).toBe(""); }); test("named slots can define a default content", async () => { class Dialog extends Component { static template = xml` default content `; } class Parent extends Component { static template = xml`
`; static components = { Dialog }; } await mount(Parent, fixture); expect(fixture.innerHTML).toBe("
default content
"); }); test("can define a default content", async () => { class Dialog extends Component { static template = xml` default content `; } class Parent extends Component { static template = xml`
`; static components = { Dialog }; } await mount(Parent, fixture); expect(fixture.innerHTML).toBe("
default content
"); }); test("default content is not rendered if slot is provided", async () => { class Dialog extends Component { static template = xml` default content `; } class Parent extends Component { static template = xml`
hey
`; static components = { Dialog }; } await mount(Parent, fixture); expect(fixture.innerHTML).toBe("
hey
"); }); test("default content is not rendered if named slot is provided", async () => { class Dialog extends Component { static template = xml` default content `; } class Parent extends Component { static template = xml`
hey
`; static components = { Dialog }; } await mount(Parent, fixture); expect(fixture.innerHTML).toBe("
hey
"); }); test("slots are properly bound to correct component", async () => { let child: any = null; class Child extends Component { // t-set t-value in template is to force compiler to protect the scope // which in turns means that the ctx propagated to the slot is a sub object static template = xml` `; state = useState({ value: 1 }); setup() { child = this; } increment() { expect(this).toBe(child); this.state.value++; } } class Parent extends Component { static template = xml``; static components = { Child }; } await mount(Parent, fixture); expect(fixture.innerHTML).toBe(""); fixture.querySelector("button")!.click(); await nextTick(); expect(fixture.innerHTML).toBe(""); }); test("slots are rendered with proper context", async () => { class Dialog extends Component { static template = xml``; } class Parent extends Component { static template = xml`
`; static components = { Dialog }; state = useState({ val: 0 }); doSomething() { this.state.val++; } } await mount(Parent, fixture); expect(fixture.innerHTML).toBe( '
0
' ); fixture.querySelector("button")!.click(); await nextTick(); expect(fixture.innerHTML).toBe( '
1
' ); }); test("slots are rendered with proper context, part 2", async () => { class Link extends Component { static template = xml` `; } class App extends Component { static template = xml`
  • User
  • `; state = useState({ users: [ { id: 1, name: "Aaron" }, { id: 2, name: "David" }, ], }); static components = { Link }; } const app = await mount(App, fixture); expect(fixture.innerHTML).toBe( '' ); // test updateprops here app.state.users[1].name = "Mathieu"; await nextTick(); expect(fixture.innerHTML).toBe( '' ); }); test("slots are rendered with proper context, part 3", async () => { class Link extends Component { static template = xml` `; } class App extends Component { static template = xml`
  • `; state = useState({ users: [ { id: 1, name: "Aaron" }, { id: 2, name: "David" }, ], }); static components = { Link }; } const app = await mount(App, fixture); expect(fixture.innerHTML).toBe( '' ); // test updateprops here app.state.users[1].name = "Mathieu"; await nextTick(); expect(fixture.innerHTML).toBe( '' ); }); test("slots are rendered with proper context, part 4", async () => { class Link extends Component { static template = xml` `; } class App extends Component { static template = xml`
    `; static components = { Link }; state = useState({ user: { id: 1, name: "Aaron" } }); } const app = await mount(App, fixture); expect(fixture.innerHTML).toBe(''); // test updateprops here app.state.user.name = "David"; await nextTick(); expect(fixture.innerHTML).toBe(''); }); test("content is the default slot", async () => { class Dialog extends Component { static template = xml`
    `; } class Parent extends Component { static template = xml`
    sts rocks
    `; static components = { Dialog }; } await mount(Parent, fixture); expect(fixture.innerHTML).toBe("
    sts rocks
    "); }); test("content is the default slot (variation)", async () => { class Dialog extends Component { static template = xml``; } class Parent extends Component { static template = xml` sts rocks `; static components = { Dialog }; } await mount(Parent, fixture); expect(fixture.innerHTML).toBe("sts rocks"); }); test("default slot work with text nodes", async () => { class Dialog extends Component { static template = xml`
    `; } class Parent extends Component { static template = xml`
    sts rocks
    `; static components = { Dialog }; } await mount(Parent, fixture); expect(fixture.innerHTML).toBe("
    sts rocks
    "); }); test("default slot work with text nodes (variation)", async () => { class Dialog extends Component { static template = xml``; } class Parent extends Component { static template = xml`sts rocks`; static components = { Dialog }; } await mount(Parent, fixture); expect(fixture.innerHTML).toBe("sts rocks"); }); test("multiple roots are allowed in a named slot", async () => { class Dialog extends Component { static template = xml`
    `; } class Parent extends Component { static template = xml`
    sts rocks
    `; static components = { Dialog }; } await mount(Parent, fixture); expect(fixture.innerHTML).toBe("
    stsrocks
    "); }); test("multiple roots are allowed in a default slot", async () => { class Dialog extends Component { static template = xml`
    `; } class Parent extends Component { static template = xml`
    sts rocks
    `; static components = { Dialog }; } await mount(Parent, fixture); expect(fixture.innerHTML).toBe("
    stsrocks
    "); }); test("missing slots are ignored", async () => { class Dialog extends Component { static template = xml` some content `; } class Parent extends Component { static template = xml`
    `; static components = { Dialog }; } await mount(Parent, fixture); expect(fixture.innerHTML).toBe("
    some content
    "); }); test("t-debug on a t-set-slot (defining a slot)", async () => { const consoleLog = console.log; console.log = jest.fn(); class Dialog extends Component { static template = xml``; } class Parent extends Component { static template = xml`
    abc
    `; static components = { Dialog }; } await mount(Parent, fixture); expect(console.log).toHaveBeenCalledTimes(0); console.log = consoleLog; }); test("slot preserves properly parented relationship", async () => { class Child extends Component { static template = xml``; } class GrandChild extends Component { static template = xml`Grand Child`; } class Parent extends Component { static template = xml`
    `; static components = { Child, GrandChild }; } const parent = await mount(Parent, fixture); expect(fixture.innerHTML).toBe("
    Grand Child
    "); const parentChildren = children(parent); expect(parentChildren.length).toBe(1); expect(parentChildren[0]).toBeInstanceOf(Child); const childrenChildren = children(parentChildren[0]); expect(childrenChildren.length).toBe(1); expect(childrenChildren[0]).toBeInstanceOf(GrandChild); }); test("slot preserves properly parented relationship, even through t-call", async () => { class Child extends Component { static template = xml``; } class GrandChild extends Component { static template = xml`Grand Child`; } class Parent extends Component { static template = xml`
    `; static components = { Child, GrandChild }; } // throw new Error("boom") const parent = await mount(Parent, fixture, { templates: ` `, }); expect(fixture.innerHTML).toBe("
    Grand Child
    "); const parentChildren = children(parent); expect(parentChildren.length).toBe(1); expect(parentChildren[0]).toBeInstanceOf(Child); const childrenChildren = children(parentChildren[0]); expect(childrenChildren.length).toBe(1); expect(childrenChildren[0]).toBeInstanceOf(GrandChild); }); test("t-slot scope context", async () => { expect.assertions(4); class Wrapper extends Component { static template = xml``; } let dialog: any; class Dialog extends Component { static template = xml`
    `; static components = { Wrapper }; setup() { dialog = this; } onClick() { // we do not use expect(this).toBe(dialog) here because if it fails, it // may blow up jest because it then tries to compute a diff, which is // infinite if there is a cycle expect(this === dialog).toBe(true); } } class Parent extends Component { static template = xml` `; static components = { Dialog }; } await mount(Parent, fixture); fixture.querySelector("button")!.click(); await nextTick(); }); test("t-slot in recursive templates", async () => { class Wrapper extends Component { static template = xml` `; } class Parent extends Component { static template = "_test_recursive_template"; static components = { Wrapper }; name = "foo"; items = [ { name: "foo-0", children: [ { name: "foo-00", children: [] }, { name: "foo-01", children: [ { name: "foo-010", children: [] }, { name: "foo-011", children: [] }, { name: "foo-012", children: [ { name: "foo-0120", children: [] }, { name: "foo-0121", children: [] }, { name: "foo-0122", children: [] }, ], }, ], }, { name: "foo-02", children: [] }, ], }, { name: "foo-1", children: [] }, { name: "foo-2", children: [] }, ]; } const recursiveTemplate = ` `; const app = new App(Parent); app.addTemplate("_test_recursive_template", recursiveTemplate); await app.mount(fixture); expect(fixture.innerHTML).toBe( "foofoo-0foo-00foo-01foo-010foo-011foo-012foo-0120foo-0121foo-0122foo-02foo-1foo-2" ); }); test("t-slot within dynamic t-call", async () => { class Child extends Component { static template = xml`
    `; } class Slotted extends Component { static template = xml`
    `; } class UsingTcallInSlotted extends Component { tcallTemplate = "sometemplate"; static template = xml`
    `; static components = { Slotted, Child }; } const parent = await mount(UsingTcallInSlotted, fixture, { templates: `
    `, }); expect(parent).toBeInstanceOf(UsingTcallInSlotted); expect(children(parent).length).toBe(1); const slotted = children(parent)[0]; expect(slotted).toBeInstanceOf(Slotted); expect(children(slotted).length).toBe(1); const child = children(slotted)[0]; expect(child).toBeInstanceOf(Child); expect(fixture.innerHTML).toBe( `
    ` ); }); test("slots in t-foreach in t-foreach", async () => { class Child extends Component { static template = xml`
    `; } class App extends Component { static template = xml`
    `; static components = { Child }; tree = [ { key: "a", value: "A", nodes: [ { key: "1", value: "A-1", }, { key: "2", value: "A-2", }, ], }, { key: "b", value: "B", nodes: [ { key: "1", value: "B-1", }, { key: "2", value: "B-2", }, ], }, ]; } await mount(App, fixture); expect(fixture.innerHTML).toBe( "
    A
    • A
    • A
    B
    • B
    • B
    " ); }); test("default slot next to named slot, with default content", async () => { class Dialog extends Component { // We're using 2 slots here: a "default" one and a "footer", // both having default children nodes. static template = xml`
    Default content
    `; } class App extends Component { // Here we're trying to assign the "footer" slot with some content static components = { Dialog }; static template = xml`
    Overridden footer
    `; } await mount(App, fixture); expect(fixture.innerHTML).toBe( '
    Default content
    ' ); }); test("dynamic t-slot call", async () => { class Toggler extends Component { static template = xml``; current = useState({ slot: "slot1" }); toggle() { this.current.slot = this.current.slot === "slot1" ? "slot2" : "slot1"; } } class Parent extends Component { static template = xml`

    slot1

    content

    slot2

    `; static components = { Toggler }; } await mount(Parent, fixture); expect(fixture.innerHTML).toBe("
    "); fixture.querySelector("button")!.click(); await nextTick(); expect(fixture.innerHTML).toBe("
    "); fixture.querySelector("button")!.click(); await nextTick(); expect(fixture.innerHTML).toBe("
    "); }); test("dynamic t-slot call with default", async () => { class Toggler extends Component { static template = xml` `; current = useState({ slot: "slot1" }); toggle() { this.current.slot = this.current.slot === "slot1" ? "slot2" : "slot1"; } } class Parent extends Component { static template = xml`

    slot1

    content

    slot2

    `; static components = { Toggler }; } await mount(Parent, fixture); expect(fixture.innerHTML).toBe("
    "); fixture.querySelector("button")!.click(); await nextTick(); expect(fixture.innerHTML).toBe("
    "); }); test("slot are properly rendered if inner props are changed", async () => { class SomeComponent extends Component { static template = xml`
    SC:
    `; } class GenericComponent extends Component { static template = xml`
    `; } class App extends Component { static template = xml`
    `; static components = { GenericComponent, SomeComponent }; state = useState({ val: 4 }); inc() { this.state.val++; } } await mount(App, fixture); expect(fixture.innerHTML).toBe("
    SC:4
    "); (fixture.querySelector("button")).click(); await nextTick(); expect(fixture.innerHTML).toBe("
    SC:5
    "); }); test("slots and wrapper components", async () => { class Link extends Component { static template = xml` `; } class A extends Component { static template = xml`hey`; static components = { Link: Link }; } await mount(A, fixture); expect(fixture.innerHTML).toBe(`hey`); }); test("template can just return a slot", async () => { class Child extends Component { static template = xml``; } class SlotComponent extends Component { static template = xml``; } class Parent extends Component { static template = xml`
    `; static components = { SlotComponent, Child }; state = useState({ value: 3 }); } const parent = await mount(Parent, fixture); expect(fixture.innerHTML).toBe("
    3
    "); parent.state.value = 5; await nextTick(); expect(fixture.innerHTML).toBe("
    5
    "); }); test("multiple slots containing components", async () => { class C extends Component { static template = xml``; } class B extends Component { static template = xml`
    `; } class A extends Component { static template = xml` `; static components = { B, C }; } await mount(A, fixture); expect(fixture.innerHTML).toBe(`
    12
    `); }); test("slots in t-foreach and re-rendering", async () => { class Child extends Component { static template = xml``; state = useState({ val: "A" }); setup() { onMounted(() => { this.state.val = "B"; }); } } class Parent extends Component { static components = { Child }; static template = xml`
    `; } await mount(Parent, fixture); expect(fixture.innerHTML).toBe("
    A0A1
    "); await nextTick(); // wait for the changes triggered in mounted to be applied expect(fixture.innerHTML).toBe("
    B0B1
    "); }); test("slots in t-foreach with t-set and re-rendering", async () => { class Child extends Component { static template = xml` `; state = useState({ val: "A" }); setup() { onMounted(() => { this.state.val = "B"; }); } } class ParentWidget extends Component { static components = { Child }; static template = xml`
    `; } await mount(ParentWidget, fixture); expect(fixture.innerHTML).toBe("
    A0A1
    "); await nextTick(); // wait for changes triggered in mounted to be applied expect(fixture.innerHTML).toBe("
    B0B1
    "); }); test("nested slots in same template", async () => { let child: any = null; let child2: any = null; let child3: any = null; class Child extends Component { static template = xml`
    `; setup() { child = this; } } class Child2 extends Component { static template = xml` `; setup() { child2 = this; } } class Child3 extends Component { static template = xml` Child 3`; setup() { child3 = this; } } class Parent extends Component { static components = { Child, Child2, Child3 }; static template = xml` `; } const parent = await mount(Parent, fixture); expect(fixture.innerHTML).toBe( '
    Child 3
    ' ); expect(children(child2)).toEqual([child3]); expect(children(child)).toEqual([child2]); expect(children(parent)).toEqual([child]); }); test("t-slot nested within another slot", async () => { let portal: any = null; let modal: any = null; let child3: any = null; class Child3 extends Component { static template = xml`Child 3`; setup() { child3 = this; } } class Modal extends Component { static template = xml``; setup() { modal = this; } } class Portal extends Component { static template = xml``; setup() { portal = this; } } class Dialog extends Component { static components = { Modal, Portal }; static template = xml` `; } class Parent extends Component { static components = { Child3, Dialog }; static template = xml` `; } await mount(Parent, fixture); expect(fixture.innerHTML).toBe( 'Child 3' ); expect(children(portal)[0]).toBe(child3); expect(children(modal)[0]).toBe(portal); }); test("slots in slots, with vars", async () => { class B extends Component { static template = xml``; } class A extends Component { static template = xml`
    `; static components = { B }; } class Parent extends Component { static template = xml` `; static components = { A }; state = { name: "aaron" }; } await mount(Parent, fixture); expect(fixture.innerHTML).toBe("

    heyaaron

    "); }); test("t-set t-value in a slot", async () => { class Dialog extends Component { static template = xml` `; } class Parent extends Component { static template = xml`
    `; static components = { Dialog }; } await mount(Parent, fixture); expect(fixture.innerHTML).toBe("
    dash
    "); }); test("slot and t-esc", async () => { class Dialog extends Component { static template = xml``; } class Parent extends Component { static template = xml`
    `; static components = { Dialog }; } await mount(Parent, fixture); expect(fixture.innerHTML).toBe("
    toph
    "); }); test("slot and t-call", async () => { let sokka = xml`

    sokka

    `; class Dialog extends Component { static template = xml``; } class Parent extends Component { static template = xml`
    `; static components = { Dialog }; } await mount(Parent, fixture); expect(fixture.innerHTML).toBe("

    sokka

    "); }); test("slot and (inline) t-call", async () => { let sokka = xml`

    sokka

    `; class Dialog extends Component { static template = xml``; } class Parent extends Component { static template = xml`
    `; static components = { Dialog }; } await mount(Parent, fixture); expect(fixture.innerHTML).toBe("

    sokka

    "); }); test("nested slots: evaluation context and parented relationship", async () => { let slot: any = null; let grandChild: any = null; class Slot extends Component { static template = xml``; setup() { slot = this; } } class GrandChild extends Component { static template = xml`
    `; setup() { grandChild = this; } } class Child extends Component { static components = { GrandChild }; static template = xml` `; } class Parent extends Component { static components = { Child, Slot }; static template = xml``; state = useState({ val: 3 }); } await mount(Parent, fixture); expect(fixture.innerHTML).toBe("
    3
    "); expect(children(grandChild)).toEqual([slot]); }); test("named slot inside slot", async () => { class Child extends Component { static template = xml`
    `; } class Parent extends Component { static template = xml`

    A

    B

    `; static components = { Child }; value = "blip"; } await mount(Parent, fixture); expect(fixture.innerHTML).toBe("

    Ablip

    Bblip

    "); }); test("named slots inside slot, again", async () => { class Child extends Component { static template = xml` default1 default2 `; } class Parent extends Component { static template = xml`

    A

    B

    `; static components = { Child }; value = "blip"; } await mount(Parent, fixture); expect(fixture.innerHTML).toBe( "

    Ablip

    default2default1

    Bblip

    " ); }); test("named slot inside slot, part 3", async () => { class Child extends Component { static template = xml`
    `; } class Parent extends Component { static template = xml`

    A

    B

    `; static components = { Child }; value = "blip"; } await mount(Parent, fixture); expect(fixture.innerHTML).toBe("

    Ablip

    Bblip

    "); }); test("named slot inside named slot in t-component", async () => { class Child extends Component { static template = xml``; } class Parent extends Component { static template = xml` outer `; static components = { Child }; Child = Child; value = "inner"; } await mount(Parent, fixture); expect(fixture.innerHTML).toBe(" outer inner"); }); test("can render only empty slot", async () => { class Parent extends Component { static template = xml``; } let error = null; try { await mount(Parent, fixture); } catch (e) { error = e; } expect(error).toBeNull(); expect(fixture.innerHTML).toEqual(""); }); test("can render node with t-ref and Component in same slot", async () => { class Child extends Component { static template = xml``; } class Parent extends Component { static template = xml`
    `; static components = { Child }; } let error = null; try { await mount(Parent, fixture); } catch (e) { error = e; } expect(error).toBeNull(); }); test("can use t-call in default-content of t-slot", async () => { const template = xml``; class Child extends Component { static template = xml``; } class Parent extends Component { static template = xml``; static components = { Child }; } await mount(Parent, fixture); }); test("can use component in default-content of t-slot", async () => { class GrandChild extends Component { static template = xml``; } class Child extends Component { static template = xml``; static components = { GrandChild }; } class Parent extends Component { static template = xml``; static components = { Child }; } await mount(Parent, fixture); }); test("slot content has different key from other content -- static slot", async () => { class Child extends Component { static template = xml`
    `; } class SlotDisplay extends Component { static components = { Child }; static template = xml``; } class Parent extends Component { static components = { Child, SlotDisplay }; static template = xml` `; } await mount(Parent, fixture); expect(fixture.innerHTML).toBe("
    SlotDisplay
    Parent
    "); }); test("slot content has different key from other content -- dynamic slot", async () => { class Child extends Component { static template = xml`
    `; } class SlotDisplay extends Component { static components = { Child }; slotName = "default"; static template = xml``; } class Parent extends Component { static components = { Child, SlotDisplay }; static template = xml` `; } await mount(Parent, fixture); expect(fixture.innerHTML).toBe("
    SlotDisplay
    Parent
    "); }); test("mix of slots, t-call, t-call with body, and giving own props child", async () => { expect.assertions(11); class C extends Component { static template = xml`[C]`; } class B extends Component { static template = xml`[B]`; static components = { C }; } const subTemplate2 = xml`[sub2]`; const subTemplate1 = xml`[sub1] `; let a: any; class A extends Component { static components = { B }; static template = xml`[A]`; setup() { a = this; } get validate() { // we check here that the actual component was not lost somehow expect(this.__owl__.component === a).toBe(true); return 1; } } class P extends Component { static components = { A }; static template = xml``; state = useState({ number: 333 }); inc() { this.state.number++; } } class Parent extends Component { static template = xml`

    `; static components = { P }; } await mount(Parent, fixture); expect(fixture.innerHTML).toBe("[B][C][A][sub1] [sub2333]"); fixture.querySelector("button")!.click(); await nextTick(); expect(fixture.innerHTML).toBe("[B][C][A][sub1] [sub2334]"); }); test("slot in multiple locations", async () => { class Child extends Component { static template = xml`

    child
    `; } class Slotter extends Component { static components = { Child }; static template = xml`

    `; } class Parent extends Component { static components = { Child, Slotter }; static template = xml` hello `; state = useState({ location: 1 }); } const parent = await mount(Parent, fixture); expect(fixture.innerHTML).toBe("

    hello

    child

    "); parent.state.location = 2; await nextTick(); expect(fixture.innerHTML).toBe(" hello
    child
    "); }); test("dynamic slot in multiple locations", async () => { class Child extends Component { static template = xml`
    child
    `; } class Slotter extends Component { static components = { Child }; static template = xml`

    `; } class Parent extends Component { static components = { Child, Slotter }; static template = xml` hello `; state = useState({ location: 1 }); } const parent = await mount(Parent, fixture); expect(fixture.innerHTML).toBe("

    hello

    child

    "); parent.state.location = 2; await nextTick(); expect(fixture.innerHTML).toBe("hello
    child
    "); }); test("slot in t-foreach locations", async () => { class Child extends Component { static template = xml`
    child
    `; } class Slotter extends Component { static components = { Child }; static template = xml`

    `; } class Parent extends Component { static components = { Child, Slotter }; static template = xml` hello `; state = useState({ list: [1] }); } const parent = await mount(Parent, fixture); expect(fixture.innerHTML).toBe("

    1 hello

    child

    "); parent.state.list.push(2); await nextTick(); expect(fixture.innerHTML).toBe( "

    1 hello

    child

    2 hello

    child

    " ); }); });