fix: folder/category switch overridden by late server response

This commit is contained in:
Tautvydas Derzinskas committed 2026-10-03 19:40:04 +01:00
1 parent 179ce389b6
commit b5ba52d8bf
3 files changed
+35 -9

No files matched your search

+4 -3
View File
@@ -9,10 +9,11 @@ const listeners = new Set<(value: CategoriesView) => void>();
function load(): Promise<CategoriesView> {
if (cached !== undefined) return Promise.resolve(cached);
// A switch made while this is in flight is newer than the server's answer, so it wins.
inFlight ??= settingsApi
.getCategoriesView()
.then((res) => (cached = res.view))
.catch((): CategoriesView => "categories")
.then((res) => (cached ??= res.view))
.catch((): CategoriesView => cached ?? "categories")
.finally(() => {
inFlight = null;
});
@@ -31,7 +32,7 @@ export function useCategoriesView(): [CategoriesView | null, (view: CategoriesVi
useEffect(() => {
let cancelled = false;
void load().then((v) => {
if (!cancelled) setValue(v);
if (!cancelled) setValue(cached ?? v);
});
listeners.add(setValue);
return () => {
+29
View File
@@ -27,3 +27,32 @@ test("uploads an STL, renames it and deletes it", async ({ page, request }) => {
const res = await request.get(`/api/print/${printId}`, { headers: { Authorization: `Bearer ${token}` } });
expect(res.status()).toBe(404);
});
test("switching to Folders while the saved view is still loading isn't undone", async ({ page }) => {
// Answer the saved-view request with the server's (old) value, but only after the switch.
let release!: () => void;
const released = new Promise<void>((r) => (release = r));
await page.route("**/api/settings/categories-view", async (route) => {
if (route.request().method() !== "GET") return route.continue();
const stale = await route.fetch();
await released;
await route.fulfill({ response: stale });
});
await page.goto("/models");
const main = page.getByRole("main");
const panelTitle = main.getByRole("heading", { level: 6 }).nth(1);
await main.getByRole("button", { name: "Folders", exact: true }).click();
await expect(panelTitle).toHaveText("Folders");
const answered = page.waitForResponse((r) => r.url().endsWith("/api/settings/categories-view"));
release();
await answered;
await expect(main.getByRole("button", { name: "Folders", exact: true })).toHaveAttribute("aria-pressed", "true");
await expect(panelTitle).toHaveText("Folders");
// The choice is saved on the account; put it back for the other specs.
await page.unroute("**/api/settings/categories-view");
await main.getByRole("button", { name: "Categories", exact: true }).click();
await expect(panelTitle).toHaveText("Categories");
});
+2 -6
View File
@@ -72,12 +72,8 @@ test("each folder becomes one model: model files as plates, images as previews,
// The tree is browsable from the Models page.
await page.goto("/models");
const main = page.getByRole("main");
const folders = main.getByRole("button", { name: "Folders", exact: true });
// A late-loading saved view can flip the toggle back, so retry until the folder panel shows.
await expect(async () => {
await folders.click();
await expect(main.getByRole("heading", { name: "Folders", level: 6 })).toBeVisible({ timeout: 1000 });
}).toPass();
await main.getByRole("button", { name: "Folders", exact: true }).click();
await expect(main.getByRole("heading", { name: "Folders", level: 6 })).toBeVisible();
await main.getByRole("button", { name: "Fold Library", exact: true }).click();
await expect(main.getByText("Fold Robot", { exact: true })).toBeVisible();
await expect(main.getByText("Fold Vase", { exact: true })).toBeVisible();