storage.ts's registry gives each key a kind (pref, device, view, cache, public); a key without one does not compile. A contract test refuses a key spelled out elsewhere, an account preference read or written straight from storage instead of through usePref, and an e2e seed of a key nobody reads. The keys the next account's wipe keeps are the public ones. The seven preferences kept only in the browser move onto usePref: start on Home, the onboarding dismissal, the release banner's last version, the channel manager's layout, the Plex playlist layout, the playlist rail's sort and channel discovery's sort. usePref reads an older bare-string copy, and a value only this browser held is saved to the server once, so the account's other devices get it. Performance mode is the device's: it is neither read from the account nor saved to it.
54 lines
2.3 KiB
TypeScript
54 lines
2.3 KiB
TypeScript
import { test, expect, type Page } from "@playwright/test";
|
|
import { openApp, gotoPage } from "./helpers";
|
|
|
|
// Locks the Settings AUTO-SAVE contract (the draft + Save button + leave-guard were removed): an
|
|
// Appearance change persists on its own via PUT /me/preferences and a transient indicator confirms
|
|
// it; leaving Settings after a change is no longer guarded.
|
|
test.describe("settings auto-save", () => {
|
|
// An Appearance switch saved to the account (locale pinned to en). Not performance mode: that one
|
|
// is the device's and is never sent.
|
|
const TOGGLE = "Show hints";
|
|
|
|
// The save is debounced (~500ms), so wait for the PUT rather than assuming it's synchronous.
|
|
const putPrefs = (page: Page) =>
|
|
page.waitForResponse(
|
|
(r) => r.url().includes("/me/preferences") && r.request().method() === "PUT",
|
|
);
|
|
|
|
test("changing a setting auto-saves and shows the saved indicator", async ({ page }) => {
|
|
await openApp(page);
|
|
await gotoPage(page, "settings");
|
|
|
|
const toggle = page.getByRole("switch", { name: TOGGLE });
|
|
const indicator = page.getByTestId("settings-save-indicator");
|
|
|
|
// Idle: nothing to save, so no indicator (and no draft Save bar).
|
|
await expect(indicator).toHaveCount(0);
|
|
|
|
// Change it → the PUT fires on its own (no Save click) and the indicator confirms it.
|
|
const [saved] = await Promise.all([putPrefs(page), toggle.click()]);
|
|
expect(saved.ok()).toBeTruthy();
|
|
await expect(indicator).toHaveAttribute("data-state", "saved");
|
|
|
|
// Restore the baseline so the shared account stays clean.
|
|
await Promise.all([putPrefs(page), toggle.click()]);
|
|
});
|
|
|
|
test("a changed setting no longer blocks leaving the page", async ({ page }) => {
|
|
await openApp(page);
|
|
await gotoPage(page, "settings");
|
|
|
|
const toggle = page.getByRole("switch", { name: TOGGLE });
|
|
await Promise.all([putPrefs(page), toggle.click()]);
|
|
|
|
// No unsaved-changes prompt — navigating away just lands on the feed.
|
|
await page.getByTestId("nav-feed").click();
|
|
await expect(page.getByTestId("nav-feed")).toHaveAttribute("aria-current", "page");
|
|
await expect(page.getByTestId("video-card").first()).toBeVisible();
|
|
|
|
// Restore the baseline (back on Settings).
|
|
await gotoPage(page, "settings");
|
|
await Promise.all([putPrefs(page), page.getByRole("switch", { name: TOGGLE }).click()]);
|
|
});
|
|
});
|