All checks were successful
Queue Release Build / prepare (push) Successful in 19s
Deploy Web Apps / deploy (push) Successful in 8m12s
Queue Release Build / build-windows (push) Successful in 27m44s
Queue Release Build / build-linux (push) Successful in 48m1s
Queue Release Build / build-android (push) Successful in 22m7s
Queue Release Build / finalize (push) Successful in 2m42s
95 lines
3.8 KiB
TypeScript
95 lines
3.8 KiB
TypeScript
import { test, expect } from '../../fixtures/multi-client';
|
|
import {
|
|
MULTI_DEVICE_VOICE_CHANNEL,
|
|
channelsSidePanel,
|
|
createMultiDeviceScenario,
|
|
expectCrossDeviceMessage,
|
|
expectActiveVoiceOnDevice,
|
|
expectPassiveVoiceOnDevice,
|
|
logoutFromMenu,
|
|
membersSidePanel,
|
|
passiveVoiceChannelJoinBadge,
|
|
readClientInstanceId,
|
|
uniqueMultiDeviceName
|
|
} from '../../helpers/multi-device-session';
|
|
|
|
test.describe('Multi-device session', () => {
|
|
test.describe.configure({ timeout: 300_000, retries: 1 });
|
|
|
|
test('covers identity, chat sync, typing exclusion, and voice exclusivity', async ({ createClient }) => {
|
|
const scenario = await createMultiDeviceScenario(createClient);
|
|
const messageAtoB = `Cross-device A to B ${uniqueMultiDeviceName('msg')}`;
|
|
const messageBtoA = `Cross-device B to A ${uniqueMultiDeviceName('msg')}`;
|
|
const typingDraft = `Typing draft ${uniqueMultiDeviceName('draft')}`;
|
|
|
|
await test.step('assigns distinct clientInstanceId per browser context', async () => {
|
|
const instanceA = await readClientInstanceId(scenario.clientA.page);
|
|
const instanceB = await readClientInstanceId(scenario.clientB.page);
|
|
|
|
expect(instanceA).toBeTruthy();
|
|
expect(instanceB).toBeTruthy();
|
|
expect(instanceA).not.toEqual(instanceB);
|
|
});
|
|
|
|
await test.step('syncs chat from device A to device B', async () => {
|
|
await expectCrossDeviceMessage(scenario.messagesA, scenario.messagesB, messageAtoB);
|
|
});
|
|
|
|
await test.step('syncs chat from device B to device A', async () => {
|
|
await expectCrossDeviceMessage(scenario.messagesB, scenario.messagesA, messageBtoA);
|
|
});
|
|
|
|
await test.step('does not show own typing indicator on the other device for the same user', async () => {
|
|
await scenario.messagesA.typeDraftWithTypingEvents(typingDraft);
|
|
|
|
await expect(
|
|
scenario.clientB.page.getByText(`${scenario.credentials.displayName} is typing`, { exact: false })
|
|
).toHaveCount(0, { timeout: 5_000 });
|
|
});
|
|
|
|
await test.step('shows passive in-voice UI on the second device when the first joins voice', async () => {
|
|
await scenario.roomA.joinVoiceChannel(MULTI_DEVICE_VOICE_CHANNEL);
|
|
await expectActiveVoiceOnDevice(scenario.clientA.page);
|
|
|
|
await expectPassiveVoiceOnDevice(scenario.clientB.page, {
|
|
displayName: scenario.credentials.displayName
|
|
});
|
|
|
|
await expect(
|
|
membersSidePanel(scenario.clientB.page).getByText('In voice on another device', { exact: false })
|
|
).toBeVisible({ timeout: 20_000 });
|
|
|
|
await expect(
|
|
channelsSidePanel(scenario.clientB.page).locator('.opacity-50')
|
|
.filter({
|
|
hasText: scenario.credentials.displayName
|
|
})
|
|
.first()
|
|
).toBeVisible({ timeout: 20_000 });
|
|
});
|
|
|
|
await test.step('shows Join takeover affordance on passive device voice channel', async () => {
|
|
await expect(passiveVoiceChannelJoinBadge(scenario.clientB.page)).toBeVisible({ timeout: 20_000 });
|
|
});
|
|
|
|
await test.step('transfers voice ownership when the passive device takes over', async () => {
|
|
await scenario.roomB.joinVoiceChannel(MULTI_DEVICE_VOICE_CHANNEL);
|
|
await expectActiveVoiceOnDevice(scenario.clientB.page);
|
|
|
|
await expectPassiveVoiceOnDevice(scenario.clientA.page, {
|
|
displayName: scenario.credentials.displayName
|
|
});
|
|
});
|
|
|
|
await test.step('keeps the second device logged in when the first device logs out', async () => {
|
|
const message = `Still logged in ${uniqueMultiDeviceName('logout')}`;
|
|
|
|
await logoutFromMenu(scenario.clientA.page);
|
|
|
|
await scenario.messagesB.sendMessage(message);
|
|
await expect(scenario.messagesB.getMessageItemByText(message)).toBeVisible({ timeout: 20_000 });
|
|
await expect(scenario.clientB.page).toHaveURL(/\/room\//, { timeout: 10_000 });
|
|
});
|
|
});
|
|
});
|