Files
odoo_source/addons/web/static/tests/helpers/test_utils.js
T
Mathieu Duckerts-Antoine 12ae5ef8e3 [IMP] web: owlCompatibilityExtraNextTick
Introduced a function that awaits for an additionnal rendering frame
initiated by the Owl compatibility layer processing.

By default a simple "nextTick" will handle the rendering of any widget/
component stuctures having at most 1 switch between the type of
entities (Component > Widget or Widget > Component). However more time
must be spent rendering in case we have additionnal switches. In such
cases this function must be used (1 call for each additionnal switch)
since it will be removed along with the compatiblity layer once the
framework has been entirely converted, and using this helper will make
it easier to wipe it from the code base.
2020-10-12 07:43:43 +00:00

288 lines
13 KiB
JavaScript

odoo.define('web.test_utils', async function (require) {
"use strict";
/**
* Test Utils
*
* In this module, we define various utility functions to help simulate a mock
* environment as close as possible as a real environment. The main function is
* certainly createView, which takes a bunch of parameters and give you back an
* instance of a view, appended in the dom, ready to be tested.
*/
const ajax = require('web.ajax');
const core = require('web.core');
const relationalFields = require('web.relational_fields');
const session = require('web.session');
const testUtilsCreate = require('web.test_utils_create');
const testUtilsControlPanel = require('web.test_utils_control_panel');
const testUtilsDom = require('web.test_utils_dom');
const testUtilsFields = require('web.test_utils_fields');
const testUtilsFile = require('web.test_utils_file');
const testUtilsForm = require('web.test_utils_form');
const testUtilsGraph = require('web.test_utils_graph');
const testUtilsKanban = require('web.test_utils_kanban');
const testUtilsMock = require('web.test_utils_mock');
const testUtilsModal = require('web.test_utils_modal');
const testUtilsPivot = require('web.test_utils_pivot');
const tools = require('web.tools');
function deprecated(fn, type) {
const msg = `Helper 'testUtils.${fn.name}' is deprecated. ` +
`Please use 'testUtils.${type}.${fn.name}' instead.`;
return tools.deprecated(fn, msg);
}
/**
* Helper function, make a promise with a public resolve function. Note that
* this is not standard and should not be used outside of tests...
*
* @returns {Promise + resolve and reject function}
*/
function makeTestPromise() {
let resolve;
let reject;
const promise = new Promise(function (_resolve, _reject) {
resolve = _resolve;
reject = _reject;
});
promise.resolve = function () {
resolve.apply(null, arguments);
return promise;
};
promise.reject = function () {
reject.apply(null, arguments);
return promise;
};
return promise;
}
/**
* Make a promise with public resolve and reject functions (see
* @makeTestPromise). Perform an assert.step when the promise is
* resolved/rejected.
*
* @param {Object} assert instance object with the assertion methods
* @param {function} assert.step
* @param {string} str message to pass to assert.step
* @returns {Promise + resolve and reject function}
*/
function makeTestPromiseWithAssert(assert, str) {
const prom = makeTestPromise();
prom.then(() => assert.step('ok ' + str)).catch(function () { });
prom.catch(() => assert.step('ko ' + str));
return prom;
}
/**
* Create a new promise that can be waited by the caller in order to execute
* code after the next microtask tick and before the next jobqueue tick.
*
* @return {Promise} an already fulfilled promise
*/
async function nextMicrotaskTick() {
return Promise.resolve();
}
/**
* Returns a promise that will be resolved after the tick after the
* nextAnimationFrame
*
* This is usefull to guarantee that OWL has had the time to render
*
* @returns {Promise}
*/
async function nextTick() {
return testUtilsDom.returnAfterNextAnimationFrame();
}
/**
* Awaits for an additionnal rendering frame initiated by the Owl
* compatibility layer processing.
*
* By default a simple "nextTick" will handle the rendering of any widget/
* component stuctures having at most 1 switch between the type of
* entities (Component > Widget or Widget > Component). However more time
* must be spent rendering in case we have additionnal switches. In such
* cases this function must be used (1 call for each additionnal switch)
* since it will be removed along with the compatiblity layer once the
* framework has been entirely converted, and using this helper will make
* it easier to wipe it from the code base.
*
* @returns {Promise}
*/
async function owlCompatibilityExtraNextTick() {
return testUtilsDom.returnAfterNextAnimationFrame();
}
// Loading static files cannot be properly simulated when their real content is
// really needed. This is the case for static XML files so we load them here,
// before starting the qunit test suite.
// (session.js is in charge of loading the static xml bundle and we also have
// to load xml files that are normally lazy loaded by specific widgets).
await Promise.all([
session.is_bound,
ajax.loadXML('/web/static/src/xml/debug.xml', core.qweb),
ajax.loadXML('/web/static/src/xml/dialog.xml', core.qweb),
ajax.loadXML('/web/static/src/xml/translation_dialog.xml', core.qweb),
]);
setTimeout(function () {
// jquery autocomplete refines the search in a setTimeout() parameterized
// with a delay, so we force this delay to 0 s.t. the dropdown is filtered
// directly on the next tick
relationalFields.FieldMany2One.prototype.AUTOCOMPLETE_DELAY = 0;
// this is done with the hope that tests are
// only started all together...
QUnit.start();
}, 0);
return {
mock: {
addMockEnvironment: testUtilsMock.addMockEnvironment,
addMockEnvironmentOwl: testUtilsMock.addMockEnvironmentOwl,
intercept: testUtilsMock.intercept,
patch: testUtilsMock.patch,
patchDate: testUtilsMock.patchDate,
unpatch: testUtilsMock.unpatch,
fieldsViewGet: testUtilsMock.fieldsViewGet,
patchSetTimeout: testUtilsMock.patchSetTimeout,
},
controlPanel: {
// Generic interactions
toggleMenu: testUtilsControlPanel.toggleMenu,
toggleMenuItem: testUtilsControlPanel.toggleMenuItem,
toggleMenuItemOption: testUtilsControlPanel.toggleMenuItemOption,
isItemSelected: testUtilsControlPanel.isItemSelected,
isOptionSelected: testUtilsControlPanel.isOptionSelected,
getMenuItemTexts: testUtilsControlPanel.getMenuItemTexts,
// Button interactions
getButtons: testUtilsControlPanel.getButtons,
// FilterMenu interactions
toggleFilterMenu: testUtilsControlPanel.toggleFilterMenu,
toggleAddCustomFilter: testUtilsControlPanel.toggleAddCustomFilter,
applyFilter: testUtilsControlPanel.applyFilter,
addCondition: testUtilsControlPanel.addCondition,
// GroupByMenu interactions
toggleGroupByMenu: testUtilsControlPanel.toggleGroupByMenu,
toggleAddCustomGroup: testUtilsControlPanel.toggleAddCustomGroup,
selectGroup: testUtilsControlPanel.selectGroup,
applyGroup: testUtilsControlPanel.applyGroup,
// FavoriteMenu interactions
toggleFavoriteMenu: testUtilsControlPanel.toggleFavoriteMenu,
toggleSaveFavorite: testUtilsControlPanel.toggleSaveFavorite,
editFavoriteName: testUtilsControlPanel.editFavoriteName,
saveFavorite: testUtilsControlPanel.saveFavorite,
deleteFavorite: testUtilsControlPanel.deleteFavorite,
// ComparisonMenu interactions
toggleComparisonMenu: testUtilsControlPanel.toggleComparisonMenu,
// SearchBar interactions
getFacetTexts: testUtilsControlPanel.getFacetTexts,
removeFacet: testUtilsControlPanel.removeFacet,
editSearch: testUtilsControlPanel.editSearch,
validateSearch: testUtilsControlPanel.validateSearch,
// Action menus interactions
toggleActionMenu: testUtilsControlPanel.toggleActionMenu,
// Pager interactions
pagerPrevious: testUtilsControlPanel.pagerPrevious,
pagerNext: testUtilsControlPanel.pagerNext,
getPagerValue: testUtilsControlPanel.getPagerValue,
getPagerSize: testUtilsControlPanel.getPagerSize,
setPagerValue: testUtilsControlPanel.setPagerValue,
// View switcher
switchView: testUtilsControlPanel.switchView,
},
dom: {
triggerKeypressEvent: testUtilsDom.triggerKeypressEvent,
triggerMouseEvent: testUtilsDom.triggerMouseEvent,
triggerPositionalMouseEvent: testUtilsDom.triggerPositionalMouseEvent,
dragAndDrop: testUtilsDom.dragAndDrop,
find: testUtilsDom.findItem,
getNode: testUtilsDom.getNode,
openDatepicker: testUtilsDom.openDatepicker,
click: testUtilsDom.click,
clickFirst: testUtilsDom.clickFirst,
clickLast: testUtilsDom.clickLast,
triggerEvents: testUtilsDom.triggerEvents,
triggerEvent: testUtilsDom.triggerEvent,
},
form: {
clickEdit: testUtilsForm.clickEdit,
clickSave: testUtilsForm.clickSave,
clickCreate: testUtilsForm.clickCreate,
clickDiscard: testUtilsForm.clickDiscard,
reload: testUtilsForm.reload,
},
graph: {
reload: testUtilsGraph.reload,
},
kanban: {
reload: testUtilsKanban.reload,
clickCreate: testUtilsKanban.clickCreate,
quickCreate: testUtilsKanban.quickCreate,
toggleGroupSettings: testUtilsKanban.toggleGroupSettings,
toggleRecordDropdown: testUtilsKanban.toggleRecordDropdown,
},
modal: {
clickButton: testUtilsModal.clickButton,
},
pivot: {
clickMeasure: testUtilsPivot.clickMeasure,
toggleMeasuresDropdown: testUtilsPivot.toggleMeasuresDropdown,
reload: testUtilsPivot.reload,
},
fields: {
many2one: {
createAndEdit: testUtilsFields.clickM2OCreateAndEdit,
clickOpenDropdown: testUtilsFields.clickOpenM2ODropdown,
clickHighlightedItem: testUtilsFields.clickM2OHighlightedItem,
clickItem: testUtilsFields.clickM2OItem,
searchAndClickItem: testUtilsFields.searchAndClickM2OItem,
},
editInput: testUtilsFields.editInput,
editSelect: testUtilsFields.editSelect,
editAndTrigger: testUtilsFields.editAndTrigger,
triggerKey: testUtilsFields.triggerKey,
triggerKeydown: testUtilsFields.triggerKeydown,
triggerKeyup: testUtilsFields.triggerKeyup,
},
file: {
createFile: testUtilsFile.createFile,
dragoverFile: testUtilsFile.dragoverFile,
dropFile: testUtilsFile.dropFile,
dropFiles: testUtilsFile.dropFiles,
inputFiles: testUtilsFile.inputFiles,
},
createActionManager: testUtilsCreate.createActionManager,
createComponent: testUtilsCreate.createComponent,
createControlPanel: testUtilsCreate.createControlPanel,
createDebugManager: testUtilsCreate.createDebugManager,
createAsyncView: testUtilsCreate.createView,
createCalendarView: testUtilsCreate.createCalendarView,
createView: testUtilsCreate.createView,
createModel: testUtilsCreate.createModel,
createParent: testUtilsCreate.createParent,
makeTestPromise: makeTestPromise,
makeTestPromiseWithAssert: makeTestPromiseWithAssert,
nextMicrotaskTick: nextMicrotaskTick,
nextTick: nextTick,
owlCompatibilityExtraNextTick,
prepareTarget: testUtilsCreate.prepareTarget,
returnAfterNextAnimationFrame: testUtilsDom.returnAfterNextAnimationFrame,
// backward-compatibility
addMockEnvironment: deprecated(testUtilsMock.addMockEnvironment, 'mock'),
dragAndDrop: deprecated(testUtilsDom.dragAndDrop, 'dom'),
fieldsViewGet: deprecated(testUtilsMock.fieldsViewGet, 'mock'),
intercept: deprecated(testUtilsMock.intercept, 'mock'),
openDatepicker: deprecated(testUtilsDom.openDatepicker, 'dom'),
patch: deprecated(testUtilsMock.patch, 'mock'),
patchDate: deprecated(testUtilsMock.patchDate, 'mock'),
triggerKeypressEvent: deprecated(testUtilsDom.triggerKeypressEvent, 'dom'),
triggerMouseEvent: deprecated(testUtilsDom.triggerMouseEvent, 'dom'),
triggerPositionalMouseEvent: deprecated(testUtilsDom.triggerPositionalMouseEvent, 'dom'),
unpatch: deprecated(testUtilsMock.unpatch, 'mock'),
};
});