Skip to content

Commit c2961fb

Browse files
committed
tests
1 parent 9214870 commit c2961fb

1 file changed

Lines changed: 146 additions & 1 deletion

File tree

src/app/components/GtfsVisualizationMap.spec.tsx

Lines changed: 146 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,149 @@
1-
import { generatePmtilesUrls } from './GtfsVisualizationMap.functions';
1+
import {
2+
generatePmtilesUrls,
3+
generateStopColorExpression,
4+
generateRouteOutlineColorExpression,
5+
} from './GtfsVisualizationMap.functions';
6+
7+
const LIGHT_BG = '#f6f6f6';
8+
const DARK_BG = '#0d0d0d';
9+
const LIGHT_ALT = '#444444';
10+
const DARK_ALT = '#ffffff';
11+
12+
describe('generateStopColorExpression', () => {
13+
it('returns fallback directly when routeIdToColor is empty', () => {
14+
expect(generateStopColorExpression({}, LIGHT_BG, LIGHT_ALT, '#888')).toBe(
15+
'#888',
16+
);
17+
});
18+
19+
it('skips entries with null or invalid hex colors and returns fallback', () => {
20+
const result = generateStopColorExpression(
21+
{ r1: null as unknown as string, r2: 'gggggg', r3: '' },
22+
LIGHT_BG,
23+
LIGHT_ALT,
24+
);
25+
expect(result).toBe('#888');
26+
});
27+
28+
it('expands 3-digit hex to 6-digit before comparison', () => {
29+
// #000 → #000000 (black) — high contrast against light bg → route color wins
30+
const result = generateStopColorExpression(
31+
{ r1: '000' },
32+
LIGHT_BG,
33+
LIGHT_ALT,
34+
) as unknown[];
35+
expect(result[0]).toBe('case');
36+
expect(result[2]).toBe('#000000');
37+
});
38+
39+
it('uses route color when crBg >= crAlt (black route on light bg)', () => {
40+
// crBg(black vs #f6f6f6) ≈ 20.3 >> crAlt(black vs #444444) ≈ 13.4 → route color wins
41+
const result = generateStopColorExpression(
42+
{ route1: '000000' },
43+
LIGHT_BG,
44+
LIGHT_ALT,
45+
) as unknown[];
46+
expect(result[0]).toBe('case');
47+
expect(result[2]).toBe('#000000');
48+
});
49+
50+
it('uses alt color when crAlt > crBg (white route on light bg)', () => {
51+
// crBg(white vs #f6f6f6) ≈ 1.03 << crAlt(white vs #444444) ≈ 3.31 → alt wins
52+
const result = generateStopColorExpression(
53+
{ route1: 'ffffff' },
54+
LIGHT_BG,
55+
LIGHT_ALT,
56+
) as unknown[];
57+
expect(result[0]).toBe('case');
58+
expect(result[2]).toBe(LIGHT_ALT);
59+
});
60+
61+
it('uses alt color for dark route on dark map background', () => {
62+
// crBg(black vs #0d0d0d) ≈ 2.02 << crAlt(black vs #ffffff) ≈ 21 → alt wins
63+
const result = generateStopColorExpression(
64+
{ route1: '000000' },
65+
DARK_BG,
66+
DARK_ALT,
67+
) as unknown[];
68+
expect(result[2]).toBe(DARK_ALT);
69+
});
70+
71+
it('picks correct color independently per route', () => {
72+
// r1=black → route color, r2=white → alt
73+
const result = generateStopColorExpression(
74+
{ r1: '000000', r2: 'ffffff' },
75+
LIGHT_BG,
76+
LIGHT_ALT,
77+
) as unknown[];
78+
expect(result[0]).toBe('case');
79+
expect(result[2]).toBe('#000000'); // r1: route color
80+
expect(result[4]).toBe(LIGHT_ALT); // r2: alt color
81+
expect(result[5]).toBe('#888'); // fallback
82+
});
83+
84+
it('includes the fallback as the last element of the case expression', () => {
85+
const result = generateStopColorExpression(
86+
{ r1: '000000' },
87+
LIGHT_BG,
88+
LIGHT_ALT,
89+
'#cccccc',
90+
) as unknown[];
91+
expect(result[result.length - 1]).toBe('#cccccc');
92+
});
93+
});
94+
95+
describe('generateRouteOutlineColorExpression', () => {
96+
// Helper to safely index into nested unknown arrays
97+
type NestedArr = Array<unknown | NestedArr>;
98+
const at = (arr: NestedArr, ...indices: number[]): NestedArr =>
99+
indices.reduce<NestedArr>((a, i) => (a as NestedArr[])[i], arr);
100+
it('returns an array expression starting with "let" and "rgba"', () => {
101+
const result = generateRouteOutlineColorExpression(LIGHT_BG, LIGHT_ALT);
102+
expect(Array.isArray(result)).toBe(true);
103+
expect(result[0]).toBe('let');
104+
expect(result[1]).toBe('rgba');
105+
});
106+
107+
it('embeds mapBgColor and altOutlineColor as the case outputs', () => {
108+
const result = generateRouteOutlineColorExpression(LIGHT_BG, LIGHT_ALT);
109+
// Navigate to the innermost case: result[3][3][3][3]
110+
const caseExpr = at(result as NestedArr, 3, 3, 3, 3);
111+
expect(caseExpr[0]).toBe('case');
112+
expect(caseExpr[2]).toBe(LIGHT_BG); // chosen when crBg >= crAlt
113+
expect(caseExpr[3]).toBe(LIGHT_ALT); // chosen when crAlt > crBg
114+
});
115+
116+
it('embeds precomputed bgLum for the given mapBgColor in the crBg expression', () => {
117+
const result = generateRouteOutlineColorExpression('#f6f6f6', LIGHT_ALT);
118+
// bgLum for #f6f6f6: (246/255) ≈ 0.9647 (Rec. 709 coefficients sum to 1 for grey)
119+
const expectedBgLum = (0.2126 * 246 + 0.7152 * 246 + 0.0722 * 246) / 255;
120+
// structure: result[3]=['let','lum',...,innerCrBg], result[3][3]=['let','crBg',crBgExpr,...]
121+
// crBgExpr = ['/', ['+', ['max', ['var','lum'], bgLum], 0.05], ...]
122+
// bgLum appears at crBgExpr[1][1][2]
123+
const crBgExpr = at(result as NestedArr, 3, 3, 2);
124+
const bgLumInExpr = at(crBgExpr, 1, 1)[2] as number;
125+
expect(bgLumInExpr).toBeCloseTo(expectedBgLum, 10);
126+
});
127+
128+
it('works for dark mode colors without error', () => {
129+
expect(() =>
130+
generateRouteOutlineColorExpression(DARK_BG, DARK_ALT),
131+
).not.toThrow();
132+
});
133+
134+
it('uses different precomputed luminance values for light vs dark bg', () => {
135+
const lightResult = generateRouteOutlineColorExpression(
136+
LIGHT_BG,
137+
LIGHT_ALT,
138+
);
139+
const darkResult = generateRouteOutlineColorExpression(DARK_BG, DARK_ALT);
140+
const crBgLight = at(lightResult as NestedArr, 3, 3, 2);
141+
const crBgDark = at(darkResult as NestedArr, 3, 3, 2);
142+
const bgLumLight = at(crBgLight, 1, 1)[2] as number;
143+
const bgLumDark = at(crBgDark, 1, 1)[2] as number;
144+
expect(bgLumLight).toBeGreaterThan(bgLumDark);
145+
});
146+
});
2147

3148
describe('generatePmtilesUrls', () => {
4149
const latestDataset = {

0 commit comments

Comments
 (0)