mirror of
https://github.com/ets-cfuhrman-pfe/EvalueTonSavoir.git
synced 2025-08-11 21:23:54 -04:00
Plus besoin de local storage si on passe toutes les questions à TeacherModeQuiz aussi
Nécessite un nouveau message launch-teacher-mode
This commit is contained in:
parent
22482592cd
commit
ca1eb4737d
10 changed files with 157 additions and 72 deletions
|
|
@ -5,6 +5,7 @@ import { MemoryRouter } from 'react-router-dom';
|
|||
import StudentModeQuiz from 'src/components/StudentModeQuiz/StudentModeQuiz';
|
||||
import { BaseQuestion, parse } from 'gift-pegjs';
|
||||
import { QuestionType } from 'src/Types/QuestionType';
|
||||
import { AnswerSubmissionToBackendType } from 'src/services/WebsocketService';
|
||||
|
||||
const mockGiftQuestions = parse(
|
||||
`::Sample Question 1:: Sample Question 1 {=Option A ~Option B}
|
||||
|
|
@ -23,12 +24,13 @@ const mockDisconnectWebSocket = jest.fn();
|
|||
|
||||
beforeEach(() => {
|
||||
// Clear local storage before each test
|
||||
localStorage.clear();
|
||||
// localStorage.clear();
|
||||
|
||||
render(
|
||||
<MemoryRouter>
|
||||
<StudentModeQuiz
|
||||
questions={mockQuestions}
|
||||
answers={Array(mockQuestions.length).fill({} as AnswerSubmissionToBackendType)}
|
||||
submitAnswer={mockSubmitAnswer}
|
||||
disconnectWebSocket={mockDisconnectWebSocket}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import { BaseQuestion, MultipleChoiceQuestion, parse } from 'gift-pegjs';
|
|||
import TeacherModeQuiz from 'src/components/TeacherModeQuiz/TeacherModeQuiz';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { QuestionType } from 'src/Types/QuestionType';
|
||||
import { AnswerSubmissionToBackendType } from 'src/services/WebsocketService';
|
||||
|
||||
const mockGiftQuestions = parse(
|
||||
`::Sample Question 1:: Sample Question 1 {=Option A ~Option B}
|
||||
|
|
@ -36,6 +37,7 @@ describe('TeacherModeQuiz', () => {
|
|||
<MemoryRouter>
|
||||
<TeacherModeQuiz
|
||||
questionInfos={{ question: mockQuestion }}
|
||||
answers={Array(mockQuestions.length).fill({} as AnswerSubmissionToBackendType)}
|
||||
submitAnswer={mockSubmitAnswer}
|
||||
disconnectWebSocket={mockDisconnectWebSocket} />
|
||||
</MemoryRouter>
|
||||
|
|
@ -80,6 +82,7 @@ describe('TeacherModeQuiz', () => {
|
|||
<MemoryRouter>
|
||||
<TeacherModeQuiz
|
||||
questionInfos={{ question: mockQuestion }}
|
||||
answers={Array(mockQuestions.length).fill({} as AnswerSubmissionToBackendType)}
|
||||
submitAnswer={mockSubmitAnswer}
|
||||
disconnectWebSocket={mockDisconnectWebSocket}
|
||||
/>
|
||||
|
|
@ -94,6 +97,7 @@ describe('TeacherModeQuiz', () => {
|
|||
<MemoryRouter>
|
||||
<TeacherModeQuiz
|
||||
questionInfos={{ question: mockQuestion }}
|
||||
answers={Array(mockQuestions.length).fill({} as AnswerSubmissionToBackendType)}
|
||||
submitAnswer={mockSubmitAnswer}
|
||||
disconnectWebSocket={mockDisconnectWebSocket}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
//WebsocketService.test.tsx
|
||||
import { BaseQuestion, parse } from 'gift-pegjs';
|
||||
import WebsocketService from '../../services/WebsocketService';
|
||||
import { io, Socket } from 'socket.io-client';
|
||||
import { ENV_VARIABLES } from 'src/constants';
|
||||
import { QuestionType } from 'src/Types/QuestionType';
|
||||
|
||||
jest.mock('socket.io-client');
|
||||
|
||||
|
|
@ -45,10 +47,16 @@ describe('WebSocketService', () => {
|
|||
|
||||
test('nextQuestion should emit next-question event with correct parameters', () => {
|
||||
const roomName = 'testRoom';
|
||||
const question = { id: 1, text: 'Sample Question' };
|
||||
|
||||
const mockGiftQuestions = parse('A {T}');
|
||||
const mockQuestions: QuestionType[] = mockGiftQuestions.map((question, index) => {
|
||||
if (question.type !== "Category")
|
||||
question.id = (index + 1).toString();
|
||||
const newMockQuestion = question;
|
||||
return {question : newMockQuestion as BaseQuestion};
|
||||
});
|
||||
mockSocket = WebsocketService.connect(ENV_VARIABLES.VITE_BACKEND_URL);
|
||||
WebsocketService.nextQuestion(roomName, question);
|
||||
WebsocketService.nextQuestion({roomName, questions: mockQuestions, questionIndex: 0, isLaunch: false});
|
||||
const question = mockQuestions[0];
|
||||
expect(mockSocket.emit).toHaveBeenCalledWith('next-question', { roomName, question });
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -7,22 +7,25 @@ import { Button } from '@mui/material';
|
|||
//import QuestionNavigation from '../QuestionNavigation/QuestionNavigation';
|
||||
import DisconnectButton from 'src/components/DisconnectButton/DisconnectButton';
|
||||
import { Question } from 'gift-pegjs';
|
||||
import { AnswerSubmissionToBackendType } from 'src/services/WebsocketService';
|
||||
|
||||
interface StudentModeQuizProps {
|
||||
questions: QuestionType[];
|
||||
answers: AnswerSubmissionToBackendType[];
|
||||
submitAnswer: (_answer: string | number | boolean, _idQuestion: number) => void;
|
||||
disconnectWebSocket: () => void;
|
||||
}
|
||||
|
||||
const StudentModeQuiz: React.FC<StudentModeQuizProps> = ({
|
||||
questions,
|
||||
answers,
|
||||
submitAnswer,
|
||||
disconnectWebSocket
|
||||
}) => {
|
||||
//Ajouter type AnswerQuestionType en remplacement de QuestionType
|
||||
const [questionInfos, setQuestion] = useState<QuestionType>(questions[0]);
|
||||
const [isAnswerSubmitted, setIsAnswerSubmitted] = useState(false);
|
||||
const [answer, setAnswer] = useState<string | number | boolean>('');
|
||||
// const [answer, setAnswer] = useState<string | number | boolean>('');
|
||||
|
||||
|
||||
const previousQuestion = () => {
|
||||
|
|
@ -30,13 +33,10 @@ const StudentModeQuiz: React.FC<StudentModeQuizProps> = ({
|
|||
};
|
||||
|
||||
useEffect(() => {
|
||||
setAnswer(JSON.parse(localStorage.getItem(`Answer${questionInfos.question.id}`)||'null'));
|
||||
if (answer !== null) {
|
||||
setIsAnswerSubmitted(true);
|
||||
} else {
|
||||
setIsAnswerSubmitted(false);
|
||||
}
|
||||
}, [questionInfos.question , answer]);
|
||||
const savedAnswer = answers[Number(questionInfos.question.id)-1]?.answer;
|
||||
console.log(`StudentModeQuiz: useEffect: savedAnswer: ${savedAnswer}`);
|
||||
setIsAnswerSubmitted(savedAnswer !== undefined);
|
||||
}, [questionInfos.question, answers]);
|
||||
|
||||
const nextQuestion = () => {
|
||||
setQuestion(questions[Number(questionInfos.question?.id)]);
|
||||
|
|
@ -73,7 +73,7 @@ const StudentModeQuiz: React.FC<StudentModeQuizProps> = ({
|
|||
handleOnSubmitAnswer={handleOnSubmitAnswer}
|
||||
question={questionInfos.question as Question}
|
||||
showAnswer={isAnswerSubmitted}
|
||||
answer={answer}
|
||||
answer={answers[Number(questionInfos.question.id)-1]?.answer}
|
||||
/>
|
||||
<div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', marginTop: '1rem' }}>
|
||||
<div>
|
||||
|
|
|
|||
|
|
@ -6,39 +6,54 @@ import { QuestionType } from '../../Types/QuestionType';
|
|||
import DisconnectButton from 'src/components/DisconnectButton/DisconnectButton';
|
||||
import { Dialog, DialogTitle, DialogContent, DialogActions, Button } from '@mui/material';
|
||||
import { Question } from 'gift-pegjs';
|
||||
import { AnswerSubmissionToBackendType } from 'src/services/WebsocketService';
|
||||
import { AnswerType } from 'src/pages/Student/JoinRoom/JoinRoom';
|
||||
// import { AnswerType } from 'src/pages/Student/JoinRoom/JoinRoom';
|
||||
|
||||
interface TeacherModeQuizProps {
|
||||
questionInfos: QuestionType;
|
||||
answers: AnswerSubmissionToBackendType[];
|
||||
submitAnswer: (_answer: string | number | boolean, _idQuestion: number) => void;
|
||||
disconnectWebSocket: () => void;
|
||||
}
|
||||
|
||||
const TeacherModeQuiz: React.FC<TeacherModeQuizProps> = ({
|
||||
questionInfos,
|
||||
answers,
|
||||
submitAnswer,
|
||||
disconnectWebSocket
|
||||
}) => {
|
||||
const [isAnswerSubmitted, setIsAnswerSubmitted] = useState(false);
|
||||
const [isFeedbackDialogOpen, setIsFeedbackDialogOpen] = useState(false);
|
||||
const [answer, setAnswer] = useState<string | number | boolean>();
|
||||
const [answer, setAnswer] = useState<AnswerType>();
|
||||
|
||||
|
||||
// arrive here the first time after waiting for next question
|
||||
useEffect(() => {
|
||||
console.log(`TeacherModeQuiz: useEffect: answers: ${JSON.stringify(answers)}`);
|
||||
console.log(`TeacherModeQuiz: useEffect: questionInfos.question.id: ${questionInfos.question.id} answer: ${answer}`);
|
||||
const oldAnswer = answers[Number(questionInfos.question.id) -1 ]?.answer;
|
||||
console.log(`TeacherModeQuiz: useEffect: oldAnswer: ${oldAnswer}`);
|
||||
setAnswer(oldAnswer);
|
||||
setIsFeedbackDialogOpen(false);
|
||||
}, [questionInfos.question, answers]);
|
||||
|
||||
// handle showing the feedback dialog
|
||||
useEffect(() => {
|
||||
console.log(`TeacherModeQuiz: useEffect: answer: ${answer}`);
|
||||
setIsAnswerSubmitted(answer !== undefined);
|
||||
setIsFeedbackDialogOpen(answer !== undefined);
|
||||
}, [answer]);
|
||||
|
||||
useEffect(() => {
|
||||
// Close the feedback dialog when the question changes
|
||||
handleFeedbackDialogClose();
|
||||
setIsAnswerSubmitted(false);
|
||||
setAnswer(JSON.parse(localStorage.getItem(`Answer${questionInfos.question.id}`)||'null'));
|
||||
if (typeof answer !== "object" && typeof answer !== "undefined") {
|
||||
setIsAnswerSubmitted(true);
|
||||
setIsFeedbackDialogOpen(true);
|
||||
}
|
||||
|
||||
}, [questionInfos.question , answer]);
|
||||
console.log(`TeacherModeQuiz: useEffect: isAnswerSubmitted: ${isAnswerSubmitted}`);
|
||||
setIsFeedbackDialogOpen(isAnswerSubmitted);
|
||||
}, [isAnswerSubmitted]);
|
||||
|
||||
const handleOnSubmitAnswer = (answer: string | number | boolean) => {
|
||||
const idQuestion = Number(questionInfos.question.id) || -1;
|
||||
submitAnswer(answer, idQuestion);
|
||||
setAnswer(answer);
|
||||
// setAnswer(answer);
|
||||
setIsFeedbackDialogOpen(true);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@ import LoginContainer from 'src/components/LoginContainer/LoginContainer'
|
|||
|
||||
import ApiService from '../../../services/ApiService'
|
||||
|
||||
export type AnswerType = string | number | boolean;
|
||||
|
||||
const JoinRoom: React.FC = () => {
|
||||
const [roomName, setRoomName] = useState('');
|
||||
const [username, setUsername] = useState(ApiService.getUsername());
|
||||
|
|
@ -25,6 +27,7 @@ const JoinRoom: React.FC = () => {
|
|||
const [question, setQuestion] = useState<QuestionType>();
|
||||
const [quizMode, setQuizMode] = useState<string>();
|
||||
const [questions, setQuestions] = useState<QuestionType[]>([]);
|
||||
const [answers, setAnswers] = useState<AnswerSubmissionToBackendType[]>([]);
|
||||
const [connectionError, setConnectionError] = useState<string>('');
|
||||
const [isConnecting, setIsConnecting] = useState<boolean>(false);
|
||||
|
||||
|
|
@ -35,6 +38,13 @@ const JoinRoom: React.FC = () => {
|
|||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
// init the answers array, one for each question
|
||||
setAnswers(Array(questions.length).fill({} as AnswerSubmissionToBackendType));
|
||||
console.log(`JoinRoom: useEffect: questions: ${JSON.stringify(questions)}`);
|
||||
}, [questions]);
|
||||
|
||||
|
||||
const handleCreateSocket = () => {
|
||||
console.log(`JoinRoom: handleCreateSocket: ${ENV_VARIABLES.VITE_BACKEND_URL}`);
|
||||
const socket = webSocketService.connect(ENV_VARIABLES.VITE_BACKEND_URL);
|
||||
|
|
@ -45,12 +55,18 @@ const JoinRoom: React.FC = () => {
|
|||
console.log(`on(join-success): Successfully joined the room ${roomJoinedName}`);
|
||||
});
|
||||
socket.on('next-question', (question: QuestionType) => {
|
||||
console.log('on(next-question): Received next-question:', question);
|
||||
console.log('JoinRoom: on(next-question): Received next-question:', question);
|
||||
setQuizMode('teacher');
|
||||
setIsWaitingForTeacher(false);
|
||||
|
||||
setQuestion(question);
|
||||
});
|
||||
socket.on('launch-teacher-mode', (questions: QuestionType[]) => {
|
||||
console.log('on(launch-teacher-mode): Received launch-teacher-mode:', questions);
|
||||
setQuizMode('teacher');
|
||||
setIsWaitingForTeacher(true);
|
||||
setQuestions(questions);
|
||||
// wait for next-question
|
||||
});
|
||||
socket.on('launch-student-mode', (questions: QuestionType[]) => {
|
||||
console.log('on(launch-student-mode): Received launch-student-mode:', questions);
|
||||
|
||||
|
|
@ -84,7 +100,7 @@ const JoinRoom: React.FC = () => {
|
|||
};
|
||||
|
||||
const disconnect = () => {
|
||||
localStorage.clear();
|
||||
// localStorage.clear();
|
||||
webSocketService.disconnect();
|
||||
setSocket(null);
|
||||
setQuestion(undefined);
|
||||
|
|
@ -109,14 +125,22 @@ const JoinRoom: React.FC = () => {
|
|||
}
|
||||
};
|
||||
|
||||
const handleOnSubmitAnswer = (answer: string | number | boolean, idQuestion: number) => {
|
||||
const handleOnSubmitAnswer = (answer: AnswerType, idQuestion: number) => {
|
||||
console.info(`JoinRoom: handleOnSubmitAnswer: answer: ${answer}, idQuestion: ${idQuestion}`);
|
||||
const answerData: AnswerSubmissionToBackendType = {
|
||||
roomName: roomName,
|
||||
answer: answer,
|
||||
username: username,
|
||||
idQuestion: idQuestion
|
||||
};
|
||||
localStorage.setItem(`Answer${idQuestion}`, JSON.stringify(answer));
|
||||
// localStorage.setItem(`Answer${idQuestion}`, JSON.stringify(answer));
|
||||
setAnswers((prevAnswers) => {
|
||||
console.log(`JoinRoom: handleOnSubmitAnswer: prevAnswers: ${JSON.stringify(prevAnswers)}`);
|
||||
const newAnswers = [...prevAnswers]; // Create a copy of the previous answers array
|
||||
newAnswers[idQuestion - 1] = answerData; // Update the specific answer
|
||||
return newAnswers; // Return the new array
|
||||
});
|
||||
console.log(`JoinRoom: handleOnSubmitAnswer: answers: ${JSON.stringify(answers)}`);
|
||||
webSocketService.submitAnswer(answerData);
|
||||
};
|
||||
|
||||
|
|
@ -154,6 +178,7 @@ const JoinRoom: React.FC = () => {
|
|||
return (
|
||||
<StudentModeQuiz
|
||||
questions={questions}
|
||||
answers={answers}
|
||||
submitAnswer={handleOnSubmitAnswer}
|
||||
disconnectWebSocket={disconnect}
|
||||
/>
|
||||
|
|
@ -163,6 +188,7 @@ const JoinRoom: React.FC = () => {
|
|||
question && (
|
||||
<TeacherModeQuiz
|
||||
questionInfos={question}
|
||||
answers={answers}
|
||||
submitAnswer={handleOnSubmitAnswer}
|
||||
disconnectWebSocket={disconnect}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -131,7 +131,11 @@ const ManageRoom: React.FC = () => {
|
|||
if (!quizStarted) return;
|
||||
|
||||
if (quizMode === 'teacher') {
|
||||
webSocketService.nextQuestion(formattedRoomName, currentQuestion);
|
||||
webSocketService.nextQuestion(
|
||||
{roomName: formattedRoomName,
|
||||
questions: quizQuestions,
|
||||
questionIndex: Number(currentQuestion?.question.id) - 1,
|
||||
isLaunch: false});
|
||||
} else if (quizMode === 'student') {
|
||||
webSocketService.launchStudentModeQuiz(formattedRoomName, quizQuestions);
|
||||
}
|
||||
|
|
@ -224,7 +228,10 @@ const ManageRoom: React.FC = () => {
|
|||
if (nextQuestionIndex === undefined || nextQuestionIndex > quizQuestions.length - 1) return;
|
||||
|
||||
setCurrentQuestion(quizQuestions[nextQuestionIndex]);
|
||||
webSocketService.nextQuestion(formattedRoomName, quizQuestions[nextQuestionIndex]);
|
||||
webSocketService.nextQuestion({roomName: formattedRoomName,
|
||||
questions: quizQuestions,
|
||||
questionIndex: nextQuestionIndex,
|
||||
isLaunch: false});
|
||||
};
|
||||
|
||||
const previousQuestion = () => {
|
||||
|
|
@ -234,7 +241,7 @@ const ManageRoom: React.FC = () => {
|
|||
|
||||
if (prevQuestionIndex === undefined || prevQuestionIndex < 0) return;
|
||||
setCurrentQuestion(quizQuestions[prevQuestionIndex]);
|
||||
webSocketService.nextQuestion(formattedRoomName, quizQuestions[prevQuestionIndex]);
|
||||
webSocketService.nextQuestion({roomName: formattedRoomName, questions: quizQuestions, questionIndex: prevQuestionIndex, isLaunch: false});
|
||||
};
|
||||
|
||||
const initializeQuizQuestion = () => {
|
||||
|
|
@ -262,7 +269,7 @@ const ManageRoom: React.FC = () => {
|
|||
}
|
||||
|
||||
setCurrentQuestion(quizQuestions[0]);
|
||||
webSocketService.nextQuestion(formattedRoomName, quizQuestions[0]);
|
||||
webSocketService.nextQuestion({roomName: formattedRoomName, questions: quizQuestions, questionIndex: 0, isLaunch: true});
|
||||
};
|
||||
|
||||
const launchStudentMode = () => {
|
||||
|
|
@ -300,9 +307,8 @@ const ManageRoom: React.FC = () => {
|
|||
const showSelectedQuestion = (questionIndex: number) => {
|
||||
if (quiz?.content && quizQuestions) {
|
||||
setCurrentQuestion(quizQuestions[questionIndex]);
|
||||
|
||||
if (quizMode === 'teacher') {
|
||||
webSocketService.nextQuestion(formattedRoomName, quizQuestions[questionIndex]);
|
||||
webSocketService.nextQuestion({roomName: formattedRoomName, questions: quizQuestions, questionIndex, isLaunch: false});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { io, Socket } from 'socket.io-client';
|
||||
import { QuestionType } from 'src/Types/QuestionType';
|
||||
|
||||
// Must (manually) sync these types to server/socket/socket.js
|
||||
|
||||
|
|
@ -59,12 +60,19 @@ class WebSocketService {
|
|||
// }
|
||||
// }
|
||||
|
||||
nextQuestion(roomName: string, question: unknown) {
|
||||
console.log('WebsocketService: nextQuestion', roomName, question);
|
||||
if (!question) {
|
||||
nextQuestion(args: {roomName: string, questions: QuestionType[] | undefined, questionIndex: number, isLaunch: boolean}) {
|
||||
// deconstruct args
|
||||
const { roomName, questions, questionIndex, isLaunch } = args;
|
||||
console.log('WebsocketService: nextQuestion', roomName, questions, questionIndex, isLaunch);
|
||||
if (!questions || !questions[questionIndex]) {
|
||||
throw new Error('WebsocketService: nextQuestion: question is null');
|
||||
}
|
||||
|
||||
if (this.socket) {
|
||||
if (isLaunch) {
|
||||
this.socket.emit('launch-teacher-mode', { roomName, questions });
|
||||
}
|
||||
const question = questions[questionIndex];
|
||||
this.socket.emit('next-question', { roomName, question });
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -109,17 +109,29 @@ describe("websocket server", () => {
|
|||
});
|
||||
});
|
||||
|
||||
test("should send next question", (done) => {
|
||||
studentSocket.on("next-question", (question) => {
|
||||
expect(question).toEqual({ question: "question2" });
|
||||
test("should launch teacher mode", (done) => {
|
||||
studentSocket.on("launch-teacher-mode", (questions) => {
|
||||
expect(questions).toEqual([
|
||||
{ question: "question1" },
|
||||
{ question: "question2" },
|
||||
]);
|
||||
done();
|
||||
});
|
||||
teacherSocket.emit("next-question", {
|
||||
teacherSocket.emit("launch-teacher-mode", {
|
||||
roomName: "ROOM1",
|
||||
question: { question: "question2" },
|
||||
questions: [{ question: "question1" }, { question: "question2" }],
|
||||
});
|
||||
});
|
||||
|
||||
test("should send next question", (done) => {
|
||||
studentSocket.on("next-question", ( question ) => {
|
||||
expect(question).toBe("question2");
|
||||
done();
|
||||
});
|
||||
teacherSocket.emit("next-question", { roomName: "ROOM1", question: 'question2'},
|
||||
);
|
||||
});
|
||||
|
||||
test("should send answer", (done) => {
|
||||
teacherSocket.on("submit-answer-room", (answer) => {
|
||||
expect(answer).toEqual({
|
||||
|
|
|
|||
|
|
@ -81,6 +81,10 @@ const setupWebsocket = (io) => {
|
|||
socket.to(roomName).emit("next-question", question);
|
||||
});
|
||||
|
||||
socket.on("launch-teacher-mode", ({ roomName, questions }) => {
|
||||
socket.to(roomName).emit("launch-teacher-mode", questions);
|
||||
});
|
||||
|
||||
socket.on("launch-student-mode", ({ roomName, questions }) => {
|
||||
socket.to(roomName).emit("launch-student-mode", questions);
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue