Unit tests
View Full Test Coverage Report
The project's frontend testing is built on a foundation of Vitest, chosen for its seamless integration with the Vite development environment, along with the React Testing Library (RTL). This combination was selected for its high performance and robust built-in mocking capabilities. RTL focuses on testing components from the user's perspective by interacting with the rendered output rather than internal implementation details. The setup is configured to automatically generate code coverage reports, providing a clear measure of test effectiveness.
Frontend
AacInterface
Handling Food Click
- Input / User Action
- Use taps a food button
- Expected Result
selectedFood
state updated to the tapped foodonFoodSelected(food)
callback invokedAudio.play()
called with the selected food.mp3
Navigate to Foods Grid
- Input / User Action
- User taps a category button
- Expected Result
selectedCategory
state set to that fruit- Food grid for that category rendered
Back to categories
- Input / User Action
- While viewing foods grid, user taps the back button
- Expected Result
selectedCategory
cleared- Categiry grid reappears
Selected food indicator updates
- Input / user Action
- User taps a food
- Expected Result
- Indicator shows the selected food
Game.ts
Method: setFoodKeys(keys: string[])
- Purpose: Defines which food keys are allowed to be spawned.
- Test Case:
- Input:
['apple', 'pizza']
- Expected Result:
foodKeys
field is updated with these values.
- Input:
Method: addFoodManually(foodKey: string)
- Purpose: Manually adds a food item to the game (e.g. from AAC).
- Test Case:
- Input:
'apple'
- Mocked:
foods.create
,setScale
,setVelocityY
,setBounce
,setCollideWorldBounds
- Expected Result:
foods.create()
called with a lanex
and'apple'
- All config methods called with correct values
- Input:
Method: spawnFood()
- Purpose: Automatically spawns food from a random key and lane.
- Test Case:
- Precondition:
foodKeys = ['apple']
- Mocked:
Phaser.Utils.Array.GetRandom
,foods.create
- Expected Result:
foods.create()
called with valid food key and lane
- Precondition:
Method: handleFoodCollision(hippoObj, foodObj)
- Purpose: Removes food when it overlaps with the hippo.
- Test Case:
- Input: a food object with
.destroy()
mocked - Expected Result:
destroy()
called on food
- Input: a food object with
Method: update()
- Purpose: Removes any food object that touches the ground.
- Test Case:
- Input: a group of food objects, some with
body.blocked.down = true
- Expected Result: Only those with
blocked.down = true
are destroyed
- Input: a group of food objects, some with
LandingPage
Join Game with Valid Code
- Input / User Action
- User enters a valid 5-character code and taps "Join Game"
- Expected Result
handleStart
sends POST request to/validate-session
- If
valid: true
, user is navigated to/GamePage
Join Game with Invalid Code
- Input / User Action
- User enters an invalid or incomplete code and taps "Join Game"
- Expected Result
isValidCode
is set tofalse
- Input fields are cleared
- Focus returns to the first input box
Create New Game Session
- Input / User Action
- User taps "No code? Create new game!"
- Expected Result
handleCreateGame
sends POST request to/create-session
- On success, user is navigated to
/GamePage
- On failure, an alert is shown and error is logged
Input Autofocus on Typing
- Input / User Action
- User types a character into a game code input field
- Expected Result
- Character is uppercased and saved to
code[index]
- Next input field is focused automatically
- Character is uppercased and saved to
Input Navigation with Backspace
- Input / User Action
- User presses Backspace in an empty input field
- Expected Result
- Focus moves to the previous input field (if it exists)
Paste Entire Code
- Input / User Action
- User pastes a 5-character code into the first input field
- Expected Result
- All input fields are populated from clipboard
- Focus moves to the next empty or last field
Error Styling Triggered on Invalid Code
- Input / User Action
- User submits an invalid code
- Expected Result
isValidCode
is set tofalse
- All inputs show error styles using CSS class
ButtonClick
Render with Text
- Input / User Action
- Component is rendered with a
text
prop
- Component is rendered with a
- Expected Result
- A
<button>
is present in the DOM - The button displays the provided text content
- A
Click Event Handling
- Input / User Action
- User clicks the rendered button
- Expected Result
onClick
callback is invoked exactly once
Styling Applied
- Input / User Action
- Component is rendered
- Expected Result
- The button has the
styles.button
class applied
- The button has the
Hippo Class (game Character)
-
Test Case 1: Initialization State -Initialization with Scene and Physics:
- Input: scene, x, y, texture, moveStrategy
- Procedure:
-
- Create a mock Phaser.scene and a mock MoveStrategy
-
- Instantiate a Hippo object
-
- Call isMouthOpen()
-
- Expected Output: true
-
Test Case 2: Toggle Mouth State
- Objective: Verify that toggleMouth() flips the mouth state and updates the frame
- Input: N/A
- Procedure:
-
- Call toggleMouth() once
-
- Check isMouthOpen() -> should be false
-
- Call toggleMouth() again
-
- Check isMouthOpen() -> should be true
-
- Expected Output: Mouth state alternates with each call
-
Test Case 3: Movement Delegation
- Objective: Confirm update() delegates to the assigned move strategy
-Input: Phaser.Types.Input.Keyboard.Cursorkeys object (mocked)
-Procedure:
-
- Spy on the update() method of the mock strategy
-
- Call hippo.update(mockCursorKeys)
-
- Expected Output: moveStrategy.update() is called with hippo and mockCursorKeys
- Objective: Confirm update() delegates to the assigned move strategy
-Input: Phaser.Types.Input.Keyboard.Cursorkeys object (mocked)
-Procedure:
-
Test Case 4: Change Strategy at Runtime
- Objective: Ensure setStrategy() properly replaces the movement strategy
- Input: New MoveStrategy instance
- Procedure:
-
- Create a second mock MoveStrategy
-
- Set the new Strategy using setStrategy()
-
- Call update() with a mock cursor input -Expected Output: The new strategy's update() method is called and not the old one
-
Backend
Session API
Generate New Session ID
- Input / User Action
- Client sends
POST /create-session
- Client sends
- Expected Result
- Server responds with
{ sessionId: string }
- The new ID is unique (not already in session file)
- Session file is updated with the new ID
- Server responds with
Validate Existing Session
- Input / User Action
- Client sends
POST /validate-session
with a valid session code
- Client sends
- Expected Result
- Server responds with
{ valid: true }
- Server responds with
Reject Invalid Session Code
- Input / User Action
- Client sends
POST /validate-session
with an invalid or non-existent code
- Client sends
- Expected Result
- Server responds with
{ valid: false }
- Server responds with
Handle Missing Game Code in Validation
- Input / User Action
- Client sends
POST /validate-session
with missing or malformed body
- Client sends
- Expected Result
- Server responds with
{ valid: false, error: 'Invalid game code format' }
- Status code is
400
- Server responds with
Get All Sessions
- Input / User Action
- Client sends
GET /sessions
- Client sends
- Expected Result
- Server responds with
{ sessions: string[] }
- Returns
[]
if file doesn't exist
- Server responds with
File Read Error Handling (GET /sessions)
- Input / User Action
- File exists but is corrupted or unreadable
- Expected Result
- Server responds with status
500
- Error is logged and message is
{ message: 'Failed to read session data' }
- Server responds with status
File Read Error Handling (POST /validate-session)
- Input / User Action
- File is unreadable or JSON parsing fails
- Expected Result
- Server responds with
{ valid: false }
- Status code is
500
- Server responds with
Write Error Handling (POST /create-session)
- Input / User Action
- Write operation fails (e.g. due to file permissions)
- Expected Result
- Server responds with
{ error: 'Failed to save session ID' }
- Status code is
500
- Server responds with