253 lines
9.6 KiB
JavaScript
253 lines
9.6 KiB
JavaScript
|
/* @odoo-module */
|
||
|
|
||
|
import { startServer } from "@bus/../tests/helpers/mock_python_environment";
|
||
|
|
||
|
import { Command } from "@mail/../tests/helpers/command";
|
||
|
import { mockGetMedia, start } from "@mail/../tests/helpers/test_utils";
|
||
|
|
||
|
import { browser } from "@web/core/browser/browser";
|
||
|
import { patchWithCleanup } from "@web/../tests/helpers/utils";
|
||
|
import { assertSteps, click, contains, step, triggerEvents } from "@web/../tests/utils";
|
||
|
|
||
|
QUnit.module("call");
|
||
|
|
||
|
QUnit.test("basic rendering", async () => {
|
||
|
mockGetMedia();
|
||
|
const pyEnv = await startServer();
|
||
|
const channelId = pyEnv["discuss.channel"].create({
|
||
|
name: "General",
|
||
|
});
|
||
|
const { openDiscuss } = await start();
|
||
|
openDiscuss(channelId);
|
||
|
await click("[title='Start a Call']");
|
||
|
await contains(".o-discuss-Call");
|
||
|
await contains(".o-discuss-CallParticipantCard[aria-label='Mitchell Admin']");
|
||
|
await contains(".o-discuss-CallActionList");
|
||
|
await contains(".o-discuss-CallMenu-buttonContent");
|
||
|
await contains(".o-discuss-CallActionList button", { count: 5 });
|
||
|
await contains("button[aria-label='Unmute'], button[aria-label='Mute']"); // FIXME depends on current browser permission
|
||
|
await contains(".o-discuss-CallActionList button[aria-label='Deafen']");
|
||
|
await contains(".o-discuss-CallActionList button[aria-label='Turn camera on']");
|
||
|
await contains("[title='More']");
|
||
|
await contains(".o-discuss-CallActionList button[aria-label='Disconnect']");
|
||
|
await click("[title='More']");
|
||
|
await contains("[title='Raise Hand']");
|
||
|
await contains("[title='Share Screen']");
|
||
|
await contains("[title='Enter Full Screen']");
|
||
|
});
|
||
|
|
||
|
QUnit.test("no call with odoobot", async () => {
|
||
|
const pyEnv = await startServer();
|
||
|
const channelId = pyEnv["discuss.channel"].create({
|
||
|
channel_member_ids: [
|
||
|
Command.create({ partner_id: pyEnv.currentPartnerId }),
|
||
|
Command.create({ partner_id: pyEnv.odoobotId }),
|
||
|
],
|
||
|
channel_type: "chat",
|
||
|
});
|
||
|
const { openDiscuss } = await start();
|
||
|
openDiscuss(channelId);
|
||
|
await contains(".o-mail-Discuss-header");
|
||
|
await contains("[title='Start a Call']", { count: 0 });
|
||
|
});
|
||
|
|
||
|
QUnit.test("should not display call UI when no more members (self disconnect)", async () => {
|
||
|
mockGetMedia();
|
||
|
const pyEnv = await startServer();
|
||
|
const channelId = pyEnv["discuss.channel"].create({ name: "General" });
|
||
|
const { openDiscuss } = await start();
|
||
|
openDiscuss(channelId);
|
||
|
await click("[title='Start a Call']");
|
||
|
await contains(".o-discuss-Call");
|
||
|
await click(".o-discuss-CallActionList button[aria-label='Disconnect']");
|
||
|
await contains(".o-discuss-Call", { count: 0 });
|
||
|
});
|
||
|
|
||
|
QUnit.test("show call UI in chat window when in call", async () => {
|
||
|
mockGetMedia();
|
||
|
const pyEnv = await startServer();
|
||
|
pyEnv["discuss.channel"].create({ name: "General" });
|
||
|
await start();
|
||
|
await click(".o_menu_systray i[aria-label='Messages']");
|
||
|
await click(".o-mail-NotificationItem", { text: "General" });
|
||
|
await contains(".o-mail-ChatWindow");
|
||
|
await contains(".o-discuss-Call", { count: 0 });
|
||
|
await click(".o-mail-ChatWindow-command[title='Start a Call']");
|
||
|
await contains(".o-discuss-Call");
|
||
|
await contains(".o-mail-ChatWindow-command[title='Start a Call']", { count: 0 });
|
||
|
});
|
||
|
|
||
|
QUnit.test("should disconnect when closing page while in call", async () => {
|
||
|
mockGetMedia();
|
||
|
const pyEnv = await startServer();
|
||
|
const channelId = pyEnv["discuss.channel"].create({ name: "General" });
|
||
|
const { openDiscuss } = await start();
|
||
|
openDiscuss(channelId);
|
||
|
patchWithCleanup(browser, {
|
||
|
navigator: {
|
||
|
...browser.navigator,
|
||
|
sendBeacon: async (route, data) => {
|
||
|
if (data instanceof Blob && route === "/mail/rtc/channel/leave_call") {
|
||
|
const blobText = await data.text();
|
||
|
const blobData = JSON.parse(blobText);
|
||
|
step(`sendBeacon_leave_call:${blobData.params.channel_id}`);
|
||
|
}
|
||
|
},
|
||
|
},
|
||
|
});
|
||
|
|
||
|
await click("[title='Start a Call']");
|
||
|
await contains(".o-discuss-Call");
|
||
|
// simulate page close
|
||
|
window.dispatchEvent(new Event("pagehide"), { bubble: true });
|
||
|
await assertSteps([`sendBeacon_leave_call:${channelId}`]);
|
||
|
});
|
||
|
|
||
|
QUnit.test("should display invitations", async () => {
|
||
|
const pyEnv = await startServer();
|
||
|
const channelId = pyEnv["discuss.channel"].create({ name: "General" });
|
||
|
const partnerId = pyEnv["res.partner"].create({ name: "InvitationSender" });
|
||
|
const memberId = pyEnv["discuss.channel.member"].create({
|
||
|
channel_id: channelId,
|
||
|
partner_id: partnerId,
|
||
|
});
|
||
|
const sessionId = pyEnv["discuss.channel.rtc.session"].create({
|
||
|
channel_member_id: memberId,
|
||
|
channel_id: channelId,
|
||
|
});
|
||
|
const { env } = await start();
|
||
|
patchWithCleanup(env.services["mail.sound_effects"], {
|
||
|
play(name) {
|
||
|
step(`play - ${name}`);
|
||
|
super.play(...arguments);
|
||
|
},
|
||
|
stop(name) {
|
||
|
step(`stop - ${name}`);
|
||
|
super.stop(...arguments);
|
||
|
},
|
||
|
});
|
||
|
pyEnv["bus.bus"]._sendone(pyEnv.currentPartner, "mail.record/insert", {
|
||
|
Thread: {
|
||
|
id: channelId,
|
||
|
model: "discuss.channel",
|
||
|
rtcInvitingSession: { id: sessionId, channelMember: { id: memberId } },
|
||
|
},
|
||
|
});
|
||
|
await contains(".o-discuss-CallInvitation");
|
||
|
await assertSteps(["play - incoming-call"]);
|
||
|
// Simulate stop receiving call invitation
|
||
|
pyEnv["bus.bus"]._sendone(pyEnv.currentPartner, "mail.record/insert", {
|
||
|
Thread: {
|
||
|
id: channelId,
|
||
|
model: "discuss.channel",
|
||
|
rtcInvitingSession: false,
|
||
|
},
|
||
|
});
|
||
|
await contains(".o-discuss-CallInvitation", { count: 0 });
|
||
|
await assertSteps(["stop - incoming-call"]);
|
||
|
});
|
||
|
|
||
|
QUnit.test("can share screen", async () => {
|
||
|
mockGetMedia();
|
||
|
const pyEnv = await startServer();
|
||
|
const channelId = pyEnv["discuss.channel"].create({
|
||
|
name: "General",
|
||
|
});
|
||
|
const { openDiscuss } = await start();
|
||
|
openDiscuss(channelId);
|
||
|
await click("[title='Start a Call']");
|
||
|
await click("[title='More']");
|
||
|
await click("[title='Share Screen']");
|
||
|
await contains("video");
|
||
|
await triggerEvents(".o-discuss-Call-mainCards", ["mousemove"]); // show overlay
|
||
|
await click("[title='More']");
|
||
|
await click("[title='Stop Sharing Screen']");
|
||
|
await contains("video", { count: 0 });
|
||
|
});
|
||
|
|
||
|
QUnit.test("can share user camera", async () => {
|
||
|
mockGetMedia();
|
||
|
const pyEnv = await startServer();
|
||
|
const channelId = pyEnv["discuss.channel"].create({
|
||
|
name: "General",
|
||
|
});
|
||
|
const { openDiscuss } = await start();
|
||
|
openDiscuss(channelId);
|
||
|
await click("[title='Start a Call']");
|
||
|
await click("[title='Turn camera on']");
|
||
|
await contains("video");
|
||
|
await click("[title='Stop camera']");
|
||
|
await contains("video", { count: 0 });
|
||
|
});
|
||
|
|
||
|
QUnit.test("Create a direct message channel when clicking on start a meeting", async () => {
|
||
|
mockGetMedia();
|
||
|
const { openDiscuss } = await start();
|
||
|
openDiscuss();
|
||
|
await click("button", { text: "Start a meeting" });
|
||
|
await contains(".o-mail-DiscussSidebarChannel", { text: "Mitchell Admin" });
|
||
|
await contains(".o-discuss-Call");
|
||
|
await contains(".o-discuss-ChannelInvitation");
|
||
|
});
|
||
|
|
||
|
QUnit.test("Can share user camera and screen together", async () => {
|
||
|
mockGetMedia();
|
||
|
const pyEnv = await startServer();
|
||
|
const channelId = pyEnv["discuss.channel"].create({
|
||
|
name: "General",
|
||
|
});
|
||
|
const { openDiscuss } = await start();
|
||
|
openDiscuss(channelId);
|
||
|
await click("[title='Start a Call']");
|
||
|
await click("[title='More']");
|
||
|
await click("[title='Share Screen']");
|
||
|
await click("[title='Turn camera on']");
|
||
|
await contains("video", { count: 2 });
|
||
|
});
|
||
|
|
||
|
QUnit.test("Click on inset card should replace the inset and active stream together", async () => {
|
||
|
mockGetMedia();
|
||
|
const pyEnv = await startServer();
|
||
|
const channelId = pyEnv["discuss.channel"].create({
|
||
|
name: "General",
|
||
|
});
|
||
|
const { openDiscuss } = await start();
|
||
|
openDiscuss(channelId);
|
||
|
await click("[title='Start a Call']");
|
||
|
await click("[title='More']");
|
||
|
await click("[title='Share Screen']");
|
||
|
await click("[title='Turn camera on']");
|
||
|
await contains("video[type='screen']:not(.o-inset)");
|
||
|
await click("video[type='camera'].o-inset");
|
||
|
await contains("video[type='screen'].o-inset");
|
||
|
await contains("video[type='camera']:not(.o-inset)");
|
||
|
});
|
||
|
|
||
|
QUnit.test("join/leave sounds are only played on main tab", async () => {
|
||
|
mockGetMedia();
|
||
|
const pyEnv = await startServer();
|
||
|
const channelId = pyEnv["discuss.channel"].create({ name: "General" });
|
||
|
const tab1 = await start({ asTab: true });
|
||
|
const tab2 = await start({ asTab: true });
|
||
|
patchWithCleanup(tab1.env.services["mail.sound_effects"], {
|
||
|
play(name) {
|
||
|
step(`tab1 - play - ${name}`);
|
||
|
},
|
||
|
});
|
||
|
patchWithCleanup(tab2.env.services["mail.sound_effects"], {
|
||
|
play(name) {
|
||
|
step(`tab2 - play - ${name}`);
|
||
|
},
|
||
|
});
|
||
|
await tab1.openDiscuss(channelId);
|
||
|
await tab2.openDiscuss(channelId);
|
||
|
await click("[title='Start a Call']", { target: tab1.target });
|
||
|
await contains(".o-discuss-Call", { target: tab1.target });
|
||
|
await contains(".o-discuss-Call", { target: tab2.target });
|
||
|
await assertSteps(["tab1 - play - channel-join"]);
|
||
|
await click("[title='Disconnect']:not([disabled])", { target: tab1.target });
|
||
|
await contains(".o-discuss-Call", { target: tab1.target, count: 0 });
|
||
|
await contains(".o-discuss-Call", { target: tab2.target, count: 0 });
|
||
|
await assertSteps(["tab1 - play - channel-leave"]);
|
||
|
});
|