diff options
| author | Alexandre Kühn <aku@odoo.com> | 2018-08-11 21:02:55 +0200 |
|---|---|---|
| committer | Alexandre Kühn <aku@odoo.com> | 2018-08-13 14:17:40 +0200 |
| commit | 5f1d32f3fb89f40740b25491caafb9a5710d555a (patch) | |
| tree | d94aab4d0a6fb36d60079391216b3dd4c89b23c7 /addons/mail/static/tests/models/cc_throttle_function_tests.js | |
| parent | 614f9f84d7da281b0feacc39a612c19193f3a4d8 (diff) | |
[IMP] mail, im_livechat: 'is typing...' indicator on mail channels
With this commit, most threads show an indicator when a member is typing
something. e.g.
"Mitchell Stephens is typing..."
Supported threads:
- DM
- (public & private) channels
- Livechat (Website & Backend)
Unsupported threads:
- mailboxes (e.g. 'Inbox')
- document thread (= 'Chatter')
- support channel (with `im_support` module)
Some technical details:
- This is a mix between 'start'/'stop' and 'regular notify' strategies.
- On typing text in composer: notify 'start typing' to all members of
thread.
- On clearing text in composer: notify 'stop typing' to all members of
thread.
- A member can only notify once every 2.5 seconds to the server when he
starts or stops typing something (throttled, buffered notify call).
- On receiving a message from someone that is typing: determine that he is
no longer typing.
- On non-empty and unchanged composer text for 5.0 seconds: automatically
notifies all members of thread that he is no longer typing anything.
(This is like a 'soft / cooperative sender' timeout).
- On non-receiving a typing notification of someone after 60 seconds:
determine that he is not longer typing something (This is like a
'hard / receiver assumption' timeout).
- When some types for more than 50 seconds straight: notify that he is
still typing something (again and again every 50 seconds).
- When two or more people are typing something (Multi-User channels), it
displays at most 2 typing users (ordering rule: longer typers first):
"Mitchell Stephens, Marc Brown and more are typing..."
- Timing summary:
- Usually 2.5 seconds uncertainty.
- Up to 5.0 seconds uncertainty on typer inactivity.
- Up to 60 seconds uncertainty on typer page reload / browser tab
closed / etc.
Future Improvements:
- Detect page reload or browser tab close with loose of longpolling
connection
Task-ID 28188
Diffstat (limited to 'addons/mail/static/tests/models/cc_throttle_function_tests.js')
| -rw-r--r-- | addons/mail/static/tests/models/cc_throttle_function_tests.js | 155 |
1 files changed, 155 insertions, 0 deletions
diff --git a/addons/mail/static/tests/models/cc_throttle_function_tests.js b/addons/mail/static/tests/models/cc_throttle_function_tests.js new file mode 100644 index 00000000000..6f74dcfe8d2 --- /dev/null +++ b/addons/mail/static/tests/models/cc_throttle_function_tests.js @@ -0,0 +1,155 @@ +odoo.define('mail.model.CCThrottleFunctionTests', function (require) { +"use strict"; + +var CCThrottleFunction = require('mail.model.CCThrottleFunction'); +var CCThrottleFunctionObject = require('mail.model.CCThrottleFunctionObject'); + +var testUtils = require('web.test_utils'); +var Widget = require('web.Widget'); + +QUnit.module('mail', { + beforeEach: function () { + + var self = this; + this.timeoutCDDef = $.Deferred(); + + this.patch = function () { + self.ORIGINAL_CCTFO_ON_CD_TIMEOUT = CCThrottleFunctionObject.prototype._onCooldownTimeout; + CCThrottleFunctionObject.prototype._onCooldownTimeout = function () { + self.timeoutCDDef.then(self.ORIGINAL_CCTFO_ON_CD_TIMEOUT.apply(this, arguments)); + }; + }; + this.unpatch = function () { + CCThrottleFunctionObject.prototype._onCooldownTimeout = self.ORIGINAL_CCTFO_ON_CD_TIMEOUT; + }; + this.patch(); + }, + afterEach: function () { + this.unpatch(); + }, +}, function () { + +QUnit.module('model', {}, function () { +QUnit.module('CC Throttle Function'); + +QUnit.test('cancel()', function (assert) { + var done = assert.async(); + assert.expect(3); + + var self = this; + var def = $.Deferred(); + var step = 1; + + var widget = new Widget(); + testUtils.addMockEnvironment(widget, { + mockRPC: function (route, args) { + if (args.method === '__rpc__') { + assert.step(args.method + args.args[0]); + + if (step === 1) { + def.resolve(); + } + else { + assert.notOk(true, "should not perform rpc more than once"); + } + step++; + + return $.when(); + } + return this._super.apply(this, arguments); + }, + }); + + var func = function () { + widget._rpc({ + method: '__rpc__', + args: arguments, + }); + }; + + var cctFunc = CCThrottleFunction({ + duration: 0, + func: func, + }); + + cctFunc(1); + + def.then(function () { + assert.verifySteps(['__rpc__1'], "function should have been called once"); + }).then(function () { + cctFunc(2); + cctFunc.cancel(); + self.timeoutCDDef.resolve(); + }); + this.timeoutCDDef.then(function () { + assert.verifySteps(['__rpc__1'], + "function should still have been called once after 2nd function call cancelled"); + done(); + }); +}); + +QUnit.test('clear()', function (assert) { + var done = assert.async(); + assert.expect(5); + + var self = this; + var def1 = $.Deferred(); + var def2 = $.Deferred(); + var step = 1; + + var widget = new Widget(); + testUtils.addMockEnvironment(widget, { + mockRPC: function (route, args) { + if (args.method === '__rpc__') { + assert.step(args.method + args.args[0]); + + if (step === 1) { + def1.resolve(); + } else if (step === 2) { + def2.resolve(); + } else { + assert.notOk(true, "should not perform rpc more than twice"); + } + step++; + + return $.when(); + } + return this._super.apply(this, arguments); + }, + }); + + var func = function () { + widget._rpc({ + method: '__rpc__', + args: arguments, + }); + }; + + var cctFunc = CCThrottleFunction({ + duration: 1000*1000, + func: func, + }); + + cctFunc(1); + + def1.then(function () { + assert.verifySteps(['__rpc__1'], "function should have been called once"); + }).then(function () { + cctFunc(2); + }).then(function () { + assert.verifySteps(['__rpc__1'], + "function should still have been called once (due to long throttle)"); + cctFunc.clear(); + self.timeoutCDDef.resolve(); + def2.resolve(); + }); + $.when(self.timeoutCDDef, def2).then(function () { + assert.verifySteps(['__rpc__1', '__rpc__2'], + "function should have been called twice after 'clear' (buffered 2nd function call)"); + done(); + }); +}); + +}); +}); +}); |
