Three dashboard test files asserted against `render()`'s `container`, but the components under test mount through `createPortal` — so `container` is **empty** and every query returns nothing. Same root cause as the earlier portal batch; these are the three that were still held back. | File | Before | After | |---|---|---| | `TaskDetailModal.pr-tab` | failing | pass | | `TaskDetailModal.worktree-terminal` | failing | pass | | `MilestoneSliceInterviewModal` | failing | pass | **Measured: 39/39 passing**, rebased on current main (`3461ae7a92`). Lint clean, FNXC date gate exit 0. ### Why this stayed hidden The queries were a **mix** of `container.querySelector(...)` and `screen.*`. `screen` queries `document`, so they kept working — a portal-mounted modal makes only the `container` half go blind. The result is a file that looks half-alive rather than obviously broken, and the failures present as five different-looking symptoms (`null`, `undefined`, `+0`, `[]`, `-1`) that don't read as one bug. Grouping candidate files by **`container.querySelector` call count** rather than by symptom is what identified these correctly, and — the part that mattered — correctly *excluded* the neighbouring files that were failing for unrelated reasons. ### One thing to know if you repeat this A blanket `container` → `document` replace is wrong: it also rewrites `renderResult.container.querySelector` into `renderResult.document.querySelector`, which is not a thing. That broke two already-passing tests on my first attempt. This uses two separate passes with a lookbehind so only the bare receiver is rewritten. ### Scope Test-side only — **no product code changes**, so no changeset. This does not fix the *cause* (tests are still free to query the wrong root); a lint rule for that is worth considering separately, but it would need to distinguish portal-mounting components from ordinary ones, and I did not want to guess at that boundary inside a test-fix PR. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Tests** * Improved modal and task detail accessibility test reliability by querying rendered elements from the document. * Updated coverage for keyboard navigation, Pull Request status indicators, tab ordering, and onboarding provider cards. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
83 lines
2.3 KiB
TypeScript
83 lines
2.3 KiB
TypeScript
import { fireEvent, render, screen } from "@testing-library/react";
|
|
import { describe, expect, it, vi } from "vitest";
|
|
|
|
vi.mock("../PrPanel", () => ({
|
|
PrPanel: () => <div data-testid="pr-panel-stub">PR Panel</div>,
|
|
}));
|
|
|
|
import {
|
|
makeTask,
|
|
noop,
|
|
noopDelete,
|
|
noopMerge,
|
|
noopMove,
|
|
noopOpenDetail,
|
|
setupTaskDetailModalHooks,
|
|
} from "./TaskDetailModal.test-helpers";
|
|
import { TaskDetailModal } from "../TaskDetailModal";
|
|
|
|
setupTaskDetailModalHooks();
|
|
|
|
describe("TaskDetailModal Pull Request tab", () => {
|
|
it("shows Pull Request tab only for in-review tasks", () => {
|
|
const { rerender } = render(
|
|
<TaskDetailModal
|
|
task={makeTask({ column: "todo" })}
|
|
onClose={noop}
|
|
onMoveTask={noopMove}
|
|
onDeleteTask={noopDelete}
|
|
onMergeTask={noopMerge}
|
|
onOpenDetail={noopOpenDetail}
|
|
addToast={noop}
|
|
/>,
|
|
);
|
|
|
|
expect(screen.queryByRole("button", { name: "Pull Request" })).toBeNull();
|
|
|
|
rerender(
|
|
<TaskDetailModal
|
|
task={makeTask({ column: "in-review" })}
|
|
onClose={noop}
|
|
onMoveTask={noopMove}
|
|
onDeleteTask={noopDelete}
|
|
onMergeTask={noopMerge}
|
|
onOpenDetail={noopOpenDetail}
|
|
addToast={noop}
|
|
/>,
|
|
);
|
|
|
|
expect(screen.getByRole("button", { name: "Pull Request" })).toBeInTheDocument();
|
|
});
|
|
|
|
it("renders PrPanel and in-review stall badge in Pull Request tab, not Definition tab", () => {
|
|
const inReviewStall = {
|
|
code: "merge-failed" as const,
|
|
reason: "merge failed",
|
|
observedAt: "2026-01-01T00:00:00Z",
|
|
consecutiveFailures: 2,
|
|
mergeRetries: 2,
|
|
maxAutoMergeRetries: 3,
|
|
};
|
|
|
|
const { container } = render(
|
|
<TaskDetailModal
|
|
task={makeTask({ column: "in-review", inReviewStall })}
|
|
onClose={noop}
|
|
onMoveTask={noopMove}
|
|
onDeleteTask={noopDelete}
|
|
onMergeTask={noopMerge}
|
|
onOpenDetail={noopOpenDetail}
|
|
addToast={noop}
|
|
/>,
|
|
);
|
|
|
|
expect(screen.queryByTestId("pr-panel-stub")).toBeNull();
|
|
expect(document.querySelector(".detail-in-review-stall")).toBeNull();
|
|
|
|
fireEvent.click(screen.getByRole("button", { name: "Pull Request" }));
|
|
|
|
expect(screen.getByTestId("pr-panel-stub")).toBeInTheDocument();
|
|
expect(document.querySelector(".detail-in-review-stall")).toBeTruthy();
|
|
});
|
|
});
|