diff --git a/src/components/settings/__tests__/token-revoke-labels.test.ts b/src/components/settings/__tests__/token-revoke-labels.test.ts new file mode 100644 index 000000000..880765edc --- /dev/null +++ b/src/components/settings/__tests__/token-revoke-labels.test.ts @@ -0,0 +1,52 @@ +/** + * The token revoke confirmation names the act. + * + * Both token lists (API tokens and MCP connections) labelled the dialog's + * confirm button with `settings.tokenRevoked` — "Revoked", the status word the + * admin overview shows for a token that is already gone — so the button that + * performs the revoke read as if it had happened. The phone layout's trigger + * carried the dialog title instead, question mark included. + * + * Read from the source because the dialog content only mounts while open, and + * an SSR render never sees it. What is pinned: every `AlertDialogAction` that + * calls `handleRevoke` is labelled with the action key, and no trigger or + * action on these two surfaces uses the status word. + */ +import { readFileSync } from "node:fs"; +import { join } from "node:path"; + +import { describe, expect, it } from "vitest"; + +const FILES = [ + "src/components/settings/api-section.tsx", + "src/components/settings/mcp-section.tsx", +]; + +function read(rel: string): string { + return readFileSync(join(process.cwd(), rel), "utf8"); +} + +/** The body of every `…` block. */ +function actions(source: string): string[] { + return [ + ...source.matchAll(//g), + ].map((m) => m[0]); +} + +describe("token revoke labels", () => { + for (const rel of FILES) { + it(`${rel}: the revoke confirm says "Revoke token"`, () => { + const revokes = actions(read(rel)).filter((a) => + a.includes("handleRevoke(tok.id)"), + ); + expect(revokes.length).toBeGreaterThan(0); + for (const action of revokes) { + expect(action).toContain('t("settings.tokenRevokeAction")'); + } + }); + + it(`${rel}: never labels a control with the status word`, () => { + expect(read(rel)).not.toContain('t("settings.tokenRevoked")'); + }); + } +}); diff --git a/src/components/settings/api-section.tsx b/src/components/settings/api-section.tsx index fa0edc26b..267fd27d3 100644 --- a/src/components/settings/api-section.tsx +++ b/src/components/settings/api-section.tsx @@ -619,7 +619,7 @@ function ApiTokensCard() { variant="destructive" onClick={() => handleRevoke(tok.id)} > - {t("settings.tokenRevoked")} + {t("settings.tokenRevokeAction")} @@ -698,7 +698,7 @@ function ApiTokensCard() { className="min-h-11 w-full" > - {t("settings.tokenRevoke")} + {t("settings.tokenRevokeAction")} @@ -718,7 +718,7 @@ function ApiTokensCard() { variant="destructive" onClick={() => handleRevoke(tok.id)} > - {t("settings.tokenRevoked")} + {t("settings.tokenRevokeAction")} diff --git a/src/components/settings/mcp-section.tsx b/src/components/settings/mcp-section.tsx index bc21fcb4f..6b448a5d6 100644 --- a/src/components/settings/mcp-section.tsx +++ b/src/components/settings/mcp-section.tsx @@ -516,7 +516,7 @@ function McpTokensCard() { className="min-h-11 w-full" > - {t("settings.tokenRevoke")} + {t("settings.tokenRevokeAction")} @@ -536,7 +536,7 @@ function McpTokensCard() { variant="destructive" onClick={() => handleRevoke(tok.id)} > - {t("settings.tokenRevoked")} + {t("settings.tokenRevokeAction")}