Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 11 additions & 4 deletions lib/src/widgets/promotion.dart
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,6 @@ class PromotionSelector extends StatelessWidget with ChessboardGeometry {
final isPromotionSquareAtTop =
orientation == Side.white && square.rank == Rank.eighth ||
orientation == Side.black && square.rank == Rank.first;
final anchorSquare =
isPromotionSquareAtTop
? square
: Square.fromCoords(square.file, orientation == Side.white ? Rank.fourth : Rank.fifth);

final topRoles = [
Role.queen,
Expand All @@ -71,11 +67,22 @@ class PromotionSelector extends StatelessWidget with ChessboardGeometry {
Role.bishop,
if (canPromoteToKing) Role.king,
];

final roles = isPromotionSquareAtTop ? topRoles : topRoles.reversed.toList(growable: false);
final pieces = roles
.map((role) => Piece(color: color, role: role, promoted: true))
.toList(growable: false);

final anchorSquare =
isPromotionSquareAtTop
? square
: Square.fromCoords(
square.file,
orientation == Side.white
? (pieces.length == 4 ? Rank.fourth : Rank.fifth)
: (pieces.length == 4 ? Rank.fifth : Rank.fourth),
);

final offset = squareOffset(anchorSquare);

return GestureDetector(
Expand Down
178 changes: 178 additions & 0 deletions test/widgets/board_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1024,6 +1024,184 @@ void main() {
expect(find.byKey(const Key('f7-whitepawn')), findsNothing);
});

testWidgets('Player on top can promote to King', (WidgetTester tester) async {
await tester.pumpWidget(
const _TestApp(
initialPlayerSide: PlayerSide.both,
initialFen: 'K7/8/k7/8/8/8/p7/1Q4n1 b - - 0 1',
canPromoteToKing: true,
),
);
await tester.tap(find.byKey(const Key('a2-blackpawn')));
await tester.pump();
await tester.tapAt(squareOffset(tester, Square.b1));
await tester.pump();

expect(find.byType(PromotionSelector), findsOneWidget);
final promotionSelector = find.byType(PromotionSelector);
final piecesInSelector = find.descendant(
of: promotionSelector,
matching: find.byType(PieceWidget),
);

// has exactly 5 pieces (queen, knight, rook, bishop, king)
expect(piecesInSelector, findsNWidgets(5));

// tap on the king is the last option in the promotion selector
await tester.tapAt(squareOffset(tester, Square.b5));
await tester.pump();

expect(find.byKey(const Key('b1-blackking')), findsOneWidget);
expect(find.byKey(const Key('a2-blackpawn')), findsNothing);
});
testWidgets('promote a piece on flipped board (orientation black)', (
WidgetTester tester,
) async {
const orientation = Side.black;
await tester.pumpWidget(
const _TestApp(
initialPlayerSide: PlayerSide.both,
initialFen: '8/5P2/2RK2P1/8/4k3/8/8/7r w - - 0 1',
orientation: orientation,
),
);

await tester.tap(find.byKey(const Key('f7-whitepawn')));
await tester.pump();
await tester.tapAt(squareOffset(tester, Square.f8, orientation: orientation));
await tester.pump();

// wait for promotion selector to show
await tester.pump();
expect(find.byType(PromotionSelector), findsOneWidget);
final promotionSelector = find.byType(PromotionSelector);
final piecesInSelector = find.descendant(
of: promotionSelector,
matching: find.byType(PieceWidget),
);

// has exactly 4 pieces (queen, knight, rook, bishop)
expect(piecesInSelector, findsNWidgets(4));

// promotion pawn is not visible
expect(find.byKey(const Key('f7-whitepawn')), findsNothing);

await tester.tapAt(squareOffset(tester, Square.f7, orientation: orientation));
await tester.pump();
expect(find.byKey(const Key('f8-whiteknight')), findsOneWidget);
expect(find.byKey(const Key('f7-whitepawn')), findsNothing);
});

testWidgets('player at bottom promotes a bishop on flipped board (orientation black)', (
WidgetTester tester,
) async {
const orientation = Side.black;
await tester.pumpWidget(
const _TestApp(
initialPlayerSide: PlayerSide.both,
initialFen: 'K7/8/k7/8/8/8/p7/1Q4n1 b - - 0 1',
orientation: orientation,
),
);

await tester.tap(find.byKey(const Key('a2-blackpawn')));
await tester.pump();
await tester.tapAt(squareOffset(tester, Square.b1, orientation: orientation));
await tester.pump();

// wait for promotion selector to show
await tester.pump();
expect(find.byType(PromotionSelector), findsOneWidget);
final promotionSelector = find.byType(PromotionSelector);
final piecesInSelector = find.descendant(
of: promotionSelector,
matching: find.byType(PieceWidget),
);

// has exactly 4 pieces (queen, knight, rook, bishop)
expect(piecesInSelector, findsNWidgets(4));

// promotion pawn is not visible
expect(find.byKey(const Key('a2-blackpawn')), findsNothing);

// selector opens downward from b1 (rank 1 at top); queen, knight, rook, bishop
await tester.tapAt(squareOffset(tester, Square.b4, orientation: orientation));
await tester.pump();
expect(find.byKey(const Key('b1-blackbishop')), findsOneWidget);
expect(find.byKey(const Key('a2-blackpawn')), findsNothing);
});

testWidgets('can promote to king on flipped board (orientation black)', (
WidgetTester tester,
) async {
const orientation = Side.black;
await tester.pumpWidget(
const _TestApp(
initialPlayerSide: PlayerSide.both,
initialFen: '8/5P2/2RK2P1/8/4k3/8/8/7r w - - 0 1',
orientation: orientation,
canPromoteToKing: true,
),
);

await tester.tap(find.byKey(const Key('f7-whitepawn')));
await tester.pump();
await tester.tapAt(squareOffset(tester, Square.f8, orientation: orientation));
await tester.pump();

// wait for promotion selector to show
await tester.pump();
expect(find.byType(PromotionSelector), findsOneWidget);
final promotionSelector = find.byType(PromotionSelector);
final piecesInSelector = find.descendant(
of: promotionSelector,
matching: find.byType(PieceWidget),
);

// has exactly 5 pieces (queen, knight, rook, bishop,king)
expect(piecesInSelector, findsNWidgets(5));

await tester.tapAt(squareOffset(tester, Square.f4, orientation: orientation));
await tester.pump();

expect(find.byKey(const Key('f8-whiteking')), findsOneWidget);
expect(find.byKey(const Key('f7-whitepawn')), findsNothing);
});

testWidgets('player at bottom can promote to King on flipped board (orientation black)', (
WidgetTester tester,
) async {
const orientation = Side.black;
await tester.pumpWidget(
const _TestApp(
initialPlayerSide: PlayerSide.both,
initialFen: 'K7/8/k7/8/8/8/p7/1Q4n1 b - - 0 1',
orientation: orientation,
canPromoteToKing: true,
),
);
await tester.tap(find.byKey(const Key('a2-blackpawn')));
await tester.pump();
await tester.tapAt(squareOffset(tester, Square.b1, orientation: orientation));
await tester.pump();

expect(find.byType(PromotionSelector), findsOneWidget);
final promotionSelector = find.byType(PromotionSelector);
final piecesInSelector = find.descendant(
of: promotionSelector,
matching: find.byType(PieceWidget),
);

// has exactly 5 pieces (queen, knight, rook, bishop, king)
expect(piecesInSelector, findsNWidgets(5));

await tester.tapAt(squareOffset(tester, Square.b5, orientation: orientation));
await tester.pump();

expect(find.byKey(const Key('b1-blackking')), findsOneWidget);
expect(find.byKey(const Key('a2-blackpawn')), findsNothing);
});

testWidgets('cancels promotion', (WidgetTester tester) async {
await tester.pumpWidget(
const _TestApp(
Expand Down