Preset refactors for HP
This commit is contained in:
parent
6883539426
commit
7c4c942938
19 changed files with 1497 additions and 288 deletions
2
packages/chess/e2e/full-flow.spec.d.ts
vendored
Normal file
2
packages/chess/e2e/full-flow.spec.d.ts
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
export {};
|
||||
//# sourceMappingURL=full-flow.spec.d.ts.map
|
||||
1
packages/chess/e2e/full-flow.spec.d.ts.map
Normal file
1
packages/chess/e2e/full-flow.spec.d.ts.map
Normal file
|
|
@ -0,0 +1 @@
|
|||
{"version":3,"file":"full-flow.spec.d.ts","sourceRoot":"","sources":["full-flow.spec.ts"],"names":[],"mappings":""}
|
||||
105
packages/chess/e2e/full-flow.spec.js
Normal file
105
packages/chess/e2e/full-flow.spec.js
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
import { test, expect } from '@playwright/test';
|
||||
/**
|
||||
* Full Phase-3 gate scenario:
|
||||
* preset-toggle → Scholar's Mate play → undo → save/reload → export → import → checkmate
|
||||
*
|
||||
* Undo is exercised on the *original* page before reload, where moveHistoryRef
|
||||
* still holds all moves since start (loadEngine resets history, so undo is only
|
||||
* meaningful there). Export/import is verified by restoring that same 8-move
|
||||
* position in a fresh browser context, then completing the Scholar's Mate.
|
||||
*/
|
||||
test("full game flow: preset toggle, Scholar's Mate, save/reload, export/import, undo, checkmate", async ({ page, browser }) => {
|
||||
// ── Step 1: Preset toggles ─────────────────────────────────────────────────
|
||||
await page.goto('/rules');
|
||||
// pawns-move-backward: incompatibleWith ["double-pawn-sprint"] only
|
||||
await page.locator('[data-preset="pawns-move-backward"] [data-role="toggle"]').click();
|
||||
// knights-leap-twice: incompatibleWith []
|
||||
await page.locator('[data-preset="knights-leap-twice"] [data-role="toggle"]').click();
|
||||
// These two presets are compatible — no compat warning
|
||||
await expect(page.locator('[data-testid="compat-warning"]')).not.toBeVisible();
|
||||
await page.locator('[data-action="start-new-game"]').click();
|
||||
await page.waitForURL('**/game');
|
||||
// ── Step 2: Play all 8 moves before any reload ────────────────────────────
|
||||
//
|
||||
// We play ALL 8 moves here on the pristine page so that moveHistoryRef still
|
||||
// holds the full sequence. loadEngine() resets moveHistoryRef to [], so any
|
||||
// subsequent reload/import invalidates the undo chain; doing undo NOW (before
|
||||
// the first reload) is the only way to test it correctly without source changes.
|
||||
//
|
||||
// Targeting `[data-square="${from}"] [data-piece]` places the drag origin on
|
||||
// the draggable piece element so that dragstart fires on it. The target is the
|
||||
// square div that has onDrop/onDragOver.
|
||||
const drag = async (from, to) => {
|
||||
await page
|
||||
.locator(`[data-square="${from}"] [data-piece]`)
|
||||
.dragTo(page.locator(`[data-square="${to}"]`));
|
||||
};
|
||||
// Scholar's Mate setup — moves 1-5
|
||||
await drag('e2', 'e4'); // 1. e4
|
||||
await drag('e7', 'e5'); // 1...e5
|
||||
await drag('f1', 'c4'); // 2. Bc4
|
||||
await drag('b8', 'c6'); // 2...Nc6
|
||||
await drag('d1', 'h5'); // 3. Qh5
|
||||
await expect(page.locator('[data-testid="turn-indicator"]')).toContainText('Black');
|
||||
// Continuation — moves 6-8
|
||||
await drag('d7', 'd6'); // 3...d6 (random; Qf7 still covers d7 via rank-7)
|
||||
await drag('a2', 'a3'); // 4. a3 (random)
|
||||
await drag('g8', 'f6'); // 4...Nf6?? — will be undone
|
||||
// ── Step 3: Undo Nf6, verify knight back on g8 ────────────────────────────
|
||||
// History = [e4,e5,Bc4,Nc6,Qh5,d6,a3,Nf6] → pop → [e4,e5,Bc4,Nc6,Qh5,d6,a3]
|
||||
// Rebuild replays all 7 moves from initial; g8 kingside knight is untouched
|
||||
// (only the b8 queenside knight moved to c6 in move 4).
|
||||
await page.locator('[data-action="undo"]').click();
|
||||
await expect(page.locator('[data-square="g8"] [data-piece="black-knight"]')).toBeVisible();
|
||||
// Re-play Nf6 to restore the 8-move export state (White's turn)
|
||||
await drag('g8', 'f6'); // 4...Nf6?? again
|
||||
// ── Step 4: Save named slot ────────────────────────────────────────────────
|
||||
await page.goto('/save');
|
||||
await page.fill('input[type="text"]', 'scholars-test');
|
||||
await page.locator('[data-action="save"]').click();
|
||||
await expect(page.locator('[data-testid="save-slot-scholars-test"]')).toBeVisible();
|
||||
// ── Step 5: Reload → autosave restore ─────────────────────────────────────
|
||||
// Each applyMove() calls saveAutoSave(); App.useEffect restores it on mount.
|
||||
// The last applyMove was Nf6 → autosave = 8-move state.
|
||||
// Queen on h5 and bishop on c4 are present in that state (neither moved
|
||||
// during moves 6-8).
|
||||
await page.reload();
|
||||
await page.goto('/game');
|
||||
await expect(page.locator('[data-square="h5"] [data-piece="white-queen"]')).toBeVisible();
|
||||
await expect(page.locator('[data-square="c4"] [data-piece="white-bishop"]')).toBeVisible();
|
||||
// ── Step 6: Export JSON (8-move state, White's turn) ──────────────────────
|
||||
await page.goto('/save');
|
||||
const downloadPromise = page.waitForEvent('download');
|
||||
await page.locator('[data-action="export"]').click();
|
||||
const download = await downloadPromise;
|
||||
const exportPath = `/tmp/chess-export-${Date.now()}.json`;
|
||||
await download.saveAs(exportPath);
|
||||
// ── Step 7: Import in a fresh browser context ──────────────────────────────
|
||||
const freshContext = await browser.newContext();
|
||||
const freshPage = await freshContext.newPage();
|
||||
await freshPage.goto('http://localhost:5173/save');
|
||||
// setInputFiles works on hidden inputs; onChange → FileReader → onLoad(facts)
|
||||
// → handleLoad in SaveWrapper → loadEngine → saveAutoSave → navigate('/game')
|
||||
const fileInput = freshPage.locator('input[type="file"]');
|
||||
await fileInput.setInputFiles(exportPath);
|
||||
await freshPage.waitForURL('**/game');
|
||||
// Verify the 8-move position was restored in the fresh context
|
||||
await expect(freshPage.locator('[data-square="h5"] [data-piece="white-queen"]')).toBeVisible();
|
||||
await expect(freshPage.locator('[data-square="c4"] [data-piece="white-bishop"]')).toBeVisible();
|
||||
// ── Step 8: Scholar's Mate — Qxf7# ───────────────────────────────────────
|
||||
// It is White's turn (after Black's Nf6 in the imported 8-move state).
|
||||
// h5→f7 is a 2-square diagonal via empty g6. f7 has the black f7 pawn.
|
||||
// Queen on f7 is defended by Bc4 (c4-d5-e6-f7 diagonal).
|
||||
// Black king on e8 cannot escape:
|
||||
// d8 = own queen (occupied), d7 = attacked by Qf7 along rank-7,
|
||||
// e7 = attacked by Qf7 (adjacent on rank-7), f8 = own bishop (occupied),
|
||||
// f7 = queen (defended by Bc4).
|
||||
// Nf6 cannot capture f7 (not an L-shaped jump from f6).
|
||||
await freshPage
|
||||
.locator('[data-square="h5"] [data-piece]')
|
||||
.dragTo(freshPage.locator('[data-square="f7"]'));
|
||||
// ── Step 9: Assert checkmate ──────────────────────────────────────────────
|
||||
await expect(freshPage.locator('[data-testid="game-over"]')).toBeVisible();
|
||||
await freshContext.close();
|
||||
});
|
||||
//# sourceMappingURL=full-flow.spec.js.map
|
||||
1
packages/chess/e2e/full-flow.spec.js.map
Normal file
1
packages/chess/e2e/full-flow.spec.js.map
Normal file
|
|
@ -0,0 +1 @@
|
|||
{"version":3,"file":"full-flow.spec.js","sourceRoot":"","sources":["full-flow.spec.ts"],"names":[],"mappings":"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"}
|
||||
Loading…
Add table
Add a link
Reference in a new issue