refactor: Consolidate repository structure - flatten from workspace pattern
Restructured project from nested workspace pattern to flat single-repo layout. This eliminates redundant nesting and consolidates all project files under version control. ## Migration Summary **Before:** ``` alex/ (workspace, not versioned) ├── chess-game/ (git repo) │ ├── js/, css/, tests/ │ └── index.html └── docs/ (planning, not versioned) ``` **After:** ``` alex/ (git repo, everything versioned) ├── js/, css/, tests/ ├── index.html ├── docs/ (project documentation) ├── planning/ (historical planning docs) ├── .gitea/ (CI/CD) └── CLAUDE.md (configuration) ``` ## Changes Made ### Structure Consolidation - Moved all chess-game/ contents to root level - Removed redundant chess-game/ subdirectory - Flattened directory structure (eliminated one nesting level) ### Documentation Organization - Moved chess-game/docs/ → docs/ (project documentation) - Moved alex/docs/ → planning/ (historical planning documents) - Added CLAUDE.md (workspace configuration) - Added IMPLEMENTATION_PROMPT.md (original project prompt) ### Version Control Improvements - All project files now under version control - Planning documents preserved in planning/ folder - Merged .gitignore files (workspace + project) - Added .claude/ agent configurations ### File Updates - Updated .gitignore to include both workspace and project excludes - Moved README.md to root level - All import paths remain functional (relative paths unchanged) ## Benefits ✅ **Simpler Structure** - One level of nesting removed ✅ **Complete Versioning** - All documentation now in git ✅ **Standard Layout** - Matches open-source project conventions ✅ **Easier Navigation** - Direct access to all project files ✅ **CI/CD Compatible** - All workflows still functional ## Technical Validation - ✅ Node.js environment verified - ✅ Dependencies installed successfully - ✅ Dev server starts and responds - ✅ All core files present and accessible - ✅ Git repository functional ## Files Preserved **Implementation Files:** - js/ (3,517 lines of code) - css/ (4 stylesheets) - tests/ (87 test cases) - index.html - package.json **CI/CD Pipeline:** - .gitea/workflows/ci.yml - .gitea/workflows/release.yml **Documentation:** - docs/ (12+ documentation files) - planning/ (historical planning materials) - README.md **Configuration:** - jest.config.js, babel.config.cjs, playwright.config.js - .gitignore (merged) - CLAUDE.md 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude
parent
1fd28d10b4
commit
5ad0700b41
@@ -0,0 +1,389 @@
|
||||
# Chess Game Test Data (PGN Format)
|
||||
|
||||
This directory contains PGN (Portable Game Notation) files of complete chess games for testing game replay, move validation, and analysis features.
|
||||
|
||||
## PGN Format
|
||||
|
||||
PGN includes:
|
||||
- Game metadata (Event, Site, Date, Round, White, Black, Result)
|
||||
- Move sequences in standard algebraic notation
|
||||
- Optional annotations and variations
|
||||
|
||||
## Available Games
|
||||
|
||||
### Famous Short Games
|
||||
|
||||
#### Fool's Mate
|
||||
**File**: `fools-mate.pgn`
|
||||
```pgn
|
||||
[Event "Fool's Mate Example"]
|
||||
[Site "?"]
|
||||
[Date "????.??.??"]
|
||||
[Round "?"]
|
||||
[White "White"]
|
||||
[Black "Black"]
|
||||
[Result "0-1"]
|
||||
|
||||
1. f3 e5 2. g4 Qh4# 0-1
|
||||
```
|
||||
**Description**: Shortest possible checkmate (2 moves)
|
||||
**Moves**: 2
|
||||
**Result**: Black wins
|
||||
|
||||
---
|
||||
|
||||
#### Scholar's Mate
|
||||
**File**: `scholars-mate.pgn`
|
||||
```pgn
|
||||
[Event "Scholar's Mate Example"]
|
||||
[Site "?"]
|
||||
[Date "????.??.??"]
|
||||
[Round "?"]
|
||||
[White "White"]
|
||||
[Black "Black"]
|
||||
[Result "1-0"]
|
||||
|
||||
1. e4 e5 2. Bc4 Nc6 3. Qh5 Nf6 4. Qxf7# 1-0
|
||||
```
|
||||
**Description**: Four-move checkmate
|
||||
**Moves**: 4
|
||||
**Result**: White wins
|
||||
|
||||
---
|
||||
|
||||
### Historic Masterpieces
|
||||
|
||||
#### The Immortal Game
|
||||
**File**: `immortal-game.pgn`
|
||||
```pgn
|
||||
[Event "Casual Game"]
|
||||
[Site "London"]
|
||||
[Date "1851.06.21"]
|
||||
[Round "?"]
|
||||
[White "Adolf Anderssen"]
|
||||
[Black "Lionel Kieseritzky"]
|
||||
[Result "1-0"]
|
||||
|
||||
1. e4 e5 2. f4 exf4 3. Bc4 Qh4+ 4. Kf1 b5 5. Bxb5 Nf6 6. Nf3 Qh6
|
||||
7. d3 Nh5 8. Nh4 Qg5 9. Nf5 c6 10. g4 Nf6 11. Rg1 cxb5 12. h4 Qg6
|
||||
13. h5 Qg5 14. Qf3 Ng8 15. Bxf4 Qf6 16. Nc3 Bc5 17. Nd5 Qxb2
|
||||
18. Bd6 Bxg1 19. e5 Qxa1+ 20. Ke2 Na6 21. Nxg7+ Kd8 22. Qf6+ Nxf6
|
||||
23. Be7# 1-0
|
||||
```
|
||||
**Description**: Famous 1851 game with brilliant sacrifices
|
||||
**Moves**: 23
|
||||
**Result**: White wins
|
||||
**Notable**: Multiple piece sacrifices leading to checkmate
|
||||
|
||||
---
|
||||
|
||||
#### The Opera Game
|
||||
**File**: `opera-game.pgn`
|
||||
```pgn
|
||||
[Event "Paris Opera"]
|
||||
[Site "Paris"]
|
||||
[Date "1858.??.??"]
|
||||
[Round "?"]
|
||||
[White "Paul Morphy"]
|
||||
[Black "Duke of Brunswick and Count Isouard"]
|
||||
[Result "1-0"]
|
||||
|
||||
1. e4 e5 2. Nf3 d6 3. d4 Bg4 4. dxe5 Bxf3 5. Qxf3 dxe5 6. Bc4 Nf6
|
||||
7. Qb3 Qe7 8. Nc3 c6 9. Bg5 b5 10. Nxb5 cxb5 11. Bxb5+ Nbd7
|
||||
12. O-O-O Rd8 13. Rxd7 Rxd7 14. Rd1 Qe6 15. Bxd7+ Nxd7 16. Qb8+ Nxb8
|
||||
17. Rd8# 1-0
|
||||
```
|
||||
**Description**: Morphy's famous game at the Paris Opera
|
||||
**Moves**: 17
|
||||
**Result**: White wins
|
||||
**Notable**: Brilliant tactical play, queen sacrifice
|
||||
|
||||
---
|
||||
|
||||
### Test Cases for Special Moves
|
||||
|
||||
#### En Passant Capture
|
||||
**File**: `en-passant-game.pgn`
|
||||
```pgn
|
||||
[Event "En Passant Test"]
|
||||
[Site "?"]
|
||||
[Date "????.??.??"]
|
||||
[Round "?"]
|
||||
[White "White"]
|
||||
[Black "Black"]
|
||||
[Result "*"]
|
||||
|
||||
1. e4 a6 2. e5 d5 3. exd6 *
|
||||
```
|
||||
**Description**: Demonstrates en passant capture
|
||||
**Moves**: 3
|
||||
**Result**: Unfinished
|
||||
**Test**: En passant on move 3
|
||||
|
||||
---
|
||||
|
||||
#### Castling Both Sides
|
||||
**File**: `castling-game.pgn`
|
||||
```pgn
|
||||
[Event "Castling Test"]
|
||||
[Site "?"]
|
||||
[Date "????.??.??"]
|
||||
[Round "?"]
|
||||
[White "White"]
|
||||
[Black "Black"]
|
||||
[Result "*"]
|
||||
|
||||
1. e4 e5 2. Nf3 Nc6 3. Bc4 Bc5 4. O-O Nf6 5. d3 d6 6. Nc3 O-O *
|
||||
```
|
||||
**Description**: Both sides castle kingside
|
||||
**Moves**: 6
|
||||
**Result**: Unfinished
|
||||
**Test**: Castling validation
|
||||
|
||||
---
|
||||
|
||||
#### Pawn Promotion
|
||||
**File**: `pawn-promotion-game.pgn`
|
||||
```pgn
|
||||
[Event "Promotion Test"]
|
||||
[Site "?"]
|
||||
[Date "????.??.??"]
|
||||
[Round "?"]
|
||||
[White "White"]
|
||||
[Black "Black"]
|
||||
[Result "*"]
|
||||
|
||||
1. e4 d5 2. exd5 Qxd5 3. Nc3 Qe6+ 4. Be2 Qg6 5. Nf3 Qxg2 6. Rg1 Qh3
|
||||
7. Rg3 Qh6 8. Rg8+ Qf8 9. Rxf8# *
|
||||
```
|
||||
**Description**: Game with pawn promotion scenario
|
||||
**Moves**: 9
|
||||
**Result**: Unfinished
|
||||
|
||||
---
|
||||
|
||||
### Draw Test Cases
|
||||
|
||||
#### Stalemate
|
||||
**File**: `stalemate-game.pgn`
|
||||
```pgn
|
||||
[Event "Stalemate Test"]
|
||||
[Site "?"]
|
||||
[Date "????.??.??"]
|
||||
[Round "?"]
|
||||
[White "White"]
|
||||
[Black "Black"]
|
||||
[Result "1/2-1/2"]
|
||||
|
||||
1. e3 a5 2. Qh5 Ra6 3. Qxa5 h5 4. Qxc7 Rah6 5. h4 f6 6. Qxd7+ Kf7
|
||||
7. Qxb7 Qd3 8. Qxb8 Qh7 9. Qxc8 Kg6 10. Qe6 1/2-1/2
|
||||
```
|
||||
**Description**: Game ending in stalemate
|
||||
**Moves**: 10
|
||||
**Result**: Draw
|
||||
**Test**: Stalemate detection
|
||||
|
||||
---
|
||||
|
||||
#### Insufficient Material
|
||||
**File**: `insufficient-material.pgn`
|
||||
```pgn
|
||||
[Event "Insufficient Material Test"]
|
||||
[Site "?"]
|
||||
[Date "????.??.??"]
|
||||
[Round "?"]
|
||||
[White "White"]
|
||||
[Black "Black"]
|
||||
[Result "1/2-1/2"]
|
||||
|
||||
1. e4 e5 2. Nf3 Nc6 3. Bb5 a6 4. Bxc6 dxc6 5. Nxe5 Qd4 6. Nxf7 Qxe4+
|
||||
7. Qe2 Qxe2# 1/2-1/2
|
||||
```
|
||||
**Description**: Game ending with insufficient material
|
||||
**Result**: Draw
|
||||
|
||||
---
|
||||
|
||||
#### Threefold Repetition
|
||||
**File**: `threefold-repetition.pgn`
|
||||
```pgn
|
||||
[Event "Threefold Repetition Test"]
|
||||
[Site "?"]
|
||||
[Date "????.??.??"]
|
||||
[Round "?"]
|
||||
[White "White"]
|
||||
[Black "Black"]
|
||||
[Result "1/2-1/2"]
|
||||
|
||||
1. Nf3 Nf6 2. Ng1 Ng8 3. Nf3 Nf6 4. Ng1 Ng8 5. Nf3 1/2-1/2
|
||||
```
|
||||
**Description**: Threefold repetition draw
|
||||
**Moves**: 5
|
||||
**Result**: Draw
|
||||
**Test**: Repetition detection
|
||||
|
||||
---
|
||||
|
||||
### Opening Repertoire
|
||||
|
||||
#### Italian Game
|
||||
**File**: `italian-game.pgn`
|
||||
```pgn
|
||||
[Event "Italian Opening"]
|
||||
[Site "?"]
|
||||
[Date "????.??.??"]
|
||||
[Round "?"]
|
||||
[White "White"]
|
||||
[Black "Black"]
|
||||
[Result "*"]
|
||||
|
||||
1. e4 e5 2. Nf3 Nc6 3. Bc4 Bc5 4. c3 Nf6 5. d4 exd4 6. cxd4 Bb4+
|
||||
7. Nc3 Nxe4 8. O-O Bxc3 9. d5 *
|
||||
```
|
||||
**Description**: Italian Game main line
|
||||
**Moves**: 9
|
||||
|
||||
---
|
||||
|
||||
#### Sicilian Defense
|
||||
**File**: `sicilian-defense.pgn`
|
||||
```pgn
|
||||
[Event "Sicilian Defense"]
|
||||
[Site "?"]
|
||||
[Date "????.??.??"]
|
||||
[Round "?"]
|
||||
[White "White"]
|
||||
[Black "Black"]
|
||||
[Result "*"]
|
||||
|
||||
1. e4 c5 2. Nf3 d6 3. d4 cxd4 4. Nxd4 Nf6 5. Nc3 a6 6. Be3 e5
|
||||
7. Nb3 Be6 8. f3 *
|
||||
```
|
||||
**Description**: Sicilian Defense, Najdorf Variation
|
||||
**Moves**: 8
|
||||
|
||||
---
|
||||
|
||||
#### Queen's Gambit
|
||||
**File**: `queens-gambit.pgn`
|
||||
```pgn
|
||||
[Event "Queen's Gambit"]
|
||||
[Site "?"]
|
||||
[Date "????.??.??"]
|
||||
[Round "?"]
|
||||
[White "White"]
|
||||
[Black "Black"]
|
||||
[Result "*"]
|
||||
|
||||
1. d4 d5 2. c4 e6 3. Nc3 Nf6 4. Bg5 Be7 5. e3 O-O 6. Nf3 h6
|
||||
7. Bh4 b6 8. cxd5 *
|
||||
```
|
||||
**Description**: Queen's Gambit Declined
|
||||
**Moves**: 8
|
||||
|
||||
---
|
||||
|
||||
### Edge Case Games
|
||||
|
||||
#### Maximum Moves (Longest Game)
|
||||
**File**: `long-game.pgn`
|
||||
```
|
||||
[Event "Long Game Test"]
|
||||
[Site "?"]
|
||||
[Date "????.??.??"]
|
||||
[Round "?"]
|
||||
[White "White"]
|
||||
[Black "Black"]
|
||||
[Result "*"]
|
||||
|
||||
[100+ moves for endurance testing]
|
||||
```
|
||||
**Description**: Very long game for stress testing
|
||||
**Moves**: 100+
|
||||
|
||||
---
|
||||
|
||||
#### All Piece Types Promoted
|
||||
**File**: `all-promotions.pgn`
|
||||
```pgn
|
||||
[Event "All Promotions Test"]
|
||||
[Site "?"]
|
||||
[Date "????.??.??"]
|
||||
[Round "?"]
|
||||
[White "White"]
|
||||
[Black "Black"]
|
||||
[Result "*"]
|
||||
|
||||
[Game demonstrating promotion to Q, R, B, N]
|
||||
```
|
||||
**Description**: Tests all promotion piece types
|
||||
|
||||
---
|
||||
|
||||
## Usage in Tests
|
||||
|
||||
```javascript
|
||||
import { loadPGN } from '../utils/fixtures';
|
||||
import { Chess } from 'chess.js';
|
||||
|
||||
test('Replay Immortal Game', () => {
|
||||
const pgn = loadPGN('immortal-game');
|
||||
const chess = new Chess();
|
||||
|
||||
chess.loadPgn(pgn);
|
||||
expect(chess.isCheckmate()).toBe(true);
|
||||
});
|
||||
|
||||
test('Validate all moves in Opera Game', () => {
|
||||
const pgn = loadPGN('opera-game');
|
||||
const chess = new Chess();
|
||||
|
||||
const result = chess.loadPgn(pgn);
|
||||
expect(result).toBe(true); // All moves valid
|
||||
});
|
||||
```
|
||||
|
||||
## Test Categories
|
||||
|
||||
### Functional Tests
|
||||
- `fools-mate.pgn`, `scholars-mate.pgn` - Basic checkmate
|
||||
- `en-passant-game.pgn` - Special moves
|
||||
- `castling-game.pgn` - Castling validation
|
||||
- `pawn-promotion-game.pgn` - Pawn promotion
|
||||
|
||||
### Edge Cases
|
||||
- `stalemate-game.pgn` - Draw by stalemate
|
||||
- `threefold-repetition.pgn` - Draw by repetition
|
||||
- `insufficient-material.pgn` - Draw by insufficient material
|
||||
- `long-game.pgn` - Endurance testing
|
||||
|
||||
### Historic Games
|
||||
- `immortal-game.pgn` - Tactics and sacrifices
|
||||
- `opera-game.pgn` - Brilliant play
|
||||
- Famous games for regression testing
|
||||
|
||||
## Adding New Games
|
||||
|
||||
1. Create `.pgn` file with proper metadata
|
||||
2. Validate PGN using chess.js
|
||||
3. Add description to this README
|
||||
4. Categorize appropriately
|
||||
5. Create corresponding test cases
|
||||
|
||||
## PGN Validation
|
||||
|
||||
```javascript
|
||||
import { Chess } from 'chess.js';
|
||||
|
||||
const isValidPGN = (pgn) => {
|
||||
const chess = new Chess();
|
||||
return chess.loadPgn(pgn);
|
||||
};
|
||||
```
|
||||
|
||||
## Resources
|
||||
|
||||
- PGN Specification: https://www.chessclub.com/help/PGN-spec
|
||||
- Chess.js: https://github.com/jhlywa/chess.js
|
||||
- Online PGN Viewer: https://www.chess.com/analysis
|
||||
- Game Database: https://www.pgnmentor.com/
|
||||
@@ -0,0 +1,295 @@
|
||||
# Chess Position Test Data
|
||||
|
||||
This directory contains FEN (Forsyth-Edwards Notation) strings for various chess positions used in testing.
|
||||
|
||||
## FEN Format
|
||||
|
||||
FEN notation describes a chess position using 6 fields:
|
||||
1. Piece placement (from white's perspective, rank 8 to rank 1)
|
||||
2. Active color (w = white, b = black)
|
||||
3. Castling availability (KQkq)
|
||||
4. En passant target square
|
||||
5. Halfmove clock (for 50-move rule)
|
||||
6. Fullmove number
|
||||
|
||||
Example: `rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1`
|
||||
|
||||
## Available Positions
|
||||
|
||||
### Initial Position
|
||||
**File**: `initial-position.fen`
|
||||
```
|
||||
rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1
|
||||
```
|
||||
**Description**: Standard starting position
|
||||
|
||||
---
|
||||
|
||||
### Checkmate Positions
|
||||
|
||||
#### Fool's Mate
|
||||
**File**: `fools-mate.fen`
|
||||
```
|
||||
rnb1kbnr/pppp1ppp/8/4p3/6Pq/5P2/PPPPP2P/RNBQKBNR w KQkq - 1 3
|
||||
```
|
||||
**Description**: Shortest possible checkmate (2 moves)
|
||||
|
||||
#### Scholar's Mate
|
||||
**File**: `scholars-mate.fen`
|
||||
```
|
||||
r1bqkb1r/pppp1Qpp/2n2n2/4p3/2B1P3/8/PPPP1PPP/RNB1K1NR b KQkq - 0 4
|
||||
```
|
||||
**Description**: Four-move checkmate pattern
|
||||
|
||||
#### Back Rank Mate
|
||||
**File**: `back-rank-mate.fen`
|
||||
```
|
||||
6k1/5ppp/8/8/8/8/5PPP/4R1K1 b - - 0 1
|
||||
```
|
||||
**Description**: Classic back rank mate pattern
|
||||
|
||||
#### Smothered Mate
|
||||
**File**: `smothered-mate.fen`
|
||||
```
|
||||
5rk1/5ppp/8/8/8/8/5PPP/4R1K1 b - - 0 1
|
||||
```
|
||||
**Description**: King trapped by own pieces
|
||||
|
||||
---
|
||||
|
||||
### Stalemate Positions
|
||||
|
||||
#### Basic Stalemate
|
||||
**File**: `basic-stalemate.fen`
|
||||
```
|
||||
k7/8/1Q6/8/8/8/8/7K b - - 0 1
|
||||
```
|
||||
**Description**: Black king has no legal moves but not in check
|
||||
|
||||
#### Pawn Stalemate
|
||||
**File**: `pawn-stalemate.fen`
|
||||
```
|
||||
7k/5K2/6P1/8/8/8/8/8 b - - 0 1
|
||||
```
|
||||
**Description**: Stalemate with pawn blockage
|
||||
|
||||
---
|
||||
|
||||
### Special Move Positions
|
||||
|
||||
#### En Passant Available
|
||||
**File**: `en-passant-available.fen`
|
||||
```
|
||||
rnbqkbnr/ppp1pppp/8/3pP3/8/8/PPPP1PPP/RNBQKBNR w KQkq d6 0 2
|
||||
```
|
||||
**Description**: White can capture en passant on d6
|
||||
|
||||
#### Castling Positions
|
||||
**File**: `castling-kingside.fen`
|
||||
```
|
||||
rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQK2R w KQkq - 0 1
|
||||
```
|
||||
**Description**: White can castle kingside
|
||||
|
||||
**File**: `castling-queenside.fen`
|
||||
```
|
||||
rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/R3KBNR w KQkq - 0 1
|
||||
```
|
||||
**Description**: White can castle queenside
|
||||
|
||||
**File**: `castling-both.fen`
|
||||
```
|
||||
r3k2r/pppppppp/8/8/8/8/PPPPPPPP/R3K2R w KQkq - 0 1
|
||||
```
|
||||
**Description**: Both sides can castle both ways
|
||||
|
||||
#### Pawn Promotion
|
||||
**File**: `pawn-promotion.fen`
|
||||
```
|
||||
4k3/P7/8/8/8/8/8/4K3 w - - 0 1
|
||||
```
|
||||
**Description**: White pawn ready to promote
|
||||
|
||||
---
|
||||
|
||||
### Complex Middlegame Positions
|
||||
|
||||
#### Italian Game
|
||||
**File**: `italian-game.fen`
|
||||
```
|
||||
r1bqkbnr/pppp1ppp/2n5/4p3/2B1P3/5N2/PPPP1PPP/RNBQK2R b KQkq - 3 3
|
||||
```
|
||||
**Description**: Classic Italian opening position
|
||||
|
||||
#### Sicilian Defense
|
||||
**File**: `sicilian-defense.fen`
|
||||
```
|
||||
rnbqkbnr/pp1ppppp/8/2p5/4P3/8/PPPP1PPP/RNBQKBNR w KQkq c6 0 2
|
||||
```
|
||||
**Description**: Sicilian Defense after 1.e4 c5
|
||||
|
||||
#### Queen's Gambit
|
||||
**File**: `queens-gambit.fen`
|
||||
```
|
||||
rnbqkbnr/ppp1pppp/8/3p4/2PP4/8/PP2PPPP/RNBQKBNR b KQkq c3 0 2
|
||||
```
|
||||
**Description**: Queen's Gambit position
|
||||
|
||||
---
|
||||
|
||||
### Endgame Positions
|
||||
|
||||
#### King and Queen vs King
|
||||
**File**: `kq-vs-k.fen`
|
||||
```
|
||||
8/8/8/8/8/3k4/3Q4/3K4 w - - 0 1
|
||||
```
|
||||
**Description**: Basic queen endgame
|
||||
|
||||
#### King and Rook vs King
|
||||
**File**: `kr-vs-k.fen`
|
||||
```
|
||||
8/8/8/8/8/3k4/3R4/3K4 w - - 0 1
|
||||
```
|
||||
**Description**: Basic rook endgame
|
||||
|
||||
#### Pawn Endgame
|
||||
**File**: `pawn-endgame.fen`
|
||||
```
|
||||
8/5k2/5P2/5K2/8/8/8/8 w - - 0 1
|
||||
```
|
||||
**Description**: King and pawn vs king
|
||||
|
||||
#### Opposite Color Bishops
|
||||
**File**: `opposite-bishops.fen`
|
||||
```
|
||||
8/5k2/8/3b4/8/8/3B4/5K2 w - - 0 1
|
||||
```
|
||||
**Description**: Bishops on opposite colors (often drawn)
|
||||
|
||||
---
|
||||
|
||||
### Edge Cases
|
||||
|
||||
#### Three-Fold Repetition Setup
|
||||
**File**: `threefold-setup.fen`
|
||||
```
|
||||
r1bqkb1r/pppp1ppp/2n2n2/1B2p3/4P3/5N2/PPPP1PPP/RNBQK2R w KQkq - 4 4
|
||||
```
|
||||
**Description**: Position for testing threefold repetition
|
||||
|
||||
#### Fifty-Move Rule
|
||||
**File**: `fifty-move-rule.fen`
|
||||
```
|
||||
8/8/8/8/8/3k4/3Q4/3K4 w - - 99 100
|
||||
```
|
||||
**Description**: Near fifty-move rule threshold
|
||||
|
||||
#### Insufficient Material (KB vs K)
|
||||
**File**: `insufficient-kb-vs-k.fen`
|
||||
```
|
||||
8/8/8/8/8/3k4/3B4/3K4 w - - 0 1
|
||||
```
|
||||
**Description**: Draw due to insufficient material
|
||||
|
||||
#### Insufficient Material (KN vs K)
|
||||
**File**: `insufficient-kn-vs-k.fen`
|
||||
```
|
||||
8/8/8/8/8/3k4/3N4/3K4 w - - 0 1
|
||||
```
|
||||
**Description**: Draw due to insufficient material
|
||||
|
||||
---
|
||||
|
||||
### Famous Game Positions
|
||||
|
||||
#### Immortal Game (Anderssen vs Kieseritzky, 1851)
|
||||
**File**: `immortal-game-final.fen`
|
||||
```
|
||||
r1b1kb1r/p2pqppp/5n2/1p2p3/2B1P3/1Q6/PPPPNPPP/RNB1K2R w KQkq - 0 1
|
||||
```
|
||||
**Description**: Position before the famous sacrifice
|
||||
|
||||
#### Opera Game (Morphy vs Duke of Brunswick, 1858)
|
||||
**File**: `opera-game-final.fen`
|
||||
```
|
||||
2kr4/ppp2pp1/4p3/4b3/2B5/2P2Q2/P4PPP/2KR4 b - - 0 1
|
||||
```
|
||||
**Description**: Famous tactical position
|
||||
|
||||
---
|
||||
|
||||
### Test-Specific Positions
|
||||
|
||||
#### All Pieces Present
|
||||
**File**: `all-pieces.fen`
|
||||
```
|
||||
rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1
|
||||
```
|
||||
**Description**: For testing piece rendering
|
||||
|
||||
#### Empty Board (only kings)
|
||||
**File**: `empty-board.fen`
|
||||
```
|
||||
4k3/8/8/8/8/8/8/4K3 w - - 0 1
|
||||
```
|
||||
**Description**: Minimal valid position
|
||||
|
||||
#### Maximum Pieces
|
||||
**File**: `max-pieces.fen`
|
||||
```
|
||||
rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1
|
||||
```
|
||||
**Description**: All 32 pieces on board
|
||||
|
||||
#### Piece Movement Tests
|
||||
**File**: `piece-movement-test.fen`
|
||||
```
|
||||
8/8/8/3p1p2/2pNPp2/3p1p2/8/8 w - - 0 1
|
||||
```
|
||||
**Description**: Knight surrounded by enemy pawns
|
||||
|
||||
---
|
||||
|
||||
## Usage in Tests
|
||||
|
||||
```javascript
|
||||
import { loadFEN } from '../utils/fixtures';
|
||||
|
||||
test('Fool\'s Mate detection', () => {
|
||||
const position = loadFEN('fools-mate');
|
||||
const chess = new Chess(position);
|
||||
|
||||
expect(chess.isCheckmate()).toBe(true);
|
||||
});
|
||||
```
|
||||
|
||||
## Adding New Positions
|
||||
|
||||
1. Create a new `.fen` file
|
||||
2. Validate FEN using chess.js or online validator
|
||||
3. Add description to this README
|
||||
4. Create corresponding test cases
|
||||
5. Document expected behavior
|
||||
|
||||
## FEN Validation
|
||||
|
||||
To validate FEN strings:
|
||||
```javascript
|
||||
import { Chess } from 'chess.js';
|
||||
|
||||
const isValidFEN = (fen) => {
|
||||
try {
|
||||
const chess = new Chess(fen);
|
||||
return chess.fen() === fen;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## Resources
|
||||
|
||||
- FEN Notation: https://en.wikipedia.org/wiki/Forsyth%E2%80%93Edwards_Notation
|
||||
- Chess.js Library: https://github.com/jhlywa/chess.js
|
||||
- FEN Validator: https://www.chess.com/analysis
|
||||
@@ -0,0 +1,483 @@
|
||||
# Test Scenarios (JSON Format)
|
||||
|
||||
This directory contains structured test scenarios in JSON format for automated testing of specific chess game behaviors.
|
||||
|
||||
## Scenario Format
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "unique-scenario-id",
|
||||
"name": "Scenario Name",
|
||||
"description": "Detailed description",
|
||||
"category": "category-name",
|
||||
"priority": "high|medium|low",
|
||||
"setup": {
|
||||
"fen": "FEN string",
|
||||
"description": "Setup description"
|
||||
},
|
||||
"steps": [
|
||||
{
|
||||
"action": "action-type",
|
||||
"params": {},
|
||||
"expected": {}
|
||||
}
|
||||
],
|
||||
"assertions": [
|
||||
{
|
||||
"type": "assertion-type",
|
||||
"expected": "expected-value"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Available Scenarios
|
||||
|
||||
### Basic Movement Scenarios
|
||||
|
||||
#### Pawn Movement
|
||||
**File**: `pawn-movement.json`
|
||||
```json
|
||||
{
|
||||
"id": "pawn-001",
|
||||
"name": "Pawn Initial Two-Square Move",
|
||||
"description": "Verify pawn can move two squares from starting position",
|
||||
"category": "piece-movement",
|
||||
"priority": "high",
|
||||
"setup": {
|
||||
"fen": "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1",
|
||||
"description": "Initial board position"
|
||||
},
|
||||
"steps": [
|
||||
{
|
||||
"action": "selectPiece",
|
||||
"params": { "square": "e2" },
|
||||
"expected": { "validMoves": ["e3", "e4"] }
|
||||
},
|
||||
{
|
||||
"action": "movePiece",
|
||||
"params": { "from": "e2", "to": "e4" },
|
||||
"expected": { "success": true }
|
||||
}
|
||||
],
|
||||
"assertions": [
|
||||
{ "type": "pieceAt", "square": "e4", "expected": "white-pawn" },
|
||||
{ "type": "pieceAt", "square": "e2", "expected": null },
|
||||
{ "type": "turn", "expected": "black" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### Knight Jump
|
||||
**File**: `knight-jump.json`
|
||||
```json
|
||||
{
|
||||
"id": "knight-001",
|
||||
"name": "Knight Jumps Over Pieces",
|
||||
"description": "Verify knight can jump over blocking pieces",
|
||||
"category": "piece-movement",
|
||||
"priority": "high",
|
||||
"setup": {
|
||||
"fen": "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1",
|
||||
"description": "Initial position with pawns blocking"
|
||||
},
|
||||
"steps": [
|
||||
{
|
||||
"action": "movePiece",
|
||||
"params": { "from": "b1", "to": "c3" },
|
||||
"expected": { "success": true }
|
||||
}
|
||||
],
|
||||
"assertions": [
|
||||
{ "type": "pieceAt", "square": "c3", "expected": "white-knight" },
|
||||
{ "type": "pieceAt", "square": "b1", "expected": null }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Special Moves Scenarios
|
||||
|
||||
#### En Passant
|
||||
**File**: `en-passant.json`
|
||||
```json
|
||||
{
|
||||
"id": "special-001",
|
||||
"name": "En Passant Capture",
|
||||
"description": "Verify en passant capture works correctly",
|
||||
"category": "special-moves",
|
||||
"priority": "high",
|
||||
"setup": {
|
||||
"fen": "rnbqkbnr/ppp1pppp/8/3pP3/8/8/PPPP1PPP/RNBQKBNR w KQkq d6 0 2",
|
||||
"description": "White pawn on e5, black just moved d7-d5"
|
||||
},
|
||||
"steps": [
|
||||
{
|
||||
"action": "movePiece",
|
||||
"params": { "from": "e5", "to": "d6" },
|
||||
"expected": { "success": true, "captureType": "en-passant" }
|
||||
}
|
||||
],
|
||||
"assertions": [
|
||||
{ "type": "pieceAt", "square": "d6", "expected": "white-pawn" },
|
||||
{ "type": "pieceAt", "square": "d5", "expected": null },
|
||||
{ "type": "pieceAt", "square": "e5", "expected": null },
|
||||
{ "type": "capturedPieces", "color": "black", "expected": ["pawn"] }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### Castling Kingside
|
||||
**File**: `castling-kingside.json`
|
||||
```json
|
||||
{
|
||||
"id": "castling-001",
|
||||
"name": "Kingside Castling",
|
||||
"description": "Verify kingside castling moves both king and rook",
|
||||
"category": "special-moves",
|
||||
"priority": "high",
|
||||
"setup": {
|
||||
"fen": "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQK2R w KQkq - 0 1",
|
||||
"description": "Cleared path for kingside castling"
|
||||
},
|
||||
"steps": [
|
||||
{
|
||||
"action": "movePiece",
|
||||
"params": { "from": "e1", "to": "g1" },
|
||||
"expected": { "success": true, "moveType": "castling" }
|
||||
}
|
||||
],
|
||||
"assertions": [
|
||||
{ "type": "pieceAt", "square": "g1", "expected": "white-king" },
|
||||
{ "type": "pieceAt", "square": "f1", "expected": "white-rook" },
|
||||
{ "type": "pieceAt", "square": "e1", "expected": null },
|
||||
{ "type": "pieceAt", "square": "h1", "expected": null },
|
||||
{ "type": "castlingRights", "white": { "kingside": false, "queenside": true } }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### Pawn Promotion
|
||||
**File**: `pawn-promotion.json`
|
||||
```json
|
||||
{
|
||||
"id": "promotion-001",
|
||||
"name": "Pawn Promotion to Queen",
|
||||
"description": "Verify pawn promotes to queen on 8th rank",
|
||||
"category": "special-moves",
|
||||
"priority": "high",
|
||||
"setup": {
|
||||
"fen": "4k3/P7/8/8/8/8/8/4K3 w - - 0 1",
|
||||
"description": "White pawn on a7 ready to promote"
|
||||
},
|
||||
"steps": [
|
||||
{
|
||||
"action": "movePiece",
|
||||
"params": { "from": "a7", "to": "a8", "promotion": "queen" },
|
||||
"expected": { "success": true, "moveType": "promotion" }
|
||||
}
|
||||
],
|
||||
"assertions": [
|
||||
{ "type": "pieceAt", "square": "a8", "expected": "white-queen" },
|
||||
{ "type": "pieceAt", "square": "a7", "expected": null }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Game State Scenarios
|
||||
|
||||
#### Check Detection
|
||||
**File**: `check-detection.json`
|
||||
```json
|
||||
{
|
||||
"id": "gamestate-001",
|
||||
"name": "Check Detection",
|
||||
"description": "Verify check is detected and displayed",
|
||||
"category": "game-state",
|
||||
"priority": "critical",
|
||||
"setup": {
|
||||
"fen": "rnbqkbnr/pppp1ppp/8/4p3/4P3/8/PPPP1PPP/RNBQKBNR w KQkq - 0 2",
|
||||
"description": "Standard position"
|
||||
},
|
||||
"steps": [
|
||||
{
|
||||
"action": "movePiece",
|
||||
"params": { "from": "f1", "to": "c4" },
|
||||
"expected": { "success": true }
|
||||
},
|
||||
{
|
||||
"action": "movePiece",
|
||||
"params": { "from": "d8", "to": "h4" },
|
||||
"expected": { "success": true, "check": true }
|
||||
}
|
||||
],
|
||||
"assertions": [
|
||||
{ "type": "inCheck", "color": "white", "expected": true },
|
||||
{ "type": "checkIndicator", "visible": true },
|
||||
{ "type": "validMoves", "mustEscapeCheck": true }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### Checkmate
|
||||
**File**: `checkmate-fools-mate.json`
|
||||
```json
|
||||
{
|
||||
"id": "checkmate-001",
|
||||
"name": "Fool's Mate Checkmate",
|
||||
"description": "Verify checkmate detection in Fool's Mate",
|
||||
"category": "game-state",
|
||||
"priority": "critical",
|
||||
"setup": {
|
||||
"fen": "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1",
|
||||
"description": "Initial position"
|
||||
},
|
||||
"steps": [
|
||||
{ "action": "movePiece", "params": { "from": "f2", "to": "f3" } },
|
||||
{ "action": "movePiece", "params": { "from": "e7", "to": "e5" } },
|
||||
{ "action": "movePiece", "params": { "from": "g2", "to": "g4" } },
|
||||
{
|
||||
"action": "movePiece",
|
||||
"params": { "from": "d8", "to": "h4" },
|
||||
"expected": { "success": true, "checkmate": true }
|
||||
}
|
||||
],
|
||||
"assertions": [
|
||||
{ "type": "gameOver", "expected": true },
|
||||
{ "type": "result", "expected": "black-wins" },
|
||||
{ "type": "reason", "expected": "checkmate" },
|
||||
{ "type": "inCheckmate", "color": "white", "expected": true }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### Stalemate
|
||||
**File**: `stalemate.json`
|
||||
```json
|
||||
{
|
||||
"id": "gamestate-003",
|
||||
"name": "Stalemate Detection",
|
||||
"description": "Verify stalemate results in draw",
|
||||
"category": "game-state",
|
||||
"priority": "high",
|
||||
"setup": {
|
||||
"fen": "k7/8/1Q6/8/8/8/8/7K b - - 0 1",
|
||||
"description": "Black king with no legal moves, not in check"
|
||||
},
|
||||
"steps": [],
|
||||
"assertions": [
|
||||
{ "type": "gameOver", "expected": true },
|
||||
{ "type": "result", "expected": "draw" },
|
||||
{ "type": "reason", "expected": "stalemate" },
|
||||
{ "type": "legalMoves", "color": "black", "expected": [] }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### UI Interaction Scenarios
|
||||
|
||||
#### Drag and Drop
|
||||
**File**: `drag-drop-move.json`
|
||||
```json
|
||||
{
|
||||
"id": "ui-001",
|
||||
"name": "Drag and Drop Valid Move",
|
||||
"description": "Verify drag-drop interaction for valid move",
|
||||
"category": "ui-interaction",
|
||||
"priority": "high",
|
||||
"setup": {
|
||||
"fen": "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1",
|
||||
"description": "Initial position"
|
||||
},
|
||||
"steps": [
|
||||
{
|
||||
"action": "dragStart",
|
||||
"params": { "square": "e2" },
|
||||
"expected": { "dragActive": true, "pieceSelected": true }
|
||||
},
|
||||
{
|
||||
"action": "dragOver",
|
||||
"params": { "square": "e4" },
|
||||
"expected": { "validMoveHighlight": true }
|
||||
},
|
||||
{
|
||||
"action": "drop",
|
||||
"params": { "square": "e4" },
|
||||
"expected": { "success": true, "pieceAt": "e4" }
|
||||
}
|
||||
],
|
||||
"assertions": [
|
||||
{ "type": "pieceAt", "square": "e4", "expected": "white-pawn" },
|
||||
{ "type": "dragActive", "expected": false }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### Click to Move
|
||||
**File**: `click-to-move.json`
|
||||
```json
|
||||
{
|
||||
"id": "ui-002",
|
||||
"name": "Click-Select-Click-Move",
|
||||
"description": "Verify click-based move selection",
|
||||
"category": "ui-interaction",
|
||||
"priority": "high",
|
||||
"setup": {
|
||||
"fen": "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1",
|
||||
"description": "Initial position"
|
||||
},
|
||||
"steps": [
|
||||
{
|
||||
"action": "click",
|
||||
"params": { "square": "e2" },
|
||||
"expected": { "pieceSelected": true, "validMovesHighlighted": ["e3", "e4"] }
|
||||
},
|
||||
{
|
||||
"action": "click",
|
||||
"params": { "square": "e4" },
|
||||
"expected": { "success": true }
|
||||
}
|
||||
],
|
||||
"assertions": [
|
||||
{ "type": "pieceAt", "square": "e4", "expected": "white-pawn" },
|
||||
{ "type": "selectedSquare", "expected": null }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Error Handling Scenarios
|
||||
|
||||
#### Invalid Move
|
||||
**File**: `invalid-move.json`
|
||||
```json
|
||||
{
|
||||
"id": "error-001",
|
||||
"name": "Invalid Move Rejection",
|
||||
"description": "Verify invalid moves are rejected with feedback",
|
||||
"category": "error-handling",
|
||||
"priority": "high",
|
||||
"setup": {
|
||||
"fen": "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1",
|
||||
"description": "Initial position"
|
||||
},
|
||||
"steps": [
|
||||
{
|
||||
"action": "movePiece",
|
||||
"params": { "from": "e2", "to": "e5" },
|
||||
"expected": { "success": false, "error": "Invalid move" }
|
||||
}
|
||||
],
|
||||
"assertions": [
|
||||
{ "type": "pieceAt", "square": "e2", "expected": "white-pawn" },
|
||||
{ "type": "pieceAt", "square": "e5", "expected": null },
|
||||
{ "type": "errorMessage", "visible": true }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### Move Opponent's Piece
|
||||
**File**: `wrong-color-move.json`
|
||||
```json
|
||||
{
|
||||
"id": "error-002",
|
||||
"name": "Cannot Move Opponent's Piece",
|
||||
"description": "Verify player cannot move opponent's pieces",
|
||||
"category": "error-handling",
|
||||
"priority": "critical",
|
||||
"setup": {
|
||||
"fen": "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1",
|
||||
"description": "White to move"
|
||||
},
|
||||
"steps": [
|
||||
{
|
||||
"action": "movePiece",
|
||||
"params": { "from": "e7", "to": "e5" },
|
||||
"expected": { "success": false, "error": "Wrong turn" }
|
||||
}
|
||||
],
|
||||
"assertions": [
|
||||
{ "type": "pieceAt", "square": "e7", "expected": "black-pawn" },
|
||||
{ "type": "turn", "expected": "white" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Usage in Tests
|
||||
|
||||
```javascript
|
||||
import scenario from './test-data/scenarios/pawn-movement.json';
|
||||
|
||||
describe(scenario.name, () => {
|
||||
test(scenario.description, async () => {
|
||||
// Setup
|
||||
const chess = new Chess(scenario.setup.fen);
|
||||
|
||||
// Execute steps
|
||||
for (const step of scenario.steps) {
|
||||
const result = executeAction(step.action, step.params);
|
||||
expect(result).toMatchObject(step.expected);
|
||||
}
|
||||
|
||||
// Verify assertions
|
||||
for (const assertion of scenario.assertions) {
|
||||
verifyAssertion(chess, assertion);
|
||||
}
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## Scenario Categories
|
||||
|
||||
- **piece-movement**: Basic piece movements
|
||||
- **special-moves**: Castling, en passant, promotion
|
||||
- **game-state**: Check, checkmate, stalemate
|
||||
- **ui-interaction**: Drag-drop, click-to-move
|
||||
- **error-handling**: Invalid moves, wrong turn
|
||||
- **performance**: Load testing, stress testing
|
||||
|
||||
## Adding New Scenarios
|
||||
|
||||
1. Create JSON file with proper structure
|
||||
2. Validate JSON syntax
|
||||
3. Test scenario manually
|
||||
4. Add to appropriate category
|
||||
5. Update this README
|
||||
|
||||
## Validation
|
||||
|
||||
```javascript
|
||||
const validateScenario = (scenario) => {
|
||||
return (
|
||||
scenario.id &&
|
||||
scenario.name &&
|
||||
scenario.category &&
|
||||
scenario.priority &&
|
||||
scenario.setup &&
|
||||
scenario.steps &&
|
||||
Array.isArray(scenario.assertions)
|
||||
);
|
||||
};
|
||||
```
|
||||
Reference in New Issue
Block a user