trails/apps/journal/app/routes/api.settings.passkey.delete.ts
Ullrich Schäfer 7a290cd56f
e2e: register and repair the settings/explore/social specs
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>
2026-06-11 08:32:18 +02:00

30 lines
1.1 KiB
TypeScript

import { data, redirect } from "react-router";
import { eq, and } from "drizzle-orm";
import type { Route } from "./+types/api.settings.passkey.delete";
import { getSessionUser } from "~/lib/auth/session.server";
import { getDb } from "~/lib/db";
import { credentials } from "@trails-cool/db/schema/journal";
export async function action({ request }: Route.ActionArgs) {
const user = await getSessionUser(request);
if (!user) throw redirect("/auth/login");
const formData = await request.formData();
const credentialId = formData.get("credentialId") as string;
if (!credentialId) return data({ error: "Missing credential ID" }, { status: 400 });
const db = getDb();
const deleted = await db
.delete(credentials)
.where(and(eq(credentials.id, credentialId), eq(credentials.userId, user.id)))
.returning();
if (deleted.length === 0) {
return data({ error: "Passkey not found" }, { status: 404 });
}
// Settings is split into sections now; the bare /settings#security
// anchor resolves to /settings/profile. Send the user back to the
// security section they were on.
return redirect("/settings/security");
}