diff options
| author | stephanchrst <stephanchrst@gmail.com> | 2022-05-10 21:51:50 +0700 |
|---|---|---|
| committer | stephanchrst <stephanchrst@gmail.com> | 2022-05-10 21:51:50 +0700 |
| commit | 3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch) | |
| tree | a44932296ef4a9b71d5f010906253d8c53727726 /addons/bus/static/tests | |
| parent | 0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff) | |
initial commit 2
Diffstat (limited to 'addons/bus/static/tests')
| -rw-r--r-- | addons/bus/static/tests/bus_tests.js | 391 | ||||
| -rw-r--r-- | addons/bus/static/tests/bus_tests_tour.js | 25 |
2 files changed, 416 insertions, 0 deletions
diff --git a/addons/bus/static/tests/bus_tests.js b/addons/bus/static/tests/bus_tests.js new file mode 100644 index 00000000..449e3ba2 --- /dev/null +++ b/addons/bus/static/tests/bus_tests.js @@ -0,0 +1,391 @@ +odoo.define('web.bus_tests', function (require) { +"use strict"; + +var BusService = require('bus.BusService'); +var CrossTabBus = require('bus.CrossTab'); +var AbstractStorageService = require('web.AbstractStorageService'); +var RamStorage = require('web.RamStorage'); +var testUtils = require('web.test_utils'); +var Widget = require('web.Widget'); + + +var LocalStorageServiceMock; + +BusService = BusService.extend({ + TAB_HEARTBEAT_PERIOD: 10, + MASTER_TAB_HEARTBEAT_PERIOD: 1, +}); + + +QUnit.module('Bus', { + beforeEach: function () { + LocalStorageServiceMock = AbstractStorageService.extend({storage: new RamStorage()}); + }, +}, function () { + QUnit.test('notifications received from the longpolling channel', async function (assert) { + assert.expect(6); + + var pollPromise = testUtils.makeTestPromise(); + + var parent = new Widget(); + await testUtils.mock.addMockEnvironment(parent, { + data: {}, + services: { + bus_service: BusService, + local_storage: LocalStorageServiceMock, + }, + mockRPC: function (route, args) { + if (route === '/longpolling/poll') { + assert.step(route + ' - ' + args.channels.join(',')); + + pollPromise = testUtils.makeTestPromise(); + pollPromise.abort = (function () { + this.reject({message: "XmlHttpRequestError abort"}, $.Event()); + }).bind(pollPromise); + return pollPromise; + } + return this._super.apply(this, arguments); + } + }); + + var widget = new Widget(parent); + await widget.appendTo($('#qunit-fixture')); + + widget.call('bus_service', 'onNotification', this, function (notifications) { + assert.step('notification - ' + notifications.toString()); + }); + widget.call('bus_service', 'addChannel', 'lambda'); + + pollPromise.resolve([{ + id: 1, + channel: 'lambda', + message: 'beta', + }]); + await testUtils.nextTick(); + + pollPromise.resolve([{ + id: 2, + channel: 'lambda', + message: 'epsilon', + }]); + await testUtils.nextTick(); + + assert.verifySteps([ + '/longpolling/poll - lambda', + 'notification - lambda,beta', + '/longpolling/poll - lambda', + 'notification - lambda,epsilon', + '/longpolling/poll - lambda', + ]); + + parent.destroy(); + }); + + QUnit.test('provide notification ID of 0 by default', async function (assert) { + // This test is important in order to ensure that we provide the correct + // sentinel value 0 when we are not aware of the last notification ID + // that we have received. We cannot provide an ID of -1, otherwise it + // may likely be handled incorrectly (before this test was written, + // it was providing -1 to the server, which in return sent every stored + // notifications related to this user). + assert.expect(3); + + // Simulate no ID of last notification in the local storage + testUtils.mock.patch(LocalStorageServiceMock, { + getItem: function (key) { + if (key === 'last_ts') { + return 0; + } + return this._super.apply(this, arguments); + }, + }); + + var pollPromise = testUtils.makeTestPromise(); + var parent = new Widget(); + await testUtils.mock.addMockEnvironment(parent, { + data: {}, + services: { + bus_service: BusService, + local_storage: LocalStorageServiceMock, + }, + mockRPC: function (route, args) { + if (route === '/longpolling/poll') { + assert.step(route); + assert.strictEqual(args.last, 0, + "provided last notification ID should be 0"); + + pollPromise = testUtils.makeTestPromise(); + pollPromise.abort = (function () { + this.reject({message: "XmlHttpRequestError abort"}, $.Event()); + }).bind(pollPromise); + return pollPromise; + } + return this._super.apply(this, arguments); + } + }); + + var widget = new Widget(parent); + await widget.appendTo($('#qunit-fixture')); + + // trigger longpolling poll RPC + widget.call('bus_service', 'addChannel', 'lambda'); + assert.verifySteps(['/longpolling/poll']); + + testUtils.mock.unpatch(LocalStorageServiceMock); + parent.destroy(); + }); + + QUnit.test('cross tab bus share message from a channel', async function (assert) { + assert.expect(5); + + // master + + var pollPromiseMaster = testUtils.makeTestPromise(); + + var parentMaster = new Widget(); + await testUtils.mock.addMockEnvironment(parentMaster, { + data: {}, + services: { + bus_service: BusService, + local_storage: LocalStorageServiceMock, + }, + mockRPC: function (route, args) { + if (route === '/longpolling/poll') { + assert.step('master' + ' - ' + route + ' - ' + args.channels.join(',')); + + pollPromiseMaster = testUtils.makeTestPromise(); + pollPromiseMaster.abort = (function () { + this.reject({message: "XmlHttpRequestError abort"}, $.Event()); + }).bind(pollPromiseMaster); + return pollPromiseMaster; + } + return this._super.apply(this, arguments); + } + }); + + var master = new Widget(parentMaster); + await master.appendTo($('#qunit-fixture')); + + master.call('bus_service', 'onNotification', master, function (notifications) { + assert.step('master - notification - ' + notifications.toString()); + }); + master.call('bus_service', 'addChannel', 'lambda'); + + // slave + await testUtils.nextTick(); + var parentSlave = new Widget(); + await testUtils.mock.addMockEnvironment(parentSlave, { + data: {}, + services: { + bus_service: BusService, + local_storage: LocalStorageServiceMock, + }, + mockRPC: function (route, args) { + if (route === '/longpolling/poll') { + throw new Error("Can not use the longpolling of the slave client"); + } + return this._super.apply(this, arguments); + } + }); + + var slave = new Widget(parentSlave); + await slave.appendTo($('#qunit-fixture')); + + slave.call('bus_service', 'onNotification', slave, function (notifications) { + assert.step('slave - notification - ' + notifications.toString()); + }); + slave.call('bus_service', 'addChannel', 'lambda'); + + pollPromiseMaster.resolve([{ + id: 1, + channel: 'lambda', + message: 'beta', + }]); + await testUtils.nextTick(); + + assert.verifySteps([ + 'master - /longpolling/poll - lambda', + 'master - notification - lambda,beta', + 'slave - notification - lambda,beta', + 'master - /longpolling/poll - lambda', + ]); + + parentMaster.destroy(); + parentSlave.destroy(); + }); + + QUnit.test('cross tab bus elect new master on master unload', async function (assert) { + assert.expect(8); + + // master + var pollPromiseMaster = testUtils.makeTestPromise(); + + var parentMaster = new Widget(); + await testUtils.mock.addMockEnvironment(parentMaster, { + data: {}, + services: { + bus_service: BusService, + local_storage: LocalStorageServiceMock, + }, + mockRPC: function (route, args) { + if (route === '/longpolling/poll') { + assert.step('master - ' + route + ' - ' + args.channels.join(',')); + + pollPromiseMaster = testUtils.makeTestPromise(); + pollPromiseMaster.abort = (function () { + this.reject({message: "XmlHttpRequestError abort"}, $.Event()); + }).bind(pollPromiseMaster); + return pollPromiseMaster; + } + return this._super.apply(this, arguments); + } + }); + + var master = new Widget(parentMaster); + await master.appendTo($('#qunit-fixture')); + + master.call('bus_service', 'onNotification', master, function (notifications) { + assert.step('master - notification - ' + notifications.toString()); + }); + master.call('bus_service', 'addChannel', 'lambda'); + + // slave + await testUtils.nextTick(); + var parentSlave = new Widget(); + var pollPromiseSlave = testUtils.makeTestPromise(); + await testUtils.mock.addMockEnvironment(parentSlave, { + data: {}, + services: { + bus_service: BusService, + local_storage: LocalStorageServiceMock, + }, + mockRPC: function (route, args) { + if (route === '/longpolling/poll') { + assert.step('slave - ' + route + ' - ' + args.channels.join(',')); + + pollPromiseSlave = testUtils.makeTestPromise(); + pollPromiseSlave.abort = (function () { + this.reject({message: "XmlHttpRequestError abort"}, $.Event()); + }).bind(pollPromiseSlave); + return pollPromiseSlave; + } + return this._super.apply(this, arguments); + } + }); + + var slave = new Widget(parentSlave); + await slave.appendTo($('#qunit-fixture')); + + slave.call('bus_service', 'onNotification', slave, function (notifications) { + assert.step('slave - notification - ' + notifications.toString()); + }); + slave.call('bus_service', 'addChannel', 'lambda'); + + pollPromiseMaster.resolve([{ + id: 1, + channel: 'lambda', + message: 'beta', + }]); + await testUtils.nextTick(); + + // simulate unloading master + master.call('bus_service', '_onUnload'); + + pollPromiseSlave.resolve([{ + id: 2, + channel: 'lambda', + message: 'gamma', + }]); + await testUtils.nextTick(); + + assert.verifySteps([ + 'master - /longpolling/poll - lambda', + 'master - notification - lambda,beta', + 'slave - notification - lambda,beta', + 'master - /longpolling/poll - lambda', + 'slave - /longpolling/poll - lambda', + 'slave - notification - lambda,gamma', + 'slave - /longpolling/poll - lambda', + ]); + + parentMaster.destroy(); + parentSlave.destroy(); + }); + + QUnit.test('two tabs calling addChannel simultaneously', async function (assert) { + assert.expect(5); + + let id = 1; + testUtils.patch(CrossTabBus, { + init: function () { + this._super.apply(this, arguments); + this.__tabId__ = id++; + }, + addChannel: function (channel) { + assert.step('Tab ' + this.__tabId__ + ': addChannel ' + channel); + this._super.apply(this, arguments); + }, + deleteChannel: function (channel) { + assert.step('Tab ' + this.__tabId__ + ': deleteChannel ' + channel); + this._super.apply(this, arguments); + }, + }); + + let pollPromise; + const parentTab1 = new Widget(); + await testUtils.addMockEnvironment(parentTab1, { + data: {}, + services: { + local_storage: LocalStorageServiceMock, + }, + mockRPC: function (route) { + if (route === '/longpolling/poll') { + pollPromise = testUtils.makeTestPromise(); + pollPromise.abort = (function () { + this.reject({message: "XmlHttpRequestError abort"}, $.Event()); + }).bind(pollPromise); + return pollPromise; + } + return this._super.apply(this, arguments); + } + }); + const parentTab2 = new Widget(); + await testUtils.addMockEnvironment(parentTab2, { + data: {}, + services: { + local_storage: LocalStorageServiceMock, + }, + mockRPC: function (route) { + if (route === '/longpolling/poll') { + pollPromise = testUtils.makeTestPromise(); + pollPromise.abort = (function () { + this.reject({message: "XmlHttpRequestError abort"}, $.Event()); + }).bind(pollPromise); + return pollPromise; + } + return this._super.apply(this, arguments); + } + }); + + const tab1 = new CrossTabBus(parentTab1); + const tab2 = new CrossTabBus(parentTab2); + + tab1.addChannel("alpha"); + tab2.addChannel("alpha"); + tab1.addChannel("beta"); + tab2.addChannel("beta"); + + assert.verifySteps([ + "Tab 1: addChannel alpha", + "Tab 2: addChannel alpha", + "Tab 1: addChannel beta", + "Tab 2: addChannel beta", + ]); + + testUtils.unpatch(CrossTabBus); + parentTab1.destroy(); + parentTab2.destroy(); + }); +}); + +}); diff --git a/addons/bus/static/tests/bus_tests_tour.js b/addons/bus/static/tests/bus_tests_tour.js new file mode 100644 index 00000000..2248e0a1 --- /dev/null +++ b/addons/bus/static/tests/bus_tests_tour.js @@ -0,0 +1,25 @@ +odoo.define("bus.tour", function (require) { + "use strict"; + + const tour = require("web_tour.tour"); + + tour.register("bundle_changed_notification", { + test: true, + url: '/web', + }, [{ + trigger: '.o_web_client', + run() { + const webClient = odoo.__DEBUG__.services['web.web_client']; + const _delayFn = webClient._getBundleNotificationDelay; + webClient._getBundleNotificationDelay = () => 0; + this.call('bus_service', 'trigger', + 'notification', + [[['db_name', 'bundle_changed'], ['web.assets_backend', 'hash']]] + ); + webClient._getBundleNotificationDelay = _delayFn; + } + }, { + trigger: '.o_notification_title:contains(Refresh)', + }] + ); +}); |
