Files
boss/tests/web-group-participants-repair-client.test.ts

95 lines
3.6 KiB
TypeScript

import test from "node:test";
import assert from "node:assert/strict";
import path from "node:path";
import { readFile } from "node:fs/promises";
import { fileURLToPath } from "node:url";
const testsDir = path.dirname(fileURLToPath(import.meta.url));
const repairClientPath = path.join(testsDir, "../src/components/group-participants-repair-client.tsx");
const participantsPagePath = path.join(testsDir, "../src/app/conversations/[projectId]/participants/page.tsx");
test("web group participants repair client handles empty candidates and disables invalid submissions", async () => {
const source = await readFile(repairClientPath, "utf8");
assert.match(
source,
/const canSubmit = !loading && availableThreads\.length >= 2 && selectedProjectIds\.size >= 2;/,
"expected repair client to derive a submit-ready state from loading, candidates, and selections",
);
assert.match(
source,
/if \(availableThreads\.length < 2\) \{\s*setMessage\("线"\);/s,
"expected repair client to show a specific empty-candidates message before attempting submit",
);
assert.match(
source,
/disabled=\{!canSubmit\}/,
"expected repair submit button to stay disabled until the request is actually valid",
);
assert.match(
source,
/availableThreads\.length \? \(/,
"expected repair client to branch between candidate list and empty-state copy",
);
assert.match(
source,
/try \{/,
"expected repair client to guard network submission with try/finally handling",
);
assert.match(
source,
/catch \(error\) \{/,
"expected repair client to surface a user-facing message when submit fetch rejects",
);
assert.match(
source,
/finally \{\s*setLoading\(false\);/s,
"expected repair client to always clear loading even after a network failure",
);
assert.match(
source,
/router\.replace\(`\/conversations\/\$\{projectId\}\/participants\?repaired=1`\);/,
"expected repair client to persist repair success via URL state before refreshing away",
);
});
test("web group participants page hides repair form when fewer than two real thread candidates remain", async () => {
const source = await readFile(participantsPagePath, "utf8");
assert.match(
source,
/const canRepairGroupMembers = availableThreads\.length >= 2;/,
"expected participants page to compute whether repair is currently possible",
);
assert.match(
source,
/participantsPayload\?\.repairRequired && canRepairGroupMembers \? \(/,
"expected participants page to only mount the repair client when at least two real thread candidates exist",
);
assert.match(
source,
/participantsPayload\?\.repairRequired && !canRepairGroupMembers \? \(/,
"expected participants page to render a fallback guidance card when repair is required but impossible",
);
assert.match(
source,
/当前设备里暂时没有足够的真实线程可用于修复群成员。/,
"expected participants page to explain why direct repair is currently unavailable",
);
assert.match(
source,
/searchParams: Promise<\{ repaired\?: string \| string\[\] \| undefined \}>;/,
"expected participants page to read repaired search params under the current Next page contract",
);
assert.match(
source,
/const repaired = \(await searchParams\)\.repaired;/,
"expected participants page to resolve repaired status from search params",
);
assert.match(
source,
/repaired === \"1\" \? \(/,
"expected participants page to show a durable success acknowledgement after repair refresh",
);
});