Skip to content

Commit 473885f

Browse files
authored
Merge pull request #354 from ets-cfuhrman-pfe/Minh-Khoi-Le/feature-progression-bar
Add progression bar and fix % badge
2 parents 8fd2e5e + b6eec12 commit 473885f

10 files changed

Lines changed: 750 additions & 29 deletions

File tree

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
import React from 'react';
2+
import { render, cleanup } from '@testing-library/react';
3+
import '@testing-library/jest-dom';
4+
import ProgressOverlay from 'src/components/QuestionsDisplay/ProgressOverlay/ProgressOverlay';
5+
6+
// Test constants
7+
const TEST_PERCENTAGES = {
8+
ZERO: 0,
9+
QUARTER: 25,
10+
THIRD: 33.33,
11+
HALF: 50,
12+
THREE_QUARTERS: 75,
13+
FULL: 100,
14+
SIXTY: 60
15+
} as const;
16+
17+
const COLOR_CLASSES = {
18+
CORRECT: 'progress-overlay-correct',
19+
INCORRECT: 'progress-overlay-incorrect'
20+
} as const;
21+
22+
describe('ProgressOverlay Component', () => {
23+
afterEach(() => {
24+
cleanup();
25+
});
26+
27+
test('should render when show is true', () => {
28+
const { container } = render(<ProgressOverlay percentage={TEST_PERCENTAGES.HALF} show={true} />);
29+
30+
const overlay = container.firstChild as HTMLElement;
31+
expect(overlay).toBeInTheDocument();
32+
expect(overlay).toHaveClass('MuiBox-root');
33+
});
34+
35+
test('should render with different percentages', () => {
36+
const percentages = [TEST_PERCENTAGES.THREE_QUARTERS, TEST_PERCENTAGES.QUARTER, TEST_PERCENTAGES.ZERO, TEST_PERCENTAGES.FULL];
37+
38+
percentages.forEach(percentage => {
39+
const { container } = render(<ProgressOverlay percentage={percentage} show={true} />);
40+
const overlay = container.firstChild as HTMLElement;
41+
expect(overlay).toBeInTheDocument();
42+
cleanup();
43+
});
44+
});
45+
46+
test('should render with different colors', () => {
47+
const { container: container1 } = render(
48+
<ProgressOverlay percentage={TEST_PERCENTAGES.HALF} show={true} colorClass={COLOR_CLASSES.CORRECT} />
49+
);
50+
const overlay1 = container1.firstChild as HTMLElement;
51+
expect(overlay1).toBeInTheDocument();
52+
expect(overlay1).toHaveClass(COLOR_CLASSES.CORRECT);
53+
cleanup();
54+
55+
const { container: container2 } = render(
56+
<ProgressOverlay percentage={TEST_PERCENTAGES.HALF} show={true} colorClass={COLOR_CLASSES.INCORRECT} />
57+
);
58+
const overlay2 = container2.firstChild as HTMLElement;
59+
expect(overlay2).toBeInTheDocument();
60+
expect(overlay2).toHaveClass(COLOR_CLASSES.INCORRECT);
61+
});
62+
63+
test('should not render when show is false', () => {
64+
const { container } = render(<ProgressOverlay percentage={TEST_PERCENTAGES.HALF} show={false} />);
65+
66+
expect(container.firstChild).toBeNull();
67+
});
68+
69+
test('should render as MUI Box component', () => {
70+
const { container } = render(<ProgressOverlay percentage={TEST_PERCENTAGES.SIXTY} show={true} />);
71+
72+
const overlay = container.firstChild as HTMLElement;
73+
expect(overlay).toBeInTheDocument();
74+
expect(overlay).toHaveClass('MuiBox-root');
75+
});
76+
77+
test('should handle prop changes correctly', () => {
78+
const TestWrapper = ({
79+
percentage = TEST_PERCENTAGES.HALF,
80+
show = true,
81+
colorClass
82+
}: {
83+
percentage?: number;
84+
show?: boolean;
85+
colorClass?: string;
86+
}) => (
87+
<div data-testid="wrapper">
88+
<ProgressOverlay percentage={percentage} show={show} colorClass={colorClass} />
89+
</div>
90+
);
91+
92+
const { rerender, getByTestId } = render(<TestWrapper percentage={TEST_PERCENTAGES.HALF} />);
93+
let wrapper = getByTestId('wrapper');
94+
let overlay = wrapper.firstChild as HTMLElement;
95+
expect(overlay).toHaveClass('MuiBox-root');
96+
97+
// Test show=false
98+
rerender(<TestWrapper percentage={TEST_PERCENTAGES.HALF} show={false} />);
99+
wrapper = getByTestId('wrapper');
100+
expect(wrapper.firstChild).toBeNull();
101+
102+
// Test show=true again
103+
rerender(<TestWrapper percentage={TEST_PERCENTAGES.THREE_QUARTERS} show={true} />);
104+
wrapper = getByTestId('wrapper');
105+
overlay = wrapper.firstChild as HTMLElement;
106+
expect(overlay).toHaveClass('MuiBox-root');
107+
108+
// Test with colorClass
109+
rerender(<TestWrapper percentage={TEST_PERCENTAGES.THREE_QUARTERS} show={true} colorClass={COLOR_CLASSES.CORRECT} />);
110+
wrapper = getByTestId('wrapper');
111+
overlay = wrapper.firstChild as HTMLElement;
112+
expect(overlay).toHaveClass('MuiBox-root');
113+
expect(overlay).toHaveClass(COLOR_CLASSES.CORRECT);
114+
});
115+
116+
test('should work with edge case values', () => {
117+
const edgeCases = [TEST_PERCENTAGES.ZERO, TEST_PERCENTAGES.FULL, TEST_PERCENTAGES.THIRD];
118+
119+
edgeCases.forEach(percentage => {
120+
const { container } = render(<ProgressOverlay percentage={percentage} show={true} />);
121+
expect(container.firstChild).toBeInTheDocument();
122+
cleanup();
123+
});
124+
});
125+
});

client/src/__tests__/components/QuestionsDisplay/QuestionDisplayV2.test.tsx

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,4 +292,122 @@ describe('QuestionDisplayV2 Component', () => {
292292
expect(screen.queryByText('Répondre')).not.toBeInTheDocument();
293293
});
294294
});
295+
296+
describe('Answer Statistics and Progression Features', () => {
297+
test('should display percentage and fraction when showStatistics is enabled', () => {
298+
render(
299+
<QuestionDisplayV2
300+
question={sampleMultipleChoiceQuestion}
301+
{...sampleProps}
302+
students={mockStudents}
303+
showStatistics={true}
304+
/>
305+
);
306+
307+
// Should display percentage badges (mocked components will show some indication)
308+
const questionContainer = screen.getByText(/Sample Multiple Choice Question/);
309+
expect(questionContainer).toBeInTheDocument();
310+
});
311+
312+
test('should not display statistics when showStatistics is false', () => {
313+
render(
314+
<QuestionDisplayV2
315+
question={sampleMultipleChoiceQuestion}
316+
{...sampleProps}
317+
students={mockStudents}
318+
showStatistics={false}
319+
/>
320+
);
321+
322+
// Should render without statistics
323+
const questionContainer = screen.getByText(/Sample Multiple Choice Question/);
324+
expect(questionContainer).toBeInTheDocument();
325+
});
326+
327+
test('should pass students array to question components for statistics calculation', () => {
328+
render(
329+
<QuestionDisplayV2
330+
question={sampleTrueFalseQuestion}
331+
{...sampleProps}
332+
students={mockStudents}
333+
showStatistics={true}
334+
/>
335+
);
336+
337+
// Should render the True/False question with students data
338+
expect(screen.getByText(/Sample True/)).toBeInTheDocument();
339+
});
340+
341+
test('should handle empty students array', () => {
342+
render(
343+
<QuestionDisplayV2
344+
question={sampleMultipleChoiceQuestion}
345+
{...sampleProps}
346+
students={[]}
347+
showStatistics={true}
348+
/>
349+
);
350+
351+
expect(screen.getByText(/Sample Multiple Choice Question/)).toBeInTheDocument();
352+
});
353+
354+
test('should work with different question types and statistics', () => {
355+
const testCases = [
356+
{ question: sampleTrueFalseQuestion, name: 'True/False' },
357+
{ question: sampleMultipleChoiceQuestion, name: 'Multiple Choice' },
358+
{ question: sampleNumericalQuestion, name: 'Numerical' },
359+
{ question: sampleShortAnswerQuestion, name: 'Short Answer' }
360+
];
361+
362+
testCases.forEach(({ question, name }) => {
363+
const { unmount } = render(
364+
<QuestionDisplayV2
365+
question={question}
366+
{...sampleProps}
367+
students={mockStudents}
368+
showStatistics={true}
369+
/>
370+
);
371+
372+
// Should render without errors
373+
expect(screen.getByText(new RegExp(name === 'True/False' ? 'Sample True' : 'Sample', 'i'))).toBeInTheDocument();
374+
375+
unmount();
376+
});
377+
});
378+
379+
test('should handle showAnswer and showStatistics combination', () => {
380+
render(
381+
<QuestionDisplayV2
382+
question={sampleTrueFalseQuestion}
383+
{...sampleProps}
384+
students={mockStudents}
385+
showStatistics={true}
386+
showAnswer={true}
387+
/>
388+
);
389+
390+
// Should render with both validation and statistics
391+
expect(screen.getByText(/Sample True/)).toBeInTheDocument();
392+
});
393+
394+
test('should pass correct props to child question components', () => {
395+
const props = {
396+
...sampleProps,
397+
students: mockStudents,
398+
showStatistics: true,
399+
showAnswer: false
400+
};
401+
402+
render(
403+
<QuestionDisplayV2
404+
question={sampleMultipleChoiceQuestion}
405+
{...props}
406+
/>
407+
);
408+
409+
// The component should render and pass props correctly
410+
expect(screen.getByText(/Sample Multiple Choice Question/)).toBeInTheDocument();
411+
});
412+
});
295413
});

0 commit comments

Comments
 (0)