mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
9b1ec5dc0e
This commit moves OwlError to its own file in the new common folder such that it is no longer associated with the runtime.
285 lines
7.4 KiB
TypeScript
285 lines
7.4 KiB
TypeScript
import { diff } from "jest-diff";
|
|
import {
|
|
blockDom,
|
|
Component,
|
|
onMounted,
|
|
onPatched,
|
|
onRendered,
|
|
onWillDestroy,
|
|
onWillPatch,
|
|
onWillRender,
|
|
onWillStart,
|
|
onWillUnmount,
|
|
onWillUpdateProps,
|
|
status,
|
|
useComponent,
|
|
xml,
|
|
} from "../src";
|
|
import { helpers } from "../src/runtime/template_helpers";
|
|
import { TemplateSet, globalTemplates } from "../src/runtime/template_set";
|
|
import { BDom } from "../src/runtime/blockdom";
|
|
import { compile } from "../src/compiler";
|
|
import { OwlError } from "../src/common/owl_error";
|
|
|
|
const mount = blockDom.mount;
|
|
|
|
export function nextMicroTick(): Promise<void> {
|
|
return Promise.resolve();
|
|
}
|
|
|
|
let lastFixture: any = null;
|
|
|
|
export function makeTestFixture() {
|
|
let fixture = document.createElement("div");
|
|
document.body.appendChild(fixture);
|
|
if (lastFixture) {
|
|
lastFixture.remove();
|
|
}
|
|
lastFixture = fixture;
|
|
return fixture;
|
|
}
|
|
|
|
beforeEach(() => {
|
|
xml.nextId = 999;
|
|
});
|
|
export async function nextTick(): Promise<void> {
|
|
await new Promise((resolve) => setTimeout(resolve));
|
|
await new Promise((resolve) => requestAnimationFrame(resolve));
|
|
}
|
|
|
|
interface Deferred extends Promise<any> {
|
|
resolve(val?: any): void;
|
|
reject(val?: any): void;
|
|
}
|
|
|
|
export function makeDeferred(): Deferred {
|
|
let resolve, reject;
|
|
let def = new Promise((_resolve, _reject) => {
|
|
resolve = _resolve;
|
|
reject = _reject;
|
|
});
|
|
(def as any).resolve = resolve;
|
|
(def as any).reject = reject;
|
|
return <Deferred>def;
|
|
}
|
|
|
|
export function trim(str: string): string {
|
|
return str.replace(/\s/g, "");
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Helpers
|
|
// -----------------------------------------------------------------------------
|
|
|
|
let shouldSnapshot = false;
|
|
let snapshottedTemplates: Set<string> = new Set();
|
|
|
|
export function snapshotTemplate(template: string) {
|
|
const fn = compile(template);
|
|
expect(fn.toString()).toMatchSnapshot();
|
|
}
|
|
|
|
export function renderToBdom(template: string, context: any = {}, node?: any): BDom {
|
|
if (!node) {
|
|
if (!context.__owl__) {
|
|
context.__owl__ = { component: context };
|
|
} else {
|
|
context.__owl__.component = context;
|
|
}
|
|
}
|
|
const fn = compile(template);
|
|
if (shouldSnapshot && !snapshottedTemplates.has(template)) {
|
|
snapshottedTemplates.add(template);
|
|
expect(fn.toString()).toMatchSnapshot();
|
|
}
|
|
const app = {
|
|
createComponent() {},
|
|
createDynamicComponent() {},
|
|
};
|
|
return fn(app as any, blockDom, helpers).call(context, context, node);
|
|
}
|
|
|
|
export function renderToString(template: string, context: any = {}, node?: any): string {
|
|
const fixture = makeTestFixture();
|
|
const bdom = renderToBdom(template, context, node);
|
|
mount(bdom, fixture);
|
|
return fixture.innerHTML;
|
|
}
|
|
|
|
export class TestContext extends TemplateSet {
|
|
renderToString(name: string, context: any = {}): string {
|
|
const renderFn = this.getTemplate(name);
|
|
const bdom = renderFn(context, {});
|
|
const fixture = makeTestFixture();
|
|
mount(bdom, fixture);
|
|
return fixture.innerHTML;
|
|
}
|
|
}
|
|
|
|
export function snapshotEverything() {
|
|
if (shouldSnapshot) {
|
|
// this function has already been called
|
|
return;
|
|
}
|
|
const globalTemplateNames = new Set(Object.keys(globalTemplates));
|
|
shouldSnapshot = true;
|
|
beforeEach(() => {
|
|
snapshottedTemplates.clear();
|
|
});
|
|
|
|
const originalCompileTemplate = TemplateSet.prototype._compileTemplate;
|
|
TemplateSet.prototype._compileTemplate = function (name: string, template: string | Element) {
|
|
const fn = originalCompileTemplate.call(this, "", template);
|
|
if (!globalTemplateNames.has(name)) {
|
|
expect(fn.toString()).toMatchSnapshot();
|
|
}
|
|
return fn;
|
|
};
|
|
}
|
|
|
|
const steps: string[] = [];
|
|
|
|
export function logStep(step: string) {
|
|
steps.push(step);
|
|
}
|
|
export function useLogLifecycle(key?: string, skipAsyncHooks: boolean = false) {
|
|
const component = useComponent();
|
|
let name = component.constructor.name;
|
|
if (key) {
|
|
name = `${name} (${key})`;
|
|
}
|
|
logStep(`${name}:setup`);
|
|
expect(name + ": " + status(component)).toBe(name + ": " + "new");
|
|
|
|
if (!skipAsyncHooks) {
|
|
onWillStart(() => {
|
|
expect(name + ": " + status(component)).toBe(name + ": " + "new");
|
|
logStep(`${name}:willStart`);
|
|
});
|
|
}
|
|
|
|
onMounted(() => {
|
|
expect(name + ": " + status(component)).toBe(name + ": " + "mounted");
|
|
logStep(`${name}:mounted`);
|
|
});
|
|
|
|
if (!skipAsyncHooks) {
|
|
onWillUpdateProps(() => {
|
|
expect(name + ": " + status(component)).toBe(name + ": " + "mounted");
|
|
logStep(`${name}:willUpdateProps`);
|
|
});
|
|
}
|
|
|
|
onWillRender(() => {
|
|
logStep(`${name}:willRender`);
|
|
});
|
|
|
|
onRendered(() => {
|
|
logStep(`${name}:rendered`);
|
|
});
|
|
|
|
onWillPatch(() => {
|
|
expect(name + ": " + status(component)).toBe(name + ": " + "mounted");
|
|
logStep(`${name}:willPatch`);
|
|
});
|
|
|
|
onPatched(() => {
|
|
expect(name + ": " + status(component)).toBe(name + ": " + "mounted");
|
|
logStep(`${name}:patched`);
|
|
});
|
|
|
|
onWillUnmount(() => {
|
|
expect(name + ": " + status(component)).toBe(name + ": " + "mounted");
|
|
logStep(`${name}:willUnmount`);
|
|
});
|
|
|
|
onWillDestroy(() => {
|
|
expect(status(component)).not.toBe("destroyed");
|
|
logStep(`${name}:willDestroy`);
|
|
});
|
|
}
|
|
|
|
export function children(w: Component): Component[] {
|
|
const childrenMap = w.__owl__.children;
|
|
return Object.keys(childrenMap).map((id) => childrenMap[id].component);
|
|
}
|
|
|
|
export function isDirectChildOf(child: Component, parent: Component): boolean {
|
|
return children(parent).includes(child);
|
|
}
|
|
|
|
export function elem(component: Component): any {
|
|
return component.__owl__.firstNode();
|
|
}
|
|
|
|
export async function editInput(input: HTMLInputElement | HTMLTextAreaElement, value: string) {
|
|
input.value = value;
|
|
input.dispatchEvent(new Event("input"));
|
|
input.dispatchEvent(new Event("change"));
|
|
return nextTick();
|
|
}
|
|
|
|
afterEach(() => {
|
|
if (steps.length) {
|
|
steps.splice(0);
|
|
throw new OwlError("Remaining steps! Should be checked by a .toBeLogged() assertion!");
|
|
}
|
|
});
|
|
|
|
expect.extend({
|
|
toBeLogged(expected) {
|
|
const options = {
|
|
comment: "steps equality",
|
|
isNot: this.isNot,
|
|
promise: this.promise,
|
|
};
|
|
|
|
const currentSteps = steps.splice(0);
|
|
const pass = this.equals(currentSteps, expected);
|
|
|
|
const message = pass
|
|
? () =>
|
|
this.utils.matcherHint("toEqual", undefined, undefined, options) +
|
|
"\n\n" +
|
|
`Expected: not ${this.utils.printExpected(expected)}\n` +
|
|
`Received: ${this.utils.printReceived(currentSteps)}`
|
|
: () => {
|
|
const diffString = diff(expected, currentSteps, {
|
|
expand: this.expand,
|
|
});
|
|
return (
|
|
this.utils.matcherHint("toBe", undefined, undefined, options) +
|
|
"\n\n" +
|
|
(diffString && diffString.includes("- Expect")
|
|
? `Difference:\n\n${diffString}`
|
|
: `Expected: ${this.utils.printExpected(expected)}\n` +
|
|
`Received: ${this.utils.printReceived(currentSteps)}`)
|
|
);
|
|
};
|
|
|
|
return { actual: currentSteps, message, pass };
|
|
},
|
|
});
|
|
|
|
export function nextAppError(app: any) {
|
|
const { handleError } = app;
|
|
return new Promise((resolve) => {
|
|
app.handleError = (...args: Parameters<typeof handleError>) => {
|
|
try {
|
|
handleError.call(app, ...args);
|
|
} catch (e: any) {
|
|
app.handleError = handleError;
|
|
resolve(e);
|
|
}
|
|
};
|
|
});
|
|
}
|
|
|
|
declare global {
|
|
namespace jest {
|
|
interface Matchers<R> {
|
|
toBeLogged(): R;
|
|
}
|
|
}
|
|
}
|