Compare commits

..

No commits in common. "73e0326f44d371d5a02d7ed612979b783b78660c" and "22482592cdbdcac9f38f5ce4b56733060b8e576b" have entirely different histories.

10 changed files with 90 additions and 177 deletions

View file

@ -1,11 +1,10 @@
import React from 'react'; import React from 'react';
import { render, screen, fireEvent, act } from '@testing-library/react'; import { render, screen, fireEvent, act, waitFor } from '@testing-library/react';
import '@testing-library/jest-dom'; import '@testing-library/jest-dom';
import { MemoryRouter } from 'react-router-dom'; import { MemoryRouter } from 'react-router-dom';
import StudentModeQuiz from 'src/components/StudentModeQuiz/StudentModeQuiz'; import StudentModeQuiz from 'src/components/StudentModeQuiz/StudentModeQuiz';
import { BaseQuestion, parse } from 'gift-pegjs'; import { BaseQuestion, parse } from 'gift-pegjs';
import { QuestionType } from 'src/Types/QuestionType'; import { QuestionType } from 'src/Types/QuestionType';
import { AnswerSubmissionToBackendType } from 'src/services/WebsocketService';
const mockGiftQuestions = parse( const mockGiftQuestions = parse(
`::Sample Question 1:: Sample Question 1 {=Option A ~Option B} `::Sample Question 1:: Sample Question 1 {=Option A ~Option B}
@ -24,13 +23,12 @@ const mockDisconnectWebSocket = jest.fn();
beforeEach(() => { beforeEach(() => {
// Clear local storage before each test // Clear local storage before each test
// localStorage.clear(); localStorage.clear();
render( render(
<MemoryRouter> <MemoryRouter>
<StudentModeQuiz <StudentModeQuiz
questions={mockQuestions} questions={mockQuestions}
answers={Array(mockQuestions.length).fill({} as AnswerSubmissionToBackendType)}
submitAnswer={mockSubmitAnswer} submitAnswer={mockSubmitAnswer}
disconnectWebSocket={mockDisconnectWebSocket} disconnectWebSocket={mockDisconnectWebSocket}
/> />
@ -55,9 +53,13 @@ describe('StudentModeQuiz', () => {
}); });
expect(mockSubmitAnswer).toHaveBeenCalledWith('Option A', 1); expect(mockSubmitAnswer).toHaveBeenCalledWith('Option A', 1);
// await waitFor(() => {
// expect(localStorage.getItem('Answer1')).toBe(JSON.stringify('Option A'));
// });
}); });
test('handles shows feedback for an already answered question', async () => { test.skip('handles shows feedback for an already answered question', async () => {
// Answer the first question // Answer the first question
act(() => { act(() => {
fireEvent.click(screen.getByText('Option A')); fireEvent.click(screen.getByText('Option A'));
@ -67,13 +69,17 @@ describe('StudentModeQuiz', () => {
}); });
expect(mockSubmitAnswer).toHaveBeenCalledWith('Option A', 1); expect(mockSubmitAnswer).toHaveBeenCalledWith('Option A', 1);
const firstButtonA = screen.getByRole("button", {name: '✅ A Option A'}); await waitFor(() => {
expect(firstButtonA).toBeInTheDocument(); expect(localStorage.getItem('Answer1')).toBe(JSON.stringify('Option A'));
expect(firstButtonA.querySelector('.selected')).toBeInTheDocument(); });
expect(screen.getByRole("button", {name: '❌ B Option B'})).toBeInTheDocument();
expect(screen.queryByText('Répondre')).not.toBeInTheDocument(); expect(screen.queryByText('Répondre')).not.toBeInTheDocument();
// Simulate feedback display (e.g., a checkmark or feedback message)
// This part depends on how feedback is displayed in your component
// For example, if you display a checkmark, you can check for it:
expect(screen.getByText('✅')).toBeInTheDocument();
// Navigate to the next question // Navigate to the next question
act(() => { act(() => {
fireEvent.click(screen.getByText('Question suivante')); fireEvent.click(screen.getByText('Question suivante'));
@ -85,19 +91,9 @@ describe('StudentModeQuiz', () => {
act(() => { act(() => {
fireEvent.click(screen.getByText('Question précédente')); fireEvent.click(screen.getByText('Question précédente'));
}); });
expect(await screen.findByText('Sample Question 1')).toBeInTheDocument(); expect(screen.getByText('Sample Question 1')).toBeInTheDocument();
// Check if feedback is shown again
// Since answers are mocked, the it doesn't recognize the question as already answered expect(screen.getByText('✅')).toBeInTheDocument();
// TODO these tests are partially faked, need to be fixed if we can mock the answers
// const buttonA = screen.getByRole("button", {name: '✅ A Option A'});
const buttonA = screen.getByRole("button", {name: 'A Option A'});
expect(buttonA).toBeInTheDocument();
// const buttonB = screen.getByRole("button", {name: '❌ B Option B'});
const buttonB = screen.getByRole("button", {name: 'B Option B'});
expect(buttonB).toBeInTheDocument();
// // "Option A" div inside the name of button should have selected class
// expect(buttonA.querySelector('.selected')).toBeInTheDocument();
}); });
test('handles quit button click', async () => { test('handles quit button click', async () => {
@ -122,4 +118,4 @@ describe('StudentModeQuiz', () => {
expect(screen.getByText('Sample Question 2')).toBeInTheDocument(); expect(screen.getByText('Sample Question 2')).toBeInTheDocument();
expect(screen.getByText('Répondre')).toBeInTheDocument(); expect(screen.getByText('Répondre')).toBeInTheDocument();
}); });
}); });

View file

@ -7,7 +7,6 @@ import { BaseQuestion, MultipleChoiceQuestion, parse } from 'gift-pegjs';
import TeacherModeQuiz from 'src/components/TeacherModeQuiz/TeacherModeQuiz'; import TeacherModeQuiz from 'src/components/TeacherModeQuiz/TeacherModeQuiz';
import { MemoryRouter } from 'react-router-dom'; import { MemoryRouter } from 'react-router-dom';
import { QuestionType } from 'src/Types/QuestionType'; import { QuestionType } from 'src/Types/QuestionType';
import { AnswerSubmissionToBackendType } from 'src/services/WebsocketService';
const mockGiftQuestions = parse( const mockGiftQuestions = parse(
`::Sample Question 1:: Sample Question 1 {=Option A ~Option B} `::Sample Question 1:: Sample Question 1 {=Option A ~Option B}
@ -37,7 +36,6 @@ describe('TeacherModeQuiz', () => {
<MemoryRouter> <MemoryRouter>
<TeacherModeQuiz <TeacherModeQuiz
questionInfos={{ question: mockQuestion }} questionInfos={{ question: mockQuestion }}
answers={Array(mockQuestions.length).fill({} as AnswerSubmissionToBackendType)}
submitAnswer={mockSubmitAnswer} submitAnswer={mockSubmitAnswer}
disconnectWebSocket={mockDisconnectWebSocket} /> disconnectWebSocket={mockDisconnectWebSocket} />
</MemoryRouter> </MemoryRouter>
@ -82,7 +80,6 @@ describe('TeacherModeQuiz', () => {
<MemoryRouter> <MemoryRouter>
<TeacherModeQuiz <TeacherModeQuiz
questionInfos={{ question: mockQuestion }} questionInfos={{ question: mockQuestion }}
answers={Array(mockQuestions.length).fill({} as AnswerSubmissionToBackendType)}
submitAnswer={mockSubmitAnswer} submitAnswer={mockSubmitAnswer}
disconnectWebSocket={mockDisconnectWebSocket} disconnectWebSocket={mockDisconnectWebSocket}
/> />
@ -97,7 +94,6 @@ describe('TeacherModeQuiz', () => {
<MemoryRouter> <MemoryRouter>
<TeacherModeQuiz <TeacherModeQuiz
questionInfos={{ question: mockQuestion }} questionInfos={{ question: mockQuestion }}
answers={Array(mockQuestions.length).fill({} as AnswerSubmissionToBackendType)}
submitAnswer={mockSubmitAnswer} submitAnswer={mockSubmitAnswer}
disconnectWebSocket={mockDisconnectWebSocket} disconnectWebSocket={mockDisconnectWebSocket}
/> />

View file

@ -1,9 +1,7 @@
//WebsocketService.test.tsx //WebsocketService.test.tsx
import { BaseQuestion, parse } from 'gift-pegjs';
import WebsocketService from '../../services/WebsocketService'; import WebsocketService from '../../services/WebsocketService';
import { io, Socket } from 'socket.io-client'; import { io, Socket } from 'socket.io-client';
import { ENV_VARIABLES } from 'src/constants'; import { ENV_VARIABLES } from 'src/constants';
import { QuestionType } from 'src/Types/QuestionType';
jest.mock('socket.io-client'); jest.mock('socket.io-client');
@ -47,16 +45,10 @@ describe('WebSocketService', () => {
test('nextQuestion should emit next-question event with correct parameters', () => { test('nextQuestion should emit next-question event with correct parameters', () => {
const roomName = 'testRoom'; const roomName = 'testRoom';
const mockGiftQuestions = parse('A {T}'); const question = { id: 1, text: 'Sample Question' };
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); mockSocket = WebsocketService.connect(ENV_VARIABLES.VITE_BACKEND_URL);
WebsocketService.nextQuestion({roomName, questions: mockQuestions, questionIndex: 0, isLaunch: false}); WebsocketService.nextQuestion(roomName, question);
const question = mockQuestions[0];
expect(mockSocket.emit).toHaveBeenCalledWith('next-question', { roomName, question }); expect(mockSocket.emit).toHaveBeenCalledWith('next-question', { roomName, question });
}); });

View file

@ -7,36 +7,36 @@ import { Button } from '@mui/material';
//import QuestionNavigation from '../QuestionNavigation/QuestionNavigation'; //import QuestionNavigation from '../QuestionNavigation/QuestionNavigation';
import DisconnectButton from 'src/components/DisconnectButton/DisconnectButton'; import DisconnectButton from 'src/components/DisconnectButton/DisconnectButton';
import { Question } from 'gift-pegjs'; import { Question } from 'gift-pegjs';
import { AnswerSubmissionToBackendType } from 'src/services/WebsocketService';
interface StudentModeQuizProps { interface StudentModeQuizProps {
questions: QuestionType[]; questions: QuestionType[];
answers: AnswerSubmissionToBackendType[];
submitAnswer: (_answer: string | number | boolean, _idQuestion: number) => void; submitAnswer: (_answer: string | number | boolean, _idQuestion: number) => void;
disconnectWebSocket: () => void; disconnectWebSocket: () => void;
} }
const StudentModeQuiz: React.FC<StudentModeQuizProps> = ({ const StudentModeQuiz: React.FC<StudentModeQuizProps> = ({
questions, questions,
answers,
submitAnswer, submitAnswer,
disconnectWebSocket disconnectWebSocket
}) => { }) => {
//Ajouter type AnswerQuestionType en remplacement de QuestionType //Ajouter type AnswerQuestionType en remplacement de QuestionType
const [questionInfos, setQuestion] = useState<QuestionType>(questions[0]); const [questionInfos, setQuestion] = useState<QuestionType>(questions[0]);
const [isAnswerSubmitted, setIsAnswerSubmitted] = useState(false); const [isAnswerSubmitted, setIsAnswerSubmitted] = useState(false);
// const [answer, setAnswer] = useState<string | number | boolean>(''); const [answer, setAnswer] = useState<string | number | boolean>('');
const previousQuestion = () => { const previousQuestion = () => {
setQuestion(questions[Number(questionInfos.question?.id) - 2]); setQuestion(questions[Number(questionInfos.question?.id) - 2]);
}; };
useEffect(() => { useEffect(() => {
const savedAnswer = answers[Number(questionInfos.question.id)-1]?.answer; setAnswer(JSON.parse(localStorage.getItem(`Answer${questionInfos.question.id}`)||'null'));
console.log(`StudentModeQuiz: useEffect: savedAnswer: ${savedAnswer}`); if (answer !== null) {
setIsAnswerSubmitted(savedAnswer !== undefined); setIsAnswerSubmitted(true);
}, [questionInfos.question, answers]); } else {
setIsAnswerSubmitted(false);
}
}, [questionInfos.question , answer]);
const nextQuestion = () => { const nextQuestion = () => {
setQuestion(questions[Number(questionInfos.question?.id)]); setQuestion(questions[Number(questionInfos.question?.id)]);
@ -73,7 +73,7 @@ const StudentModeQuiz: React.FC<StudentModeQuizProps> = ({
handleOnSubmitAnswer={handleOnSubmitAnswer} handleOnSubmitAnswer={handleOnSubmitAnswer}
question={questionInfos.question as Question} question={questionInfos.question as Question}
showAnswer={isAnswerSubmitted} showAnswer={isAnswerSubmitted}
answer={answers[Number(questionInfos.question.id)-1]?.answer} answer={answer}
/> />
<div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', marginTop: '1rem' }}> <div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', marginTop: '1rem' }}>
<div> <div>

View file

@ -6,54 +6,39 @@ import { QuestionType } from '../../Types/QuestionType';
import DisconnectButton from 'src/components/DisconnectButton/DisconnectButton'; import DisconnectButton from 'src/components/DisconnectButton/DisconnectButton';
import { Dialog, DialogTitle, DialogContent, DialogActions, Button } from '@mui/material'; import { Dialog, DialogTitle, DialogContent, DialogActions, Button } from '@mui/material';
import { Question } from 'gift-pegjs'; 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 { interface TeacherModeQuizProps {
questionInfos: QuestionType; questionInfos: QuestionType;
answers: AnswerSubmissionToBackendType[];
submitAnswer: (_answer: string | number | boolean, _idQuestion: number) => void; submitAnswer: (_answer: string | number | boolean, _idQuestion: number) => void;
disconnectWebSocket: () => void; disconnectWebSocket: () => void;
} }
const TeacherModeQuiz: React.FC<TeacherModeQuizProps> = ({ const TeacherModeQuiz: React.FC<TeacherModeQuizProps> = ({
questionInfos, questionInfos,
answers,
submitAnswer, submitAnswer,
disconnectWebSocket disconnectWebSocket
}) => { }) => {
const [isAnswerSubmitted, setIsAnswerSubmitted] = useState(false); const [isAnswerSubmitted, setIsAnswerSubmitted] = useState(false);
const [isFeedbackDialogOpen, setIsFeedbackDialogOpen] = useState(false); const [isFeedbackDialogOpen, setIsFeedbackDialogOpen] = useState(false);
const [answer, setAnswer] = useState<AnswerType>(); const [answer, setAnswer] = useState<string | number | boolean>();
// 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(() => { useEffect(() => {
console.log(`TeacherModeQuiz: useEffect: isAnswerSubmitted: ${isAnswerSubmitted}`); // Close the feedback dialog when the question changes
setIsFeedbackDialogOpen(isAnswerSubmitted); handleFeedbackDialogClose();
}, [isAnswerSubmitted]); 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]);
const handleOnSubmitAnswer = (answer: string | number | boolean) => { const handleOnSubmitAnswer = (answer: string | number | boolean) => {
const idQuestion = Number(questionInfos.question.id) || -1; const idQuestion = Number(questionInfos.question.id) || -1;
submitAnswer(answer, idQuestion); submitAnswer(answer, idQuestion);
// setAnswer(answer); setAnswer(answer);
setIsFeedbackDialogOpen(true); setIsFeedbackDialogOpen(true);
}; };
@ -64,21 +49,21 @@ const TeacherModeQuiz: React.FC<TeacherModeQuizProps> = ({
return ( return (
<div className='room'> <div className='room'>
<div className='roomHeader'> <div className='roomHeader'>
<DisconnectButton <DisconnectButton
onReturn={disconnectWebSocket} onReturn={disconnectWebSocket}
message={`Êtes-vous sûr de vouloir quitter?`} /> message={`Êtes-vous sûr de vouloir quitter?`} />
<div className='centerTitle'>
<div className='title'>Question {questionInfos.question.id}</div>
</div>
<div className='dumb'></div>
<div className='centerTitle'>
<div className='title'>Question {questionInfos.question.id}</div>
</div> </div>
<div className='dumb'></div> {isAnswerSubmitted ? (
</div>
{isAnswerSubmitted ? (
<div> <div>
En attente pour la prochaine question... En attente pour la prochaine question...
</div> </div>
@ -86,7 +71,7 @@ const TeacherModeQuiz: React.FC<TeacherModeQuizProps> = ({
<QuestionComponent <QuestionComponent
handleOnSubmitAnswer={handleOnSubmitAnswer} handleOnSubmitAnswer={handleOnSubmitAnswer}
question={questionInfos.question as Question} question={questionInfos.question as Question}
answer={answer} answer={answer}
/> />
)} )}
@ -97,20 +82,20 @@ const TeacherModeQuiz: React.FC<TeacherModeQuizProps> = ({
<DialogTitle>Rétroaction</DialogTitle> <DialogTitle>Rétroaction</DialogTitle>
<DialogContent> <DialogContent>
<div style={{ <div style={{
wordWrap: 'break-word', wordWrap: 'break-word',
whiteSpace: 'pre-wrap', whiteSpace: 'pre-wrap',
maxHeight: '400px', maxHeight: '400px',
overflowY: 'auto', overflowY: 'auto',
}}> }}>
<div style={{ textAlign: 'left', fontWeight: 'bold', marginTop: '10px' }} <div style={{ textAlign: 'left', fontWeight: 'bold', marginTop: '10px'}}
>Question : </div> >Question : </div>
</div> </div>
<QuestionComponent <QuestionComponent
handleOnSubmitAnswer={handleOnSubmitAnswer} handleOnSubmitAnswer={handleOnSubmitAnswer}
question={questionInfos.question as Question} question={questionInfos.question as Question}
showAnswer={true} showAnswer={true}
answer={answer} answer={answer}
/> />
</DialogContent> </DialogContent>
@ -120,7 +105,7 @@ const TeacherModeQuiz: React.FC<TeacherModeQuizProps> = ({
</Button> </Button>
</DialogActions> </DialogActions>
</Dialog> </Dialog>
</div> </div>
); );
}; };

View file

@ -17,8 +17,6 @@ import LoginContainer from 'src/components/LoginContainer/LoginContainer'
import ApiService from '../../../services/ApiService' import ApiService from '../../../services/ApiService'
export type AnswerType = string | number | boolean;
const JoinRoom: React.FC = () => { const JoinRoom: React.FC = () => {
const [roomName, setRoomName] = useState(''); const [roomName, setRoomName] = useState('');
const [username, setUsername] = useState(ApiService.getUsername()); const [username, setUsername] = useState(ApiService.getUsername());
@ -27,7 +25,6 @@ const JoinRoom: React.FC = () => {
const [question, setQuestion] = useState<QuestionType>(); const [question, setQuestion] = useState<QuestionType>();
const [quizMode, setQuizMode] = useState<string>(); const [quizMode, setQuizMode] = useState<string>();
const [questions, setQuestions] = useState<QuestionType[]>([]); const [questions, setQuestions] = useState<QuestionType[]>([]);
const [answers, setAnswers] = useState<AnswerSubmissionToBackendType[]>([]);
const [connectionError, setConnectionError] = useState<string>(''); const [connectionError, setConnectionError] = useState<string>('');
const [isConnecting, setIsConnecting] = useState<boolean>(false); const [isConnecting, setIsConnecting] = useState<boolean>(false);
@ -38,13 +35,6 @@ 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 = () => { const handleCreateSocket = () => {
console.log(`JoinRoom: handleCreateSocket: ${ENV_VARIABLES.VITE_BACKEND_URL}`); console.log(`JoinRoom: handleCreateSocket: ${ENV_VARIABLES.VITE_BACKEND_URL}`);
const socket = webSocketService.connect(ENV_VARIABLES.VITE_BACKEND_URL); const socket = webSocketService.connect(ENV_VARIABLES.VITE_BACKEND_URL);
@ -55,18 +45,12 @@ const JoinRoom: React.FC = () => {
console.log(`on(join-success): Successfully joined the room ${roomJoinedName}`); console.log(`on(join-success): Successfully joined the room ${roomJoinedName}`);
}); });
socket.on('next-question', (question: QuestionType) => { socket.on('next-question', (question: QuestionType) => {
console.log('JoinRoom: on(next-question): Received next-question:', question); console.log('on(next-question): Received next-question:', question);
setQuizMode('teacher'); setQuizMode('teacher');
setIsWaitingForTeacher(false); setIsWaitingForTeacher(false);
setQuestion(question); 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[]) => { socket.on('launch-student-mode', (questions: QuestionType[]) => {
console.log('on(launch-student-mode): Received launch-student-mode:', questions); console.log('on(launch-student-mode): Received launch-student-mode:', questions);
@ -100,7 +84,7 @@ const JoinRoom: React.FC = () => {
}; };
const disconnect = () => { const disconnect = () => {
// localStorage.clear(); localStorage.clear();
webSocketService.disconnect(); webSocketService.disconnect();
setSocket(null); setSocket(null);
setQuestion(undefined); setQuestion(undefined);
@ -125,22 +109,14 @@ const JoinRoom: React.FC = () => {
} }
}; };
const handleOnSubmitAnswer = (answer: AnswerType, idQuestion: number) => { const handleOnSubmitAnswer = (answer: string | number | boolean, idQuestion: number) => {
console.info(`JoinRoom: handleOnSubmitAnswer: answer: ${answer}, idQuestion: ${idQuestion}`);
const answerData: AnswerSubmissionToBackendType = { const answerData: AnswerSubmissionToBackendType = {
roomName: roomName, roomName: roomName,
answer: answer, answer: answer,
username: username, username: username,
idQuestion: idQuestion 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); webSocketService.submitAnswer(answerData);
}; };
@ -178,7 +154,6 @@ const JoinRoom: React.FC = () => {
return ( return (
<StudentModeQuiz <StudentModeQuiz
questions={questions} questions={questions}
answers={answers}
submitAnswer={handleOnSubmitAnswer} submitAnswer={handleOnSubmitAnswer}
disconnectWebSocket={disconnect} disconnectWebSocket={disconnect}
/> />
@ -188,7 +163,6 @@ const JoinRoom: React.FC = () => {
question && ( question && (
<TeacherModeQuiz <TeacherModeQuiz
questionInfos={question} questionInfos={question}
answers={answers}
submitAnswer={handleOnSubmitAnswer} submitAnswer={handleOnSubmitAnswer}
disconnectWebSocket={disconnect} disconnectWebSocket={disconnect}
/> />

View file

@ -131,11 +131,7 @@ const ManageRoom: React.FC = () => {
if (!quizStarted) return; if (!quizStarted) return;
if (quizMode === 'teacher') { if (quizMode === 'teacher') {
webSocketService.nextQuestion( webSocketService.nextQuestion(formattedRoomName, currentQuestion);
{roomName: formattedRoomName,
questions: quizQuestions,
questionIndex: Number(currentQuestion?.question.id) - 1,
isLaunch: false});
} else if (quizMode === 'student') { } else if (quizMode === 'student') {
webSocketService.launchStudentModeQuiz(formattedRoomName, quizQuestions); webSocketService.launchStudentModeQuiz(formattedRoomName, quizQuestions);
} }
@ -228,10 +224,7 @@ 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(formattedRoomName, quizQuestions[nextQuestionIndex]);
questions: quizQuestions,
questionIndex: nextQuestionIndex,
isLaunch: false});
}; };
const previousQuestion = () => { const previousQuestion = () => {
@ -241,7 +234,7 @@ 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(formattedRoomName, quizQuestions[prevQuestionIndex]);
}; };
const initializeQuizQuestion = () => { const initializeQuizQuestion = () => {
@ -269,7 +262,7 @@ const ManageRoom: React.FC = () => {
} }
setCurrentQuestion(quizQuestions[0]); setCurrentQuestion(quizQuestions[0]);
webSocketService.nextQuestion({roomName: formattedRoomName, questions: quizQuestions, questionIndex: 0, isLaunch: true}); webSocketService.nextQuestion(formattedRoomName, quizQuestions[0]);
}; };
const launchStudentMode = () => { const launchStudentMode = () => {
@ -307,8 +300,9 @@ const ManageRoom: React.FC = () => {
const showSelectedQuestion = (questionIndex: number) => { const showSelectedQuestion = (questionIndex: number) => {
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(formattedRoomName, quizQuestions[questionIndex]);
} }
} }
}; };

View file

@ -1,5 +1,4 @@
import { io, Socket } from 'socket.io-client'; import { io, Socket } from 'socket.io-client';
import { QuestionType } from 'src/Types/QuestionType';
// Must (manually) sync these types to server/socket/socket.js // Must (manually) sync these types to server/socket/socket.js
@ -60,19 +59,12 @@ class WebSocketService {
// } // }
// } // }
nextQuestion(args: {roomName: string, questions: QuestionType[] | undefined, questionIndex: number, isLaunch: boolean}) { nextQuestion(roomName: string, question: unknown) {
// deconstruct args console.log('WebsocketService: nextQuestion', roomName, question);
const { roomName, questions, questionIndex, isLaunch } = args; if (!question) {
console.log('WebsocketService: nextQuestion', roomName, questions, questionIndex, isLaunch);
if (!questions || !questions[questionIndex]) {
throw new Error('WebsocketService: nextQuestion: question is null'); throw new Error('WebsocketService: nextQuestion: question is null');
} }
if (this.socket) { if (this.socket) {
if (isLaunch) {
this.socket.emit('launch-teacher-mode', { roomName, questions });
}
const question = questions[questionIndex];
this.socket.emit('next-question', { roomName, question }); this.socket.emit('next-question', { roomName, question });
} }
} }

View file

@ -109,27 +109,15 @@ describe("websocket server", () => {
}); });
}); });
test("should launch teacher mode", (done) => {
studentSocket.on("launch-teacher-mode", (questions) => {
expect(questions).toEqual([
{ question: "question1" },
{ question: "question2" },
]);
done();
});
teacherSocket.emit("launch-teacher-mode", {
roomName: "ROOM1",
questions: [{ question: "question1" }, { question: "question2" }],
});
});
test("should send next question", (done) => { test("should send next question", (done) => {
studentSocket.on("next-question", ( question ) => { studentSocket.on("next-question", (question) => {
expect(question).toBe("question2"); expect(question).toEqual({ question: "question2" });
done(); done();
}); });
teacherSocket.emit("next-question", { roomName: "ROOM1", question: 'question2'}, teacherSocket.emit("next-question", {
); roomName: "ROOM1",
question: { question: "question2" },
});
}); });
test("should send answer", (done) => { test("should send answer", (done) => {

View file

@ -81,10 +81,6 @@ const setupWebsocket = (io) => {
socket.to(roomName).emit("next-question", question); 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.on("launch-student-mode", ({ roomName, questions }) => {
socket.to(roomName).emit("launch-student-mode", questions); socket.to(roomName).emit("launch-student-mode", questions);
}); });