import { OwlError } from "../../src/common/owl_error";
import { App, Component, mount, onMounted, useState, xml } from "../../src";
import { makeTestFixture, nextAppError, nextTick, snapshotEverything } from "../helpers";
snapshotEverything();
let fixture: HTMLElement;
let originalconsoleWarn = console.warn;
let mockConsoleWarn: any;
beforeEach(() => {
fixture = makeTestFixture();
mockConsoleWarn = jest.fn(() => {});
console.warn = mockConsoleWarn;
});
afterEach(() => {
console.warn = originalconsoleWarn;
});
describe("style and class handling", () => {
test("can set style and class inside component", async () => {
class Test extends Component {
static template = xml`
world
`;
}
await mount(Test, fixture);
expect(fixture.innerHTML).toBe(`world
`);
});
test("can set class on sub component, as prop", async () => {
class Child extends Component {
static template = xml`child
`;
}
class Parent extends Component {
static template = xml``;
static components = { Child };
}
await mount(Parent, fixture);
expect(fixture.innerHTML).toBe(`child
`);
});
test("no class is set is parent does not give it as prop", async () => {
class Child extends Component {
static template = xml`child
`;
}
class Parent extends Component {
static template = xml``;
static components = { Child };
}
await mount(Parent, fixture);
expect(fixture.innerHTML).toBe(`child
`);
});
test("no class is set is child ignores it", async () => {
class Child extends Component {
static template = xml`child
`;
}
class Parent extends Component {
static template = xml``;
static components = { Child };
}
await mount(Parent, fixture);
expect(fixture.innerHTML).toBe(`child
`);
});
test("empty class attribute is not added on widget root el", 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(`
`);
});
test("can set more than one class on sub component", async () => {
class Child extends Component {
static template = xml`child
`;
}
class Parent extends Component {
static template = xml``;
static components = { Child };
}
await mount(Parent, fixture);
expect(fixture.innerHTML).toBe(`child
`);
});
test("component class and parent class combine together", async () => {
class Child extends Component {
static template = xml`child
`;
}
class Parent extends Component {
static template = xml``;
static components = { Child };
}
await mount(Parent, fixture);
expect(fixture.innerHTML).toBe(`child
`);
});
test("can set class on sub sub component", async () => {
class ChildChild extends Component {
static template = xml`childchild
`;
}
class Child extends Component {
static template = xml``;
static components = { ChildChild };
}
class Parent extends Component {
static template = xml``;
static components = { Child };
}
await mount(Parent, fixture);
expect(fixture.innerHTML).toBe(`childchild
`);
});
test("can set class on multi root component", async () => {
class Child extends Component {
static template = xml`a
b`;
}
class Parent extends Component {
static template = xml``;
static components = { Child };
}
await mount(Parent, fixture);
expect(fixture.innerHTML).toBe(`a
b`);
});
test("class on sub component, which is switched to another", async () => {
class ChildA extends Component {
static template = xml`a
`;
}
class ChildB extends Component {
static template = xml`b`;
}
class Child extends Component {
static template = xml``;
static components = { ChildA, ChildB };
}
class Parent extends Component {
static template = xml``;
static components = { Child };
state = useState({ child: "a" });
}
const parent = await mount(Parent, fixture);
expect(fixture.innerHTML).toBe(`a
`);
parent.state.child = "b";
await nextTick();
expect(fixture.innerHTML).toBe(`b`);
});
// test("t-att-class is properly added/removed on widget root el", async () => {
// class Child extends Component {
// static template = xml``;
// }
// class Parent extends Component {
// static template = xml`
`;
// static components = { Child };
// state = useState({ a: true, b: false });
// }
// const widget = await mount(Parent, fixture);
// expect(fixture.innerHTML).toBe(``);
// widget.state.a = false;
// widget.state.b = true;
// await nextTick();
// expect(fixture.innerHTML).toBe(``);
// });
test("class with extra whitespaces", 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(``);
});
test("class with extra whitespaces (variation)", 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(``);
});
// TODO: adapt name (t-att-class no longer makes sense on Components)
test("t-att-class is properly added/removed on widget root el (v2)", async () => {
let child: Child;
class Child extends Component {
static template = xml``;
state = useState({ d: true });
setup() {
child = this;
}
}
class Parent extends Component {
static template = xml`
`;
static components = { Child };
state = useState({ b: true });
}
const widget = await mount(Parent, fixture);
const span = fixture.querySelector("span")!;
expect(span.className).toBe("c d b");
widget.state.b = false;
await nextTick();
expect(span.className).toBe("c d");
child!.state.d = false;
await nextTick();
expect(span.className).toBe("c");
widget.state.b = true;
await nextTick();
expect(span.className).toBe("c b");
child!.state.d = true;
await nextTick();
expect(span.className).toBe("c b d");
});
// TODO: adapt name (t-att-class no longer makes sense on Components)
test("t-att-class is properly added/removed on widget root el (v3)", async () => {
let child: Child;
class Child extends Component {
static template = xml``;
state = useState({ d: true });
setup() {
child = this;
}
}
class Parent extends Component {
static template = xml``;
static components = { Child };
state = useState({ b: true });
}
const widget = await mount(Parent, fixture);
const span = fixture.querySelector("span")!;
expect(span.className).toBe("c d a b");
widget.state.b = false;
await nextTick();
expect(span.className).toBe("c d a");
child!.state.d = false;
await nextTick();
expect(span.className).toBe("c a");
widget.state.b = true;
await nextTick();
expect(span.className).toBe("c a b");
child!.state.d = true;
await nextTick();
expect(span.className).toBe("c a b d");
});
test("class on components do not interfere with user defined classes", async () => {
class App extends Component {
static template = xml``;
state = useState({ c: true });
setup() {
onMounted(() => {
fixture.querySelector("div")!.classList.add("user");
});
}
}
const widget = await mount(App, fixture);
expect(fixture.innerHTML).toBe('');
widget.state.c = false;
await nextTick();
expect(fixture.innerHTML).toBe('');
});
// TODO: adapt name
test("style is properly added on widget root el", async () => {
class SomeComponent extends Component {
static template = xml``;
}
class ParentWidget extends Component {
static components = { Child: SomeComponent };
static template = xml``;
}
await mount(ParentWidget, fixture);
expect(fixture.innerHTML).toBe(``);
});
// TODO: adapt name
// TODO: t-att-style as object (like class)
test.skip("dynamic t-att-style is properly added and updated on widget root el", async () => {
class SomeComponent extends Component {
static template = xml``;
}
class ParentWidget extends Component {
static template = xml``;
static components = { Child: SomeComponent };
state = useState({ style: { "font-size": "20px" } });
}
const widget = await mount(ParentWidget, fixture);
expect(fixture.innerHTML).toBe(``);
widget.state.style["font-size"] = "30px";
await nextTick();
expect(fixture.innerHTML).toBe(``);
});
// TODO: does this test need to be moved? (class now a standard prop)
test("error in subcomponent with class", async () => {
class Child extends Component {
static template = xml``;
}
class Parent extends Component {
static template = xml``;
static components = { Child };
}
let error: OwlError;
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!).toBeDefined();
expect(error!.cause).toBeDefined();
const regexp =
/Cannot read properties of undefined \(reading 'crash'\)|Cannot read property 'crash' of undefined/g;
expect(error!.cause.message).toMatch(regexp);
expect(fixture.innerHTML).toBe("");
expect(mockConsoleWarn).toBeCalledTimes(1);
});
});