feat: Add game activity status (Experimental)
All checks were successful
Queue Release Build / prepare (push) Successful in 21s
Deploy Web Apps / deploy (push) Successful in 5m14s
Queue Release Build / build-windows (push) Successful in 16m18s
Queue Release Build / build-linux (push) Successful in 29m20s
Queue Release Build / finalize (push) Successful in 36s

This commit is contained in:
2026-04-27 05:46:33 +02:00
parent 3858beb28e
commit 66c6f34cd3
52 changed files with 2120 additions and 113 deletions

View File

@@ -44,9 +44,11 @@ test.describe('Chat messaging features', () => {
await test.step('Opening first server once restores only its channels', async () => {
await openSavedRoomByName(scenario.client.page, alphaServerName);
await expect(
channelsPanel.locator(`button[data-channel-type="text"][data-channel-name="${alphaChannelName}"]`)
).toBeVisible({ timeout: 20_000 });
await expect(
channelsPanel.locator(`button[data-channel-type="text"][data-channel-name="${betaChannelName}"]`)
).toHaveCount(0);
@@ -54,9 +56,11 @@ test.describe('Chat messaging features', () => {
await test.step('Opening second server once restores only its channels', async () => {
await openSavedRoomByName(scenario.client.page, betaServerName);
await expect(
channelsPanel.locator(`button[data-channel-type="text"][data-channel-name="${betaChannelName}"]`)
).toBeVisible({ timeout: 20_000 });
await expect(
channelsPanel.locator(`button[data-channel-type="text"][data-channel-name="${alphaChannelName}"]`)
).toHaveCount(0);
@@ -304,11 +308,8 @@ async function createChatScenario(createClient: () => Promise<Client>): Promise<
await expect(alice.page).toHaveURL(/\/room\//, { timeout: 15_000 });
const bobSearchPage = new ServerSearchPage(bob.page);
const serverCard = bob.page.locator('button', { hasText: serverName }).first();
await bobSearchPage.searchInput.fill(serverName);
await expect(serverCard).toBeVisible({ timeout: 15_000 });
await serverCard.click();
await bobSearchPage.joinServerFromSearch(serverName);
await expect(bob.page).toHaveURL(/\/room\//, { timeout: 15_000 });
const aliceRoom = new ChatRoomPage(alice.page);

View File

@@ -41,14 +41,16 @@ test.describe('Direct message flow', () => {
await expect(scenario.alice.page).toHaveURL(/\/search/, { timeout: 20_000 });
await expect(scenario.alice.page.locator('app-server-search')).toBeVisible({ timeout: 20_000 });
await expect(scenario.alice.page.locator('app-user-search-list')).toBeVisible({ timeout: 20_000 });
const bobPeopleCard = scenario.alice.page.locator(`app-user-search-list [data-testid="user-card-${scenario.bobUserId}"]`);
const bobPeopleCard = scenario.alice.page
.locator('app-user-search-list [data-testid$="-' + scenario.bobUserId + '"]', { hasText: 'Bob' })
.first();
await expect(bobPeopleCard).toBeVisible({ timeout: 15_000 });
const friendButton = bobPeopleCard.locator(`[data-testid="friend-button-${scenario.bobUserId}"]`);
const messageButton = bobPeopleCard.locator(`[data-testid="message-user-${scenario.bobUserId}"]`);
const messageButton = bobPeopleCard.getByRole('button', { name: 'Message Bob' });
await expect(friendButton).toBeVisible({ timeout: 15_000 });
await expect(messageButton).toBeVisible({ timeout: 15_000 });
await expect(friendButton).toBeAttached({ timeout: 15_000 });
await expect(messageButton).toBeAttached({ timeout: 15_000 });
});
});
@@ -76,12 +78,7 @@ async function createDmScenario(createClient: () => Promise<Client>): Promise<Dm
const bobSearch = new ServerSearchPage(bob.page);
await bobSearch.searchInput.fill(serverName);
await bob.page.locator('button', { hasText: serverName })
.first()
.click();
await bobSearch.joinServerFromSearch(serverName);
await expect(bob.page).toHaveURL(/\/room\//, { timeout: 15_000 });
await new ChatMessagesPage(bob.page).waitForReady();
const bobRoomCard = alice.page.locator('[data-testid^="room-user-card-"]', { hasText: 'Bob' }).first();

View File

@@ -3,10 +3,7 @@ import {
type Locator,
type Page
} from '@playwright/test';
import {
test,
type Client
} from '../../fixtures/multi-client';
import { test, type Client } from '../../fixtures/multi-client';
import { RegisterPage } from '../../pages/register.page';
import { ServerSearchPage } from '../../pages/server-search.page';
import { ChatRoomPage } from '../../pages/chat-room.page';
@@ -109,14 +106,12 @@ async function createNotificationScenario(createClient: () => Promise<Client>):
await aliceSearch.createServer(serverName, {
description: 'E2E notification coverage server'
});
await expect(alice.page).toHaveURL(/\/room\//, { timeout: 15_000 });
const bobSearch = new ServerSearchPage(bob.page);
const serverCard = bob.page.locator('button', { hasText: serverName }).first();
await bobSearch.searchInput.fill(serverName);
await expect(serverCard).toBeVisible({ timeout: 15_000 });
await serverCard.click();
await bobSearch.joinServerFromSearch(serverName);
await expect(bob.page).toHaveURL(/\/room\//, { timeout: 15_000 });
const aliceRoom = new ChatRoomPage(alice.page);
@@ -155,10 +150,6 @@ async function installDesktopNotificationSpy(page: Page): Promise<void> {
class MockNotification {
static permission = 'granted';
static async requestPermission(): Promise<NotificationPermission> {
return 'granted';
}
onclick: (() => void) | null = null;
constructor(title: string, options?: NotificationOptions) {
@@ -168,6 +159,10 @@ async function installDesktopNotificationSpy(page: Page): Promise<void> {
});
}
static async requestPermission(): Promise<NotificationPermission> {
return 'granted';
}
close(): void {
return;
}
@@ -256,7 +251,8 @@ function getUnreadBadge(container: Locator): Locator {
}
function uniqueName(prefix: string): string {
return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
return `${prefix}-${Date.now()}-${Math.random().toString(36)
.slice(2, 8)}`;
}
interface WindowWithDesktopNotifications extends Window {

View File

@@ -384,11 +384,8 @@ async function registerUser(client: PersistentClient): Promise<void> {
async function joinServerFromSearch(page: Page, serverName: string): Promise<void> {
const searchPage = new ServerSearchPage(page);
const serverCard = page.locator('button', { hasText: serverName }).first();
await searchPage.searchInput.fill(serverName);
await expect(serverCard).toBeVisible({ timeout: 15_000 });
await serverCard.click();
await searchPage.joinServerFromSearch(serverName);
await expect(page).toHaveURL(/\/room\//, { timeout: 15_000 });
}

View File

@@ -56,12 +56,7 @@ async function setupServerWithBothUsers(
// Bob joins server
const bobSearch = new ServerSearchPage(bob.page);
await bobSearch.searchInput.fill(SERVER_NAME);
const serverCard = bob.page.locator('button', { hasText: SERVER_NAME }).first();
await expect(serverCard).toBeVisible({ timeout: 10_000 });
await serverCard.click();
await bobSearch.joinServerFromSearch(SERVER_NAME);
await expect(bob.page).toHaveURL(/\/room\//, { timeout: 15_000 });
}

View File

@@ -117,22 +117,14 @@ test.describe('Connectivity warning', () => {
await test.step('Bob joins the server', async () => {
const search = new ServerSearchPage(bob.page);
await search.searchInput.fill(serverName);
const card = bob.page.locator('button', { hasText: serverName }).first();
await expect(card).toBeVisible({ timeout: 15_000 });
await card.click();
await search.joinServerFromSearch(serverName);
await expect(bob.page).toHaveURL(/\/room\//, { timeout: 15_000 });
});
await test.step('Charlie joins the server', async () => {
const search = new ServerSearchPage(charlie.page);
await search.searchInput.fill(serverName);
const card = charlie.page.locator('button', { hasText: serverName }).first();
await expect(card).toBeVisible({ timeout: 15_000 });
await card.click();
await search.joinServerFromSearch(serverName);
await expect(charlie.page).toHaveURL(/\/room\//, { timeout: 15_000 });
});

View File

@@ -11,8 +11,9 @@ test.describe('ICE server settings', () => {
await register.register(`user_${suffix}`, 'IceTestUser', 'TestPass123!');
await expect(page.getByPlaceholder('Search servers and users...')).toBeVisible({ timeout: 30_000 });
await page.getByTitle('Settings').click();
await expect(page.getByRole('dialog')).toBeVisible({ timeout: 10_000 });
await expect(page.getByRole('button', { name: 'Network' })).toBeVisible({ timeout: 10_000 });
await page.getByRole('button', { name: 'Network' }).click();
await expect(page.getByTestId('ice-server-settings')).toBeVisible({ timeout: 10_000 });
}
test('allows adding, removing, and reordering ICE servers', async ({ createClient }) => {
@@ -101,7 +102,7 @@ test.describe('ICE server settings', () => {
await page.reload({ waitUntil: 'domcontentloaded' });
await page.getByTitle('Settings').click();
await expect(page.getByRole('dialog')).toBeVisible({ timeout: 10_000 });
await expect(page.getByRole('button', { name: 'Network' })).toBeVisible({ timeout: 10_000 });
await page.getByRole('button', { name: 'Network' }).click();
await expect(page.getByText('stun:persist-test.example.com:3478')).toBeVisible({ timeout: 10_000 });
});

View File

@@ -109,11 +109,7 @@ test.describe('STUN/TURN fallback behaviour', () => {
await test.step('Bob joins Alice server', async () => {
const search = new ServerSearchPage(bob.page);
await search.searchInput.fill(serverName);
const serverCard = bob.page.locator('button', { hasText: serverName }).first();
await expect(serverCard).toBeVisible({ timeout: 15_000 });
await serverCard.click();
await search.joinServerFromSearch(serverName);
await expect(bob.page).toHaveURL(/\/room\//, { timeout: 15_000 });
});

View File

@@ -572,10 +572,10 @@ async function joinRoomFromSearch(page: Page, roomName: string): Promise<void> {
await expect(searchInput).toBeVisible({ timeout: 20_000 });
await searchInput.fill(roomName);
const roomCard = page.locator('button', { hasText: roomName }).first();
const roomCard = page.locator('div[title]', { hasText: roomName }).first();
await expect(roomCard).toBeVisible({ timeout: 20_000 });
await roomCard.click();
await roomCard.dblclick();
await expect(page).toHaveURL(/\/room\//, { timeout: 20_000 });
await expect(page.locator('app-rooms-side-panel').first()).toBeVisible({ timeout: 20_000 });
await waitForCurrentRoomName(page, roomName);

View File

@@ -335,10 +335,10 @@ async function joinRoomFromSearch(page: Page, roomName: string): Promise<void> {
await expect(searchInput).toBeVisible({ timeout: 20_000 });
await searchInput.fill(roomName);
const roomCard = page.locator('button', { hasText: roomName }).first();
const roomCard = page.locator('div[title]', { hasText: roomName }).first();
await expect(roomCard).toBeVisible({ timeout: 20_000 });
await roomCard.click();
await roomCard.dblclick();
await expect(page).toHaveURL(/\/room\//, { timeout: 20_000 });
await expect(page.locator('app-rooms-side-panel').first()).toBeVisible({ timeout: 20_000 });
await waitForCurrentRoomName(page, roomName);

View File

@@ -96,14 +96,7 @@ test.describe('Full user journey: register -> server -> voice chat', () => {
await test.step('Bob finds and joins the server', async () => {
const searchPage = new ServerSearchPage(bob.page);
// Search for the server
await searchPage.searchInput.fill(SERVER_NAME);
// Wait for search results and click the server
const serverCard = bob.page.locator('button', { hasText: SERVER_NAME }).first();
await expect(serverCard).toBeVisible({ timeout: 10_000 });
await serverCard.click();
await searchPage.joinServerFromSearch(SERVER_NAME);
// Bob should be in the room now
await expect(bob.page).toHaveURL(/\/room\//, { timeout: 15_000 });