Files
self cfbed53f96 refactor: unify provider identity
Use ProviderId as the canonical provider model identity and remove resource-level/provider-kind/source model duplication.

Treat manual as the provider id for manual servers and external domains, deriving domain DNS association from same-name zones.
2026-06-27 21:38:45 +02:00

215 lines
7.3 KiB
TypeScript

import { expect, test, type Locator, type Page } from "@playwright/test"
import { closeLegionGui, launchLegionGui, type LegionGuiApp } from "./legion-electron"
const UNLOCK_PASSWORD = "legion-gui-map-password"
const SERVER_LABEL = "MockKing24 map node"
test.setTimeout(45_000)
test("moves a planned server location through the map context menu", async () => {
const gui: LegionGuiApp | null = await launchLegionGui({
mockNetwork: true
})
try {
await test.step("plan a mock server", async () => {
await installStateProbe(gui.page)
await unlock(gui.page)
await dismissTutorial(gui.page)
await configureTribe(gui.page)
await configureMockProvider(gui.page)
await planMockServer(gui.page)
})
await test.step("move the server marker through the map context menu", async () => {
await gui.page.getByRole("button", { name: "Cluster" }).click()
const marker = gui.page.getByRole("button", { exact: true, name: SERVER_LABEL })
await expect(marker).toBeVisible({ timeout: 10_000 })
await marker.click({ button: "right" })
const moveLocation = gui.page.getByRole("button", { name: "Move map location" })
const popover = gui.page.locator(".world-map-marker-popover")
await expect(popover).toBeVisible()
await expect(moveLocation).toBeVisible()
await expectMenuButtonToReceivePointerEvents(moveLocation)
await moveLocation.click()
const map = gui.page.locator(".world-map")
const box = await map.boundingBox()
if (!box) {
throw new Error("World map bounding box was not available.")
}
await gui.page.mouse.click(box.x + box.width * 0.55, box.y + box.height * 0.55)
await expect(popover).toBeHidden()
await gui.page.mouse.click(box.x + box.width * 0.65, box.y + box.height * 0.45, {
button: "right"
})
await gui.page.getByRole("button", { name: "Move server here" }).click()
await expect
.poll(() => plannedServerLocation(gui!.page), { timeout: 10_000 })
.toMatchObject({
hasLocationOverride: true
})
})
} finally {
await closeLegionGui(gui)
}
})
async function unlock(page: Page): Promise<void> {
await expect(page.getByRole("heading", { name: "Create local Legion instance" })).toBeVisible()
await page.getByLabel("Password", { exact: true }).fill(UNLOCK_PASSWORD)
await page.getByLabel("Confirm password").fill(UNLOCK_PASSWORD)
await page.getByRole("button", { name: "Create" }).click()
await expect(page.getByRole("button", { name: "Instances" })).toBeVisible()
}
async function dismissTutorial(page: Page): Promise<void> {
const dismissCheckbox = page.getByLabel("Go away forever").last()
if ((await dismissCheckbox.count()) > 0) {
await dismissCheckbox.check({ force: true, timeout: 1_000 }).catch(() => undefined)
}
}
async function configureTribe(page: Page): Promise<void> {
await page.getByRole("button", { name: "Open settings" }).click()
await page.getByRole("textbox", { name: "Tribe name" }).fill("Legion GUI Map")
await page.getByRole("textbox", { name: "Description" }).fill("gui-map.example.test")
await page.getByRole("button", { name: "Save tribe" }).click()
await expect
.poll(() => tribeState(page), { timeout: 10_000 })
.toMatchObject({
name: "Legion GUI Map",
description: "gui-map.example.test"
})
}
async function configureMockProvider(page: Page): Promise<void> {
await page.getByRole("button", { name: "Providers" }).click()
const mockCard = page.locator("article").filter({ hasText: "MockKing24" })
await mockCard.getByLabel("Error rate").fill("0")
await mockCard.getByLabel("Average latency (ms)").fill("0")
await mockCard.getByLabel("Seed").fill("gui-map")
await mockCard.getByRole("button", { name: "Save" }).click()
await expect.poll(() => providerCount(page, "mock"), { timeout: 10_000 }).toBe(1)
await page.getByRole("button", { name: "Close dialog" }).click()
}
async function planMockServer(page: Page): Promise<void> {
await page.getByRole("button", { name: "Instances" }).click()
await page.getByRole("button", { name: "Add server" }).first().click()
await expect(page.getByRole("heading", { name: "Server" })).toBeVisible()
await page.locator("dialog select").first().selectOption("mock")
await expect(page.getByRole("button", { name: "Selected" }).first()).toBeVisible()
await page.getByRole("textbox", { name: "Label" }).fill(SERVER_LABEL)
await page.getByRole("button", { name: "Save server" }).click()
await expect(page.getByLabel(`Toggle details for ${SERVER_LABEL}`)).toBeVisible()
}
async function expectMenuButtonToReceivePointerEvents(button: Locator): Promise<void> {
await expect
.poll(async () => {
return button.evaluate((element) => {
const bounds = element.getBoundingClientRect()
const target = document.elementFromPoint(
bounds.left + bounds.width / 2,
bounds.top + bounds.height / 2
)
return element === target || element.contains(target)
})
})
.toBe(true)
}
async function plannedServerLocation(page: Page): Promise<{
hasLocationOverride: boolean
latitude?: number
longitude?: number
}> {
return page.evaluate(() => {
return (window as unknown as StateProbeWindow).__legionGuiTestState.plannedServerLocation
})
}
async function tribeState(page: Page): Promise<{ name?: string; description?: string }> {
return page.evaluate(() => {
return (window as unknown as StateProbeWindow).__legionGuiTestState.tribe
})
}
async function providerCount(page: Page, id: string): Promise<number> {
return page.evaluate((provider) => {
return (window as unknown as StateProbeWindow).__legionGuiTestState.providers.filter(
(entry) => entry === provider
).length
}, id)
}
async function installStateProbe(page: Page): Promise<void> {
await page.evaluate((serverLabel) => {
const target = window as unknown as StateProbeWindow
target.__legionGuiTestState = {
plannedServerLocation: {
hasLocationOverride: false
},
tribe: {},
providers: []
}
target.api.onSnapshot((snapshot) => {
const server = snapshot?.scheme.servers.find((entry) => entry.label === serverLabel)
target.__legionGuiTestState.plannedServerLocation = {
hasLocationOverride: Boolean(server?.locationOverride),
latitude: server?.locationOverride?.latitude,
longitude: server?.locationOverride?.longitude
}
target.__legionGuiTestState.tribe = {
name: snapshot?.tribe?.name,
description: snapshot?.tribe?.description
}
target.__legionGuiTestState.providers =
snapshot?.providers.map((provider) => provider.id) ?? []
})
}, SERVER_LABEL)
}
interface AppSnapshotLike {
scheme: {
servers: Array<{
label: string
locationOverride?: {
latitude: number
longitude: number
}
}>
}
tribe?: {
name?: string
description?: string
}
providers: Array<{
id: string
}>
}
interface StateProbeWindow {
api: {
onSnapshot(callback: (snapshot: AppSnapshotLike | null) => void): () => void
}
__legionGuiTestState: {
plannedServerLocation: {
hasLocationOverride: boolean
latitude?: number
longitude?: number
}
tribe: {
name?: string
description?: string
}
providers: string[]
}
}