Testing Strategy
Relevant source files
The following files were used as context for generating this wiki page:
The testing strategy for this project is pragmatic, focusing on validating core game logic through direct script execution and ensuring UI component robustness via mock data and simulated asynchronous operations. While no formal testing frameworks like Jest or Vitest are specified in the project's dependencies, the codebase employs several ad-hoc testing and validation techniques.
This approach centers on a clear separation between the UI components and the underlying game logic (e.g., poker-engine.ts). This separation allows the core logic to be tested independently, while UI components are developed and verified using static, pre-defined data structures. Asynchronous behaviors, such as API calls, are simulated to test UI states like loading and error handling.
Core Logic Unit Testing
The most explicit form of testing is applied to the poker hand evaluation logic. This is handled through a dedicated test file that defines a suite of test cases and a runner function to execute them.
Sources: lib/hand-evaluation-test.ts, lib/poker-engine.ts
Hand Evaluation Tests
A set of predefined test cases in lib/hand-evaluation-test.ts covers all major poker hand ranks, from High Card to Royal Flush. Each test case provides a specific set of five cards and the expected hand rank.
The runHandEvaluationTests function iterates through these cases, calls the evaluateHand function from the poker-engine, and compares the result to the expected outcome. This provides a direct and effective way to verify the correctness of the hand evaluation algorithm.
The diagram below shows the execution flow of the hand evaluation test runner.
Sources: lib/hand-evaluation-test.ts:114-144
Test Case Structure
Each test case is an object conforming to the HandTestCase interface, which includes a description, an array of Card objects, and the expectedRank.
| Property | Type | Description |
|---|---|---|
description | string | A human-readable description of the test case. |
cards | Card[] | An array of 5 Card objects representing the hand. |
expectedRank | HandRank | The expected hand rank string (e.g., "royal-flush"). |
Sources: lib/hand-evaluation-test.ts:4-8
An example test case for a Royal Flush is shown below:
// lib/hand-evaluation-test.ts:14-24
{
description: "Royal Flush in Hearts",
cards: [
createCard("A", "hearts"),
createCard("K", "hearts"),
createCard("Q", "hearts"),
createCard("J", "hearts"),
createCard("10", "hearts"),
],
expectedRank: "royal-flush",
},
Omaha Hand Validation
In addition to standard hand evaluation, a specific validation function, validateOmahaEvaluation, exists to test the unique constraints of Omaha poker. This function ensures that the evaluateOmahaHand function is called with the correct number of hole cards (4) and community cards (5).
Sources: lib/hand-evaluation-test.ts:147-169, lib/poker-engine.ts:114-134
Component-Level Mocking and Stubbing
Many UI components are developed and tested using mock data. This allows for rapid UI development and visual verification without requiring a live backend or fully functional game state.
Stats Dashboard
The StatsDashboard component uses a hardcoded stats object of type PlayerStats to render the user's statistics. This object serves as a stub, providing predictable data for the UI to display, including win rate, VPIP, and best hand.
This is the structure of the mock PlayerStats object.
Sources: components/stats-dashboard.tsx:18-35
User Profile and Achievements
Similarly, the UserProfile and AchievementsPage components are populated with extensive mock data. The UserProfile receives a profile object containing nested stats, achievements, and other details. The AchievementsPage further expands on this by merging unlocked achievements from the profile with a hardcoded list of locked achievements to display a complete list. This pattern allows for testing the rendering logic for various data states (e.g., locked vs. unlocked achievements, progress bars).
Sources: components/user-profile.tsx:21, components/achievements-page.tsx:24-61
Simulation of Asynchronous Operations
To test UI behavior for asynchronous actions like API calls, the application uses setTimeout to simulate network latency and responses.
Login Form Simulation
The LoginForm component provides a clear example of this strategy. On form submission, it sets a isLoading state to true and initiates a setTimeout function. After a 1-second delay, it simulates a successful login by redirecting the user. If the inputs are invalid, it sets an error message.
This sequence diagram illustrates the simulated login flow.
Sources: components/login-form.tsx:22-35
Lucky Wheel Spin
The LuckyWheel component also uses setTimeout to manage the spinning animation. The handleSpin function initiates a 5-second timeout to simulate the wheel spinning. After the delay, it processes the result, updates the component's state, and calls the onPrizeWon callback. This tests the component's ability to handle a multi-stage asynchronous user interaction.
Sources: components/lucky-wheel.tsx:101-131
Conclusion
The project's testing strategy, while lacking a formal framework, is effective for its current scope. It relies on:
- Unit tests for critical, pure functions like hand evaluation.
- Mock data to build and visually verify UI components in isolation.
- Asynchronous simulation with
setTimeoutto test dynamic UI states like loading and errors. - A modular architecture (e.g.,
poker-engine,tournament-engine) that decouples logic from the UI, making it inherently more testable.
This approach ensures that the core game mechanics are reliable and that the user interface can handle various data and asynchronous states gracefully.
Sources: lib/hand-evaluation-test.ts, components/stats-dashboard.tsx, components/login-form.tsx, hooks/use-tournament.tsx