feat(studio): preserve work position and guide import completion
This commit is contained in:
1 parent
c5a2b44807
commit
89af4eb1f6
6 files changed
+425
-10
No files matched your search
@@ -0,0 +1,185 @@
|
||||
import { expect, type Page, test } from "@playwright/test";
|
||||
|
||||
const items = Array.from({ length: 60 }, (_, index) => ({
|
||||
id: index + 1,
|
||||
classname: `chair_${index}`,
|
||||
name: `Chair ${index}`,
|
||||
description: "Fixture furniture",
|
||||
type: "flooritem",
|
||||
revision: 1,
|
||||
category: "other",
|
||||
alreadyImported: false,
|
||||
nitroExists: false,
|
||||
iconUrl: "/fixture/cover.svg",
|
||||
}));
|
||||
async function mockStudio(page: Page) {
|
||||
await page.route("**/api/**", async (route) => {
|
||||
const url = new URL(route.request().url());
|
||||
if (url.pathname.endsWith("/inspect"))
|
||||
return route.fulfill({
|
||||
json: {
|
||||
items: (route.request().postDataJSON().classnames as string[]).map(
|
||||
(classname) => ({
|
||||
classname,
|
||||
sql: [{ id: 1, spriteId: 1, name: "Chair", type: "s" }],
|
||||
catalog: [],
|
||||
furnidata: [],
|
||||
furnidataReadable: true,
|
||||
nitro: { exists: true, bytes: 42 },
|
||||
icon: { exists: false, bytes: 0 },
|
||||
}),
|
||||
),
|
||||
},
|
||||
});
|
||||
if (url.pathname.endsWith("/source-assets"))
|
||||
return route.fulfill({ json: { items: [] } });
|
||||
if (url.pathname.endsWith("/furni"))
|
||||
return route.fulfill({
|
||||
json: url.searchParams.has("action")
|
||||
? { totalInDb: 0, inCatalog: 0, notInCatalog: 0, missingNitro: 0 }
|
||||
: {
|
||||
items,
|
||||
meta: {
|
||||
currentPage: 1,
|
||||
lastPage: 1,
|
||||
total: items.length,
|
||||
perPage: 100,
|
||||
},
|
||||
},
|
||||
});
|
||||
if (url.pathname.endsWith("/clone"))
|
||||
return route.fulfill({ json: { sources: [] } });
|
||||
if (url.pathname.endsWith("/import-jobs"))
|
||||
return route.fulfill({ json: { ok: true, jobs: [], nextCursor: null } });
|
||||
return route.fulfill({
|
||||
status: 404,
|
||||
json: { error: "Unknown fixture endpoint" },
|
||||
});
|
||||
});
|
||||
}
|
||||
test("closing furniture restores its scroll, focus, search, filter and selection", async ({
|
||||
page,
|
||||
}) => {
|
||||
await mockStudio(page);
|
||||
await page.goto("/admin/studio-harness");
|
||||
await page.getByRole("textbox", { name: "Search furniture" }).fill("chair");
|
||||
await page.waitForResponse(
|
||||
(res) => new URL(res.url()).searchParams.get("search") === "chair",
|
||||
);
|
||||
await page.getByRole("combobox", { name: "Furniture type" }).click();
|
||||
await page.getByRole("option", { name: "Floor items", exact: true }).click();
|
||||
const trigger = page.getByRole("button", {
|
||||
name: "View Chair 30",
|
||||
exact: true,
|
||||
});
|
||||
await trigger.scrollIntoViewIfNeeded();
|
||||
await page
|
||||
.getByRole("checkbox", { name: "Select chair_30", exact: true })
|
||||
.check();
|
||||
const list = page.getByTestId("studio-furniture-list");
|
||||
const top = await list.evaluate((element) => element.scrollTop);
|
||||
await trigger.click();
|
||||
await page
|
||||
.getByRole("button", { name: "Close furniture details", exact: true })
|
||||
.click();
|
||||
await expect(trigger).toBeFocused();
|
||||
expect(await list.evaluate((element) => element.scrollTop)).toBe(top);
|
||||
await expect(
|
||||
page.getByRole("textbox", { name: "Search furniture" }),
|
||||
).toHaveValue("chair");
|
||||
await expect(
|
||||
page.getByRole("combobox", { name: "Furniture type" }),
|
||||
).toContainText("Floor");
|
||||
await expect(
|
||||
page.getByRole("checkbox", { name: "Select chair_30", exact: true }),
|
||||
).toBeChecked();
|
||||
await trigger.click();
|
||||
await page.keyboard.press("Escape");
|
||||
await expect(
|
||||
page.getByRole("button", { name: "Close furniture details", exact: true }),
|
||||
).toHaveCount(0);
|
||||
await expect(trigger).toBeFocused();
|
||||
await expect(
|
||||
page.getByRole("checkbox", { name: "Select chair_30", exact: true }),
|
||||
).toBeChecked();
|
||||
});
|
||||
test("review proposes completing existing furniture and filters actionable groups", async ({
|
||||
page,
|
||||
}) => {
|
||||
await mockStudio(page);
|
||||
await page.goto("/admin/studio-harness");
|
||||
await page
|
||||
.getByRole("button", { name: "Import chair_0", exact: true })
|
||||
.click();
|
||||
const review = page.getByRole("dialog", {
|
||||
name: "Review furniture import",
|
||||
exact: true,
|
||||
});
|
||||
await expect(
|
||||
review.getByText("Suggested: complete existing furniture", { exact: true }),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
review.getByText("Missing: Catalog offer, Furnidata, Local icon"),
|
||||
).toBeVisible();
|
||||
await review
|
||||
.getByRole("button", { name: "Conflicts (0)", exact: true })
|
||||
.click();
|
||||
await expect(
|
||||
review.getByText("No furniture in this group.", { exact: true }),
|
||||
).toBeVisible();
|
||||
await review
|
||||
.getByRole("button", { name: "To complete (1)", exact: true })
|
||||
.click();
|
||||
await expect(
|
||||
review.getByText("Suggested: complete existing furniture", { exact: true }),
|
||||
).toBeVisible();
|
||||
await review
|
||||
.getByRole("button", { name: "Continue to confirmation", exact: true })
|
||||
.click();
|
||||
await expect(
|
||||
page
|
||||
.getByRole("dialog", { name: "Confirm furniture import", exact: true })
|
||||
.getByRole("button", { name: "Start import (1)", exact: true }),
|
||||
).toBeEnabled();
|
||||
});
|
||||
|
||||
test("an older search response cannot replace newer furniture", async ({
|
||||
page,
|
||||
}) => {
|
||||
await mockStudio(page);
|
||||
await page.route("**/api/admin/import/furni?*", async (route) => {
|
||||
const search = new URL(route.request().url()).searchParams.get("search");
|
||||
if (!search) return route.fallback();
|
||||
if (search === "old")
|
||||
await new Promise((resolve) => setTimeout(resolve, 1500));
|
||||
await route.fulfill({
|
||||
json: {
|
||||
items: [
|
||||
{
|
||||
...items[0],
|
||||
name: search === "old" ? "Old response" : "New response",
|
||||
},
|
||||
],
|
||||
meta: { currentPage: 1, lastPage: 1, total: 1, perPage: 100 },
|
||||
},
|
||||
});
|
||||
});
|
||||
await page.goto("/admin/studio-harness");
|
||||
const search = page.getByRole("textbox", { name: "Search furniture" });
|
||||
const oldRequest = page.waitForRequest(
|
||||
(request) => new URL(request.url()).searchParams.get("search") === "old",
|
||||
);
|
||||
await search.fill("old");
|
||||
await oldRequest;
|
||||
await search.fill("new");
|
||||
await expect(
|
||||
page.getByRole("button", { name: "View New response", exact: true }),
|
||||
).toBeVisible();
|
||||
await page.waitForTimeout(1800);
|
||||
await expect(
|
||||
page.getByRole("button", { name: "View New response", exact: true }),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole("button", { name: "View Old response", exact: true }),
|
||||
).toHaveCount(0);
|
||||
});
|
||||
Reference in new issue
Block a user