Skip to content

Commit

Permalink
[IMP] web: tests - centralize 'get_definitions'
Browse files Browse the repository at this point in the history
This commit ensures that a single call to the server `get_definitions`
is performed for all models that will be needed in the current test run
at the start of the run.

It also allows models fetched from the server to ignore inheritance from
models that have not been fetched.

Part-of: #158916
  • Loading branch information
Arcasias committed Mar 28, 2024
1 parent d8df0f9 commit 488b230
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 15 deletions.
45 changes: 36 additions & 9 deletions addons/web/static/tests/_framework/mock_server/mock_server.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { registry } from "@web/core/registry";
import { isIterable } from "@web/core/utils/arrays";
import { deepCopy, isObject } from "@web/core/utils/objects";
import { serverState } from "../mock_server_state.hoot";
import { fetchModelDefinitions } from "../module_set.hoot";
import { fetchModelDefinitions, registerModelToFetch } from "../module_set.hoot";
import { DEFAULT_FIELD_VALUES, FIELD_SYMBOL } from "./mock_fields";
import {
FIELD_NOT_FOUND,
Expand Down Expand Up @@ -219,6 +219,7 @@ const DEFAULT_MENU = {
};
const R_DATASET_ROUTE = /\/web\/dataset\/call_(button|kw)\/[\w.-]+\/(?<step>\w+)/;
const R_WEBCLIENT_ROUTE = /(?<step>\/web\/webclient\/\w+)/;
const serverFields = new WeakSet();

//-----------------------------------------------------------------------------
// Exports
Expand Down Expand Up @@ -590,6 +591,7 @@ export class MockServer {

async loadModels() {
const models = this.modelSpecs;
const serverModelInheritances = new Set();
this.modelSpecs = [];
if (this.modelNamesToFetch.size) {
const modelEntries = await fetchModelDefinitions(this.modelNamesToFetch);
Expand All @@ -601,18 +603,25 @@ export class MockServer {
] of modelEntries) {
const localModelDef = [...models].find((model) => model._name === name);
localModelDef._description = description;
localModelDef._inherit = [...new Set([...(localModelDef._inherit || []), inherit])];
localModelDef._order = order;
localModelDef._parent_name = parent_name;
localModelDef._rec_name = rec_name;
const inheritList = new Set(safeSplit(localModelDef._inherit));
for (const inherittedModelName of inherit) {
inheritList.add(inherittedModelName);
serverModelInheritances.add([name, inherittedModelName].join(","));
}
localModelDef._inherit = [...inheritList].join(",");
for (const name in others) {
localModelDef[name] = others[name];
}
for (const [fieldName, serverFieldDef] of Object.entries(fields)) {
localModelDef._fields[fieldName] = {
const serverField = {
...serverFieldDef,
...localModelDef._fields[fieldName],
};
serverFields.add(serverField);
localModelDef._fields[fieldName] = serverField;
}
}
}
Expand Down Expand Up @@ -649,18 +658,28 @@ export class MockServer {
continue;
}
const parentModel = this.models[modelName];
if (!parentModel) {
if (parentModel) {
for (const fieldName in parentModel._fields) {
model._fields[fieldName] ??= parentModel._fields[fieldName];
}
} else if (serverModelInheritances.has([model._name, modelName].join(","))) {
// Inheritance comes from the server, so we can safely remove it:
// it means that the inherited model has not been fetched in this
// context.
model._inherit = model._inherit.replace(new RegExp(`${modelName},?`), "");
} else {
throw modelNotFoundError(modelName, "could not inherit from model");
}
for (const fieldName in parentModel._fields) {
model._fields[fieldName] ??= parentModel._fields[fieldName];
}
}

// Check missing models
for (const field of Object.values(model._fields)) {
if (field.relation && !this.models[field.relation]) {
throw modelNotFoundError(field.relation, "could not find model");
if (serverFields.has(field)) {
delete model._fields[field.name];
} else {
throw modelNotFoundError(field.relation, "could not find model");
}
}
}
}
Expand Down Expand Up @@ -987,7 +1006,15 @@ export function defineMenus(menus) {
* @param {ModelConstructor[] | Record<string, ModelConstructor>} ModelClasses
*/
export function defineModels(ModelClasses) {
return defineParams({ models: Object.values(ModelClasses) }, "add").models;
const models = Object.values(ModelClasses);
for (const ModelClass of models) {
const { definition } = ModelClass;
if (definition._fetch) {
registerModelToFetch(definition._name);
}
}

return defineParams({ models }, "add").models;
}

/**
Expand Down
22 changes: 16 additions & 6 deletions addons/web/static/tests/_framework/module_set.hoot.js
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,8 @@ const DEFAULT_MOCKS = {

const dependencyCache = {};
const { loader } = odoo;
/** @type {Set<string>} */
const modelsToFetch = new Set();
/** @type {Record<string, string[]>} */
const moduleNamesCache = {};
/** @type {Map<string, Record<string, any>>} */
Expand Down Expand Up @@ -325,14 +327,12 @@ export async function describeSuite(entryPoints, params) {
* @param {Iterable<string>} modelNames
*/
export async function fetchModelDefinitions(modelNames) {
const namesList = [...modelNames];
const modelNamesToFetch = namesList.filter((modelName) => !serverModelCache.has(modelName));

// Fetch missing definitions
if (modelNamesToFetch.length) {
const namesList = [...modelsToFetch];
if (namesList.length) {
const formData = new FormData();
formData.set("csrf_token", odoo.csrf_token);
formData.set("model_names", JSON.stringify(modelNamesToFetch));
formData.set("model_names", JSON.stringify(namesList));

const response = await realFetch("/web/model/get_definitions", {
body: formData,
Expand All @@ -350,8 +350,18 @@ export async function fetchModelDefinitions(modelNames) {

for (const [modelName, modelDef] of Object.entries(modelDefs)) {
serverModelCache.set(modelName, modelDef);
modelsToFetch.delete(modelName);
}
}

return namesList.map((modelName) => [modelName, serverModelCache.get(modelName)]);
return [...modelNames].map((modelName) => [modelName, serverModelCache.get(modelName)]);
}

/**
* @param {string} modelName
*/
export function registerModelToFetch(modelName) {
if (!serverModelCache.has(modelName)) {
modelsToFetch.add(modelName);
}
}

0 comments on commit 488b230

Please sign in to comment.