[REF] web: properly confirm value change of debounced input field

Input fields notify their changes when the user is typing text at a
rate of max 1 per second (by default) and when a change is confirmed.
This last case was handled by listening to the "focusout" event. The
"change" event is however more suited to handle that.

This commit also solves a problem with the related test. Before this
commit, the test triggered some "input" events on an <input/>, then
waited some time so that the debounced action is executed, then
restarted triggering events... the problem is that this "some time" was
long enough to trigger the onchange but not necessarily long enough
to be sure another onchange can be started. Indeed, the mockserver get
rid of RPC delays but not of CPU delay. As two subsequent onchange
cannot be done at the same time (the second one is done after the first
one is finished (see Mutex)) and that this test simulates a 3ms delay
for the onchange, there is no guarantee that waiting x ms (even > 3ms)
will be enough, indeed sometimes we had this sequence:

1) trigger "input" event
2) trigger "input" event
3) start delay of 75ms
4) debouncing time (3ms) is finished: call `_setValue`
5) basic model mutex (already resolved)
6) start async onchange RPC
7) (1) -> (6) took more than 75ms CPU time, the delay started in (3)
   is finished: trigger "change" event
8) call `_setValue` (no debouncing of "change" events)
9) basic model mutex... NOT resolved as the async onchange RPC
   started in (6) did not had the opportunity to continue its
   call sequence (no matter the time it would take)

To solve this problem, the 75ms delay had to be replaced by a non
arbitrary waiting: the mutex resolution. As the test does not know
about this mutex, the only way to be sure it is finished is to wait
for the `mockRPC` to be finished thanks to a deferred and then wait
for another callstack to be completed thanks to a 0ms delay.
This commit is contained in:
qsm-odoo
2017-04-11 19:44:38 +02:00
committed by Aaron Bohy
parent c60743428d
commit ef52914ea2
2 changed files with 17 additions and 8 deletions
@@ -30,7 +30,7 @@ var _t = core._t;
var InputField = AbstractField.extend({
events: _.extend({}, AbstractField.prototype.events, {
'input': '_onInput',
'focusout': '_onFocusout',
'change': '_onChange',
}),
/**
@@ -137,11 +137,12 @@ var InputField = AbstractField.extend({
//--------------------------------------------------------------------------
/**
* We immediately notify the outside world when this input loses focus.
* We immediately notify the outside world when this input confirms its
* changes.
*
* @private
*/
_onFocusout: function () {
_onChange: function () {
this._setValue(this.$input.val());
},
/**
+13 -5
View File
@@ -790,7 +790,7 @@ QUnit.module('Views', {
res_id: 1,
mockRPC: function (route, args) {
if (args.method === "onchange") {
assert.deepEqual(args.args[3],
assert.deepEqual(args.args[3],
{"foo": "1", "p": "", "p.bar": "", "p.display_name": "", "p.product_id": "", "timmy": "", "timmy.name": ""},
"should send only the fields used in the views");
}
@@ -2003,6 +2003,7 @@ QUnit.module('Views', {
obj.int_field = obj.foo.length + 1000;
},
};
var def = $.Deferred();
var form = createView({
View: FormView,
model: 'partner',
@@ -2017,6 +2018,7 @@ QUnit.module('Views', {
if (args.method === 'onchange') {
onchangeNbr++;
return concurrency.delay(3).then(function () {
def.resolve();
return result;
});
}
@@ -2031,16 +2033,16 @@ QUnit.module('Views', {
form.$('input').first().val("12").trigger('input');
assert.strictEqual(onchangeNbr, 0, "no onchange has been called yet");
return concurrency.delay(75).then(function () {
return waitForFinishedOnChange().then(function () {
assert.strictEqual(onchangeNbr, 1, "one onchange has been called");
// add something in the input, then focus another input
form.$('input').first().val("123").trigger('input');
form.$('input').first().focusout();
form.$('input').first().change();
assert.strictEqual(onchangeNbr, 2,
"one onchange has been called immediately");
return concurrency.delay(75);
return waitForFinishedOnChange();
}).then(function () {
assert.strictEqual(onchangeNbr, 2,
"no extra onchange should have been called");
@@ -2048,7 +2050,13 @@ QUnit.module('Views', {
form.destroy();
done();
});
});
function waitForFinishedOnChange() {
return def.then(function () {
def = $.Deferred();
return concurrency.delay(0);
});
}
});
});
});