summaryrefslogtreecommitdiff
path: root/addons/bus/static/tests
diff options
context:
space:
mode:
authorstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
committerstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
commit3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch)
treea44932296ef4a9b71d5f010906253d8c53727726 /addons/bus/static/tests
parent0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff)
initial commit 2
Diffstat (limited to 'addons/bus/static/tests')
-rw-r--r--addons/bus/static/tests/bus_tests.js391
-rw-r--r--addons/bus/static/tests/bus_tests_tour.js25
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)',
+ }]
+ );
+});