mirror of
https://github.com/bringout/oca-ocb-core.git
synced 2026-04-21 17:12:05 +02:00
vanilla 18.0
This commit is contained in:
parent
5454004ff9
commit
d7f6d2725e
979 changed files with 428093 additions and 0 deletions
|
|
@ -0,0 +1,114 @@
|
|||
/** @odoo-module alias=@web/../tests/helpers/cleanup default=false */
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Cleanup
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
const cleanups = [];
|
||||
|
||||
/**
|
||||
* Register a cleanup callback that will be executed whenever the current test
|
||||
* is done.
|
||||
*
|
||||
* - the cleanups will be executed in reverse order
|
||||
* - they will be executed even if the test fails/crashes
|
||||
*
|
||||
* @param {Function} callback
|
||||
*/
|
||||
export function registerCleanup(callback) {
|
||||
cleanups.push(callback);
|
||||
}
|
||||
|
||||
if (window.QUnit) {
|
||||
QUnit.on("OdooAfterTestHook", (info) => {
|
||||
if (QUnit.config.debug) {
|
||||
return;
|
||||
}
|
||||
let cleanup;
|
||||
// note that this calls the cleanup callbacks in reverse order!
|
||||
while ((cleanup = cleanups.pop())) {
|
||||
try {
|
||||
cleanup(info);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Check leftovers
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* List of elements tolerated in the body after a test. The property "keep"
|
||||
* prevents the element from being removed (typically: qunit suite elements).
|
||||
*/
|
||||
const validElements = [
|
||||
// always in the body:
|
||||
{ tagName: "DIV", attr: "id", value: "qunit", keep: true },
|
||||
{ tagName: "DIV", attr: "id", value: "qunit-fixture", keep: true },
|
||||
// shouldn't be in the body after a test but are tolerated:
|
||||
{ tagName: "SCRIPT", attr: "id", value: "" },
|
||||
{ tagName: "DIV", attr: "class", value: "o_notification_manager" },
|
||||
{ tagName: "DIV", attr: "class", value: "tooltip fade bs-tooltip-auto" },
|
||||
{ tagName: "DIV", attr: "class", value: "tooltip fade bs-tooltip-auto show" },
|
||||
{ tagName: "DIV", attr: "class", value: "tooltip tooltip-field-info fade bs-tooltip-auto" },
|
||||
{
|
||||
tagName: "DIV",
|
||||
attr: "class",
|
||||
value: "tooltip tooltip-field-info fade bs-tooltip-auto show",
|
||||
},
|
||||
|
||||
// Due to a Document Kanban bug (already present in 12.0)
|
||||
{ tagName: "DIV", attr: "class", value: "ui-helper-hidden-accessible" },
|
||||
{
|
||||
tagName: "UL",
|
||||
attr: "class",
|
||||
value: "ui-menu ui-widget ui-widget-content ui-autocomplete ui-front",
|
||||
},
|
||||
{
|
||||
tagName: "UL",
|
||||
attr: "class",
|
||||
value: "ui-menu ui-widget ui-widget-content ui-autocomplete dropdown-menu ui-front", // many2ones
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* After each test, we check that there is no leftover in the DOM.
|
||||
*
|
||||
* Note: this event is not QUnit standard, we added it for this specific use case.
|
||||
* As a payload, an object with keys 'moduleName' and 'testName' is provided. It
|
||||
* is used to indicate the test that left elements in the DOM, when it happens.
|
||||
*/
|
||||
QUnit.on("OdooAfterTestHook", function (info) {
|
||||
if (QUnit.config.debug) {
|
||||
return;
|
||||
}
|
||||
const failed = info.testReport.getStatus() === "failed";
|
||||
const toRemove = [];
|
||||
// check for leftover elements in the body
|
||||
for (const bodyChild of document.body.children) {
|
||||
const tolerated = validElements.find(
|
||||
(e) => e.tagName === bodyChild.tagName && bodyChild.getAttribute(e.attr) === e.value
|
||||
);
|
||||
if (!failed && !tolerated) {
|
||||
QUnit.pushFailure(
|
||||
`Body still contains undesirable elements:\n${bodyChild.outerHTML}`
|
||||
);
|
||||
}
|
||||
if (!tolerated || !tolerated.keep) {
|
||||
toRemove.push(bodyChild);
|
||||
}
|
||||
}
|
||||
// cleanup leftovers in #qunit-fixture
|
||||
const qunitFixture = document.getElementById("qunit-fixture");
|
||||
if (qunitFixture.children.length) {
|
||||
toRemove.push(...qunitFixture.children);
|
||||
}
|
||||
// remove unwanted elements if not in debug
|
||||
for (const el of toRemove) {
|
||||
el.remove();
|
||||
}
|
||||
document.body.classList.remove("modal-open");
|
||||
});
|
||||
}
|
||||
|
|
@ -0,0 +1,150 @@
|
|||
/** @odoo-module alias=@web/../tests/helpers/mock_env default=false */
|
||||
|
||||
import { SERVICES_METADATA } from "@web/core/utils/hooks";
|
||||
import { registry } from "@web/core/registry";
|
||||
import { makeEnv, startServices } from "@web/env";
|
||||
import { registerCleanup } from "./cleanup";
|
||||
import { makeMockServer } from "./mock_server";
|
||||
import { mocks } from "./mock_services";
|
||||
import { patchWithCleanup } from "./utils";
|
||||
import { Component } from "@odoo/owl";
|
||||
import { startRouter } from "@web/core/browser/router";
|
||||
|
||||
function prepareRegistry(registry, keepContent = false) {
|
||||
const _addEventListener = registry.addEventListener.bind(registry);
|
||||
const _removeEventListener = registry.removeEventListener.bind(registry);
|
||||
const patch = {
|
||||
content: keepContent ? { ...registry.content } : {},
|
||||
elements: null,
|
||||
entries: null,
|
||||
subRegistries: {},
|
||||
addEventListener(type, callback) {
|
||||
_addEventListener(type, callback);
|
||||
registerCleanup(() => {
|
||||
_removeEventListener(type, callback);
|
||||
});
|
||||
},
|
||||
};
|
||||
patchWithCleanup(registry, patch);
|
||||
}
|
||||
|
||||
export function clearRegistryWithCleanup(registry) {
|
||||
prepareRegistry(registry);
|
||||
}
|
||||
|
||||
function cloneRegistryWithCleanup(registry) {
|
||||
prepareRegistry(registry, true);
|
||||
}
|
||||
|
||||
export function clearServicesMetadataWithCleanup() {
|
||||
const servicesMetadata = Object.assign({}, SERVICES_METADATA);
|
||||
for (const key of Object.keys(SERVICES_METADATA)) {
|
||||
delete SERVICES_METADATA[key];
|
||||
}
|
||||
registerCleanup(() => {
|
||||
for (const key of Object.keys(SERVICES_METADATA)) {
|
||||
delete SERVICES_METADATA[key];
|
||||
}
|
||||
Object.assign(SERVICES_METADATA, servicesMetadata);
|
||||
});
|
||||
}
|
||||
|
||||
export const registryNamesToCloneWithCleanup = [
|
||||
"actions",
|
||||
"command_provider",
|
||||
"command_setup",
|
||||
"error_handlers",
|
||||
"fields",
|
||||
"fields",
|
||||
"main_components",
|
||||
"view_widgets",
|
||||
"views",
|
||||
];
|
||||
|
||||
export const utils = {
|
||||
prepareRegistriesWithCleanup() {
|
||||
// Clone registries
|
||||
registryNamesToCloneWithCleanup.forEach((registryName) =>
|
||||
cloneRegistryWithCleanup(registry.category(registryName))
|
||||
);
|
||||
|
||||
// Clear registries
|
||||
clearRegistryWithCleanup(registry.category("command_categories"));
|
||||
clearRegistryWithCleanup(registry.category("debug"));
|
||||
clearRegistryWithCleanup(registry.category("error_dialogs"));
|
||||
clearRegistryWithCleanup(registry.category("favoriteMenu"));
|
||||
clearRegistryWithCleanup(registry.category("ir.actions.report handlers"));
|
||||
clearRegistryWithCleanup(registry.category("main_components"));
|
||||
|
||||
clearRegistryWithCleanup(registry.category("services"));
|
||||
clearServicesMetadataWithCleanup();
|
||||
|
||||
clearRegistryWithCleanup(registry.category("systray"));
|
||||
clearRegistryWithCleanup(registry.category("user_menuitems"));
|
||||
clearRegistryWithCleanup(registry.category("kanban_examples"));
|
||||
clearRegistryWithCleanup(registry.category("__processed_archs__"));
|
||||
// fun fact: at least one registry is missing... this shows that we need a
|
||||
// better design for the way we clear these registries...
|
||||
},
|
||||
};
|
||||
|
||||
// This is exported in a utils object to allow for patching
|
||||
export function prepareRegistriesWithCleanup() {
|
||||
return utils.prepareRegistriesWithCleanup();
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {import("@web/env").OdooEnv} OdooEnv
|
||||
*/
|
||||
|
||||
/**
|
||||
* Create a test environment
|
||||
*
|
||||
* @param {*} config
|
||||
* @returns {Promise<OdooEnv>}
|
||||
*/
|
||||
export async function makeTestEnv(config = {}) {
|
||||
startRouter();
|
||||
// add all missing dependencies if necessary
|
||||
const serviceRegistry = registry.category("services");
|
||||
const servicesToProcess = serviceRegistry.getAll();
|
||||
while (servicesToProcess.length) {
|
||||
const service = servicesToProcess.pop();
|
||||
if (service.dependencies) {
|
||||
for (const depName of service.dependencies) {
|
||||
if (depName in mocks && !serviceRegistry.contains(depName)) {
|
||||
const dep = mocks[depName]();
|
||||
serviceRegistry.add(depName, dep);
|
||||
servicesToProcess.push(dep);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (config.serverData || config.mockRPC || config.activateMockServer) {
|
||||
await makeMockServer(config.serverData, config.mockRPC);
|
||||
}
|
||||
|
||||
let env = makeEnv();
|
||||
await startServices(env);
|
||||
Component.env = env;
|
||||
if ("config" in config) {
|
||||
env = Object.assign(Object.create(env), { config: config.config });
|
||||
}
|
||||
return env;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a test environment for dialog tests
|
||||
*
|
||||
* @param {*} config
|
||||
* @returns {Promise<OdooEnv>}
|
||||
*/
|
||||
export async function makeDialogTestEnv(config = {}) {
|
||||
const env = await makeTestEnv(config);
|
||||
env.dialogData = {
|
||||
isActive: true,
|
||||
close() {},
|
||||
};
|
||||
return env;
|
||||
}
|
||||
|
|
@ -0,0 +1,331 @@
|
|||
/** @odoo-module alias=@web/../tests/helpers/mock_services default=false */
|
||||
|
||||
import { effectService } from "@web/core/effects/effect_service";
|
||||
import { localization } from "@web/core/l10n/localization";
|
||||
import { ConnectionAbortedError, rpcBus, rpc } from "@web/core/network/rpc";
|
||||
import { ormService } from "@web/core/orm_service";
|
||||
import { overlayService } from "@web/core/overlay/overlay_service";
|
||||
import { uiService } from "@web/core/ui/ui_service";
|
||||
import { user } from "@web/core/user";
|
||||
import { patchWithCleanup } from "./utils";
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Mock Services
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
export const defaultLocalization = {
|
||||
dateFormat: "MM/dd/yyyy",
|
||||
timeFormat: "HH:mm:ss",
|
||||
shortTimeFormat: "HH:mm",
|
||||
dateTimeFormat: "MM/dd/yyyy HH:mm:ss",
|
||||
decimalPoint: ".",
|
||||
direction: "ltr",
|
||||
grouping: [],
|
||||
multiLang: false,
|
||||
thousandsSep: ",",
|
||||
weekStart: 7,
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {Partial<typeof defaultLocalization>} [config]
|
||||
*/
|
||||
export function makeFakeLocalizationService(config = {}) {
|
||||
patchWithCleanup(localization, { ...defaultLocalization, ...config });
|
||||
patchWithCleanup(luxon.Settings, { defaultNumberingSystem: "latn" });
|
||||
|
||||
return {
|
||||
name: "localization",
|
||||
start: async (env) => {
|
||||
return localization;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function patchRPCWithCleanup(mockRPC = () => {}) {
|
||||
let nextId = 1;
|
||||
patchWithCleanup(rpc, {
|
||||
_rpc: function (route, params = {}, settings = {}) {
|
||||
let rejectFn;
|
||||
const data = {
|
||||
id: nextId++,
|
||||
jsonrpc: "2.0",
|
||||
method: "call",
|
||||
params: params,
|
||||
};
|
||||
rpcBus.trigger("RPC:REQUEST", { data, url: route, settings });
|
||||
const rpcProm = new Promise((resolve, reject) => {
|
||||
rejectFn = reject;
|
||||
Promise.resolve(mockRPC(...arguments))
|
||||
.then((result) => {
|
||||
rpcBus.trigger("RPC:RESPONSE", { data, settings, result });
|
||||
resolve(result);
|
||||
})
|
||||
.catch((error) => {
|
||||
rpcBus.trigger("RPC:RESPONSE", {
|
||||
data,
|
||||
settings,
|
||||
error,
|
||||
});
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
rpcProm.abort = (rejectError = true) => {
|
||||
if (rejectError) {
|
||||
rejectFn(new ConnectionAbortedError("XmlHttpRequestError abort"));
|
||||
}
|
||||
};
|
||||
return rpcProm;
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function makeMockXHR(response, sendCb, def) {
|
||||
const MockXHR = function () {
|
||||
return {
|
||||
_loadListener: null,
|
||||
url: "",
|
||||
addEventListener(type, listener) {
|
||||
if (type === "load") {
|
||||
this._loadListener = listener;
|
||||
} else if (type === "error") {
|
||||
this._errorListener = listener;
|
||||
}
|
||||
},
|
||||
set onload(listener) {
|
||||
this._loadListener = listener;
|
||||
},
|
||||
set onerror(listener) {
|
||||
this._errorListener = listener;
|
||||
},
|
||||
open(method, url) {
|
||||
this.url = url;
|
||||
},
|
||||
getResponseHeader() {},
|
||||
setRequestHeader() {},
|
||||
async send(data) {
|
||||
let listener = this._loadListener;
|
||||
if (sendCb) {
|
||||
if (typeof data === "string") {
|
||||
try {
|
||||
data = JSON.parse(data);
|
||||
} catch {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
try {
|
||||
await sendCb.call(this, data);
|
||||
} catch {
|
||||
listener = this._errorListener;
|
||||
}
|
||||
}
|
||||
if (def) {
|
||||
await def;
|
||||
}
|
||||
listener.call(this);
|
||||
},
|
||||
response: JSON.stringify(response || ""),
|
||||
};
|
||||
};
|
||||
return MockXHR;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Low level API mocking
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
export function makeMockFetch(mockRPC) {
|
||||
return async (input, params) => {
|
||||
let route = typeof input === "string" ? input : input.url;
|
||||
if (route.includes("load_menus")) {
|
||||
const routeArray = route.split("/");
|
||||
params = {
|
||||
hash: routeArray.pop(),
|
||||
};
|
||||
route = routeArray.join("/");
|
||||
}
|
||||
let res;
|
||||
let status;
|
||||
try {
|
||||
res = await mockRPC(route, params);
|
||||
status = 200;
|
||||
} catch {
|
||||
status = 500;
|
||||
}
|
||||
const blob = new Blob([JSON.stringify(res || {})], { type: "application/json" });
|
||||
const response = new Response(blob, { status });
|
||||
// Mock some functions of the Response API to make them almost synchronous (micro-tick level)
|
||||
// as their native implementation is async (tick level), which can lead to undeterministic
|
||||
// errors as it breaks the hypothesis that calling nextTick after fetching data is enough
|
||||
// to see the result rendered in the DOM.
|
||||
response.json = () => Promise.resolve(JSON.parse(JSON.stringify(res || {})));
|
||||
response.text = () => Promise.resolve(String(res || {}));
|
||||
response.blob = () => Promise.resolve(blob);
|
||||
return response;
|
||||
};
|
||||
}
|
||||
|
||||
export const fakeCommandService = {
|
||||
start() {
|
||||
return {
|
||||
add() {
|
||||
return () => {};
|
||||
},
|
||||
getCommands() {
|
||||
return [];
|
||||
},
|
||||
openPalette() {},
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
export const fakeTitleService = {
|
||||
start() {
|
||||
let current = {};
|
||||
return {
|
||||
get current() {
|
||||
return JSON.stringify(current);
|
||||
},
|
||||
getParts() {
|
||||
return current;
|
||||
},
|
||||
setParts(parts) {
|
||||
current = Object.assign({}, current, parts);
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
export const fakeColorSchemeService = {
|
||||
start() {
|
||||
return {
|
||||
switchToColorScheme() {},
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
export function makeFakeNotificationService(mock) {
|
||||
return {
|
||||
start() {
|
||||
function add() {
|
||||
if (mock) {
|
||||
return mock(...arguments);
|
||||
}
|
||||
}
|
||||
return {
|
||||
add,
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function makeFakeDialogService(addDialog, closeAllDialog) {
|
||||
return {
|
||||
start() {
|
||||
return {
|
||||
add: addDialog || (() => () => {}),
|
||||
closeAll: closeAllDialog || (() => () => {}),
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function makeFakePwaService() {
|
||||
return {
|
||||
start() {
|
||||
return {
|
||||
canPromptToInstall: false,
|
||||
isAvailable: false,
|
||||
isScopedApp: false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function patchUserContextWithCleanup(patch) {
|
||||
const context = user.context;
|
||||
patchWithCleanup(user, {
|
||||
get context() {
|
||||
return Object.assign({}, context, patch);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function patchUserWithCleanup(patch) {
|
||||
patchWithCleanup(user, patch);
|
||||
}
|
||||
|
||||
export const fakeCompanyService = {
|
||||
start() {
|
||||
return {
|
||||
allowedCompanies: {},
|
||||
allowedCompaniesWithAncestors: {},
|
||||
activeCompanyIds: [],
|
||||
currentCompany: {},
|
||||
setCompanies: () => {},
|
||||
getCompany: () => {},
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
export function makeFakeBarcodeService() {
|
||||
return {
|
||||
start() {
|
||||
return {
|
||||
bus: {
|
||||
async addEventListener() {},
|
||||
async removeEventListener() {},
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function makeFakeHTTPService(getResponse, postResponse) {
|
||||
getResponse =
|
||||
getResponse ||
|
||||
((route, readMethod) => {
|
||||
return readMethod === "json" ? {} : "";
|
||||
});
|
||||
postResponse =
|
||||
postResponse ||
|
||||
((route, params, readMethod) => {
|
||||
return readMethod === "json" ? {} : "";
|
||||
});
|
||||
return {
|
||||
start() {
|
||||
return {
|
||||
async get(...args) {
|
||||
return getResponse(...args);
|
||||
},
|
||||
async post(...args) {
|
||||
return postResponse(...args);
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function makeFakeActionService() {
|
||||
return {
|
||||
start() {
|
||||
return {
|
||||
doAction() {},
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export const mocks = {
|
||||
color_scheme: () => fakeColorSchemeService,
|
||||
company: () => fakeCompanyService,
|
||||
command: () => fakeCommandService,
|
||||
effect: () => effectService, // BOI The real service ? Is this what we want ?
|
||||
localization: makeFakeLocalizationService,
|
||||
notification: makeFakeNotificationService,
|
||||
title: () => fakeTitleService,
|
||||
ui: () => uiService,
|
||||
dialog: makeFakeDialogService,
|
||||
orm: () => ormService,
|
||||
action: makeFakeActionService,
|
||||
overlay: () => overlayService,
|
||||
};
|
||||
|
|
@ -0,0 +1,107 @@
|
|||
/** @odoo-module alias=@web/../tests/helpers/mount_in_fixture default=false**/
|
||||
|
||||
import { App, Component, xml } from "@odoo/owl";
|
||||
import { registerCleanup } from "@web/../tests/helpers/cleanup";
|
||||
import { makeTestEnv } from "@web/../tests/helpers/mock_env";
|
||||
import { mocks } from "@web/../tests/helpers/mock_services";
|
||||
import { MainComponentsContainer } from "@web/core/main_components_container";
|
||||
import { registry } from "@web/core/registry";
|
||||
import { getTemplate } from "@web/core/templates";
|
||||
|
||||
class TestComponent extends Component {
|
||||
static props = {
|
||||
components: { type: Array },
|
||||
};
|
||||
|
||||
static template = xml`
|
||||
<t t-foreach="props.components" t-as="comp" t-key="comp.component.name">
|
||||
<t t-component="comp.component" t-props="comp.props"/>
|
||||
</t>
|
||||
`;
|
||||
|
||||
/**
|
||||
* Returns the instance of the first component.
|
||||
* @returns {Component}
|
||||
*/
|
||||
get defaultComponent() {
|
||||
return this.__owl__.bdom.children[0].child.component;
|
||||
}
|
||||
}
|
||||
|
||||
function getApp(env, props) {
|
||||
const appConfig = {
|
||||
env,
|
||||
getTemplate,
|
||||
test: true,
|
||||
props: props,
|
||||
};
|
||||
if (env.services && "localization" in env.services) {
|
||||
appConfig.translateFn = env._t;
|
||||
}
|
||||
const app = new App(TestComponent, appConfig);
|
||||
registerCleanup(() => app.destroy());
|
||||
return app;
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} Config
|
||||
* @property {Object} env
|
||||
* @property {Object} props
|
||||
* @property {string[]} templates
|
||||
*/
|
||||
|
||||
/**
|
||||
* This functions will mount the given component and
|
||||
* will add a MainComponentsContainer if the overlay
|
||||
* service is loaded.
|
||||
*
|
||||
* @template T
|
||||
* @param {new (...args: any[]) => T} Comp
|
||||
* @param {HTMLElement} target
|
||||
* @param {Config} config
|
||||
* @returns {Promise<T>} Instance of Comp
|
||||
*/
|
||||
export async function mountInFixture(Comp, target, config = {}) {
|
||||
const serviceRegistry = registry.category("services");
|
||||
|
||||
let env = config.env || {};
|
||||
const isEnvInitialized = env && env.services;
|
||||
|
||||
function isServiceRegistered(serviceName) {
|
||||
return isEnvInitialized
|
||||
? serviceName in env.services
|
||||
: serviceRegistry.contains(serviceName);
|
||||
}
|
||||
|
||||
async function addService(serviceName, service) {
|
||||
if (isServiceRegistered(serviceName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
service = typeof service === "function" ? service() : service;
|
||||
if (isEnvInitialized) {
|
||||
env.services[serviceName] = await service.start(env);
|
||||
} else {
|
||||
serviceRegistry.add(serviceName, service);
|
||||
}
|
||||
}
|
||||
|
||||
const components = [{ component: Comp, props: config.props || {} }];
|
||||
if (isServiceRegistered("overlay")) {
|
||||
await addService("localization", mocks.localization);
|
||||
components.push({ component: MainComponentsContainer, props: {} });
|
||||
}
|
||||
|
||||
if (!isEnvInitialized) {
|
||||
env = await makeTestEnv(env);
|
||||
}
|
||||
|
||||
const app = getApp(env, { components });
|
||||
|
||||
if (config.templates) {
|
||||
app.addTemplates(config.templates);
|
||||
}
|
||||
|
||||
const testComp = await app.mount(target);
|
||||
return testComp.defaultComponent;
|
||||
}
|
||||
1134
odoo-bringout-oca-ocb-web/web/static/tests/legacy/helpers/utils.js
Normal file
1134
odoo-bringout-oca-ocb-web/web/static/tests/legacy/helpers/utils.js
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue