From 182402e6213aa3e5a6d303af5bfca19546e7af10 Mon Sep 17 00:00:00 2001 From: JubaAzul <118773284+JubaAzul@users.noreply.github.com> Date: Thu, 20 Mar 2025 12:49:15 -0400 Subject: [PATCH 1/2] =?UTF-8?q?[BUG]=20Le=20progr=C3=A8s=20des=20=C3=A9tud?= =?UTF-8?q?iants=20n'est=20pas=20sauvegard=C3=A9=20lorsqu'ils=20quittent?= =?UTF-8?q?=20et=20reviennent=20dans=20le=20quiz.=20Fixes=20#296?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pages/Teacher/ManageRoom/ManageRoom.tsx | 50 ++++++++++++++++++- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/client/src/pages/Teacher/ManageRoom/ManageRoom.tsx b/client/src/pages/Teacher/ManageRoom/ManageRoom.tsx index 01d9c27..83ae9f6 100644 --- a/client/src/pages/Teacher/ManageRoom/ManageRoom.tsx +++ b/client/src/pages/Teacher/ManageRoom/ManageRoom.tsx @@ -30,6 +30,7 @@ const ManageRoom: React.FC = () => { const navigate = useNavigate(); const [socket, setSocket] = useState(null); const [students, setStudents] = useState([]); + const [allStudents, setAllStudents] = useState([]); const { quizId = '', roomName = '' } = useParams<{ quizId: string, roomName: string }>(); const [quizQuestions, setQuizQuestions] = useState(); const [quiz, setQuiz] = useState(null); @@ -46,7 +47,8 @@ const ManageRoom: React.FC = () => { if (newlyConnectedUser) { console.log(`Handling newly connected user: ${newlyConnectedUser.name}`); setStudents((prevStudents) => [...prevStudents, newlyConnectedUser]); - + setAllStudents((prevStudents) => [...prevStudents, newlyConnectedUser]); + // only send nextQuestion if the quiz has started if (!quizStarted) { console.log(`!quizStarted: returning.... `); @@ -134,6 +136,8 @@ const ManageRoom: React.FC = () => { setQuizQuestions(undefined); setCurrentQuestion(undefined); setStudents(new Array()); + setAllStudents(new Array()); + } }; @@ -245,6 +249,48 @@ const ManageRoom: React.FC = () => { } return updatedStudents; }); + setAllStudents((prevStudents) => { + let foundStudent = false; + const updatedStudents = prevStudents.map((student) => { + console.log(`Comparing ${student.id} to ${idUser}`); + if (student.id === idUser) { + foundStudent = true; + const existingAnswer = student.answers.find( + (ans) => ans.idQuestion === idQuestion + ); + let updatedAnswers: Answer[] = []; + if (existingAnswer) { + updatedAnswers = student.answers.map((ans) => { + console.log(`Comparing ${ans.idQuestion} to ${idQuestion}`); + return ans.idQuestion === idQuestion + ? { + ...ans, + answer, + isCorrect: checkIfIsCorrect( + answer, + idQuestion, + quizQuestions! + ) + } + : ans; + }); + } else { + const newAnswer = { + idQuestion, + answer, + isCorrect: checkIfIsCorrect(answer, idQuestion, quizQuestions!) + }; + updatedAnswers = [...student.answers, newAnswer]; + } + return { ...student, answers: updatedAnswers }; + } + return student; + }); + if (!foundStudent) { + console.log(`Student ${username} not found in the list.`); + } + return updatedStudents; + }); }); setSocket(socket); } @@ -496,7 +542,7 @@ const ManageRoom: React.FC = () => { socket={socket} questions={quizQuestions} showSelectedQuestion={showSelectedQuestion} - students={students} + students={allStudents} > From e2c4bb07226da3d9fb7542d0289cd88a05cc13a5 Mon Sep 17 00:00:00 2001 From: JubaAzul <118773284+JubaAzul@users.noreply.github.com> Date: Mon, 24 Mar 2025 16:33:26 -0400 Subject: [PATCH 2/2] =?UTF-8?q?[BUG]=20Le=20progr=C3=A8s=20des=20=C3=A9tud?= =?UTF-8?q?iants=20n'est=20pas=20sauvegard=C3=A9=20lorsqu'ils=20quittent?= =?UTF-8?q?=20et=20reviennent=20dans=20le=20quiz.=20Fixes=20#296?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pages/ManageRoom/ManageRoom.test.tsx | 118 ++++++++++++++---- .../pages/Teacher/ManageRoom/ManageRoom.tsx | 82 ++++++------ 2 files changed, 136 insertions(+), 64 deletions(-) diff --git a/client/src/__tests__/pages/ManageRoom/ManageRoom.test.tsx b/client/src/__tests__/pages/ManageRoom/ManageRoom.test.tsx index 142f1f3..948d96f 100644 --- a/client/src/__tests__/pages/ManageRoom/ManageRoom.test.tsx +++ b/client/src/__tests__/pages/ManageRoom/ManageRoom.test.tsx @@ -75,36 +75,36 @@ describe('ManageRoom', () => { ); }); - + await act(async () => { const createSuccessCallback = (mockSocket.on as jest.Mock).mock.calls.find(call => call[0] === 'create-success')[1]; createSuccessCallback('Test Room'); }); - + await waitFor(() => { expect(ApiService.getQuiz).toHaveBeenCalledWith('test-quiz-id'); }); - + const launchButton = screen.getByText('Lancer'); fireEvent.click(launchButton); - + const rythmeButton = screen.getByText('Rythme du professeur'); fireEvent.click(rythmeButton); - + const secondLaunchButton = screen.getAllByText('Lancer'); fireEvent.click(secondLaunchButton[1]); - + await waitFor(() => { expect(screen.getByText('Test Quiz')).toBeInTheDocument(); - + const roomHeader = document.querySelector('h1'); expect(roomHeader).toHaveTextContent('Salle : TEST ROOM'); - + expect(screen.getByText('0/60')).toBeInTheDocument(); expect(screen.getByText('Question 1/2')).toBeInTheDocument(); }); }); - + test('handles create-success event', async () => { await act(async () => { render( @@ -171,30 +171,30 @@ describe('ManageRoom', () => { ); }); - + await act(async () => { const createSuccessCallback = (mockSocket.on as jest.Mock).mock.calls.find(call => call[0] === 'create-success')[1]; createSuccessCallback('Test Room'); }); - + fireEvent.click(screen.getByText('Lancer')); fireEvent.click(screen.getByText('Rythme du professeur')); fireEvent.click(screen.getAllByText('Lancer')[1]); - + await waitFor(() => { screen.debug(); }); - + const nextQuestionButton = await screen.findByRole('button', { name: /Prochaine question/i }); expect(nextQuestionButton).toBeInTheDocument(); - + fireEvent.click(nextQuestionButton); - + await waitFor(() => { expect(screen.getByText('Question 2/2')).toBeInTheDocument(); }); }); - + test('handles disconnect', async () => { await act(async () => { render( @@ -230,37 +230,37 @@ describe('ManageRoom', () => { ); }); - + await act(async () => { const createSuccessCallback = (mockSocket.on as jest.Mock).mock.calls.find(call => call[0] === 'create-success')[1]; createSuccessCallback('test-room-name'); }); - + const launchButton = screen.getByText('Lancer'); fireEvent.click(launchButton); - + const rythmeButton = screen.getByText('Rythme du professeur'); fireEvent.click(rythmeButton); - + const secondLaunchButton = screen.getAllByText('Lancer'); fireEvent.click(secondLaunchButton[1]); - + await act(async () => { const userJoinedCallback = (mockSocket.on as jest.Mock).mock.calls.find(call => call[0] === 'user-joined')[1]; userJoinedCallback(mockStudents[0]); }); - + await act(async () => { const submitAnswerCallback = (mockSocket.on as jest.Mock).mock.calls.find(call => call[0] === 'submit-answer-room')[1]; submitAnswerCallback(mockAnswerData); }); - + await waitFor(() => { expect(consoleSpy).toHaveBeenCalledWith( 'Received answer from Student 1 for question 1: Answer1' ); }); - + consoleSpy.mockRestore(); }); @@ -293,4 +293,74 @@ describe('ManageRoom', () => { expect(screen.queryByText('Student 1')).not.toBeInTheDocument(); }); }); + + test('handles user joined and leaves but name still seeable', async () => { + await act(async () => { + render( + + + + ); + }); + + await act(async () => { + const createSuccessCallback = (mockSocket.on as jest.Mock).mock.calls.find(call => call[0] === 'create-success')[1]; + createSuccessCallback('Test Room'); + }); + + await act(async () => { + const userJoinedCallback = (mockSocket.on as jest.Mock).mock.calls.find(call => call[0] === 'user-joined')[1]; + userJoinedCallback(mockStudents[0]); + }); + await act(async () => { + const userJoinedCallback = (mockSocket.on as jest.Mock).mock.calls.find(call => call[0] === 'user-joined')[1]; + userJoinedCallback(mockStudents[1]); + }); + + await waitFor(() => { + expect(screen.getByText('Student 1')).toBeInTheDocument(); + + }); + await waitFor(() => { + expect(screen.getByText('Student 2')).toBeInTheDocument(); + + }); + + const launchButton = screen.getByText('Lancer'); + fireEvent.click(launchButton); + + const rythmeButton = screen.getByText('Rythme du professeur'); + fireEvent.click(rythmeButton); + + const secondLaunchButton = screen.getAllByText('Lancer'); + fireEvent.click(secondLaunchButton[1]); + + await waitFor(() => { + expect(screen.getByText('2/60')).toBeInTheDocument(); + + }); + + await act(async () => { + const userDisconnectedCallback = (mockSocket.on as jest.Mock).mock.calls.find(call => call[0] === 'user-disconnected')[1]; + userDisconnectedCallback(mockStudents[1].id); + }); + + + await waitFor(() => { + expect(screen.getByText('Résultats du quiz')).toBeInTheDocument(); + + }); + const toggleUsernamesSwitch = screen.getByLabelText('Afficher les noms'); + + // Toggle the display of usernames back + fireEvent.click(toggleUsernamesSwitch); + + expect(screen.getByText('Student 1')).toBeInTheDocument(); + + expect(screen.getByText('Student 2')).toBeInTheDocument(); + + await waitFor(() => { + expect(screen.getByText('1/60')).toBeInTheDocument(); + }); + }); }); diff --git a/client/src/pages/Teacher/ManageRoom/ManageRoom.tsx b/client/src/pages/Teacher/ManageRoom/ManageRoom.tsx index 83ae9f6..7ac4caa 100644 --- a/client/src/pages/Teacher/ManageRoom/ManageRoom.tsx +++ b/client/src/pages/Teacher/ManageRoom/ManageRoom.tsx @@ -29,9 +29,9 @@ import { AnswerType } from 'src/pages/Student/JoinRoom/JoinRoom'; const ManageRoom: React.FC = () => { const navigate = useNavigate(); const [socket, setSocket] = useState(null); - const [students, setStudents] = useState([]); - const [allStudents, setAllStudents] = useState([]); - const { quizId = '', roomName = '' } = useParams<{ quizId: string, roomName: string }>(); + const [connectedStudents, setConnectedStudents] = useState([]); + const [totalStudents, setTotalStudents] = useState([]); + const { quizId = '', roomName = '' } = useParams<{ quizId: string, roomName: string }>(); const [quizQuestions, setQuizQuestions] = useState(); const [quiz, setQuiz] = useState(null); const [quizMode, setQuizMode] = useState<'teacher' | 'student'>('teacher'); @@ -46,15 +46,15 @@ const ManageRoom: React.FC = () => { useEffect(() => { if (newlyConnectedUser) { console.log(`Handling newly connected user: ${newlyConnectedUser.name}`); - setStudents((prevStudents) => [...prevStudents, newlyConnectedUser]); - setAllStudents((prevStudents) => [...prevStudents, newlyConnectedUser]); + setConnectedStudents((prevStudents) => [...prevStudents, newlyConnectedUser]); + setTotalStudents((prevStudents) => [...prevStudents, newlyConnectedUser]); // only send nextQuestion if the quiz has started if (!quizStarted) { console.log(`!quizStarted: returning.... `); return; } - + if (quizMode === 'teacher') { webSocketService.nextQuestion({ roomName: formattedRoomName, @@ -67,7 +67,7 @@ const ManageRoom: React.FC = () => { } else { console.error('Invalid quiz mode:', quizMode); } - + // Reset the newly connected user state setNewlyConnectedUser(null); } @@ -98,7 +98,7 @@ const ManageRoom: React.FC = () => { return () => { disconnectWebSocket(); }; - }, [roomName, navigate]); + }, [roomName, navigate]); useEffect(() => { if (quizId) { @@ -135,8 +135,8 @@ const ManageRoom: React.FC = () => { setSocket(null); setQuizQuestions(undefined); setCurrentQuestion(undefined); - setStudents(new Array()); - setAllStudents(new Array()); + setConnectedStudents(new Array()); + setTotalStudents(new Array()); } }; @@ -181,7 +181,7 @@ const ManageRoom: React.FC = () => { socket.on('user-disconnected', (userId: string) => { console.log(`Student left: id = ${userId}`); - setStudents((prevUsers) => prevUsers.filter((user) => user.id !== userId)); + setConnectedStudents((prevUsers) => prevUsers.filter((user) => user.id !== userId)); }); setSocket(socket); @@ -202,7 +202,7 @@ const ManageRoom: React.FC = () => { } // Update the students state using the functional form of setStudents - setStudents((prevStudents) => { + setConnectedStudents((prevStudents) => { console.log('Current students:'); prevStudents.forEach((student) => { console.log(student.name); @@ -222,14 +222,14 @@ const ManageRoom: React.FC = () => { console.log(`Comparing ${ans.idQuestion} to ${idQuestion}`); return ans.idQuestion === idQuestion ? { - ...ans, - answer, - isCorrect: checkIfIsCorrect( - answer, - idQuestion, - quizQuestions! - ) - } + ...ans, + answer, + isCorrect: checkIfIsCorrect( + answer, + idQuestion, + quizQuestions! + ) + } : ans; }); } else { @@ -249,7 +249,7 @@ const ManageRoom: React.FC = () => { } return updatedStudents; }); - setAllStudents((prevStudents) => { + setTotalStudents((prevStudents) => { let foundStudent = false; const updatedStudents = prevStudents.map((student) => { console.log(`Comparing ${student.id} to ${idUser}`); @@ -264,14 +264,14 @@ const ManageRoom: React.FC = () => { console.log(`Comparing ${ans.idQuestion} to ${idQuestion}`); return ans.idQuestion === idQuestion ? { - ...ans, - answer, - isCorrect: checkIfIsCorrect( - answer, - idQuestion, - quizQuestions! - ) - } + ...ans, + answer, + isCorrect: checkIfIsCorrect( + answer, + idQuestion, + quizQuestions! + ) + } : ans; }); } else { @@ -304,10 +304,12 @@ const ManageRoom: React.FC = () => { if (nextQuestionIndex === undefined || nextQuestionIndex > quizQuestions.length - 1) return; setCurrentQuestion(quizQuestions[nextQuestionIndex]); - webSocketService.nextQuestion({roomName: formattedRoomName, - questions: quizQuestions, - questionIndex: nextQuestionIndex, - isLaunch: false}); + webSocketService.nextQuestion({ + roomName: formattedRoomName, + questions: quizQuestions, + questionIndex: nextQuestionIndex, + isLaunch: false + }); }; const previousQuestion = () => { @@ -317,7 +319,7 @@ const ManageRoom: React.FC = () => { if (prevQuestionIndex === undefined || prevQuestionIndex < 0) return; setCurrentQuestion(quizQuestions[prevQuestionIndex]); - webSocketService.nextQuestion({roomName: formattedRoomName, questions: quizQuestions, questionIndex: prevQuestionIndex, isLaunch: false}); + webSocketService.nextQuestion({ roomName: formattedRoomName, questions: quizQuestions, questionIndex: prevQuestionIndex, isLaunch: false }); }; const initializeQuizQuestion = () => { @@ -345,7 +347,7 @@ const ManageRoom: React.FC = () => { } setCurrentQuestion(quizQuestions[0]); - webSocketService.nextQuestion({roomName: formattedRoomName, questions: quizQuestions, questionIndex: 0, isLaunch: true}); + webSocketService.nextQuestion({ roomName: formattedRoomName, questions: quizQuestions, questionIndex: 0, isLaunch: true }); }; const launchStudentMode = () => { @@ -382,7 +384,7 @@ const ManageRoom: React.FC = () => { if (quiz?.content && quizQuestions) { setCurrentQuestion(quizQuestions[questionIndex]); if (quizMode === 'teacher') { - webSocketService.nextQuestion({roomName: formattedRoomName, questions: quizQuestions, questionIndex, isLaunch: false}); + webSocketService.nextQuestion({ roomName: formattedRoomName, questions: quizQuestions, questionIndex, isLaunch: false }); } } }; @@ -496,7 +498,7 @@ const ManageRoom: React.FC = () => { style={{ display: "flex", justifyContent: "flex-end" }} > - {students.length}/60 + {connectedStudents.length}/60 )} @@ -533,7 +535,7 @@ const ManageRoom: React.FC = () => { )} @@ -542,7 +544,7 @@ const ManageRoom: React.FC = () => { socket={socket} questions={quizQuestions} showSelectedQuestion={showSelectedQuestion} - students={allStudents} + students={totalStudents} > @@ -578,7 +580,7 @@ const ManageRoom: React.FC = () => { ) : (