From 4ebb88883e486376e585fc6d6f388f9c774aac75 Mon Sep 17 00:00:00 2001 From: "Lucas Perais (lpe)" Date: Wed, 26 May 2021 09:02:10 +0200 Subject: [PATCH 1/2] fixup! [REF] web: rewrite webclient in owl --- .../web/static/tests/helpers/mock_server.js | 540 ++++++++++++++++ .../web/static/tests/helpers/mock_services.js | 31 - addons/web/static/tests/helpers/utils.js | 5 + .../static/tests/legacy/helpers/test_env.js | 32 +- .../tests/legacy/helpers/test_utils_mock.js | 3 + .../legacy/report/client_action_tests.js | 110 ---- .../legacy/services/crash_manager_tests.js | 64 -- .../tests/legacy/views/abstract_view_tests.js | 49 +- .../tests/legacy/views/calendar_tests.js | 71 +-- .../static/tests/legacy/views/form_tests.js | 603 +++++++++--------- .../static/tests/legacy/views/graph_tests.js | 59 +- .../static/tests/legacy/views/list_tests.js | 430 ++++++------- .../static/tests/legacy/views/pivot_tests.js | 92 ++- .../static/tests/legacy/views/qweb_tests.js | 87 +-- .../tests/legacy/views/search_panel_tests.js | 364 +++++------ .../legacy/widgets/domain_selector_tests.js | 47 +- .../tests/legacy/widgets/week_days_tests.js | 10 +- addons/web/static/tests/setup.js | 9 + .../static/tests/webclient/actions/helpers.js | 51 +- .../tests/webclient/actions/legacy_tests.js | 50 ++ .../webclient/actions/report_action_tests.js | 7 +- .../tests/webclient/actions/target_tests.js | 2 - .../webclient/actions/window_action_tests.js | 5 - 23 files changed, 1555 insertions(+), 1166 deletions(-) delete mode 100644 addons/web/static/tests/legacy/report/client_action_tests.js delete mode 100644 addons/web/static/tests/legacy/services/crash_manager_tests.js diff --git a/addons/web/static/tests/helpers/mock_server.js b/addons/web/static/tests/helpers/mock_server.js index c8bc9e9eecea3..de34f8e72806a 100644 --- a/addons/web/static/tests/helpers/mock_server.js +++ b/addons/web/static/tests/helpers/mock_server.js @@ -398,6 +398,8 @@ export class MockServer { return Promise.resolve(this.mockLoadViews(args.model, args.kwargs)); case "name_create": return Promise.resolve(this.mockNameCreate(args.model, args.args[0])); + case "name_get": + return Promise.resolve(this.mockNameGet(args.model, args.args)); case "name_search": return Promise.resolve(this.mockNameSearch(args.model, args.args, args.kwargs)); case "onchange": @@ -406,8 +408,18 @@ export class MockServer { return Promise.resolve(this.mockRead(args.model, args.args)); case "search": return Promise.resolve(this.mockSearch(args.model, args.args, args.kwargs)); + case "search_count": + return Promise.resolve(this.mockSearchCount(args.model, args.args, args.kwargs)); case "search_read": return Promise.resolve(this.mockSearchRead(args.model, args.args, args.kwargs)); + case "search_panel_select_range": + return Promise.resolve( + this.mockSearchPanelSelectRange(args.model, args.args, args.kwargs) + ); + case "search_panel_select_multi_range": + return Promise.resolve( + this.mockSearchPanelSelectMultiRange(args.model, args.args, args.kwargs) + ); case "web_search_read": return Promise.resolve(this.mockWebSearchRead(args.model, args.args, args.kwargs)); case "read_group": @@ -533,6 +545,31 @@ export class MockServer { return [id, name]; } + /** + * Simulate a 'name_get' operation + * + * @private + * @param {string} model + * @param {Array} args + * @returns {Array[]} a list of [id, display_name] + */ + mockNameGet(model, args) { + var ids = args[0]; + if (!args.length) { + throw new Error("name_get: expected one argument"); + } else if (!ids) { + return []; + } + if (!Array.isArray(ids)) { + ids = [ids]; + } + var records = this.models[model].records; + var names = ids.map((id) => + id ? [id, records.find((r) => r.id === id).display_name] : [null, "False"] + ); + return names; + } + /** * Simulate a 'name_search' operation. * @@ -858,6 +895,18 @@ export class MockServer { return result.records; } + /** + * Simulate a 'search_count' operation + * + * @private + * @param {string} model + * @param {Array} args + * @returns {integer} + */ + mockSearchCount(model, args) { + return this.getRecords(model, args[0]).length; + } + mockSearchRead(modelName, args, kwargs) { const result = this.mockSearchReadController({ model: modelName, @@ -917,6 +966,497 @@ export class MockServer { }; } + /** + * Simulates a call to the server '_search_panel_domain_image' method. + * + * @private + * @param {string} model + * @param {Array[]} domain + * @param {string} fieldName + * @param {boolean} setCount + * @returns {Map} + */ + mockSearchPanelDomainImage(model, fieldName, domain, setCount = false, limit = false) { + const field = this.models[model].fields[fieldName]; + let groupIdName; + if (field.type === "many2one") { + groupIdName = (value) => value || [false, undefined]; + // mockReadGroup does not take care of the condition [fieldName, '!=', false] + // in the domain defined below !!! + } else if (field.type === "selection") { + const selection = {}; + for (const [value, label] of this.models[model].fields[fieldName].selection) { + selection[value] = label; + } + groupIdName = (value) => [value, selection[value]]; + } + domain = Domain.combine([domain, [[fieldName, "!=", false]]]).toList(); + const groups = this.mockReadGroup(model, { + domain, + fields: [fieldName], + groupby: [fieldName], + limit, + }); + const domainImage = new Map(); + for (const group of groups) { + const [id, display_name] = groupIdName(group[fieldName]); + const values = { id, display_name }; + if (setCount) { + values.__count = group[fieldName + "_count"]; + } + domainImage.set(id, values); + } + return domainImage; + } + + /** + * Simulates a call to the server '_search_panel_field_image' method. + * + * @private + * @param {string} model + * @param {string} fieldName + * @param {Object} kwargs + * @see _mockSearchPanelDomainImage() + */ + mockSearchPanelFieldImage(model, fieldName, kwargs) { + const enableCounters = kwargs.enable_counters; + const onlyCounters = kwargs.only_counters; + const extraDomain = kwargs.extra_domain || []; + const normalizedExtra = new Domain(extraDomain).toList(); + const noExtra = JSON.stringify(normalizedExtra) === "[]"; + const modelDomain = kwargs.model_domain || []; + const countDomain = Domain.combine([modelDomain, extraDomain]).toList(); + + const limit = kwargs.limit; + const setLimit = kwargs.set_limit; + + if (onlyCounters) { + return this.mockSearchPanelDomainImage(model, fieldName, countDomain, true); + } + + const modelDomainImage = this.mockSearchPanelDomainImage( + model, + fieldName, + modelDomain, + enableCounters && noExtra, + setLimit && limit + ); + if (enableCounters && !noExtra) { + const countDomainImage = this.mockSearchPanelDomainImage( + model, + fieldName, + countDomain, + true + ); + for (const [id, values] of modelDomainImage.entries()) { + const element = countDomainImage.get(id); + values.__count = element ? element.__count : 0; + } + } + + return modelDomainImage; + } + + /** + * Simulates a call to the server '_search_panel_global_counters' method. + * + * @private + * @param {Map} valuesRange + * @param {(string|boolean)} parentName 'parent_id' or false + */ + mockSearchPanelGlobalCounters(valuesRange, parentName) { + const localCounters = [...valuesRange.keys()].map((id) => valuesRange.get(id).__count); + for (let [id, values] of valuesRange.entries()) { + const count = localCounters[id]; + if (count) { + let parent_id = values[parentName]; + while (parent_id) { + values = valuesRange.get(parent_id); + values.__count += count; + parent_id = values[parentName]; + } + } + } + } + + /** + * Simulates a call to the server '_search_panel_sanitized_parent_hierarchy' method. + * + * @private + * @param {Object[]} records + * @param {(string|boolean)} parentName 'parent_id' or false + * @param {number[]} ids + * @returns {Object[]} + */ + mockSearchPanelSanitizedParentHierarchy(records, parentName, ids) { + const getParentId = (record) => record[parentName] && record[parentName][0]; + const allowedRecords = {}; + for (const record of records) { + allowedRecords[record.id] = record; + } + const recordsToKeep = {}; + for (const id of ids) { + const ancestorChain = {}; + let recordId = id; + let chainIsFullyIncluded = true; + while (chainIsFullyIncluded && recordId) { + const knownStatus = recordsToKeep[recordId]; + if (knownStatus !== undefined) { + chainIsFullyIncluded = knownStatus; + break; + } + const record = allowedRecords[recordId]; + if (record) { + ancestorChain[recordId] = record; + recordId = getParentId(record); + } else { + chainIsFullyIncluded = false; + } + } + for (const id in ancestorChain) { + recordsToKeep[id] = chainIsFullyIncluded; + } + } + return records.filter((rec) => recordsToKeep[rec.id]); + } + + /** + * Simulates a call to the server 'search_panel_select_range' method. + * + * @private + * @param {string} model + * @param {string[]} args + * @param {string} args[fieldName] + * @param {Object} [kwargs={}] + * @param {Array[]} [kwargs.category_domain] domain generated by categories + * (this parameter is used in _search_panel_range) + * @param {Array[]} [kwargs.comodel_domain] domain of field values (if relational) + * (this parameter is used in _search_panel_range) + * @param {boolean} [kwargs.enable_counters] whether to count records by value + * @param {Array[]} [kwargs.filter_domain] domain generated by filters + * @param {integer} [kwargs.limit] maximal number of values to fetch + * @param {Array[]} [kwargs.search_domain] base domain of search (this parameter + * is used in _search_panel_range) + * @returns {Object} + */ + mockSearchPanelSelectRange(model, [fieldName], kwargs) { + const field = this.models[model].fields[fieldName]; + const supportedTypes = ["many2one", "selection"]; + if (!supportedTypes.includes(field.type)) { + throw new Error( + `Only types ${supportedTypes} are supported for category (found type ${field.type})` + ); + } + + const modelDomain = kwargs.search_domain || []; + const extraDomain = Domain.combine([ + kwargs.category_domain || [], + kwargs.filter_domain || [], + ]).toList(); + + if (field.type === "selection") { + const newKwargs = Object.assign({}, kwargs, { + model_domain: modelDomain, + extra_domain: extraDomain, + }); + kwargs.model_domain = modelDomain; + return { + parent_field: false, + values: this.mockSearchPanelSelectionRange(model, fieldName, newKwargs), + }; + } + + const fieldNames = ["display_name"]; + let hierarchize = "hierarchize" in kwargs ? kwargs.hierarchize : true; + let getParentId; + let parentName = false; + if (hierarchize && this.models[field.relation].fields.parent_id) { + parentName = "parent_id"; // in tests, parent field is always 'parent_id' + fieldNames.push(parentName); + getParentId = (record) => record.parent_id && record.parent_id[0]; + } else { + hierarchize = false; + } + let comodelDomain = kwargs.comodel_domain || []; + const enableCounters = kwargs.enable_counters; + const expand = kwargs.expand; + const limit = kwargs.limit; + let domainImage; + if (enableCounters || !expand) { + const newKwargs = Object.assign({}, kwargs, { + model_domain: modelDomain, + extra_domain: extraDomain, + only_counters: expand, + set_limit: limit && !(expand || hierarchize || comodelDomain), + }); + domainImage = this.mockSearchPanelFieldImage(model, fieldName, newKwargs); + } + if (!expand && !hierarchize && !comodelDomain.length) { + if (limit && domainImage.size === limit) { + return { error_msg: "Too many items to display." }; + } + return { + parent_field: parentName, + values: [...domainImage.values()], + }; + } + let imageElementIds; + if (!expand) { + imageElementIds = [...domainImage.keys()].map(Number); + let condition; + if (hierarchize) { + const records = this.models[field.relation].records; + const ancestorIds = new Set(); + for (const id of imageElementIds) { + let recordId = id; + let record; + while (recordId) { + ancestorIds.add(recordId); + record = records.find((rec) => rec.id === recordId); + recordId = record[parentName]; + } + } + condition = ["id", "in", [...new Set(ancestorIds)]]; + } else { + condition = ["id", "in", imageElementIds]; + } + comodelDomain = Domain.combine([comodelDomain, [condition]]).toList(); + } + let comodelRecords = this.mockSearchRead(field.relation, [comodelDomain, fieldNames], { + limit, + }); + + if (hierarchize) { + const ids = expand ? comodelRecords.map((rec) => rec.id) : imageElementIds; + comodelRecords = this.mockSearchPanelSanitizedParentHierarchy( + comodelRecords, + parentName, + ids + ); + } + + if (limit && comodelRecords.length === limit) { + return { error_msg: "Too many items to display." }; + } + // A map is used to keep the initial order. + const fieldRange = new Map(); + for (const record of comodelRecords) { + const values = { + id: record.id, + display_name: record.display_name, + }; + if (hierarchize) { + values[parentName] = getParentId(record); + } + if (enableCounters) { + values.__count = domainImage.get(record.id) + ? domainImage.get(record.id).__count + : 0; + } + fieldRange.set(record.id, values); + } + + if (hierarchize && enableCounters) { + this.mockSearchPanelGlobalCounters(fieldRange, parentName); + } + + return { + parent_field: parentName, + values: [...fieldRange.values()], + }; + } + + /** + * Simulates a call to the server 'search_panel_select_multi_range' method. + * + * @private + * @param {string} model + * @param {string[]} args + * @param {string} args[fieldName] + * @param {Object} [kwargs={}] + * @param {Array[]} [kwargs.category_domain] domain generated by categories + * @param {Array[]} [kwargs.comodel_domain] domain of field values (if relational) + * (this parameter is used in _search_panel_range) + * @param {boolean} [kwargs.enable_counters] whether to count records by value + * @param {Array[]} [kwargs.filter_domain] domain generated by filters + * @param {string} [kwargs.group_by] extra field to read on comodel, to group + * comodel records + * @param {Array[]} [kwargs.group_domain] dict, one domain for each activated + * group for the group_by (if any). Those domains are used to fech accurate + * counters for values in each group + * @param {integer} [kwargs.limit] maximal number of values to fetch + * @param {Array[]} [kwargs.search_domain] base domain of search + * @returns {Object} + */ + mockSearchPanelSelectMultiRange(model, [fieldName], kwargs) { + const field = this.models[model].fields[fieldName]; + const supportedTypes = ["many2one", "many2many", "selection"]; + if (!supportedTypes.includes(field.type)) { + throw new Error( + `Only types ${supportedTypes} are supported for filter (found type ${field.type})` + ); + } + let modelDomain = kwargs.search_domain || []; + let extraDomain = Domain.combine([ + kwargs.category_domain || [], + kwargs.filter_domain || [], + ]).toList(); + if (field.type === "selection") { + const newKwargs = Object.assign({}, kwargs, { + model_domain: modelDomain, + extra_domain: extraDomain, + }); + return { + values: this.mockSearchPanelSelectionRange(model, fieldName, newKwargs), + }; + } + const fieldNames = ["display_name"]; + const groupBy = kwargs.group_by; + let groupIdName; + if (groupBy) { + const groupByField = this.models[field.relation].fields[groupBy]; + fieldNames.push(groupBy); + if (groupByField.type === "many2one") { + groupIdName = (value) => value || [false, "Not set"]; + } else if (groupByField.type === "selection") { + const groupBySelection = Object.assign( + {}, + this.models[field.relation].fields[groupBy].selection + ); + groupBySelection[false] = "Not Set"; + groupIdName = (value) => [value, groupBySelection[value]]; + } else { + groupIdName = (value) => (value ? [value, value] : [false, "Not set"]); + } + } + let comodelDomain = kwargs.comodel_domain || []; + const enableCounters = kwargs.enable_counters; + const expand = kwargs.expand; + const limit = kwargs.limit; + if (field.type === "many2many") { + const comodelRecords = this.mockSearchRead( + field.relation, + [comodelDomain, fieldNames], + { limit } + ); + if (expand && limit && comodelRecords.length === limit) { + return { error_msg: "Too many items to display." }; + } + + const groupDomain = kwargs.group_domain; + const fieldRange = []; + for (const record of comodelRecords) { + const values = { + id: record.id, + display_name: record.display_name, + }; + let groupId; + if (groupBy) { + const [gId, gName] = groupIdName(record[groupBy]); + values.group_id = groupId = gId; + values.group_name = gName; + } + let count; + let inImage; + if (enableCounters || !expand) { + const searchDomain = Domain.combine([ + modelDomain, + [[fieldName, "in", record.id]], + ]).toList(); + let localExtraDomain = extraDomain; + if (groupBy && groupDomain) { + localExtraDomain = Domain.combine([ + localExtraDomain, + groupDomain[JSON.stringify(groupId)] || [], + ]).toList(); + } + const searchCountDomain = Domain.combine([ + searchDomain, + localExtraDomain, + ]).toList(); + if (enableCounters) { + count = this.mockSearchCount(model, [searchCountDomain]); + } + if (!expand) { + if (enableCounters && JSON.stringify(localExtraDomain) === "[]") { + inImage = count; + } else { + inImage = this.mockSearch(model, [searchDomain], { limit: 1 }).length; + } + } + } + if (expand || inImage) { + if (enableCounters) { + values.__count = count; + } + fieldRange.push(values); + } + } + + if (!expand && limit && fieldRange.length === limit) { + return { error_msg: "Too many items to display." }; + } + + return { values: fieldRange }; + } + + if (field.type === "many2one") { + let domainImage; + if (enableCounters || !expand) { + extraDomain = Domain.combine([extraDomain, kwargs.group_domain || []]).toList(); + modelDomain = Domain.combine([modelDomain, kwargs.group_domain || []]).toList(); + const newKwargs = Object.assign({}, kwargs, { + model_domain: modelDomain, + extra_domain: extraDomain, + only_counters: expand, + set_limit: limit && !(expand || groupBy || comodelDomain), + }); + domainImage = this.mockSearchPanelFieldImage(model, fieldName, newKwargs); + } + if (!expand && !groupBy && !comodelDomain.length) { + if (limit && domainImage.size === limit) { + return { error_msg: "Too many items to display." }; + } + return { values: [...domainImage.values()] }; + } + if (!expand) { + const imageElementIds = [...domainImage.keys()].map(Number); + comodelDomain = Domain.combine([ + comodelDomain, + [["id", "in", imageElementIds]], + ]).toList(); + } + const comodelRecords = this.mockSearchRead( + field.relation, + [comodelDomain, fieldNames], + { limit } + ); + if (limit && comodelRecords.length === limit) { + return { error_msg: "Too many items to display." }; + } + + const fieldRange = []; + for (const record of comodelRecords) { + const values = { + id: record.id, + display_name: record.display_name, + }; + if (groupBy) { + const [groupId, groupName] = groupIdName(record[groupBy]); + values.group_id = groupId; + values.group_name = groupName; + } + if (enableCounters) { + values.__count = domainImage.get(record.id) + ? domainImage.get(record.id).__count + : 0; + } + fieldRange.push(values); + } + return { values: fieldRange }; + } + } + mockWrite(modelName, args) { args[0].forEach((id) => this.writeRecord(modelName, args[1], id)); return true; diff --git a/addons/web/static/tests/helpers/mock_services.js b/addons/web/static/tests/helpers/mock_services.js index ad14eb187c72a..4b6695604fc27 100644 --- a/addons/web/static/tests/helpers/mock_services.js +++ b/addons/web/static/tests/helpers/mock_services.js @@ -94,37 +94,6 @@ export function makeFakeUserService(values) { }; } -/*export function makeFakeMenusService(menuData?: MenuData): Service { - const _menuData = menuData || { - root: { id: "root", children: [1], name: "root" }, - 1: { id: 1, children: [], name: "App0" }, - }; - return { - name: "menus", - start() { - const menusService = { - getMenu(menuId: keyof MenuData) { - return _menuData![menuId]; - }, - getApps() { - return this.getMenu("root").children.map((mid) => this.getMenu(mid)); - }, - getAll() { - return Object.values(_menuData); - }, - getMenuAsTree(menuId: keyof MenuData) { - const menu = this.getMenu(menuId) as MenuTree; - if (!menu.childrenTree) { - menu.childrenTree = menu.children.map((mid: Menu["id"]) => this.getMenuAsTree(mid)); - } - return menu; - }, - }; - return menusService; - }, - }; -}*/ - function buildMockRPC(mockRPC) { return async function (...args) { if (this instanceof Component && this.__owl__.status === 5) { diff --git a/addons/web/static/tests/helpers/utils.js b/addons/web/static/tests/helpers/utils.js index 06f34e7c263e1..c7cd3d7830465 100644 --- a/addons/web/static/tests/helpers/utils.js +++ b/addons/web/static/tests/helpers/utils.js @@ -3,6 +3,7 @@ import { isMacOS } from "@web/core/browser/feature_detection"; import { patch, unpatch } from "@web/core/utils/patch"; import { registerCleanup } from "./cleanup"; +import { download } from "@web/core/network/download"; const { Settings } = luxon; @@ -128,3 +129,7 @@ export function triggerHotkey(hotkey, altIsOptional = false, eventAttrs = {}) { export async function legacyExtraNextTick() { return nextTick(); } + +export function mockDownload(cb) { + patchWithCleanup(download, { _download: cb }); +} diff --git a/addons/web/static/tests/legacy/helpers/test_env.js b/addons/web/static/tests/legacy/helpers/test_env.js index fd455240ab5a5..78debbbe1a868 100644 --- a/addons/web/static/tests/legacy/helpers/test_env.js +++ b/addons/web/static/tests/legacy/helpers/test_env.js @@ -22,19 +22,28 @@ odoo.define('web.test_env', async function (require) { // time and they never change qweb = new owl.QWeb({ templates: session.owlTemplates }); } - const database = { - parameters: { - code: "en_US", - date_format: '%m/%d/%Y', - decimal_point: ".", - direction: 'ltr', - grouping: [], - thousands_sep: ",", - time_format: '%H:%M:%S', - }, + + const defaultTranslationParamters = { + code: "en_US", + date_format: '%m/%d/%Y', + decimal_point: ".", + direction: 'ltr', + grouping: [], + thousands_sep: ",", + time_format: '%H:%M:%S', }; + + let _t; + if ('_t' in env) { + _t = Object.assign(env._t, {database: env._t.database || {}}) + } else { + _t = Object.assign(((s) => s), { database: {} }); + } + + _t.database.parameters = Object.assign(defaultTranslationParamters, _t.database.parameters); + const defaultEnv = { - _t: env._t || Object.assign((s => s), { database }), + _t, browser: Object.assign({ setTimeout: window.setTimeout.bind(window), clearTimeout: window.clearTimeout.bind(window), @@ -76,6 +85,7 @@ odoo.define('web.test_env', async function (require) { throw new Error(`No method to perform RPC`); }, url: session.url, + getTZOffset: (() => 0), }, env.session), }; return Object.assign(env, defaultEnv); diff --git a/addons/web/static/tests/legacy/helpers/test_utils_mock.js b/addons/web/static/tests/legacy/helpers/test_utils_mock.js index 8cf2c79e52816..17eb9121ac805 100644 --- a/addons/web/static/tests/legacy/helpers/test_utils_mock.js +++ b/addons/web/static/tests/legacy/helpers/test_utils_mock.js @@ -51,11 +51,14 @@ const DebouncedField = basic_fields.DebouncedField; async function _getMockedOwlEnv(params, mockServer) { params.env = params.env || {}; + const database = {parameters: params.translateParameters || {}}; + // build the env const favoriteFilters = params.favoriteFilters; const debug = params.debug; const services = {}; const env = Object.assign({}, params.env, { + _t: params.env && params.env._t || Object.assign((s => s), { database }), browser: Object.assign({ fetch: (resource, init) => mockServer.performFetch(resource, init), }, params.env.browser), diff --git a/addons/web/static/tests/legacy/report/client_action_tests.js b/addons/web/static/tests/legacy/report/client_action_tests.js deleted file mode 100644 index 4607b882e905c..0000000000000 --- a/addons/web/static/tests/legacy/report/client_action_tests.js +++ /dev/null @@ -1,110 +0,0 @@ -odoo.define('web/static/tests/report/client_action_tests', function (require) { - "use strict"; - - const ControlPanel = require('web.ControlPanel'); - const ReportClientAction = require('report.client_action'); - const testUtils = require("web.test_utils"); - const { patch, unpatch } = require('web.utils'); - - const { createActionManager, dom, mock } = testUtils; - - QUnit.module('Client Action Report', {}, () => { - QUnit.skip("mounted is called once when returning on 'Client Action Report' from breadcrumb", async assert => { - // This test can be removed as soon as we don't mix legacy and owl layers anymore. - assert.expect(7); - - let mountCount = 0; - - // patch the report client action to override its iframe's url so that - // it doesn't trigger an RPC when it is appended to the DOM (for this - // usecase, using removeSRCAttribute doesn't work as the RPC is - // triggered as soon as the iframe is in the DOM, even if its src - // attribute is removed right after) - mock.patch(ReportClientAction, { - start: function () { - var self = this; - return this._super.apply(this, arguments).then(function () { - self._rpc({route: self.iframe.getAttribute('src')}); - self.iframe.setAttribute('src', 'about:blank'); - }); - } - }); - - patch(ControlPanel.prototype, 'test.ControlPanel', { - mounted() { - mountCount = mountCount + 1; - this.__uniqueId = mountCount; - assert.step(`mounted ${this.__uniqueId}`); - this.__superMounted = this._super.bind(this); - this.__superMounted(...arguments); - }, - willUnmount() { - assert.step(`willUnmount ${this.__uniqueId}`); - this.__superMounted(...arguments); - }, - }); - const actionManager = await createActionManager({ - actions: [ - { - id: 42, - name: "Client Action Report", - tag: 'report.client_action', - type: 'ir.actions.report', - report_type: 'qweb-html', - }, - { - id: 43, - type: "ir.actions.act_window", - res_id: 1, - res_model: "partner", - views: [ - [false, "form"], - ], - } - ], - archs: { - 'partner,false,form': '
', - 'partner,false,search': '', - }, - data: { - partner: { - fields: { - display_name: { string: "Displayed name", type: "char" }, - }, - records: [ - {id: 1, display_name: "Genda Swami"}, - ], - }, - }, - mockRPC: function (route) { - if (route === '/report/html/undefined?context=%7B%7D') { - return Promise.resolve('Go to detail view'); - } - return this._super.apply(this, arguments); - }, - intercepts: { - do_action: ev => actionManager.doAction(ev.data.action, ev.data.options), - }, - }); - - await actionManager.doAction(42); - // simulate an action as we are not able to reproduce a real doAction using 'Client Action Report' - await actionManager.doAction(43); - await dom.click(actionManager.$('.breadcrumb-item:first')); - actionManager.destroy(); - - assert.verifySteps([ - 'mounted 1', - 'willUnmount 1', - 'mounted 2', - 'willUnmount 2', - 'mounted 3', - 'willUnmount 3', - ]); - - unpatch(ControlPanel.prototype, 'test.ControlPanel'); - mock.unpatch(ReportClientAction); - }); - }); - -}); diff --git a/addons/web/static/tests/legacy/services/crash_manager_tests.js b/addons/web/static/tests/legacy/services/crash_manager_tests.js deleted file mode 100644 index ebff3bd15655b..0000000000000 --- a/addons/web/static/tests/legacy/services/crash_manager_tests.js +++ /dev/null @@ -1,64 +0,0 @@ -odoo.define('web.crash_manager_tests', function (require) { - "use strict"; - const CrashManager = require('web.CrashManager').CrashManager; - const Bus = require('web.Bus'); - const testUtils = require('web.test_utils'); - const core = require('web.core'); - const ajax = require('web.ajax'); - const createActionManager = testUtils.createActionManager; - -QUnit.module('Services', {}, function() { - - QUnit.module('CrashManager'); - - QUnit.skip("Execute an action and close the RedirectWarning when clicking on the primary button", async function (assert) { - assert.expect(4); - - var dummy_action_name = "crash_manager_tests_dummy_action"; - var dummy_action = function() { - assert.step('do_action'); - }; - core.action_registry.add(dummy_action_name, dummy_action); - - // What we want to test is a do-action triggered by the crashManagerService - // the intercept feature of testUtilsMock is not fit for this, because it is too low in the hierarchy - const bus = new Bus(); - bus.on('do-action', null, payload => { - const { action, options } = payload; - actionManager.doAction(action, options); - }); - - var actionManager = await createActionManager({ - actions: [dummy_action], - services: { - crash_manager: CrashManager, - }, - bus - }); - actionManager.call('crash_manager', 'rpc_error', { - code: 200, - data: { - name: "odoo.exceptions.RedirectWarning", - arguments: [ - "crash_manager_tests_warning_modal_text", - dummy_action_name, - "crash_manager_tests_button_text", - null, - ] - } - }); - await ajax.loadJS('/web/static/lib/stacktracejs/stacktrace.js'); - await testUtils.nextTick(); - - var modal_selector = 'div.modal:contains("crash_manager_tests_warning_modal_text")'; - assert.containsOnce($, modal_selector, "Warning Modal should be opened"); - - await testUtils.dom.click($(modal_selector).find('button.btn-primary')); - - assert.containsNone($, modal_selector, "Warning Modal should be closed"); - assert.verifySteps(['do_action'], "Warning Modal Primary Button Action should be executed"); - - actionManager.destroy(); - }); -}); -}); diff --git a/addons/web/static/tests/legacy/views/abstract_view_tests.js b/addons/web/static/tests/legacy/views/abstract_view_tests.js index 6eadb56c0f654..d3c42f0934d3c 100644 --- a/addons/web/static/tests/legacy/views/abstract_view_tests.js +++ b/addons/web/static/tests/legacy/views/abstract_view_tests.js @@ -5,7 +5,7 @@ var AbstractView = require('web.AbstractView'); var ajax = require('web.ajax'); var testUtils = require('web.test_utils'); -var createActionManager = testUtils.createActionManager; +const { createWebClient, doAction, getActionManagerTestConfig } = require('@web/../tests/webclient/actions/helpers'); var createView = testUtils.createView; QUnit.module('Views', { @@ -110,36 +110,37 @@ QUnit.module('Views', { await proms.c.resolve(); }); - QUnit.skip('group_by from context can be a string, instead of a list of strings', async function (assert) { + QUnit.test('group_by from context can be a string, instead of a list of strings', async function (assert) { assert.expect(1); - var actionManager = await createActionManager({ - actions: [{ - id: 1, - name: 'Foo', - res_model: 'foo', - type: 'ir.actions.act_window', - views: [[false, 'list']], - context: { - group_by: 'bar', - }, - }], - archs: { + const testConfig = getActionManagerTestConfig(); + Object.assign(testConfig.serverData, { + actions: { + 1:{ + id: 1, + name: 'Foo', + res_model: 'foo', + type: 'ir.actions.act_window', + views: [[false, 'list']], + context: { + group_by: 'bar', + }, + } + }, + views: { 'foo,false,list': '', 'foo,false,search': '', }, - data: this.data, - mockRPC: function (route, args) { - if (args.method === 'web_read_group') { - assert.deepEqual(args.kwargs.groupby, ['bar']); - } - return this._super.apply(this, arguments); - }, + models: this.data }); - await actionManager.doAction(1); - - actionManager.destroy(); + const mockRPC = (route, args) => { + if (args.method === 'web_read_group') { + assert.deepEqual(args.kwargs.groupby, ['bar']); + } + }; + const webClient = await createWebClient({testConfig, mockRPC}); + await doAction(webClient, 1); }); }); diff --git a/addons/web/static/tests/legacy/views/calendar_tests.js b/addons/web/static/tests/legacy/views/calendar_tests.js index 0c34982c26588..35672d97b941a 100644 --- a/addons/web/static/tests/legacy/views/calendar_tests.js +++ b/addons/web/static/tests/legacy/views/calendar_tests.js @@ -15,7 +15,10 @@ var testUtils = require('web.test_utils'); var session = require('web.session'); const Widget = require('web.Widget'); -var createActionManager = testUtils.createActionManager; +const { patchWithCleanup } = require("@web/../tests/helpers/utils"); + +const { createWebClient, getActionManagerTestConfig, doAction } = require('@web/../tests/webclient/actions/helpers'); +let testConfig; CalendarRenderer.include({ getAvatars: function () { @@ -41,7 +44,9 @@ function _preventScroll(ev) { QUnit.module('Views', { beforeEach: function () { window.addEventListener('scroll', _preventScroll, true); - session.uid = -1; // TO CHECK + patchWithCleanup(session, { + uid: -1 + }); this.data = { event: { fields: { @@ -125,6 +130,11 @@ QUnit.module('Views', { ] }, }; + testConfig = getActionManagerTestConfig(); + this.data.event.methods = { + check_access_rights: this.data.event.check_access_rights, + } + Object.assign(testConfig.serverData, {models: this.data}); }, afterEach: function () { window.removeEventListener('scroll', _preventScroll, true); @@ -293,15 +303,15 @@ QUnit.module('Views', { calendar.destroy(); }); - QUnit.skip('breadcrumbs are updated with the displayed period', async function (assert) { + QUnit.test('breadcrumbs are updated with the displayed period', async function (assert) { assert.expect(4); - var archs = { + testConfig.serverData.views = { 'event,1,calendar': '', 'event,false,search': '', }; - var actions = [{ + testConfig.serverData.actions[1] = { id: 1, flags: { initialDate: initialDate, @@ -310,16 +320,10 @@ QUnit.module('Views', { res_model: 'event', type: 'ir.actions.act_window', views: [[1, 'calendar']], - }]; - - var actionManager = await createActionManager({ - actions: actions, - archs: archs, - data: this.data, - }); + }; - await actionManager.doAction(1); - await testUtils.nextTick(); + const webClient = await createWebClient({ testConfig }); + await doAction(webClient, 1); // displays month mode by default assert.strictEqual($('.o_control_panel .breadcrumb-item').text(), @@ -339,8 +343,6 @@ QUnit.module('Views', { await testUtils.dom.click($('.o_control_panel .o_calendar_button_year')); assert.strictEqual($('.o_control_panel .breadcrumb-item').text(), 'Meetings Test (2016)', "should display the current year"); - - actionManager.destroy(); }); QUnit.test('create and change events', async function (assert) { @@ -2863,10 +2865,10 @@ QUnit.module('Views', { testUtils.mock.unpatch(Dialog); }); - QUnit.skip('calendar is configured to have no groupBy menu', async function (assert) { + QUnit.test('calendar is configured to have no groupBy menu', async function (assert) { assert.expect(1); - var archs = { + testConfig.serverData.views = { 'event,1,calendar': '', }; - var actions = [{ + testConfig.serverData.actions[1] = { id: 1, name: 'some action', res_model: 'event', type: 'ir.actions.act_window', views: [[1, 'calendar']] - }]; + }; - var actionManager = await createActionManager({ - actions: actions, - archs: archs, - data: this.data, - }); + const webClient = await createWebClient({ testConfig }); - await actionManager.doAction(1); - assert.containsNone(actionManager.$('.o_control_panel .o_search_options span.fa.fa-bars'), + await doAction(webClient, 1); + assert.containsNone($(webClient.el).find('.o_control_panel .o_search_options span.fa.fa-bars'), "the control panel has no groupBy menu"); - actionManager.destroy(); }); QUnit.test('timezone does not affect current day', async function (assert) { @@ -3368,34 +3365,28 @@ QUnit.module('Views', { calendar.destroy(); }); - QUnit.skip('initial_date given in the context', async function (assert) { + QUnit.test('initial_date given in the context', async function (assert) { assert.expect(1); - var archs = { + testConfig.serverData.views = { 'event,1,calendar': '', 'event,false,search': '', }; - var actions = [{ + testConfig.serverData.actions[1] = { id: 1, name: 'context initial date', res_model: 'event', type: 'ir.actions.act_window', views: [[1, 'calendar']], context: {initial_date: initialDate} - }]; - - var actionManager = await createActionManager({ - actions: actions, - archs: archs, - data: this.data, - }); + }; - await actionManager.doAction(1); + const webClient = await createWebClient({ testConfig }); + await doAction(webClient, 1); await testUtils.nextTick(); assert.strictEqual($('.o_control_panel .breadcrumb-item').text(), 'context initial date (December 12, 2016)', "should display day passed in the context"); - actionManager.destroy(); }); QUnit.test('default week start (US) month mode', async function (assert) { diff --git a/addons/web/static/tests/legacy/views/form_tests.js b/addons/web/static/tests/legacy/views/form_tests.js index 0c9e8be3c6a52..686991255e19e 100644 --- a/addons/web/static/tests/legacy/views/form_tests.js +++ b/addons/web/static/tests/legacy/views/form_tests.js @@ -8,7 +8,6 @@ var concurrency = require('web.concurrency'); var core = require('web.core'); var fieldRegistry = require('web.field_registry'); const fieldRegistryOwl = require('web.field_registry_owl'); -const FormController = require('web.FormController'); const FormRenderer = require('web.FormRenderer'); var FormView = require('web.FormView'); var mixins = require('web.mixins'); @@ -23,8 +22,11 @@ var Widget = require('web.Widget'); var _t = core._t; const cpHelpers = testUtils.controlPanel; var createView = testUtils.createView; -var createActionManager = testUtils.createActionManager; +const { legacyExtraNextTick } = require("@web/../tests/helpers/utils"); +const { createWebClient, doAction, getActionManagerTestConfig } = require('@web/../tests/webclient/actions/helpers'); + +let testConfig; QUnit.module('Views', { beforeEach: function () { this.data = { @@ -152,6 +154,15 @@ QUnit.module('Views', { type: 'ir.actions.act_window', views: [[false, 'kanban'], [false, 'form']], }]; + + testConfig = getActionManagerTestConfig(); + + // map legacy test data + const actions = {}; + this.actions.forEach((act) => { + actions[act.xmlId || act.id] = act; + }); + Object.assign(testConfig.serverData, {actions, models: this.data}); }, }, function () { @@ -489,78 +500,68 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.skip('Form and subview with _view_ref contexts', async function (assert) { - assert.expect(2); + QUnit.test('Form and subview with _view_ref contexts', async function (assert) { + assert.expect(3); - this.data.product.fields.partner_type_ids = {string: "one2many field", type: "one2many", relation: "partner_type"}, - this.data.product.records = [{id: 1, name: 'Tromblon', partner_type_ids: [12,14]}]; - this.data.partner.records[0].product_id = 1; + testConfig.serverData.models.product.fields.partner_type_ids = {string: "one2many field", type: "one2many", relation: "partner_type"}, + testConfig.serverData.models.product.records = [{id: 1, name: 'Tromblon', partner_type_ids: [12,14]}]; + testConfig.serverData.models.partner.records[0].product_id = 1; - var actionManager = await createActionManager({ - data: this.data, - archs: { - 'product,false,form': '
'+ - ''+ - '' + - '', - - 'partner_type,false,list': ''+ - ''+ - '', - 'product,false,search': '', - }, - mockRPC: function (route, args) { - if (args.method === 'load_views') { - var context = args.kwargs.context; - if (args.model === 'product') { - assert.deepEqual(context, {tree_view_ref: 'some_tree_view'}, - 'The correct _view_ref should have been sent to the server, first time'); - } - if (args.model === 'partner_type') { - assert.deepEqual(context, { - base_model_name: 'product', - tree_view_ref: 'some_other_tree_view', - }, 'The correct _view_ref should have been sent to the server for the subview'); - } - } - return this._super.apply(this, arguments); - }, - }); + testConfig.serverData.views = { + 'product,false,form': '
'+ + ''+ + '' + + '', - var form = await createView({ - View: FormView, - model: 'partner', - data: this.data, - arch: '
' + + 'partner_type,false,list': ''+ + ''+ + '', + 'product,false,search': '', + 'partner,false,form': '' + '' + '' + '', - res_id: 1, + 'partner,false,search': '', + }; - mockRPC: function(route, args) { - if (args.method === 'get_formview_action') { - return Promise.resolve({ - res_id: 1, - type: 'ir.actions.act_window', - target: 'current', - res_model: args.model, - context: args.kwargs.context, - 'view_mode': 'form', - 'views': [[false, 'form']], - }); + const mockRPC = (route, args) => { + if (args.method === 'load_views') { + var context = args.kwargs.context; + if (args.model === 'product') { + assert.strictEqual(context.tree_view_ref, 'some_tree_view', + 'The correct _view_ref should have been sent to the server, first time'); } - return this._super(route, args); - }, + if (args.model === 'partner_type') { + assert.strictEqual(context.base_model_name, 'product', + 'The correct base_model_name should have been sent to the server for the subview'); + assert.strictEqual(context.tree_view_ref, 'some_other_tree_view', + 'The correct _view_ref should have been sent to the server for the subview'); + } + } + if (args.method === 'get_formview_action') { + return Promise.resolve({ + res_id: 1, + type: 'ir.actions.act_window', + target: 'current', + res_model: args.model, + context: args.kwargs.context, + 'view_mode': 'form', + 'views': [[false, 'form']], + }); + } + }; - interceptsPropagate: { - do_action: function (ev) { - actionManager.doAction(ev.data.action); - }, - }, + const webClient = await createWebClient({testConfig, mockRPC}); + await doAction(webClient, { + res_id: 1, + type: 'ir.actions.act_window', + target: 'current', + res_model: 'partner', + 'view_mode': 'form', + 'views': [[false, 'form']], }); - await testUtils.dom.click(form.$('.o_field_widget[name="product_id"]')); - form.destroy(); - actionManager.destroy(); + + await testUtils.dom.click(webClient.el.querySelector('.o_field_widget[name="product_id"]')); }); QUnit.test('invisible fields are properly hidden', async function (assert) { @@ -6812,10 +6813,16 @@ QUnit.module('Views', { _t.database.multi_lang = multi_lang; }); + // LPE: map translationService to session + // with loading translations only once QUnit.skip('translation alerts preseved on reverse breadcrumb', async function (assert) { assert.expect(2); - this.data['ir.translation'] = { + testConfig.localizationParameters = { + multiLang: true, + }; + + testConfig.serverData.models['ir.translation'] = { fields: { name: { string: "name", type: "char" }, source: {string: "Source", type: "char"}, @@ -6824,12 +6831,9 @@ QUnit.module('Views', { records: [], }; - this.data.partner.fields.foo.translate = true; - - var multi_lang = _t.database.multi_lang; - _t.database.multi_lang = true; + testConfig.serverData.models.partner.fields.foo.translate = true; - var archs = { + testConfig.serverData.views = { 'partner,false,form': '
' + '' + '' + @@ -6844,51 +6848,41 @@ QUnit.module('Views', { 'ir.translation,false,search': '', }; - var actions = [{ - id: 1, - name: 'Partner', - res_model: 'partner', - type: 'ir.actions.act_window', - views: [[false, 'form']], - }, { - id: 2, - name: 'Translate', - res_model: 'ir.translation', - type: 'ir.actions.act_window', - views: [[false, 'list']], - target: 'current', - flags: {'search_view': true, 'action_buttons': true}, - }]; - - var actionManager = await createActionManager({ - actions: actions, - archs: archs, - data: this.data, - }); + testConfig.serverData.actions = { + 1: { + id: 1, + name: 'Partner', + res_model: 'partner', + type: 'ir.actions.act_window', + views: [[false, 'form']], + }, + 2: { + id: 2, + name: 'Translate', + res_model: 'ir.translation', + type: 'ir.actions.act_window', + views: [[false, 'list']], + target: 'current', + flags: {'search_view': true, 'action_buttons': true}, + } + }; - await actionManager.doAction(1); - actionManager.$('input[name="foo"]').val("test").trigger("input"); - await testUtils.dom.click(actionManager.$('.o_form_button_save')); + const webClient = await createWebClient({ testConfig }); + await doAction(webClient, 1); + $(webClient.el).find('input[name="foo"]').val("test").trigger("input"); + //await new Promise(() => {}); + await testUtils.dom.click(webClient.el.querySelector('.o_form_button_save')); + await legacyExtraNextTick(); - assert.strictEqual(actionManager.$('.o_form_view .alert > div').length, 1, + assert.containsOnce(webClient, '.o_form_view .alert > div', "should have a translation alert"); - var currentController = actionManager.getCurrentController().widget; - await actionManager.doAction(2, { - on_reverse_breadcrumb: function () { - if (!_.isEmpty(currentController.renderer.alertFields)) { - currentController.renderer.displayTranslationAlert(); - } - return false; - }, - }); + await doAction(webClient, 2); await testUtils.dom.click($('.o_control_panel .breadcrumb a:first')); - assert.strictEqual(actionManager.$('.o_form_view .alert > div').length, 1, + await legacyExtraNextTick(); + assert.containsOnce(webClient, '.o_form_view .alert > div', "should have a translation alert"); - - actionManager.destroy(); - _t.database.multi_lang = multi_lang; }); QUnit.test('translate event correctly handled with multiple controllers', async function (assert) { @@ -8819,35 +8813,32 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.skip('discard after a failed save', async function (assert) { + QUnit.test('discard after a failed save', async function (assert) { assert.expect(2); - var actionManager = await createActionManager({ - data: this.data, - archs: { - 'partner,false,form': '' + - '' + - '' + - '', - 'partner,false,kanban': '' + - '', - 'partner,false,search': '', - }, - actions: this.actions, - }); + testConfig.serverData.views = { + 'partner,false,form': '
' + + '' + + '' + + '', + 'partner,false,kanban': '' + + '', + 'partner,false,search': '', + }; - await actionManager.doAction(1); + const webClient = await createWebClient({ testConfig }); + await doAction(webClient, 1); await testUtils.dom.click('.o_control_panel .o-kanban-button-new'); + await legacyExtraNextTick(); //cannot save because there is a required field await testUtils.dom.click('.o_control_panel .o_form_button_save'); + await legacyExtraNextTick(); await testUtils.dom.click('.o_control_panel .o_form_button_cancel'); - - assert.containsNone(actionManager, '.o_form_view'); - assert.containsOnce(actionManager, '.o_kanban_view'); - - actionManager.destroy(); + await legacyExtraNextTick(); + assert.containsNone(webClient, '.o_form_view'); + assert.containsOnce(webClient, '.o_kanban_view'); }); QUnit.test("one2many create record dialog shouldn't have a 'remove' button", async function (assert) { @@ -9725,205 +9716,206 @@ QUnit.module('Views', { assert.verifySteps(['mounted', 'willUnmount']); }); - QUnit.skip('Auto save: save when page changed', async function (assert) { + QUnit.test('Auto save: save when page changed', async function (assert) { assert.expect(10); - const actions = [{ + testConfig.serverData.actions[1] = { id: 1, name: 'Partner', res_model: 'partner', type: 'ir.actions.act_window', views: [[false, 'list'], [false, 'form']], - }]; + }; - const actionManager = await createActionManager({ - actions, - data: this.data, - archs: { - 'partner,false,list': ` - + testConfig.serverData.views = { + 'partner,false,list': ` + + + + `, + 'partner,false,form': ` +
+ - - `, - 'partner,false,form': ` - - - - - - `, - 'partner,false,search': '', - }, - mockRPC(route, { args, method }) { - if (method === 'write') { - assert.deepEqual(args, [ - [1], - { name: "aaa" }, - ]); - } - return this._super(...arguments); - }, - }); - await actionManager.doAction(1); +
+ + `, + 'partner,false,search': '', + }; + + const mockRPC = (route, args) => { + if (args.method === 'write') { + assert.deepEqual(args.args, [ + [1], + { name: "aaa" }, + ]); + } + }; + + const webClient = await createWebClient({ testConfig , mockRPC }); - await testUtils.dom.click(actionManager.$('.o_data_row:first')); - assert.strictEqual(actionManager.$('.breadcrumb').text(), 'Partnerfirst record'); + await doAction(webClient, 1); - await testUtils.dom.click(actionManager.$('.o_form_button_edit')); - await testUtils.fields.editInput(actionManager.$('.o_field_widget[name="name"]'), 'aaa'); + await testUtils.dom.click($(webClient.el).find('.o_data_row:first')); + await legacyExtraNextTick(); + assert.strictEqual($(webClient.el).find('.breadcrumb').text(), 'Partnerfirst record'); - await testUtils.controlPanel.pagerNext(actionManager); - assert.containsOnce(actionManager, '.o_form_editable'); - assert.strictEqual(actionManager.$('.breadcrumb').text(), 'Partnersecond record'); - assert.strictEqual(actionManager.$('.o_field_widget[name="name"]').val(), 'name'); + await testUtils.dom.click($(webClient.el).find('.o_form_button_edit')); + await testUtils.fields.editInput($(webClient.el).find('.o_field_widget[name="name"]'), 'aaa'); - await testUtils.dom.click(actionManager.$('.o_form_button_cancel')); - assert.strictEqual(actionManager.$('.breadcrumb').text(), 'Partnersecond record'); - assert.strictEqual(actionManager.$('.o_field_widget[name="name"]').text(), 'name'); + await testUtils.controlPanel.pagerNext(webClient); + await legacyExtraNextTick(); + assert.containsOnce(webClient, '.o_form_editable'); + assert.strictEqual($(webClient.el).find('.breadcrumb').text(), 'Partnersecond record'); + assert.strictEqual($(webClient.el).find('.o_field_widget[name="name"]').val(), 'name'); - await testUtils.controlPanel.pagerPrevious(actionManager); - assert.containsOnce(actionManager, '.o_form_readonly'); - assert.strictEqual(actionManager.$('.breadcrumb').text(), 'Partnerfirst record'); - assert.strictEqual(actionManager.$('.o_field_widget[name="name"]').text(), 'aaa'); + await testUtils.dom.click($(webClient.el).find('.o_form_button_cancel')); + assert.strictEqual($(webClient.el).find('.breadcrumb').text(), 'Partnersecond record'); + assert.strictEqual($(webClient.el).find('.o_field_widget[name="name"]').text(), 'name'); - actionManager.destroy(); + await testUtils.controlPanel.pagerPrevious(webClient); + await legacyExtraNextTick(); + assert.containsOnce(webClient, '.o_form_readonly'); + assert.strictEqual($(webClient.el).find('.breadcrumb').text(), 'Partnerfirst record'); + assert.strictEqual($(webClient.el).find('.o_field_widget[name="name"]').text(), 'aaa'); }); - QUnit.skip('Auto save: save when breadcrumb clicked', async function (assert) { + QUnit.test('Auto save: save when breadcrumb clicked', async function (assert) { assert.expect(7); - const actions = [{ + testConfig.serverData.actions[1] = { id: 1, name: 'Partner', res_model: 'partner', type: 'ir.actions.act_window', views: [[false, 'list'], [false, 'form']], - }]; + }; - const actionManager = await createActionManager({ - actions, - data: this.data, - archs: { - 'partner,false,list': ` - + testConfig.serverData.views = { + 'partner,false,list': ` + + + + `, + 'partner,false,form': ` +
+ - - `, - 'partner,false,form': ` - - - - - - `, - 'partner,false,search': '', - }, - mockRPC(route, { args, method }) { - if (method === 'write') { - assert.deepEqual(args, [ - [1], - { name: "aaa" }, - ]); - } - return this._super(...arguments); - }, - }); - await actionManager.doAction(1); +
+ + `, + 'partner,false,search': '', + }; + + const mockRPC = (route, args) => { + if (args.method === 'write') { + assert.deepEqual(args.args, [ + [1], + { name: "aaa" }, + ]); + } + }; + + const webClient = await createWebClient({ testConfig , mockRPC }); - await testUtils.dom.click(actionManager.$('.o_data_row:first')); - assert.strictEqual(actionManager.$('.breadcrumb').text(), 'Partnerfirst record'); + await doAction(webClient, 1); - await testUtils.dom.click(actionManager.$('.o_form_button_edit')); - await testUtils.fields.editInput(actionManager.$('.o_field_widget[name="name"]'), 'aaa'); + await testUtils.dom.click($(webClient.el).find('.o_data_row:first')); + await legacyExtraNextTick(); + assert.strictEqual($(webClient.el).find('.breadcrumb').text(), 'Partnerfirst record'); - await testUtils.dom.click(actionManager.$('.breadcrumb-item.o_back_button')); + await testUtils.dom.click($(webClient.el).find('.o_form_button_edit')); + await testUtils.fields.editInput($(webClient.el).find('.o_field_widget[name="name"]'), 'aaa'); - assert.strictEqual(actionManager.$('.breadcrumb').text(), 'Partner'); - assert.strictEqual(actionManager.$('.o_field_cell[name="name"]:first').text(), 'aaa'); + await testUtils.dom.click($(webClient.el).find('.breadcrumb-item.o_back_button')); + await legacyExtraNextTick(); - await testUtils.dom.click(actionManager.$('.o_data_row:first')); - assert.containsOnce(actionManager, '.o_form_readonly'); - assert.strictEqual(actionManager.$('.breadcrumb').text(), 'Partnerfirst record'); - assert.strictEqual(actionManager.$('.o_field_widget[name="name"]').text(), 'aaa'); + assert.strictEqual($(webClient.el).find('.breadcrumb').text(), 'Partner'); + assert.strictEqual($(webClient.el).find('.o_field_cell[name="name"]:first').text(), 'aaa'); - actionManager.destroy(); + await testUtils.dom.click($(webClient.el).find('.o_data_row:first')); + await legacyExtraNextTick(); + assert.containsOnce(webClient, '.o_form_readonly'); + assert.strictEqual($(webClient.el).find('.breadcrumb').text(), 'Partnerfirst record'); + assert.strictEqual($(webClient.el).find('.o_field_widget[name="name"]').text(), 'aaa'); }); - QUnit.skip('Auto save: save when action changed', async function (assert) { + QUnit.test('Auto save: save when action changed', async function (assert) { assert.expect(6); - const actions = [{ + testConfig.serverData.actions[1] = { id: 1, name: 'Partner', res_model: 'partner', type: 'ir.actions.act_window', views: [[false, 'list'], [false, 'form']], - }, { + }; + + testConfig.serverData.actions[2] = { id: 2, name: 'Other action', res_model: 'partner', type: 'ir.actions.act_window', views: [[false, 'kanban']], - }]; + }; - const actionManager = await createActionManager({ - actions, - data: this.data, - archs: { - 'partner,false,list': ` - - - - `, - 'partner,false,form': ` -
- - - -
- `, - 'partner,false,search': '', - 'partner,false,kanban': ` - + testConfig.serverData.views = { + 'partner,false,list': ` + + + + `, + 'partner,false,form': ` +
+ - - -
-
-
- - `, - }, - mockRPC(route, { args, method }) { - if (method === 'write') { - assert.deepEqual(args, [ - [1], - { name: "aaa" }, - ]); - } - return this._super(...arguments); - }, - }); - await actionManager.doAction(1); +
+
+ `, + 'partner,false,search': '', + 'partner,false,kanban': ` + + + + +
+
+
+
+ `, + }; + + const mockRPC = (route, args) => { + if (args.method === 'write') { + assert.deepEqual(args.args, [ + [1], + { name: "aaa" }, + ]); + } + }; + + const webClient = await createWebClient({ testConfig , mockRPC }); - await testUtils.dom.click(actionManager.$('.o_data_row:first')); - assert.strictEqual(actionManager.$('.breadcrumb').text(), 'Partnerfirst record'); + await doAction(webClient, 1); - await testUtils.dom.click(actionManager.$('.o_form_button_edit')); - await testUtils.fields.editInput(actionManager.$('.o_field_widget[name="name"]'), 'aaa'); + await testUtils.dom.click($(webClient.el).find('.o_data_row:first')); + await legacyExtraNextTick(); + assert.strictEqual($(webClient.el).find('.breadcrumb').text(), 'Partnerfirst record'); - await actionManager.doAction(2, { clear_breadcrumbs: true }); + await testUtils.dom.click($(webClient.el).find('.o_form_button_edit')); + await testUtils.fields.editInput($(webClient.el).find('.o_field_widget[name="name"]'), 'aaa'); - assert.strictEqual(actionManager.$('.breadcrumb').text(), 'Other action'); + await doAction(webClient, 2, { clearBreadcrumbs: true }); - await actionManager.doAction(1, { clear_breadcrumbs: true }); + assert.strictEqual($(webClient.el).find('.breadcrumb').text(), 'Other action'); - await testUtils.dom.click(actionManager.$('.o_data_row:first')); - assert.containsOnce(actionManager, '.o_form_readonly'); - assert.strictEqual(actionManager.$('.breadcrumb').text(), 'Partnerfirst record'); - assert.strictEqual(actionManager.$('.o_field_widget[name="name"]').text(), 'aaa'); + await doAction(webClient, 1, { clearBreadcrumbs: true }); - actionManager.destroy(); + await testUtils.dom.click($(webClient.el).find('.o_data_row:first')); + await legacyExtraNextTick(); + assert.containsOnce(webClient, '.o_form_readonly'); + assert.strictEqual($(webClient.el).find('.breadcrumb').text(), 'Partnerfirst record'); + assert.strictEqual($(webClient.el).find('.o_field_widget[name="name"]').text(), 'aaa'); }); QUnit.test('Auto save: save on closing tab/browser', async function (assert) { @@ -10025,51 +10017,52 @@ QUnit.module('Views', { form.destroy(); }); - QUnit.skip('Auto save: save on closing tab/browser (detached form)', async function (assert) { + QUnit.test('Auto save: save on closing tab/browser (detached form)', async function (assert) { assert.expect(3); - const actions = [{ + testConfig.serverData.actions[1] = { id: 1, name: 'Partner', res_model: 'partner', type: 'ir.actions.act_window', views: [[false, 'list'], [false, 'form']], - }]; + }; - const actionManager = await createActionManager({ - actions, - data: this.data, - archs: { - 'partner,false,list': ` - + testConfig.serverData.views = { + 'partner,false,list': ` + + + + `, + 'partner,false,form': ` +
+ - - `, - 'partner,false,form': ` - - - - - - `, - 'partner,false,search': '', - }, - mockRPC(route, { args, method }) { - if (method === 'write') { - assert.step('save'); - } - return this._super(...arguments); - }, - }); - await actionManager.doAction(1); +
+ + `, + 'partner,false,search': '', + }; + + const mockRPC = (route, args) => { + if (args.method === 'write') { + assert.step('save'); + } + }; + + const webClient = await createWebClient({ testConfig , mockRPC }); + + await doAction(webClient, 1); // Click on a row to open a record - await testUtils.dom.click(actionManager.$('.o_data_row:first')); - assert.strictEqual(actionManager.$('.breadcrumb').text(), 'Partnerfirst record'); + await testUtils.dom.click($(webClient.el).find('.o_data_row:first')); + await legacyExtraNextTick(); + assert.strictEqual($(webClient.el).find('.breadcrumb').text(), 'Partnerfirst record'); // Return in the list view to detach the form view - await testUtils.dom.click(actionManager.$('.o_back_button')); - assert.strictEqual(actionManager.$('.breadcrumb').text(), 'Partner'); + await testUtils.dom.click($(webClient.el).find('.o_back_button')); + await legacyExtraNextTick(); + assert.strictEqual($(webClient.el).find('.breadcrumb').text(), 'Partner'); // Simulate tab/browser close in the list window.dispatchEvent(new Event("beforeunload")); @@ -10078,8 +10071,6 @@ QUnit.module('Views', { // write rpc should not trigger because form view has been detached // and list has nothing to save assert.verifySteps([]); - - actionManager.destroy(); }); QUnit.test('Auto save: save on closing tab/browser (onchanges)', async function (assert) { diff --git a/addons/web/static/tests/legacy/views/graph_tests.js b/addons/web/static/tests/legacy/views/graph_tests.js index e9872c31f4426..ed6ede924e998 100644 --- a/addons/web/static/tests/legacy/views/graph_tests.js +++ b/addons/web/static/tests/legacy/views/graph_tests.js @@ -6,6 +6,9 @@ const GraphView = require('web.GraphView'); const testUtils = require('web.test_utils'); const { sortBy } = require('web.utils'); +const { legacyExtraNextTick } = require("@web/../tests/helpers/utils"); +const { createWebClient, doAction, getActionManagerTestConfig } = require('@web/../tests/webclient/actions/helpers'); + const { createView } = testUtils; const cpHelpers = testUtils.controlPanel; const patchDate = testUtils.mock.patchDate; @@ -72,6 +75,7 @@ function checkLegend(assert, graph, expectedLegendLabels) { assert.deepEqual(actualLegendLabels, expectedLegendLabels); } +let testConfig; QUnit.module('Views', { beforeEach: function () { this.data = { @@ -120,6 +124,9 @@ QUnit.module('Views', { }] }, }; + + testConfig = getActionManagerTestConfig(); + Object.assign(testConfig.serverData, {models: this.data}); } }, function () { @@ -783,28 +790,28 @@ QUnit.module('Views', { graph.destroy(); }); - QUnit.skip('measure dropdown consistency', async function (assert) { + QUnit.test('measure dropdown consistency', async function (assert) { assert.expect(2); - const actionManager = await testUtils.createActionManager({ - archs: { - 'foo,false,graph': ` - - - `, - 'foo,false,search': ``, - 'foo,false,kanban': ` - - -
- -
-
-
`, - }, - data: this.data, - }); - await actionManager.doAction({ + testConfig.serverData.views = { + 'foo,false,graph': ` + + + `, + 'foo,false,search': ``, + 'foo,false,kanban': ` + + +
+ +
+
+
`, + }; + + const webClient = await createWebClient({ testConfig }); + + await doAction(webClient, { res_model: 'foo', type: 'ir.actions.act_window', views: [[false, 'graph'], [false, 'kanban']], @@ -815,18 +822,18 @@ QUnit.module('Views', { }, }); - assert.containsOnce(actionManager, '.o_control_panel .o_graph_measures_list', + assert.containsOnce(webClient, '.o_control_panel .o_graph_measures_list', "Measures dropdown is present at init" ); - await cpHelpers.switchView(actionManager, 'kanban'); - await cpHelpers.switchView(actionManager, 'graph'); + await cpHelpers.switchView(webClient, 'kanban'); + await legacyExtraNextTick(); + await cpHelpers.switchView(webClient, 'graph'); + await legacyExtraNextTick(); - assert.containsOnce(actionManager, '.o_control_panel .o_graph_measures_list', + assert.containsOnce(webClient, '.o_control_panel .o_graph_measures_list', "Measures dropdown is present after reload" ); - - actionManager.destroy(); }); QUnit.test('graph view crash when moving from search view using Down key', async function (assert) { diff --git a/addons/web/static/tests/legacy/views/list_tests.js b/addons/web/static/tests/legacy/views/list_tests.js index fdc90033a6d51..9413e467d35f7 100644 --- a/addons/web/static/tests/legacy/views/list_tests.js +++ b/addons/web/static/tests/legacy/views/list_tests.js @@ -24,9 +24,13 @@ var Widget = require('web.Widget'); var _t = core._t; const cpHelpers = testUtils.controlPanel; -var createActionManager = testUtils.createActionManager; var createView = testUtils.createView; +const { legacyExtraNextTick } = require("@web/../tests/helpers/utils"); +const { createWebClient, doAction, getActionManagerTestConfig, loadState } = require('@web/../tests/webclient/actions/helpers'); + +let testConfig; + QUnit.module('Views', { beforeEach: function () { this.data = { @@ -128,6 +132,9 @@ QUnit.module('Views', { }] }, }; + + testConfig = getActionManagerTestConfig(); + Object.assign(testConfig.serverData, {models: this.data}); } }, function () { @@ -1762,45 +1769,42 @@ QUnit.module('Views', { list.destroy(); }); - QUnit.skip('editable list view: check that controlpanel buttons are updating when groupby applied', async function (assert) { + QUnit.test('editable list view: check that controlpanel buttons are updating when groupby applied', async function (assert) { assert.expect(4); - this.data.foo.fields.foo = {string: "Foo", type: "char", required:true}; + testConfig.serverData.models.foo.fields.foo = {string: "Foo", type: "char", required:true}; - var actionManager = await createActionManager({ - actions: [{ - id: 11, - name: 'Partners Action 11', - res_model: 'foo', - type: 'ir.actions.act_window', - views: [[3, 'list']], - search_view_id: [9, 'search'], - }], - archs: { - 'foo,3,list': '', + testConfig.serverData.actions[11] = { + id: 11, + name: 'Partners Action 11', + res_model: 'foo', + type: 'ir.actions.act_window', + views: [[3, 'list']], + search_view_id: [9, 'search'], + }; + testConfig.serverData.views = { + 'foo,3,list': '', - 'foo,9,search': ''+ - '' + - '', - }, - data: this.data, - }); + 'foo,9,search': ''+ + '' + + '', + }; - await actionManager.doAction(11); - await testUtils.dom.click(actionManager.$('.o_list_button_add')); + const webClient = await createWebClient({ testConfig }); - assert.isNotVisible(actionManager.$('.o_list_button_add'), + await doAction(webClient, 11); + await testUtils.dom.click($(webClient.el).find('.o_list_button_add')); + + assert.isNotVisible($(webClient.el).find('.o_list_button_add'), "create button should be invisible"); - assert.isVisible(actionManager.$('.o_list_button_save'), "save button should be visible"); + assert.isVisible($(webClient.el).find('.o_list_button_save'), "save button should be visible"); - await testUtils.dom.click(actionManager.$('.o_dropdown_toggler_btn:contains("Group By")')); - await testUtils.dom.click(actionManager.$('.o_group_by_menu .o_menu_item a:contains("candle")')); + await testUtils.dom.click($(webClient.el).find('.o_dropdown_toggler_btn:contains("Group By")')); + await testUtils.dom.click($(webClient.el).find('.o_group_by_menu .o_menu_item a:contains("candle")')); - assert.isNotVisible(actionManager.$('.o_list_button_add'), "create button should be invisible"); - assert.isNotVisible(actionManager.$('.o_list_button_save'), + assert.isNotVisible($(webClient.el).find('.o_list_button_add'), "create button should be invisible"); + assert.isNotVisible($(webClient.el).find('.o_list_button_save'), "save button should be invisible after applying groupby"); - - actionManager.destroy(); }); QUnit.test('list view not groupable', async function (assert) { @@ -9305,62 +9309,60 @@ QUnit.module('Views', { list.destroy(); }); - QUnit.skip('add filter in a grouped list with a pager', async function (assert) { + QUnit.test('add filter in a grouped list with a pager', async function (assert) { assert.expect(11); - const actionManager = await createActionManager({ - data: this.data, - actions: [{ - id: 11, - name: 'Action 11', - res_model: 'foo', - type: 'ir.actions.act_window', - views: [[3, 'list']], - search_view_id: [9, 'search'], - flags: { - context: { group_by: ['int_field'] }, - }, - }], - archs: { - 'foo,3,list': '', - 'foo,9,search': ` - - - `, - }, - mockRPC: function (route, args) { - if (args.method === 'web_read_group') { - assert.step(JSON.stringify(args.kwargs.domain) + ', ' + args.kwargs.offset); - } - return this._super.apply(this, arguments); + testConfig.serverData.actions[11] = { + id: 11, + name: 'Action 11', + res_model: 'foo', + type: 'ir.actions.act_window', + views: [[3, 'list']], + search_view_id: [9, 'search'], + flags: { + context: { group_by: ['int_field'] }, }, - }); + }; - await actionManager.doAction(11); + testConfig.serverData.views = { + 'foo,3,list': '', + 'foo,9,search': ` + + + `, + }; - assert.containsOnce(actionManager, '.o_list_view'); - assert.strictEqual(actionManager.$('.o_pager_counter').text().trim(), '1-3 / 4'); - assert.containsN(actionManager, '.o_group_header', 3); // page 1 + const mockRPC = (route, args) => { + if (args.method === 'web_read_group') { + assert.step(JSON.stringify(args.kwargs.domain) + ', ' + args.kwargs.offset); + } + }; + + const webClient = await createWebClient({testConfig, mockRPC}); + await doAction(webClient, 11); + + assert.containsOnce(webClient, '.o_list_view'); + assert.strictEqual($(webClient.el).find('.o_pager_counter').text().trim(), '1-3 / 4'); + assert.containsN(webClient, '.o_group_header', 3); // page 1 - await testUtils.dom.click(actionManager.$('.o_pager_next')); // switch to page 2 + await testUtils.dom.click($(webClient.el).find('.o_pager_next')); // switch to page 2 + await legacyExtraNextTick(); - assert.strictEqual(actionManager.$('.o_pager_counter').text().trim(), '4-4 / 4'); - assert.containsN(actionManager, '.o_group_header', 1); // page 2 + assert.strictEqual($(webClient.el).find('.o_pager_counter').text().trim(), '4-4 / 4'); + assert.containsN(webClient, '.o_group_header', 1); // page 2 // toggle a filter -> there should be only one group left (on page 1) - await cpHelpers.toggleFilterMenu(actionManager); - await cpHelpers.toggleMenuItem(actionManager, 0); + await cpHelpers.toggleFilterMenu(webClient); + await cpHelpers.toggleMenuItem(webClient, 0); - assert.strictEqual(actionManager.$('.o_pager_counter').text().trim(), '1-1 / 1'); - assert.containsN(actionManager, '.o_group_header', 1); // page 1 + assert.strictEqual($(webClient.el).find('.o_pager_counter').text().trim(), '1-1 / 1'); + assert.containsN(webClient, '.o_group_header', 1); // page 1 assert.verifySteps([ '[], undefined', '[], 3', '[["bar","=",false]], undefined', ]); - - actionManager.destroy(); }); QUnit.test('editable grouped lists', async function (assert) { @@ -10878,24 +10880,25 @@ QUnit.module('Views', { delete fieldRegistry.map.asyncwidget; }); - QUnit.skip('change the viewType of the current action', async function (assert) { + QUnit.test('change the viewType of the current action', async function (assert) { assert.expect(25); - this.actions = [{ + testConfig.serverData.actions[1] = { id: 1, name: 'Partners Action 1', res_model: 'foo', type: 'ir.actions.act_window', views: [[1, 'kanban']], - }, { + }; + testConfig.serverData.actions[2] = { id: 2, name: 'Partners', res_model: 'foo', type: 'ir.actions.act_window', views: [[false, 'list'], [1, 'kanban']], - }]; + }; - this.archs = { + testConfig.serverData.views = { 'foo,1,kanban': '' + '
' + '
', @@ -10908,94 +10911,82 @@ QUnit.module('Views', { 'foo,false,search': '', }; - var RamStorageService = AbstractStorageService.extend({ - storage: new RamStorage(), - }); + const webClient = await createWebClient({ testConfig }); - var actionManager = await testUtils.createActionManager({ - actions: this.actions, - archs: this.archs, - data: this.data, - services: { - local_storage: RamStorageService, - }, - }); - await actionManager.doAction(2); + await doAction(webClient, 2); - assert.containsOnce(actionManager, '.o_list_view', + assert.containsOnce(webClient, '.o_list_view', "should have rendered a list view"); - assert.containsN(actionManager, 'th', 3, "should display 3 th (selector + 2 fields)"); + assert.containsN(webClient, 'th', 3, "should display 3 th (selector + 2 fields)"); // enable optional field - await testUtils.dom.click(actionManager.$('table .o_optional_columns_dropdown_toggle')); - assert.notOk(actionManager.$('div.o_optional_columns div.dropdown-item [name="m2o"]').is(":checked")); - assert.ok(actionManager.$('div.o_optional_columns div.dropdown-item [name="o2m"]').is(":checked")); - await testUtils.dom.click(actionManager.$('div.o_optional_columns div.dropdown-item:first')); - assert.containsN(actionManager, 'th', 4, "should display 4 th (selector + 3 fields)"); - assert.ok(actionManager.$('th:contains(M2O field)').is(':visible'), + await testUtils.dom.click($(webClient.el).find('table .o_optional_columns_dropdown_toggle')); + assert.notOk($(webClient.el).find('div.o_optional_columns div.dropdown-item [name="m2o"]').is(":checked")); + assert.ok($(webClient.el).find('div.o_optional_columns div.dropdown-item [name="o2m"]').is(":checked")); + await testUtils.dom.click($(webClient.el).find('div.o_optional_columns div.dropdown-item:first')); + assert.containsN(webClient, 'th', 4, "should display 4 th (selector + 3 fields)"); + assert.ok($(webClient.el).find('th:contains(M2O field)').is(':visible'), "should have a visible m2o field"); //m2o field // switch to kanban view - await actionManager.loadState({ + await loadState(webClient, { action: 2, view_type: 'kanban', }); - assert.containsNone(actionManager, '.o_list_view', + assert.containsNone(webClient, '.o_list_view', "should not display the list view anymore"); - assert.containsOnce(actionManager, '.o_kanban_view', + assert.containsOnce(webClient, '.o_kanban_view', "should have switched to the kanban view"); // switch back to list view - await actionManager.loadState({ + await loadState(webClient, { action: 2, view_type: 'list', }); - assert.containsNone(actionManager, '.o_kanban_view', + assert.containsNone(webClient, '.o_kanban_view', "should not display the kanban view anymoe"); - assert.containsOnce(actionManager, '.o_list_view', + assert.containsOnce(webClient, '.o_list_view', "should display the list view"); - assert.containsN(actionManager, 'th', 4, "should display 4 th"); - assert.ok(actionManager.$('th:contains(M2O field)').is(':visible'), + assert.containsN(webClient, 'th', 4, "should display 4 th"); + assert.ok($(webClient.el).find('th:contains(M2O field)').is(':visible'), "should have a visible m2o field"); //m2o field - assert.ok(actionManager.$('th:contains(O2M field)').is(':visible'), + assert.ok($(webClient.el).find('th:contains(O2M field)').is(':visible'), "should have a visible o2m field"); //m2o field // disable optional field - await testUtils.dom.click(actionManager.$('table .o_optional_columns_dropdown_toggle')); - assert.ok(actionManager.$('div.o_optional_columns div.dropdown-item [name="m2o"]').is(":checked")); - assert.ok(actionManager.$('div.o_optional_columns div.dropdown-item [name="o2m"]').is(":checked")); - await testUtils.dom.click(actionManager.$('div.o_optional_columns div.dropdown-item:last input')); - assert.ok(actionManager.$('th:contains(M2O field)').is(':visible'), + await testUtils.dom.click($(webClient.el).find('table .o_optional_columns_dropdown_toggle')); + assert.ok($(webClient.el).find('div.o_optional_columns div.dropdown-item [name="m2o"]').is(":checked")); + assert.ok($(webClient.el).find('div.o_optional_columns div.dropdown-item [name="o2m"]').is(":checked")); + await testUtils.dom.click($(webClient.el).find('div.o_optional_columns div.dropdown-item:last input')); + assert.ok($(webClient.el).find('th:contains(M2O field)').is(':visible'), "should have a visible m2o field"); //m2o field - assert.notOk(actionManager.$('th:contains(O2M field)').is(':visible'), + assert.notOk($(webClient.el).find('th:contains(O2M field)').is(':visible'), "should have a visible o2m field"); //m2o field - assert.containsN(actionManager, 'th', 3, "should display 3 th"); + assert.containsN(webClient, 'th', 3, "should display 3 th"); - await actionManager.doAction(1); + await doAction(webClient, 1); - assert.containsNone(actionManager, '.o_list_view', + assert.containsNone(webClient, '.o_list_view', "should not display the list view anymore"); - assert.containsOnce(actionManager, '.o_kanban_view', + assert.containsOnce(webClient, '.o_kanban_view', "should have switched to the kanban view"); - await actionManager.doAction(2); + await doAction(webClient, 2); - assert.containsNone(actionManager, '.o_kanban_view', + assert.containsNone(webClient, '.o_kanban_view', "should not havethe kanban view anymoe"); - assert.containsOnce(actionManager, '.o_list_view', + assert.containsOnce(webClient, '.o_list_view', "should display the list view"); - assert.containsN(actionManager, 'th', 3, "should display 3 th"); - assert.ok(actionManager.$('th:contains(M2O field)').is(':visible'), + assert.containsN(webClient, 'th', 3, "should display 3 th"); + assert.ok($(webClient.el).find('th:contains(M2O field)').is(':visible'), "should have a visible m2o field"); //m2o field - assert.notOk(actionManager.$('th:contains(O2M field)').is(':visible'), + assert.notOk($(webClient.el).find('th:contains(O2M field)').is(':visible'), "should have a visible o2m field"); //m2o field - - actionManager.destroy(); }); QUnit.test('list view with optional fields rendering and local storage mock', async function (assert) { @@ -11464,135 +11455,126 @@ QUnit.module('Views', { list.destroy(); }); - QUnit.skip("Auto save: add a record and leave action", async function (assert) { + QUnit.test("Auto save: add a record and leave action", async function (assert) { assert.expect(4); - const actionManager = await createActionManager({ - actions: [{ - id: 1, - name: 'Action 1', - res_model: 'foo', - type: 'ir.actions.act_window', - views: [[2, 'list']], - search_view_id: [1, 'search'], - }, { - id: 2, - name: 'Action 2', - res_model: 'foo', - type: 'ir.actions.act_window', - views: [[3, 'list']], - search_view_id: [1, 'search'], - }], - archs: { - 'foo,1,search': '', - 'foo,2,list': '', - 'foo,3,list': '', - }, - data: this.data, - }); + testConfig.serverData.actions[1] = { + id: 1, + name: 'Action 1', + res_model: 'foo', + type: 'ir.actions.act_window', + views: [[2, 'list']], + search_view_id: [1, 'search'], + }; + testConfig.serverData.actions[2] = { + id: 2, + name: 'Action 2', + res_model: 'foo', + type: 'ir.actions.act_window', + views: [[3, 'list']], + search_view_id: [1, 'search'], + }; + testConfig.serverData.views = { + 'foo,1,search': '', + 'foo,2,list': '', + 'foo,3,list': '', + }; + const webClient = await createWebClient({ testConfig }); - await actionManager.doAction(1); + await doAction(webClient, 1); - assert.strictEqual(actionManager.$('.o_field_cell[name="foo"]').text(), "yopblipgnapblip"); - assert.containsN(actionManager, '.o_data_row', 4); + assert.strictEqual($(webClient.el).find('.o_field_cell[name="foo"]').text(), "yopblipgnapblip"); + assert.containsN(webClient, '.o_data_row', 4); - await testUtils.dom.click(actionManager.$('.o_list_button_add')); - await testUtils.fields.editInput(actionManager.$('.o_field_widget[name="foo"]'), "test"); + await testUtils.dom.click($(webClient.el).find('.o_list_button_add')); + await testUtils.fields.editInput($(webClient.el).find('.o_field_widget[name="foo"]'), "test"); // change action and come back - await actionManager.doAction(2); - await actionManager.doAction(1, { clear_breadcrumbs: true }); - - assert.strictEqual(actionManager.$('.o_field_cell[name="foo"]').text(), "yopblipgnapbliptest"); - assert.containsN(actionManager, '.o_data_row', 5); + await doAction(webClient, 2); + await doAction(webClient, 1, { clearBreadcrumbs: true }); - actionManager.destroy(); + assert.strictEqual($(webClient.el).find('.o_field_cell[name="foo"]').text(), "yopblipgnapbliptest"); + assert.containsN(webClient, '.o_data_row', 5); }); - QUnit.skip("Auto save: modify a record and leave action", async function (assert) { + QUnit.test("Auto save: modify a record and leave action", async function (assert) { assert.expect(2); - const actionManager = await createActionManager({ - actions: [{ - id: 1, - name: 'Action 1', - res_model: 'foo', - type: 'ir.actions.act_window', - views: [[2, 'list']], - search_view_id: [1, 'search'], - }, { - id: 2, - name: 'Action 2', - res_model: 'foo', - type: 'ir.actions.act_window', - views: [[3, 'list']], - search_view_id: [1, 'search'], - }], - archs: { - 'foo,1,search': '', - 'foo,2,list': '', - 'foo,3,list': '', - }, - data: this.data, - }); + testConfig.serverData.actions[1] = { + id: 1, + name: 'Action 1', + res_model: 'foo', + type: 'ir.actions.act_window', + views: [[2, 'list']], + search_view_id: [1, 'search'], + }; + testConfig.serverData.actions[2] = { + id: 2, + name: 'Action 2', + res_model: 'foo', + type: 'ir.actions.act_window', + views: [[3, 'list']], + search_view_id: [1, 'search'], + }; + testConfig.serverData.views = { + 'foo,1,search': '', + 'foo,2,list': '', + 'foo,3,list': '', + }; + const webClient = await createWebClient({ testConfig }); - await actionManager.doAction(1); + await doAction(webClient, 1); - assert.strictEqual(actionManager.$('.o_field_cell[name="foo"]').text(), "yopblipgnapblip"); + assert.strictEqual($(webClient.el).find('.o_field_cell[name="foo"]').text(), "yopblipgnapblip"); - await testUtils.dom.click(actionManager.$('.o_field_cell[name="foo"]:first')); - await testUtils.fields.editInput(actionManager.$('.o_field_widget[name="foo"]'), "test"); + await testUtils.dom.click($(webClient.el).find('.o_field_cell[name="foo"]:first')); + await testUtils.fields.editInput($(webClient.el).find('.o_field_widget[name="foo"]'), "test"); // change action and come back - await actionManager.doAction(2); - await actionManager.doAction(1, { clear_breadcrumbs: true }); + await doAction(webClient, 2); + await doAction(webClient, 1, { clearBreadcrumbs: true }); - assert.strictEqual(actionManager.$('.o_field_cell[name="foo"]').text(), "testblipgnapblip"); - - actionManager.destroy(); + assert.strictEqual($(webClient.el).find('.o_field_cell[name="foo"]').text(), "testblipgnapblip"); }); - QUnit.skip("Auto save: modify a record and leave action (reject)", async function (assert) { + QUnit.test("Auto save: modify a record and leave action (reject)", async function (assert) { assert.expect(5); - const actionManager = await createActionManager({ - actions: [{ - id: 1, - name: 'Action 1', - res_model: 'foo', - type: 'ir.actions.act_window', - views: [[2, 'list']], - search_view_id: [1, 'search'], - }, { - id: 2, - name: 'Action 2', - res_model: 'foo', - type: 'ir.actions.act_window', - views: [[3, 'list']], - search_view_id: [1, 'search'], - }], - archs: { - 'foo,1,search': '', - 'foo,2,list': '', - 'foo,3,list': '', - }, - data: this.data, - }); - - await actionManager.doAction(1); + testConfig.serverData.actions[1] = { + id: 1, + name: 'Action 1', + res_model: 'foo', + type: 'ir.actions.act_window', + views: [[2, 'list']], + search_view_id: [1, 'search'], + }; + testConfig.serverData.actions[2] = { + id: 2, + name: 'Action 2', + res_model: 'foo', + type: 'ir.actions.act_window', + views: [[3, 'list']], + search_view_id: [1, 'search'], + }; + testConfig.serverData.views = { + 'foo,1,search': '', + 'foo,2,list': '', + 'foo,3,list': '', + }; + const webClient = await createWebClient({ testConfig }); - assert.strictEqual(actionManager.$('.o_field_cell[name="foo"]').text(), "yopblipgnapblip"); + await doAction(webClient,1); - await testUtils.dom.click(actionManager.$('.o_field_cell[name="foo"]:first')); - await testUtils.fields.editInput(actionManager.$('.o_field_widget[name="foo"]'), ""); + assert.strictEqual($(webClient.el).find('.o_field_cell[name="foo"]').text(), "yopblipgnapblip"); - await assert.rejects(actionManager.doAction(2)); + await testUtils.dom.click($(webClient.el).find('.o_field_cell[name="foo"]:first')); + await testUtils.fields.editInput($(webClient.el).find('.o_field_widget[name="foo"]'), ""); - assert.strictEqual(actionManager.$('.o_field_cell[name="foo"]').text(), "blipgnapblip"); - assert.hasClass(actionManager.$('.o_field_widget[name="foo"]:first'), 'o_field_invalid'); - assert.containsN(actionManager, '.o_data_row', 4); + await assert.rejects(doAction(webClient,2)); - actionManager.destroy(); + assert.strictEqual($(webClient.el).find('.o_field_cell[name="foo"]').text(), "blipgnapblip"); + assert.hasClass($(webClient.el).find('.o_field_widget[name="foo"]:first'), 'o_field_invalid'); + assert.containsN(webClient, '.o_data_row', 4); }); QUnit.test("Auto save: add a record and change page", async function (assert) { diff --git a/addons/web/static/tests/legacy/views/pivot_tests.js b/addons/web/static/tests/legacy/views/pivot_tests.js index e0fed1f4e15f6..22f65309dfafa 100644 --- a/addons/web/static/tests/legacy/views/pivot_tests.js +++ b/addons/web/static/tests/legacy/views/pivot_tests.js @@ -7,9 +7,10 @@ const PivotController = require("web.PivotController"); var testUtils = require('web.test_utils'); var testUtilsDom = require('web.test_utils_dom'); +const { createWebClient, doAction, getActionManagerTestConfig } = require('@web/../tests/webclient/actions/helpers'); + var _t = core._t; const cpHelpers = testUtils.controlPanel; -var createActionManager = testUtils.createActionManager; var createView = testUtils.createView; var patchDate = testUtils.mock.patchDate; @@ -26,6 +27,8 @@ var getCurrentValues = function (pivot) { }; +let testConfig; + QUnit.module('Views', { beforeEach: function () { this.data = { @@ -110,6 +113,9 @@ QUnit.module('Views', { }] }, }; + + testConfig = getActionManagerTestConfig(); + Object.assign(testConfig.serverData, {models: this.data}); }, }, function () { QUnit.module('PivotView'); @@ -2709,73 +2715,65 @@ QUnit.module('Views', { pivot.destroy(); }); - QUnit.skip('Navigation list view for a group and back with breadcrumbs', async function (assert) { + QUnit.test('Navigation list view for a group and back with breadcrumbs', async function (assert) { assert.expect(16); - // create an action manager to test the interactions with the search view - var readGroupCount = 0; + // create a webClient to test the interactions with the search view + + testConfig.serverData.views = { + 'partner,false,pivot': '' + + '' + + '', + 'partner,false,search': '', + 'partner,false,list': '', + 'partner,false,form': '
', + }; - var actionManager = await createActionManager({ - data: this.data, - archs: { - 'partner,false,pivot': '' + - '' + - '', - 'partner,false,search': '', - 'partner,false,list': '', - 'partner,false,form': '
', - }, - intercepts: { - do_action: function (event) { - var action = event.data.action; - actionManager.doAction(action); - } - }, - mockRPC: function (route, args) { - if (args.method === 'read_group') { - assert.step('read_group'); - const domain = args.kwargs.domain; - if ([0,1].indexOf(readGroupCount) !== -1) { - assert.deepEqual(domain, [], 'domain empty'); - } else if ([2,3,4,5].indexOf(readGroupCount) !== -1) { - assert.deepEqual(domain, [['foo', '=', 12]], - 'domain conserved when back with breadcrumbs'); - } - readGroupCount++; - } - if (route === '/web/dataset/search_read') { - assert.step('search_read'); - const domain = args.domain; - assert.deepEqual(domain, ['&', ['customer', '=', 1], ['foo', '=', 12]], - 'list domain is correct'); + let readGroupCount = 0; + const mockRPC = (route, args) => { + if (args.method === 'read_group') { + assert.step('read_group'); + const domain = args.kwargs.domain; + if ([0,1].indexOf(readGroupCount) !== -1) { + assert.deepEqual(domain, [], 'domain empty'); + } else if ([2,3,4,5].indexOf(readGroupCount) !== -1) { + assert.deepEqual(domain, [['foo', '=', 12]], + 'domain conserved when back with breadcrumbs'); } - return this._super.apply(this, arguments); - }, - }); + readGroupCount++; + } + if (route === '/web/dataset/search_read') { + assert.step('search_read'); + const domain = args.domain; + assert.deepEqual(domain, ['&', ['customer', '=', 1], ['foo', '=', 12]], + 'list domain is correct'); + } + }; + + const webClient = await createWebClient({testConfig, mockRPC}); - await actionManager.doAction({ + await doAction(webClient, { res_model: 'partner', type: 'ir.actions.act_window', views: [[false, 'pivot']], }); - await cpHelpers.toggleFilterMenu(actionManager); - await cpHelpers.toggleMenuItem(actionManager, 0); + await cpHelpers.toggleFilterMenu(webClient); + await cpHelpers.toggleMenuItem(webClient, 0); await testUtils.nextTick(); - await testUtilsDom.click(actionManager.$('.o_pivot_cell_value:nth(1)')); + await testUtilsDom.click($(webClient.el).find('.o_pivot_cell_value:nth(1)')); await testUtils.nextTick(); - assert.containsOnce(actionManager, '.o_list_view'); + assert.containsOnce(webClient, '.o_list_view'); - await testUtilsDom.click(actionManager.$('.o_control_panel ol.breadcrumb li.breadcrumb-item').eq(0)); + await testUtilsDom.click($(webClient.el).find('.o_control_panel ol.breadcrumb li.breadcrumb-item').eq(0)); assert.verifySteps([ 'read_group', 'read_group', 'read_group', 'read_group', 'search_read', 'read_group', 'read_group']); - actionManager.destroy(); }); QUnit.test('Cell values are kept when flippin a pivot view in comparison mode', async function (assert) { diff --git a/addons/web/static/tests/legacy/views/qweb_tests.js b/addons/web/static/tests/legacy/views/qweb_tests.js index 255126a05ad79..53db230fa2582 100644 --- a/addons/web/static/tests/legacy/views/qweb_tests.js +++ b/addons/web/static/tests/legacy/views/qweb_tests.js @@ -1,36 +1,40 @@ odoo.define('web.qweb_view_tests', function (require) { "use strict"; -var utils = require('web.test_utils'); +const utils = require('web.test_utils'); +const { legacyExtraNextTick } = require("@web/../tests/helpers/utils"); +const { createWebClient, doAction, getActionManagerTestConfig } = require('@web/../tests/webclient/actions/helpers'); QUnit.module("Views", { }, function () { QUnit.module("QWeb"); - QUnit.skip("basic", async function (assert) { + QUnit.test("basic", async function (assert) { assert.expect(14); - var am = await utils.createActionManager({ - data: { - test: { - fields: {}, - records: [], - } - }, - archs: { - 'test,5,qweb': '
', - 'test,false,search': '' - }, - mockRPC: function (route, args) { - if (/^\/web\/dataset\/call_kw/.test(route)) { - switch (_.str.sprintf('%(model)s.%(method)s', args)) { + const testConfig = getActionManagerTestConfig(); + + testConfig.serverData.models = { + test: { + fields: {}, + records: [], + } + }; + testConfig.serverData.views = { + 'test,5,qweb': '
', + 'test,false,search': '' + }; + + const mockRPC = (route, args) => { + if (/^\/web\/dataset\/call_kw/.test(route)) { + switch (_.str.sprintf('%(model)s.%(method)s', args)) { case 'test.qweb_render_view': assert.step('fetch'); assert.equal(args.kwargs.view_id, 5); return Promise.resolve( '
foo' + - '
' + - 'Unfold' + - '
' + + '
' + + 'Unfold' + + '
' + '
' ); case 'test.wheee': @@ -38,35 +42,32 @@ QUnit.module("Views", { assert.deepEqual(args.args, [42]); assert.deepEqual(args.kwargs, {other: 5}); return Promise.resolve('
ok
'); - } } - return this._super.apply(this, arguments); } - }); - try { - var resolved = false; - am.doAction({ - type: 'ir.actions.act_window', - views: [[false, 'qweb']], - res_model: 'test', - }).then(function () { resolved = true; }); - assert.ok(!resolved, "Action cannot be resolved synchronously"); + }; + + const webClient = await createWebClient({testConfig, mockRPC}); + + let resolved = false; + const doActionProm = doAction(webClient, { + type: 'ir.actions.act_window', + views: [[false, 'qweb']], + res_model: 'test', + }).then(function () { resolved = true; }); + assert.ok(!resolved, "Action cannot be resolved synchronously"); - await utils.nextTick(); - assert.ok(resolved, "Action is resolved asynchronously"); + await doActionProm; + assert.ok(resolved, "Action is resolved asynchronously"); - var $content = am.$('.o_content'); - assert.ok(/^\s*foo/.test($content.text())); - await utils.dom.click($content.find('[type=toggle]')); - assert.equal($content.find('div#sub').text(), 'ok', 'should have unfolded the sub-item'); - await utils.dom.click($content.find('[type=toggle]')); - assert.equal($content.find('div#sub').length, 0, "should have removed the sub-item"); - await utils.dom.click($content.find('[type=toggle]')); + const content = webClient.el.querySelector('.o_content'); + assert.ok(/^\s*foo/.test(content.textContent)); + await utils.dom.click(content.querySelector('[type=toggle]')); + assert.equal(content.querySelector('div#sub').textContent, 'ok', 'should have unfolded the sub-item'); + await utils.dom.click(content.querySelector('[type=toggle]')); + assert.containsNone(content, "div#sub") + await utils.dom.click(content.querySelector('[type=toggle]')); - assert.verifySteps(['fetch', 'unfold', 'unfold']); - } finally { - am.destroy(); - } + assert.verifySteps(['fetch', 'unfold', 'unfold']); }); }); }); diff --git a/addons/web/static/tests/legacy/views/search_panel_tests.js b/addons/web/static/tests/legacy/views/search_panel_tests.js index 8a90def96c0ad..c7a28be2d1f58 100644 --- a/addons/web/static/tests/legacy/views/search_panel_tests.js +++ b/addons/web/static/tests/legacy/views/search_panel_tests.js @@ -8,9 +8,11 @@ const testUtils = require('web.test_utils'); const SearchPanel = require("web.searchPanel"); const cpHelpers = testUtils.controlPanel; -const createActionManager = testUtils.createActionManager; const createView = testUtils.createView; +const { createWebClient, doAction, getActionManagerTestConfig, loadState } = require('@web/../tests/webclient/actions/helpers'); +const { legacyExtraNextTick } = require("@web/../tests/helpers/utils"); + /** * Return the list of counters displayed in the search panel (if any). * @param {Widget} view, view controller @@ -36,6 +38,8 @@ function toggleFold(widget, text) { return testUtils.dom.click(target); } +let testConfig; + QUnit.module('Views', { beforeEach: function () { this.data = { @@ -116,6 +120,15 @@ QUnit.module('Views', {
`, }; + + testConfig = getActionManagerTestConfig(); + + // map legacy test data + const actions = {}; + this.actions.forEach((act) => { + actions[act.xmlId || act.id] = act; + }); + Object.assign(testConfig.serverData, {actions, models: this.data, views: this.archs}); }, }, function () { @@ -2317,39 +2330,36 @@ QUnit.module('Views', { kanban.destroy(); }); - QUnit.skip('search panel is available on list and kanban by default', async function (assert) { + QUnit.test('search panel is available on list and kanban by default', async function (assert) { assert.expect(8); - var actionManager = await createActionManager({ - actions: this.actions, - archs: this.archs, - data: this.data, - }); - await actionManager.doAction(1); + const webClient = await createWebClient({ testConfig }); - assert.containsOnce(actionManager, '.o_content.o_controller_with_searchpanel .o_kanban_view'); - assert.containsOnce(actionManager, '.o_content.o_controller_with_searchpanel .o_search_panel'); - - await cpHelpers.switchView(actionManager, 'pivot'); - await testUtils.nextTick(); - assert.containsOnce(actionManager, '.o_content .o_pivot'); - assert.containsNone(actionManager, '.o_content .o_search_panel'); + await doAction(webClient, 1); - await cpHelpers.switchView(actionManager, 'list'); - assert.containsOnce(actionManager, '.o_content.o_controller_with_searchpanel .o_list_view'); - assert.containsOnce(actionManager, '.o_content.o_controller_with_searchpanel .o_search_panel'); + assert.containsOnce(webClient, '.o_content.o_controller_with_searchpanel .o_kanban_view'); + assert.containsOnce(webClient, '.o_content.o_controller_with_searchpanel .o_search_panel'); - await testUtils.dom.click(actionManager.$('.o_data_row .o_data_cell:first')); - assert.containsOnce(actionManager, '.o_content .o_form_view'); - assert.containsNone(actionManager, '.o_content .o_search_panel'); - - actionManager.destroy(); + await cpHelpers.switchView(webClient, 'pivot'); + await testUtils.nextTick(); + assert.containsOnce(webClient, '.o_content .o_pivot'); + assert.containsNone(webClient, '.o_content .o_search_panel'); + + await cpHelpers.switchView(webClient, 'list'); + await legacyExtraNextTick(); + assert.containsOnce(webClient, '.o_content.o_controller_with_searchpanel .o_list_view'); + assert.containsOnce(webClient, '.o_content.o_controller_with_searchpanel .o_search_panel'); + + await testUtils.dom.click($(webClient.el).find('.o_data_row .o_data_cell:first')); + await legacyExtraNextTick(); + assert.containsOnce(webClient, '.o_content .o_form_view'); + assert.containsNone(webClient, '.o_content .o_search_panel'); }); - QUnit.skip('search panel with view_types attribute', async function (assert) { + QUnit.test('search panel with view_types attribute', async function (assert) { assert.expect(6); - this.archs['partner,false,search'] = + testConfig.serverData.views['partner,false,search'] = ` @@ -2358,63 +2368,60 @@ QUnit.module('Views', { `; - var actionManager = await createActionManager({ - actions: this.actions, - archs: this.archs, - data: this.data, - }); - await actionManager.doAction(1); - - assert.containsOnce(actionManager, '.o_content.o_controller_with_searchpanel .o_kanban_view'); - assert.containsOnce(actionManager, '.o_content.o_controller_with_searchpanel .o_search_panel'); + const webClient = await createWebClient({ testConfig }); + await doAction(webClient, 1); - await cpHelpers.switchView(actionManager, 'list'); - assert.containsOnce(actionManager, '.o_content .o_list_view'); - assert.containsNone(actionManager, '.o_content .o_search_panel'); + assert.containsOnce(webClient, '.o_content.o_controller_with_searchpanel .o_kanban_view'); + assert.containsOnce(webClient, '.o_content.o_controller_with_searchpanel .o_search_panel'); - await cpHelpers.switchView(actionManager, 'pivot'); - assert.containsOnce(actionManager, '.o_content.o_controller_with_searchpanel .o_pivot'); - assert.containsOnce(actionManager, '.o_content.o_controller_with_searchpanel .o_search_panel'); + await cpHelpers.switchView(webClient, 'list'); + await legacyExtraNextTick(); + assert.containsOnce(webClient, '.o_content .o_list_view'); + assert.containsNone(webClient, '.o_content .o_search_panel'); - actionManager.destroy(); + await cpHelpers.switchView(webClient, 'pivot'); + await legacyExtraNextTick(); + assert.containsOnce(webClient, '.o_content.o_controller_with_searchpanel .o_pivot'); + assert.containsOnce(webClient, '.o_content.o_controller_with_searchpanel .o_search_panel'); }); - QUnit.skip('search panel state is shared between views', async function (assert) { + QUnit.test('search panel state is shared between views', async function (assert) { assert.expect(16); - var actionManager = await createActionManager({ - actions: this.actions, - archs: this.archs, - data: this.data, - mockRPC: function (route, args) { - if (route === '/web/dataset/search_read') { - assert.step(JSON.stringify(args.domain)); - } - return this._super.apply(this, arguments); - }, - }); - await actionManager.doAction(1); + const mockRPC = (route, args) => { + if (route === '/web/dataset/search_read') { + assert.step(JSON.stringify(args.domain)); + } + }; + + const webClient = await createWebClient({ testConfig , mockRPC }); - assert.hasClass(actionManager.$('.o_search_panel_category_value:first header'), 'active'); - assert.containsN(actionManager, '.o_kanban_record:not(.o_kanban_ghost)', 4); + await doAction(webClient, 1); + + assert.hasClass($(webClient.el).find('.o_search_panel_category_value:first header'), 'active'); + assert.containsN(webClient, '.o_kanban_record:not(.o_kanban_ghost)', 4); // select 'asustek' company - await testUtils.dom.click(actionManager.$('.o_search_panel_category_value:nth(1) header')); - assert.hasClass(actionManager.$('.o_search_panel_category_value:nth(1) header'), 'active'); - assert.containsN(actionManager, '.o_kanban_record:not(.o_kanban_ghost)', 2); + await testUtils.dom.click($(webClient.el).find('.o_search_panel_category_value:nth(1) header')); + await legacyExtraNextTick(); + assert.hasClass($(webClient.el).find('.o_search_panel_category_value:nth(1) header'), 'active'); + assert.containsN(webClient, '.o_kanban_record:not(.o_kanban_ghost)', 2); - await cpHelpers.switchView(actionManager, 'list'); - assert.hasClass(actionManager.$('.o_search_panel_category_value:nth(1) header'), 'active'); - assert.containsN(actionManager, '.o_data_row', 2); + await cpHelpers.switchView(webClient, 'list'); + await legacyExtraNextTick(); + assert.hasClass($(webClient.el).find('.o_search_panel_category_value:nth(1) header'), 'active'); + assert.containsN(webClient, '.o_data_row', 2); // select 'agrolait' company - await testUtils.dom.click(actionManager.$('.o_search_panel_category_value:nth(2) header')); - assert.hasClass(actionManager.$('.o_search_panel_category_value:nth(2) header'), 'active'); - assert.containsN(actionManager, '.o_data_row', 2); + await testUtils.dom.click($(webClient.el).find('.o_search_panel_category_value:nth(2) header')); + await legacyExtraNextTick(); + assert.hasClass($(webClient.el).find('.o_search_panel_category_value:nth(2) header'), 'active'); + assert.containsN(webClient, '.o_data_row', 2); - await cpHelpers.switchView(actionManager, 'kanban'); - assert.hasClass(actionManager.$('.o_search_panel_category_value:nth(2) header'), 'active'); - assert.containsN(actionManager, '.o_kanban_record:not(.o_kanban_ghost)', 2); + await cpHelpers.switchView(webClient, 'kanban'); + await legacyExtraNextTick(); + assert.hasClass($(webClient.el).find('.o_search_panel_category_value:nth(2) header'), 'active'); + assert.containsN(webClient, '.o_kanban_record:not(.o_kanban_ghost)', 2); assert.verifySteps([ '[]', // initial search_read @@ -2423,49 +2430,49 @@ QUnit.module('Views', { '[["company_id","child_of",5]]', // list, after selecting the other company '[["company_id","child_of",5]]', // kanban ]); - - actionManager.destroy(); }); - QUnit.skip('search panel filters are kept between switch views', async function (assert) { + QUnit.test('search panel filters are kept between switch views', async function (assert) { assert.expect(17); - const actionManager = await createActionManager({ - actions: this.actions, - archs: this.archs, - data: this.data, - mockRPC: function (route, args) { - if (route === '/web/dataset/search_read') { - assert.step(JSON.stringify(args.domain)); - } - return this._super.apply(this, arguments); - }, - }); - await actionManager.doAction(1); + const mockRPC = (route, args) => { + if (route === '/web/dataset/search_read') { + assert.step(JSON.stringify(args.domain)); + } + }; - assert.containsNone(actionManager, '.o_search_panel_filter_value input:checked'); - assert.containsN(actionManager, '.o_kanban_record:not(.o_kanban_ghost)', 4); + const webClient = await createWebClient({ testConfig , mockRPC }); + await doAction(webClient, 1); + + assert.containsNone(webClient, '.o_search_panel_filter_value input:checked'); + assert.containsN(webClient, '.o_kanban_record:not(.o_kanban_ghost)', 4); // select gold filter - await testUtils.dom.click(actionManager.$('.o_search_panel_filter input[type="checkbox"]:nth(0)')); - assert.containsOnce(actionManager, '.o_search_panel_filter_value input:checked'); - assert.containsN(actionManager, '.o_kanban_record:not(.o_kanban_ghost)', 1); + await testUtils.dom.click($(webClient.el).find('.o_search_panel_filter input[type="checkbox"]:nth(0)')); + await legacyExtraNextTick(); + assert.containsOnce(webClient, '.o_search_panel_filter_value input:checked'); + assert.containsN(webClient, '.o_kanban_record:not(.o_kanban_ghost)', 1); - await cpHelpers.switchView(actionManager, 'list'); - assert.containsOnce(actionManager, '.o_search_panel_filter_value input:checked'); - assert.containsN(actionManager, '.o_data_row', 1); + await cpHelpers.switchView(webClient, 'list'); + await legacyExtraNextTick(); + assert.containsOnce(webClient, '.o_search_panel_filter_value input:checked'); + assert.containsN(webClient, '.o_data_row', 1); // select silver filter - await testUtils.dom.click(actionManager.$('.o_search_panel_filter input[type="checkbox"]:nth(1)')); - assert.containsN(actionManager, '.o_search_panel_filter_value input:checked', 2); - assert.containsN(actionManager, '.o_data_row', 4); + await testUtils.dom.click($(webClient.el).find('.o_search_panel_filter input[type="checkbox"]:nth(1)')); + await legacyExtraNextTick(); + assert.containsN(webClient, '.o_search_panel_filter_value input:checked', 2); + assert.containsN(webClient, '.o_data_row', 4); - await cpHelpers.switchView(actionManager, 'kanban'); - assert.containsN(actionManager, '.o_search_panel_filter_value input:checked', 2); - assert.containsN(actionManager, '.o_kanban_record:not(.o_kanban_ghost)', 4); + await cpHelpers.switchView(webClient, 'kanban'); + await legacyExtraNextTick(); + assert.containsN(webClient, '.o_search_panel_filter_value input:checked', 2); + assert.containsN(webClient, '.o_kanban_record:not(.o_kanban_ghost)', 4); - await testUtils.dom.click(actionManager.$(".o_kanban_record:nth(0)")); - await testUtils.dom.click(actionManager.$(".breadcrumb-item:nth(0)")); + await testUtils.dom.click($(webClient.el).find(".o_kanban_record:nth(0)")); + await legacyExtraNextTick(); + await testUtils.dom.click($(webClient.el).find(".breadcrumb-item:nth(0)")); + await legacyExtraNextTick(); assert.verifySteps([ '[]', // initial search_read @@ -2475,153 +2482,130 @@ QUnit.module('Views', { '[["category_id","in",[6,7]]]', // kanban '[["category_id","in",[6,7]]]', // kanban, after switching back from form view ]); - - actionManager.destroy(); }); - QUnit.skip('search panel filters are kept when switching to a view with no search panel', async function (assert) { + QUnit.test('search panel filters are kept when switching to a view with no search panel', async function (assert) { assert.expect(13); - var actionManager = await createActionManager({ - actions: this.actions, - archs: this.archs, - data: this.data, - }); - await actionManager.doAction(1); + const webClient = await createWebClient({ testConfig }); + await doAction(webClient, 1); - assert.containsOnce(actionManager, '.o_content.o_controller_with_searchpanel .o_kanban_view'); - assert.containsOnce(actionManager, '.o_content.o_controller_with_searchpanel .o_search_panel'); - assert.containsNone(actionManager, '.o_search_panel_filter_value input:checked'); - assert.containsN(actionManager, '.o_kanban_record:not(.o_kanban_ghost)', 4); + assert.containsOnce(webClient, '.o_content.o_controller_with_searchpanel .o_kanban_view'); + assert.containsOnce(webClient, '.o_content.o_controller_with_searchpanel .o_search_panel'); + assert.containsNone(webClient, '.o_search_panel_filter_value input:checked'); + assert.containsN(webClient, '.o_kanban_record:not(.o_kanban_ghost)', 4); // select gold filter - await testUtils.dom.click(actionManager.$('.o_search_panel_filter input[type="checkbox"]:nth(0)')); - assert.containsOnce(actionManager, '.o_search_panel_filter_value input:checked'); - assert.containsN(actionManager, '.o_kanban_record:not(.o_kanban_ghost)', 1); + await testUtils.dom.click($(webClient.el).find('.o_search_panel_filter input[type="checkbox"]:nth(0)')); + await legacyExtraNextTick(); + assert.containsOnce(webClient, '.o_search_panel_filter_value input:checked'); + assert.containsN(webClient, '.o_kanban_record:not(.o_kanban_ghost)', 1); // switch to pivot - await cpHelpers.switchView(actionManager, 'pivot'); - assert.containsOnce(actionManager, '.o_content .o_pivot'); - assert.containsNone(actionManager, '.o_content .o_search_panel'); - assert.strictEqual(actionManager.$('.o_pivot_cell_value').text(), '15'); + await cpHelpers.switchView(webClient, 'pivot'); + await legacyExtraNextTick(); + assert.containsOnce(webClient, '.o_content .o_pivot'); + assert.containsNone(webClient, '.o_content .o_search_panel'); + assert.strictEqual($(webClient.el).find('.o_pivot_cell_value').text(), '15'); // switch to list - await cpHelpers.switchView(actionManager, 'list'); - assert.containsOnce(actionManager, '.o_content.o_controller_with_searchpanel .o_list_view'); - assert.containsOnce(actionManager, '.o_content.o_controller_with_searchpanel .o_search_panel'); - assert.containsOnce(actionManager, '.o_search_panel_filter_value input:checked'); - assert.containsN(actionManager, '.o_data_row', 1); - - actionManager.destroy(); + await cpHelpers.switchView(webClient, 'list'); + await legacyExtraNextTick(); + assert.containsOnce(webClient, '.o_content.o_controller_with_searchpanel .o_list_view'); + assert.containsOnce(webClient, '.o_content.o_controller_with_searchpanel .o_search_panel'); + assert.containsOnce(webClient, '.o_search_panel_filter_value input:checked'); + assert.containsN(webClient, '.o_data_row', 1); }); - QUnit.skip('after onExecuteAction, selects "All" as default category value', async function (assert) { + QUnit.test('after onExecuteAction, selects "All" as default category value', async function (assert) { assert.expect(4); - var actionManager = await createActionManager({ - actions: this.actions, - archs: this.archs, - data: this.data, - }); + const webClient = await createWebClient({ testConfig }); + await doAction(webClient, 2); - await actionManager.doAction(2); - await testUtils.dom.click(actionManager.$('.o_form_view button:contains("multi view")')); + await testUtils.dom.click($(webClient.el).find('.o_form_view button:contains("multi view")')); + await legacyExtraNextTick(); - assert.containsOnce(actionManager, '.o_kanban_view'); - assert.containsOnce(actionManager, '.o_search_panel'); - assert.containsOnce(actionManager, '.o_search_panel_category_value:first .active'); + assert.containsOnce(webClient, '.o_kanban_view'); + assert.containsOnce(webClient, '.o_search_panel'); + assert.containsOnce(webClient, '.o_search_panel_category_value:first .active'); assert.verifySteps([]); // should not communicate with localStorage - - actionManager.destroy(); }); - QUnit.skip('search panel is not instantiated if stated in context', async function (assert) { + QUnit.test('search panel is not instantiated if stated in context', async function (assert) { assert.expect(2); - this.actions[0].context = {search_panel: false}; - var actionManager = await createActionManager({ - actions: this.actions, - archs: this.archs, - data: this.data, - }); + testConfig.serverData.actions[2].context = {search_panel: false}; + const webClient = await createWebClient({ testConfig }); + await doAction(webClient, 2); - await actionManager.doAction(2); - await testUtils.dom.click(actionManager.$('.o_form_view button:contains("multi view")')); + await testUtils.dom.click($(webClient.el).find('.o_form_view button:contains("multi view")')); + await legacyExtraNextTick(); - assert.containsOnce(actionManager, '.o_kanban_view'); - assert.containsNone(actionManager, '.o_search_panel'); - - actionManager.destroy(); + assert.containsOnce(webClient, '.o_kanban_view'); + assert.containsNone(webClient, '.o_search_panel'); }); - QUnit.skip('categories and filters are not reloaded when switching between views', async function (assert) { + QUnit.test('categories and filters are not reloaded when switching between views', async function (assert) { assert.expect(3); - var actionManager = await createActionManager({ - actions: this.actions, - archs: this.archs, - data: this.data, - mockRPC: function (route, args) { - if (args.method && args.method.includes('search_panel_')) { - assert.step(args.method); - } - return this._super.apply(this, arguments); - }, - }); - await actionManager.doAction(1); + const mockRPC = (route, args) => { + if (args.method && args.method.includes('search_panel_')) { + assert.step(args.method); + } + }; + + const webClient = await createWebClient({ testConfig, mockRPC }); + await doAction(webClient, 1); - await cpHelpers.switchView(actionManager, 'list'); - await cpHelpers.switchView(actionManager, 'kanban'); + await cpHelpers.switchView(webClient, 'list'); + await legacyExtraNextTick(); + await cpHelpers.switchView(webClient, 'kanban'); + await legacyExtraNextTick(); assert.verifySteps([ 'search_panel_select_range', // kanban: categories 'search_panel_select_multi_range', // kanban: filters ]); - - actionManager.destroy(); }); - QUnit.skip('scroll position is kept when switching between controllers', async function (assert) { + QUnit.test('scroll position is kept when switching between controllers', async function (assert) { assert.expect(6); const originalDebounce = SearchPanel.scrollDebounce; SearchPanel.scrollDebounce = 0; for (var i = 10; i < 20; i++) { - this.data.category.records.push({id: i, name: "Cat " + i}); + testConfig.serverData.models.category.records.push({id: i, name: "Cat " + i}); } - var actionManager = await createActionManager({ - actions: this.actions, - archs: this.archs, - data: this.data, - }); - actionManager.$el.css('max-height', 300); + const webClient = await createWebClient({ testConfig }); + webClient.el.querySelector('.o_action_manager').style.maxHeight = "300px"; + + await doAction(webClient, 1); async function scroll(top) { - actionManager.el.querySelector(".o_search_panel").scrollTop = top; + webClient.el.querySelector(".o_search_panel").scrollTop = top; await testUtils.nextTick(); } - await actionManager.doAction(1); - - assert.containsOnce(actionManager, '.o_content .o_kanban_view'); - assert.strictEqual(actionManager.$('.o_search_panel').scrollTop(), 0); + assert.containsOnce(webClient, '.o_content .o_kanban_view'); + assert.strictEqual($(webClient.el).find('.o_search_panel').scrollTop(), 0); // simulate a scroll in the search panel and switch into list await scroll(50); - await cpHelpers.switchView(actionManager, 'list'); - assert.containsOnce(actionManager, '.o_content .o_list_view'); - assert.strictEqual(actionManager.$('.o_search_panel').scrollTop(), 50); + await cpHelpers.switchView(webClient, 'list'); + await legacyExtraNextTick(); + assert.containsOnce(webClient, '.o_content .o_list_view'); + assert.strictEqual($(webClient.el).find('.o_search_panel').scrollTop(), 50); // simulate another scroll and switch back to kanban await scroll(30); - await cpHelpers.switchView(actionManager, 'kanban'); - assert.containsOnce(actionManager, '.o_content .o_kanban_view'); - assert.strictEqual(actionManager.$('.o_search_panel').scrollTop(), 30); - - actionManager.destroy(); + await cpHelpers.switchView(webClient, 'kanban'); + await legacyExtraNextTick(); + assert.containsOnce(webClient, '.o_content .o_kanban_view'); + assert.strictEqual($(webClient.el).find('.o_search_panel').scrollTop(), 30); SearchPanel.scrollDebounce = originalDebounce; }); diff --git a/addons/web/static/tests/legacy/widgets/domain_selector_tests.js b/addons/web/static/tests/legacy/widgets/domain_selector_tests.js index 9f4fc24245864..9e49c8d644c26 100644 --- a/addons/web/static/tests/legacy/widgets/domain_selector_tests.js +++ b/addons/web/static/tests/legacy/widgets/domain_selector_tests.js @@ -3,6 +3,7 @@ odoo.define('web.domain_selector_tests', function (require) { var DomainSelector = require("web.DomainSelector"); var testUtils = require("web.test_utils"); +const { createWebClient, doAction, getActionManagerTestConfig } = require('@web/../tests/webclient/actions/helpers'); QUnit.module('widgets', {}, function () { @@ -245,33 +246,31 @@ QUnit.module('DomainSelector', { domainSelector.destroy(); }); - QUnit.skip("inline domain editor in modal", async function (assert) { + QUnit.test("inline domain editor in modal", async function (assert) { assert.expect(1); - const actions = [ - { - id: 5, - name: "Partner Form", - res_model: "partner", - target: "new", - type: "ir.actions.act_window", - views: [["view_ref", "form"]], - }, - ]; - const actionManager = await testUtils.createActionManager({ - actions, - archs: { - "partner,view_ref,form": ` -
- - - `, - }, - data: this.data, - }); - await actionManager.doAction(5); + const testConfig = getActionManagerTestConfig(); + + testConfig.serverData.actions[5] = { + id: 5, + name: "Partner Form", + res_model: "partner", + target: "new", + type: "ir.actions.act_window", + views: [["view_ref", "form"]], + }; + + testConfig.serverData.views = { + "partner,view_ref,form": ` +
+ + + `, + }; + + const webClient = await createWebClient({testConfig}); + await doAction(webClient, 5); assert.strictEqual(document.querySelector('div[name="foo"]').closest('.modal-body').style.overflow, 'visible', "modal should have visible overflow if there is inline domain field widget"); - actionManager.destroy(); }); }); }); diff --git a/addons/web/static/tests/legacy/widgets/week_days_tests.js b/addons/web/static/tests/legacy/widgets/week_days_tests.js index fdda354a94900..80430c4e05108 100644 --- a/addons/web/static/tests/legacy/widgets/week_days_tests.js +++ b/addons/web/static/tests/legacy/widgets/week_days_tests.js @@ -136,14 +136,8 @@ odoo.define('web.week_days_tests', function (require) {
`, - env: { - _t: { - database: { - parameters: Object.assign(_t.database.parameters,{ - week_start: 5, - }), - } - } + translateParameters: { + week_start: 5, }, }); diff --git a/addons/web/static/tests/setup.js b/addons/web/static/tests/setup.js index 3a6863c12df99..c8eb1f3485efc 100644 --- a/addons/web/static/tests/setup.js +++ b/addons/web/static/tests/setup.js @@ -1,6 +1,7 @@ /** @odoo-module **/ import core from "web.core"; +import session from "web.session"; import { browser } from "@web/core/browser/browser"; import { patchWithCleanup } from "@web/../tests/helpers/utils"; import { legacyProm } from "web.test_legacy"; @@ -53,6 +54,13 @@ function patchLegacyCoreBus() { }); } +function patchLegacySession() { + const userContext = Object.getOwnPropertyDescriptor(session, "user_context"); + registerCleanup(() => { + Object.defineProperty(session, "user_context", userContext); + }); +} + export async function setupTests() { QUnit.testStart(() => { setTestOdooWithCleanup(); @@ -60,6 +68,7 @@ export async function setupTests() { forceLocaleAndTimezoneWithCleanup(); patchBrowserWithCleanup(); patchLegacyCoreBus(); + patchLegacySession(); }); const templatesUrl = `/web/webclient/qweb/${new Date().getTime()}?bundle=web.assets_qweb`; diff --git a/addons/web/static/tests/webclient/actions/helpers.js b/addons/web/static/tests/webclient/actions/helpers.js index 6d8b1e3ffc8f9..6ea6f6cccdb50 100644 --- a/addons/web/static/tests/webclient/actions/helpers.js +++ b/addons/web/static/tests/webclient/actions/helpers.js @@ -11,6 +11,7 @@ import { makeLegacyActionManagerService, makeLegacyNotificationService, mapLegacyEnvToWowlEnv, + makeLegacySessionService, } from "@web/legacy/utils"; import { viewService } from "@web/views/view_service"; import { actionService } from "@web/webclient/actions/action_service"; @@ -41,6 +42,9 @@ import { } from "../../helpers/mock_services"; import { getFixture, legacyExtraNextTick, nextTick, patchWithCleanup } from "../../helpers/utils"; import session from "web.session"; +import { ComponentAdapter } from "web.OwlCompatibility"; +import LegacyMockServer from "web.MockServer"; +import Widget from "web.Widget"; const { Component, mount, tags } = owl; @@ -78,9 +82,17 @@ export async function createWebClient(params) { controllers.push(this); }, }); - if (params.legacyParams && params.legacyParams.getTZOffset) { - patchWithCleanup(session, { - getTZOffset: params.legacyParams.getTZOffset, + + const legacyParams = params.legacyParams; + const models = params.testConfig.serverData.models; + if (legacyParams && legacyParams.withLegacyMockServer && models) { + legacyParams.models = Object.assign({}, models); + // In lagacy, data may not be sole models, but can contain some other variables + // So we filter them out for our WOWL mockServer + Object.entries(legacyParams.models).forEach(([k, v]) => { + if (!(v instanceof Object) || !("fields" in v)) { + delete models[k]; + } }); } @@ -89,7 +101,7 @@ export async function createWebClient(params) { ...params.testConfig, mockRPC, }); - addLegacyMockEnvironment(env, params.testConfig, params.legacyParams); + addLegacyMockEnvironment(env, params.testConfig, legacyParams); const WebClientClass = params.WebClientClass || WebClient; const target = params && params.target ? params.target : getFixture(); @@ -157,7 +169,15 @@ function addLegacyMockEnvironment(env, testConfig, legacyParams = {}) { ActionMenus.registry = new Registry(); registerCleanup(() => (ActionMenus.registry = actionMenusRegistry)); - const legacyEnv = makeTestEnvironment({ dataManager, bus: core.bus }); + let localSession; + if (legacyParams && legacyParams.getTZOffset) { + patchWithCleanup(session, { + getTZOffset: legacyParams.getTZOffset, + }); + localSession = { getTZOffset: legacyParams.getTZOffset }; + } + + const legacyEnv = makeTestEnvironment({ dataManager, bus: core.bus, session: localSession }); if (legacyParams.serviceRegistry) { const legacyServiceMap = core.serviceRegistry.map; @@ -173,6 +193,7 @@ function addLegacyMockEnvironment(env, testConfig, legacyParams = {}) { Component.env = legacyEnv; mapLegacyEnvToWowlEnv(legacyEnv, env); + serviceRegistry.add("legacy_session", makeLegacySessionService(legacyEnv, session)); // deploy the legacyActionManagerService (in Wowl env) const legacyActionManagerService = makeLegacyActionManagerService(legacyEnv); serviceRegistry.add("legacy_action_manager", legacyActionManagerService); @@ -183,6 +204,26 @@ function addLegacyMockEnvironment(env, testConfig, legacyParams = {}) { const initialDebouncedVal = debouncedField.prototype.DEBOUNCE; debouncedField.prototype.DEBOUNCE = 0; registerCleanup(() => (debouncedField.prototype.DEBOUNCE = initialDebouncedVal)); + + if (legacyParams.withLegacyMockServer) { + const adapter = new ComponentAdapter(null, { Component: owl.Component }); + adapter.env = legacyEnv; + const W = Widget.extend({ do_push_state() {} }); + const widget = new W(adapter); + const legacyMockServer = new LegacyMockServer(legacyParams.models, { widget }); + const originalRPC = env.services.rpc; + env.services.rpc = async (...args) => { + try { + return await originalRPC(...args); + } catch (e) { + if (e.message.includes("Unimplemented")) { + return legacyMockServer._performRpc(...args); + } else { + throw e; + } + } + }; + } } export async function doAction(env, ...args) { diff --git a/addons/web/static/tests/webclient/actions/legacy_tests.js b/addons/web/static/tests/webclient/actions/legacy_tests.js index dcaeb184dfd94..9095994922db0 100644 --- a/addons/web/static/tests/webclient/actions/legacy_tests.js +++ b/addons/web/static/tests/webclient/actions/legacy_tests.js @@ -13,6 +13,7 @@ import { ClientActionAdapter } from "@web/legacy/action_adapters"; import { useDebugManager } from "@web/core/debug/debug_menu"; import { debugService } from "@web/core/debug/debug_service"; +import ControlPanel from "web.ControlPanel"; import core from "web.core"; import AbstractAction from "web.AbstractAction"; @@ -136,4 +137,53 @@ QUnit.module("ActionManager", (hooks) => { assert.verifySteps(["debugItems executed"]); delete core.action_registry.map.customLegacy; }); + + QUnit.test("willUnmount is called down the legacy layers", async (assert) => { + assert.expect(7); + + let mountCount = 0; + patchWithCleanup(ControlPanel.prototype, { + mounted() { + mountCount = mountCount + 1; + this.__uniqueId = mountCount; + assert.step(`mounted ${this.__uniqueId}`); + this._super(...arguments); + }, + willUnmount() { + assert.step(`willUnmount ${this.__uniqueId}`); + this._super(...arguments); + }, + }); + + const LegacyAction = AbstractAction.extend({ + hasControlPanel: true, + start() { + const ret = this._super(...arguments); + const el = document.createElement("div"); + el.classList.add("custom-action"); + this.el.append(el); + return ret; + }, + }); + core.action_registry.add("customLegacy", LegacyAction); + + const webClient = await createWebClient({ testConfig }); + await doAction(webClient, 1); + await doAction(webClient, "customLegacy"); + await click(webClient.el.querySelectorAll(".breadcrumb-item")[0]); + await legacyExtraNextTick(); + + webClient.destroy(); + + assert.verifySteps([ + "mounted 1", + "willUnmount 1", + "mounted 2", + "willUnmount 2", + "mounted 3", + "willUnmount 3", + ]); + + delete core.action_registry.map.customLegacy; + }); }); diff --git a/addons/web/static/tests/webclient/actions/report_action_tests.js b/addons/web/static/tests/webclient/actions/report_action_tests.js index d4b7400cd0a47..9e624e9931bdc 100644 --- a/addons/web/static/tests/webclient/actions/report_action_tests.js +++ b/addons/web/static/tests/webclient/actions/report_action_tests.js @@ -1,24 +1,19 @@ /** @odoo-module **/ import { registry } from "@web/core/registry"; -import { download } from "@web/core/network/download"; import { uiService } from "@web/core/ui_service"; import testUtils from "web.test_utils"; import ReportClientAction from "report.client_action"; import { clearRegistryWithCleanup } from "../../helpers/mock_env"; import { makeFakeNotificationService, makeFakeUserService } from "../../helpers/mock_services"; -import { patchWithCleanup } from "../../helpers/utils"; import { createWebClient, doAction, getActionManagerTestConfig } from "./helpers"; +import { mockDownload } from "@web/../tests/helpers/utils"; let testConfig; const mainComponentRegistry = registry.category("main_components"); const serviceRegistry = registry.category("services"); -function mockDownload(cb) { - patchWithCleanup(download, { _download: cb }); -} - QUnit.module("ActionManager", (hooks) => { hooks.beforeEach(() => { testConfig = getActionManagerTestConfig(); diff --git a/addons/web/static/tests/webclient/actions/target_tests.js b/addons/web/static/tests/webclient/actions/target_tests.js index 0f54d7d480d42..c94456eae496d 100644 --- a/addons/web/static/tests/webclient/actions/target_tests.js +++ b/addons/web/static/tests/webclient/actions/target_tests.js @@ -294,8 +294,6 @@ QUnit.module("ActionManager", (hooks) => { await testUtils.dom.click($(".modal:last .modal-footer .btn-primary")); assert.containsOnce(document.body, ".modal"); assert.strictEqual($(".modal:last .modal-body").text().trim(), "Another action"); - - webClient.destroy(); } ); diff --git a/addons/web/static/tests/webclient/actions/window_action_tests.js b/addons/web/static/tests/webclient/actions/window_action_tests.js index 63a506f83f16c..dbdf3aa2bad99 100644 --- a/addons/web/static/tests/webclient/actions/window_action_tests.js +++ b/addons/web/static/tests/webclient/actions/window_action_tests.js @@ -2168,8 +2168,6 @@ QUnit.module("ActionManager", (hooks) => { ".modal.o_technical_modal", "Warning modal should be closed" ); - - webClient.destroy(); } ); @@ -2190,7 +2188,6 @@ QUnit.module("ActionManager", (hooks) => { await legacyExtraNextTick(); assert.containsOnce(webClient, ".o_dialog"); assert.containsOnce(webClient, ".o_dialog .o_act_window .o_view_controller"); - webClient.destroy(); } ); @@ -2224,7 +2221,6 @@ QUnit.module("ActionManager", (hooks) => { await click(webClient.el.querySelector(".o_form_buttons_edit .o_form_button_save")); await legacyExtraNextTick(); assert.isVisible(webClient.el.querySelector(".o_form_buttons_view .o_form_button_edit")); - webClient.destroy(); }); QUnit.test("debugManager is active for (legacy) views", async function (assert) { @@ -2248,7 +2244,6 @@ QUnit.module("ActionManager", (hooks) => { webClient.el, ".o_debug_manager .o_dropdown_item:contains('Edit View: Kanban')" ); - webClient.destroy(); }); QUnit.test("reload a view via the view switcher keep state", async function (assert) { From b818ceb8eb45d216f001cb2e6226f4b73beef9cb Mon Sep 17 00:00:00 2001 From: "Lucas Perais (lpe)" Date: Wed, 26 May 2021 09:02:58 +0200 Subject: [PATCH 2/2] fixup! [REF] *: adapt code to new owl webclient --- addons/board/static/tests/dashboard_tests.js | 344 +++++----- .../bus/static/tests/assets_watchdog_tests.js | 1 - .../tests/calendar_notification_tests.js | 6 - addons/mail/static/src/utils/test_utils.js | 39 +- addons/mail/static/tests/activity_tests.js | 202 +++--- .../tests/product_pricelist_report_test.js | 63 +- ...stock_traceability_report_backend_tests.js | 69 +- .../base/static/tests/base_settings_tests.js | 597 ++++++++++-------- 8 files changed, 719 insertions(+), 602 deletions(-) diff --git a/addons/board/static/tests/dashboard_tests.js b/addons/board/static/tests/dashboard_tests.js index 86e203c7d9bf8..5510be32bd9f0 100644 --- a/addons/board/static/tests/dashboard_tests.js +++ b/addons/board/static/tests/dashboard_tests.js @@ -9,11 +9,16 @@ var ListRenderer = require('web.ListRenderer'); var pyUtils = require('web.py_utils'); const cpHelpers = testUtils.controlPanel; -var createActionManager = testUtils.createActionManager; +const { + createWebClient, + doAction, + getActionManagerTestConfig, +} = require("@web/../tests/webclient/actions/helpers"); var createView = testUtils.createView; const patchDate = testUtils.mock.patchDate; +let testConfig; QUnit.module('Dashboard', { beforeEach: function () { this.data = { @@ -48,6 +53,9 @@ QUnit.module('Dashboard', { }], }, }; + testConfig = getActionManagerTestConfig(); + // map legacy test data + Object.assign(testConfig.serverData, { models: this.data }); }, }); @@ -696,143 +704,156 @@ QUnit.test('dashboard intercepts custom events triggered by sub controllers', as board.destroy(); }); -QUnit.skip('save actions to dashboard', async function (assert) { +QUnit.test("save actions to dashboard", async function (assert) { assert.expect(6); testUtils.patch(ListController, { getOwnedQueryParams: function () { var result = this._super.apply(this, arguments); result.context = { - 'fire': 'on the bayou', + fire: "on the bayou", }; return result; - } + }, }); - this.data['partner'].fields.foo.sortable = true; - - var actionManager = await createActionManager({ - data: this.data, - archs: { - 'partner,false,list': '', - 'partner,false,search': '', - }, - mockRPC: function (route, args) { - if (route === '/board/add_to_dashboard') { - assert.deepEqual(args.context_to_save.group_by, ['foo'], - 'The group_by should have been saved'); - assert.deepEqual(args.context_to_save.orderedBy, - [{ - name: 'foo', + testConfig.serverData.models["partner"].fields.foo.sortable = true; + + testConfig.serverData.views = { + "partner,false,list": '', + "partner,false,search": "", + }; + + const mockRPC = (route, args) => { + if (route === "/board/add_to_dashboard") { + assert.deepEqual( + args.context_to_save.group_by, + ["foo"], + "The group_by should have been saved" + ); + assert.deepEqual( + args.context_to_save.orderedBy, + [ + { + name: "foo", asc: true, - }], - 'The orderedBy should have been saved'); - assert.strictEqual(args.context_to_save.fire, 'on the bayou', - 'The context of a controller should be passed and flattened'); - assert.strictEqual(args.action_id, 1, - "should save the correct action"); - assert.strictEqual(args.view_mode, 'list', - "should save the correct view type"); - return Promise.resolve(true); - } - return this._super.apply(this, arguments); + }, + ], + "The orderedBy should have been saved" + ); + assert.strictEqual( + args.context_to_save.fire, + "on the bayou", + "The context of a controller should be passed and flattened" + ); + assert.strictEqual(args.action_id, 1, "should save the correct action"); + assert.strictEqual(args.view_mode, "list", "should save the correct view type"); + return Promise.resolve(true); } - }); + }; - await actionManager.doAction({ + const webClient = await createWebClient({ testConfig, mockRPC }); + + await doAction(webClient, { id: 1, - res_model: 'partner', - type: 'ir.actions.act_window', - views: [[false, 'list']], + res_model: "partner", + type: "ir.actions.act_window", + views: [[false, "list"]], }); - assert.containsOnce(actionManager, '.o_list_view', - "should display the list view"); + assert.containsOnce(webClient, ".o_list_view", "should display the list view"); // Sort the list - await testUtils.dom.click($('.o_column_sortable')); + await testUtils.dom.click($(".o_column_sortable")); // Group It - await cpHelpers.toggleGroupByMenu(actionManager); - await cpHelpers.toggleAddCustomGroup(actionManager); - await cpHelpers.applyGroup(actionManager); + await cpHelpers.toggleGroupByMenu(webClient); + await cpHelpers.toggleAddCustomGroup(webClient); + await cpHelpers.applyGroup(webClient); // add this action to dashboard - await cpHelpers.toggleFavoriteMenu(actionManager); + await cpHelpers.toggleFavoriteMenu(webClient); - await testUtils.dom.click($('.o_add_to_board > button')); - await testUtils.fields.editInput($('.o_add_to_board input'), 'a name'); - await testUtils.dom.click($('.o_add_to_board div button')); + await testUtils.dom.click($(".o_add_to_board > button")); + await testUtils.fields.editInput($(".o_add_to_board input"), "a name"); + await testUtils.dom.click($(".o_add_to_board div button")); testUtils.unpatch(ListController); - - actionManager.destroy(); }); -QUnit.skip('save two searches to dashboard', async function (assert) { +QUnit.test("save two searches to dashboard", async function (assert) { // the second search saved should not be influenced by the first assert.expect(2); - var actionManager = await createActionManager({ - data: this.data, - archs: { - 'partner,false,list': '', - 'partner,false,search': '', - }, - mockRPC: function (route, args) { - if (route === '/board/add_to_dashboard') { - if (filter_count === 0) { - assert.deepEqual(args.domain, [["display_name", "ilike", "a"]], - "the correct domain should be sent"); - } - if (filter_count === 1) { - assert.deepEqual(args.domain, [["display_name", "ilike", "b"]], - "the correct domain should be sent"); - } - - filter_count += 1; - return Promise.resolve(true); + testConfig.serverData.views = { + "partner,false,list": '', + "partner,false,search": "", + }; + + const mockRPC = (route, args) => { + if (route === "/board/add_to_dashboard") { + if (filter_count === 0) { + assert.deepEqual( + args.domain, + [["display_name", "ilike", "a"]], + "the correct domain should be sent" + ); } - return this._super.apply(this, arguments); - }, - }); + if (filter_count === 1) { + assert.deepEqual( + args.domain, + [["display_name", "ilike", "b"]], + "the correct domain should be sent" + ); + } + + filter_count += 1; + return Promise.resolve(true); + } + }; + + const webClient = await createWebClient({ testConfig, mockRPC }); - await actionManager.doAction({ + await doAction(webClient, { id: 1, - res_model: 'partner', - type: 'ir.actions.act_window', - views: [[false, 'list']], + res_model: "partner", + type: "ir.actions.act_window", + views: [[false, "list"]], }); var filter_count = 0; // Add a first filter - await cpHelpers.toggleFilterMenu(actionManager); - await cpHelpers.toggleAddCustomFilter(actionManager); - await testUtils.fields.editInput(actionManager.el.querySelector('.o_generator_menu_value .o_input'), 'a'); - await cpHelpers.applyFilter(actionManager); + await cpHelpers.toggleFilterMenu(webClient); + await cpHelpers.toggleAddCustomFilter(webClient); + await testUtils.fields.editInput( + webClient.el.querySelector(".o_generator_menu_value .o_input"), + "a" + ); + await cpHelpers.applyFilter(webClient); // Add it to dashboard - await cpHelpers.toggleFavoriteMenu(actionManager); - await testUtils.dom.click($('.o_add_to_board > button')); - await testUtils.dom.click($('.o_add_to_board div button')); + await cpHelpers.toggleFavoriteMenu(webClient); + await testUtils.dom.click($(".o_add_to_board > button")); + await testUtils.dom.click($(".o_add_to_board div button")); // Remove it - await testUtils.dom.click(actionManager.el.querySelector('.o_facet_remove')); + await testUtils.dom.click(webClient.el.querySelector(".o_facet_remove")); // Add the second filter - await cpHelpers.toggleFilterMenu(actionManager); - await cpHelpers.toggleAddCustomFilter(actionManager); - await testUtils.fields.editInput(actionManager.el.querySelector('.o_generator_menu_value .o_input'), "b"); - await cpHelpers.applyFilter(actionManager); + await cpHelpers.toggleFilterMenu(webClient); + await cpHelpers.toggleAddCustomFilter(webClient); + await testUtils.fields.editInput( + webClient.el.querySelector(".o_generator_menu_value .o_input"), + "b" + ); + await cpHelpers.applyFilter(webClient); // Add it to dashboard - await cpHelpers.toggleFavoriteMenu(actionManager); - await testUtils.dom.click(actionManager.el.querySelector('.o_add_to_board > button')); - await testUtils.dom.click(actionManager.el.querySelector('.o_add_to_board div button')); - - actionManager.destroy(); + await cpHelpers.toggleFavoriteMenu(webClient); + await testUtils.dom.click(webClient.el.querySelector(".o_add_to_board > button")); + await testUtils.dom.click(webClient.el.querySelector(".o_add_to_board div button")); }); -QUnit.skip('save a action domain to dashboard', async function (assert) { +QUnit.test("save a action domain to dashboard", async function (assert) { // View domains are to be added to the dashboard domain assert.expect(1); @@ -840,47 +861,43 @@ QUnit.skip('save a action domain to dashboard', async function (assert) { var filter_domain = ["display_name", "ilike", "b"]; // The filter domain already contains the view domain, but is always added by dashboard.., - var expected_domain = ['&', view_domain, '&', view_domain, filter_domain]; + var expected_domain = ["&", view_domain, "&", view_domain, filter_domain]; - var actionManager = await createActionManager({ - data: this.data, - archs: { - 'partner,false,list': '', - 'partner,false,search': '', - }, - mockRPC: function (route, args) { - if (route === '/board/add_to_dashboard') { - assert.deepEqual(args.domain, expected_domain, - "the correct domain should be sent"); - return Promise.resolve(true); - } - return this._super.apply(this, arguments); - }, - }); + testConfig.serverData.views = { + "partner,false,list": '', + "partner,false,search": "", + }; + + const mockRPC = (route, args) => { + if (route === "/board/add_to_dashboard") { + assert.deepEqual(args.domain, expected_domain, "the correct domain should be sent"); + return Promise.resolve(true); + } + }; + + const webClient = await createWebClient({ testConfig, mockRPC }); - await actionManager.doAction({ + await doAction(webClient, { id: 1, - res_model: 'partner', - type: 'ir.actions.act_window', - views: [[false, 'list']], + res_model: "partner", + type: "ir.actions.act_window", + views: [[false, "list"]], domain: [view_domain], }); // Add a filter - await cpHelpers.toggleFilterMenu(actionManager); - await cpHelpers.toggleAddCustomFilter(actionManager); + await cpHelpers.toggleFilterMenu(webClient); + await cpHelpers.toggleAddCustomFilter(webClient); await testUtils.fields.editInput( - actionManager.el.querySelector('.o_generator_menu_value .o_input'), + webClient.el.querySelector(".o_generator_menu_value .o_input"), "b" ); - await cpHelpers.applyFilter(actionManager); + await cpHelpers.applyFilter(webClient); // Add it to dashboard - await cpHelpers.toggleFavoriteMenu(actionManager); - await testUtils.dom.click(actionManager.el.querySelector('.o_add_to_board > button')); + await cpHelpers.toggleFavoriteMenu(webClient); + await testUtils.dom.click(webClient.el.querySelector(".o_add_to_board > button")); // add - await testUtils.dom.click(actionManager.el.querySelector('.o_add_to_board div button')); - - actionManager.destroy(); + await testUtils.dom.click(webClient.el.querySelector(".o_add_to_board div button")); }); QUnit.test("Views should be loaded in the user's language", async function (assert) { @@ -1041,62 +1058,71 @@ QUnit.test('click on a cell of pivot view inside dashboard', async function (ass form.destroy(); }); -QUnit.skip('correctly save the time ranges of a reporting view in comparison mode', async function (assert) { - assert.expect(1); +QUnit.test( + "correctly save the time ranges of a reporting view in comparison mode", + async function (assert) { + assert.expect(1); - const unpatchDate = patchDate(2020, 6, 1, 11, 0, 0); + const unpatchDate = patchDate(2020, 6, 1, 11, 0, 0); - this.data.partner.fields.date = { string: 'Date', type: 'date', sortable: true }; + testConfig.serverData.models.partner.fields.date = { + string: "Date", + type: "date", + sortable: true, + }; - const actionManager = await createActionManager({ - data: this.data, - archs: { - 'partner,false,pivot': '', - 'partner,false,search': '', - }, - mockRPC: function (route, args) { - if (route === '/board/add_to_dashboard') { + testConfig.serverData.views = { + "partner,false,pivot": '', + "partner,false,search": '', + }; + + const mockRPC = (route, args) => { + if (route === "/board/add_to_dashboard") { assert.deepEqual(args.context_to_save.comparison, { comparisonId: "previous_period", fieldName: "date", fieldDescription: "Date", rangeDescription: "July 2020", - range: ["&",["date", ">=", "2020-07-01"], ["date", "<=", "2020-07-31"]], - comparisonRange: ["&", ["date", ">=", "2020-06-01"], ["date", "<=", "2020-06-30"]], + range: ["&", ["date", ">=", "2020-07-01"], ["date", "<=", "2020-07-31"]], + comparisonRange: [ + "&", + ["date", ">=", "2020-06-01"], + ["date", "<=", "2020-06-30"], + ], comparisonRangeDescription: "June 2020", }); return Promise.resolve(true); } - return this._super.apply(this, arguments); - }, - }); + }; - await actionManager.doAction({ - id: 1, - res_model: 'partner', - type: 'ir.actions.act_window', - views: [[false, 'pivot']], - }); + const webClient = await createWebClient({ testConfig, mockRPC }); - // filter on July 2020 - await cpHelpers.toggleFilterMenu(actionManager); - await cpHelpers.toggleMenuItem(actionManager, 'Date'); - await cpHelpers.toggleMenuItemOption(actionManager, 'Date', 'July'); + await doAction(webClient, { + id: 1, + res_model: "partner", + type: "ir.actions.act_window", + views: [[false, "pivot"]], + }); - // compare July 2020 to June 2020 - await cpHelpers.toggleComparisonMenu(actionManager); - await cpHelpers.toggleMenuItem(actionManager, 0); + // filter on July 2020 + await cpHelpers.toggleFilterMenu(webClient); + await cpHelpers.toggleMenuItem(webClient, "Date"); + await cpHelpers.toggleMenuItemOption(webClient, "Date", "July"); - // add the view to the dashboard - await cpHelpers.toggleFavoriteMenu(actionManager); + // compare July 2020 to June 2020 + await cpHelpers.toggleComparisonMenu(webClient); + await cpHelpers.toggleMenuItem(webClient, 0); - await testUtils.dom.click($('.o_add_to_board > button')); - await testUtils.fields.editInput($('.o_add_to_board input'), 'a name'); - await testUtils.dom.click($('.o_add_to_board div button')); + // add the view to the dashboard + await cpHelpers.toggleFavoriteMenu(webClient); - unpatchDate(); - actionManager.destroy(); -}); + await testUtils.dom.click($(".o_add_to_board > button")); + await testUtils.fields.editInput($(".o_add_to_board input"), "a name"); + await testUtils.dom.click($(".o_add_to_board div button")); + + unpatchDate(); + } +); QUnit.test('correctly display the time range descriptions of a reporting view in comparison mode', async function (assert) { assert.expect(1); diff --git a/addons/bus/static/tests/assets_watchdog_tests.js b/addons/bus/static/tests/assets_watchdog_tests.js index e96ceaca835bc..175a069176e2c 100644 --- a/addons/bus/static/tests/assets_watchdog_tests.js +++ b/addons/bus/static/tests/assets_watchdog_tests.js @@ -82,6 +82,5 @@ QUnit.module("Bus Assets WatchDog", (hooks) => { // reload by clicking on the reload button await click(webClient.el, ".o_notification_buttons .btn-primary"); assert.verifySteps(["reloadPage"]); - webClient.destroy(); }); }); diff --git a/addons/calendar/static/tests/calendar_notification_tests.js b/addons/calendar/static/tests/calendar_notification_tests.js index 69a04d46a2408..9ac4238b81c28 100644 --- a/addons/calendar/static/tests/calendar_notification_tests.js +++ b/addons/calendar/static/tests/calendar_notification_tests.js @@ -98,8 +98,6 @@ QUnit.module("Calendar Notification", (hooks) => { await click(webClient.el.querySelector(".o_notification_buttons .btn")); assert.verifySteps(["notifyAck"]); assert.containsNone(webClient.el, ".o_notification"); - - webClient.destroy(); } ); @@ -171,8 +169,6 @@ QUnit.module("Calendar Notification", (hooks) => { await click(webClient.el.querySelectorAll(".o_notification_buttons .btn")[1]); assert.verifySteps(["calendar.action_calendar_event_notify"]); assert.containsNone(webClient.el, ".o_notification"); - - webClient.destroy(); } ); @@ -232,8 +228,6 @@ QUnit.module("Calendar Notification", (hooks) => { await click(webClient.el.querySelectorAll(".o_notification_buttons .btn")[2]); assert.verifySteps([], "should only close the notification withtout calling a rpc"); assert.containsNone(webClient.el, ".o_notification"); - - webClient.destroy(); } ); }); diff --git a/addons/mail/static/src/utils/test_utils.js b/addons/mail/static/src/utils/test_utils.js index 3d94c28b9df7b..0919be6314632 100644 --- a/addons/mail/static/src/utils/test_utils.js +++ b/addons/mail/static/src/utils/test_utils.js @@ -18,12 +18,17 @@ import AbstractStorageService from 'web.AbstractStorageService'; import NotificationService from 'web.NotificationService'; import RamStorage from 'web.RamStorage'; import { - createActionManager, createView, makeTestPromise, mock, } from 'web.test_utils'; import Widget from 'web.Widget'; +import { + createWebClient, + getActionManagerTestConfig, +} from "@web/../tests/webclient/actions/helpers"; +import { ComponentAdapter } from "web.OwlCompatibility"; +import LegacyMockServer from "web.MockServer"; const { addMockEnvironment, @@ -546,8 +551,36 @@ async function start(param0 = {}) { } }); } else if (hasActionManager) { - throw new Error("This is deprecated, should be adapted"); - widget = await createActionManager(kwargs); + let testConfig; + if (!kwargs.testConfig) { + testConfig = getActionManagerTestConfig(); + } else { + testConfig = kwargs.testConfig; + delete kwargs.testConfig; + } + + if (kwargs.actions) { + const actions = {}; + kwargs.actions.forEach((act) => { + actions[act.xml_id || act.id] = act; + }); + Object.assign(testConfig.serverData.actions, actions); + delete kwargs.actions; + } + + Object.assign(testConfig.serverData.views, kwargs.archs); + delete kwargs.archs; + + Object.assign(testConfig.serverData.models, kwargs.data); + delete kwargs.data; + + const mockRPC = kwargs.mockRPC; + delete kwargs.mockRPC; + + const legacyParams = kwargs; + legacyParams.withLegacyMockServer = true; + const widget = await createWebClient({ testConfig, mockRPC, legacyParams }); + legacyPatch(widget, { destroy() { destroyCallbacks.forEach(callback => callback({ widget })); diff --git a/addons/mail/static/tests/activity_tests.js b/addons/mail/static/tests/activity_tests.js index bd01586654a0a..92af5da6c6bf5 100644 --- a/addons/mail/static/tests/activity_tests.js +++ b/addons/mail/static/tests/activity_tests.js @@ -2,8 +2,14 @@ import ActivityView from '@mail/js/views/activity/activity_view'; import testUtils from 'web.test_utils'; +import { createWebClient } from "@web/../tests/webclient/actions/helpers"; -var createActionManager = testUtils.createActionManager; +import { legacyExtraNextTick } from "@web/../tests/helpers/utils"; +import { doAction, getActionManagerTestConfig } from "@web/../tests/webclient/actions/helpers"; +import { registry } from "@web/core/registry"; +import { makeFakeUserService } from "@web/../tests/helpers/mock_services"; + +let testConfig; var createView = testUtils.createView; @@ -108,6 +114,8 @@ QUnit.module('activity view', { ], }, }; + testConfig = getActionManagerTestConfig(); + Object.assign(testConfig.serverData.models, this.data); } }); @@ -350,49 +358,68 @@ QUnit.test('activity view: activity widget', async function (assert) { activity.destroy(); }); -QUnit.skip('activity view: no group_by_menu and no comparison_menu', async function (assert) { + +QUnit.test("activity view: no group_by_menu and no comparison_menu", async function (assert) { assert.expect(4); - var actionManager = await createActionManager({ - actions: [{ - id: 1, - name: 'Task Action', - res_model: 'task', - type: 'ir.actions.act_window', - views: [[false, 'activity']], - }], - archs: { - 'task,false,activity': '' + - '' + - '
' + - '' + - '
' + - '
' + - '
', - 'task,false,search': '', - }, - data: this.data, - session: { - user_context: {lang: 'zz_ZZ'}, - }, - mockRPC: function(route, args) { - if (args.method === 'get_activity_data') { - assert.deepEqual(args.kwargs.context, {lang: 'zz_ZZ'}, - 'The context should have been passed'); - } - return this._super.apply(this, arguments); - }, - }); + testConfig.serverData.actions[1] = { + id: 1, + name: "Task Action", + res_model: "task", + type: "ir.actions.act_window", + views: [[false, "activity"]], + }; - await actionManager.doAction(1); + testConfig.serverData.views = { + "task,false,activity": + '' + + "" + + '
' + + '' + + "
" + + "
" + + "
", + "task,false,search": "", + }; + + const mockRPC = (route, args) => { + if (args.method === "get_activity_data") { + assert.strictEqual( + args.kwargs.context.lang, + "zz_ZZ", + "The context should have been passed" + ); + } + }; - assert.containsN(actionManager, '.o_search_options .o_dropdown button:visible', 2, - "only two elements should be available in view search"); - assert.isVisible(actionManager.$('.o_search_options .o_dropdown.o_filter_menu > button'), - "filter should be available in view search"); - assert.isVisible(actionManager.$('.o_search_options .o_dropdown.o_favorite_menu > button'), - "favorites should be available in view search"); - actionManager.destroy(); + registry.category("services").add( + "user", + makeFakeUserService({ + session_info: { + user_context: { lang: "zz_ZZ" }, + }, + }), + { force: true } + ); + + const webClient = await createWebClient({ testConfig, mockRPC , legacyParams: {withLegacyMockServer: true}}); + + await doAction(webClient, 1); + + assert.containsN( + webClient, + ".o_search_options .o_dropdown button:visible", + 2, + "only two elements should be available in view search" + ); + assert.isVisible( + $(webClient.el).find(".o_search_options .o_dropdown.o_filter_menu > button"), + "filter should be available in view search" + ); + assert.isVisible( + $(webClient.el).find(".o_search_options .o_dropdown.o_favorite_menu > button"), + "favorites should be available in view search" + ); }); QUnit.test('activity view: search more to schedule an activity for a record of a respecting model', async function (assert) { @@ -458,63 +485,54 @@ QUnit.test('activity view: search more to schedule an activity for a record of a activity.destroy(); }); -QUnit.skip('Activity view: discard an activity creation dialog', async function (assert) { +QUnit.test("Activity view: discard an activity creation dialog", async function (assert) { assert.expect(2); - var actionManager = await createActionManager({ - actions: [{ - id: 1, - name: 'Task Action', - res_model: 'task', - type: 'ir.actions.act_window', - views: [[false, 'activity']], - }], - archs: { - 'task,false,activity': ` - - -
- -
-
-
`, - 'task,false,search': '', - 'mail.activity,false,form': ` -
- -
-
- ` - }, - data: this.data, - intercepts: { - do_action(ev) { - actionManager.doAction(ev.data.action, ev.data.options); - } - }, - async mockRPC(route, args) { - if (args.method === 'check_access_rights') { - return true; - } - return this._super(...arguments); - }, - }); - await actionManager.doAction(1); + testConfig.serverData.actions[1] = { + id: 1, + name: "Task Action", + res_model: "task", + type: "ir.actions.act_window", + views: [[false, "activity"]], + }; + + testConfig.serverData.views = { + "task,false,activity": ` + + +
+ +
+
+
`, + "task,false,search": "", + "mail.activity,false,form": ` +
+ +
+
+ `, + }; + + const mockRPC = (route, args) => { + if (args.method === "check_access_rights") { + return true; + } + }; - await testUtils.dom.click(actionManager.$('.o_activity_view .o_data_row .o_activity_empty_cell')[0]); - assert.containsOnce( - $, - '.modal.o_technical_modal.show', - "Activity Modal should be opened"); + const webClient = await createWebClient({ testConfig, mockRPC, legacyParams: {withLegacyMockServer: true} }); + await doAction(webClient, 1); - await testUtils.dom.click($('.modal.o_technical_modal.show button[special="cancel"]')); - assert.containsNone( - $, - '.modal.o_technical_modal.show', - "Activity Modal should be closed"); + await testUtils.dom.click( + $(webClient.el).find(".o_activity_view .o_data_row .o_activity_empty_cell")[0] + ); + await legacyExtraNextTick(); + assert.containsOnce($, ".modal.o_technical_modal", "Activity Modal should be opened"); - actionManager.destroy(); + await testUtils.dom.click($('.modal.o_technical_modal button[special="cancel"]')); + await legacyExtraNextTick(); + assert.containsNone($, ".modal.o_technical_modal", "Activity Modal should be closed"); }); }); diff --git a/addons/product/static/tests/product_pricelist_report_test.js b/addons/product/static/tests/product_pricelist_report_test.js index d1bd158b1e773..a381884a4d0ee 100644 --- a/addons/product/static/tests/product_pricelist_report_test.js +++ b/addons/product/static/tests/product_pricelist_report_test.js @@ -1,11 +1,10 @@ odoo.define('product.pricelist.report.tests', function (require) { "use strict"; -const core = require('web.core'); const GeneratePriceList = require('product.generate_pricelist').GeneratePriceList; -const NotificationService = require('web.NotificationService'); const testUtils = require('web.test_utils'); -const createActionManager = testUtils.createActionManager; -const testUtilsMock = require('web.test_utils_mock'); + +const { createWebClient, getActionManagerTestConfig, doAction } = require('@web/../tests/webclient/actions/helpers'); +let testConfig; QUnit.module('Product Pricelist', { beforeEach: function () { @@ -32,9 +31,11 @@ QUnit.module('Product Pricelist', { }] } }; + testConfig = getActionManagerTestConfig(); + Object.assign(testConfig.serverData, {models: this.data}); }, }, function () { - QUnit.skip('Pricelist Client Action', async function (assert) { + QUnit.test('Pricelist Client Action', async function (assert) { assert.expect(21); const self = this; @@ -50,21 +51,14 @@ QUnit.module('Product Pricelist', { return this._super.apply(this, arguments); }, }); + const mockRPC = (route, args) => { + if (route === '/web/dataset/call_kw/report.product.report_pricelist/get_html') { + return Promise.resolve(""); + } + }; - const actionManager = await createActionManager({ - data: this.data, - mockRPC: function(route, args) { - if (route === '/web/dataset/call_kw/report.product.report_pricelist/get_html') { - return Promise.resolve(""); - } - return this._super(route, args); - }, - services: { - notification: NotificationService, - }, - }); - - await actionManager.doAction({ + const webClient = await createWebClient({ testConfig, mockRPC }); + await doAction(webClient, { id: 1, name: 'Generate Pricelist', tag: 'generate_pricelist', @@ -78,7 +72,7 @@ QUnit.module('Product Pricelist', { }); // checking default pricelist - assert.strictEqual(actionManager.$('.o_field_many2one input').val(), "Public Pricelist", + assert.strictEqual($(webClient.el).find('.o_field_many2one input').val(), "Public Pricelist", "should have default pricelist"); // changing pricelist @@ -86,28 +80,28 @@ QUnit.module('Product Pricelist', { await testUtils.fields.many2one.clickItem("pricelist_id", "Test"); // check wherther pricelist value has been updated or not. along with that check default quantities should be there. - assert.strictEqual(actionManager.$('.o_field_many2one input').val(), "Test", + assert.strictEqual($(webClient.el).find('.o_field_many2one input').val(), "Test", "After pricelist change, the pricelist_id field should be updated"); - assert.strictEqual(actionManager.$('.o_badges > .badge').length, 3, + assert.strictEqual($(webClient.el).find('.o_badges > .badge').length, 3, "There should be 3 default Quantities"); // existing quantity can not be added. - await testUtils.dom.click(actionManager.$('.o_add_qty')); + await testUtils.dom.click($(webClient.el).find('.o_add_qty')); let notificationElement = document.body.querySelector('.o_notification_manager .o_notification.bg-info'); assert.strictEqual(notificationElement.querySelector('.o_notification_content').textContent, "Quantity already present (1).", "Existing Quantity can not be added"); // adding few more quantities to check. - actionManager.$('.o_product_qty').val(2); + $(webClient.el).find('.o_product_qty').val(2); Qty.push(2); - await testUtils.dom.click(actionManager.$('.o_add_qty')); - actionManager.$('.o_product_qty').val(3); + await testUtils.dom.click($(webClient.el).find('.o_add_qty')); + $(webClient.el).find('.o_product_qty').val(3); Qty.push(3); - await testUtils.dom.click(actionManager.$('.o_add_qty')); + await testUtils.dom.click($(webClient.el).find('.o_add_qty')); // should not be added more then 5 quantities. - actionManager.$('.o_product_qty').val(4); - await testUtils.dom.click(actionManager.$('.o_add_qty')); + $(webClient.el).find('.o_product_qty').val(4); + await testUtils.dom.click($(webClient.el).find('.o_add_qty')); notificationElement = document.body.querySelector('.o_notification_manager .o_notification.bg-warning'); assert.strictEqual(notificationElement.querySelector('.o_notification_content').textContent, @@ -116,15 +110,15 @@ QUnit.module('Product Pricelist', { // removing all the quantities should work Qty.pop(10); - await testUtils.dom.click(actionManager.$('.o_badges .badge:contains("10") .o_remove_qty')); + await testUtils.dom.click($(webClient.el).find('.o_badges .badge:contains("10") .o_remove_qty')); Qty.pop(5); - await testUtils.dom.click(actionManager.$('.o_badges .badge:contains("5") .o_remove_qty')); + await testUtils.dom.click($(webClient.el).find('.o_badges .badge:contains("5") .o_remove_qty')); Qty.pop(3); - await testUtils.dom.click(actionManager.$('.o_badges .badge:contains("3") .o_remove_qty')); + await testUtils.dom.click($(webClient.el).find('.o_badges .badge:contains("3") .o_remove_qty')); Qty.pop(2); - await testUtils.dom.click(actionManager.$('.o_badges .badge:contains("2") .o_remove_qty')); + await testUtils.dom.click($(webClient.el).find('.o_badges .badge:contains("2") .o_remove_qty')); Qty.pop(1); - await testUtils.dom.click(actionManager.$('.o_badges .badge:contains("1") .o_remove_qty')); + await testUtils.dom.click($(webClient.el).find('.o_badges .badge:contains("1") .o_remove_qty')); assert.verifySteps([ 'field_changed', @@ -138,7 +132,6 @@ QUnit.module('Product Pricelist', { ]); testUtils.mock.unpatch(GeneratePriceList); - actionManager.destroy(); }); } diff --git a/addons/stock/static/tests/stock_traceability_report_backend_tests.js b/addons/stock/static/tests/stock_traceability_report_backend_tests.js index 2f2e531104ffe..e5b1f55273ca0 100644 --- a/addons/stock/static/tests/stock_traceability_report_backend_tests.js +++ b/addons/stock/static/tests/stock_traceability_report_backend_tests.js @@ -7,7 +7,9 @@ odoo.define('stock.stock_traceability_report_backend_tests', function (require) const testUtils = require('web.test_utils'); const { patch, unpatch } = require('web.utils'); - const { createActionManager, dom: domUtils } = testUtils; + const { dom: domUtils } = testUtils; + const { legacyExtraNextTick } = require("@web/../tests/helpers/utils"); + const { createWebClient, getActionManagerTestConfig, doAction } = require('@web/../tests/webclient/actions/helpers'); /** * Helper function to instantiate a stock report action. @@ -72,7 +74,7 @@ odoo.define('stock.stock_traceability_report_backend_tests', function (require) report.destroy(); }); - QUnit.skip("mounted is called once when returning on 'Stock report' from breadcrumb", async assert => { + QUnit.test("mounted is called once when returning on 'Stock report' from breadcrumb", async assert => { // This test can be removed as soon as we don't mix legacy and owl layers anymore. assert.expect(7); @@ -91,49 +93,48 @@ odoo.define('stock.stock_traceability_report_backend_tests', function (require) this.__superMounted(...arguments); }, }); - - const actionManager = await createActionManager({ - actions: [ - { - id: 42, - name: "Stock report", - tag: 'stock_report_generic', - type: 'ir.actions.client', - context: {}, - params: {}, - }, - ], - archs: { - 'partner,false,form': '
', - 'partner,false,search': '', - }, - data: { - partner: { - fields: { - display_name: { string: "Displayed name", type: "char" }, - }, - records: [ - {id: 1, display_name: "Genda Swami"}, - ], + const models = { + partner: { + fields: { + display_name: { string: "Displayed name", type: "char" }, }, + records: [ + {id: 1, display_name: "Genda Swami"}, + ], }, + }; + let testConfig = getActionManagerTestConfig(); + Object.assign(testConfig.serverData, { models }); + + testConfig.serverData.views = { + 'partner,false,form': '
', + 'partner,false,search': '', + }; + testConfig.serverData.actions[42] = { + id: 42, + name: "Stock report", + tag: 'stock_report_generic', + type: 'ir.actions.client', + context: {}, + params: {}, + }; + const webClient = await createWebClient({ + testConfig, mockRPC: function (route) { if (route === '/web/dataset/call_kw/stock.traceability.report/get_html') { return Promise.resolve({ html: 'Go to form view', }); } - return this._super.apply(this, arguments); - }, - intercepts: { - do_action: ev => actionManager.doAction(ev.data.action, ev.data.options), }, }); - await actionManager.doAction(42); - await domUtils.click(actionManager.$('.o_stock_reports_web_action')); - await domUtils.click(actionManager.$('.breadcrumb-item:first')); - actionManager.destroy(); + await doAction(webClient, 42); + await domUtils.click($(webClient.el).find('.o_stock_reports_web_action')); + await legacyExtraNextTick(); + await domUtils.click($(webClient.el).find('.breadcrumb-item:first')); + await legacyExtraNextTick(); + webClient.destroy(); assert.verifySteps([ 'mounted 1', diff --git a/odoo/addons/base/static/tests/base_settings_tests.js b/odoo/addons/base/static/tests/base_settings_tests.js index 61b5f821fb3fc..a08c27d977570 100644 --- a/odoo/addons/base/static/tests/base_settings_tests.js +++ b/odoo/addons/base/static/tests/base_settings_tests.js @@ -6,9 +6,15 @@ var view_registry = require('web.view_registry'); var createView = testUtils.createView; var BaseSettingsView = view_registry.get('base_settings'); -var createActionManager = testUtils.createActionManager; +const { legacyExtraNextTick } = require("@web/../tests/helpers/utils"); +const { + createWebClient, + doAction, + getActionManagerTestConfig, +} = require("@web/../tests/webclient/actions/helpers"); +let testConfig; QUnit.module('base_settings_tests', { beforeEach: function () { this.data = { @@ -23,6 +29,9 @@ QUnit.module('base_settings_tests', { fields: {} } }; + testConfig = getActionManagerTestConfig(); + const models = Object.assign(testConfig.serverData.models, this.data); + Object.assign(testConfig.serverData, { models }); } }, function () { @@ -91,160 +100,189 @@ QUnit.module('base_settings_tests', { form.destroy(); }); - QUnit.skip('settings views does not read existing id when coming back in breadcrumbs', async function (assert) { - assert.expect(8); - - var actions = [{ - id: 1, - name: 'Settings view', - res_model: 'res.config.settings', - type: 'ir.actions.act_window', - views: [[1, 'form']], - }, { - id: 4, - name: 'Other action', - res_model: 'task', - type: 'ir.actions.act_window', - views: [[2, 'list']], - }]; - var archs = { - 'res.config.settings,1,form': '
' + - '
' + - '
' + - '
', - 'task,2,list': '', - 'res.config.settings,false,search': '', - 'task,false,search': '', - }; + QUnit.test( + "settings views does not read existing id when coming back in breadcrumbs", + async function (assert) { + assert.expect(8); + + testConfig.serverData.actions = { + 1: { + id: 1, + name: "Settings view", + res_model: "res.config.settings", + type: "ir.actions.act_window", + views: [[1, "form"]], + }, + 4: { + id: 4, + name: "Other action", + res_model: "task", + type: "ir.actions.act_window", + views: [[2, "list"]], + }, + }; - var actionManager = await createActionManager({ - actions: actions, - archs: archs, - data: this.data, - mockRPC: function (route, args) { + testConfig.serverData.views = { + "res.config.settings,1,form": + `
+
+
+
`, + "task,2,list": '', + "res.config.settings,false,search": "", + "task,false,search": "", + }; + + const mockRPC = (route, args) => { if (args.method) { assert.step(args.method); } - return this._super.apply(this, arguments); - }, - }); - - await actionManager.doAction(1); - await testUtils.nextTick(); - await testUtils.dom.click(actionManager.$('button[name="4"]')); - await testUtils.dom.click($('.o_control_panel .breadcrumb-item a')); - assert.hasClass(actionManager.$('.o_form_view'), 'o_form_editable'); - assert.verifySteps([ - 'load_views', // initial setting action - 'onchange', // this is a setting view => create new record - 'create', // when we click on action button => save - 'read', // with save, we have a reload... (not necessary actually) - 'load_views', // for other action in breadcrumb, - // with a searchread (not shown here since it is a route) - 'onchange', // when we come back, we want to restart from scratch - ]); - - actionManager.destroy(); - }); - - QUnit.skip('clicking on any button in setting should show discard warning if setting form is dirty', async function (assert) { - assert.expect(11); - - var actions = [{ - id: 1, - name: 'Settings view', - res_model: 'res.config.settings', - type: 'ir.actions.act_window', - views: [[1, 'form']], - }, { - id: 4, - name: 'Other action', - res_model: 'task', - type: 'ir.actions.act_window', - views: [[2, 'list']], - }]; - var archs = { - 'res.config.settings,1,form': '
' + - '
' + - '
' + - '
' + - '
'+ - '
'+ - '
' + - ''+ - '
'+ - '
'+ - 'Foo'+ - '
'+ - 'this is foo'+ - '
'+ - '
' + - '
'+ - '
'+ - '
' + - '
', - 'task,2,list': '', - 'res.config.settings,false,search': '', - 'task,false,search': '', - }; - - var actionManager = await createActionManager({ - actions: actions, - archs: archs, - data: this.data, - mockRPC: function (route, args) { - if (route === '/web/dataset/call_button') { + }; + + const webClient = await createWebClient({ testConfig, mockRPC }); + + await doAction(webClient, 1); + await testUtils.dom.click($(webClient.el).find('button[name="4"]')); + await legacyExtraNextTick(); + await testUtils.dom.click($(".o_control_panel .breadcrumb-item a")); + await legacyExtraNextTick(); + assert.hasClass($(webClient.el).find(".o_form_view"), "o_form_editable"); + assert.verifySteps([ + "load_views", // initial setting action + "onchange", // this is a setting view => create new record + "create", // when we click on action button => save + "read", // with save, we have a reload... (not necessary actually) + "load_views", // for other action in breadcrumb, + // with a searchread (not shown here since it is a route) + "onchange", // when we come back, we want to restart from scratch + ]); + } + ); + + QUnit.test( + "clicking on any button in setting should show discard warning if setting form is dirty", + async function (assert) { + assert.expect(11); + + testConfig.serverData.actions = { + 1: { + id: 1, + name: "Settings view", + res_model: "res.config.settings", + type: "ir.actions.act_window", + views: [[1, "form"]], + }, + 4: { + id: 4, + name: "Other action", + res_model: "task", + type: "ir.actions.act_window", + views: [[2, "list"]], + }, + }; + + testConfig.serverData.views = { + "res.config.settings,1,form": + `
+
+
+
+
+
+
+ +
+
+ Foo +
+ this is foo +
+
+
+
+
+
`, + "task,2,list": '', + "res.config.settings,false,search": "", + "task,false,search": "", + }; + + const mockRPC = (route, args) => { + if (route === "/web/dataset/call_button") { if (args.method === "execute") { assert.ok("execute method called"); - return Promise.resolve(true); + return true; } if (args.method === "cancel") { assert.ok("cancel method called"); - return Promise.resolve(true); + return true; } } - return this._super.apply(this, arguments); - }, - }); - - await actionManager.doAction(1); - assert.containsNone(actionManager, '.o_field_boolean input:checked', - "checkbox should not be checked"); - - await testUtils.dom.click(actionManager.$("input[type='checkbox']")); - assert.containsOnce(actionManager, '.o_field_boolean input:checked', - "checkbox should be checked"); - - await testUtils.dom.click(actionManager.$('button[name="4"]')); - assert.containsOnce(document.body, '.modal', "should open a warning dialog"); - - await testUtils.dom.click($('.modal button:contains(Discard)')); - assert.containsOnce(actionManager, '.o_list_view', "should be open list view"); - - await testUtils.dom.click($('.o_control_panel .breadcrumb-item a')); - assert.containsNone(actionManager, '.o_field_boolean input:checked', - "checkbox should not be checked"); - - await testUtils.dom.click(actionManager.$("input[type='checkbox']")); - await testUtils.dom.click(actionManager.$('button[name="4"]')); - assert.containsOnce(document.body, '.modal', "should open a warning dialog"); - - await testUtils.dom.click($('.modal button:contains(Stay Here)')); - assert.containsOnce(actionManager, '.o_form_view' ,"should be remain on form view"); - - await testUtils.dom.click(actionManager.$("button[name='execute']")); - assert.containsNone(document.body, '.modal', "should not open a warning dialog"); - - await testUtils.dom.click(actionManager.$("input[type='checkbox']")); - await testUtils.dom.click(actionManager.$("button[name='cancel']")); - assert.containsNone(document.body, '.modal', "should not open a warning dialog"); - - actionManager.destroy(); - }); + }; + + const webClient = await createWebClient({ testConfig, mockRPC }); + + await doAction(webClient, 1); + assert.containsNone( + webClient, + ".o_field_boolean input:checked", + "checkbox should not be checked" + ); + + await testUtils.dom.click($(webClient.el).find("input[type='checkbox']")); + assert.containsOnce( + webClient, + ".o_field_boolean input:checked", + "checkbox should be checked" + ); + + await testUtils.dom.click($(webClient.el).find('button[name="4"]')); + await legacyExtraNextTick(); + assert.containsOnce(document.body, ".modal", "should open a warning dialog"); + + await testUtils.dom.click($(".modal button:contains(Discard)")); + await legacyExtraNextTick(); + assert.containsOnce(webClient, ".o_list_view", "should be open list view"); + + await testUtils.dom.click($(".o_control_panel .breadcrumb-item a")); + await legacyExtraNextTick(); + assert.containsNone( + webClient, + ".o_field_boolean input:checked", + "checkbox should not be checked" + ); + + await testUtils.dom.click($(webClient.el).find("input[type='checkbox']")); + await testUtils.dom.click($(webClient.el).find('button[name="4"]')); + await legacyExtraNextTick(); + assert.containsOnce(document.body, ".modal", "should open a warning dialog"); + + await testUtils.dom.click($(".modal button:contains(Stay Here)")); + await legacyExtraNextTick(); + assert.containsOnce(webClient, ".o_form_view", "should be remain on form view"); + + await testUtils.dom.click($(webClient.el).find("button[name='execute']")); + await legacyExtraNextTick(); + assert.containsNone( + document.body, + ".modal", + "should not open a warning dialog" + ); + + await testUtils.dom.click($(webClient.el).find("input[type='checkbox']")); + await testUtils.dom.click($(webClient.el).find("button[name='cancel']")); + await legacyExtraNextTick(); + assert.containsNone( + document.body, + ".modal", + "should not open a warning dialog" + ); + } + ); QUnit.test('settings view does not display other settings after reload', async function (assert) { assert.expect(2); @@ -365,72 +403,83 @@ QUnit.module('base_settings_tests', { form.destroy(); }); - QUnit.skip('execute action from settings view with several actions in the breadcrumb', async function (assert) { - // This commit fixes a race condition, that's why we artificially slow down a read rpc - assert.expect(4); - - const actions = [{ - id: 1, - name: 'First action', - res_model: 'task', - type: 'ir.actions.act_window', - views: [[1, 'list']], - }, { - id: 2, - name: 'Settings view', - res_model: 'res.config.settings', - type: 'ir.actions.act_window', - views: [[2, 'form']], - }, { - id: 3, - name: 'Other action', - res_model: 'task', - type: 'ir.actions.act_window', - views: [[3, 'list']], - }]; - const archs = { - 'task,1,list': '', - 'res.config.settings,2,form': ` -
-
-
-
`, - 'task,3,list': '', - 'res.config.settings,false,search': '', - 'task,false,search': '', - }; - - let loadViewsDef; - const actionManager = await createActionManager({ - actions: actions, - archs: archs, - data: this.data, - async mockRPC(route, args) { - const _super = this._super.bind(this); - if (args.method === 'read') { - await Promise.resolve(loadViewsDef); // slow down reload of settings view + QUnit.test( + "execute action from settings view with several actions in the breadcrumb", + async function (assert) { + // This commit fixes a race condition, that's why we artificially slow down a read rpc + assert.expect(4); + + testConfig.serverData.actions = { + 1: { + id: 1, + name: "First action", + res_model: "task", + type: "ir.actions.act_window", + views: [[1, "list"]], + }, + 2: { + id: 2, + name: "Settings view", + res_model: "res.config.settings", + type: "ir.actions.act_window", + views: [[2, "form"]], + }, + 3: { + id: 3, + name: "Other action", + res_model: "task", + type: "ir.actions.act_window", + views: [[3, "list"]], + }, + }; + + testConfig.serverData.views = { + "task,1,list": '', + "res.config.settings,2,form": ` +
+
+
+
`, + "task,3,list": '', + "res.config.settings,false,search": "", + "task,false,search": "", + }; + + let loadViewsDef; + const mockRPC = async (route, args) => { + if (args.method === "read") { + await loadViewsDef; // slow down reload of settings view } - return _super(route, args); - }, - }); - - await actionManager.doAction(1); - assert.strictEqual(actionManager.$('.breadcrumb').text(), 'First action'); - - await actionManager.doAction(2); - assert.strictEqual(actionManager.$('.breadcrumb').text(), 'First actionNew'); - - loadViewsDef = testUtils.makeTestPromise(); - await testUtils.dom.click(actionManager.$('button[name="3"]')); - assert.strictEqual(actionManager.$('.breadcrumb').text(), 'First actionNew'); - - loadViewsDef.resolve(); - await testUtils.nextTick(); - assert.strictEqual(actionManager.$('.breadcrumb').text(), 'First actionNewOther action'); - - actionManager.destroy(); - }); + }; + + const webClient = await createWebClient({ testConfig, mockRPC }); + await doAction(webClient, 1); + assert.strictEqual($(webClient.el).find(".breadcrumb").text(), "First action"); + + await doAction(webClient, 2); + assert.strictEqual( + $(webClient.el).find(".breadcrumb").text(), + "First actionNew" + ); + + loadViewsDef = testUtils.makeTestPromise(); + await testUtils.dom.click($(webClient.el).find('button[name="3"]')); + await legacyExtraNextTick(); + assert.strictEqual( + $(webClient.el).find(".breadcrumb").text(), + "First actionNew" + ); + + loadViewsDef.resolve(); + await testUtils.nextTick(); + await legacyExtraNextTick(); + assert.strictEqual( + $(webClient.el).find(".breadcrumb").text(), + "First actionNewOther action" + ); + } + ); QUnit.test('settings can contain one2many fields', async function (assert) { assert.expect(2); @@ -477,75 +526,79 @@ QUnit.module('base_settings_tests', { form.destroy(); }); - QUnit.skip('call "call_button/execute" when clicking on a button in dirty settings', async function (assert) { - assert.expect(7); - - const actions = [{ - id: 1, - name: 'Settings view', - res_model: 'res.config.settings', - type: 'ir.actions.act_window', - views: [[1, 'form']], - }]; - const archs = { - 'res.config.settings,1,form': ` -
-
-
-
-
- -
-
- Foo -
- this is foo + QUnit.test( + 'call "call_button/execute" when clicking on a button in dirty settings', + async function (assert) { + assert.expect(7); + + testConfig.serverData.actions[1] = { + id: 1, + name: "Settings view", + res_model: "res.config.settings", + type: "ir.actions.act_window", + views: [[1, "form"]], + }; + + testConfig.serverData.views = Object.assign(testConfig.serverData.views, { + "res.config.settings,1,form": ` + +
+
+
+
+ +
+
+ Foo +
+ this is foo +
+
-
- + `, - 'res.config.settings,false,search': '', - }; - - const actionManager = await createActionManager({ - actions: actions, - archs: archs, - data: this.data, - mockRPC(route, args) { - if (route === '/web/dataset/call_button' && args.method === 'execute') { - assert.step('execute'); - return Promise.resolve(true); - } else if (args.method === 'create') { - assert.step('create'); + "res.config.settings,false,search": "", + }); + + const mockRPC = (route, args) => { + if (route === "/web/dataset/call_button" && args.method === "execute") { + assert.step("execute"); + return true; + } else if (args.method === "create") { + assert.step("create"); } - return this._super.apply(this, arguments); - }, - }); - - await actionManager.doAction(1); - assert.containsNone(actionManager, '.o_field_boolean input:checked', - 'checkbox should not be checked'); - - await testUtils.dom.click(actionManager.$('input[type="checkbox"]')); - assert.containsOnce(actionManager, '.o_field_boolean input:checked', - 'checkbox should be checked'); - - await testUtils.dom.click(actionManager.$('button[name="4"]')); - assert.containsOnce(document.body, '.modal', 'should open a warning dialog'); - - await testUtils.dom.click($('.modal-footer .btn-primary')); - assert.verifySteps([ - 'create', // saveRecord from modal - 'execute', // execute_action - 'create' // saveRecord from FormController._onButtonClicked - ]); - - actionManager.destroy(); - }); + }; + + const webClient = await createWebClient({ testConfig, mockRPC }); + + await doAction(webClient, 1); + assert.containsNone( + webClient, + ".o_field_boolean input:checked", + "checkbox should not be checked" + ); + + await testUtils.dom.click($(webClient.el).find('input[type="checkbox"]')); + assert.containsOnce( + webClient, + ".o_field_boolean input:checked", + "checkbox should be checked" + ); + + await testUtils.dom.click($(webClient.el).find('button[name="4"]')); + assert.containsOnce(document.body, ".modal", "should open a warning dialog"); + + await testUtils.dom.click($(".modal-footer .btn-primary")); + assert.verifySteps([ + "create", // saveRecord from modal + "execute", // execute_action + "create", // saveRecord from FormController._onButtonClicked + ]); + } + ); }); });