Compare commits

...

4 commits

Author SHA1 Message Date
Philippe Côté
4f46957cc2
Merge 814a022bc2 into 42c0bee527 2025-04-07 14:50:51 -04:00
Christopher (Cris) Fuhrman
42c0bee527
Merge pull request #304 from ets-cfuhrman-pfe/feature/finish-quiz-button
Ajout du bouton "Terminer le quiz"
2025-04-07 14:50:10 -04:00
NouhailaAater
74764ee695 Test 2025-03-24 03:00:05 -04:00
NouhailaAater
6600da990b Add Finish Quiz button 2025-03-24 02:52:41 -04:00
3 changed files with 83 additions and 23 deletions

View file

@ -294,5 +294,36 @@ describe('ManageRoom', () => {
expect(screen.queryByText('Student 1')).not.toBeInTheDocument(); expect(screen.queryByText('Student 1')).not.toBeInTheDocument();
}); });
}); });
test('terminates the quiz and navigates to teacher dashboard when the "Terminer le quiz" button is clicked', async () => {
await act(async () => {
render(
<MemoryRouter>
<ManageRoom />
</MemoryRouter>
);
});
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(() => {
expect(screen.getByText('Test Quiz')).toBeInTheDocument();
});
const finishQuizButton = screen.getByText('Terminer le quiz');
fireEvent.click(finishQuizButton);
await waitFor(() => {
expect(navigate).toHaveBeenCalledWith('/teacher/dashboard');
});
});
}); });

View file

@ -25,29 +25,29 @@ const ManageRoom: React.FC = () => {
const navigate = useNavigate(); const navigate = useNavigate();
const [socket, setSocket] = useState<Socket | null>(null); const [socket, setSocket] = useState<Socket | null>(null);
const [students, setStudents] = useState<StudentType[]>([]); const [students, setStudents] = useState<StudentType[]>([]);
const { quizId = '', roomName = '' } = useParams<{ quizId: string, roomName: string }>(); const { quizId = '', roomName = '' } = useParams<{ quizId: string; roomName: string }>();
const [quizQuestions, setQuizQuestions] = useState<QuestionType[] | undefined>(); const [quizQuestions, setQuizQuestions] = useState<QuestionType[] | undefined>();
const [quiz, setQuiz] = useState<QuizType | null>(null); const [quiz, setQuiz] = useState<QuizType | null>(null);
const [quizMode, setQuizMode] = useState<'teacher' | 'student'>('teacher'); const [quizMode, setQuizMode] = useState<'teacher' | 'student'>('teacher');
const [connectingError, setConnectingError] = useState<string>(''); const [connectingError, setConnectingError] = useState<string>('');
const [currentQuestion, setCurrentQuestion] = useState<QuestionType | undefined>(undefined); const [currentQuestion, setCurrentQuestion] = useState<QuestionType | undefined>(undefined);
const [quizStarted, setQuizStarted] = useState<boolean>(false); const [quizStarted, setQuizStarted] = useState<boolean>(false);
const [formattedRoomName, setFormattedRoomName] = useState(""); const [formattedRoomName, setFormattedRoomName] = useState('');
const [newlyConnectedUser, setNewlyConnectedUser] = useState<StudentType | null>(null); const [newlyConnectedUser, setNewlyConnectedUser] = useState<StudentType | null>(null);
// Handle the newly connected user in useEffect, because it needs state info // Handle the newly connected user in useEffect, because it needs state info
// not available in the socket.on() callback // not available in the socket.on() callback
useEffect(() => { useEffect(() => {
if (newlyConnectedUser) { if (newlyConnectedUser) {
console.log(`Handling newly connected user: ${newlyConnectedUser.name}`); console.log(`Handling newly connected user: ${newlyConnectedUser.name}`);
setStudents((prevStudents) => [...prevStudents, newlyConnectedUser]); setStudents((prevStudents) => [...prevStudents, newlyConnectedUser]);
// only send nextQuestion if the quiz has started // only send nextQuestion if the quiz has started
if (!quizStarted) { if (!quizStarted) {
console.log(`!quizStarted: returning.... `); console.log(`!quizStarted: returning.... `);
return; return;
} }
if (quizMode === 'teacher') { if (quizMode === 'teacher') {
webSocketService.nextQuestion({ webSocketService.nextQuestion({
roomName: formattedRoomName, roomName: formattedRoomName,
@ -60,7 +60,7 @@ const ManageRoom: React.FC = () => {
} else { } else {
console.error('Invalid quiz mode:', quizMode); console.error('Invalid quiz mode:', quizMode);
} }
// Reset the newly connected user state // Reset the newly connected user state
setNewlyConnectedUser(null); setNewlyConnectedUser(null);
} }
@ -91,7 +91,7 @@ const ManageRoom: React.FC = () => {
return () => { return () => {
disconnectWebSocket(); disconnectWebSocket();
}; };
}, [roomName, navigate]); }, [roomName, navigate]);
useEffect(() => { useEffect(() => {
if (quizId) { if (quizId) {
@ -138,8 +138,8 @@ const ManageRoom: React.FC = () => {
setFormattedRoomName(roomNameUpper); setFormattedRoomName(roomNameUpper);
console.log(`Creating WebSocket room named ${roomNameUpper}`); console.log(`Creating WebSocket room named ${roomNameUpper}`);
/** /**
* ATTENTION: Lire les variables d'état dans * ATTENTION: Lire les variables d'état dans
* les .on() n'est pas une bonne pratique. * les .on() n'est pas une bonne pratique.
* Les valeurs sont celles au moment de la création * Les valeurs sont celles au moment de la création
* de la fonction et non au moment de l'exécution. * de la fonction et non au moment de l'exécution.
@ -179,7 +179,6 @@ const ManageRoom: React.FC = () => {
}; };
useEffect(() => { useEffect(() => {
if (socket) { if (socket) {
console.log(`Listening for submit-answer-room in room ${formattedRoomName}`); console.log(`Listening for submit-answer-room in room ${formattedRoomName}`);
socket.on('submit-answer-room', (answerData: AnswerReceptionFromBackendType) => { socket.on('submit-answer-room', (answerData: AnswerReceptionFromBackendType) => {
@ -253,10 +252,12 @@ const ManageRoom: React.FC = () => {
if (nextQuestionIndex === undefined || nextQuestionIndex > quizQuestions.length - 1) return; if (nextQuestionIndex === undefined || nextQuestionIndex > quizQuestions.length - 1) return;
setCurrentQuestion(quizQuestions[nextQuestionIndex]); setCurrentQuestion(quizQuestions[nextQuestionIndex]);
webSocketService.nextQuestion({roomName: formattedRoomName, webSocketService.nextQuestion({
questions: quizQuestions, roomName: formattedRoomName,
questionIndex: nextQuestionIndex, questions: quizQuestions,
isLaunch: false}); questionIndex: nextQuestionIndex,
isLaunch: false
});
}; };
const previousQuestion = () => { const previousQuestion = () => {
@ -266,7 +267,12 @@ const ManageRoom: React.FC = () => {
if (prevQuestionIndex === undefined || prevQuestionIndex < 0) return; if (prevQuestionIndex === undefined || prevQuestionIndex < 0) return;
setCurrentQuestion(quizQuestions[prevQuestionIndex]); 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 = () => { const initializeQuizQuestion = () => {
@ -294,7 +300,12 @@ const ManageRoom: React.FC = () => {
} }
setCurrentQuestion(quizQuestions[0]); 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 = () => { const launchStudentMode = () => {
@ -331,11 +342,21 @@ const ManageRoom: React.FC = () => {
if (quiz?.content && quizQuestions) { if (quiz?.content && quizQuestions) {
setCurrentQuestion(quizQuestions[questionIndex]); setCurrentQuestion(quizQuestions[questionIndex]);
if (quizMode === 'teacher') { if (quizMode === 'teacher') {
webSocketService.nextQuestion({roomName: formattedRoomName, questions: quizQuestions, questionIndex, isLaunch: false}); webSocketService.nextQuestion({
roomName: formattedRoomName,
questions: quizQuestions,
questionIndex,
isLaunch: false
});
} }
} }
}; };
const finishQuiz = () => {
disconnectWebSocket();
navigate('/teacher/dashboard');
};
const handleReturn = () => { const handleReturn = () => {
disconnectWebSocket(); disconnectWebSocket();
navigate('/teacher/dashboard'); navigate('/teacher/dashboard');
@ -382,15 +403,15 @@ const ManageRoom: React.FC = () => {
width: '100%' width: '100%'
}} }}
> >
{( {
<div <div
className="userCount subtitle smallText" className="userCount subtitle smallText"
style={{ display: "flex", justifyContent: "flex-end" }} style={{ display: 'flex', justifyContent: 'flex-end' }}
> >
<GroupIcon style={{ marginRight: '5px' }} /> <GroupIcon style={{ marginRight: '5px' }} />
{students.length}/60 {students.length}/60
</div> </div>
)} }
</div> </div>
<div className="dumb"></div> <div className="dumb"></div>
@ -425,7 +446,6 @@ const ManageRoom: React.FC = () => {
<QuestionDisplay <QuestionDisplay
showAnswer={false} showAnswer={false}
question={currentQuestion?.question as Question} question={currentQuestion?.question as Question}
/> />
)} )}
@ -467,6 +487,11 @@ const ManageRoom: React.FC = () => {
</div> </div>
</div> </div>
)} )}
<div className="finishQuizButton">
<Button onClick={finishQuiz} variant="contained">
Terminer le quiz
</Button>
</div>
</div> </div>
) : ( ) : (
<StudentWaitPage <StudentWaitPage

View file

@ -37,8 +37,12 @@
/* align-items: center; */ /* align-items: center; */
} }
.room .finishQuizButton {
display: flex;
justify-content: flex-end;
margin-left: auto;
width: 100%;
}