1- /* @vitest -environment jsdom */
21/* eslint-disable @typescript-eslint/no-explicit-any */
32import { render , screen } from '@testing-library/react' ;
43import { describe , expect , it , vi } from 'vitest' ;
@@ -23,9 +22,9 @@ vi.mock('framer-motion', () => ({
2322 } ,
2423} ) ) ;
2524
26- describe ( 'ComparisonStatsCard responsive breakpoints ' , ( ) => {
27- it ( 'renders the expected card structure and positive growth indicators ' , ( ) => {
28- const { container } = render (
25+ describe ( 'ComparisonStatsCard' , ( ) => {
26+ it ( 'renders correctly with title, labels and values ' , ( ) => {
27+ render (
2928 < ComparisonStatsCard
3029 title = "Developer Score"
3130 valueA = { 85 }
@@ -41,7 +40,204 @@ describe('ComparisonStatsCard responsive breakpoints', () => {
4140 expect ( screen . getByText ( 'User Two' ) ) . toBeDefined ( ) ;
4241 expect ( screen . getByText ( '85' ) ) . toBeDefined ( ) ;
4342 expect ( screen . getByText ( '72' ) ) . toBeDefined ( ) ;
43+ } ) ;
44+
45+ it ( 'renders a Winner badge on User One when valueA is greater' , ( ) => {
46+ render (
47+ < ComparisonStatsCard
48+ title = "Developer Score"
49+ valueA = { 100 }
50+ valueB = { 50 }
51+ labelA = "User One"
52+ labelB = "User Two"
53+ icon = "Award"
54+ />
55+ ) ;
56+
57+ const winnerBadges = screen . getAllByText ( 'Winner' ) ;
58+ expect ( winnerBadges . length ) . toBe ( 1 ) ;
59+ expect ( screen . getByText ( '100' ) . parentElement ?. innerHTML ) . toContain ( 'Winner' ) ;
60+ } ) ;
61+
62+ it ( 'renders a Winner badge on User Two when valueB is greater' , ( ) => {
63+ render (
64+ < ComparisonStatsCard
65+ title = "Developer Score"
66+ valueA = { 30 }
67+ valueB = { 90 }
68+ labelA = "User One"
69+ labelB = "User Two"
70+ icon = "Award"
71+ />
72+ ) ;
73+
74+ const winnerBadges = screen . getAllByText ( 'Winner' ) ;
75+ expect ( winnerBadges . length ) . toBe ( 1 ) ;
76+ expect ( screen . getByText ( '90' ) . parentElement ?. innerHTML ) . toContain ( 'Winner' ) ;
77+ } ) ;
78+
79+ it ( 'does not render any Winner badge if values are equal' , ( ) => {
80+ render (
81+ < ComparisonStatsCard
82+ title = "Developer Score"
83+ valueA = { 50 }
84+ valueB = { 50 }
85+ labelA = "User One"
86+ labelB = "User Two"
87+ icon = "Award"
88+ />
89+ ) ;
90+
91+ expect ( screen . queryByText ( 'Winner' ) ) . toBeNull ( ) ;
92+ } ) ;
93+
94+ it ( 'renders neutral fallback progress bar when both values are zero' , ( ) => {
95+ const { container } = render (
96+ < ComparisonStatsCard
97+ title = "Developer Score"
98+ valueA = { 0 }
99+ valueB = { 0 }
100+ labelA = "User One"
101+ labelB = "User Two"
102+ icon = "Award"
103+ />
104+ ) ;
105+
106+ const fallbackBar = container . querySelector ( '.bg-gray-700\\/50' ) ;
107+ expect ( fallbackBar ) . toBeDefined ( ) ;
108+ } ) ;
109+
110+ it ( 'renders both progress bar segments when total is greater than zero' , ( ) => {
111+ const { container } = render (
112+ < ComparisonStatsCard
113+ title = "Developer Score"
114+ valueA = { 75 }
115+ valueB = { 25 }
116+ labelA = "User One"
117+ labelB = "User Two"
118+ icon = "Award"
119+ />
120+ ) ;
121+
122+ const userOneSegment = container . querySelector ( '.bg-cyan-400' ) ;
123+ const userTwoSegment = container . querySelector ( '.bg-purple-400' ) ;
124+
125+ expect ( userOneSegment ) . toBeDefined ( ) ;
126+ expect ( userTwoSegment ) . toBeDefined ( ) ;
127+ } ) ;
128+
129+ it ( 'renders a balanced 50/50 split progress bar without any emerald color highlight when values are equal' , ( ) => {
130+ const { container } = render (
131+ < ComparisonStatsCard
132+ title = "Developer Score"
133+ valueA = { 50 }
134+ valueB = { 50 }
135+ labelA = "User One"
136+ labelB = "User Two"
137+ icon = "Award"
138+ />
139+ ) ;
140+
141+ const emeraldElement =
142+ container . querySelector ( '[className*="emerald"]' ) ||
143+ container . querySelector ( '.text-emerald-400' ) ;
144+
145+ expect ( emeraldElement ) . toBeNull ( ) ;
146+ expect ( screen . queryByText ( 'Winner' ) ) . toBeNull ( ) ;
147+ } ) ;
148+ } ) ;
149+
150+ describe ( 'ComparisonStatsCard responsive rendering and growth trends (Variation 3)' , ( ) => {
151+ it ( 'renders positive growth trend with winner badge for higher value' , ( ) => {
152+ render (
153+ < ComparisonStatsCard
154+ title = "Streak"
155+ valueA = { 120 }
156+ valueB = { 40 }
157+ labelA = "Alice"
158+ labelB = "Bob"
159+ icon = "Flame"
160+ />
161+ ) ;
44162 expect ( screen . getByText ( 'Winner' ) ) . toBeDefined ( ) ;
163+ expect ( screen . getByText ( '120' ) ) . toBeDefined ( ) ;
164+ expect ( screen . getByText ( '40' ) ) . toBeDefined ( ) ;
165+ } ) ;
166+
167+ it ( 'renders negative growth indicator — no winner badge for lower value' , ( ) => {
168+ render (
169+ < ComparisonStatsCard
170+ title = "Streak"
171+ valueA = { 20 }
172+ valueB = { 80 }
173+ labelA = "Alice"
174+ labelB = "Bob"
175+ icon = "Flame"
176+ />
177+ ) ;
178+ const winners = screen . getAllByText ( 'Winner' ) ;
179+ expect ( winners . length ) . toBe ( 1 ) ;
180+ expect ( screen . getByText ( '20' ) . className ) . not . toMatch ( / e m e r a l d / ) ;
181+ } ) ;
182+
183+ it ( 'renders title and icon correctly' , ( ) => {
184+ render (
185+ < ComparisonStatsCard
186+ title = "Pull Requests"
187+ valueA = { 10 }
188+ valueB = { 5 }
189+ labelA = "Dev A"
190+ labelB = "Dev B"
191+ icon = "GitBranch"
192+ />
193+ ) ;
194+ expect ( screen . getByText ( / P u l l R e q u e s t s / i) ) . toBeDefined ( ) ;
195+ expect ( screen . getByText ( 'Dev A' ) ) . toBeDefined ( ) ;
196+ expect ( screen . getByText ( 'Dev B' ) ) . toBeDefined ( ) ;
197+ } ) ;
198+
199+ it ( 'renders zero values without crashing' , ( ) => {
200+ render (
201+ < ComparisonStatsCard
202+ title = "Commits"
203+ valueA = { 0 }
204+ valueB = { 0 }
205+ labelA = "Alice"
206+ labelB = "Bob"
207+ icon = "GitCommit"
208+ />
209+ ) ;
210+ expect ( screen . queryByText ( 'Winner' ) ) . toBeNull ( ) ;
211+ } ) ;
212+
213+ it ( 'renders large values correctly' , ( ) => {
214+ render (
215+ < ComparisonStatsCard
216+ title = "Total Contributions"
217+ valueA = { 9999 }
218+ valueB = { 1 }
219+ labelA = "Alice"
220+ labelB = "Bob"
221+ icon = "TrendingUp"
222+ />
223+ ) ;
224+ expect ( screen . getByText ( '9999' ) ) . toBeDefined ( ) ;
225+ expect ( screen . getByText ( 'Winner' ) ) . toBeDefined ( ) ;
226+ } ) ;
227+ } ) ;
228+
229+ describe ( 'ComparisonStatsCard responsive breakpoints' , ( ) => {
230+ it ( 'renders expected card structure with correct HTML nodes' , ( ) => {
231+ const { container } = render (
232+ < ComparisonStatsCard
233+ title = "Developer Score"
234+ valueA = { 85 }
235+ valueB = { 72 }
236+ labelA = "User One"
237+ labelB = "User Two"
238+ icon = "Award"
239+ />
240+ ) ;
45241
46242 const card = container . firstElementChild ;
47243 const header = container . querySelector ( '.flex.justify-between.items-center.mb-6' ) ;
@@ -56,9 +252,13 @@ describe('ComparisonStatsCard responsive breakpoints', () => {
56252 expect ( comparisonGrid ?. tagName ) . toBe ( 'DIV' ) ;
57253 expect ( progressBar ?. tagName ) . toBe ( 'DIV' ) ;
58254 expect ( divider ?. tagName ) . toBe ( 'DIV' ) ;
255+
256+ expect ( screen . getByText ( 'Winner' ) ) . toBeDefined ( ) ;
257+ expect ( screen . getByText ( '85' ) ) . toBeDefined ( ) ;
258+ expect ( screen . getByText ( '72' ) ) . toBeDefined ( ) ;
59259 } ) ;
60260
61- it ( 'keeps the divider responsive and preserves the negative growth state ' , ( ) => {
261+ it ( 'renders responsive divider and preserves winner badge for higher value ' , ( ) => {
62262 const { container } = render (
63263 < ComparisonStatsCard
64264 title = "Streak"
@@ -71,23 +271,19 @@ describe('ComparisonStatsCard responsive breakpoints', () => {
71271 ) ;
72272
73273 const divider = container . querySelector ( '.hidden.md\\:block' ) ;
74- const winnerBadges = screen . getAllByText ( 'Winner ') ;
274+ expect ( divider ?. tagName ) . toBe ( 'DIV ') ;
75275
76- expect ( divider ) . toBeDefined ( ) ;
276+ const winnerBadges = screen . getAllByText ( 'Winner' ) ;
77277 expect ( winnerBadges . length ) . toBe ( 1 ) ;
78- expect ( screen . getByText ( 'Alice' ) ) . toBeDefined ( ) ;
79- expect ( screen . getByText ( 'Bob' ) ) . toBeDefined ( ) ;
80- expect ( screen . getByText ( '20' ) ) . toBeDefined ( ) ;
81- expect ( screen . getByText ( '80' ) ) . toBeDefined ( ) ;
82- expect ( screen . getByTitle ( 'Alice' ) ) . toBeDefined ( ) ;
83- expect ( screen . getByTitle ( 'Bob' ) ) . toBeDefined ( ) ;
278+ expect ( screen . getByText ( '80' ) . parentElement ?. textContent ) . toContain ( 'Winner' ) ;
84279
85- const winnerText = winnerBadges [ 0 ] . textContent ;
86- expect ( winnerText ) . toBe ( 'Winner' ) ;
87280 expect ( screen . getByText ( '20' ) . className ) . not . toMatch ( / e m e r a l d / ) ;
281+
282+ expect ( screen . getByTitle ( 'Alice' ) ) . toBeDefined ( ) ;
283+ expect ( screen . getByTitle ( 'Bob' ) ) . toBeDefined ( ) ;
88284 } ) ;
89285
90- it ( 'renders a neutral fallback progress bar when both values are zero' , ( ) => {
286+ it ( 'renders neutral fallback progress bar and no winner badge when both values are zero' , ( ) => {
91287 const { container } = render (
92288 < ComparisonStatsCard
93289 title = "Commits"
@@ -100,10 +296,11 @@ describe('ComparisonStatsCard responsive breakpoints', () => {
100296 ) ;
101297
102298 const progressBar = container . querySelector ( '.w-full.h-2.bg-gray-100' ) ;
103- const fallbackBar = container . querySelector ( '.w-full.h-full.bg-zinc-300' ) ;
104-
105299 expect ( progressBar ?. tagName ) . toBe ( 'DIV' ) ;
106- expect ( fallbackBar ?. tagName ) . toBe ( 'DIV' ) ;
300+
301+ const fallbackBar = container . querySelector ( '.bg-gray-700\\/50' ) ;
302+ expect ( fallbackBar ) . toBeDefined ( ) ;
303+
107304 expect ( screen . queryByText ( 'Winner' ) ) . toBeNull ( ) ;
108305 } ) ;
109306} ) ;
0 commit comments