DS0 (Design v2): the Entity Layer — teams/players/books render as themselves
Founder's #1 priority. A single cached asset+rendering system, swapped into every surface, so entities stop being flat gray strings (DESIGN-SPEC Part 2). - web/src/lib/teamMeta.js: static registry for ALL 4 sports — 30 MLB, 30 NBA, 13 WNBA teams + 48 World Cup national teams, each with real colors + the ESPN logo/flag CDN abbr. resolveTeam (abbr/full-name/nickname/alias), teamLogoUrl (statsapi->ESPN mapping: AZ->ari, CWS->chw; soccer via the countries/ flag CDN), accentColor (picks the VISIBLE color of the pair so a #000000 primary never renders an invisible accent on #06060B). Colors + abbrs sourced once from ESPN's team API — stable public facts, zero-latency static data, no paid dependency. - TeamLogo: real ESPN-CDN logo with a team-colored MONOGRAM fallback (never a gray box / bare abbr). PlayerAvatar: real headshot with a team-colored monogram fallback (kills the gray silhouette). BookWordmark: brand-color wordmark, proper casing (DraftKings, not 'draftkings'). - Swapped into the class-level shared components so it propagates to ALL surfaces: GameCard (team logos + team-colored accent edge), StatStrip (player identity avatar), StreaksPanel (P0 billboard avatars), TeamHub header (the team's real crest leads its hub). Barrel-exported. ZERO OUT-OF-POCKET: ESPN logo/flag CDN + league headshot CDNs, all verified 200 image/png across MLB/NBA/WNBA/soccer. ACCEPTANCE (SSR render proof): /entity-demo harness server-rendered the exact real asset URLs across all 4 sports — mlb/500/nyy.png, mlb/500/chw.png (White Sox, correct ESPN abbr), nba/500/lal.png, wnba/500/ny.png, countries/500/ usa.png + bra/eng/arg/jpn flags, real mlbstatic/nba headshots (Judge 592450, LeBron 2544), DraftKings/FanDuel wordmarks. Each URL curl-verified 200 image/png. Harness removed post-proof (not a product surface). Pixel screenshot blocked by WSL2<->Windows-Chrome localhost networking, not code. 2757 -> 2776 tests (+13 entityLayer, +6 boot resilience), web build exit 0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
// DS0 (Design v2) — THE ENTITY LAYER. Teams resolve to real logos + real
|
||||
// colors; the accent rule keeps team color VISIBLE on the near-black terminal.
|
||||
|
||||
const { resolveTeam, teamLogoUrl, accentColor, luminance } = require('../../web/src/lib/teamMeta');
|
||||
|
||||
describe('resolveTeam — abbr / full name / nickname / alias', () => {
|
||||
test('canonical statsapi abbr', () => {
|
||||
expect(resolveTeam('NYY', 'mlb').name).toBe('New York Yankees');
|
||||
expect(resolveTeam('MIL', 'mlb').name).toBe('Milwaukee Brewers');
|
||||
});
|
||||
test('full name and nickname', () => {
|
||||
expect(resolveTeam('Milwaukee Brewers', 'mlb').abbr).toBe('MIL');
|
||||
expect(resolveTeam('Pirates', 'mlb').abbr).toBe('PIT');
|
||||
expect(resolveTeam('Red Sox', 'mlb').abbr).toBe('BOS');
|
||||
expect(resolveTeam('White Sox', 'mlb').abbr).toBe('CWS');
|
||||
});
|
||||
test('statsapi↔ESPN abbr aliases (AZ↔ARI, CWS↔CHW)', () => {
|
||||
expect(resolveTeam('ARI', 'mlb').abbr).toBe('AZ');
|
||||
expect(resolveTeam('CHW', 'mlb').abbr).toBe('CWS');
|
||||
});
|
||||
test('unknown team → null (never a fake)', () => {
|
||||
expect(resolveTeam('ZZZ', 'mlb')).toBeNull();
|
||||
expect(resolveTeam('', 'mlb')).toBeNull();
|
||||
});
|
||||
test('resolves across leagues', () => {
|
||||
expect(resolveTeam('LAL', 'nba').name).toBe('Los Angeles Lakers');
|
||||
expect(resolveTeam('NY', 'wnba').name).toBe('New York Liberty');
|
||||
});
|
||||
test('soccer = World Cup national teams, by name + alias', () => {
|
||||
expect(resolveTeam('USA', 'soccer').name).toBe('United States');
|
||||
expect(resolveTeam('United States', 'soccer').abbr).toBe('USA');
|
||||
expect(resolveTeam('Brazil', 'soccer').abbr).toBe('BRA');
|
||||
expect(resolveTeam('South Korea', 'soccer').abbr).toBe('KOR');
|
||||
expect(resolveTeam('Ivory Coast', 'soccer').abbr).toBe('CIV'); // alias
|
||||
});
|
||||
});
|
||||
|
||||
describe('teamLogoUrl — real ESPN CDN paths', () => {
|
||||
test('MLB logo url uses the ESPN logo abbr, not the app abbr', () => {
|
||||
expect(teamLogoUrl('AZ', 'mlb')).toBe('https://a.espncdn.com/i/teamlogos/mlb/500/ari.png');
|
||||
expect(teamLogoUrl('CWS', 'mlb')).toBe('https://a.espncdn.com/i/teamlogos/mlb/500/chw.png');
|
||||
expect(teamLogoUrl('NYY', 'mlb')).toBe('https://a.espncdn.com/i/teamlogos/mlb/500/nyy.png');
|
||||
});
|
||||
test('per-league path', () => {
|
||||
expect(teamLogoUrl('LAL', 'nba')).toContain('/teamlogos/nba/500/lal.png');
|
||||
expect(teamLogoUrl('NY', 'wnba')).toContain('/teamlogos/wnba/500/ny.png');
|
||||
});
|
||||
test('soccer national teams use the flag CDN (countries/ + numeric edge)', () => {
|
||||
expect(teamLogoUrl('USA', 'soccer')).toBe('https://a.espncdn.com/i/teamlogos/countries/500/usa.png');
|
||||
expect(teamLogoUrl('South Korea', 'soccer')).toContain('/countries/500/kors.png');
|
||||
expect(teamLogoUrl('Curaçao', 'soccer')).toBe('https://a.espncdn.com/i/teamlogos/soccer/500/11678.png');
|
||||
});
|
||||
test('unknown → null', () => {
|
||||
expect(teamLogoUrl('ZZZ', 'mlb')).toBeNull();
|
||||
expect(teamLogoUrl('Narnia', 'soccer')).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('accentColor — VISIBLE on #06060B (the near-black bug guard)', () => {
|
||||
test('picks the more luminous of primary/secondary', () => {
|
||||
// White Sox: primary silver (#c4ced4) vs black — must pick silver.
|
||||
expect(accentColor('CWS', 'mlb').toLowerCase()).toBe('#c4ced4');
|
||||
// Pirates: black primary + gold secondary — must pick gold.
|
||||
expect(accentColor('PIT', 'mlb').toLowerCase()).toBe('#fdb827');
|
||||
});
|
||||
test('both-dark team never returns an invisible accent', () => {
|
||||
const acc = accentColor('SF', 'mlb'); // black + orange → orange, visible
|
||||
expect(luminance(acc)).toBeGreaterThan(40);
|
||||
});
|
||||
test('luminance ranks black low, white high', () => {
|
||||
expect(luminance('#000000')).toBeLessThan(10);
|
||||
expect(luminance('#ffffff')).toBeGreaterThan(240);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user