fix: improve plugins functionality with server management
This commit is contained in:
@@ -1,7 +1,13 @@
|
||||
import { mkdtemp, rm } from 'node:fs/promises';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { join } from 'node:path';
|
||||
import { chromium, type BrowserContext, type Locator, type Page, type Route } from '@playwright/test';
|
||||
import {
|
||||
chromium,
|
||||
type BrowserContext,
|
||||
type Locator,
|
||||
type Page,
|
||||
type Route
|
||||
} from '@playwright/test';
|
||||
import { test, expect } from '../../fixtures/multi-client';
|
||||
import { installTestServerEndpoint } from '../../helpers/seed-test-endpoint';
|
||||
import { installWebRTCTracking } from '../../helpers/webrtc-helpers';
|
||||
@@ -31,7 +37,11 @@ interface PersistentClient {
|
||||
}
|
||||
|
||||
const STATIC_GIF_BASE64 = 'R0lGODlhAQABAPAAAP///wAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==';
|
||||
const GIF_FRAME_MARKER = Buffer.from([0x21, 0xf9, 0x04]);
|
||||
const GIF_FRAME_MARKER = Buffer.from([
|
||||
0x21,
|
||||
0xf9,
|
||||
0x04
|
||||
]);
|
||||
const CLIENT_LAUNCH_ARGS = ['--use-fake-device-for-media-stream', '--use-fake-ui-for-media-stream'];
|
||||
const SERVER_ICON_SYNC_TIMEOUT_MS = 45_000;
|
||||
|
||||
@@ -77,6 +87,7 @@ test.describe('Server icon sync', () => {
|
||||
await new ServerSearchPage(alice.page).createServer(serverName, {
|
||||
description: 'Server icon synchronization E2E coverage'
|
||||
});
|
||||
|
||||
await expect(alice.page).toHaveURL(/\/room\//, { timeout: 15_000 });
|
||||
|
||||
await joinServerFromSearch(bob.page, serverName);
|
||||
@@ -263,15 +274,15 @@ async function openServerSettings(page: Page, serverName: string): Promise<void>
|
||||
|
||||
async function openSettingsModalThroughAngularDevMode(page: Page): Promise<void> {
|
||||
await page.evaluate(() => {
|
||||
type SettingsModalComponentHandle = {
|
||||
interface SettingsModalComponentHandle {
|
||||
modal?: {
|
||||
open: (page: string) => void;
|
||||
};
|
||||
};
|
||||
type AngularDebugApi = {
|
||||
}
|
||||
interface AngularDebugApi {
|
||||
getComponent: (element: Element) => SettingsModalComponentHandle;
|
||||
applyChanges?: (component: SettingsModalComponentHandle) => void;
|
||||
};
|
||||
}
|
||||
|
||||
const host = document.querySelector('app-settings-modal');
|
||||
const debugApi = (window as Window & { ng?: AngularDebugApi }).ng;
|
||||
@@ -373,33 +384,33 @@ async function retryTransientNavigation<T>(navigate: () => Promise<T>, attempts
|
||||
|
||||
async function expectServerSettingsIcon(page: Page, serverName: string, expectedDataUrl: string): Promise<void> {
|
||||
const settingsPanel = page.locator('app-server-settings');
|
||||
const image = settingsPanel.locator(`img[alt="${serverName} icon"]`).first();
|
||||
const image = settingsPanel.locator('[style*="background-image"]').first();
|
||||
|
||||
await expectImageLoadedWithSrc(image, expectedDataUrl, 'settings server icon');
|
||||
await expectBackgroundImageLoadedWithUrl(image, expectedDataUrl, 'settings server icon');
|
||||
}
|
||||
|
||||
async function expectRoomHeaderIcon(page: Page, serverName: string, expectedDataUrl: string): Promise<void> {
|
||||
const channelsPanel = page.locator('app-rooms-side-panel').first();
|
||||
const image = channelsPanel.locator(`img[alt="${serverName} icon"]`).first();
|
||||
const image = channelsPanel.locator('[style*="background-image"]').first();
|
||||
|
||||
await expectImageLoadedWithSrc(image, expectedDataUrl, 'room header server icon');
|
||||
await expectBackgroundImageLoadedWithUrl(image, expectedDataUrl, 'room header server icon');
|
||||
}
|
||||
|
||||
async function expectRailIcon(page: Page, serverName: string, expectedDataUrl: string): Promise<void> {
|
||||
const image = page.locator(`app-servers-rail img[alt="${serverName} icon"]`).first();
|
||||
const image = page.locator(`app-servers-rail button[title="${serverName}"] [style*="background-image"]`).first();
|
||||
|
||||
await expectImageLoadedWithSrc(image, expectedDataUrl, 'servers rail icon');
|
||||
await expectBackgroundImageLoadedWithUrl(image, expectedDataUrl, 'servers rail icon');
|
||||
}
|
||||
|
||||
async function expectSearchResultIcon(page: Page, serverName: string, expectedDataUrl: string): Promise<void> {
|
||||
const serverCard = page.locator('app-server-search div[title]', { hasText: serverName }).first();
|
||||
const image = serverCard.locator(`img[alt="${serverName} icon"]`).first();
|
||||
const image = serverCard.locator('[style*="background-image"]').first();
|
||||
|
||||
await expect(serverCard).toBeVisible({ timeout: 20_000 });
|
||||
await expectImageLoadedWithSrc(image, expectedDataUrl, 'search result server icon');
|
||||
await expectBackgroundImageLoadedWithUrl(image, expectedDataUrl, 'search result server icon');
|
||||
}
|
||||
|
||||
async function expectImageLoadedWithSrc(image: Locator, expectedDataUrl: string, label: string): Promise<void> {
|
||||
async function expectBackgroundImageLoadedWithUrl(image: Locator, expectedDataUrl: string, label: string): Promise<void> {
|
||||
await expect
|
||||
.poll(
|
||||
async () => {
|
||||
@@ -407,14 +418,14 @@ async function expectImageLoadedWithSrc(image: Locator, expectedDataUrl: string,
|
||||
return null;
|
||||
}
|
||||
|
||||
return image.getAttribute('src');
|
||||
return image.evaluate((element) => getComputedStyle(element).backgroundImage);
|
||||
},
|
||||
{
|
||||
timeout: SERVER_ICON_SYNC_TIMEOUT_MS,
|
||||
message: `${label} src should update`
|
||||
message: `${label} background should update`
|
||||
}
|
||||
)
|
||||
.toBe(expectedDataUrl);
|
||||
.toContain(expectedDataUrl);
|
||||
|
||||
await expect
|
||||
.poll(
|
||||
@@ -423,11 +434,23 @@ async function expectImageLoadedWithSrc(image: Locator, expectedDataUrl: string,
|
||||
return false;
|
||||
}
|
||||
|
||||
return image.evaluate((element) => {
|
||||
const img = element as HTMLImageElement;
|
||||
return image.evaluate(
|
||||
(element) =>
|
||||
new Promise<boolean>((resolve) => {
|
||||
const backgroundImage = getComputedStyle(element).backgroundImage;
|
||||
const match = /^url\("?(.*?)"?\)$/.exec(backgroundImage);
|
||||
const img = new Image();
|
||||
|
||||
return img.complete && img.naturalWidth > 0 && img.naturalHeight > 0;
|
||||
});
|
||||
if (!match?.[1]) {
|
||||
resolve(false);
|
||||
return;
|
||||
}
|
||||
|
||||
img.onload = () => resolve(img.naturalWidth > 0 && img.naturalHeight > 0);
|
||||
img.onerror = () => resolve(false);
|
||||
img.src = match[1];
|
||||
})
|
||||
);
|
||||
},
|
||||
{
|
||||
timeout: SERVER_ICON_SYNC_TIMEOUT_MS,
|
||||
@@ -448,8 +471,22 @@ function buildGifUpload(label: string): ImageUploadPayload {
|
||||
const header = baseGif.subarray(0, frameStart);
|
||||
const frame = baseGif.subarray(frameStart, baseGif.length - 1);
|
||||
const commentData = Buffer.from(label, 'ascii');
|
||||
const commentExtension = Buffer.concat([Buffer.from([0x21, 0xfe, commentData.length]), commentData, Buffer.from([0x00])]);
|
||||
const buffer = Buffer.concat([header, commentExtension, frame, Buffer.from([0x3b])]);
|
||||
const commentExtension = Buffer.concat([
|
||||
Buffer.from([
|
||||
0x21,
|
||||
0xfe,
|
||||
commentData.length
|
||||
]),
|
||||
commentData,
|
||||
Buffer.from([0x00])
|
||||
]);
|
||||
const buffer = Buffer.concat([
|
||||
header,
|
||||
commentExtension,
|
||||
frame,
|
||||
frame,
|
||||
Buffer.from([0x3b])
|
||||
]);
|
||||
const base64 = buffer.toString('base64');
|
||||
|
||||
return {
|
||||
@@ -461,5 +498,6 @@ function buildGifUpload(label: string): ImageUploadPayload {
|
||||
}
|
||||
|
||||
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)}`;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user