settings.test.ts, explore.test.ts, and social.test.ts weren't matched
by any Playwright project, so they had silently never run — which is
how they rotted. Register them (one project each) and repair the
selectors against the current UI:
- settings: the page was split into sibling sections
(/settings/{profile,security,account}); the spec assumed one page.
Navigate to the right sub-page per test, use the stable section-nav
links + #id input locators (the Vite dev server transiently
double-renders the profile form during hydration, breaking
getByLabel), and wait for hydration before interacting with
fetcher-backed forms and the avatar dropdown.
- explore: setProfileVisibility now targets /settings/profile and
waits for hydration so the visibility save uses the fetcher.
- social: already green once registered.
Also fixes an app bug the specs surfaced: deleting a passkey redirected
to /settings#security, a stale anchor that now resolves to
/settings/profile — so you'd land on the wrong section. It now
redirects to /settings/security.
Verified locally against Postgres/BRouter: green under CI-style
retries (the residual registration flake is the same cold-start class
the rest of the suite has, which is why CI runs retries=2).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
51 lines
2.2 KiB
TypeScript
51 lines
2.2 KiB
TypeScript
import { test, expect, waitForHydration, type CDPSession, type Page } from "./fixtures/test";
|
|
import { setupVirtualAuthenticator, registerUser } from "./helpers/auth";
|
|
|
|
async function setProfileVisibility(page: Page, value: "public" | "private") {
|
|
await page.goto("/settings/profile");
|
|
// Wait for hydration so the visibility form submits via the fetcher
|
|
// (and shows the "Profile saved." toast) instead of native-navigating.
|
|
await waitForHydration(page);
|
|
await page.locator(`input[type=radio][name=profileVisibility][value=${value}]`).check();
|
|
await page.getByRole("button", { name: /^Save$/ }).first().click();
|
|
await expect(page.getByText("Profile saved.")).toBeVisible({ timeout: 10000 });
|
|
}
|
|
|
|
test.describe.configure({ mode: "serial" });
|
|
|
|
test.describe("/explore", () => {
|
|
test("anonymous visitor can load /explore", async ({ page }) => {
|
|
const resp = await page.goto("/explore");
|
|
expect(resp?.status()).toBe(200);
|
|
await expect(page.getByRole("heading", { name: "Explore" })).toBeVisible();
|
|
});
|
|
|
|
test("private profile is excluded from the directory", async ({ page, browser }) => {
|
|
const cdp = await page.context().newCDPSession(page);
|
|
await setupVirtualAuthenticator(cdp);
|
|
|
|
const stamp = Date.now();
|
|
// A: signed-in viewer (public, default in test below)
|
|
const aEmail = `ex-a-${stamp}@example.com`;
|
|
const aUsername = `exa${stamp}`;
|
|
await registerUser(page, aEmail, aUsername);
|
|
await setProfileVisibility(page, "public");
|
|
|
|
// B: a separate user who stays at the default `private`
|
|
const bCtx = await browser.newContext();
|
|
const bPage = await bCtx.newPage();
|
|
const bCdp = await bPage.context().newCDPSession(bPage);
|
|
await setupVirtualAuthenticator(bCdp);
|
|
const bEmail = `ex-b-${stamp}@example.com`;
|
|
const bUsername = `exb${stamp}`;
|
|
await registerUser(bPage, bEmail, bUsername);
|
|
// B stays private — should NOT appear on /explore.
|
|
|
|
// A loads /explore — the directory should include A but not B.
|
|
await page.goto("/explore");
|
|
await expect(page.getByText(`@${aUsername}`)).toBeVisible({ timeout: 5000 });
|
|
await expect(page.getByText(`@${bUsername}`)).toHaveCount(0);
|
|
|
|
await bCtx.close();
|
|
});
|
|
});
|