mirror of
https://github.com/kuhyx/testsAndMisc-archive.git
synced 2026-11-10 19:27:05 +01:00
bucket_catch: share the must() DOM helper from src/test/
The eslint commit added must() to DropZone.test.tsx, which pushed that file from exactly 250 lines to 256 - so it removed lint errors while silently adding an over-cap file. Moving the helper to src/test/domQueries.ts drops it to 229 and takes the session's over-cap count to its intended 41 -> 39. src/test/ is the right home: coverage excludes that path, which is why the fixtures already live there. PuzzleResult.test.tsx now uses the same helper instead of its own inline instanceof guard. 145 tests, coverage 100% with totals unchanged at 579/217/98/530, eslint still at the 4 known remaining errors. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011w2Pj4Ek3ZNMQVYcFHgXDY
This commit is contained in:
parent
031bff898e
commit
b866561c27
@ -1,31 +1,16 @@
|
|||||||
import { describe, it, expect, vi } from "vitest";
|
import { describe, it, expect, vi } from "vitest";
|
||||||
import { render, screen, fireEvent } from "@testing-library/react";
|
import { render, screen, fireEvent } from "@testing-library/react";
|
||||||
import { DropZone } from "./DropZone";
|
import { DropZone } from "./DropZone";
|
||||||
|
import { must } from "../test/domQueries";
|
||||||
|
|
||||||
const makeFile = (name: string, type = "text/plain"): File =>
|
const makeFile = (name: string, type = "text/plain"): File =>
|
||||||
new File(["x"], name, { type });
|
new File(["x"], name, { type });
|
||||||
|
|
||||||
/**
|
|
||||||
* Query one element and narrow it, failing the test if it is absent.
|
|
||||||
*
|
|
||||||
* Both `as HTMLElement` and `!` are rejected here - the former by
|
|
||||||
* non-nullable-type-assertion-style, the latter by no-non-null-assertion - so
|
|
||||||
* the narrowing is done with a real check that also gives a useful message.
|
|
||||||
*/
|
|
||||||
const must = (el: Element | null, what: string): HTMLElement => {
|
|
||||||
if (!(el instanceof HTMLElement)) {
|
|
||||||
throw new Error(`expected to find ${what}`);
|
|
||||||
}
|
|
||||||
return el;
|
|
||||||
};
|
|
||||||
|
|
||||||
describe("DropZone", () => {
|
describe("DropZone", () => {
|
||||||
it("renders title and subtitle", () => {
|
it("renders title and subtitle", () => {
|
||||||
render(<DropZone onFiles={() => undefined} onPuzzle={() => undefined} />);
|
render(<DropZone onFiles={() => undefined} onPuzzle={() => undefined} />);
|
||||||
expect(screen.getByText("Bucket Catch")).toBeInTheDocument();
|
expect(screen.getByText("Bucket Catch")).toBeInTheDocument();
|
||||||
expect(
|
expect(screen.getByText(/Drop your files here/)).toBeInTheDocument();
|
||||||
screen.getByText(/Drop your files here/),
|
|
||||||
).toBeInTheDocument();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("shows initial grid equation", () => {
|
it("shows initial grid equation", () => {
|
||||||
@ -37,10 +22,7 @@ describe("DropZone", () => {
|
|||||||
const { container } = render(
|
const { container } = render(
|
||||||
<DropZone onFiles={() => undefined} onPuzzle={() => undefined} />,
|
<DropZone onFiles={() => undefined} onPuzzle={() => undefined} />,
|
||||||
);
|
);
|
||||||
const zone = must(
|
const zone = must(container.firstChild as Element | null, "the drop zone");
|
||||||
container.firstChild as Element | null,
|
|
||||||
"the drop zone",
|
|
||||||
);
|
|
||||||
expect(zone.className).not.toContain("dragging");
|
expect(zone.className).not.toContain("dragging");
|
||||||
fireEvent.dragOver(zone);
|
fireEvent.dragOver(zone);
|
||||||
expect(zone.className).toContain("dragging");
|
expect(zone.className).toContain("dragging");
|
||||||
@ -53,10 +35,7 @@ describe("DropZone", () => {
|
|||||||
const { container } = render(
|
const { container } = render(
|
||||||
<DropZone onFiles={onFiles} onPuzzle={() => undefined} />,
|
<DropZone onFiles={onFiles} onPuzzle={() => undefined} />,
|
||||||
);
|
);
|
||||||
const zone = must(
|
const zone = must(container.firstChild as Element | null, "the drop zone");
|
||||||
container.firstChild as Element | null,
|
|
||||||
"the drop zone",
|
|
||||||
);
|
|
||||||
const f1 = makeFile("a.txt");
|
const f1 = makeFile("a.txt");
|
||||||
const f2 = makeFile("b.txt");
|
const f2 = makeFile("b.txt");
|
||||||
fireEvent.drop(zone, { dataTransfer: { files: [f1, f2] } });
|
fireEvent.drop(zone, { dataTransfer: { files: [f1, f2] } });
|
||||||
@ -68,10 +47,7 @@ describe("DropZone", () => {
|
|||||||
const { container } = render(
|
const { container } = render(
|
||||||
<DropZone onFiles={() => undefined} onPuzzle={() => undefined} />,
|
<DropZone onFiles={() => undefined} onPuzzle={() => undefined} />,
|
||||||
);
|
);
|
||||||
const zone = must(
|
const zone = must(container.firstChild as Element | null, "the drop zone");
|
||||||
container.firstChild as Element | null,
|
|
||||||
"the drop zone",
|
|
||||||
);
|
|
||||||
fireEvent.drop(zone, { dataTransfer: { files: [makeFile("a.txt")] } });
|
fireEvent.drop(zone, { dataTransfer: { files: [makeFile("a.txt")] } });
|
||||||
expect(screen.getByText("1 file ready")).toBeInTheDocument();
|
expect(screen.getByText("1 file ready")).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
@ -81,10 +57,7 @@ describe("DropZone", () => {
|
|||||||
const { container } = render(
|
const { container } = render(
|
||||||
<DropZone onFiles={onFiles} onPuzzle={() => undefined} />,
|
<DropZone onFiles={onFiles} onPuzzle={() => undefined} />,
|
||||||
);
|
);
|
||||||
const zone = must(
|
const zone = must(container.firstChild as Element | null, "the drop zone");
|
||||||
container.firstChild as Element | null,
|
|
||||||
"the drop zone",
|
|
||||||
);
|
|
||||||
fireEvent.drop(zone, { dataTransfer: { files: [] } });
|
fireEvent.drop(zone, { dataTransfer: { files: [] } });
|
||||||
expect(onFiles).not.toHaveBeenCalled();
|
expect(onFiles).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
@ -201,9 +174,9 @@ describe("DropZone", () => {
|
|||||||
container.querySelector("input[accept='image/*']"),
|
container.querySelector("input[accept='image/*']"),
|
||||||
"input[accept='image/*']",
|
"input[accept='image/*']",
|
||||||
);
|
);
|
||||||
const clickSpy = vi.spyOn(puzzleInput, "click").mockImplementation(
|
const clickSpy = vi
|
||||||
() => undefined,
|
.spyOn(puzzleInput, "click")
|
||||||
);
|
.mockImplementation(() => undefined);
|
||||||
fireEvent.click(screen.getByText(/Play Puzzle Mode/));
|
fireEvent.click(screen.getByText(/Play Puzzle Mode/));
|
||||||
expect(clickSpy).toHaveBeenCalled();
|
expect(clickSpy).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import { describe, it, expect, vi } from "vitest";
|
import { describe, it, expect, vi } from "vitest";
|
||||||
import { render, screen, fireEvent } from "@testing-library/react";
|
import { render, screen, fireEvent } from "@testing-library/react";
|
||||||
import { PuzzleResult } from "./PuzzleResult";
|
import { PuzzleResult } from "./PuzzleResult";
|
||||||
|
import { must } from "../test/domQueries";
|
||||||
import type { FallingPuzzleItem, PuzzlePiece } from "../types";
|
import type { FallingPuzzleItem, PuzzlePiece } from "../types";
|
||||||
|
|
||||||
const makePiece = (row: number, col: number): PuzzlePiece => ({
|
const makePiece = (row: number, col: number): PuzzlePiece => ({
|
||||||
@ -129,10 +130,10 @@ describe("PuzzleResult", () => {
|
|||||||
const imgs = container.querySelectorAll("img");
|
const imgs = container.querySelectorAll("img");
|
||||||
expect(imgs).toHaveLength(2);
|
expect(imgs).toHaveLength(2);
|
||||||
// The grid div has inline style with gridTemplateColumns; its non-img children are holes
|
// The grid div has inline style with gridTemplateColumns; its non-img children are holes
|
||||||
const gridEl = container.querySelector('[style*="repeat"]');
|
const gridEl = must(
|
||||||
if (!(gridEl instanceof HTMLElement)) {
|
container.querySelector('[style*="repeat"]'),
|
||||||
throw new Error("expected to find the puzzle grid element");
|
"the puzzle grid element",
|
||||||
}
|
);
|
||||||
const holeDivs = Array.from(gridEl.children).filter(
|
const holeDivs = Array.from(gridEl.children).filter(
|
||||||
(c) => c.tagName === "DIV",
|
(c) => c.tagName === "DIV",
|
||||||
);
|
);
|
||||||
|
|||||||
21
bucket_catch/packages/frontend/src/test/domQueries.ts
Normal file
21
bucket_catch/packages/frontend/src/test/domQueries.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* DOM query helpers shared by the component tests.
|
||||||
|
*
|
||||||
|
* Lives under src/test/ because the coverage config excludes that path — a
|
||||||
|
* helper placed beside a component would be counted as production code and
|
||||||
|
* break the 100% gate.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Query one element and narrow it, failing the test if it is absent.
|
||||||
|
*
|
||||||
|
* Both `as HTMLElement` and `!` are rejected here — the former by
|
||||||
|
* non-nullable-type-assertion-style, the latter by no-non-null-assertion — so
|
||||||
|
* the narrowing is done with a real check that also gives a useful message.
|
||||||
|
*/
|
||||||
|
export const must = (el: Element | null, what: string): HTMLElement => {
|
||||||
|
if (!(el instanceof HTMLElement)) {
|
||||||
|
throw new Error(`expected to find ${what}`);
|
||||||
|
}
|
||||||
|
return el;
|
||||||
|
};
|
||||||
Loading…
Reference in New Issue
Block a user