[IMP] mail: call tests

Part-of: odoo/odoo#99701
This commit is contained in:
Thanh Dodeur
2022-09-15 17:18:22 +02:00
parent 63a5822cda
commit 1c8fd08e7b
7 changed files with 378 additions and 12 deletions
@@ -19,14 +19,14 @@
</label>
</div>
<div class="o_CallSettingsMenu_option mb-3 d-flex align-items-center flex-wrap">
<label class="o_CallSettingsMenu_optionLabel d-flex align-items-center flex-wrap mw-100 cursor-pointer" title="Enable Push-to-talk" aria-label="Enable Push-to-talk">
<label class="o_CallSettingsMenu_optionLabel o_CallSettingsMenu_pushToTalkOption d-flex align-items-center flex-wrap mw-100 cursor-pointer" title="Enable Push-to-talk" aria-label="Enable Push-to-talk">
<input type="checkbox" aria-label="toggle push-to-talk" title="toggle push-to-talk" t-on-change="callSettingsMenu.onChangePushToTalk" t-att-checked="messaging.userSetting.usePushToTalk ? 'checked' : ''"/>
<span class="o_CallSettingsMenu_optionName ms-2 text-truncate">Enable Push-to-talk</span>
</label>
</div>
<t t-if="messaging.userSetting.usePushToTalk">
<div class="o_CallSettingsMenu_option mb-3 d-flex align-items-center flex-wrap">
<label class="o_CallSettingsMenu_optionLabel d-flex align-items-center flex-wrap mw-100 cursor-pointer" title="Push-to-talk key" aria-label="Push-to-talk key">
<label class="o_CallSettingsMenu_optionLabel o_CallSettingsMenu_pushToTalkKeyOption d-flex align-items-center flex-wrap mw-100 cursor-pointer" title="Push-to-talk key" aria-label="Push-to-talk key">
<span class="o_CallSettingsMenu_optionName me-2 text-truncate">Push-to-talk key</span>
<span class="o_CallSettingsMenu_optionPushToTalkGroup d-flex">
<t t-if="messaging.userSetting.pushToTalkKey">
@@ -46,7 +46,7 @@
</div>
<div t-if="callSettingsMenu.userSetting.isRegisteringKey">Press a key to register it as the push-to-talk shortcut</div>
<div class="o_CallSettingsMenu_option mb-3 d-flex align-items-center flex-wrap">
<label class="o_CallSettingsMenu_optionLabel d-flex align-items-center flex-wrap mw-100 cursor-pointer" title="Delay after releasing push-to-talk" aria-label="Delay after releasing push-to-talk">
<label class="o_CallSettingsMenu_optionLabel o_CallSettingsMenu_pushToTalkDelayOption d-flex align-items-center flex-wrap mw-100 cursor-pointer" title="Delay after releasing push-to-talk" aria-label="Delay after releasing push-to-talk">
<span class="o_CallSettingsMenu_optionName me-2 text-truncate">Delay after releasing push-to-talk</span>
<div class="o_CallSettingsMenu_optionInputGroup d-flex w-100">
<input class="o_CallSettingsMenu_optionInputGroupInput" type="range" min="1" max="2000" step="1" t-att-value="messaging.userSetting.voiceActiveDuration" t-on-change="callSettingsMenu.onChangeDelay"/>
@@ -56,7 +56,7 @@
</t>
<t t-else="">
<div class="o_CallSettingsMenu_option mb-3 d-flex align-items-center flex-wrap">
<label class="o_CallSettingsMenu_optionLabel d-flex align-items-center flex-wrap mw-100 cursor-pointer" title="Voice detection threshold" aria-label="Voice detection threshold">
<label class="o_CallSettingsMenu_optionLabel o_CallSettingsMenu_voiceThresholdOption d-flex align-items-center flex-wrap mw-100 cursor-pointer" title="Voice detection threshold" aria-label="Voice detection threshold">
<span class="o_CallSettingsMenu_optionName me-2 text-truncate">Voice detection threshold</span>
<div class="o_CallSettingsMenu_optionInputGroup d-flex w-100">
<input class="o_CallSettingsMenu_optionInputGroupInput" type="range" min="0.001" max="1" step="0.001" t-att-value="messaging.userSetting.voiceActivationThreshold" t-on-change="callSettingsMenu.onChangeThreshold"/>
@@ -68,14 +68,14 @@
<div class="o_CallSettingsMenu_category d-flex flex-column px-3 overflow-auto">
<div class="o_CallSettingsMenu_categoryTitle py-2 fw-bolder text-700 text-truncate text-uppercase">Video Settings</div>
<div class="o_CallSettingsMenu_option mb-3 d-flex align-items-center flex-wrap">
<label class="o_CallSettingsMenu_optionLabel d-flex align-items-center flex-wrap mw-100 cursor-pointer" title="Show video participants only" aria-label="Show video participants only">
<label class="o_CallSettingsMenu_optionLabel o_CallSettingsMenu_showOnlyVideoOption d-flex align-items-center flex-wrap mw-100 cursor-pointer" title="Show video participants only" aria-label="Show video participants only">
<input type="checkbox" aria-label="toggle push-to-talk" title="Show video participants only" t-on-change="callSettingsMenu.onChangeVideoFilterCheckbox" t-att-checked="callSettingsMenu.thread.channel.showOnlyVideo ? 'checked' : ''"/>
<span class="o_CallSettingsMenu_optionName ms-2 text-truncate">Show video participants only</span>
</label>
</div>
<t t-if="messaging.device.hasCanvasFilterSupport">
<div class="o_CallSettingsMenu_option mb-3 d-flex align-items-center flex-wrap">
<label class="o_CallSettingsMenu_optionLabel d-flex align-items-center flex-wrap mw-100 cursor-pointer" title="Blur video background" aria-label="Blur video background">
<label class="o_CallSettingsMenu_optionLabel o_CallSettingsMenu_blurOption d-flex align-items-center flex-wrap mw-100 cursor-pointer" title="Blur video background" aria-label="Blur video background">
<input type="checkbox" aria-label="Blur video background" title="Blur video background" t-on-change="callSettingsMenu.onChangeBlur" t-att-checked="messaging.userSetting.useBlur ? 'checked' : ''"/>
<span class="o_CallSettingsMenu_optionName ms-2 text-truncate">Blur video background</span>
</label>
@@ -83,7 +83,7 @@
</t>
<t t-if="messaging.userSetting.useBlur">
<div class="o_CallSettingsMenu_option mb-3 d-flex align-items-center flex-wrap">
<label class="o_CallSettingsMenu_optionLabel d-flex align-items-center flex-wrap mw-100 cursor-pointer" title="Background blur intensity" aria-label="Background blur intensity">
<label class="o_CallSettingsMenu_optionLabel o_CallSettingsMenu_backgroundBlurIntensityOption d-flex align-items-center flex-wrap mw-100 cursor-pointer" title="Background blur intensity" aria-label="Background blur intensity">
<span class="o_CallSettingsMenu_optionName me-2 text-truncate">Background blur intensity</span>
<div class="o_CallSettingsMenu_optionInputGroup d-flex w-100">
<input class="o_CallSettingsMenu_optionInputGroupInput" type="range" min="0" max="20" step="1" t-att-value="messaging.userSetting.backgroundBlurAmount" t-on-change="callSettingsMenu.onChangeBackgroundBlurAmount"/>
@@ -91,7 +91,7 @@
</label>
</div>
<div class="o_CallSettingsMenu_option mb-3 d-flex align-items-center flex-wrap">
<label class="o_CallSettingsMenu_optionLabel d-flex align-items-center flex-wrap mw-100 cursor-pointer" title="Edge blur intensity" aria-label="Edge blur intensity">
<label class="o_CallSettingsMenu_optionLabel o_CallSettingsMenu_edgeBlurIntensityOption d-flex align-items-center flex-wrap mw-100 cursor-pointer" title="Edge blur intensity" aria-label="Edge blur intensity">
<span class="o_CallSettingsMenu_optionName me-2 text-truncate">Edge blur intensity</span>
<div class="o_CallSettingsMenu_optionInputGroup d-flex w-100">
<input class="o_CallSettingsMenu_optionInputGroupInput" type="range" min="0" max="20" step="1" t-att-value="messaging.userSetting.edgeBlurAmount" t-on-change="callSettingsMenu.onChangeEdgeBlurAmount"/>
@@ -74,7 +74,7 @@
</button>
</t>
<t t-if="threadViewTopbar.thread and threadViewTopbar.thread.hasCallFeature and !threadViewTopbar.threadView.isCallSettingsMenuOpen">
<button class="o_ThreadViewTopbar_button btn px-2 border-none rounded shadow-none" title="Show Call Settings" t-on-click="threadViewTopbar.onClickShowCallSettingsMenu">
<button class="o_ThreadViewTopbar_openCallSettingsButton o_ThreadViewTopbar_button btn px-2 border-none rounded shadow-none" title="Show Call Settings" t-on-click="threadViewTopbar.onClickShowCallSettingsMenu">
<i class="fa fa-lg fa-gear text-700"/>
</button>
</t>
@@ -14,6 +14,18 @@ patch(MockServer.prototype, 'mail/controllers/discuss', {
const context = args.context;
return this._mockRouteMailChannelNotifyTyping(id, is_typing, context);
}
if (route === '/mail/channel/ping') {
return;
}
if (route === '/mail/rtc/channel/join_call') {
return this._mockRouteMailRtcChannelJoinCall(args.channel_id, args.check_rtc_session_ids);
}
if (route === '/mail/rtc/channel/leave_call') {
return this._mockRouteMailRtcChannelLeaveCall(args.channel_id);
}
if (route === '/mail/rtc/session/update_and_broadcast') {
return;
}
return this._super(route, args);
},
/**
@@ -29,4 +41,68 @@ patch(MockServer.prototype, 'mail/controllers/discuss', {
const [memberOfCurrentUser] = this.getRecords('mail.channel.member', [['channel_id', '=', channel_id], ['partner_id', '=', partnerId]]);
this._mockMailChannelMember_NotifyTyping([memberOfCurrentUser.id], is_typing);
},
/**
* Simulates the `/mail/rtc/channel/join_call` route.
*
* @private
* @param {integer} channel_id
* @returns {integer[]} [check_rtc_session_ids]
*/
async _mockRouteMailRtcChannelJoinCall(channel_id, check_rtc_session_ids = []) {
const [currentChannelMember] = this.getRecords('mail.channel.member', [
['channel_id', '=', channel_id],
['partner_id', '=', this.currentPartnerId],
]);
const sessionId = this.pyEnv['mail.channel.rtc.session'].create({
channel_member_id: currentChannelMember.id,
channel_id, // on the server, this is a related field from channel_member_id and not explicitly set
});
const channelMembers = this.getRecords('mail.channel.member', [['channel_id', '=', channel_id]]);
const rtcSessions = this.getRecords('mail.channel.rtc.session', [
['channel_member_id', 'in', channelMembers.map(channelMember => channelMember.id)],
]);
return {
'iceServers': false,
'rtcSessions': [
['insert', rtcSessions.map(rtcSession => this._mockMailChannelRtcSession_MailChannelRtcSessionFormat(rtcSession.id))],
],
'sessionId': sessionId,
};
},
/**
* Simulates the `/mail/rtc/channel/leave_call` route.
*
* @private
* @param {integer} channelId
*/
async _mockRouteMailRtcChannelLeaveCall(channel_id) {
const channelMembers = this.getRecords('mail.channel.member', [['channel_id', '=', channel_id]]);
const rtcSessions = this.getRecords('mail.channel.rtc.session', [
['channel_member_id', 'in', channelMembers.map(channelMember => channelMember.id)],
]);
const notifications = [];
const channelInfo = this._mockMailChannelRtcSession_MailChannelRtcSessionFormatByChannel(rtcSessions.map(rtcSession => rtcSession.id));
for (const [channelId, sessionsData] of Object.entries(channelInfo)) {
const notificationRtcSessions = sessionsData.map((sessionsDataPoint) => {
return { 'id': sessionsDataPoint.id };
});
notifications.push([
channelId,
'mail.channel/rtc_sessions_update',
{
'id': Number(channelId), // JS object keys are strings, but the type from the server is number
'rtcSessions': [['insert-and-unlink', notificationRtcSessions]],
}
]);
}
for (const rtcSession of rtcSessions) {
const target = rtcSession.guest_id || rtcSession.partner_id;
notifications.push([
target,
'mail.channel.rtc.session/ended',
{ 'sessionId': rtcSession.id },
]);
}
this.pyEnv['bus.bus']._sendmany(notifications);
},
});
@@ -0,0 +1,43 @@
/** @odoo-module **/
import { patch } from "@web/core/utils/patch";
import { MockServer } from "@web/../tests/helpers/mock_server";
patch(MockServer.prototype, 'mail/models/mail_channel_rtc_session', {
/**
* Simulates `_mail_rtc_session_format` on `mail.channel.rtc.session`.
*
* @private
* @param {integer} id
* @returns {Object}
*/
_mockMailChannelRtcSession_MailChannelRtcSessionFormat(id) {
const [rtcSession] = this.getRecords('mail.channel.rtc.session', [['id', '=', id]]);
return {
'id': rtcSession.id,
'channelMember': this._mockMailChannelMember_MailChannelMemberFormat([rtcSession.channel_member_id])[0],
'isCameraOn': rtcSession.is_camera_on,
'isDeaf': rtcSession.is_deaf,
'isSelfMuted': rtcSession.is_self_muted,
'isScreenSharingOn': rtcSession.is_screen_sharing_on,
};
},
/**
* Simulates `_mail_rtc_session_format` on `mail.channel.rtc.session`.
*
* @private
* @param {integer[]} ids
* @returns {Object}
*/
_mockMailChannelRtcSession_MailChannelRtcSessionFormatByChannel(ids) {
const rtcSessions = this.getRecords('mail.channel.rtc.session', [['id', 'in', ids]]);
const data = {};
for (const rtcSession of rtcSessions) {
if (!data[rtcSession.channel_id]) {
data[rtcSession.channel_id] = [];
}
data[rtcSession.channel_id].push(this._mockMailChannelRtcSession_MailChannelRtcSessionFormat(rtcSession.id));
}
return data;
}
});
@@ -14,9 +14,9 @@ import {
addModelNamesToFetch([
'mail.activity', 'mail.activity.type', 'mail.channel', 'mail.channel.member',
'mail.followers', 'mail.link.preview', 'mail.message', 'mail.message.subtype',
'mail.notification', 'mail.shortcode', 'mail.template', 'mail.tracking.value',
'res.users.settings', 'res.users.settings.volumes'
'mail.channel.rtc.session', 'mail.followers', 'mail.link.preview', 'mail.message',
'mail.message.subtype', 'mail.notification', 'mail.shortcode', 'mail.template',
'mail.tracking.value', 'res.users.settings', 'res.users.settings.volumes'
]);
addFakeModel('res.fake', {
@@ -0,0 +1,77 @@
/** @odoo-module **/
import {
start,
startServer,
} from '@mail/../tests/helpers/test_utils';
QUnit.module('mail', {}, function () {
QUnit.module('components', {}, function () {
QUnit.module('call_main_view_tests.js');
QUnit.test('Join a call', async function (assert) {
assert.expect(4);
const pyEnv = await startServer();
const mailChannelId = pyEnv['mail.channel'].create({});
const { click, openDiscuss } = await start({
discuss: {
params: {
default_active_id: `mail.channel_${mailChannelId}`,
},
},
});
await openDiscuss();
await click('.o_ThreadViewTopbar_callButton');
assert.containsOnce(
document.body,
'.o_CallView',
"Should have a call view"
);
assert.containsOnce(
document.body,
'.o_CallParticipantCard',
"Should have a call participant card"
);
assert.containsOnce(
document.body,
'.o_CallMainView_controls',
"Should have call controls"
);
assert.containsNone(
document.body,
'.o_ThreadViewTopbar_callButton',
"Should not have a join call button anymore"
);
});
QUnit.test('Leave a call', async function (assert) {
assert.expect(2);
const pyEnv = await startServer();
const mailChannelId = pyEnv['mail.channel'].create({});
const { click, openDiscuss } = await start({
discuss: {
params: {
default_active_id: `mail.channel_${mailChannelId}`,
},
},
});
await openDiscuss();
await click('.o_ThreadViewTopbar_callButton');
assert.containsOnce(
document.body,
'.o_CallActionList_callToggle',
"Should have a button to leave the call"
);
await click('.o_CallActionList_callToggle');
assert.containsNone(
document.body,
'.o_CallView',
"Should not have a call view"
);
});
});
});
@@ -0,0 +1,170 @@
/** @odoo-module **/
import {
start,
startServer,
} from '@mail/../tests/helpers/test_utils';
import { browser } from '@web/core/browser/browser';
import { patchWithCleanup } from '@web/../tests/helpers/utils';
QUnit.module('mail', {}, function () {
QUnit.module('components', {}, function () {
QUnit.module('call_settings_menu_tests.js');
QUnit.test('Renders the call settings', async function (assert) {
assert.expect(9);
patchWithCleanup(browser, {
navigator: {
...browser.navigator,
mediaDevices: {
enumerateDevices: () => Promise.resolve([
{ deviceId: 'mockAudioDeviceId', kind: 'audioinput', label: 'mockAudioDeviceLabel' },
{ deviceId: 'mockVideoDeviceId', kind: 'videoinput', label: 'mockVideoDeviceLabel' },
]),
},
}
});
const pyEnv = await startServer();
const mailChannelId = pyEnv['mail.channel'].create({});
const { click, openDiscuss } = await start({
discuss: {
params: {
default_active_id: `mail.channel_${mailChannelId}`,
},
},
});
await openDiscuss();
await click('.o_ThreadViewTopbar_openCallSettingsButton');
assert.containsOnce(
document.body,
'.o_CallSettingsMenu',
"Should have a call settings menu"
);
assert.containsN(
document.body,
'.o_CallSettingsMenu_option',
5,
"Should have five options",
);
assert.containsOnce(
document.body,
'.o_CallSettingsMenu_optionDeviceSelect',
"should have an audio device selection",
);
assert.containsOnce(
document.body,
'option[value=mockAudioDeviceId]',
"should have an option to select an audio input device",
);
assert.containsNone(
document.body,
'option[value=mockVideoDeviceId]',
"should not have an option to select a video input device",
);
assert.containsOnce(
document.body,
'.o_CallSettingsMenu_pushToTalkOption',
"should have an option to toggle push-to-talk",
);
assert.containsOnce(
document.body,
'.o_CallSettingsMenu_voiceThresholdOption',
"should have an option to set the voice detection threshold",
);
assert.containsOnce(
document.body,
'.o_CallSettingsMenu_showOnlyVideoOption',
"should have an option to filter participants who have no video",
);
assert.containsOnce(
document.body,
'.o_CallSettingsMenu_blurOption',
"should have an option to toggle the background blur feature",
);
});
QUnit.test('activate push to talk', async function (assert) {
assert.expect(3);
patchWithCleanup(browser, {
navigator: {
...browser.navigator,
mediaDevices: {
enumerateDevices: () => Promise.resolve([]),
},
}
});
const pyEnv = await startServer();
const mailChannelId = pyEnv['mail.channel'].create({});
const { click, openDiscuss } = await start({
discuss: {
params: {
default_active_id: `mail.channel_${mailChannelId}`,
},
},
});
await openDiscuss();
await click('.o_ThreadViewTopbar_openCallSettingsButton');
await click('.o_CallSettingsMenu_pushToTalkOption');
assert.containsOnce(
document.body,
'.o_CallSettingsMenu_pushToTalkKeyOption',
"should have an option set the push to talk shortcut key",
);
assert.containsOnce(
document.body,
'.o_CallSettingsMenu_pushToTalkDelayOption',
"should have an option to set the push-to-talk delay",
);
assert.containsNone(
document.body,
'.o_CallSettingsMenu_voiceThresholdOption',
"should not have an option to set the voice detection threshold",
);
});
QUnit.test('activate blur', async function (assert) {
assert.expect(2);
patchWithCleanup(browser, {
navigator: {
...browser.navigator,
mediaDevices: {
enumerateDevices: () => Promise.resolve([]),
},
}
});
const pyEnv = await startServer();
const mailChannelId = pyEnv['mail.channel'].create({});
const { click, openDiscuss } = await start({
discuss: {
params: {
default_active_id: `mail.channel_${mailChannelId}`,
},
},
});
await openDiscuss();
await click('.o_ThreadViewTopbar_openCallSettingsButton');
await click('.o_CallSettingsMenu_blurOption');
assert.containsOnce(
document.body,
'.o_CallSettingsMenu_backgroundBlurIntensityOption',
"should have an option set the background blur intensity",
);
assert.containsOnce(
document.body,
'.o_CallSettingsMenu_edgeBlurIntensityOption',
"should have an option to set the edge blur intensity",
);
});
});
});