brackt/app/models/__tests__/nfl-bracket.test.ts
Chris Parsons 4bffa40606
Fix oxlint warnings: no-shadow, consistent-function-scoping, no-non-null-assertion, and others (#196)
* Fix no-shadow and consistent-function-scoping lint violations

Resolves all 11 no-shadow and 16 consistent-function-scoping oxlint
warnings and promotes both rules to errors in .oxlintrc.json.

no-shadow: renamed Drizzle callback params (sports→s, matches→m,
seasons→s) to avoid shadowing outer imports; removed shadowed
destructures (eq, inArray) from where callbacks; renamed inner
template→bracketTemplate, prev→currentTimers, season→ss, name→teamName
(with name: teamName fix to preserve semantics).

consistent-function-scoping: moved formatDate, getRankBadge,
getMovementIndicator, getPositionBadge, getStatusBadge, toDateStr,
elo (×2), weightedPick, sortByMatchNumber (×2) to module scope;
moved formatTime (×2), isValidLeagueName, getDraftTimes,
makeSeasonQueues to file scope in test files.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Fix no-non-null-assertion lint violations and promote to error

Eliminates all 208 no-non-null-assertion warnings across 38 files.
Promotes typescript/no-non-null-assertion from warn to error in
.oxlintrc.json.

Fix patterns applied:
- Map.get(key)! after .has() check → extract with get() + null guard
- Map.get(key)! on pre-populated count maps → ?? 0 default
- .set(id, map.get(id)! + 1) increment → ?? 0 before adding
- participant1Id!/participant2Id! on DB matches → ?? "" fallback
- array.find()! in tests → guard + throw or expect().toBeDefined()
- bracketTemplateCache.get(id)! → null guard extract
- Various nullable field accesses → optional chain or ?? default

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Fix prefer-add-event-listener, no-unassigned-import, require-module-specifiers

Resolves all 9 remaining non-console lint warnings and promotes all
three rules to errors in .oxlintrc.json.

- prefer-add-event-listener: converted onchange/onclick/onload
  assignments to addEventListener in useDraftNotifications.ts and
  admin.data-sync.tsx; stored changeHandler ref for proper cleanup
  with removeEventListener
- no-unassigned-import: configured rule with allow list for legitimate
  side-effect imports (*.css, @testing-library/jest-dom,
  @testing-library/cypress/add-commands)
- require-module-specifiers: removed redundant `export {}` from
  cypress/support/e2e.ts (file already has an import)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Fix TypeScript errors from no-non-null-assertion fixes

Two fixes introduced by the non-null assertion cleanup produced type
errors:

- scoring-event.ts: `?? ""` was wrong type for a participant object map;
  restructured to explicit null guards so TypeScript can narrow correctly
- standings-sync/index.ts: `?? null` after name-match lookup lost the
  truthy guarantee, causing TS18047 on the write-back block; added
  `participant &&` guard before accessing its properties

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Add npm run typecheck as Stop hook in Claude settings

Runs a full project typecheck at the end of each Claude turn so type
errors surface as feedback before the next message.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 10:59:51 -07:00

139 lines
5.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { describe, it, expect } from "vitest";
import { NFL_14, getScoringRoundType } from "~/lib/bracket-templates";
/**
* NFL 14 Bracket Unit Tests - Phase 2.9
*
* Tests the NFL 14 tournament template structure:
* - NFL 14: Wild Card (12 teams) + 2 bye teams → Divisional → Championship → Super Bowl
*/
describe("NFL 14 Bracket Template - Phase 2.9", () => {
describe("Template Structure", () => {
it("has correct total teams", () => {
expect(NFL_14.totalTeams).toBe(14);
});
it("has all 4 rounds", () => {
expect(NFL_14.rounds).toHaveLength(4);
expect(NFL_14.rounds[0].name).toBe("Wild Card");
expect(NFL_14.rounds[1].name).toBe("Divisional");
expect(NFL_14.rounds[2].name).toBe("Conference Championship");
expect(NFL_14.rounds[3].name).toBe("Super Bowl");
});
it("has correct match counts per round", () => {
expect(NFL_14.rounds[0].matchCount).toBe(6); // Wild Card: 6 games (12 teams)
expect(NFL_14.rounds[1].matchCount).toBe(4); // Divisional: 4 games
expect(NFL_14.rounds[2].matchCount).toBe(2); // Conference Championship: 2 games
expect(NFL_14.rounds[3].matchCount).toBe(1); // Super Bowl: 1 game
});
it("has correct round feeding structure", () => {
expect(NFL_14.rounds[0].feedsInto).toBe("Divisional");
expect(NFL_14.rounds[1].feedsInto).toBe("Conference Championship");
expect(NFL_14.rounds[2].feedsInto).toBe("Super Bowl");
expect(NFL_14.rounds[3].feedsInto).toBeNull();
});
});
describe("Scoring Configuration", () => {
it("marks scoring to start at Divisional", () => {
expect(NFL_14.scoringStartsAtRound).toBe("Divisional");
});
it("marks Wild Card as non-scoring", () => {
const wildCard = NFL_14.rounds.find((r) => r.name === "Wild Card");
expect(wildCard?.isScoring).toBe(false);
});
it("marks Divisional as scoring (Quarterfinals)", () => {
const divisional = NFL_14.rounds.find((r) => r.name === "Divisional");
expect(divisional?.isScoring).toBe(true);
});
it("marks Conference Championship as scoring (Semifinals)", () => {
const championship = NFL_14.rounds.find((r) => r.name === "Conference Championship");
expect(championship?.isScoring).toBe(true);
});
it("marks Super Bowl as scoring (Finals)", () => {
const superBowl = NFL_14.rounds.find((r) => r.name === "Super Bowl");
expect(superBowl?.isScoring).toBe(true);
});
it("has exactly 3 scoring rounds (Divisional, Championship, Super Bowl)", () => {
const scoringRounds = NFL_14.rounds.filter((r) => r.isScoring);
expect(scoringRounds).toHaveLength(3);
});
it("has exactly 1 non-scoring round (Wild Card)", () => {
const nonScoringRounds = NFL_14.rounds.filter((r) => !r.isScoring);
expect(nonScoringRounds).toHaveLength(1);
});
});
describe("Scoring Round Types", () => {
it("identifies Divisional as quarterfinals", () => {
expect(getScoringRoundType("Divisional", NFL_14)).toBe("quarterfinals");
});
it("identifies Conference Championship as semifinals", () => {
expect(getScoringRoundType("Conference Championship", NFL_14)).toBe("semifinals");
});
it("identifies Super Bowl as finals", () => {
expect(getScoringRoundType("Super Bowl", NFL_14)).toBe("finals");
});
it("returns null for non-scoring rounds", () => {
expect(getScoringRoundType("Wild Card", NFL_14)).toBeNull();
});
});
describe("Bye Week Logic", () => {
it("verifies Wild Card has 6 matches for 12 teams (2 teams get byes)", () => {
const wildCard = NFL_14.rounds.find((r) => r.name === "Wild Card");
expect(wildCard?.matchCount).toBe(6); // 6 matches × 2 teams = 12 teams
// Total teams (14) - Wild Card teams (12) = 2 bye teams
const byeTeams = NFL_14.totalTeams - ((wildCard?.matchCount ?? 0) * 2);
expect(byeTeams).toBe(2);
});
it("verifies Divisional round can accommodate 8 teams (6 Wild Card winners + 2 bye teams)", () => {
const wildCard = NFL_14.rounds.find((r) => r.name === "Wild Card");
const divisional = NFL_14.rounds.find((r) => r.name === "Divisional");
const wildCardWinners = wildCard?.matchCount ?? 0; // 6 winners
const byeTeams = 2;
const divisionalTeams = (divisional?.matchCount ?? 0) * 2; // 4 matches × 2 = 8 teams
expect(wildCardWinners + byeTeams).toBe(divisionalTeams);
});
});
describe("Tournament Math", () => {
it("has correct number of total matches", () => {
const totalMatches = NFL_14.rounds.reduce((sum, round) => sum + round.matchCount, 0);
expect(totalMatches).toBe(13); // 6 + 4 + 2 + 1
});
it("eliminates 6 teams in Wild Card", () => {
const wildCard = NFL_14.rounds.find((r) => r.name === "Wild Card");
expect(wildCard?.matchCount).toBe(6); // 6 losers
});
it("has 8 teams in Divisional (top 8 scoring)", () => {
const divisional = NFL_14.rounds.find((r) => r.name === "Divisional");
expect(divisional?.matchCount).toBe(4); // 4 matches = 8 teams
expect(divisional?.isScoring).toBe(true);
});
it("verifies only 8 teams score fantasy points (Divisional and beyond)", () => {
const divisional = NFL_14.rounds.find((r) => r.name === "Divisional");
const divisionalTeams = (divisional?.matchCount ?? 0) * 2; // 8 teams
expect(divisionalTeams).toBe(8); // Matches requirement from Q18
});
});
});