[REF] web: Update generic files
General cleaning of some helpers and generic files. The idea is that they are less likely to be entirely re-written in the near future so this is a way to keep the code clean, readable and compliant to the current standards. Changes include: - Use of async/await when possible - Use of const/let instead of var - Removal of underscore.js - Use of native JS instead of jQuery when possible The logic of the affected code remains the same.
This commit is contained in:
@@ -26,7 +26,7 @@ var Widget = require('web.Widget');
|
||||
* This is async due to mail_manager/mail_service that needs to be ready.
|
||||
*
|
||||
* @param {Object} params
|
||||
* @return {Promise} resolved with the discuss widget
|
||||
* @returns {Promise<Discuss>}
|
||||
*/
|
||||
async function createDiscuss(params) {
|
||||
var Parent = Widget.extend({
|
||||
|
||||
@@ -20,9 +20,9 @@ odoo.define('web.ActionMixin', function (require) {
|
||||
* @module web.ActionMixin
|
||||
*/
|
||||
|
||||
var core = require('web.core');
|
||||
const core = require('web.core');
|
||||
|
||||
var ActionMixin = {
|
||||
const ActionMixin = {
|
||||
template: 'Action',
|
||||
|
||||
/**
|
||||
@@ -79,7 +79,7 @@ odoo.define('web.ActionMixin', function (require) {
|
||||
renderElement: function () {
|
||||
this._super.apply(this, arguments);
|
||||
if (this.contentTemplate) {
|
||||
var content = core.qweb.render(this.contentTemplate, { widget: this });
|
||||
const content = core.qweb.render(this.contentTemplate, { widget: this });
|
||||
this.$('.o_content').append(content);
|
||||
}
|
||||
},
|
||||
@@ -223,7 +223,7 @@ odoo.define('web.ActionMixin', function (require) {
|
||||
*/
|
||||
_onGetControllerQueryParams: function (ev) {
|
||||
ev.stopPropagation();
|
||||
var state = this.getOwnedQueryParams();
|
||||
const state = this.getOwnedQueryParams();
|
||||
ev.data.callback(state || {});
|
||||
},
|
||||
};
|
||||
|
||||
@@ -14,18 +14,18 @@ odoo.define('web.custom_hooks', function () {
|
||||
*/
|
||||
function useFocusOnUpdate() {
|
||||
const component = Component.current;
|
||||
component.__willFocus = null;
|
||||
let willFocus = null;
|
||||
|
||||
function _focusSelector() {
|
||||
if (component.__willFocus) {
|
||||
const target = component.el.querySelector(component.__willFocus);
|
||||
if (willFocus) {
|
||||
const target = component.el.querySelector(willFocus);
|
||||
if (target) {
|
||||
target.focus();
|
||||
if (['INPUT', 'TEXTAREA'].includes(target.tagName)) {
|
||||
target.selectionStart = target.selectionEnd = target.value.length;
|
||||
}
|
||||
}
|
||||
component.__willFocus = null;
|
||||
willFocus = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ odoo.define('web.custom_hooks', function () {
|
||||
onPatched(_focusSelector);
|
||||
|
||||
return function focusOnUpdate(selector = '[autofocus]') {
|
||||
component.__willFocus = selector;
|
||||
willFocus = selector;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
odoo.define('web.web_client', function (require) {
|
||||
"use strict";
|
||||
|
||||
const env = require('web.env');
|
||||
const session = require("web.session");
|
||||
|
||||
@@ -11,7 +11,7 @@ odoo.define('web.qunit_asserts', function (require) {
|
||||
* matches exactly one element.
|
||||
*/
|
||||
|
||||
var Widget = require('web.Widget');
|
||||
const Widget = require('web.Widget');
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Private functions
|
||||
@@ -56,8 +56,8 @@ odoo.define('web.qunit_asserts', function (require) {
|
||||
*/
|
||||
function _checkVisible(w, shouldBeVisible, msg) {
|
||||
if (w instanceof jQuery && w.length !== 1) {
|
||||
const assertion = shouldHaveClass ? 'hasClass' : 'doesNotHaveClass';
|
||||
QUnit.assert.ok(false, `Assertion '${assertion} ${className}' targets ${w.length} elements instead of 1`);
|
||||
const assertion = shouldBeVisible ? 'isVisible' : 'isNotVisible';
|
||||
QUnit.assert.ok(false, `Assertion '${assertion}' targets ${w.length} elements instead of 1`);
|
||||
}
|
||||
|
||||
const el = w instanceof Widget || w instanceof owl.Component ? w.el :
|
||||
@@ -184,12 +184,12 @@ odoo.define('web.qunit_asserts', function (require) {
|
||||
* @param {string} [msg]
|
||||
*/
|
||||
function hasAttrValue(w, attr, value, msg) {
|
||||
var $el = w instanceof Widget ? w.$el :
|
||||
const $el = w instanceof Widget ? w.$el :
|
||||
w instanceof HTMLElement ? $(w) :
|
||||
w; // jquery element
|
||||
|
||||
if ($el.length !== 1) {
|
||||
var descr = `hasAttrValue (${attr}: ${value})`;
|
||||
const descr = `hasAttrValue (${attr}: ${value})`;
|
||||
QUnit.assert.ok(false,
|
||||
`Assertion '${descr}' targets ${$el.length} elements instead of 1`
|
||||
);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
odoo.define('web.test_utils', function (require) {
|
||||
odoo.define('web.test_utils', async function (require) {
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
@@ -10,26 +10,25 @@ odoo.define('web.test_utils', function (require) {
|
||||
* instance of a view, appended in the dom, ready to be tested.
|
||||
*/
|
||||
|
||||
var ajax = require('web.ajax');
|
||||
var concurrency = require('web.concurrency');
|
||||
var core = require('web.core');
|
||||
var relationalFields = require('web.relational_fields');
|
||||
var session = require('web.session');
|
||||
var testUtilsCreate = require('web.test_utils_create');
|
||||
var testUtilsDom = require('web.test_utils_dom');
|
||||
var testUtilsFields = require('web.test_utils_fields');
|
||||
var testUtilsFile = require('web.test_utils_file');
|
||||
var testUtilsForm = require('web.test_utils_form');
|
||||
var testUtilsGraph = require('web.test_utils_graph');
|
||||
var testUtilsKanban = require('web.test_utils_kanban');
|
||||
var testUtilsMock = require('web.test_utils_mock');
|
||||
var testUtilsModal = require('web.test_utils_modal');
|
||||
var testUtilsPivot = require('web.test_utils_pivot');
|
||||
var tools = require('web.tools');
|
||||
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 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) {
|
||||
var msg = `Helper 'testUtils.${fn.name}' is deprecated. ` +
|
||||
const msg = `Helper 'testUtils.${fn.name}' is deprecated. ` +
|
||||
`Please use 'testUtils.${type}.${fn.name}' instead.`;
|
||||
return tools.deprecated(fn, msg);
|
||||
}
|
||||
@@ -41,9 +40,9 @@ odoo.define('web.test_utils', function (require) {
|
||||
* @returns {Promise + resolve and reject function}
|
||||
*/
|
||||
function makeTestPromise() {
|
||||
var resolve;
|
||||
var reject;
|
||||
var promise = new Promise(function (_resolve, _reject) {
|
||||
let resolve;
|
||||
let reject;
|
||||
const promise = new Promise(function (_resolve, _reject) {
|
||||
resolve = _resolve;
|
||||
reject = _reject;
|
||||
});
|
||||
@@ -69,7 +68,7 @@ odoo.define('web.test_utils', function (require) {
|
||||
* @returns {Promise + resolve and reject function}
|
||||
*/
|
||||
function makeTestPromiseWithAssert(assert, str) {
|
||||
var prom = makeTestPromise();
|
||||
const prom = makeTestPromise();
|
||||
prom.then(() => assert.step('ok ' + str)).catch(function () { });
|
||||
prom.catch(() => assert.step('ko ' + str));
|
||||
return prom;
|
||||
@@ -102,118 +101,117 @@ odoo.define('web.test_utils', function (require) {
|
||||
// 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).
|
||||
return Promise.all([
|
||||
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),
|
||||
]).then(function () {
|
||||
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;
|
||||
]);
|
||||
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,
|
||||
getMockedOwlEnv: testUtilsMock.getMockedOwlEnv,
|
||||
intercept: testUtilsMock.intercept,
|
||||
patch: testUtilsMock.patch,
|
||||
patchDate: testUtilsMock.patchDate,
|
||||
unpatch: testUtilsMock.unpatch,
|
||||
fieldsViewGet: testUtilsMock.fieldsViewGet,
|
||||
patchSetTimeout: testUtilsMock.patchSetTimeout,
|
||||
},
|
||||
dom: {
|
||||
triggerKeypressEvent: testUtilsDom.triggerKeypressEvent,
|
||||
triggerMouseEvent: testUtilsDom.triggerMouseEvent,
|
||||
triggerPositionalMouseEvent: testUtilsDom.triggerPositionalMouseEvent,
|
||||
dragAndDrop: testUtilsDom.dragAndDrop,
|
||||
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: {
|
||||
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,
|
||||
// this is done with the hope that tests are
|
||||
// only started all together...
|
||||
QUnit.start();
|
||||
}, 0);
|
||||
return {
|
||||
mock: {
|
||||
addMockEnvironment: testUtilsMock.addMockEnvironment,
|
||||
getMockedOwlEnv: testUtilsMock.getMockedOwlEnv,
|
||||
intercept: testUtilsMock.intercept,
|
||||
patch: testUtilsMock.patch,
|
||||
patchDate: testUtilsMock.patchDate,
|
||||
unpatch: testUtilsMock.unpatch,
|
||||
fieldsViewGet: testUtilsMock.fieldsViewGet,
|
||||
patchSetTimeout: testUtilsMock.patchSetTimeout,
|
||||
},
|
||||
dom: {
|
||||
triggerKeypressEvent: testUtilsDom.triggerKeypressEvent,
|
||||
triggerMouseEvent: testUtilsDom.triggerMouseEvent,
|
||||
triggerPositionalMouseEvent: testUtilsDom.triggerPositionalMouseEvent,
|
||||
dragAndDrop: testUtilsDom.dragAndDrop,
|
||||
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: {
|
||||
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,
|
||||
},
|
||||
|
||||
createActionManager: testUtilsCreate.createActionManager,
|
||||
createDebugManager: testUtilsCreate.createDebugManager,
|
||||
createAsyncView: testUtilsCreate.createView,
|
||||
createCalendarView: testUtilsCreate.createCalendarView,
|
||||
createControlPanel: testUtilsCreate.createControlPanel,
|
||||
createView: testUtilsCreate.createView,
|
||||
createModel: testUtilsCreate.createModel,
|
||||
createParent: testUtilsCreate.createParent,
|
||||
makeTestPromise: makeTestPromise,
|
||||
makeTestPromiseWithAssert: makeTestPromiseWithAssert,
|
||||
nextMicrotaskTick: nextMicrotaskTick,
|
||||
nextTick: nextTick,
|
||||
prepareTarget: testUtilsCreate.prepareTarget,
|
||||
returnAfterNextAnimationFrame: testUtilsDom.returnAfterNextAnimationFrame,
|
||||
createActionManager: testUtilsCreate.createActionManager,
|
||||
createDebugManager: testUtilsCreate.createDebugManager,
|
||||
createAsyncView: testUtilsCreate.createView,
|
||||
createCalendarView: testUtilsCreate.createCalendarView,
|
||||
createControlPanel: testUtilsCreate.createControlPanel,
|
||||
createView: testUtilsCreate.createView,
|
||||
createModel: testUtilsCreate.createModel,
|
||||
createParent: testUtilsCreate.createParent,
|
||||
makeTestPromise: makeTestPromise,
|
||||
makeTestPromiseWithAssert: makeTestPromiseWithAssert,
|
||||
nextMicrotaskTick: nextMicrotaskTick,
|
||||
nextTick: nextTick,
|
||||
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'),
|
||||
};
|
||||
});
|
||||
// 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'),
|
||||
};
|
||||
});
|
||||
|
||||
@@ -10,70 +10,68 @@ odoo.define('web.test_utils_create', function (require) {
|
||||
* testUtils file.
|
||||
*/
|
||||
|
||||
var ActionManager = require('web.ActionManager');
|
||||
var config = require('web.config');
|
||||
var ControlPanelView = require('web.ControlPanelView');
|
||||
var concurrency = require('web.concurrency');
|
||||
var DebugManager = require('web.DebugManager.Backend');
|
||||
var dom = require('web.dom');
|
||||
var testUtilsMock = require('web.test_utils_mock');
|
||||
var Widget = require('web.Widget');
|
||||
const ActionManager = require('web.ActionManager');
|
||||
const concurrency = require('web.concurrency');
|
||||
const config = require('web.config');
|
||||
const ControlPanelView = require('web.ControlPanelView');
|
||||
const DebugManager = require('web.DebugManager.Backend');
|
||||
const dom = require('web.dom');
|
||||
const testUtilsMock = require('web.test_utils_mock');
|
||||
const Widget = require('web.Widget');
|
||||
|
||||
/**
|
||||
* create and return an instance of ActionManager with all rpcs going through a
|
||||
* Create and return an instance of ActionManager with all rpcs going through a
|
||||
* mock method using the data, actions and archs objects as sources.
|
||||
*
|
||||
* @param {Object} [params]
|
||||
* @param {Object} [params={}]
|
||||
* @param {Object} [params.actions] the actions given to the mock server
|
||||
* @param {Object} [params.archs] this archs given to the mock server
|
||||
* @param {Object} [params.data] the business data given to the mock server
|
||||
* @param {function} [params.mockRPC]
|
||||
* @returns {ActionManager}
|
||||
* @returns {Promise<ActionManager>}
|
||||
*/
|
||||
async function createActionManager(params) {
|
||||
params = params || {};
|
||||
async function createActionManager(params = {}) {
|
||||
const target = prepareTarget(params.debug);
|
||||
|
||||
var widget = new Widget();
|
||||
const widget = new Widget();
|
||||
// when 'document' addon is installed, the sidebar does a 'search_read' on
|
||||
// model 'ir_attachment' each time a record is open, so we monkey-patch
|
||||
// 'mockRPC' to mute those RPCs, so that the tests can be written uniformly,
|
||||
// whether or not 'document' is installed
|
||||
var mockRPC = params.mockRPC;
|
||||
_.extend(params, {
|
||||
mockRPC: function (route, args) {
|
||||
const mockRPC = params.mockRPC;
|
||||
Object.assign(params, {
|
||||
async mockRPC(route, args) {
|
||||
if (args.model === 'ir.attachment') {
|
||||
return Promise.resolve([]);
|
||||
return [];
|
||||
}
|
||||
if (mockRPC) {
|
||||
return mockRPC.apply(this, arguments);
|
||||
}
|
||||
return this._super.apply(this, arguments);
|
||||
return this._super(...arguments);
|
||||
},
|
||||
});
|
||||
testUtilsMock.addMockEnvironment(widget, _.defaults(params, { debounce: false }));
|
||||
testUtilsMock.addMockEnvironment(widget, Object.assign({ debounce: false }, params));
|
||||
await widget.prependTo(target);
|
||||
widget.$el.addClass('o_web_client');
|
||||
widget.el.classList.add('o_web_client');
|
||||
if (config.device.isMobile) {
|
||||
widget.$el.addClass('o_touch_device');
|
||||
widget.el.classList.add('o_touch_device');
|
||||
}
|
||||
|
||||
var userContext = params.context && params.context.user_context || {};
|
||||
var actionManager = new ActionManager(widget, userContext);
|
||||
const userContext = params.context && params.context.user_context || {};
|
||||
const actionManager = new ActionManager(widget, userContext);
|
||||
|
||||
var originalDestroy = ActionManager.prototype.destroy;
|
||||
const originalDestroy = ActionManager.prototype.destroy;
|
||||
actionManager.destroy = function () {
|
||||
actionManager.destroy = originalDestroy;
|
||||
widget.destroy();
|
||||
};
|
||||
var fragment = document.createDocumentFragment();
|
||||
return actionManager.appendTo(fragment).then(function () {
|
||||
dom.append(widget.$el, fragment, {
|
||||
callbacks: [{ widget: actionManager }],
|
||||
in_DOM: true,
|
||||
});
|
||||
return actionManager;
|
||||
const fragment = document.createDocumentFragment();
|
||||
await actionManager.appendTo(fragment);
|
||||
dom.append(widget.el, fragment, {
|
||||
callbacks: [{ widget: actionManager }],
|
||||
in_DOM: true,
|
||||
});
|
||||
return actionManager;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -85,21 +83,22 @@ odoo.define('web.test_utils_create', function (require) {
|
||||
* makes the calendar scroll to 6:00 in a setTimeout(0), which might have an
|
||||
* impact according to where we want to trigger positional clicks.
|
||||
*
|
||||
* @param {Object} params see @createView
|
||||
* @param {Object} params @see createView
|
||||
* @param {Object} [options]
|
||||
* @param {boolean} [options.positionalClicks=false]
|
||||
* @returns {Promise<CalendarController>}
|
||||
*/
|
||||
async function createCalendarView(params, options) {
|
||||
var calendar = await createView(params);
|
||||
const calendar = await createView(params);
|
||||
if (!options || !options.positionalClicks) {
|
||||
return calendar;
|
||||
}
|
||||
var $view = $('#qunit-fixture').contents();
|
||||
$view.prependTo('body');
|
||||
var destroy = calendar.destroy;
|
||||
calendar.destroy = function () {
|
||||
$view.remove();
|
||||
const viewElements = [...document.getElementById('qunit-fixture').children];
|
||||
viewElements.forEach(el => document.body.prepend(el));
|
||||
|
||||
const destroy = calendar.destroy;
|
||||
calendar.destroy = () => {
|
||||
viewElements.forEach(el => el.remove());
|
||||
destroy();
|
||||
};
|
||||
await concurrency.delay(0);
|
||||
@@ -107,7 +106,7 @@ odoo.define('web.test_utils_create', function (require) {
|
||||
}
|
||||
|
||||
/**
|
||||
* create a controlPanel from various parameters.
|
||||
* Create a controlPanel from various parameters.
|
||||
*
|
||||
* It returns an instance of ControlPanelController
|
||||
*
|
||||
@@ -138,17 +137,17 @@ odoo.define('web.test_utils_create', function (require) {
|
||||
*
|
||||
* @returns {Promise<ControlPanel>} resolves with an instance of the ControlPanelController
|
||||
*/
|
||||
function createControlPanel(params) {
|
||||
async function createControlPanel(params) {
|
||||
params = params || {};
|
||||
const target = prepareTarget(params.debug);
|
||||
// reproduce the DOM environment of a view control panel
|
||||
var $webClient = $('<div>').addClass('o_web_client').prependTo(target);
|
||||
var $actionManager = $('<div>').addClass('o_action_manager').appendTo($webClient);
|
||||
var $action = $('<div>').addClass('o_action').appendTo($actionManager);
|
||||
const $webClient = $('<div>').addClass('o_web_client').prependTo(target);
|
||||
const $actionManager = $('<div>').addClass('o_action_manager').appendTo($webClient);
|
||||
const $action = $('<div>').addClass('o_action').appendTo($actionManager);
|
||||
|
||||
var widget = new Widget();
|
||||
const widget = new Widget();
|
||||
// add mock environment: mock server, session, fieldviewget, ...
|
||||
var mockServer = testUtilsMock.addMockEnvironment(widget, params);
|
||||
const mockServer = testUtilsMock.addMockEnvironment(widget, params);
|
||||
if (!params.viewInfo) {
|
||||
try {
|
||||
params.viewInfo = testUtilsMock.fieldsViewGet(mockServer, params);
|
||||
@@ -159,37 +158,35 @@ odoo.define('web.test_utils_create', function (require) {
|
||||
}
|
||||
}
|
||||
|
||||
var viewOptions = _.defaults({}, params.viewOptions, {
|
||||
const viewOptions = Object.assign({
|
||||
context: params.context,
|
||||
modelName: params.model,
|
||||
searchMenuTypes: params.searchMenuTypes,
|
||||
viewInfo: params.viewInfo,
|
||||
});
|
||||
var controlPanelView = new ControlPanelView(viewOptions);
|
||||
return controlPanelView.getController(widget).then(function (controlPanel) {
|
||||
// override the controlPanel's 'destroy' so that it calls 'destroy' on
|
||||
// the widget instead, as the widget is the parent of the controlPanel
|
||||
// and the mockServer.
|
||||
controlPanel.__destroy = controlPanel.destroy;
|
||||
controlPanel.destroy = function () {
|
||||
// remove the override to properly destroy the controlPanel and its
|
||||
// children when it will be called the second time (by its parent)
|
||||
delete controlPanel.destroy;
|
||||
widget.destroy();
|
||||
$webClient.remove();
|
||||
};
|
||||
}, params.viewOptions);
|
||||
const controlPanelView = new ControlPanelView(viewOptions);
|
||||
const controlPanel = await controlPanelView.getController(widget);
|
||||
// override the controlPanel's 'destroy' so that it calls 'destroy' on
|
||||
// the widget instead, as the widget is the parent of the controlPanel
|
||||
// and the mockServer.
|
||||
controlPanel.__destroy = controlPanel.destroy;
|
||||
controlPanel.destroy = function () {
|
||||
// remove the override to properly destroy the controlPanel and its
|
||||
// children when it will be called the second time (by its parent)
|
||||
delete controlPanel.destroy;
|
||||
widget.destroy();
|
||||
$webClient.remove();
|
||||
};
|
||||
|
||||
// render the controlPanel in a fragment as it must be able to render
|
||||
// correctly without being in the DOM
|
||||
var fragment = document.createDocumentFragment();
|
||||
return controlPanel.appendTo(fragment).then(function () {
|
||||
dom.prepend($action, fragment, {
|
||||
callbacks: [{ widget: controlPanel }],
|
||||
in_DOM: true,
|
||||
});
|
||||
return controlPanel;
|
||||
});
|
||||
// render the controlPanel in a fragment as it must be able to render
|
||||
// correctly without being in the DOM
|
||||
const fragment = document.createDocumentFragment();
|
||||
await controlPanel.appendTo(fragment);
|
||||
dom.prepend($action, fragment, {
|
||||
callbacks: [{ widget: controlPanel }],
|
||||
in_DOM: true,
|
||||
});
|
||||
return controlPanel;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -197,39 +194,39 @@ odoo.define('web.test_utils_create', function (require) {
|
||||
* mock method, assuming that the user has access rights, and is an admin.
|
||||
*
|
||||
* @param {Object} [params={}]
|
||||
* @returns {DebugManager}
|
||||
*/
|
||||
var createDebugManager = function (params) {
|
||||
params = params || {};
|
||||
var mockRPC = params.mockRPC;
|
||||
_.extend(params, {
|
||||
mockRPC: function (route, args) {
|
||||
function createDebugManager(params = {}) {
|
||||
const mockRPC = params.mockRPC;
|
||||
Object.assign(params, {
|
||||
async mockRPC(route, args) {
|
||||
if (args.method === 'check_access_rights') {
|
||||
return Promise.resolve(true);
|
||||
return true;
|
||||
}
|
||||
if (args.method === 'xmlid_to_res_id') {
|
||||
return Promise.resolve(true);
|
||||
return true;
|
||||
}
|
||||
if (mockRPC) {
|
||||
return mockRPC.apply(this, arguments);
|
||||
}
|
||||
return this._super.apply(this, arguments);
|
||||
return this._super(...arguments);
|
||||
},
|
||||
session: {
|
||||
user_has_group: function (group) {
|
||||
async user_has_group(group) {
|
||||
if (group === 'base.group_no_one') {
|
||||
return Promise.resolve(true);
|
||||
return true;
|
||||
}
|
||||
return this._super.apply(this, arguments);
|
||||
return this._super(...arguments);
|
||||
},
|
||||
},
|
||||
});
|
||||
var debugManager = new DebugManager();
|
||||
const debugManager = new DebugManager();
|
||||
testUtilsMock.addMockEnvironment(debugManager, params);
|
||||
return debugManager;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* create a model from given parameters.
|
||||
* Create a model from given parameters.
|
||||
*
|
||||
* @param {Object} params This object will be given to addMockEnvironment, so
|
||||
* any parameters from that method applies
|
||||
@@ -237,9 +234,9 @@ odoo.define('web.test_utils_create', function (require) {
|
||||
* @returns {Model}
|
||||
*/
|
||||
function createModel(params) {
|
||||
var widget = new Widget();
|
||||
const widget = new Widget();
|
||||
|
||||
var model = new params.Model(widget);
|
||||
const model = new params.Model(widget);
|
||||
|
||||
testUtilsMock.addMockEnvironment(widget, params);
|
||||
|
||||
@@ -256,20 +253,20 @@ odoo.define('web.test_utils_create', function (require) {
|
||||
}
|
||||
|
||||
/**
|
||||
* create a widget parent from given parameters.
|
||||
* Create a widget parent from given parameters.
|
||||
*
|
||||
* @param {Object} params This object will be given to addMockEnvironment, so
|
||||
* any parameters from that method applies
|
||||
* @returns {Widget}
|
||||
*/
|
||||
function createParent(params) {
|
||||
var widget = new Widget();
|
||||
const widget = new Widget();
|
||||
testUtilsMock.addMockEnvironment(widget, params);
|
||||
return widget;
|
||||
}
|
||||
|
||||
/**
|
||||
* create a view from various parameters. Here, a view means a javascript
|
||||
* Create a view from various parameters. Here, a view means a javascript
|
||||
* instance of an AbstractView class, such as a form view, a list view or a
|
||||
* kanban view.
|
||||
*
|
||||
@@ -291,37 +288,42 @@ odoo.define('web.test_utils_create', function (require) {
|
||||
* Note that this is particularly useful if you want to intercept events going
|
||||
* up in the init process of the view, because there are no other way to do it
|
||||
* after this method returns
|
||||
* @param {Boolean} [params.doNotDisableAHref=false] will not preventDefault on the A elements of the view if true.
|
||||
* @param {Boolean} [params.doNotDisableAHref=false] will not preventDefault on the A elements of the view if true.
|
||||
* Default is false.
|
||||
* @returns {Promise<AbstractController>} resolves with the instance of the view
|
||||
* @returns {Promise<AbstractController>} the instance of the view
|
||||
*/
|
||||
async function createView(params) {
|
||||
const target = prepareTarget(params.debug);
|
||||
var widget = new Widget();
|
||||
const widget = new Widget();
|
||||
// reproduce the DOM environment of views
|
||||
var $webClient = $('<div>').addClass('o_web_client').prependTo(target);
|
||||
var $actionManager = $('<div>').addClass('o_action_manager').appendTo($webClient);
|
||||
|
||||
const webClient = Object.assign(document.createElement('div'), {
|
||||
className: 'o_web_client',
|
||||
});
|
||||
const actionManager = Object.assign(document.createElement('div'), {
|
||||
className: 'o_action_manager',
|
||||
});
|
||||
target.prepend(webClient);
|
||||
webClient.append(actionManager);
|
||||
|
||||
// add mock environment: mock server, session, fieldviewget, ...
|
||||
var mockServer = testUtilsMock.addMockEnvironment(widget, params);
|
||||
var viewInfo = testUtilsMock.fieldsViewGet(mockServer, params);
|
||||
const mockServer = testUtilsMock.addMockEnvironment(widget, params);
|
||||
const viewInfo = testUtilsMock.fieldsViewGet(mockServer, params);
|
||||
|
||||
// create the view
|
||||
var View = params.View;
|
||||
var viewOptions = _.defaults({}, params.viewOptions, {
|
||||
const View = params.View;
|
||||
const viewOptions = Object.assign({
|
||||
modelName: params.model || 'foo',
|
||||
ids: 'res_id' in params ? [params.res_id] : undefined,
|
||||
currentId: 'res_id' in params ? params.res_id : undefined,
|
||||
domain: params.domain || [],
|
||||
context: params.context || {},
|
||||
hasSidebar: false,
|
||||
});
|
||||
}, params.viewOptions);
|
||||
// patch the View to handle the groupBy given in params, as we can't give it
|
||||
// in init (unlike the domain and context which can be set in the action)
|
||||
testUtilsMock.patch(View, {
|
||||
_updateMVCParams: function () {
|
||||
this._super.apply(this, arguments);
|
||||
_updateMVCParams() {
|
||||
this._super(...arguments);
|
||||
this.loadParams.groupedBy = params.groupBy || viewOptions.groupBy || [];
|
||||
testUtilsMock.unpatch(View);
|
||||
},
|
||||
@@ -330,7 +332,7 @@ odoo.define('web.test_utils_create', function (require) {
|
||||
viewOptions.hasSelectors = params.hasSelectors;
|
||||
}
|
||||
|
||||
var view;
|
||||
let view;
|
||||
if (viewInfo.type === 'controlpanel' || viewInfo.type === 'search') {
|
||||
// TODO: probably needs to create an helper just for that
|
||||
view = new params.View({
|
||||
@@ -347,42 +349,40 @@ odoo.define('web.test_utils_create', function (require) {
|
||||
}
|
||||
|
||||
if (params.interceptsPropagate) {
|
||||
_.each(params.interceptsPropagate, function (cb, name) {
|
||||
testUtilsMock.intercept(widget, name, cb, true);
|
||||
});
|
||||
for (const name in params.interceptsPropagate) {
|
||||
testUtilsMock.intercept(widget, name, params.interceptsPropagate[name], true);
|
||||
}
|
||||
}
|
||||
|
||||
return view.getController(widget).then(function (view) {
|
||||
// override the view's 'destroy' so that it calls 'destroy' on the widget
|
||||
// instead, as the widget is the parent of the view and the mockServer.
|
||||
view.__destroy = view.destroy;
|
||||
view.destroy = function () {
|
||||
// remove the override to properly destroy the view and its children
|
||||
// when it will be called the second time (by its parent)
|
||||
delete view.destroy;
|
||||
widget.destroy();
|
||||
$webClient.remove();
|
||||
};
|
||||
const viewController = await view.getController(widget);
|
||||
// override the view's 'destroy' so that it calls 'destroy' on the widget
|
||||
// instead, as the widget is the parent of the view and the mockServer.
|
||||
viewController.__destroy = viewController.destroy;
|
||||
viewController.destroy = function () {
|
||||
// remove the override to properly destroy the viewController and its children
|
||||
// when it will be called the second time (by its parent)
|
||||
delete viewController.destroy;
|
||||
widget.destroy();
|
||||
webClient.remove();
|
||||
};
|
||||
|
||||
// render the view in a fragment as they must be able to render correctly
|
||||
// without being in the DOM
|
||||
var fragment = document.createDocumentFragment();
|
||||
return view.appendTo(fragment).then(() => {
|
||||
dom.prepend($actionManager, fragment, {
|
||||
callbacks: [{ widget: view }],
|
||||
in_DOM: true,
|
||||
});
|
||||
|
||||
if (!params.doNotDisableAHref) {
|
||||
[...view.el.getElementsByTagName('A')].forEach((elem) => {
|
||||
elem.addEventListener('click', (ev) => {
|
||||
ev.preventDefault();
|
||||
});
|
||||
});
|
||||
}
|
||||
return view;
|
||||
});
|
||||
// render the viewController in a fragment as they must be able to render correctly
|
||||
// without being in the DOM
|
||||
const fragment = document.createDocumentFragment();
|
||||
await viewController.appendTo(fragment);
|
||||
dom.prepend(actionManager, fragment, {
|
||||
callbacks: [{ widget: viewController }],
|
||||
in_DOM: true,
|
||||
});
|
||||
|
||||
if (!params.doNotDisableAHref) {
|
||||
[...viewController.el.getElementsByTagName('A')].forEach(elem => {
|
||||
elem.addEventListener('click', ev => {
|
||||
ev.preventDefault();
|
||||
});
|
||||
});
|
||||
}
|
||||
return viewController;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -395,17 +395,17 @@ odoo.define('web.test_utils_create', function (require) {
|
||||
*/
|
||||
function prepareTarget(debug = false) {
|
||||
document.body.classList.toggle('debug', debug);
|
||||
return debug ? document.body : document.querySelector("#qunit-fixture");
|
||||
return debug ? document.body : document.getElementById('qunit-fixture');
|
||||
}
|
||||
|
||||
return {
|
||||
createActionManager: createActionManager,
|
||||
createCalendarView: createCalendarView,
|
||||
createControlPanel: createControlPanel,
|
||||
createDebugManager: createDebugManager,
|
||||
createModel: createModel,
|
||||
createParent: createParent,
|
||||
createView: createView,
|
||||
prepareTarget: prepareTarget,
|
||||
createActionManager,
|
||||
createCalendarView,
|
||||
createControlPanel,
|
||||
createDebugManager,
|
||||
createModel,
|
||||
createParent,
|
||||
createView,
|
||||
prepareTarget,
|
||||
};
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
odoo.define('web.test_utils_dom', function (require) {
|
||||
"use strict";
|
||||
|
||||
var concurrency = require('web.concurrency');
|
||||
const concurrency = require('web.concurrency');
|
||||
|
||||
/**
|
||||
* DOM Test Utils
|
||||
@@ -13,40 +13,29 @@ odoo.define('web.test_utils_dom', function (require) {
|
||||
*/
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Private
|
||||
// Private functions
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
function keyboardEventBubble(args) {
|
||||
return Object.assign({}, args, { bubbles: true, keyCode: args.which });
|
||||
}
|
||||
|
||||
function mouseEventMapping(args) {
|
||||
return Object.assign({}, args, {
|
||||
view: window,
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
clientX: args ? args.pageX : undefined,
|
||||
clientY: args ? args.pageY : undefined,
|
||||
});
|
||||
}
|
||||
|
||||
function mouseEventNoBubble(args) {
|
||||
return Object.assign({}, args, {
|
||||
view: window,
|
||||
bubbles: false,
|
||||
cancelable: false,
|
||||
clientX: args ? args.pageX : undefined,
|
||||
clientY: args ? args.pageY : undefined,
|
||||
});
|
||||
}
|
||||
|
||||
function onlyBubble(args) {
|
||||
return Object.assign({}, args, {
|
||||
bubbles: true
|
||||
});
|
||||
}
|
||||
|
||||
const eventTypes = {
|
||||
// TriggerEvent helpers
|
||||
const keyboardEventBubble = args => Object.assign({}, args, { bubbles: true, keyCode: args.which });
|
||||
const mouseEventMapping = args => Object.assign({}, args, {
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
clientX: args ? args.pageX : undefined,
|
||||
clientY: args ? args.pageY : undefined,
|
||||
view: window,
|
||||
});
|
||||
const mouseEventNoBubble = args => Object.assign({}, args, {
|
||||
bubbles: false,
|
||||
cancelable: false,
|
||||
clientX: args ? args.pageX : undefined,
|
||||
clientY: args ? args.pageY : undefined,
|
||||
view: window,
|
||||
});
|
||||
const noBubble = args => Object.assign({}, args, { bubbles: false });
|
||||
const onlyBubble = args => Object.assign({}, args, { bubbles: true });
|
||||
// TriggerEvent constructor/args processor mapping
|
||||
const EVENT_TYPES = {
|
||||
auxclick: { constructor: MouseEvent, processParameters: mouseEventMapping },
|
||||
click: { constructor: MouseEvent, processParameters: mouseEventMapping },
|
||||
contextmenu: { constructor: MouseEvent, processParameters: mouseEventMapping },
|
||||
@@ -60,17 +49,9 @@ odoo.define('web.test_utils_dom', function (require) {
|
||||
mouseover: { constructor: MouseEvent, processParameters: mouseEventMapping },
|
||||
mouseout: { constructor: MouseEvent, processParameters: mouseEventMapping },
|
||||
|
||||
focus: {
|
||||
constructor: FocusEvent, processParameters: (args) => {
|
||||
return Object.assign({}, args, { bubbles: false });
|
||||
}
|
||||
},
|
||||
focus: { constructor: FocusEvent, processParameters: noBubble },
|
||||
focusin: { constructor: FocusEvent, processParameters: onlyBubble },
|
||||
blur: {
|
||||
constructor: FocusEvent, processParameters: (args) => {
|
||||
return Object.assign({}, args, { bubbles: false });
|
||||
}
|
||||
},
|
||||
blur: { constructor: FocusEvent, processParameters: noBubble },
|
||||
|
||||
cut: { constructor: ClipboardEvent, processParameters: onlyBubble },
|
||||
copy: { constructor: ClipboardEvent, processParameters: onlyBubble },
|
||||
@@ -95,7 +76,10 @@ odoo.define('web.test_utils_dom', function (require) {
|
||||
* @param {Object} node
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function isEventTarget(node) {
|
||||
function _isEventTarget(node) {
|
||||
if (!node) {
|
||||
throw new Error(`Provided node is ${node}.`);
|
||||
}
|
||||
if (node instanceof window.top.EventTarget) {
|
||||
return true;
|
||||
}
|
||||
@@ -105,7 +89,7 @@ odoo.define('web.test_utils_dom', function (require) {
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Public
|
||||
// Public functions
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
@@ -126,7 +110,7 @@ odoo.define('web.test_utils_dom', function (require) {
|
||||
if (typeof el === 'string') {
|
||||
el = $(el);
|
||||
}
|
||||
if (isEventTarget(el)) {
|
||||
if (_isEventTarget(el)) {
|
||||
// EventTarget
|
||||
matches = [el];
|
||||
} else {
|
||||
@@ -174,8 +158,8 @@ odoo.define('web.test_utils_dom', function (require) {
|
||||
* element event if it is invisible
|
||||
* @returns {Promise}
|
||||
*/
|
||||
function clickFirst(el, options) {
|
||||
return click(el, _.extend({}, options, { first: true }));
|
||||
async function clickFirst(el, options) {
|
||||
return click(el, Object.assign({}, options, { first: true }));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -189,12 +173,12 @@ odoo.define('web.test_utils_dom', function (require) {
|
||||
* element event if it is invisible
|
||||
* @returns {Promise}
|
||||
*/
|
||||
function clickLast(el, options) {
|
||||
return click(el, _.extend({}, options, { last: true }));
|
||||
async function clickLast(el, options) {
|
||||
return click(el, Object.assign({}, options, { last: true }));
|
||||
}
|
||||
|
||||
/**
|
||||
* simulate a drag and drop operation between 2 jquery nodes: $el and $to.
|
||||
* Simulate a drag and drop operation between 2 jquery nodes: $el and $to.
|
||||
* This is a crude simulation, with only the mousedown, mousemove and mouseup
|
||||
* events, but it is enough to help test drag and drop operations with jqueryUI
|
||||
* sortable.
|
||||
@@ -225,25 +209,25 @@ odoo.define('web.test_utils_dom', function (require) {
|
||||
*/
|
||||
async function dragAndDrop($el, $to, options) {
|
||||
let el = null;
|
||||
if (isEventTarget($el)) {
|
||||
if (_isEventTarget($el)) {
|
||||
el = $el;
|
||||
$el = $(el);
|
||||
}
|
||||
if (isEventTarget($to)) {
|
||||
if (_isEventTarget($to)) {
|
||||
$to = $($to);
|
||||
}
|
||||
options = options || {};
|
||||
var position = options.position || 'center';
|
||||
var elementCenter = $el.offset();
|
||||
var toOffset = $to.offset();
|
||||
const position = options.position || 'center';
|
||||
const elementCenter = $el.offset();
|
||||
const toOffset = $to.offset();
|
||||
|
||||
if (_.isObject(position)) {
|
||||
if (typeof position === 'object') {
|
||||
toOffset.top += position.top + 1;
|
||||
toOffset.left += position.left + 1;
|
||||
} else {
|
||||
toOffset.top += $to.outerHeight() / 2;
|
||||
toOffset.left += $to.outerWidth() / 2;
|
||||
var vertical_offset = (toOffset.top < elementCenter.top) ? -1 : 1;
|
||||
const vertical_offset = (toOffset.top < elementCenter.top) ? -1 : 1;
|
||||
if (position === 'top') {
|
||||
toOffset.top -= $to.outerHeight() / 2 + vertical_offset;
|
||||
} else if (position === 'bottom') {
|
||||
@@ -257,7 +241,7 @@ odoo.define('web.test_utils_dom', function (require) {
|
||||
|
||||
if ($to[0].ownerDocument !== document) {
|
||||
// we are in an iframe
|
||||
var bound = $('iframe')[0].getBoundingClientRect();
|
||||
const bound = $('iframe')[0].getBoundingClientRect();
|
||||
toOffset.left += bound.left;
|
||||
toOffset.top += bound.top;
|
||||
}
|
||||
@@ -301,11 +285,11 @@ odoo.define('web.test_utils_dom', function (require) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the datepicker of a given element.
|
||||
* Open the datepicker of a given element.
|
||||
*
|
||||
* @param {jQuery} $datepickerEl element to which a datepicker is attached
|
||||
*/
|
||||
function openDatepicker($datepickerEl) {
|
||||
async function openDatepicker($datepickerEl) {
|
||||
return click($datepickerEl.find('.o_datepicker_input'));
|
||||
}
|
||||
|
||||
@@ -325,7 +309,7 @@ odoo.define('web.test_utils_dom', function (require) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers an event on the specified target.
|
||||
* Trigger an event on the specified target.
|
||||
* This function will dispatch a native event to an EventTarget or a
|
||||
* jQuery event to a jQuery object. This behaviour can be overridden by the
|
||||
* jquery option.
|
||||
@@ -340,7 +324,7 @@ odoo.define('web.test_utils_dom', function (require) {
|
||||
async function triggerEvent(el, eventType, eventAttrs = {}, fast = false) {
|
||||
let matches;
|
||||
let selectorMsg = "";
|
||||
if (isEventTarget(el)) {
|
||||
if (_isEventTarget(el)) {
|
||||
matches = [el];
|
||||
} else {
|
||||
matches = [...el];
|
||||
@@ -353,15 +337,15 @@ odoo.define('web.test_utils_dom', function (require) {
|
||||
const target = matches[0];
|
||||
let event;
|
||||
|
||||
if (!eventTypes[eventType] && !eventTypes[eventType.type]) {
|
||||
if (!EVENT_TYPES[eventType] && !EVENT_TYPES[eventType.type]) {
|
||||
event = new Event(eventType, Object.assign({}, eventAttrs, { bubbles: true }));
|
||||
} else {
|
||||
if (typeof eventType === "object") {
|
||||
const { constructor, processParameters } = eventTypes[eventType.type];
|
||||
const { constructor, processParameters } = EVENT_TYPES[eventType.type];
|
||||
const eventParameters = processParameters(eventType);
|
||||
event = new constructor(eventType.type, eventParameters);
|
||||
} else {
|
||||
const { constructor, processParameters } = eventTypes[eventType];
|
||||
const { constructor, processParameters } = EVENT_TYPES[eventType];
|
||||
event = new constructor(eventType, processParameters(eventAttrs));
|
||||
}
|
||||
}
|
||||
@@ -370,7 +354,7 @@ odoo.define('web.test_utils_dom', function (require) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers multiple events on the specified element.
|
||||
* Trigger multiple events on the specified element.
|
||||
*
|
||||
* @param {EventTarget|EventTarget[]} el
|
||||
* @param {string[]} events the events you want to trigger
|
||||
@@ -387,10 +371,32 @@ odoo.define('web.test_utils_dom', function (require) {
|
||||
}
|
||||
|
||||
for (let e = 0; e < events.length; e++) {
|
||||
await triggerEvent(el, events[e], {});
|
||||
await triggerEvent(el, events[e]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulate a keypress event for a given character
|
||||
*
|
||||
* @param {string} char the character, or 'ENTER'
|
||||
* @returns {Promise}
|
||||
*/
|
||||
async function triggerKeypressEvent(char) {
|
||||
let keycode;
|
||||
if (char === 'Enter') {
|
||||
keycode = $.ui.keyCode.ENTER;
|
||||
} else if (char === "Tab") {
|
||||
keycode = $.ui.keyCode.TAB;
|
||||
} else {
|
||||
keycode = char.charCodeAt(0);
|
||||
}
|
||||
return triggerEvent(document.body, 'keypress', {
|
||||
key: char,
|
||||
keyCode: keycode,
|
||||
which: keycode,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* simulate a mouse event with a custom event who add the item position. This is
|
||||
* sometimes necessary because the basic way to trigger an event (such as
|
||||
@@ -400,7 +406,7 @@ odoo.define('web.test_utils_dom', function (require) {
|
||||
* @param {string} type a mouse event type, such as 'mousedown' or 'mousemove'
|
||||
* @returns {Promise}
|
||||
*/
|
||||
function triggerMouseEvent($el, type) {
|
||||
async function triggerMouseEvent($el, type) {
|
||||
const el = $el instanceof jQuery ? $el[0] : $el;
|
||||
if (!el) {
|
||||
throw new Error(`no target found to trigger MouseEvent`);
|
||||
@@ -426,9 +432,9 @@ odoo.define('web.test_utils_dom', function (require) {
|
||||
* @param {string} type a mouse event type, such as 'mousedown' or 'mousemove'
|
||||
* @returns {HTMLElement}
|
||||
*/
|
||||
function triggerPositionalMouseEvent(x, y, type) {
|
||||
var ev = document.createEvent("MouseEvent");
|
||||
var el = document.elementFromPoint(x, y);
|
||||
async function triggerPositionalMouseEvent(x, y, type) {
|
||||
const ev = document.createEvent("MouseEvent");
|
||||
const el = document.elementFromPoint(x, y);
|
||||
ev.initMouseEvent(
|
||||
type,
|
||||
true /* bubble */,
|
||||
@@ -442,39 +448,17 @@ odoo.define('web.test_utils_dom', function (require) {
|
||||
return el;
|
||||
}
|
||||
|
||||
/**
|
||||
* simulate a keypress event for a given character
|
||||
*
|
||||
* @param {string} char the character, or 'ENTER'
|
||||
* @returns {Promise}
|
||||
*/
|
||||
function triggerKeypressEvent(char) {
|
||||
var keycode;
|
||||
if (char === 'Enter') {
|
||||
keycode = $.ui.keyCode.ENTER;
|
||||
} else if (char === "Tab") {
|
||||
keycode = $.ui.keyCode.TAB;
|
||||
} else {
|
||||
keycode = char.charCodeAt(0);
|
||||
}
|
||||
return triggerEvent(document.body, 'keypress', {
|
||||
key: char,
|
||||
keyCode: keycode,
|
||||
which: keycode,
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
click: click,
|
||||
clickFirst: clickFirst,
|
||||
clickLast: clickLast,
|
||||
dragAndDrop: dragAndDrop,
|
||||
openDatepicker: openDatepicker,
|
||||
returnAfterNextAnimationFrame: returnAfterNextAnimationFrame,
|
||||
triggerEvent: triggerEvent,
|
||||
triggerEvents: triggerEvents,
|
||||
triggerKeypressEvent: triggerKeypressEvent,
|
||||
triggerMouseEvent: triggerMouseEvent,
|
||||
triggerPositionalMouseEvent: triggerPositionalMouseEvent,
|
||||
click,
|
||||
clickFirst,
|
||||
clickLast,
|
||||
dragAndDrop,
|
||||
openDatepicker,
|
||||
returnAfterNextAnimationFrame,
|
||||
triggerEvent,
|
||||
triggerEvents,
|
||||
triggerKeypressEvent,
|
||||
triggerMouseEvent,
|
||||
triggerPositionalMouseEvent,
|
||||
};
|
||||
});
|
||||
|
||||
@@ -10,8 +10,14 @@ odoo.define('web.test_utils_fields', function (require) {
|
||||
* testUtils file.
|
||||
*/
|
||||
|
||||
var concurrency = require('web.concurrency');
|
||||
var domUtils = require('web.test_utils_dom');
|
||||
const testUtilsDom = require('web.test_utils_dom');
|
||||
|
||||
const ARROW_KEYS_MAPPING = {
|
||||
down: 'ArrowDown',
|
||||
left: 'ArrowLeft',
|
||||
right: 'ArrowRight',
|
||||
up: 'ArrowUp',
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Public functions
|
||||
@@ -23,13 +29,13 @@ odoo.define('web.test_utils_fields', function (require) {
|
||||
* @param {string} fieldName
|
||||
* @param {[string]} selector if set, this will restrict the search for the m2o
|
||||
* input
|
||||
* @returns {Promise}
|
||||
*/
|
||||
function clickM2OHighlightedItem(fieldName, selector) {
|
||||
var m2oSelector = `${selector || ''} .o_field_many2one[name=${fieldName}] input`;
|
||||
var $dropdown = $(m2oSelector).autocomplete('widget');
|
||||
async function clickM2OHighlightedItem(fieldName, selector) {
|
||||
const m2oSelector = `${selector || ''} .o_field_many2one[name=${fieldName}] input`;
|
||||
const $dropdown = $(m2oSelector).autocomplete('widget');
|
||||
// clicking on an li (no matter which one), will select the focussed one
|
||||
$dropdown.find('li:first()').click();
|
||||
return concurrency.delay(0);
|
||||
return testUtilsDom.click($dropdown[0].querySelector('li'));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -44,17 +50,15 @@ odoo.define('web.test_utils_fields', function (require) {
|
||||
* @param {string} searchText
|
||||
* @returns {Promise}
|
||||
*/
|
||||
function clickM2OItem(fieldName, searchText) {
|
||||
var m2oSelector = `.o_field_many2one[name=${fieldName}] input`;
|
||||
var $dropdown = $(m2oSelector).autocomplete('widget');
|
||||
var $target = $dropdown.find(`li:contains(${searchText})`).first();
|
||||
async function clickM2OItem(fieldName, searchText) {
|
||||
const m2oSelector = `.o_field_many2one[name=${fieldName}] input`;
|
||||
const $dropdown = $(m2oSelector).autocomplete('widget');
|
||||
const $target = $dropdown.find(`li:contains(${searchText})`).first();
|
||||
if ($target.length !== 1 || !$target.is(':visible')) {
|
||||
throw new Error('Menu item should be unique and visible');
|
||||
throw new Error('Menu item should be visible');
|
||||
}
|
||||
$target.mouseenter();
|
||||
$target.click();
|
||||
|
||||
return concurrency.delay(0);
|
||||
$target.mouseenter(); // This is NOT a mouseenter event. See jquery.js:5516 for more headaches.
|
||||
return testUtilsDom.click($target);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -63,16 +67,16 @@ odoo.define('web.test_utils_fields', function (require) {
|
||||
* @param {string} fieldName
|
||||
* @param {[string]} selector if set, this will restrict the search for the m2o
|
||||
* input
|
||||
* @returns {HTMLInputElement} the main many2one input
|
||||
* @returns {Promise<HTMLInputElement>} the main many2one input
|
||||
*/
|
||||
async function clickOpenM2ODropdown(fieldName, selector) {
|
||||
var m2oSelector = `${selector || ''} .o_field_many2one[name=${fieldName}] input`;
|
||||
var matches = document.querySelectorAll(m2oSelector);
|
||||
const m2oSelector = `${selector || ''} .o_field_many2one[name=${fieldName}] input`;
|
||||
const matches = document.querySelectorAll(m2oSelector);
|
||||
if (matches.length !== 1) {
|
||||
throw new Error(`cannot open m2o: selector ${selector} has been found ${matches.length} instead of 1`);
|
||||
}
|
||||
|
||||
await domUtils.click(matches[0]);
|
||||
await testUtilsDom.click(matches[0]);
|
||||
return matches[0];
|
||||
}
|
||||
|
||||
@@ -88,7 +92,7 @@ odoo.define('web.test_utils_fields', function (require) {
|
||||
* @param {string[]} events
|
||||
* @returns {Promise}
|
||||
*/
|
||||
function editAndTrigger(el, value, events) {
|
||||
async function editAndTrigger(el, value, events) {
|
||||
if (el instanceof jQuery) {
|
||||
if (el.length !== 1) {
|
||||
throw new Error(`target ${el.selector} has length ${el.length} instead of 1`);
|
||||
@@ -97,7 +101,7 @@ odoo.define('web.test_utils_fields', function (require) {
|
||||
} else {
|
||||
el.value = value;
|
||||
}
|
||||
return domUtils.triggerEvents(el, events);
|
||||
return testUtilsDom.triggerEvents(el, events);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -112,7 +116,7 @@ odoo.define('web.test_utils_fields', function (require) {
|
||||
* @param {string|number} value
|
||||
* @returns {Promise}
|
||||
*/
|
||||
function editInput(el, value) {
|
||||
async function editInput(el, value) {
|
||||
return editAndTrigger(el, value, ['input']);
|
||||
}
|
||||
|
||||
@@ -143,30 +147,22 @@ odoo.define('web.test_utils_fields', function (require) {
|
||||
* testUtils.fields.many2one.searchAndClickM2OItem('partner_id', {search: 'George'});
|
||||
*
|
||||
* @param {string} fieldName
|
||||
* @param {[Object]} options
|
||||
* @param {[string]} options.selector
|
||||
* @param {[string]} options.search
|
||||
* @param {[string]} options.item
|
||||
* @param {[Object]} [options = {}]
|
||||
* @param {[string]} [options.selector]
|
||||
* @param {[string]} [options.search]
|
||||
* @param {[string]} [options.item]
|
||||
* @returns {Promise}
|
||||
*/
|
||||
function searchAndClickM2OItem(fieldName, options) {
|
||||
options = options || {};
|
||||
|
||||
return clickOpenM2ODropdown(fieldName, options.selector).then(function (input) {
|
||||
var def;
|
||||
if (options.search) {
|
||||
input.value = options.search;
|
||||
input.dispatchEvent(new Event('input'));
|
||||
def = concurrency.delay(0);
|
||||
}
|
||||
return Promise.resolve(def).then(function () {
|
||||
if (options.item) {
|
||||
return clickM2OItem(fieldName, options.item);
|
||||
} else {
|
||||
return clickM2OHighlightedItem(fieldName, options.selector);
|
||||
}
|
||||
});
|
||||
});
|
||||
async function searchAndClickM2OItem(fieldName, options = {}) {
|
||||
const input = await clickOpenM2ODropdown(fieldName, options.selector);
|
||||
if (options.search) {
|
||||
await editInput(input, options.search);
|
||||
}
|
||||
if (options.item) {
|
||||
return clickM2OItem(fieldName, options.item);
|
||||
} else {
|
||||
return clickM2OHighlightedItem(fieldName, options.selector);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -181,7 +177,15 @@ odoo.define('web.test_utils_fields', function (require) {
|
||||
*/
|
||||
function triggerKey(type, $el, keyCode) {
|
||||
type = 'key' + type;
|
||||
const params = {};
|
||||
if (typeof keyCode === 'string') {
|
||||
// Key (new API)
|
||||
if (keyCode in ARROW_KEYS_MAPPING) {
|
||||
params.key = ARROW_KEYS_MAPPING[keyCode];
|
||||
} else {
|
||||
params.key = keyCode[0].toUpperCase() + keyCode.slice(1).toLowerCase();
|
||||
}
|
||||
// KeyCode/which (jQuery)
|
||||
if (keyCode.length > 1) {
|
||||
keyCode = keyCode.toUpperCase();
|
||||
keyCode = $.ui.keyCode[keyCode];
|
||||
@@ -189,8 +193,9 @@ odoo.define('web.test_utils_fields', function (require) {
|
||||
keyCode = keyCode.charCodeAt(0);
|
||||
}
|
||||
}
|
||||
$el.trigger({ type: type, which: keyCode, keyCode: keyCode });
|
||||
return concurrency.delay(0);
|
||||
params.keyCode = keyCode;
|
||||
params.which = keyCode;
|
||||
return testUtilsDom.triggerEvent($el, type, params);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -216,15 +221,15 @@ odoo.define('web.test_utils_fields', function (require) {
|
||||
}
|
||||
|
||||
return {
|
||||
clickM2OHighlightedItem: clickM2OHighlightedItem,
|
||||
clickM2OItem: clickM2OItem,
|
||||
clickOpenM2ODropdown: clickOpenM2ODropdown,
|
||||
editAndTrigger: editAndTrigger,
|
||||
editInput: editInput,
|
||||
editSelect: editSelect,
|
||||
searchAndClickM2OItem: searchAndClickM2OItem,
|
||||
triggerKey: triggerKey,
|
||||
triggerKeydown: triggerKeydown,
|
||||
triggerKeyup: triggerKeyup,
|
||||
clickM2OHighlightedItem,
|
||||
clickM2OItem,
|
||||
clickOpenM2ODropdown,
|
||||
editAndTrigger,
|
||||
editInput,
|
||||
editSelect,
|
||||
searchAndClickM2OItem,
|
||||
triggerKey,
|
||||
triggerKeydown,
|
||||
triggerKeyup,
|
||||
};
|
||||
});
|
||||
|
||||
@@ -465,7 +465,7 @@ function intercept(widget, eventName, fn, propagate) {
|
||||
* @param {integer} hours the digits for hours (24h)
|
||||
* @param {integer} minutes
|
||||
* @param {integer} seconds
|
||||
* @returns {function} a callback to unpatch window.Date.
|
||||
* @returns {Function} a callback to unpatch window.Date.
|
||||
*/
|
||||
function patchDate(year, month, day, hours, minutes, seconds) {
|
||||
var RealDate = window.Date;
|
||||
|
||||
@@ -1,36 +1,26 @@
|
||||
odoo.define('web.test_utils_modal', function (require) {
|
||||
"use strict";
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Modal Test Utils
|
||||
*
|
||||
* This module defines various utility functions to help test pivot views.
|
||||
*
|
||||
* Note that all methods defined in this module are exported in the main
|
||||
* testUtils file.
|
||||
*/
|
||||
/**
|
||||
* Modal Test Utils
|
||||
*
|
||||
* This module defines various utility functions to help test pivot views.
|
||||
*
|
||||
* Note that all methods defined in this module are exported in the main
|
||||
* testUtils file.
|
||||
*/
|
||||
|
||||
var core = require('web.core');
|
||||
var concurrency = require('web.concurrency');
|
||||
const { _t } = require('web.core');
|
||||
const testUtilsDom = require('web.test_utils_dom');
|
||||
|
||||
/**
|
||||
* Click on a button in the footer of a modal (which contains a given string).
|
||||
* Note that this method checks the unicity of the button.
|
||||
*
|
||||
* @param {string} text (in english: this method will perform the translation)
|
||||
*/
|
||||
function clickButton(text) {
|
||||
var selector = `.modal-footer button:contains(${core._t(text)})`;
|
||||
var $button = $(selector);
|
||||
if ($button.length !== 1) {
|
||||
throw new Error(`Found ${$button.length} button(s) containing '${text}'`);
|
||||
/**
|
||||
* Click on a button in the footer of a modal (which contains a given string).
|
||||
*
|
||||
* @param {string} text (in english: this method will perform the translation)
|
||||
*/
|
||||
function clickButton(text) {
|
||||
return testUtilsDom.click($(`.modal-footer button:contains(${_t(text)})`));
|
||||
}
|
||||
$button.click();
|
||||
return concurrency.delay(0);
|
||||
}
|
||||
|
||||
return {
|
||||
clickButton: clickButton,
|
||||
};
|
||||
|
||||
return { clickButton };
|
||||
});
|
||||
|
||||
@@ -8,6 +8,80 @@ var Widget = require('web.Widget');
|
||||
var Wysiwyg = require('web_editor.wysiwyg');
|
||||
var options = require('web_editor.snippets.options');
|
||||
|
||||
const COLOR_PICKER_TEMPLATE = `
|
||||
<t t-name="web_editor.colorpicker">
|
||||
<colorpicker>
|
||||
<div class="o_colorpicker_section" data-name="theme" data-display="Theme Colors" data-icon-class="fa fa-flask">
|
||||
<button data-color="alpha"/>
|
||||
<button data-color="beta"/>
|
||||
<button data-color="gamma"/>
|
||||
<button data-color="delta"/>
|
||||
<button data-color="epsilon"/>
|
||||
</div>
|
||||
<div class="o_colorpicker_section" data-name="transparent_grayscale" data-display="Transparent Colors" data-icon-class="fa fa-eye-slash">
|
||||
<button class="o_btn_transparent"/>
|
||||
<button data-color="black-25"/>
|
||||
<button data-color="black-50"/>
|
||||
<button data-color="black-75"/>
|
||||
<button data-color="white-25"/>
|
||||
<button data-color="white-50"/>
|
||||
<button data-color="white-75"/>
|
||||
</div>
|
||||
<div class="o_colorpicker_section" data-name="common" data-display="Common Colors" data-icon-class="fa fa-paint-brush"/>
|
||||
</colorpicker>
|
||||
</t>`;
|
||||
const SNIPPETS_TEMPLATE = `
|
||||
<h2 id="snippets_menu">Add blocks</h2>
|
||||
<div id="o_scroll">
|
||||
<div id="snippet_structure" class="o_panel">
|
||||
<div class="o_panel_header">
|
||||
<i class="fa fa-th-large"/> First Panel
|
||||
</div>
|
||||
<div class="o_panel_body">
|
||||
<div name="Separator" data-oe-type="snippet" data-oe-thumbnail="/website/static/src/img/snippets_thumbs/s_separator.png">
|
||||
<div class="s_hr pt32 pb32">
|
||||
<hr class="s_hr_1px s_hr_solid w-100 mx-auto"/>
|
||||
</div>
|
||||
</div>
|
||||
<div name="Content" data-oe-type="snippet" data-oe-thumbnail="/website/static/src/img/snippets_thumbs/s_text_block.png">
|
||||
<section name="Content+Options" class="test_option_all pt32 pb32" data-oe-type="snippet" data-oe-thumbnail="/website/static/src/img/snippets_thumbs/s_text_block.png">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-lg-10 offset-lg-1 pt32 pb32">
|
||||
<h2>Title</h2>
|
||||
<p class="lead o_default_snippet_text">Content</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="snippet_options" class="d-none">
|
||||
<div data-js="many2one" data-selector="[data-oe-many2one-model]:not([data-oe-readonly])" data-no-check="true"/>
|
||||
<div data-js="content"
|
||||
data-selector=".s_hr, .test_option_all"
|
||||
data-drop-in=".note-editable"
|
||||
data-drop-near="p, h1, h2, h3, blockquote, .s_hr"/>
|
||||
<div data-js="sizing_y" data-selector=".s_hr, .test_option_all"/>
|
||||
<div data-selector=".test_option_all">
|
||||
<we-colorpicker string="Background Color" data-select-style="true" data-css-property="background-color" data-color-prefix="bg-"/>
|
||||
</div>
|
||||
<div data-js="background" data-selector=".test_option_all">
|
||||
<we-button data-choose-image="true" data-no-preview="true">
|
||||
<i class="fa fa-picture-o"/> Background Image
|
||||
</we-button>
|
||||
</div>
|
||||
<div data-js="option_test" data-selector=".s_hr">
|
||||
<we-select string="Alignment">
|
||||
<we-button data-select-class="align-items-start">Top</we-button>
|
||||
<we-button data-select-class="align-items-center">Middle</we-button>
|
||||
<we-button data-select-class="align-items-end">Bottom</we-button>
|
||||
<we-button data-select-class="align-items-stretch">Equal height</we-button>
|
||||
</we-select>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
MockServer.include({
|
||||
//--------------------------------------------------------------------------
|
||||
@@ -19,84 +93,16 @@ MockServer.include({
|
||||
* @private
|
||||
* @returns {Promise}
|
||||
*/
|
||||
_performRpc: function (route, args) {
|
||||
async _performRpc(route, args) {
|
||||
if (args.model === "ir.ui.view") {
|
||||
if (args.method === 'read_template' && args.args[0] === "web_editor.colorpicker") {
|
||||
var template = '<templates><t t-name="web_editor.colorpicker">' +
|
||||
'<colorpicker>' +
|
||||
' <div class="o_colorpicker_section" data-name="theme" data-display="Theme Colors" data-icon-class="fa fa-flask">' +
|
||||
' <button data-color="alpha"></button>' +
|
||||
' <button data-color="beta"></button>' +
|
||||
' <button data-color="gamma"></button>' +
|
||||
' <button data-color="delta"></button>' +
|
||||
' <button data-color="epsilon"></button>' +
|
||||
' </div>' +
|
||||
' <div class="o_colorpicker_section" data-name="transparent_grayscale" data-display="Transparent Colors" data-icon-class="fa fa-eye-slash">' +
|
||||
' <button class="o_btn_transparent"></button>' +
|
||||
' <button data-color="black-25"></button>' +
|
||||
' <button data-color="black-50"></button>' +
|
||||
' <button data-color="black-75"></button>' +
|
||||
' <button data-color="white-25"></button>' +
|
||||
' <button data-color="white-50"></button>' +
|
||||
' <button data-color="white-75"></button>' +
|
||||
' </div>' +
|
||||
' <div class="o_colorpicker_section" data-name="common" data-display="Common Colors" data-icon-class="fa fa-paint-brush"></div>' +
|
||||
'</colorpicker>' +
|
||||
'</t></templates>';
|
||||
return Promise.resolve(template);
|
||||
return COLOR_PICKER_TEMPLATE;
|
||||
}
|
||||
if (args.method === 'render_template' && args.args[0] === "web_editor.snippets") {
|
||||
var template = '<h2 id="snippets_menu">Add blocks</h2>' +
|
||||
'<div id="o_scroll">' +
|
||||
' <div id="snippet_structure" class="o_panel">' +
|
||||
' <div class="o_panel_header">' +
|
||||
' <i class="fa fa-th-large"></i> First Panel' +
|
||||
' </div>' +
|
||||
' <div class="o_panel_body">' +
|
||||
' <div name="Separator" data-oe-type="snippet" data-oe-thumbnail="/website/static/src/img/snippets_thumbs/s_separator.png">' +
|
||||
' <div class="s_hr pt32 pb32">' +
|
||||
' <hr class="s_hr_1px s_hr_solid w-100 mx-auto"/>' +
|
||||
' </div>' +
|
||||
' </div>' +
|
||||
' <div name="Content" data-oe-type="snippet" data-oe-thumbnail="/website/static/src/img/snippets_thumbs/s_text_block.png">' +
|
||||
' <section name="Content+Options" class="test_option_all pt32 pb32" data-oe-type="snippet" data-oe-thumbnail="/website/static/src/img/snippets_thumbs/s_text_block.png">' +
|
||||
' <div class="container">' +
|
||||
' <div class="row">' +
|
||||
' <div class="col-lg-10 offset-lg-1 pt32 pb32">' +
|
||||
' <h2>Title</h2>' +
|
||||
' <p class="lead o_default_snippet_text">Content</p>' +
|
||||
' </div>' +
|
||||
' </div>' +
|
||||
' </div>' +
|
||||
' </section>' +
|
||||
' </div>' +
|
||||
' </div>' +
|
||||
' </div>' +
|
||||
'</div>' +
|
||||
'<div id="snippet_options" class="d-none">' +
|
||||
' <div data-js="many2one" data-selector="[data-oe-many2one-model]:not([data-oe-readonly])" data-no-check="true"></div>' +
|
||||
' <div data-js="content" data-selector=".s_hr, .test_option_all" ' +
|
||||
'data-drop-in=".note-editable" data-drop-near="p, h1, h2, h3, blockquote, .s_hr"></div>' +
|
||||
' <div data-js="sizing_y" data-selector=".s_hr, .test_option_all"></div>' +
|
||||
' <div data-selector=".test_option_all">' +
|
||||
' <we-colorpicker string="Background Color" data-select-style="true" data-css-property="background-color" data-color-prefix="bg-"/>' +
|
||||
' </div>' +
|
||||
' <div data-js="background" data-selector=".test_option_all">' +
|
||||
' <we-button data-choose-image="true" data-no-preview="true"><i class="fa fa-picture-o"/> Background Image</we-button>' +
|
||||
' </div>' +
|
||||
' <div data-js="option_test" data-selector=".s_hr">' +
|
||||
' <we-select string="Alignment">' +
|
||||
' <we-button data-select-class="align-items-start">Top</we-button>' +
|
||||
' <we-button data-select-class="align-items-center">Middle</we-button>' +
|
||||
' <we-button data-select-class="align-items-end">Bottom</we-button>' +
|
||||
' <we-button data-select-class="align-items-stretch">Equal height</we-button>' +
|
||||
' </we-select>' +
|
||||
' </div>' +
|
||||
'</div>';
|
||||
return Promise.resolve(template);
|
||||
return SNIPPETS_TEMPLATE;
|
||||
}
|
||||
}
|
||||
return this._super.apply(this, arguments);
|
||||
return this._super(...arguments);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -188,80 +194,14 @@ function wysiwygData(data) {
|
||||
},
|
||||
},
|
||||
records: [],
|
||||
read_template: function (args) {
|
||||
read_template(args) {
|
||||
if (args[0] === 'web_editor.colorpicker') {
|
||||
return '<templates><t t-name="web_editor.colorpicker">' +
|
||||
'<colorpicker>' +
|
||||
' <div class="o_colorpicker_section" data-name="theme" data-display="Theme Colors" data-icon-class="fa fa-flask">' +
|
||||
' <button data-color="alpha"></button>' +
|
||||
' <button data-color="beta"></button>' +
|
||||
' <button data-color="gamma"></button>' +
|
||||
' <button data-color="delta"></button>' +
|
||||
' <button data-color="epsilon"></button>' +
|
||||
' </div>' +
|
||||
' <div class="o_colorpicker_section" data-name="transparent_grayscale" data-display="Transparent Colors" data-icon-class="fa fa-eye-slash">' +
|
||||
' <button class="o_btn_transparent"></button>' +
|
||||
' <button data-color="black-25"></button>' +
|
||||
' <button data-color="black-50"></button>' +
|
||||
' <button data-color="black-75"></button>' +
|
||||
' <button data-color="white-25"></button>' +
|
||||
' <button data-color="white-50"></button>' +
|
||||
' <button data-color="white-75"></button>' +
|
||||
' </div>' +
|
||||
' <div class="o_colorpicker_section" data-name="common" data-display="Common Colors" data-icon-class="fa fa-paint-brush"></div>' +
|
||||
'</colorpicker>' +
|
||||
'</t></templates>';
|
||||
return COLOR_PICKER_TEMPLATE;
|
||||
}
|
||||
},
|
||||
render_template: function (args) {
|
||||
render_template(args) {
|
||||
if (args[0] === 'web_editor.snippets') {
|
||||
return '<h2 id="snippets_menu">Add blocks</h2>' +
|
||||
'<div id="o_scroll">' +
|
||||
' <div id="snippet_structure" class="o_panel">' +
|
||||
' <div class="o_panel_header">' +
|
||||
' <i class="fa fa-th-large"></i> First Panel' +
|
||||
' </div>' +
|
||||
' <div class="o_panel_body">' +
|
||||
' <div name="Separator" data-oe-type="snippet" data-oe-thumbnail="/website/static/src/img/snippets_thumbs/s_separator.png">' +
|
||||
' <div class="s_hr pt32 pb32">' +
|
||||
' <hr class="s_hr_1px s_hr_solid w-100 mx-auto"/>' +
|
||||
' </div>' +
|
||||
' </div>' +
|
||||
' <div name="Content" data-oe-type="snippet" data-oe-thumbnail="/website/static/src/img/snippets_thumbs/s_text_block.png">' +
|
||||
' <section name="Content+Options" class="test_option_all pt32 pb32" data-oe-type="snippet" data-oe-thumbnail="/website/static/src/img/snippets_thumbs/s_text_block.png">' +
|
||||
' <div class="container">' +
|
||||
' <div class="row">' +
|
||||
' <div class="col-lg-10 offset-lg-1 pt32 pb32">' +
|
||||
' <h2>Title</h2>' +
|
||||
' <p class="lead o_default_snippet_text">Content</p>' +
|
||||
' </div>' +
|
||||
' </div>' +
|
||||
' </div>' +
|
||||
' </section>' +
|
||||
' </div>' +
|
||||
' </div>' +
|
||||
' </div>' +
|
||||
'</div>' +
|
||||
'<div id="snippet_options" class="d-none">' +
|
||||
' <div data-js="many2one" data-selector="[data-oe-many2one-model]:not([data-oe-readonly])" data-no-check="true"></div>' +
|
||||
' <div data-js="content" data-selector=".s_hr, .test_option_all" ' +
|
||||
'data-drop-in=".note-editable" data-drop-near="p, h1, h2, h3, blockquote, .s_hr"></div>' +
|
||||
' <div data-js="sizing_y" data-selector=".s_hr, .test_option_all"></div>' +
|
||||
' <div data-selector=".test_option_all">' +
|
||||
' <we-colorpicker string="Background Color" data-select-style="true" data-css-property="background-color" data-color-prefix="bg-"/>' +
|
||||
' </div>' +
|
||||
' <div data-js="background" data-selector=".test_option_all">' +
|
||||
' <we-button data-choose-image="true" data-no-preview="true"><i class="fa fa-picture-o"/> Background Image</we-button>' +
|
||||
' </div>' +
|
||||
' <div data-js="option_test" data-selector=".s_hr">' +
|
||||
' <we-select string="Alignment">' +
|
||||
' <we-button data-select-class="align-items-start">Top</we-button>' +
|
||||
' <we-button data-select-class="align-items-center">Middle</we-button>' +
|
||||
' <we-button data-select-class="align-items-end">Bottom</we-button>' +
|
||||
' <we-button data-select-class="align-items-stretch">Equal height</we-button>' +
|
||||
' </we-select>' +
|
||||
' </div>' +
|
||||
'</div>';
|
||||
return SNIPPETS_TEMPLATE;
|
||||
}
|
||||
},
|
||||
},
|
||||
@@ -758,7 +698,7 @@ var textInput = function (target, char) {
|
||||
if (!ev.defaultPrevented) {
|
||||
document.execCommand("insertText", 0, ev.data);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
wysiwygData: wysiwygData,
|
||||
|
||||
Reference in New Issue
Block a user