Files
EpicNext-Cms/e2e/ui/studio.spec.ts
T
openhands ffd68e604a
CI / check (push) Successful in 4m23s
CI / preflight (push) Skipped
CI / deploy (push) Failing after 3m57s
CI / publish-container (push) Skipped
Fix: e2e test for organize imports dialog by updating item mock data
2026-09-14 19:38:01 +02:00

319 lines
9.3 KiB
TypeScript

import { expect, test } from "@playwright/test";
const items = [
{
id: 1,
classname: "fixture_chair",
name: "Fixture chair",
description: "Synthetic chair",
type: "flooritem",
revision: 1,
category: "other",
alreadyImported: true,
nitroExists: true,
iconUrl: "/fixture/cover.svg",
},
{
id: 2,
classname: "fixture_table",
name: "Fixture table",
description: "Synthetic table",
type: "flooritem",
revision: 1,
category: "other",
alreadyImported: false,
nitroExists: false,
iconUrl: "/fixture/cover.svg",
},
];
test("Studio opens and dismisses detail, Nitro editor and import review", async ({
page,
}) => {
const errors: string[] = [];
const mutations: string[] = [];
let metadataRequests = 0;
let qualityRequests = 0;
page.on("pageerror", (error) => errors.push(error.message));
await page.route("**/api/**", async (route) => {
const request = route.request();
const url = new URL(request.url());
if (url.pathname === "/api/admin/studio/nitro-quality") {
qualityRequests++;
return route.fulfill({
json: {
report: {
scales: [32, 64].map((size) => ({
size,
state: "missing",
assets: [],
animations: [],
directions: [],
issues: [],
})),
},
},
});
}
if (url.pathname === "/api/admin/studio/inspect") {
const { classnames } = request.postDataJSON() as { classnames: string[] };
return route.fulfill({
json: {
items: classnames.map((classname) => ({
classname,
sql: [],
catalog: [],
furnidata: [],
furnidataReadable: true,
nitro: { exists: false, bytes: 0 },
icon: { exists: false, bytes: 0 },
})),
},
});
}
if (url.pathname === "/api/admin/studio/source-assets") {
const body = request.postDataJSON() as {
items: Array<{ classname: string }>;
};
return route.fulfill({
json: {
items: body.items.map(({ classname }) => ({
classname,
state: "available",
revision: 1,
alternatives: [],
attempts: [],
})),
},
});
}
if (request.method() !== "GET") {
mutations.push(`${request.method()} ${url.pathname}`);
return route.fulfill({
status: 405,
json: { error: "Fixture forbids mutations" },
});
}
if (url.pathname.endsWith("/nitro-editor")) {
metadataRequests++;
return route.fulfill({
json: {
metadata: {
name: "fixture_chair",
logicType: "furniture_basic",
visualizationType: "furniture_static",
},
flags: { canstandon: false, cansiton: false, canlayon: false },
},
});
}
if (url.pathname === "/api/admin/import/furni") {
return route.fulfill({
json:
url.searchParams.get("action") === "stats"
? { totalInDb: 1, inCatalog: 1, notInCatalog: 0, missingNitro: 0 }
: {
items,
meta: { currentPage: 1, lastPage: 1, total: 2, perPage: 20 },
},
});
}
if (url.pathname === "/api/admin/import/clone")
return route.fulfill({ json: { sources: [] } });
if (url.pathname === "/api/admin/studio/import-jobs")
return route.fulfill({ json: { ok: true, jobs: [], nextCursor: null } });
return route.fulfill({
status: 404,
json: { error: "Unknown fixture endpoint" },
});
});
await page.goto("/admin/studio-harness");
await expect(
page.getByRole("button", { name: "View Fixture chair", exact: true }),
).toBeVisible();
const history = page.getByRole("button", {
name: "Refresh history",
includeHidden: true,
});
await expect(history).toHaveCount(0);
await page.locator("summary").filter({ hasText: "Import history" }).click();
await expect(history).toBeVisible();
await page.locator("summary").filter({ hasText: "Import history" }).click();
await expect(history).toBeHidden();
await page.locator("summary").filter({ hasText: "Import history" }).click();
await expect(history).toBeVisible();
expect(metadataRequests).toBe(0);
await page
.getByRole("button", { name: "View Fixture chair", exact: true })
.click();
await expect(
page.getByRole("button", { name: "Close furniture details", exact: true }),
).toBeVisible();
await expect(
page.getByText("Synthetic chair", { exact: true }).first(),
).toBeVisible();
await page
.getByRole("button", { name: "Close furniture details", exact: true })
.click();
await expect(
page.getByRole("button", { name: "Close furniture details", exact: true }),
).toHaveCount(0);
await page
.getByRole("button", { name: "Edit nitro fixture_chair", exact: true })
.click();
const editor = page.getByRole("dialog", { name: "Edit Nitro", exact: true });
await expect(editor).toBeVisible();
await expect(
editor.getByRole("switch", { name: "Can Sit On", exact: true }),
).toBeVisible();
expect(metadataRequests).toBe(1);
expect(qualityRequests).toBe(0);
await editor.getByRole("tab", { name: "32 / 64", exact: true }).click();
await expect(
editor.getByRole("button", { name: "Preview generation", exact: true }),
).toBeVisible();
await expect(
editor.getByText("Scale 32 · Not included", { exact: true }),
).toBeVisible();
expect(qualityRequests).toBe(1);
await editor.getByRole("tab", { name: "Visual", exact: true }).click();
await expect(
editor.getByRole("switch", { name: "Can Sit On", exact: true }),
).toBeVisible();
await editor.getByRole("button", { name: "Cancel", exact: true }).click();
await expect(editor).toHaveCount(0);
await page
.getByRole("button", { name: "Import fixture_table", exact: true })
.click();
const review = page.getByRole("dialog", {
name: "Review furniture import",
exact: true,
});
await expect(review).toBeVisible();
await expect(
review.getByText("Fixture table", { exact: true }),
).toBeVisible();
await expect(
review.getByRole("button", {
name: "Continue to confirmation",
exact: true,
}),
).toBeEnabled();
await review.getByRole("button", { name: "Cancel", exact: true }).click();
await expect(review).toHaveCount(0);
expect(mutations).toEqual([]);
expect(errors).toEqual([]);
});
test("Studio loads organize imports on demand and preserves the dialog on reopen", async ({
page,
}, testInfo) => {
const errors: string[] = [];
const mutations: string[] = [];
let organizeRequests = 0;
page.on("pageerror", (error) => errors.push(error.message));
await page.route("**/fixture/actions/**", async (route) => {
mutations.push(route.request().url());
await route.fulfill({
status: 405,
json: { error: "No mutations expected" },
});
});
await page.route("**/api/**", async (route) => {
const request = route.request();
const url = new URL(request.url());
if (request.method() !== "GET") {
mutations.push(`${request.method()} ${url.pathname}`);
return route.fulfill({
status: 405,
json: { error: "No mutations expected" },
});
}
if (url.pathname === "/api/admin/import/organize") {
organizeRequests++;
return route.fulfill({
json: {
groups: [
{
name: "Fixture imports",
icon: 1,
layout: "default_3x3",
total: 1,
moved: 0,
added: 1,
thumbs: [],
items: [
{
itemId: 1,
catalogItemId: null,
alreadyPlaced: false,
itemName: "fixture_table",
publicName: "Fixture Table",
spriteId: 0,
type: "s",
interactionType: "default",
sourcePageCaption: null,
},
],
},
],
importRootPageId: null,
importRootCaption: null,
},
});
}
if (url.pathname === "/api/admin/catalog/tree")
return route.fulfill({ json: { pages: [] } });
if (url.pathname === "/api/admin/import/furni")
return route.fulfill({
json:
url.searchParams.get("action") === "stats"
? { totalInDb: 0, inCatalog: 0, notInCatalog: 0, missingNitro: 0 }
: {
items: [],
meta: { currentPage: 1, lastPage: 1, total: 0, perPage: 20 },
},
});
if (url.pathname === "/api/admin/import/clone")
return route.fulfill({ json: { sources: [] } });
if (url.pathname === "/api/admin/studio/import-jobs")
return route.fulfill({ json: { ok: true, jobs: [], nextCursor: null } });
return route.fulfill({
status: 404,
json: { error: "Unknown fixture endpoint" },
});
});
await page.goto("/admin/studio-harness");
const trigger = page.getByRole("button", {
name: "Organize imports",
exact: true,
});
await expect(trigger).toBeVisible();
expect(organizeRequests).toBe(0);
await trigger.click();
const dialog = page.getByRole("dialog", { name: /^Organize imports/ });
await expect(dialog).toBeVisible();
const filter = dialog.getByPlaceholder("Filter import groups...");
await expect(filter).toBeVisible();
await expect(dialog.locator('input[value="Fixture imports"]')).toBeVisible();
await filter.fill("Fixture");
expect(organizeRequests).toBe(1);
await testInfo.attach("organize-imports", {
body: await page.screenshot(),
contentType: "image/png",
});
await dialog.getByRole("button", { name: "Cancel", exact: true }).click();
await expect(dialog).toHaveCount(0);
await expect(trigger).toBeFocused();
await trigger.click();
await expect(dialog).toBeVisible();
await expect(filter).toHaveValue("Fixture");
await expect.poll(() => organizeRequests).toBe(2);
await page.keyboard.press("Escape");
await expect(dialog).toHaveCount(0);
await expect(trigger).toBeFocused();
expect(mutations).toEqual([]);
expect(errors).toEqual([]);
});