diff --git a/mobile/bulingo/__tests__/quizReview.test.tsx b/mobile/bulingo/__tests__/quizReview.test.tsx new file mode 100644 index 00000000..2c566c27 --- /dev/null +++ b/mobile/bulingo/__tests__/quizReview.test.tsx @@ -0,0 +1,166 @@ +import React from 'react'; +import { render, waitFor, fireEvent, act } from '@testing-library/react-native'; +import QuizReviewQuestion from '@/app/(tabs)/quizzes/quizReview'; +import TokenManager from '@/app/TokenManager'; + +jest.useFakeTimers(); + +jest.mock('react-native/Libraries/Animated/NativeAnimatedHelper'); + +jest.mock('expo-router', () => ({ + useLocalSearchParams: jest.fn(() => ({ quizId: '1' })), + router: { back: jest.fn(), push: jest.fn() }, +})); + +jest.mock('@/app/TokenManager', () => ({ + __esModule: true, + default: { + authenticatedFetch: jest.fn(), + }, + })); + +describe('QuizReviewQuestion Component', () => { + const mockQuizData = { + quiz_progress_id: 1, + quiz_title: 'Sample Quiz', + question_count: 3, + questions: [ + { + question_number: 1, + question: 'Mercimek', + choices: ['Lentils', 'Tomato', 'Potato', 'Peas'], + correct_choice: 1, + previous_answer: 3, + }, + { + question_number: 2, + question: 'Elma', + choices: ['Apple', 'Orange', 'Banana', 'Grape'], + correct_choice: 1, + previous_answer: 2, + }, + { + question_number: 3, + question: 'Kedi', + choices: ['Cat', 'Dog', 'Bird', 'Fish'], + correct_choice: 1, + previous_answer: 4, + }, + ], + }; + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('renders quiz questions after data is loaded', async () => { + (TokenManager.authenticatedFetch as jest.Mock).mockResolvedValueOnce({ + ok: true, + json: jest.fn().mockResolvedValue(mockQuizData), + }); + + const { getByText } = render(); + + await waitFor(() => { + expect(getByText('Mercimek')).toBeTruthy(); + expect(getByText('Lentils')).toBeTruthy(); + expect(getByText('Tomato')).toBeTruthy(); + expect(getByText('Potato')).toBeTruthy(); + expect(getByText('Peas')).toBeTruthy(); + }); + }); + + it('handles API error gracefully', async () => { + (TokenManager.authenticatedFetch as jest.Mock).mockResolvedValueOnce({ + ok: false, + json: jest.fn().mockResolvedValue({ error: 'Failed to fetch quiz review.' }), + }); + + const { getByText } = render(); + + await waitFor(() => { + expect(getByText('Error: Failed to fetch quiz review.')).toBeTruthy(); + }); + }); + + it('navigates to the next question', async () => { + (TokenManager.authenticatedFetch as jest.Mock).mockResolvedValueOnce({ + ok: true, + json: jest.fn().mockResolvedValue(mockQuizData), + }); + + const { getByText } = render(); + + await act(async () => { + await waitFor(() => { + expect(getByText('Mercimek')).toBeTruthy(); + }); + + fireEvent.press(getByText('Next')); + jest.advanceTimersByTime(1000); + }); + + await waitFor(() => { + expect(getByText('Elma')).toBeTruthy(); + }); + }); + + it('handles the "Previous" button correctly', async () => { + (TokenManager.authenticatedFetch as jest.Mock).mockResolvedValueOnce({ + ok: true, + json: jest.fn().mockResolvedValue(mockQuizData), + }); + + const { getByText } = render(); + + await act(async () => { + await waitFor(() => { + expect(getByText('Mercimek')).toBeTruthy(); + }); + + fireEvent.press(getByText('Next')); + jest.advanceTimersByTime(1000); + }); + + await waitFor(() => { + expect(getByText('Elma')).toBeTruthy(); + }); + + await act(async () => { + fireEvent.press(getByText('Previous')); + jest.advanceTimersByTime(1000); + }); + + await waitFor(() => { + expect(getByText('Mercimek')).toBeTruthy(); + }); + }); + + it('styles correct and wrong answer buttons correctly', async () => { + (TokenManager.authenticatedFetch as jest.Mock).mockResolvedValueOnce({ + ok: true, + json: jest.fn().mockResolvedValue(mockQuizData), + }); + + const { getAllByTestId } = render(); + + await waitFor(() => { + const buttons = getAllByTestId('button'); + const correctAnswerButton = buttons.find((button) => { + const textElement = button.findByType('Text'); + return textElement.props.children === 'Lentils'; + }); + expect(correctAnswerButton.props.style).toEqual( + expect.objectContaining({ backgroundColor: 'lightgreen' }) + ); + + const wrongAnswerButton = buttons.find((button) => { + const textElement = button.findByType('Text'); + return textElement.props.children === 'Potato'; + }); + expect(wrongAnswerButton.props.style).toEqual( + expect.objectContaining({ backgroundColor: 'lightcoral' }) + ); + }); + }); +}); diff --git a/mobile/bulingo/app/(tabs)/quizzes/quizReview.tsx b/mobile/bulingo/app/(tabs)/quizzes/quizReview.tsx index f8ef78e9..f7365f0a 100644 --- a/mobile/bulingo/app/(tabs)/quizzes/quizReview.tsx +++ b/mobile/bulingo/app/(tabs)/quizzes/quizReview.tsx @@ -130,6 +130,7 @@ useEffect(() => { isUserAnswer && !isCorrectAnswer && styles.wrongAnswer, ]} onPress={() => handleOptionSelect(index)} + testID='button' > {choice}