/* Copyright 2026 Element Creations Ltd. SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial Please see LICENSE in the repository root for full details. */ import { expect, test } from "@playwright/test"; import { widgetTest } from "../fixtures/widget-user.ts"; import { TestHelpers } from "./test-helpers.ts"; widgetTest.use({ callType: "dm" }); widgetTest( "Start a new voice call in DM as widget", async ({ asWidget, browserName }) => { test.skip( browserName === "firefox", "The is test is not working on firefox CI environment. No mic/audio device inputs so cam/mic are disabled", ); test.slow(); // Triples the timeout const { brooks, whistler } = asWidget; await TestHelpers.startCallInCurrentRoom(brooks.page, true); await expect( brooks.page.locator('iframe[title="Element Call"]'), ).toBeVisible(); const brooksFrame = brooks.page .locator('iframe[title="Element Call"]') .contentFrame(); // We should show a ringing overlay, let's check for that await expect( brooksFrame.getByText(`Waiting for ${whistler.displayName} to join…`), ).toBeVisible(); await expect(whistler.page.getByText("Incoming voice call")).toBeVisible(); await whistler.page.getByRole("button", { name: "Accept" }).click(); await expect( whistler.page.locator('iframe[title="Element Call"]'), ).toBeVisible(); const whistlerFrame = whistler.page .locator('iframe[title="Element Call"]') .contentFrame(); // ASSERT the button states for whistler (the callee) { // The only way to know if it is muted or not is to look at the data-kind attribute.. const videoButton = whistlerFrame.getByTestId("incall_videomute"); // video should be off by default in a voice call await expect(videoButton).toHaveAttribute("aria-label", /^Start video$/); const audioButton = whistlerFrame.getByTestId("incall_mute"); // audio should be on for the voice call await expect(audioButton).toHaveAttribute( "aria-label", /^Mute microphone$/, ); } // ASSERT the button states for brools (the caller) { // The only way to know if it is muted or not is to look at the data-kind attribute.. const videoButton = brooksFrame.getByTestId("incall_videomute"); // video should be off by default in a voice call await expect(videoButton).toHaveAttribute("aria-label", /^Start video$/); const audioButton = brooksFrame.getByTestId("incall_mute"); // audio should be on for the voice call await expect(audioButton).toHaveAttribute( "aria-label", /^Mute microphone$/, ); } // In order to confirm that the call is disconnected we will check that the message composer is shown again. // So first we need to confirm that it is hidden when in the call. await expect( whistler.page.locator(".mx_BasicMessageComposer"), ).not.toBeVisible(); await expect( brooks.page.locator(".mx_BasicMessageComposer"), ).not.toBeVisible(); // ASSERT hanging up on one side ends the call for both { const hangupButton = brooksFrame.getByTestId("incall_leave"); await hangupButton.click(); } // The widget should be closed on both sides and the timeline should be back on screen await expect( whistler.page.locator(".mx_BasicMessageComposer"), ).toBeVisible(); await expect(brooks.page.locator(".mx_BasicMessageComposer")).toBeVisible(); }, ); widgetTest( "Start a new video call in DM as widget", async ({ asWidget, browserName }) => { test.skip( browserName === "firefox", "The is test is not working on firefox CI environment. No mic/audio device inputs so cam/mic are disabled", ); test.slow(); // Triples the timeout const { brooks, whistler } = asWidget; await TestHelpers.startCallInCurrentRoom(brooks.page, false); await expect( brooks.page.locator('iframe[title="Element Call"]'), ).toBeVisible(); const brooksFrame = brooks.page .locator('iframe[title="Element Call"]') .contentFrame(); // We should show a ringing overlay, let's check for that await expect( brooksFrame.getByText(`Waiting for ${whistler.displayName} to join…`), ).toBeVisible(); await expect(whistler.page.getByText("Incoming video call")).toBeVisible(); await whistler.page.getByRole("button", { name: "Accept" }).click(); await expect( whistler.page.locator('iframe[title="Element Call"]'), ).toBeVisible(); const whistlerFrame = whistler.page .locator('iframe[title="Element Call"]') .contentFrame(); // ASSERT the button states for whistler (the callee) { // The only way to know if it is muted or not is to look at the data-kind attribute.. const videoButton = whistlerFrame.getByTestId("incall_videomute"); // video should be on by default in a voice call await expect(videoButton).toHaveAttribute("aria-label", /^Stop video$/); const audioButton = whistlerFrame.getByTestId("incall_mute"); // audio should be on for the voice call await expect(audioButton).toHaveAttribute( "aria-label", /^Mute microphone$/, ); } // ASSERT the button states for brools (the caller) { // The only way to know if it is muted or not is to look at the data-kind attribute.. const videoButton = brooksFrame.getByTestId("incall_videomute"); // video should be on by default in a voice call await expect(videoButton).toHaveAttribute("aria-label", /^Stop video$/); const audioButton = brooksFrame.getByTestId("incall_mute"); // audio should be on for the voice call await expect(audioButton).toHaveAttribute( "aria-label", /^Mute microphone$/, ); } // In order to confirm that the call is disconnected we will check that the message composer is shown again. // So first we need to confirm that it is hidden when in the call. await expect( whistler.page.locator(".mx_BasicMessageComposer"), ).not.toBeVisible(); await expect( brooks.page.locator(".mx_BasicMessageComposer"), ).not.toBeVisible(); // ASSERT hanging up on one side ends the call for both { const hangupButton = brooksFrame.getByTestId("incall_leave"); await hangupButton.click(); } // The widget should be closed on both sides and the timeline should be back on screen await expect( whistler.page.locator(".mx_BasicMessageComposer"), ).toBeVisible(); await expect(brooks.page.locator(".mx_BasicMessageComposer")).toBeVisible(); }, ); widgetTest( "Decline a new video call in DM as widget", async ({ asWidget, browserName }) => { test.skip( browserName === "firefox", "The is test is not working on firefox CI environment. No mic/audio device inputs so cam/mic are disabled", ); test.slow(); // Triples the timeout const { brooks, whistler } = asWidget; await TestHelpers.startCallInCurrentRoom(brooks.page, false); await expect( brooks.page.locator('iframe[title="Element Call"]'), ).toBeVisible(); const brooksFrame = brooks.page .locator('iframe[title="Element Call"]') .contentFrame(); // We should show a ringing overlay, let's check for that await expect( brooksFrame.getByText(`Waiting for ${whistler.displayName} to join…`), ).toBeVisible(); await expect(whistler.page.getByText("Incoming video call")).toBeVisible(); await whistler.page.getByRole("button", { name: "Decline" }).click(); await expect( whistler.page.locator('iframe[title="Element Call"]'), ).not.toBeVisible(); // The widget should be closed and the timeline should be back on screen await expect( brooks.page.locator('iframe[title="Element Call"]'), ).not.toBeVisible(); await expect( brooks.page.getByText("This is the beginning of your"), ).toBeVisible(); }, );