[REF] web,*: native event dispatching in test utils
*: mail,partner_autocomplete: adapted tests Added a new function to trigger the adequate type of event on an element (native JS event to DOM Nodes and jQuery event to jQuery objects), and refactored the generic event-triggering functions to call this new function.
This commit is contained in:
@@ -179,7 +179,7 @@ QUnit.test('activity view: simple activity rendering', async function (assert) {
|
||||
assert.containsN(activity, td, 2, 'should contain an empty cell as no activity scheduled yet.');
|
||||
|
||||
// schedule an activity (this triggers a do_action)
|
||||
testUtils.fields.editAndTrigger(activity.$(td + ':first'), null, ['mouseenter', 'click']);
|
||||
await testUtils.fields.editAndTrigger(activity.$(td + ':first'), null, ['mouseenter', 'click']);
|
||||
assert.containsOnce(activity, 'table tfoot tr .o_record_selector',
|
||||
'should contain search more selector to choose the record to schedule an activity for it');
|
||||
|
||||
|
||||
@@ -205,13 +205,13 @@ QUnit.test('notify is typing', async function (assert) {
|
||||
// STEP 1: current user is typing something
|
||||
step = 1;
|
||||
$input.focus();
|
||||
testUtils.fields.editInput($input, '1');
|
||||
await testUtils.fields.editInput($input, '1');
|
||||
|
||||
assert.verifySteps(['notify_typing']);
|
||||
|
||||
// STEP 2: current user clears input
|
||||
step = 2;
|
||||
testUtils.fields.editInput($input, '');
|
||||
await testUtils.fields.editInput($input, '');
|
||||
|
||||
assert.verifySteps(['notify_typing']);
|
||||
|
||||
|
||||
@@ -360,11 +360,11 @@ odoo.define('partner_autocomplete.tests', function (require) {
|
||||
'<field name="name"/>' +
|
||||
'<field name="parent_id" widget="res_partner_many2one"/>' +
|
||||
'</form>',
|
||||
}).then(function (form) {
|
||||
}).then(async function (form) {
|
||||
var $input = form.$('.o_field_many2one[name="parent_id"] input:visible');
|
||||
assert.strictEqual($input.length, 1, "there should be an <input/> for the Many2one");
|
||||
|
||||
testUtils.fields.editInput($input, 'odoo');
|
||||
await testUtils.fields.editInput($input, 'odoo');
|
||||
|
||||
concurrency.delay(0).then(function () {
|
||||
var $dropdown = $input.autocomplete('widget');
|
||||
|
||||
@@ -2967,17 +2967,17 @@ QUnit.module('basic_fields', {
|
||||
// switch to edit mode
|
||||
await testUtils.form.clickEdit(form);
|
||||
// open datepicker and select another value
|
||||
testUtils.dom.openDatepicker(form.$('.o_datepicker'));
|
||||
testUtils.dom.click($('.bootstrap-datetimepicker-widget .picker-switch').first());
|
||||
testUtils.dom.click($('.bootstrap-datetimepicker-widget .picker-switch:eq(1)'));
|
||||
testUtils.dom.click($('.bootstrap-datetimepicker-widget .year').eq(11));
|
||||
testUtils.dom.click($('.bootstrap-datetimepicker-widget .month').eq(11));
|
||||
testUtils.dom.click($('.day:contains(31)'));
|
||||
await testUtils.dom.openDatepicker(form.$('.o_datepicker'));
|
||||
await testUtils.dom.click($('.bootstrap-datetimepicker-widget .picker-switch').first());
|
||||
await testUtils.dom.click($('.bootstrap-datetimepicker-widget .picker-switch:eq(1)'));
|
||||
await testUtils.dom.click($('.bootstrap-datetimepicker-widget .year').eq(11));
|
||||
await testUtils.dom.click($('.bootstrap-datetimepicker-widget .month').eq(11));
|
||||
await testUtils.dom.click($('.day:contains(31)'));
|
||||
|
||||
var $warn = form.$('.o_datepicker_warning:visible');
|
||||
assert.strictEqual($warn.length, 1, "should have a warning in the form view");
|
||||
|
||||
testUtils.fields.editSelect(form.$('.o_field_widget[name=date] input'), ''); // remove the value
|
||||
await testUtils.fields.editSelect(form.$('.o_field_widget[name=date] input'), ''); // remove the value
|
||||
|
||||
$warn = form.$('.o_datepicker_warning:visible');
|
||||
assert.strictEqual($warn.length, 0, "the warning in the form view should be hidden");
|
||||
@@ -3100,7 +3100,7 @@ QUnit.module('basic_fields', {
|
||||
assert.strictEqual(form.$('.o_datepicker_input').val(), '02/03/2017',
|
||||
'the date should be correct in edit mode');
|
||||
|
||||
testUtils.fields.editAndTrigger(form.$('.o_datepicker_input'), '', ['input', 'change', 'focusout']);
|
||||
await testUtils.fields.editAndTrigger(form.$('.o_datepicker_input'), '', ['input', 'change', 'focusout']);
|
||||
assert.strictEqual(form.$('.o_datepicker_input').val(), '',
|
||||
'should have correctly removed the value');
|
||||
|
||||
|
||||
@@ -6611,7 +6611,7 @@ QUnit.module('fields', {}, function () {
|
||||
|
||||
await testUtils.dom.click(form.$('.o_field_x2many_list_row_add a'));
|
||||
prom = testUtils.makeTestPromise();
|
||||
testUtils.fields.editAndTrigger(form.$('.o_field_widget[name=int_field]'), '44',
|
||||
await testUtils.fields.editAndTrigger(form.$('.o_field_widget[name=int_field]'), '44',
|
||||
['input', { type: 'keydown', which: $.ui.keyCode.TAB }]);
|
||||
prom.resolve();
|
||||
await testUtils.nextTick();
|
||||
|
||||
@@ -135,6 +135,7 @@ return Promise.all([
|
||||
clickFirst: testUtilsDom.clickFirst,
|
||||
clickLast: testUtilsDom.clickLast,
|
||||
triggerEvents: testUtilsDom.triggerEvents,
|
||||
triggerEvent: testUtilsDom.triggerEvent,
|
||||
},
|
||||
form: {
|
||||
clickEdit: testUtilsForm.clickEdit,
|
||||
|
||||
@@ -23,8 +23,8 @@ var concurrency = require('web.concurrency');
|
||||
* merged. This is not the default as of now, because handlers are triggered
|
||||
* synchronously, which is not the same as the 'reality'.
|
||||
*
|
||||
* @param {jqueryElement} $el
|
||||
* @param {jqueryElement} $to
|
||||
* @param {jqueryElement|HTMLElement} $el
|
||||
* @param {jqueryElement|HTMLElement} $to
|
||||
* @param {Object} [options]
|
||||
* @param {string|Object} [options.position='center'] target position:
|
||||
* can either be one of {'top', 'bottom', 'left', 'right'} or
|
||||
@@ -36,8 +36,21 @@ var concurrency = require('web.concurrency');
|
||||
* @param {boolean} [options.withTrailingClick=false] if true, this utility
|
||||
* function will also trigger a click on the target after the mouseup event
|
||||
* (this is actually what happens when a drag and drop operation is done)
|
||||
* @param {jQuery|HTMLElement} [options.mouseenterTarget=undefined] target of the mouseenter event
|
||||
* @param {jQuery|HTMLElement} [options.mousedownTarget=undefined] target of the mousedown event
|
||||
* @param {jQuery|HTMLElement} [options.mousemoveTarget=undefined] target of the mousemove event
|
||||
* @param {jQuery|HTMLElement} [options.mouseupTarget=undefined] target of the mouseup event
|
||||
* @returns {Promise}
|
||||
*/
|
||||
function dragAndDrop($el, $to, options) {
|
||||
let el = null;
|
||||
if ($el instanceof EventTarget) {
|
||||
el = $el;
|
||||
$el = $(el);
|
||||
}
|
||||
if ($to instanceof EventTarget) {
|
||||
$to = $($to);
|
||||
}
|
||||
options = options || {};
|
||||
var position = options.position || 'center';
|
||||
var elementCenter = $el.offset();
|
||||
@@ -67,40 +80,40 @@ function dragAndDrop($el, $to, options) {
|
||||
toOffset.left += bound.left;
|
||||
toOffset.top += bound.top;
|
||||
}
|
||||
$el.trigger($.Event("mouseenter"));
|
||||
triggerEvent(options.mouseenterTarget || el || $el, 'mouseenter');
|
||||
if (!(options.continueMove)) {
|
||||
elementCenter.left += $el.outerWidth() / 2;
|
||||
elementCenter.top += $el.outerHeight() / 2;
|
||||
|
||||
$el.trigger($.Event("mousedown", {
|
||||
triggerEvent(options.mousedownTarget || el || $el, 'mousedown', {
|
||||
which: 1,
|
||||
pageX: elementCenter.left,
|
||||
pageY: elementCenter.top
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
$el.trigger($.Event("mousemove", {
|
||||
triggerEvent(options.mousemoveTarget || el || $el, 'mousemove', {
|
||||
which: 1,
|
||||
pageX: toOffset.left,
|
||||
pageY: toOffset.top
|
||||
}));
|
||||
});
|
||||
|
||||
if (!options.disableDrop) {
|
||||
$el.trigger($.Event("mouseup", {
|
||||
triggerEvent(options.mouseupTarget || el || $el, 'mouseup', {
|
||||
which: 1,
|
||||
pageX: toOffset.left,
|
||||
pageY: toOffset.top
|
||||
}));
|
||||
});
|
||||
if (options.withTrailingClick) {
|
||||
$el.click();
|
||||
triggerEvent(options.mouseupTarget || el || $el, 'click');
|
||||
}
|
||||
} else {
|
||||
// It's impossible to drag another element when one is already
|
||||
// being dragged. So it's necessary to finish the drop when the test is
|
||||
// over otherwise it's impossible for the next tests to drag and
|
||||
// drop elements.
|
||||
$el.on("remove", function () {
|
||||
$el.trigger($.Event("mouseup"));
|
||||
$el.on('remove', function () {
|
||||
triggerEvent($el, 'mouseup');
|
||||
});
|
||||
}
|
||||
return concurrency.delay(0);
|
||||
@@ -111,17 +124,24 @@ function dragAndDrop($el, $to, options) {
|
||||
* sometimes necessary because the basic way to trigger an event (such as
|
||||
* $el.trigger('mousemove')); ) is too crude for some uses.
|
||||
*
|
||||
* @param {jqueryElement} $el
|
||||
* @param {jQuery|HTMLElement} $el
|
||||
* @param {string} type a mouse event type, such as 'mousedown' or 'mousemove'
|
||||
* @returns {Promise}
|
||||
*/
|
||||
function triggerMouseEvent($el, type) {
|
||||
var pos = $el.offset();
|
||||
var e = new $.Event(type);
|
||||
const el = $el instanceof jQuery ? $el[0] : $el;
|
||||
if (!el) {
|
||||
throw new Error(`no target found to trigger MouseEvent`);
|
||||
}
|
||||
const rect = el.getBoundingClientRect();
|
||||
// little fix since it seems on chrome, it triggers 1px too on the left
|
||||
e.pageX = e.layerX = e.screenX = pos.left + 1;
|
||||
e.pageY = e.layerY = e.screenY = pos.top;
|
||||
e.which = 1;
|
||||
$el.trigger(e);
|
||||
const left = rect.x + 1;
|
||||
const top = rect.y;
|
||||
return triggerEvent($el, type, {
|
||||
which: 1,
|
||||
pageX: left, layerX: left, screenX: left,
|
||||
pageY: top, layerY: top, screenY: top,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -132,6 +152,7 @@ function triggerMouseEvent($el, type) {
|
||||
* @param {integer} x
|
||||
* @param {integer} y
|
||||
* @param {string} type a mouse event type, such as 'mousedown' or 'mousemove'
|
||||
* @returns {HTMLElement}
|
||||
*/
|
||||
function triggerPositionalMouseEvent(x, y, type) {
|
||||
var ev = document.createEvent("MouseEvent");
|
||||
@@ -153,6 +174,7 @@ function triggerPositionalMouseEvent(x, y, type) {
|
||||
* simulate a keypress event for a given character
|
||||
*
|
||||
* @param {string} char the character, or 'ENTER'
|
||||
* @returns {Promise}
|
||||
*/
|
||||
function triggerKeypressEvent(char) {
|
||||
var keycode;
|
||||
@@ -163,7 +185,10 @@ function triggerKeypressEvent(char) {
|
||||
} else {
|
||||
keycode = char.charCodeAt(0);
|
||||
}
|
||||
return $('body').trigger($.Event('keypress', { which: keycode, keyCode: keycode }));
|
||||
return triggerEvent(document.body, 'keypress', {
|
||||
keyCode: keycode,
|
||||
which: keycode,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -178,6 +203,7 @@ function triggerKeypressEvent(char) {
|
||||
* element event if it is invisible
|
||||
* @param {boolean} [options.shouldTriggerClick=false] if true, trigger the
|
||||
* click event without calling the function click of jquery
|
||||
* @returns {Promise}
|
||||
*/
|
||||
function click(el, options) {
|
||||
options = options || {};
|
||||
@@ -209,7 +235,7 @@ function click(el, options) {
|
||||
throw new Error(`Found ${validMatches.length} elements to click on, instead of 1 ${selectorMsg}`);
|
||||
}
|
||||
if (options.shouldTriggerClick) {
|
||||
validMatches.trigger('click');
|
||||
return triggerEvent(validMatches, 'click');
|
||||
} else {
|
||||
validMatches.click();
|
||||
}
|
||||
@@ -227,6 +253,7 @@ function click(el, options) {
|
||||
* element event if it is invisible
|
||||
* @param {boolean} [options.shouldTriggerClick=false] if true, trigger the
|
||||
* click event without calling the function click of jquery
|
||||
* @returns {Promise}
|
||||
*/
|
||||
function clickFirst(el, options) {
|
||||
return click(el, _.extend({}, options, {first: true}));
|
||||
@@ -242,28 +269,58 @@ function clickFirst(el, options) {
|
||||
* element event if it is invisible
|
||||
* @param {boolean} [options.shouldTriggerClick=false] if true, trigger the
|
||||
* click event without calling the function click of jquery
|
||||
* @returns {Promise}
|
||||
*/
|
||||
function clickLast(el, options) {
|
||||
return click(el, _.extend({}, options, {last: true}));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* trigger events on the specified target
|
||||
* @param {jQuery} $el should target a single dom node
|
||||
* Trigger events on the specified target
|
||||
* @param {jQuery|HTMLElement} $el should target a single dom node
|
||||
* @param {string[]} events the events you want to trigger
|
||||
* @returns Promise
|
||||
* @returns {Promise}
|
||||
*/
|
||||
function triggerEvents($el, events) {
|
||||
if ($el.length !== 1) {
|
||||
throw new Error(`target has length ${$el.length} instead of 1`);
|
||||
if ($el instanceof jQuery) {
|
||||
if ($el.length !== 1) {
|
||||
throw new Error(`target has length ${$el.length} instead of 1`);
|
||||
}
|
||||
} else if (!($el instanceof EventTarget)) {
|
||||
throw new Error(`target is neither a jQuery element nor an HTML element`);
|
||||
}
|
||||
if (typeof events === 'string') {
|
||||
events = [events];
|
||||
}
|
||||
events.forEach(function (event) {
|
||||
return events.reduce((previous, event) => {
|
||||
return previous.then(() => triggerEvent($el, event));
|
||||
}, Promise.resolve());
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger an event on the specified target.
|
||||
* This function will dispatch a native event to an HTMLElement or a
|
||||
* jQuery event to a jQuery object.
|
||||
* @param {jQuery|HTMLElement} $el event target.
|
||||
* @param {string} type event type
|
||||
* @param {Object} [options] event attributes
|
||||
* @returns {Promise}
|
||||
*/
|
||||
function triggerEvent($el, type, options={}) {
|
||||
if ($el instanceof EventTarget) {
|
||||
const event = new Event(type);
|
||||
Object.assign(event, options);
|
||||
Object.defineProperty(event, 'target', {
|
||||
writable: false,
|
||||
value: $el,
|
||||
});
|
||||
$el.dispatchEvent(event);
|
||||
} else {
|
||||
const event = Object.keys(options).length ?
|
||||
$.Event(type, options) :
|
||||
type;
|
||||
$el.trigger(event);
|
||||
});
|
||||
}
|
||||
return concurrency.delay(0);
|
||||
}
|
||||
|
||||
@@ -274,7 +331,7 @@ function triggerEvents($el, events) {
|
||||
* @param {jQuery} $datepickerEl element to which a datepicker is attached
|
||||
*/
|
||||
function openDatepicker($datepickerEl) {
|
||||
click($datepickerEl.find('.o_datepicker_input'));
|
||||
return click($datepickerEl.find('.o_datepicker_input'));
|
||||
}
|
||||
|
||||
|
||||
@@ -288,6 +345,7 @@ return {
|
||||
clickFirst: clickFirst,
|
||||
clickLast: clickLast,
|
||||
triggerEvents: triggerEvents,
|
||||
triggerEvent: triggerEvent,
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
@@ -27,6 +27,7 @@ var domUtils = require('web.test_utils_dom');
|
||||
* @param {jQuery} $el should target an input, textarea or select
|
||||
* @param {string|number} value
|
||||
* @param {string[]} events
|
||||
* @returns {Promise}
|
||||
*/
|
||||
function editAndTrigger($el, value, events) {
|
||||
if ($el.length !== 1) {
|
||||
@@ -46,6 +47,7 @@ function editAndTrigger($el, value, events) {
|
||||
*
|
||||
* @param {jQuery} $el
|
||||
* @param {string|number} value
|
||||
* @returns {Promise}
|
||||
*/
|
||||
function editInput($el, value) {
|
||||
return editAndTrigger($el, value, ['input']);
|
||||
@@ -61,10 +63,10 @@ function editInput($el, value) {
|
||||
*
|
||||
* @param {jQuery} $el
|
||||
* @param {string|number} value
|
||||
* @returns {Promise}
|
||||
*/
|
||||
function editSelect($el, value) {
|
||||
editAndTrigger($el, value, ['change']);
|
||||
return concurrency.delay(0);
|
||||
return editAndTrigger($el, value, ['change']);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -112,6 +114,7 @@ function clickM2OHighlightedItem(fieldName, selector) {
|
||||
*
|
||||
* @param {string} fieldName
|
||||
* @param {string} searchText
|
||||
* @returns {Promise}
|
||||
*/
|
||||
function clickM2OItem(fieldName, searchText) {
|
||||
var m2oSelector = `.o_field_many2one[name=${fieldName}] input`;
|
||||
|
||||
@@ -1036,7 +1036,7 @@ QUnit.module('Views', {
|
||||
keyCode: $.ui.keyCode.ENTER,
|
||||
which: $.ui.keyCode.ENTER,
|
||||
};
|
||||
testUtils.fields.editAndTrigger(
|
||||
await testUtils.fields.editAndTrigger(
|
||||
kanban.$('.o_kanban_quick_create').find('input[name=foo]'),
|
||||
'new partner',
|
||||
['input', $.Event('keydown', enterEvent), $.Event('keydown', enterEvent)]
|
||||
@@ -1189,7 +1189,7 @@ QUnit.module('Views', {
|
||||
which: $.ui.keyCode.ENTER,
|
||||
};
|
||||
|
||||
testUtils.fields.editAndTrigger(
|
||||
await testUtils.fields.editAndTrigger(
|
||||
kanban.$('.o_kanban_quick_create').find('input[name=foo]'),
|
||||
'new partner',
|
||||
['input', $.Event('keydown', enterEvent), $.Event('keydown', enterEvent)]
|
||||
|
||||
@@ -969,7 +969,7 @@ QUnit.module('Views', {
|
||||
n += 1;
|
||||
});
|
||||
await testUtils.dom.click($td);
|
||||
testUtils.fields.editInput($td.find('input'), 'abc');
|
||||
await testUtils.fields.editInput($td.find('input'), 'abc');
|
||||
assert.strictEqual(n, 1, "field_changed should have been triggered");
|
||||
await testUtils.dom.click(list.$('td:not(.o_list_record_selector)').eq(2));
|
||||
assert.strictEqual(n, 1, "field_changed should not have been triggered");
|
||||
|
||||
Reference in New Issue
Block a user