Measured first: the new oracle SheetRun types 77 texts into FILIUS' own settings panels and records what was marked red, what the device took and what the field showed afterwards. Java's two judgements disagree (red text taken, black text refused, any int as a modem port, a refused text dropped without a word); here one rule decides both (packages/doc fields.ts), a refused text stays marked in the field, and only what Java's model accepts reaches the document. - doc: settings.ts (name, label policy, addresses, IP forwarding, DHCP stopping the own DHCP server, LAN/WLAN with the wireless connection, switch SSID dropping its wireless hosts, retention time, cloud, modem port as its pair), sheet.ts (what the sheet shows) - fls: modemPair/modemPort in one place - editor: DeviceSheet beside the surface or below it on a phone, opened with the selection; every change one undo step - not adopted from Java: the name overwritten by the MAC address, the access point search depending on unrelated devices Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
68 lines
3.3 KiB
TypeScript
68 lines
3.3 KiB
TypeScript
// What the editor's browser tests share: the built page served once per file,
|
|
// and reading the surface (its transform, a device's place on the screen, the
|
|
// document's drawing through the development page's filiusDev).
|
|
|
|
import { dirname, join } from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
import type { Page } from '@playwright/test';
|
|
import { build, preview, type PreviewServer } from 'vite';
|
|
import type { DeviceSheet, TopologyView } from '@mdg-filius/doc';
|
|
|
|
const config = join(dirname(fileURLToPath(import.meta.url)), '../vite.config.ts');
|
|
|
|
export async function serveEditor(): Promise<{ readonly base: string; readonly close: () => Promise<void> }> {
|
|
await build({ configFile: config, logLevel: 'silent' });
|
|
const server: PreviewServer = await preview({ configFile: config, preview: { port: 0 }, logLevel: 'silent' });
|
|
return {
|
|
base: server.resolvedUrls!.local[0]!,
|
|
close: () => new Promise<void>((resolve) => server.httpServer.close(() => resolve())),
|
|
};
|
|
}
|
|
|
|
export async function openEditor(page: Page, base: string, example: string, query = ''): Promise<void> {
|
|
await page.goto(`${base}?example=${example}${query}`);
|
|
await page.waitForSelector('svg.filius-canvas');
|
|
await page.waitForFunction(() => (window as unknown as { filiusDev?: unknown }).filiusDev !== undefined);
|
|
}
|
|
|
|
/** The document's drawing. */
|
|
export function drawing(page: Page): Promise<TopologyView> {
|
|
return page.evaluate(() => (window as unknown as { filiusDev: { view: () => TopologyView } }).filiusDev.view());
|
|
}
|
|
|
|
/** What the device sheet reads of a device. */
|
|
export function sheetOf(page: Page, id: string): Promise<DeviceSheet | undefined> {
|
|
return page.evaluate((each) => (window as unknown as { filiusDev: { sheet: (id: string) => DeviceSheet | undefined } }).filiusDev.sheet(each), id);
|
|
}
|
|
|
|
/** A device's button as data-box says it (surface pixels). */
|
|
export async function boxOf(page: Page, id: string): Promise<{ x: number; y: number; width: number; height: number }> {
|
|
const [x, y, width, height] = (await page.locator(`g.device[data-node="${id}"]`).getAttribute('data-box'))!.split(' ').map(Number) as [number, number, number, number];
|
|
return { x, y, width, height };
|
|
}
|
|
|
|
/** Where a device's icon's middle is on the screen. */
|
|
export async function iconCentre(page: Page, id: string): Promise<{ x: number; y: number }> {
|
|
const box = (await page.locator(`g.device[data-node="${id}"] image`).boundingBox())!;
|
|
return { x: box.x + box.width / 2, y: box.y + box.height / 2 };
|
|
}
|
|
|
|
/** A point of a device's button on the screen, by its share of the button's width and height. */
|
|
export async function onDevice(page: Page, id: string, across = 0.5, down = 0.4): Promise<{ x: number; y: number }> {
|
|
const box = (await page.locator(`g.device[data-node="${id}"] > rect`).first().boundingBox())!;
|
|
return { x: box.x + box.width * across, y: box.y + box.height * down };
|
|
}
|
|
|
|
/** The surface's scale. */
|
|
export async function scaleOf(page: Page): Promise<number> {
|
|
return Number(await page.locator('[data-zoom]').getAttribute('data-zoom'));
|
|
}
|
|
|
|
/** Drags with the mouse from one point to another in steps. */
|
|
export async function drag(page: Page, from: { x: number; y: number }, to: { x: number; y: number }): Promise<void> {
|
|
await page.mouse.move(from.x, from.y);
|
|
await page.mouse.down();
|
|
await page.mouse.move(to.x, to.y, { steps: 8 });
|
|
await page.mouse.up();
|
|
}
|