Odoo18-Base/addons/web/static/tests/env.test.js
2025-01-06 10:57:38 +07:00

236 lines
6.9 KiB
JavaScript

import { after, beforeEach, describe, expect, getFixture, test } from "@odoo/hoot";
import { Deferred, tick } from "@odoo/hoot-mock";
import { Component, xml } from "@odoo/owl";
import { clearRegistry, makeMockEnv, patchTranslations } from "@web/../tests/web_test_helpers";
import { registry } from "@web/core/registry";
import { makeEnv, mountComponent, startServices } from "@web/env";
describe.current.tags("headless");
const servicesRegistry = registry.category("services");
beforeEach(() => {
clearRegistry(servicesRegistry);
});
/**
* @param {string} name
* @param {string[]} dependencies
* @param {(env: import("@web/env").OdooEnv, dependencies: Record<string, any>) => any} factory
*/
function registerService(name, dependencies, factory) {
servicesRegistry.add(name, {
dependencies,
start: factory,
});
}
test(`can start a service`, async () => {
registerService("test", [], () => 17);
const env = await makeMockEnv();
expect(env.services.test).toBe(17);
});
test(`crashing service start causes startService to crash`, async () => {
registerService("ouch", [], () => {
throw new Error("boom");
});
await expect(makeMockEnv()).rejects.toThrow("boom");
});
test(`crashing async service start causes startService to crash`, async () => {
registerService("ouch", [], async () => {
throw new Error("boom");
});
await expect(makeMockEnv()).rejects.toThrow("boom");
});
test(`can start an asynchronous service`, async () => {
const deferred = new Deferred();
registerService("test", [], async () => {
expect.step("before");
const result = await deferred;
expect.step("after");
return result;
});
const envCreationPromise = makeMockEnv();
await tick(); // wait for startServices
expect.verifySteps(["before"]);
deferred.resolve(15);
const env = await envCreationPromise;
expect.verifySteps(["after"]);
expect(env.services.test).toBe(15);
});
test(`can start a service with a dependency`, async () => {
registerService("aang", ["appa"], () => expect.step("aang"));
registerService("appa", [], () => expect.step("appa"));
await makeMockEnv();
expect.verifySteps(["appa", "aang"]);
});
test(`get an object containing dependencies as second arg`, async () => {
registerService("aang", ["appa"], (_, dependencies) => {
expect.step("aang");
expect(dependencies).toEqual({ appa: "flying bison" });
});
registerService("appa", [], () => {
expect.step("appa");
return "flying bison";
});
await makeMockEnv();
expect.verifySteps(["appa", "aang"]);
});
test(`can start two sequentially dependant asynchronous services`, async () => {
const deferred2 = new Deferred();
registerService("test2", ["test1"], () => {
expect.step("test2");
return deferred2;
});
const deferred1 = new Deferred();
registerService("test1", [], () => {
expect.step("test1");
return deferred1;
});
registerService("test3", ["test2"], () => {
expect.step("test3");
});
const envCreationPromise = makeMockEnv();
await tick();
expect.verifySteps(["test1"]);
deferred2.resolve();
await tick();
expect.verifySteps([]);
deferred1.resolve();
await tick();
expect.verifySteps(["test2", "test3"]);
await envCreationPromise;
});
test(`can start two independant asynchronous services in parallel`, async () => {
const deferred1 = new Deferred();
registerService("test1", [], () => {
expect.step("test1");
return deferred1;
});
const deferred2 = new Deferred();
registerService("test2", [], () => {
expect.step("test2");
return deferred2;
});
registerService("test3", ["test1", "test2"], () => {
expect.step("test3");
});
const envCreationPromise = makeMockEnv();
await tick();
expect.verifySteps(["test1", "test2"]);
deferred1.resolve();
await tick();
expect.verifySteps([]);
deferred2.resolve();
await tick();
expect.verifySteps(["test3"]);
await envCreationPromise;
});
test(`startServices: throws if all dependencies are not met in the same microtick as the call`, async () => {
const env = makeEnv();
registerService("b", ["a"], () => "b");
const serviceStartingPromise = startServices(env);
await expect(serviceStartingPromise).rejects.toThrow(
"Some services could not be started: b. Missing dependencies: a"
);
expect(env.services).toEqual({});
registerService("a", [], () => "a");
await startServices(env);
expect(env.services).toEqual({ a: "a", b: "b" });
});
test(`startServices: waits for all synchronous code before attempting to start services`, async () => {
const env = makeEnv();
registerService("b", ["a"], () => "b");
const serviceStartingPromise = startServices(env);
// Dependency added in the same microtick doesn't cause startServices to throw even if it was added after the call
// (eg, a module is defined after main.js)
registerService("a", [], () => "a");
await serviceStartingPromise;
expect(env.services).toEqual({ a: "a", b: "b" });
});
test(`mountComponent creates an env and sets the application as root when no env is provided`, async () => {
patchTranslations();
registerService("my_service", [], () => "a");
class Root extends Component {
static template = xml`Root`;
static props = ["*"];
}
const app = await mountComponent(Root, getFixture());
after(() => {
delete odoo.__WOWL_DEBUG__;
});
const { env } = app;
expect(env.services).toEqual({ my_service: "a" });
expect(odoo.__WOWL_DEBUG__).toEqual({ root: app.root.component });
expect(getFixture()).toHaveText("Root");
});
test(`mountComponent uses the env when provided and doesn't start the services`, async () => {
patchTranslations();
registerService("my_service", [], () => {
expect.step("starting myService");
return "a";
});
const env = makeEnv();
expect.verifySteps([]);
await startServices(env);
expect.verifySteps(["starting myService"]);
class Root extends Component {
static template = xml`Root`;
static props = ["*"];
}
const app = await mountComponent(Root, getFixture(), { env });
expect.verifySteps([]);
expect(app.env.services).toBe(env.services);
expect(odoo.__WOWL_DEBUG__).toBe(undefined);
expect(getFixture()).toHaveText("Root");
});
test(`mountComponent: can pass props to the root component`, async () => {
class Root extends Component {
static template = xml`<t t-esc="props.text"/>`;
static props = ["*"];
}
await mountComponent(Root, getFixture(), { props: { text: "text from props" } });
after(() => {
delete odoo.__WOWL_DEBUG__;
});
expect(getFixture()).toHaveText("text from props");
});