import { App, Component, mount, onWillStart, useState, xml } from "../../src";
import { status } from "../../src/runtime/status";
import {
makeTestFixture,
snapshotEverything,
nextTick,
elem,
useLogLifecycle,
makeDeferred,
nextMicroTick,
steps,
} from "../helpers";
let fixture: HTMLElement;
snapshotEverything();
beforeEach(() => {
fixture = makeTestFixture();
});
describe("app", () => {
test("destroy remove the widget from the DOM", async () => {
class SomeComponent extends Component {
static template = xml`
`;
}
const app = new App(SomeComponent);
const comp = await app.mount(fixture);
const el = elem(comp);
expect(document.contains(el)).toBe(true);
app.destroy();
expect(document.contains(el)).toBe(false);
expect(status(comp)).toBe("destroyed");
});
test("App supports env with getters/setters", async () => {
let someVal = "maggot";
const services: any = { serv1: "" };
const env = {
get someVal() {
return someVal;
},
services,
};
class SomeComponent extends Component {
static template = xml`
`;
}
const app = new App(SomeComponent, { env });
const comp = await app.mount(fixture);
expect(fixture.innerHTML).toBe("maggot serv1
");
someVal = "brain";
services.serv2 = "";
comp.render();
await nextTick();
expect(fixture.innerHTML).toBe("brain serv1,serv2
");
});
test("can configure an app with props", async () => {
class SomeComponent extends Component {
static template = xml``;
}
const app = new App(SomeComponent, { props: { value: 333 } });
await app.mount(fixture);
expect(fixture.innerHTML).toBe("333
");
});
test("warnIfNoStaticProps works as expected", async () => {
let originalconsoleWarn = console.warn;
let mockConsoleWarn = jest.fn(() => {});
console.warn = mockConsoleWarn;
class Root extends Component {
static template = xml``;
}
await mount(Root, fixture, { test: true, props: { messge: "hey" }, warnIfNoStaticProps: true });
console.warn = originalconsoleWarn;
expect(mockConsoleWarn).toBeCalledWith(
"Component 'Root' does not have a static props description"
);
});
test("can mount app in an iframe", async () => {
class SomeComponent extends Component {
static template = xml``;
}
const iframe = document.createElement("iframe");
fixture.appendChild(iframe);
const app = new App(SomeComponent);
const iframeDoc = iframe.contentDocument!;
const comp = await app.mount(iframeDoc.body);
const div = iframeDoc.querySelector(".my-div");
expect(div).not.toBe(null);
expect(iframeDoc.contains(div)).toBe(true);
app.destroy();
expect(iframeDoc.contains(div)).toBe(false);
expect(status(comp)).toBe("destroyed");
});
test("app: clear scheduler tasks and destroy cancelled nodes immediately on destroy", async () => {
let def = makeDeferred();
class B extends Component {
static template = xml`B`;
setup() {
useLogLifecycle();
onWillStart(() => def);
}
}
class A extends Component {
static template = xml`A`;
static components = { B };
state = useState({ value: false });
setup() {
useLogLifecycle();
}
}
const app = new App(A);
const comp = await app.mount(fixture);
expect(steps.splice(0)).toMatchInlineSnapshot(`
Array [
"A:setup",
"A:willStart",
"A:willRender",
"A:rendered",
"A:mounted",
]
`);
comp.state.value = true;
await nextTick();
expect(steps.splice(0)).toMatchInlineSnapshot(`
Array [
"A:willRender",
"B:setup",
"B:willStart",
"A:rendered",
]
`);
// rerender to force the instantiation of a new B component (and cancelling the first)
comp.render();
await nextMicroTick();
expect(steps.splice(0)).toMatchInlineSnapshot(`
Array [
"A:willRender",
"B:setup",
"B:willStart",
"A:rendered",
]
`);
app.destroy();
expect(steps.splice(0)).toMatchInlineSnapshot(`
Array [
"A:willUnmount",
"B:willDestroy",
"A:willDestroy",
"B:willDestroy",
]
`);
});
});