Skip to content

Commit 317e3e4

Browse files
authored
test(dashboard): verify type compiler validation and schema constraints for historical trend view (JhaSourav07#3399)
## Description Fixes JhaSourav07#2682 Program: GSSoC 2026 This PR adds an isolated type-compiler and schema constraint test suite to protect the historical trend view component configurations (`components/dashboard/HistoricalTrendView.tsx`). Previously, properties like data metrics arrays, range periods, and intensity fields lacked static validation barriers, leaving data keys vulnerable to implicit mutations or breakage if core models shifted upstream. ### Changes Made * Created a brand new targeted compilation-level testing file at `components/dashboard/HistoricalTrendView.type-compiler.test.tsx`. * Designed exactly 5 specific verification cases evaluating field interface safety via `expectTypeOf`, assignability mismatch blocks, optional prop compliance, structured constraint violations checking via Zod, and valid type preservation boundaries. ### Why this matters Secures high-frequency analytics dashboards by transforming type declarations into active compiler gates. This minimizes data integrity regressions during future codebase adjustments and locks down component APIs safely. ## Pillar - [ ] 🎨 Pillar 1 — New Theme Design - [ ] 📐 Pillar 2 — Geometric SVG Improvement - [ ] 🕐 Pillar 3 — Timezone Logic Optimization - [x] 🛠️ Other (Bug fix, refactoring, docs) ## Checklist before requesting a review: - [x] I have read the `CONTRIBUTING.md` file. - [x] I have tested these changes locally (`npm run test`). - [x] I have run `npm run format` and `npm run lint` locally and resolved all errors (CI will fail otherwise). - [x] My commits follow the Conventional Commits format (e.g., `feat(themes): ...`, `fix(calculate): ...`). - [ ] I have updated `README.md` if I added a new theme or URL parameter. - [x] I have starred the repo. - [x] I have made sure that i have only one commit to merge in this PR. - [ ] The SVG output matches the CommitPulse "premium quality" aesthetic standard (no raw elements, smooth animations, correct fonts). - [x] (Recommended) I joined the CommitPulse Discord server for faster collaboration, mentorship, and PR support.
2 parents e197395 + 2f81e87 commit 317e3e4

1 file changed

Lines changed: 150 additions & 0 deletions

File tree

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
import { describe, it, expect, expectTypeOf } from 'vitest';
2+
import { z } from 'zod';
3+
import type { ActivityData } from '@/types/dashboard';
4+
import type { DashboardPeriod } from '@/utils/dashboardPeriod';
5+
6+
export interface HistoricalTrendViewProps {
7+
username: string;
8+
activity: ActivityData[];
9+
period: DashboardPeriod;
10+
}
11+
12+
describe('HistoricalTrendView type & schema compiler checks (Variation 10)', () => {
13+
// Case 1: Verifies baseline types for primitive and union properties match accurately
14+
it('Case 1: Validate baseline property fields (date, count, intensity) to verify type matching parameters align perfectly', () => {
15+
expectTypeOf<ActivityData['date']>().toEqualTypeOf<string>();
16+
expectTypeOf<ActivityData['count']>().toEqualTypeOf<number>();
17+
expectTypeOf<ActivityData['intensity']>().toEqualTypeOf<0 | 1 | 2 | 3 | 4>();
18+
});
19+
20+
// Case 2: Asserts static type checking blocks misaligned interface layouts from compiling
21+
it('Case 2: Ensure invalid structural definitions are caught and blocked during static assignment checks', () => {
22+
type InvalidActivity = {
23+
date: number;
24+
count: string;
25+
intensity: 5;
26+
};
27+
28+
type InvalidPeriod = {
29+
kind: 'unknown';
30+
label: number;
31+
};
32+
33+
expectTypeOf<InvalidActivity>().not.toMatchTypeOf<ActivityData>();
34+
expectTypeOf<InvalidPeriod>().not.toMatchTypeOf<DashboardPeriod>();
35+
});
36+
37+
// Case 3: Confirms the component wrapper handles minimal and complete optional envelopes cleanly
38+
it('Case 3: Verify the complex trend props envelope seamlessly tolerates optional parameters without dropping compilation support', () => {
39+
type MinimalActivity = {
40+
date: string;
41+
count: number;
42+
intensity: 0 | 1 | 2 | 3 | 4;
43+
};
44+
45+
type MinimalPeriod = {
46+
kind: 'rolling';
47+
label: string;
48+
from: string;
49+
to: string;
50+
};
51+
52+
type MinimalProps = {
53+
username: string;
54+
activity: MinimalActivity[];
55+
period: MinimalPeriod;
56+
};
57+
58+
expectTypeOf<MinimalProps>().toMatchTypeOf<HistoricalTrendViewProps>();
59+
60+
type FullActivity = {
61+
date: string;
62+
count: number;
63+
intensity: 0 | 1 | 2 | 3 | 4;
64+
locAdditions: number;
65+
locDeletions: number;
66+
};
67+
68+
type FullPeriod = {
69+
kind: 'month';
70+
label: string;
71+
from: string;
72+
to: string;
73+
month: string;
74+
year: string;
75+
};
76+
77+
type FullProps = {
78+
username: string;
79+
activity: FullActivity[];
80+
period: FullPeriod;
81+
};
82+
83+
expectTypeOf<FullProps>().toMatchTypeOf<HistoricalTrendViewProps>();
84+
});
85+
86+
// Case 4: Checks that out-of-bounds parameters generate flat field errors and catch unexpected fields
87+
it('Case 4: Assert that an associated strict validation schema rejects out-of-bounds metrics fields with explicit flat error reports', () => {
88+
const activityDataSchema = z
89+
.object({
90+
date: z.string().min(1),
91+
count: z.number().int().nonnegative(),
92+
intensity: z.union([z.literal(0), z.literal(1), z.literal(2), z.literal(3), z.literal(4)]),
93+
locAdditions: z.number().int().nonnegative().optional(),
94+
locDeletions: z.number().int().nonnegative().optional(),
95+
})
96+
.strict();
97+
98+
const outOfBoundsRecord = {
99+
date: '',
100+
count: -15,
101+
intensity: 10,
102+
locAdditions: -5,
103+
extraField: 'unexpected',
104+
};
105+
106+
const result = activityDataSchema.safeParse(outOfBoundsRecord);
107+
expect(result.success).toBe(false);
108+
109+
if (!result.success) {
110+
const flatErrors = result.error.flatten();
111+
112+
expect(flatErrors.fieldErrors).toHaveProperty('date');
113+
expect(flatErrors.fieldErrors).toHaveProperty('count');
114+
expect(flatErrors.fieldErrors).toHaveProperty('intensity');
115+
expect(flatErrors.fieldErrors).toHaveProperty('locAdditions');
116+
117+
const hasUnrecognizedExtra = result.error.issues.some(
118+
(issue) => issue.code === 'unrecognized_keys' && issue.keys.includes('extraField')
119+
);
120+
expect(hasUnrecognizedExtra).toBe(true);
121+
}
122+
});
123+
124+
// Case 5: Validates clean payloads clear parsing pipelines and maintain type assignability properties
125+
it('Case 5: Prove that standard compliant data records cleanly clear validation gates and preserve underlying type integrity definitions', () => {
126+
const activityDataSchema = z
127+
.object({
128+
date: z.string().min(1),
129+
count: z.number().int().nonnegative(),
130+
intensity: z.union([z.literal(0), z.literal(1), z.literal(2), z.literal(3), z.literal(4)]),
131+
locAdditions: z.number().int().nonnegative().optional(),
132+
locDeletions: z.number().int().nonnegative().optional(),
133+
})
134+
.strict();
135+
136+
const validRecord = {
137+
date: '2026-06-03',
138+
count: 12,
139+
intensity: 3 as const,
140+
locAdditions: 150,
141+
locDeletions: 20,
142+
};
143+
144+
const parsed = activityDataSchema.parse(validRecord);
145+
expect(parsed).toEqual(validRecord);
146+
147+
type ParsedType = z.infer<typeof activityDataSchema>;
148+
expectTypeOf<ParsedType>().toMatchTypeOf<ActivityData>();
149+
});
150+
});

0 commit comments

Comments
 (0)