Files
blind_chess/packages/shared/test/promotion.test.ts
T
claude (blind_chess) c01244c850 fix: promotion dialog only fires for genuine pawn promotions
The "Promote pawn" dialog popped for any pawn "moved" toward the last
rank: the commit paths checked piece type + destination rank but never
the pawn's SOURCE rank. With the phantom layer now filling ranks 7-8
with tappable phantom pieces, tapping one (which falls through to the
real-move handler) while a real pawn was armed triggered the dialog for
a move no pawn could make — and for any phantom type, not just pawns.

Root cause: incomplete promotion detection, duplicated in Game.svelte
`onCommit` and the server's `isPromotionRequired`. Replaced with one
shared `isPromotionMove(piece, from, to)` — pawn, from the rank adjacent
to promotion, to the promotion rank, at most one file over — used by
both. 7 unit tests in packages/shared.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 21:45:42 -04:00

43 lines
1.5 KiB
TypeScript

import { describe, it, expect } from 'vitest';
import { isPromotionMove } from '../src/promotion.js';
import type { Piece } from '../src/types.js';
const wp: Piece = { color: 'w', type: 'p' };
const bp: Piece = { color: 'b', type: 'p' };
const wn: Piece = { color: 'w', type: 'n' };
describe('isPromotionMove', () => {
it('white pawn from the 7th rank to the 8th is a promotion', () => {
expect(isPromotionMove(wp, 'e7', 'e8')).toBe(true);
expect(isPromotionMove(wp, 'd7', 'e8')).toBe(true); // capture-promotion
});
it('black pawn from the 2nd rank to the 1st is a promotion', () => {
expect(isPromotionMove(bp, 'e2', 'e1')).toBe(true);
expect(isPromotionMove(bp, 'd2', 'c1')).toBe(true);
});
it('a pawn NOT on the rank adjacent to promotion is not a promotion', () => {
expect(isPromotionMove(wp, 'e2', 'e8')).toBe(false); // the reported bug
expect(isPromotionMove(wp, 'e5', 'e8')).toBe(false);
expect(isPromotionMove(bp, 'e7', 'e1')).toBe(false);
});
it('an ordinary pawn move is not a promotion', () => {
expect(isPromotionMove(wp, 'e2', 'e4')).toBe(false);
expect(isPromotionMove(bp, 'e7', 'e5')).toBe(false);
});
it('a non-pawn reaching the last rank is not a promotion', () => {
expect(isPromotionMove(wn, 'g6', 'e8')).toBe(false);
});
it('a pawn move spanning more than one file is not a promotion', () => {
expect(isPromotionMove(wp, 'a7', 'h8')).toBe(false);
});
it('an undefined piece is not a promotion', () => {
expect(isPromotionMove(undefined, 'e7', 'e8')).toBe(false);
});
});