210 lines
9.0 KiB
TypeScript
210 lines
9.0 KiB
TypeScript
import { assert, assertEquals } from "jsr:@std/assert@1.0.19";
|
|
import { chromium } from "npm:playwright@1.59.1";
|
|
import { loadScenario } from "../../../../tools/web-ux/src/scenario.ts";
|
|
import { startOwnedProcesses, stopOwnedProcesses } from "../../../../tools/web-ux/src/processes.ts";
|
|
import { creationResponse } from "../../src/lib/workspace/api/workspace-catalog.test-fixtures.ts";
|
|
|
|
// Browser-only contract over the production static build and loopback API fixtures;
|
|
// no actual Backend, Runtime, Worker, account or repository is created.
|
|
Deno.test("production empty Workspace supports optional Git validation, reopening, and Workdir-less Worker submission at all review widths", async () => {
|
|
const scenarioPath = new URL("./optional-repository.json", import.meta.url).pathname;
|
|
const scenario = await loadScenario(scenarioPath);
|
|
async function freePort() {
|
|
const listener = Deno.listen({ hostname: "127.0.0.1", port: 0 });
|
|
const port = (listener.addr as Deno.NetAddr).port;
|
|
listener.close();
|
|
return port;
|
|
}
|
|
const upstreamPort = await freePort();
|
|
let proxyPort = await freePort();
|
|
while (proxyPort === upstreamPort) proxyPort = await freePort();
|
|
scenario.baseUrl = `http://127.0.0.1:${proxyPort}`;
|
|
scenario.processes![0].args![4] = String(upstreamPort);
|
|
scenario.processes![0].readyUrl = `http://127.0.0.1:${upstreamPort}/health`;
|
|
scenario.processes![1].args![3] = String(proxyPort);
|
|
scenario.processes![1].args![4] = `http://127.0.0.1:${upstreamPort}`;
|
|
scenario.processes![1].readyUrl = `${scenario.baseUrl}/health`;
|
|
const output = new URL("../../.svelte-kit/t717/browser/", import.meta.url).pathname;
|
|
const processes = await startOwnedProcesses(
|
|
scenario.processes!,
|
|
scenarioPath,
|
|
`${output}/process-logs`,
|
|
[],
|
|
);
|
|
try {
|
|
const browser = await chromium.launch({ headless: true });
|
|
try {
|
|
for (const colorScheme of ["light", "dark"] as const) {
|
|
for (const width of [1440, 768, 390, 320]) {
|
|
const context = await browser.newContext({
|
|
viewport: { width, height: 900 },
|
|
colorScheme,
|
|
reducedMotion: "reduce",
|
|
});
|
|
try {
|
|
const page = await context.newPage();
|
|
const errors: string[] = [];
|
|
page.on("pageerror", (error) => errors.push(String(error)));
|
|
page.on("console", (message) => {
|
|
if (message.type() === "error") errors.push(message.text());
|
|
});
|
|
page.on("response", (response) => {
|
|
if (response.status() >= 400) {
|
|
errors.push(`${response.status()} ${response.url()}`);
|
|
}
|
|
});
|
|
const creationBodies: Record<string, unknown>[] = [];
|
|
await page.route("**/api/workspaces", async (route) => {
|
|
if (route.request().method() !== "POST") {
|
|
return await route.continue();
|
|
}
|
|
creationBodies.push(route.request().postDataJSON());
|
|
await route.fulfill({ json: creationResponse() });
|
|
});
|
|
await page.goto(scenario.baseUrl! + "/dashboard#workspace-create-title");
|
|
const name = page.getByLabel("Workspace display name");
|
|
await name.fill("New Workspace");
|
|
const toggle = page.getByRole("checkbox", {
|
|
name: "Add an initial repository (optional)",
|
|
});
|
|
await toggle.check();
|
|
await page.getByRole("button", {
|
|
name: "Create Workspace",
|
|
exact: true,
|
|
}).click();
|
|
assertEquals(creationBodies.length, 0); // Native required fields stop partial repository submission.
|
|
await page.getByLabel("Repository key", { exact: true }).fill(
|
|
"Invalid key",
|
|
);
|
|
await page.getByLabel("Initial repository absolute path or URI")
|
|
.fill("/srv/platform");
|
|
assertEquals(
|
|
await page.getByLabel("Repository key", { exact: true }).evaluate(
|
|
(input) =>
|
|
(input as unknown as { checkValidity(): boolean })
|
|
.checkValidity(),
|
|
),
|
|
false,
|
|
);
|
|
await page.getByLabel("Repository key", { exact: true }).fill(
|
|
"platform",
|
|
);
|
|
assertEquals(
|
|
await page.locator("form").evaluate((form) =>
|
|
(form as unknown as { checkValidity(): boolean })
|
|
.checkValidity()
|
|
),
|
|
true,
|
|
);
|
|
await Deno.mkdir(output, { recursive: true });
|
|
await page.screenshot({
|
|
path: `${output}/${colorScheme}-${width}-repository.png`,
|
|
fullPage: true,
|
|
});
|
|
assertEquals(
|
|
await page.evaluate(
|
|
"document.documentElement.scrollWidth > innerWidth",
|
|
),
|
|
false,
|
|
`${colorScheme} ${width}px`,
|
|
);
|
|
await toggle.uncheck();
|
|
await page.getByRole("button", {
|
|
name: "Create Workspace",
|
|
exact: true,
|
|
}).click();
|
|
await page.waitForURL("**/w/home-empty");
|
|
assertEquals(creationBodies.length, 1);
|
|
assertEquals(creationBodies[0].repository, null);
|
|
await page.locator("[data-feed-ready=home-active]").waitFor();
|
|
assertEquals(await page.locator("main [role=alert]").count(), 0);
|
|
// Reopening is based on Workspace identity, not a repository record.
|
|
await page.goto(scenario.baseUrl! + "/dashboard");
|
|
await page.locator('a.workspace-catalog-card[href="/w/home-empty"]')
|
|
.click();
|
|
await page.waitForURL("**/w/home-empty");
|
|
await page.goto(`${scenario.baseUrl}/w/home-empty/workers/new`);
|
|
await page.locator(".worker-launch-form").waitFor();
|
|
assert(
|
|
await page.getByRole("button", { name: "Start Worker" })
|
|
.isEnabled(),
|
|
);
|
|
await page.getByRole("combobox", { name: "Runtime", exact: true })
|
|
.selectOption("remote");
|
|
assert(
|
|
await page.getByRole("button", { name: "Start Worker" })
|
|
.isEnabled(),
|
|
);
|
|
await page.getByRole("button", { name: "Add attachment", exact: true }).click();
|
|
await page.getByLabel("Attachment 1 Workdir").selectOption(
|
|
"__new_working_directory__",
|
|
);
|
|
assert(
|
|
await page.getByRole("button", {
|
|
name: "Create Workdir",
|
|
exact: true,
|
|
}).isDisabled(),
|
|
);
|
|
await page.getByRole("combobox", { name: "Runtime", exact: true })
|
|
.selectOption("embedded");
|
|
const submitted = page.waitForRequest((request) =>
|
|
request.url().endsWith("/api/w/home-empty/workers") &&
|
|
request.method() === "POST"
|
|
);
|
|
await page.route("**/api/w/home-empty/workers", async (route) => {
|
|
if (route.request().method() !== "POST") {
|
|
return await route.continue();
|
|
}
|
|
// Successful, synthetic launch projection; the test only exercises browser/API wiring.
|
|
await route.fulfill({
|
|
json: {
|
|
workspace_id: "home-empty",
|
|
runtime_id: "embedded",
|
|
worker_id: "fixture-worker",
|
|
console_href: "/w/home-empty",
|
|
diagnostics: [],
|
|
worker: {
|
|
runtime_id: "embedded",
|
|
worker_id: "fixture-worker",
|
|
host_id: "fixture-host",
|
|
display_name: "Worker",
|
|
label: "Worker",
|
|
profile: "builtin:companion",
|
|
singleton_key: null,
|
|
tags: [],
|
|
workspace: {
|
|
visibility: "workspace",
|
|
identity: "home-empty",
|
|
workspace_id: "home-empty",
|
|
},
|
|
state: "idle",
|
|
last_seen_at: null,
|
|
pinned: false,
|
|
retention_state: "active",
|
|
implementation: { kind: "yoi", display_hint: "Yoi" },
|
|
workdir_attachments: [],
|
|
diagnostics: [],
|
|
},
|
|
},
|
|
});
|
|
});
|
|
await page.getByRole("button", { name: "Start Worker" }).click();
|
|
const body = (await submitted).postDataJSON();
|
|
assertEquals(body.workdir_attachments, []);
|
|
assertEquals(body.ticket_assignment, null);
|
|
assertEquals(body.runtime_id, "embedded");
|
|
await page.waitForURL("**/w/home-empty");
|
|
assertEquals(errors, []);
|
|
} finally {
|
|
await context.close();
|
|
}
|
|
}
|
|
}
|
|
} finally {
|
|
await browser.close();
|
|
}
|
|
} finally {
|
|
assertEquals(await stopOwnedProcesses(processes), []);
|
|
}
|
|
});
|