118 lines
4.7 KiB
TypeScript
118 lines
4.7 KiB
TypeScript
import { type Page } from '@playwright/test';
|
|
import {
|
|
test,
|
|
expect,
|
|
type Client
|
|
} from '../../fixtures/multi-client';
|
|
import { RegisterPage } from '../../pages/register.page';
|
|
import { ServerSearchPage } from '../../pages/server-search.page';
|
|
import { ChatMessagesPage } from '../../pages/chat-messages.page';
|
|
|
|
test.describe('Direct message flow', () => {
|
|
test.describe.configure({ timeout: 180_000 });
|
|
|
|
test('opens a DM from a user card and queues messages while offline', async ({ createClient }) => {
|
|
const scenario = await createDmScenario(createClient);
|
|
const offlineMessage = `Offline DM ${uniqueName('msg')}`;
|
|
|
|
await test.step('Alice opens Bob from the room user list', async () => {
|
|
const bobUserCard = scenario.alice.page.locator('[data-testid^="room-user-card-"]', { hasText: 'Bob' }).first();
|
|
|
|
await expect(bobUserCard).toBeVisible({ timeout: 20_000 });
|
|
await bobUserCard.getByRole('button', { name: 'Message Bob' }).click();
|
|
await expect(scenario.alice.page).toHaveURL(/\/dm\//, { timeout: 15_000 });
|
|
await expect(scenario.alice.page.getByRole('heading', { name: 'Bob' })).toBeVisible({ timeout: 10_000 });
|
|
});
|
|
|
|
await test.step('Offline send persists locally as queued', async () => {
|
|
await scenario.alice.page.evaluate(() => window.simulateOffline?.());
|
|
await scenario.alice.page.getByTestId('dm-input').fill(offlineMessage);
|
|
await scenario.alice.page.getByTestId('dm-input').press('Enter');
|
|
|
|
await expect(scenario.alice.page.locator('app-dm-chat').getByText(offlineMessage)).toBeVisible({ timeout: 10_000 });
|
|
await expect(scenario.alice.page.getByTestId('message-status').last()).toContainText('QUEUED');
|
|
});
|
|
});
|
|
|
|
test('shows friend and message actions on the search people list', async ({ createClient }) => {
|
|
const scenario = await createDmScenario(createClient);
|
|
|
|
await scenario.alice.page.goto('/search', { waitUntil: 'domcontentloaded' });
|
|
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}"]`);
|
|
|
|
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}"]`);
|
|
|
|
await expect(friendButton).toBeVisible({ timeout: 15_000 });
|
|
await expect(messageButton).toBeVisible({ timeout: 15_000 });
|
|
});
|
|
});
|
|
|
|
interface DmScenario {
|
|
alice: Client;
|
|
bob: Client;
|
|
bobUserId: string;
|
|
aliceSearch: ServerSearchPage;
|
|
}
|
|
|
|
async function createDmScenario(createClient: () => Promise<Client>): Promise<DmScenario> {
|
|
const suffix = uniqueName('dm');
|
|
const serverName = `DM Server ${suffix}`;
|
|
const alice = await createClient();
|
|
const bob = await createClient();
|
|
|
|
await registerUser(alice.page, `alice_${suffix}`, 'Alice');
|
|
await registerUser(bob.page, `bob_${suffix}`, 'Bob');
|
|
|
|
const aliceSearch = new ServerSearchPage(alice.page);
|
|
|
|
await aliceSearch.createServer(serverName, { description: 'E2E direct message discovery server' });
|
|
await expect(alice.page).toHaveURL(/\/room\//, { timeout: 15_000 });
|
|
await new ChatMessagesPage(alice.page).waitForReady();
|
|
|
|
const bobSearch = new ServerSearchPage(bob.page);
|
|
|
|
await bobSearch.searchInput.fill(serverName);
|
|
|
|
await bob.page.locator('button', { hasText: serverName })
|
|
.first()
|
|
.click();
|
|
|
|
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();
|
|
|
|
await expect(bobRoomCard).toBeVisible({ timeout: 20_000 });
|
|
|
|
const bobUserCardTestId = await bobRoomCard.getAttribute('data-testid');
|
|
const bobUserId = bobUserCardTestId?.replace('room-user-card-', '');
|
|
|
|
if (!bobUserId) {
|
|
throw new Error('Expected Bob room user card to expose a stable test id.');
|
|
}
|
|
|
|
return {
|
|
alice,
|
|
bob,
|
|
bobUserId,
|
|
aliceSearch
|
|
};
|
|
}
|
|
|
|
async function registerUser(page: Page, username: string, displayName: string): Promise<void> {
|
|
const registerPage = new RegisterPage(page);
|
|
|
|
await registerPage.goto();
|
|
await registerPage.register(username, displayName, 'TestPass123!');
|
|
await expect(page).toHaveURL(/\/search/, { timeout: 15_000 });
|
|
}
|
|
|
|
function uniqueName(prefix: string): string {
|
|
return `${prefix}-${Date.now()}-${Math.random().toString(36)
|
|
.slice(2, 8)}`;
|
|
}
|