Skip to content

Commit aed644b

Browse files
authored
test(template): verify light and dark mode contrast visual cohesion JhaSourav07#2865 (JhaSourav07#3016)
test(template): verify light and dark mode contrast visual cohesion
2 parents bb7c726 + 8381f74 commit aed644b

1 file changed

Lines changed: 128 additions & 0 deletions

File tree

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
import React from 'react';
2+
import { render, screen, act } from '@testing-library/react';
3+
import { describe, it, expect } from 'vitest';
4+
import Template from './template';
5+
6+
// Contrast utilities (sRGB luminance & ratio)
7+
function hexToRgb(hex: string) {
8+
const sanitized = hex.replace('#', '');
9+
const bigint = parseInt(
10+
sanitized.length === 3
11+
? sanitized
12+
.split('')
13+
.map((c) => c + c)
14+
.join('')
15+
: sanitized,
16+
16
17+
);
18+
return {
19+
r: (bigint >> 16) & 255,
20+
g: (bigint >> 8) & 255,
21+
b: bigint & 255,
22+
};
23+
}
24+
25+
function linearizeChannel(channel: number) {
26+
const srgb = channel / 255;
27+
return srgb <= 0.03928 ? srgb / 12.92 : Math.pow((srgb + 0.055) / 1.055, 2.4);
28+
}
29+
30+
function luminance(hex: string) {
31+
const { r, g, b } = hexToRgb(hex);
32+
return 0.2126 * linearizeChannel(r) + 0.7152 * linearizeChannel(g) + 0.0722 * linearizeChannel(b);
33+
}
34+
35+
function contrastRatio(hex1: string, hex2: string) {
36+
const L1 = luminance(hex1);
37+
const L2 = luminance(hex2);
38+
const lighter = Math.max(L1, L2);
39+
const darker = Math.min(L1, L2);
40+
return (lighter + 0.05) / (darker + 0.05);
41+
}
42+
43+
describe('App Template theme contrast checks (Variation 3)', () => {
44+
it('Case 1: Emulate a light preset and assert structural identifiers appear', () => {
45+
// Simulate global light preset
46+
document.documentElement.setAttribute('data-theme', 'light');
47+
48+
render(
49+
<Template>
50+
<div data-testid="child-light">Light mode content</div>
51+
</Template>
52+
);
53+
54+
expect(document.documentElement.getAttribute('data-theme')).toBe('light');
55+
expect(screen.getByTestId('child-light')).toBeTruthy();
56+
});
57+
58+
it('Case 2: Emulate a dark preset and verify class adaptations align', () => {
59+
// Simulate dark preset via class
60+
document.documentElement.classList.add('theme-dark');
61+
render(
62+
<Template>
63+
<div data-testid="child-dark">Dark mode content</div>
64+
</Template>
65+
);
66+
67+
expect(document.documentElement.classList.contains('theme-dark')).toBe(true);
68+
expect(screen.getByTestId('child-dark')).toBeTruthy();
69+
});
70+
71+
it('Case 3: Style token strings contain premium color tokens meeting accessibility contrast', () => {
72+
// Example tokens (background and foreground)
73+
const bg = '#ffffff';
74+
const fg = '#0f172a'; // Tailwind slate-900 similar
75+
76+
const ratio = contrastRatio(bg, fg);
77+
// WCAG AA for normal text requires >= 4.5
78+
expect(ratio).toBeGreaterThanOrEqual(4.5);
79+
});
80+
81+
it('Case 4: Background overlays do not clip or hide text colors', () => {
82+
const { container } = render(
83+
<Template>
84+
<div style={{ position: 'relative' }}>
85+
<div
86+
data-testid="overlay"
87+
style={{ position: 'absolute', inset: 0, zIndex: 0, opacity: 0.6 }}
88+
/>
89+
<div data-testid="text" style={{ position: 'relative', zIndex: 1 }}>
90+
Visible text
91+
</div>
92+
</div>
93+
</Template>
94+
);
95+
96+
const overlay = screen.getByTestId('overlay');
97+
const text = screen.getByTestId('text');
98+
99+
// Validate DOM stacking via zIndex values (overlay beneath text)
100+
const overlayZ = (overlay as HTMLElement).style.zIndex || '0';
101+
const textZ = (text as HTMLElement).style.zIndex || '0';
102+
expect(Number(textZ)).toBeGreaterThanOrEqual(Number(overlayZ));
103+
104+
// Ensure text node remains in document and not occluded by being removed
105+
expect(container.contains(text)).toBe(true);
106+
});
107+
108+
it('Case 5: Custom style variables and class bindings remain active across transitions', () => {
109+
// set custom variable
110+
document.documentElement.style.setProperty('--brand', '#123456');
111+
// Render template once
112+
render(
113+
<Template>
114+
<div data-testid="brand">Brand</div>
115+
</Template>
116+
);
117+
118+
// Toggle theme attribute to simulate a global transition
119+
act(() => {
120+
document.documentElement.setAttribute('data-theme', 'dark');
121+
});
122+
123+
const preserved = document.documentElement.style.getPropertyValue('--brand');
124+
expect(preserved).toBe('#123456');
125+
expect(document.documentElement.getAttribute('data-theme')).toBe('dark');
126+
expect(screen.getByTestId('brand')).toBeTruthy();
127+
});
128+
});

0 commit comments

Comments
 (0)