mirror of
https://github.com/kuhyx/testsAndMisc-archive.git
synced 2026-11-10 19:27:05 +01:00
Removes the last fixable non-null assertion. GamePhase goes from a string union to a tagged one whose "done" arm carries `result: GameResult`, so narrowing on phase.kind yields a non-null result and `fileResult!` disappears. GameResult - previously an untagged union nothing consumed - is now tagged, so the render picks a screen from the discriminant instead of inferring it from which of two nullable states was set. Three earlier attempts at this site were reverted: a null guard returning null, a widened ReactElement | null return, and a tagged GameResult with phase left as a string. Each either added a branch no test could reach - dropping the 100% gate - or left the value nullable in the fall-through. The invariant lived in the phase, so the fix had to as well. Coverage stays 100%. Statements 585 -> 579 and lines 536 -> 530, exactly the two useState declarations and their setters that the collapse removed; branches unchanged at 217, so nothing new went untested. All 10 App tests pass unmodified - they drive the app through mocked onDone callbacks, so both done arms are exercised through the new union. eslint 5 -> 4 (45 -> 4 for the session). The remaining four are reported in the previous commit's evidence. usePuzzleGameLoop's Map.get assertion is not among the fixable ones: `?? Infinity` was tried and measured at 219 branches / 99.54%, since the default side is unreachable. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011w2Pj4Ek3ZNMQVYcFHgXDY
87 lines
2.5 KiB
TypeScript
87 lines
2.5 KiB
TypeScript
import React, { useCallback, useState } from "react";
|
|
import type {
|
|
GamePhase,
|
|
FileGameResult,
|
|
PuzzleGameResult,
|
|
TransferMode,
|
|
} from "./types";
|
|
import { DropZone } from "./components/DropZone";
|
|
import { ModeSelect } from "./components/ModeSelect";
|
|
import { GameCanvas } from "./components/GameCanvas";
|
|
import { PuzzleCanvas } from "./components/PuzzleCanvas";
|
|
import { ScoreScreen } from "./components/ScoreScreen";
|
|
import { PuzzleResult } from "./components/PuzzleResult";
|
|
|
|
export default function App(): React.ReactElement {
|
|
const [phase, setPhase] = useState<GamePhase>({ kind: "drop" });
|
|
const [files, setFiles] = useState<File[]>([]);
|
|
const [mode, setMode] = useState<TransferMode>("download");
|
|
const [puzzleGridSize, setPuzzleGridSize] = useState(4);
|
|
|
|
const handleFiles = useCallback((incoming: File[]) => {
|
|
setFiles(incoming);
|
|
setPhase({ kind: "mode" });
|
|
}, []);
|
|
|
|
const handlePuzzleDirect = useCallback(
|
|
(imageFile: File, gridSize: number) => {
|
|
setFiles([imageFile]);
|
|
setMode("puzzle");
|
|
setPuzzleGridSize(gridSize);
|
|
setPhase({ kind: "playing" });
|
|
},
|
|
[],
|
|
);
|
|
|
|
const handleStart = useCallback((selected: TransferMode, gridSize = 4) => {
|
|
setMode(selected);
|
|
if (selected === "puzzle") setPuzzleGridSize(gridSize);
|
|
setPhase({ kind: "playing" });
|
|
}, []);
|
|
|
|
const handleFileDone = useCallback((value: FileGameResult) => {
|
|
setPhase({ kind: "done", result: { kind: "file", value } });
|
|
}, []);
|
|
|
|
const handlePuzzleDone = useCallback((value: PuzzleGameResult) => {
|
|
setPhase({ kind: "done", result: { kind: "puzzle", value } });
|
|
}, []);
|
|
|
|
const handleRestart = useCallback(() => {
|
|
setFiles([]);
|
|
setPhase({ kind: "drop" });
|
|
}, []);
|
|
|
|
if (phase.kind === "drop") {
|
|
return <DropZone onFiles={handleFiles} onPuzzle={handlePuzzleDirect} />;
|
|
}
|
|
if (phase.kind === "mode") {
|
|
return <ModeSelect files={files} onStart={handleStart} />;
|
|
}
|
|
if (phase.kind === "playing") {
|
|
if (mode === "puzzle") {
|
|
return (
|
|
<PuzzleCanvas
|
|
imageFile={files[0]}
|
|
gridSize={puzzleGridSize}
|
|
onDone={handlePuzzleDone}
|
|
/>
|
|
);
|
|
}
|
|
return <GameCanvas files={files} onDone={handleFileDone} />;
|
|
}
|
|
// done phase: the phase carries its result, so both arms are non-null.
|
|
if (phase.result.kind === "puzzle") {
|
|
return (
|
|
<PuzzleResult result={phase.result.value} onRestart={handleRestart} />
|
|
);
|
|
}
|
|
return (
|
|
<ScoreScreen
|
|
result={phase.result.value}
|
|
mode={mode}
|
|
onRestart={handleRestart}
|
|
/>
|
|
);
|
|
}
|