brackt/plans/phase-2.7-testing-instructions.md
Chris Parsons dfe4b7b643 Add bracket seeding tests and implement tournament-style seeding for 16 and 32-team brackets
- Introduced `generateStandardSeeding()` function to create proper tournament matchups (e.g., 1v16, 8v9).
- Added comprehensive tests for 4, 8, 16, and 32-team brackets to ensure correct seeding and balance.
- Updated testing instructions to reflect new features and improvements in bracket generation.
- Fixed issues with sequential seeding and ensured all matchups sum to n+1 for balance.
- Implemented batch winner selection and duplicate participant prevention for improved user experience.
- Enhanced fantasy points display on event pages for better visibility of participant results.
2025-11-04 22:09:44 -08:00

18 KiB

Phase 2.7 Testing Instructions

Phase: 2.7 - Simple Template Expansion (16, 32 teams) Status: Completed Date: 2025-01-04

Overview

Phase 2.7 adds support for larger bracket templates (16-team and 32-team) with non-scoring rounds. Only participants who reach the Elite Eight (top 8) score fantasy points. Early eliminations receive 0 points per Q20.

Phase 2.7 Testing Checklist

1. Bracket Generation UI (/admin/sports-seasons/:id/events/:eventId/bracket)

Test the template selector:

  • Navigate to a playoff event that has no bracket yet
  • See a "Bracket Template" dropdown with all options:
    • Simple 4-Team Bracket - 4 teams
    • Simple 8-Team Bracket - 8 teams
    • Simple 16-Team Bracket - 16 teams
    • Simple 32-Team Bracket - 32 teams
    • NCAA March Madness (68 teams) - 68 teams
    • NFL Playoffs (14 teams) - 14 teams
    • NBA Playoffs (16 teams) - 16 teams
  • Change template selection - participant count should update dynamically
  • See round progression text (e.g., "Rounds: Round of 16 → Quarterfinals → Semifinals → Finals")

2. 16-Team Bracket Creation

Generate a 16-team bracket:

  • Select "Simple 16-Team Bracket"
  • Fill in 16 participants (seeds 1-16)
  • Click "Generate Bracket"
  • Verify success message

Check bracket structure:

  • See "Round of 16" section with 8 matches
  • See "Quarterfinals" section with 4 matches (TBD participants)
  • See "Semifinals" section with 2 matches (TBD participants)
  • See "Finals" section with 1 match (TBD participants)

Complete Round of 16:

  • Set winners for all 8 matches
  • Winners should auto-advance to Quarterfinals
  • Click "Complete Round & Calculate Placements"
  • Select "Round of 16" from dropdown
  • Verify 8 losers get 0 fantasy points (early elimination per Q20)

Complete Quarterfinals:

  • Set winners for all 4 matches
  • Winners advance to Semifinals
  • Complete the round
  • Verify 4 losers share 5th-8th place (20 points each with default scoring)

Complete Semifinals:

  • Set winners for 2 matches
  • Winners advance to Finals
  • Complete the round
  • Verify 2 losers share 3rd-4th place (45 points each)

Complete Finals:

  • Set winner
  • Complete the round
  • Verify winner gets 1st (100 points), loser gets 2nd (70 points)
  • Navigate back to event detail page (click "Back to Event" or navigate to /admin/sports-seasons/{id}/events/{eventId})
  • See new "Fantasy Points Awarded" card showing all participant results with positions and points
  • Verify early eliminations show "Early Elimination" with 0 points
  • Verify top 8 finishers show correct positions (1st-8th) with correct point totals

3. Batch Winner Selection (NEW)

Test the batch winner selection UI:

  • Navigate to any generated bracket (16 or 32-team)
  • See winner dropdowns for each incomplete match
  • Select a winner for one match - verify no submit button appears yet
  • Select winners for 2-3 matches in a round
  • Verify "Save X Winner(s) for [Round]" button appears
  • Click the batch save button
  • Verify success message shows correct count
  • Verify all selected winners are set
  • Verify winners auto-advance to next round
  • Verify winner dropdowns are cleared after submission

Test error handling:

  • Try to submit with no winners selected - should not see button
  • Select winners for matches in different rounds - each round has separate button
  • Test partial success scenario (if possible to trigger)

Test workflow efficiency:

  • Set all 8 winners for Round of 16 at once
  • Verify all 8 winners advance to Quarterfinals correctly
  • Set all 4 Quarterfinals winners at once
  • Verify advancement and placement calculation

4. 32-Team Bracket Creation

Generate a 32-team bracket:

  • Select "Simple 32-Team Bracket"
  • Fill in 32 participants
  • Generate bracket
  • See 5 rounds: Round of 32 → Round of 16 → Quarterfinals → Semifinals → Finals

Test non-scoring rounds:

  • Complete Round of 32 (16 matches)
  • Verify 16 losers get 0 points (non-scoring round)
  • Complete Round of 16 (8 matches)
  • Verify 8 losers get 0 points (non-scoring round)
  • Complete Quarterfinals through Finals
  • Verify scoring matches Q20: Only top 8 finishers get points

Final tally check:

  • Total participants: 32
  • Participants with 0 points: 24 (Round of 32 + Round of 16 losers)
  • Participants scoring: 8 (QF losers through Champion)

4. Template-Based Advancement

Verify auto-advancement works:

  • In any multi-round bracket, set a winner in Round 1
  • Check that winner appears in correct slot in Round 2
  • Match advancement pattern:
    • Match 1 winner → Next round Match 1, Participant 1
    • Match 2 winner → Next round Match 1, Participant 2
    • Match 3 winner → Next round Match 2, Participant 1
    • Match 4 winner → Next round Match 2, Participant 2

5. Scoring Calculator Integration

Check participant results:

  • After completing a round, check database participantResults table
  • Non-scoring round losers should have finalPosition = 0
  • Scoring round participants should have correct placements (1-8)

Verify team standings update:

  • If you have teams that drafted these participants
  • Check that standings recalculate after each round completion
  • Early elimination participants (0 points) should not add to team score

6. Error Handling

Test validation:

  • Try to generate bracket without selecting all participants → error
  • Try to select same participant twice → "Each participant can only be selected once"
  • Try to set winner when participants aren't assigned → should be disabled
  • Try to complete round with incomplete matches → error

7. Backward Compatibility

Test existing 4 and 8 team brackets:

  • Generate a "Simple 4-Team Bracket" → should work as before
  • Generate a "Simple 8-Team Bracket" → should work as before
  • All rounds should be scoring rounds for these templates
  • No 0-point eliminations (all 4 or 8 participants score)

Viewing Fantasy Points (NEW)

After completing rounds and calculating placements, you can view the fantasy points awarded:

Navigation:

  1. From the bracket management page, click "Back to Event"
  2. Or navigate to /admin/sports-seasons/{sportsSeasonId}/events/{eventId}

Fantasy Points Awarded Card:

  • Shows all participants with calculated fantasy points
  • Sorted by final position (1st place at top)
  • Displays position with medals (🥇🥈🥉) for top 3
  • Shows "Early Elimination" for participants with 0 points (early knockout)
  • Points displayed in green for scoring participants, gray for 0 points

Example Display:

Final Position | Participant      | Fantasy Points
1st 🥇        | Arizona          | 100 pts
2nd 🥈        | UConn            | 70 pts
3rd 🥉        | Houston          | 45 pts
4th           | Duke             | 45 pts
5th           | Kansas           | 20 pts
6th           | Purdue           | 20 pts
7th           | Tennessee        | 20 pts
8th           | North Carolina   | 20 pts
Early Elim.   | UCLA             | 0 pts
Early Elim.   | Alabama          | 0 pts
...

Note: Team standings (total points across all events) are not yet displayed in the UI but are being calculated and stored in the database.

Key Expected Behaviors

Non-Scoring Rounds (Q20)

  • Round of 16 in 16-team bracket: 8 teams get 0 points
  • Round of 32 + Round of 16 in 32-team bracket: 24 teams get 0 points
  • Early eliminations don't count toward team standings

Scoring Rounds

  • Only Quarterfinals and beyond score fantasy points
  • Always exactly 8 participants score (1st through 8th)
  • Point distribution per default scoring:
    • 1st: 100, 2nd: 70
    • 3rd/4th (avg): 45 each
    • 5th-8th (avg): 20 each
    • Total: 340 points

Template Storage

  • Check scoringEvents table has bracketTemplateId stored
  • Check scoringEvents table has scoringStartsAtRound stored
  • Check playoffMatches have isScoring flag set correctly

Things NOT to Test Yet (Phase 2.8+)

  • NCAA 68-team bracket generation (won't assign participants correctly)
  • NFL 14-team with bye weeks
  • First Four play-in games
  • TBD slot handling

Database Verification Queries

-- Check event has template ID stored
SELECT id, name, bracketTemplateId, scoringStartsAtRound
FROM scoring_events
WHERE id = 'your-event-id';

-- Check matches have isScoring flag
SELECT round, matchNumber, isScoring, participant1Id, participant2Id, winnerId
FROM playoff_matches
WHERE scoringEventId = 'your-event-id'
ORDER BY round, matchNumber;

-- Check participant results after round completion
SELECT p.name, pr.finalPosition, pr.qualifyingPoints
FROM participant_results pr
JOIN participants p ON p.id = pr.participantId
WHERE pr.sportsSeasonId = 'your-sports-season-id'
ORDER BY pr.finalPosition;

-- Check team standings
SELECT t.name, ts.totalPoints, ts.currentRank,
       ts.firstPlaceCount, ts.secondPlaceCount, ts.thirdPlaceCount, ts.fourthPlaceCount,
       ts.fifthPlaceCount, ts.sixthPlaceCount, ts.seventhPlaceCount, ts.eighthPlaceCount
FROM team_standings ts
JOIN teams t ON t.id = ts.teamId
WHERE ts.seasonId = 'your-season-id'
ORDER BY ts.currentRank;

Bug Fixes

Bug #1: Round of 16 and other non-standard rounds not displaying (Fixed)

Issue: When creating a 16-team bracket, only Quarterfinals, Semifinals, and Finals were displayed. Round of 16 was missing.

Root Cause:

  • Client component had hardcoded rounds array: ["Quarterfinals", "Semifinals", "Finals"]
  • Server validation also had hardcoded round checking

Fix:

  • Changed to dynamically get rounds from actual matches: Array.from(new Set(matches.map(m => m.round)))
  • Updated server validation to check if round exists in matches instead of hardcoding allowed values
  • Files changed:
    • app/routes/admin.sports-seasons.$id.events.$eventId.bracket.tsx (line 54)
    • app/routes/admin.sports-seasons.$id.events.$eventId.bracket.server.ts (lines 178-196)

Status: Fixed and tested.

Bug #2: Sequential seeding instead of tournament-style seeding (Fixed)

Issue: When creating a 16-team bracket with participants seeded 1-16, the bracket showed:

  • Match 1: Seed 1 vs Seed 2 (Arizona vs Atlanta)
  • Match 2: Seed 3 vs Seed 4
  • etc.

Instead of proper tournament seeding:

  • Match 1: Seed 1 vs Seed 16
  • Match 2: Seed 8 vs Seed 9
  • etc.

Root Cause:

  • Bracket generation assigned participants sequentially instead of using standard tournament seeding algorithm

Fix:

  • Added generateStandardSeeding() function that creates balanced bracket matchups
  • Algorithm ensures:
    • All matchups sum to n+1 (e.g., 1v16, 2v15, 3v14 all sum to 17 for 16 teams)
    • Top seeds are separated into different bracket regions
    • Balanced pods for each quarterfinal section
  • Added seedInfo field to store display text (e.g., "1 vs 16")
  • Works for 4, 8, 16, and 32-team brackets
  • Non-standard sizes (68, 14) fall back to sequential until custom logic is implemented

Files changed:

  • app/models/playoff-match.ts (lines 255-369)
  • New test file: app/models/__tests__/bracket-seeding.test.ts (14 tests)

Status: Fixed and tested. All 221 tests passing.

QOL Improvement: Duplicate participant prevention (Implemented)

Feature: When selecting participants for bracket seeds, once a participant is selected for one seed, they are automatically removed from the dropdown options for all other seeds.

How it works:

  • Each seed dropdown only shows participants that haven't been selected yet
  • If you select "Arizona" as Seed 1, "Arizona" disappears from all other seed dropdowns
  • Changing the bracket template resets all selections
  • Real-time updates as you make selections

Benefits:

  • Prevents duplicate selections (the server already validated this, but now it's prevented in the UI)
  • Clearer UX - you can see which participants are still available
  • Faster bracket setup - no need to remember which teams you've already picked

Files changed:

  • app/routes/admin.sports-seasons.$id.events.$eventId.bracket.tsx (lines 40-68, 141, 167-190)
    • Added selectedParticipants state to track selections
    • Added handleTemplateChange to reset selections on template change
    • Added handleParticipantChange to update selections
    • Added getAvailableParticipants to filter out selected participants
    • Updated Select components to use controlled state and filtered options

Status: Implemented and tested. All 221 tests passing.

QOL Improvement #2: Batch winner selection (Implemented)

Feature: Instead of having individual "Set" buttons for each match, users can now select winners for multiple matches in a round and submit them all with a single button click.

How it works:

  • Each match in a round has a winner dropdown (controlled component)
  • Select winners for one or more matches
  • A "Save X Winner(s) for [Round]" button appears at the bottom of the round card
  • Click the button to submit all selected winners at once
  • Winners are automatically advanced to the next round
  • Selected winners are cleared after successful submission

Benefits:

  • Faster workflow - set multiple winners at once instead of one at a time
  • Better UX - clear visual indication of pending selections
  • Fewer page reloads - batch submission reduces server round trips
  • Consistent advancement - all winners advance in a single operation

Implementation details:

  • Client-side state tracking with selectedWinners state
  • Server-side batch processing with new "set-round-winners" intent
  • Automatic state cleanup after successful submission via useEffect
  • Error handling for partial success scenarios

Files changed:

  • app/routes/admin.sports-seasons.$id.events.$eventId.bracket.tsx (lines 1-2, 40-51, 294-334)
    • Added useEffect import
    • Added batch winner state management
    • Replaced Form-wrapped Select with controlled Select components
    • Added batch submit button per round
    • Added useEffect to clear selections after success
  • app/routes/admin.sports-seasons.$id.events.$eventId.bracket.server.ts (lines 175-276)
    • Added "set-round-winners" intent handler
    • Processes multiple winner assignments in batch
    • Handles partial success with detailed error messages

Status: Implemented and tested. All 221 tests passing.

QOL Improvement #3: Fantasy points display on event page (Implemented)

Feature: After completing rounds and calculating placements, users can now view the fantasy points awarded to each participant on the event detail page.

How it works:

  • Navigate to the event detail page (/admin/sports-seasons/{id}/events/{eventId})
  • See "Fantasy Points Awarded" card with all participant results
  • Sorted by final position (1st place at top)
  • Shows position, participant name, and fantasy points
  • Early eliminations (0 points) clearly marked

Benefits:

  • Instant verification of point calculations
  • Visual confirmation of bracket completion
  • Easy reference for standings verification
  • Clear distinction between scoring and non-scoring participants

Implementation details:

  • Loader fetches participant_results from database
  • Includes participant relation for name display
  • Client sorts by finalPosition ascending
  • Displays medals for top 3 finishers
  • Color-codes points (green for scoring, gray for 0)

Files changed:

  • app/routes/admin.sports-seasons.$id.events.$eventId.server.ts (lines 1, 16, 31-43)
    • Added import for findParticipantResultsBySportsSeasonId
    • Added participantResults to loader return data
  • app/routes/admin.sports-seasons.$id.events.$eventId.tsx (lines 38, 292-368)
    • Added participantResults to destructured loader data
    • Added "Fantasy Points Awarded" card with table
    • Displays position, participant, and points with proper formatting

Status: Implemented and tested. All 221 tests passing.

Bug #3: Round cards displaying in wrong order (Fixed)

Issue: When setting a winner in Round of 16, the round cards would shuffle and display in incorrect order (e.g., "Semifinals, Quarterfinals, Round of 16, Finals" instead of chronological order).

Root Cause:

  • Rounds were being displayed in the order they appeared from the database query
  • Database ordering was based on match results, not chronological tournament progression
  • As matches completed and were updated, the database result order could change

Fix:

  • Added getOrderedRounds() function that uses the bracket template's round order
  • If event has a bracketTemplateId, uses the template's chronological round order
  • Filters to only show rounds that have matches
  • Falls back to database order if no template is available

How it works now:

  1. Get unique rounds from matches
  2. If event has bracket template, get template
  3. Use template's round order (first round → finals)
  4. Filter to only rounds that exist in matches
  5. Display cards in correct chronological order

Example:

  • 16-team bracket: Round of 16 → Quarterfinals → Semifinals → Finals
  • 32-team bracket: Round of 32 → Round of 16 → Quarterfinals → Semifinals → Finals
  • Order remains stable even as matches are completed

Files changed:

  • app/routes/admin.sports-seasons.$id.events.$eventId.bracket.tsx (lines 23, 79-98, 221, 330)
    • Imported getBracketTemplate
    • Added getOrderedRounds() function
    • Updated round mapping to include type annotations

Status: Fixed and tested. All 221 tests passing.

Known Issues

None at this time. All 221 tests passing (207 original + 14 seeding tests).

Notes

  • Phase 2.8 will add full NCAA 68-team bracket support with First Four play-in games
  • NFL 14-team and NBA 16-team templates exist but need bye week logic (Phase 2.9)