FN-6441: rescue and quarantine dashboard component tests

Rescue passing dashboard component tests and move still-failing orphaned tests under the quarantine ratchet.

- Remove the FN-6441 dashboard component batch from the curated skip-list.
- Keep passing PlanningModeModal, TaskReviewTab, and TerminalModal coverage documented in app backfill.
- Register remaining failing dashboard component/CSS tests in the dated quarantine ledger and Vitest excludes.
- Teach the dashboard curated inventory guard to accept dated quarantine entries without returning them to the skip-list.

Files changed:
 .../PlanningModeModal.ui-interactions.test.tsx     |  9 ++-
 .../components/__tests__/TaskReviewTab.test.tsx    |  4 ++
 .../components/__tests__/TerminalModal.test.tsx    |  4 ++
 packages/dashboard/vitest.config.ts                | 14 +++++
 scripts/__tests__/check-test-inventory.test.mjs    | 32 +++++++++++
 scripts/check-test-inventory.mjs                   | 67 +++++++++++++++++-----
 scripts/lib/dashboard-curated-skiplist.json        | 56 ------------------
 scripts/lib/test-quarantine.json                   | 58 ++++++++++++++++++-
 8 files changed, 172 insertions(+), 72 deletions(-)

Fusion-Task-Id: FN-6441

Fusion-Task-Lineage: 19065d1f-31eb-45fc-a6ce-083773b094e4
This commit is contained in:
gsxdsm
2026-06-14 08:39:17 -07:00
parent 0b047d4fc8
commit 175aab15ac
8 changed files with 172 additions and 72 deletions

View File

@@ -1,7 +1,12 @@
/*
FNXC:DashboardTests 2026-06-14-08:31:
FN-6441 rescued this orphaned component test after standalone dashboard-app execution passed without assertion, timeout, or source-code changes. Keep the planning modal UI-interaction coverage in app backfill so question flow, summary, and breakdown interactions remain executed after leaving the skip-list.
FNXC:DashboardTests 2026-06-14-08:32:
PlanningModeModal calls useToast(), which throws without a ToastProvider. These tests render it bare, so the hook stays mocked in the same style as PlanningModeModal.autosize.test.tsx instead of introducing broad provider wiring during skip-list rescue.
*/
import { describe, it, expect, vi, beforeEach } from "vitest";
// PlanningModeModal calls useToast(), which throws without a ToastProvider.
// These tests render it bare, so mock the hook (mirrors PlanningModeModal.autosize.test.tsx).
vi.mock("../../hooks/useToast", () => ({
useToast: () => ({
addToast: vi.fn(),

View File

@@ -1,3 +1,7 @@
/*
FNXC:DashboardTests 2026-06-14-08:31:
FN-6441 rescued this orphaned component test after standalone dashboard-app execution passed without assertion, timeout, or source-code changes. Keep it registered through the app backfill lane so task-review UI regressions cannot silently fall out of quality coverage again.
*/
import { describe, it, expect, vi, beforeEach } from "vitest";
import { act, render as rtlRender, screen, fireEvent, waitFor } from "@testing-library/react";
import { TaskReviewTab } from "../TaskReviewTab";

View File

@@ -1,3 +1,7 @@
/*
FNXC:DashboardTests 2026-06-14-08:31:
FN-6441 rescued this orphaned component test after standalone dashboard-app execution passed without assertion, timeout, or source-code changes. Keep the terminal modal coverage in app backfill because keyboard, session, and mobile terminal regressions are user-facing and should not remain skip-listed.
*/
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
import { render, screen, fireEvent, waitFor, act } from "@testing-library/react";
import { TerminalModal, _resetInitialViewportHeight, ctrlChar, altChar } from "../TerminalModal";

View File

@@ -241,7 +241,21 @@ const quarantinedDashboardTests: string[] = [
FNXC:DashboardTests 2026-06-14-02:24:
FN-6433 rescued the dashboard quarantine batch after unquarantined app-backfill and API-quality runs passed with no assertion or timeout changes. Keep this array empty unless a future dashboard quarantine is mirrored in scripts/lib/test-quarantine.json in the same commit.
FNXC:DashboardTests 2026-06-14-08:28:
FN-6441 removed the dashboard component orphan batch from the curated skip-list so passing rescues run in backfill and still-failing tests are excluded only through the dated quarantine ledger. Keep these one-line excludes mirrored with scripts/lib/test-quarantine.json until each file is rescued or deleted under the deletion ratchet.
*/
"app/components/__tests__/ChatView.regular-composer-no-right-line.test.tsx",
"app/components/__tests__/MissionManager.test.tsx",
"app/components/__tests__/ModalReentry.test.tsx",
"app/components/__tests__/ModelSelectorTab.test.tsx",
"app/components/__tests__/NewAgentDialog.test.tsx",
"app/components/__tests__/OAuthReloginBanner.test.tsx",
"app/components/__tests__/PlanningModeModal.favorites.test.tsx",
"app/components/__tests__/PlanningModeModal.questions.test.tsx",
"app/components/__tests__/PlanningModeModal.swipe-back.test.tsx",
"app/components/__tests__/SkillsView.css.test.ts",
"app/components/__tests__/mobile-css.test.tsx",
];
const qualityApiTests = [

View File

@@ -119,6 +119,38 @@ test("curated guard: a skip-listed file does not trip the unregistered check", (
assert.equal(ok, true);
});
test("curated guard: a quarantined file is registered without returning to the skip-list", () => {
const { ok, errors } = validateDashboardCurated({
includedFiles: new Set(),
allTestFiles: ["packages/dashboard/app/quarantined.test.ts"],
skipList: [],
quarantineList: [
{
file: "packages/dashboard/app/quarantined.test.ts",
reason: "quarantined under deletion ratchet FN-4",
quarantinedAt: "2026-06-14",
},
],
});
assert.equal(ok, true, errors.join("; "));
});
test("curated guard: rejects quarantine entries without a ratchet date", () => {
const { ok, errors } = validateDashboardCurated({
includedFiles: new Set(),
allTestFiles: ["packages/dashboard/app/quarantined.test.ts"],
skipList: [],
quarantineList: [
{
file: "packages/dashboard/app/quarantined.test.ts",
reason: "quarantined under deletion ratchet FN-4",
},
],
});
assert.equal(ok, false);
assert.ok(errors.some((e) => e.includes("quarantinedAt")));
});
// ---------------------------------------------------------------------------
// end-to-end curated guard against a synthetic temp fixture dir, exercising
// the real file walk + skip-list validation in one pass (no real repo file).

View File

@@ -27,10 +27,10 @@
* --dashboard-curated
* Assert that every `*.test.{ts,tsx}` file under packages/dashboard/app
* and packages/dashboard/src is included by at least one *executed*
* dashboard quality project, OR listed on the explicit skip-list with a
* non-empty reason. Fails (exit 1) otherwise. This closes the curated-gate
* coverage hole: a new dashboard test file that nobody registered trips
* this guard.
* dashboard quality project, OR listed on the explicit skip-list / dated
* quarantine ledger with a non-empty reason. Fails (exit 1) otherwise.
* This closes the curated-gate coverage hole: a new dashboard test file
* that nobody registered trips this guard.
*
* The capture spec (which packages/projects to enumerate) is data, not code:
* it lives in scripts/lib/test-inventory-spec.json so the CI shard planner and
@@ -48,9 +48,10 @@ const REPO_ROOT = resolve(__dirname, "..");
const DEFAULT_SPEC_PATH = join(__dirname, "lib", "test-inventory-spec.json");
const DASHBOARD_SKIPLIST_PATH = join(__dirname, "lib", "dashboard-curated-skiplist.json");
const TEST_QUARANTINE_PATH = join(__dirname, "lib", "test-quarantine.json");
// ---------------------------------------------------------------------------
// Spec + skip-list loading
// Spec + skip-list / quarantine loading
// ---------------------------------------------------------------------------
function loadSpec(specPathOverride) {
@@ -73,6 +74,17 @@ function loadSkipList(skipListPathOverride) {
return { skipListPath, entries: raw.entries };
}
function loadQuarantineList(quarantinePathOverride) {
const quarantinePath =
quarantinePathOverride || process.env.FUSION_TEST_QUARANTINE || TEST_QUARANTINE_PATH;
if (!existsSync(quarantinePath)) return { quarantinePath, entries: [] };
const raw = JSON.parse(readFileSync(quarantinePath, "utf8"));
if (!Array.isArray(raw.entries)) {
throw new Error(`quarantine ledger ${quarantinePath} must have an "entries" array`);
}
return { quarantinePath, entries: raw.entries };
}
// ---------------------------------------------------------------------------
// vitest list invocation
// ---------------------------------------------------------------------------
@@ -201,9 +213,10 @@ function walkTestFiles(rootDir, repoRoot) {
* @param {Set<string>} opts.includedFiles repo-relative files executed by quality projects
* @param {string[]} opts.allTestFiles repo-relative dashboard app/src test files
* @param {Array<{file:string,reason:string}>} opts.skipList
* @param {Array<{file:string,reason:string,quarantinedAt?:string}>} [opts.quarantineList]
* @returns {{ ok: boolean, errors: string[] }}
*/
export function validateDashboardCurated({ includedFiles, allTestFiles, skipList }) {
export function validateDashboardCurated({ includedFiles, allTestFiles, skipList, quarantineList = [] }) {
const errors = [];
const skipByFile = new Map();
for (const entry of skipList) {
@@ -217,18 +230,36 @@ export function validateDashboardCurated({ includedFiles, allTestFiles, skipList
skipByFile.set(entry.file, entry);
}
// A skip-listed file that is actually covered is allowed but noisy; we don't
// error on it (it keeps the guard green while a flaky file is being fixed).
const quarantineByFile = new Map();
for (const entry of quarantineList) {
if (!entry || typeof entry.file !== "string" || entry.file.length === 0) {
errors.push(`quarantine entry missing "file": ${JSON.stringify(entry)}`);
continue;
}
if (typeof entry.reason !== "string" || entry.reason.trim().length === 0) {
errors.push(`quarantine entry for ${entry.file} has an empty "reason"`);
}
if (typeof entry.quarantinedAt !== "string" || entry.quarantinedAt.trim().length === 0) {
errors.push(`quarantine entry for ${entry.file} has an empty "quarantinedAt"`);
}
quarantineByFile.set(entry.file, entry);
}
/*
FNXC:DashboardTesting 2026-06-14-08:42:
A quarantined dashboard test is intentionally not executed by quality projects, but it must not be re-added to the curated skip-list. Treat the dated quarantine ledger as a second explicit registration source so rescued tests can leave the skip-list while failing tests remain governed by the deletion ratchet.
*/
for (const file of allTestFiles) {
if (includedFiles.has(file)) continue;
if (skipByFile.has(file)) continue;
if (quarantineByFile.has(file)) continue;
errors.push(
`dashboard test file is not executed by any quality project and is not skip-listed: ${file}`,
`dashboard test file is not executed by any quality project and is not skip-listed or quarantined: ${file}`,
);
}
// Stale skip-list entries pointing at deleted files are a soft error so the
// list doesn't rot, but only when the file genuinely no longer exists.
// Stale explicit registrations pointing at deleted files are a soft error so
// the lists don't rot, but only when the file genuinely no longer exists.
for (const entry of skipList) {
if (!entry || typeof entry.file !== "string") continue;
if (!allTestFiles.includes(entry.file) && !includedFiles.has(entry.file)) {
@@ -238,6 +269,15 @@ export function validateDashboardCurated({ includedFiles, allTestFiles, skipList
}
}
}
for (const entry of quarantineList) {
if (!entry || typeof entry.file !== "string") continue;
if (!allTestFiles.includes(entry.file) && !includedFiles.has(entry.file)) {
const abs = join(REPO_ROOT, entry.file);
if (!existsSync(abs)) {
errors.push(`quarantine ledger references a non-existent file: ${entry.file}`);
}
}
}
return { ok: errors.length === 0, errors };
}
@@ -321,7 +361,8 @@ async function main() {
].sort();
const includedFiles = listExecutedDashboardQualityFiles();
const { entries: skipList } = loadSkipList();
const { ok, errors } = validateDashboardCurated({ includedFiles, allTestFiles, skipList });
const { entries: quarantineList } = loadQuarantineList();
const { ok, errors } = validateDashboardCurated({ includedFiles, allTestFiles, skipList, quarantineList });
if (!ok) {
console.error(`✗ dashboard curated-gate guard failed (${errors.length} issue(s)):`);
for (const e of errors) console.error(` - ${e}`);
@@ -330,7 +371,7 @@ async function main() {
console.log(
`✓ dashboard curated gate complete: ${allTestFiles.length} test files, ${
includedFiles.size
} executed, ${skipList.length} skip-listed`,
} executed, ${skipList.length} skip-listed, ${quarantineList.length} quarantined`,
);
return;
}

View File

@@ -5,62 +5,6 @@
"file": "packages/dashboard/app/__tests__/build-output.test.ts",
"reason": "asserts the built bundle; runs standalone via `pnpm --filter @fusion/dashboard test:build` (needs a prior vite build), not in the unit gate"
},
{
"file": "packages/dashboard/app/components/__tests__/ChatView.regular-composer-no-right-line.test.tsx",
"reason": "pre-existing failure (orphaned from curated gate, never previously executed in CI; tracked as FN-6441)"
},
{
"file": "packages/dashboard/app/components/__tests__/MissionManager.test.tsx",
"reason": "pre-existing failure (orphaned from curated gate, never previously executed in CI; tracked as FN-6441)"
},
{
"file": "packages/dashboard/app/components/__tests__/ModalReentry.test.tsx",
"reason": "pre-existing failure (orphaned from curated gate, never previously executed in CI; tracked as FN-6441)"
},
{
"file": "packages/dashboard/app/components/__tests__/ModelSelectorTab.test.tsx",
"reason": "pre-existing failure (orphaned from curated gate, never previously executed in CI; tracked as FN-6441)"
},
{
"file": "packages/dashboard/app/components/__tests__/NewAgentDialog.test.tsx",
"reason": "pre-existing failure (orphaned from curated gate, never previously executed in CI; tracked as FN-6441)"
},
{
"file": "packages/dashboard/app/components/__tests__/OAuthReloginBanner.test.tsx",
"reason": "pre-existing failure (orphaned from curated gate, never previously executed in CI; tracked as FN-6441)"
},
{
"file": "packages/dashboard/app/components/__tests__/PlanningModeModal.favorites.test.tsx",
"reason": "pre-existing failure (orphaned from curated gate, never previously executed in CI; tracked as FN-6441)"
},
{
"file": "packages/dashboard/app/components/__tests__/PlanningModeModal.questions.test.tsx",
"reason": "pre-existing failure (orphaned from curated gate, never previously executed in CI; tracked as FN-6441)"
},
{
"file": "packages/dashboard/app/components/__tests__/PlanningModeModal.swipe-back.test.tsx",
"reason": "pre-existing failure (orphaned from curated gate, never previously executed in CI; tracked as FN-6441)"
},
{
"file": "packages/dashboard/app/components/__tests__/PlanningModeModal.ui-interactions.test.tsx",
"reason": "pre-existing failure (orphaned from curated gate, never previously executed in CI; tracked as FN-6441)"
},
{
"file": "packages/dashboard/app/components/__tests__/SkillsView.css.test.ts",
"reason": "pre-existing failure (orphaned from curated gate, never previously executed in CI; tracked as FN-6441)"
},
{
"file": "packages/dashboard/app/components/__tests__/TaskReviewTab.test.tsx",
"reason": "pre-existing failure (orphaned from curated gate, never previously executed in CI; tracked as FN-6441)"
},
{
"file": "packages/dashboard/app/components/__tests__/TerminalModal.test.tsx",
"reason": "pre-existing failure (orphaned from curated gate, never previously executed in CI; tracked as FN-6441)"
},
{
"file": "packages/dashboard/app/components/__tests__/mobile-css.test.tsx",
"reason": "pre-existing failure (orphaned from curated gate, never previously executed in CI; tracked as FN-6441)"
},
{
"file": "packages/dashboard/app/hooks/__tests__/quickChatLastSessionStorage.test.ts",
"reason": "pre-existing failure (orphaned from curated gate, never previously executed in CI; tracked as FN-6442)"

View File

@@ -1,4 +1,60 @@
{
"$comment": "Flaky-test quarantine ledger (deletion ratchet \u2014 see AGENTS.md 'Flaky tests: quarantine on sight' and docs/testing.md 'Quarantine ledger and the deletion ratchet'). A test observed failing without a corresponding real bug is quarantined ON SIGHT: add an entry here AND a matching one-line `exclude` entry in that package's vitest config, in the same commit. Every entry needs a non-empty `reason` (link the failing run) and a `quarantinedAt` date \u2014 the entry expires 14 days later, at which point the test file is DELETED unless someone rescues it with evidence it catches real regressions plus a root-cause fix (never appeasement). There is deliberately no loader module and no automation around this file: it is a dated record, the vitest config exclude is the mechanism, and the sweep is policy executed by whoever touches the suite.",
"entries": []
"entries": [
{
"file": "packages/dashboard/app/components/__tests__/ChatView.regular-composer-no-right-line.test.tsx",
"reason": "FN-6441: orphaned dashboard component test fails standalone; ChatView emits act warnings and regular-composer right-line invariant assertion fails. Quarantined instead of widening waits or weakening assertions.",
"quarantinedAt": "2026-06-14"
},
{
"file": "packages/dashboard/app/components/__tests__/MissionManager.test.tsx",
"reason": "FN-6441: orphaned dashboard component test fails standalone with stale mission hierarchy/progress/status expectations while most cases pass. Quarantined for rescue/delete ratchet instead of assertion appeasement.",
"quarantinedAt": "2026-06-14"
},
{
"file": "packages/dashboard/app/components/__tests__/ModalReentry.test.tsx",
"reason": "FN-6441: orphaned dashboard component test fails standalone because PlanningModal cases render outside ToastProvider. Quarantined for harness rescue instead of product/source changes.",
"quarantinedAt": "2026-06-14"
},
{
"file": "packages/dashboard/app/components/__tests__/ModelSelectorTab.test.tsx",
"reason": "FN-6441: orphaned dashboard component test fails standalone across model selector cases because expected Executor Model labels/options are no longer rendered by the current component contract. Quarantined for harness/expectation rescue.",
"quarantinedAt": "2026-06-14"
},
{
"file": "packages/dashboard/app/components/__tests__/NewAgentDialog.test.tsx",
"reason": "FN-6441: orphaned dashboard component test fails standalone across broad dialog flows with duplicate fetch/update calls and stale favorite labels. Quarantined for focused rescue rather than timeout/assertion appeasement.",
"quarantinedAt": "2026-06-14"
},
{
"file": "packages/dashboard/app/components/__tests__/OAuthReloginBanner.test.tsx",
"reason": "FN-6441: orphaned dashboard component test times out every case under current async/polling behavior. Quarantined instead of increasing test timeouts.",
"quarantinedAt": "2026-06-14"
},
{
"file": "packages/dashboard/app/components/__tests__/PlanningModeModal.favorites.test.tsx",
"reason": "FN-6441: orphaned dashboard component test fails standalone because PlanningModeModal favorite/keyboard cases render outside ToastProvider. Quarantined for harness rescue.",
"quarantinedAt": "2026-06-14"
},
{
"file": "packages/dashboard/app/components/__tests__/PlanningModeModal.questions.test.tsx",
"reason": "FN-6441: orphaned dashboard component test fails standalone because PlanningModeModal question/summary cases render outside ToastProvider. Quarantined for harness rescue.",
"quarantinedAt": "2026-06-14"
},
{
"file": "packages/dashboard/app/components/__tests__/PlanningModeModal.swipe-back.test.tsx",
"reason": "FN-6441: orphaned dashboard component test fails standalone because PlanningModeModal mobile navigation cases render outside ToastProvider. Quarantined for harness rescue.",
"quarantinedAt": "2026-06-14"
},
{
"file": "packages/dashboard/app/components/__tests__/SkillsView.css.test.ts",
"reason": "FN-6441: orphaned dashboard CSS guardrail fails standalone because runtime-card toggle positioning expectation no longer matches current stylesheet. Quarantined for rescue/delete review without weakening assertion.",
"quarantinedAt": "2026-06-14"
},
{
"file": "packages/dashboard/app/components/__tests__/mobile-css.test.tsx",
"reason": "FN-6441: orphaned dashboard CSS foundation test fails standalone on stale workflow-step-manager modal and breakpoint assertions. Quarantined for rescue/delete review without broad CSS changes.",
"quarantinedAt": "2026-06-14"
}
]
}