173 lines
6.4 KiB
JavaScript
173 lines
6.4 KiB
JavaScript
|
/** @odoo-module **/
|
||
|
|
||
|
import { TEST_USER_IDS } from "@bus/../tests/helpers/test_constants";
|
||
|
import { patchWebsocketWorkerWithCleanup } from "@bus/../tests/helpers/mock_websocket";
|
||
|
|
||
|
import { patch } from "@web/core/utils/patch";
|
||
|
import { MockServer } from "@web/../tests/helpers/mock_server";
|
||
|
import { registry } from "@web/core/registry";
|
||
|
|
||
|
QUnit.testDone(() => {
|
||
|
const callbackRegistry = registry.category("mock_server_websocket_callbacks");
|
||
|
callbackRegistry.getEntries().map(([key]) => callbackRegistry.remove(key));
|
||
|
});
|
||
|
|
||
|
patch(MockServer.prototype, {
|
||
|
init() {
|
||
|
super.init(...arguments);
|
||
|
Object.assign(this, TEST_USER_IDS);
|
||
|
const self = this;
|
||
|
this.notificationQueue = [];
|
||
|
this.websocketWorker = patchWebsocketWorkerWithCleanup({
|
||
|
_sendToServer(message) {
|
||
|
self._performWebsocketRequest(message);
|
||
|
super._sendToServer(message);
|
||
|
},
|
||
|
});
|
||
|
this.lastBusNotificationId = 0;
|
||
|
this.channelsByUser = {};
|
||
|
for (const modelName in this.models) {
|
||
|
const records = Array.isArray(this.models[modelName].records)
|
||
|
? this.models[modelName].records
|
||
|
: [];
|
||
|
for (const record of records) {
|
||
|
Object.defineProperty(record, "__model", { value: modelName });
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
|
||
|
mockCreate(modelName, valsList, kwargs = {}) {
|
||
|
const result = super.mockCreate(modelName, valsList, kwargs);
|
||
|
const returnArrayOfIds = Array.isArray(result);
|
||
|
const recordIds = Array.isArray(result) ? result : [result];
|
||
|
for (const recordId of recordIds) {
|
||
|
const record = this.models[modelName].records.find((r) => r.id === recordId);
|
||
|
Object.defineProperty(record, "__model", { value: modelName });
|
||
|
}
|
||
|
return returnArrayOfIds ? recordIds : recordIds[0];
|
||
|
},
|
||
|
|
||
|
mockSearchRead(modelName, domain, kwargs = {}) {
|
||
|
const records = super.mockSearchRead(modelName, domain, kwargs);
|
||
|
for (const record of records) {
|
||
|
Object.defineProperty(record, "__model", { value: modelName });
|
||
|
}
|
||
|
return records;
|
||
|
},
|
||
|
|
||
|
//--------------------------------------------------------------------------
|
||
|
// Private
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
/**
|
||
|
* @param {Object} message Message sent through the websocket to the
|
||
|
* server.
|
||
|
* @param {string} [message.event_name]
|
||
|
* @param {any} [message.data]
|
||
|
*/
|
||
|
_performWebsocketRequest({ event_name, data }) {
|
||
|
if (event_name === "update_presence") {
|
||
|
const { inactivity_period, im_status_ids_by_model } = data;
|
||
|
this._mockIrWebsocket__updatePresence(inactivity_period, im_status_ids_by_model);
|
||
|
} else if (event_name === "subscribe") {
|
||
|
const { channels } = data;
|
||
|
this.channelsByUser[this.pyEnv?.currentUser] = this.pyEnv
|
||
|
? this._mockIrWebsocket__buildBusChannelList(channels)
|
||
|
: channels;
|
||
|
}
|
||
|
const callbackFn = registry
|
||
|
.category("mock_server_websocket_callbacks")
|
||
|
.get(event_name, null);
|
||
|
if (callbackFn) {
|
||
|
callbackFn(data);
|
||
|
}
|
||
|
},
|
||
|
/**
|
||
|
* Simulates `_sendone` on `bus.bus`.
|
||
|
*
|
||
|
* @param {string} channel
|
||
|
* @param {string} notificationType
|
||
|
* @param {any} message
|
||
|
*/
|
||
|
_mockBusBus__sendone(channel, notificationType, message) {
|
||
|
this._mockBusBus__sendmany([[channel, notificationType, message]]);
|
||
|
},
|
||
|
/**
|
||
|
* Simulates `_sendmany` on `bus.bus`.
|
||
|
*
|
||
|
* @param {Array} notifications
|
||
|
*/
|
||
|
_mockBusBus__sendmany(notifications) {
|
||
|
if (!notifications.length) {
|
||
|
return;
|
||
|
}
|
||
|
const values = [];
|
||
|
const authenticatedUserId =
|
||
|
"res.users" in this.models
|
||
|
? this.pyEnv.cookie.get("authenticated_user_sid")
|
||
|
: undefined;
|
||
|
const authenticatedUser = authenticatedUserId
|
||
|
? this.pyEnv["res.users"].searchRead([["id", "=", authenticatedUserId]], {
|
||
|
context: { active_test: false },
|
||
|
})[0]
|
||
|
: null;
|
||
|
const channels =
|
||
|
this.channelsByUser[authenticatedUser] ?? this._mockIrWebsocket__buildBusChannelList();
|
||
|
notifications = notifications.filter(([target]) =>
|
||
|
channels.some((channel) => {
|
||
|
if (typeof target === "string") {
|
||
|
return channel === target;
|
||
|
}
|
||
|
if (Array.isArray(channel) !== Array.isArray(target)) {
|
||
|
return false;
|
||
|
}
|
||
|
if (Array.isArray(channel)) {
|
||
|
const { __model: cModel, id: cId } = channel[0];
|
||
|
const { __model: tModel, id: tId } = target[0];
|
||
|
return cModel === tModel && cId === tId && channel[1] === target[1];
|
||
|
}
|
||
|
return channel?.__model === target.__model && channel?.id === target?.id;
|
||
|
})
|
||
|
);
|
||
|
if (notifications.length === 0) {
|
||
|
return;
|
||
|
}
|
||
|
for (const notification of notifications) {
|
||
|
const [type, payload] = notification.slice(1, notification.length);
|
||
|
values.push({ id: ++this.lastBusNotificationId, message: { payload, type } });
|
||
|
}
|
||
|
this.notificationQueue.push(...values);
|
||
|
if (this.notificationQueue.length === values.length) {
|
||
|
this._planNotificationSending();
|
||
|
}
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Helper to send the pending notifications to the client. This method is
|
||
|
* push to the micro task queue to simulate server-side batching of
|
||
|
* notifications.
|
||
|
*/
|
||
|
_planNotificationSending() {
|
||
|
queueMicrotask(() => {
|
||
|
if (this.debug) {
|
||
|
console.group("%c[BUS]", "color: #c6e; font-weight: bold;");
|
||
|
for (const { message } of this.notificationQueue) {
|
||
|
console.log(message.type, message.payload);
|
||
|
}
|
||
|
console.groupEnd();
|
||
|
}
|
||
|
this.websocketWorker.broadcast("notification", this.notificationQueue);
|
||
|
this.notificationQueue = [];
|
||
|
});
|
||
|
},
|
||
|
/**
|
||
|
* Simulate the lost of the connection by simulating a closeEvent on
|
||
|
* the worker websocket.
|
||
|
*
|
||
|
* @param {number} closeCode the code to close the connection with.
|
||
|
*/
|
||
|
_simulateConnectionLost(closeCode) {
|
||
|
this.websocketWorker.websocket.close(closeCode);
|
||
|
},
|
||
|
});
|