mirror of
https://github.com/ets-cfuhrman-pfe/EvalueTonSavoir.git
synced 2025-08-11 21:23:54 -04:00
Merge pull request #227 from ets-cfuhrman-pfe/JubaAzul/issue226
Some checks failed
CI/CD Pipeline for Backend / build_and_push_backend (push) Failing after 52s
CI/CD Pipeline for Nginx Router / build_and_push_nginx (push) Failing after 51s
CI/CD Pipeline for Frontend / build_and_push_frontend (push) Failing after 17s
Tests / tests (client) (push) Failing after 1m17s
Tests / tests (server) (push) Failing after 54s
Some checks failed
CI/CD Pipeline for Backend / build_and_push_backend (push) Failing after 52s
CI/CD Pipeline for Nginx Router / build_and_push_nginx (push) Failing after 51s
CI/CD Pipeline for Frontend / build_and_push_frontend (push) Failing after 17s
Tests / tests (client) (push) Failing after 1m17s
Tests / tests (server) (push) Failing after 54s
[Katex] La L'affichage du tableau AVEC réponses pose problème lorsqu'il y a des équations
This commit is contained in:
commit
80a31f5b38
2 changed files with 165 additions and 185 deletions
163
client/src/__tests__/components/LiveResults/LiveResults.test.tsx
Normal file
163
client/src/__tests__/components/LiveResults/LiveResults.test.tsx
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
import React from 'react';
|
||||
import { render, screen, fireEvent } from '@testing-library/react';
|
||||
import '@testing-library/jest-dom';
|
||||
import LiveResults from 'src/components/LiveResults/LiveResults';
|
||||
import { QuestionType } from 'src/Types/QuestionType';
|
||||
import { StudentType } from 'src/Types/StudentType';
|
||||
import { Socket } from 'socket.io-client';
|
||||
import { BaseQuestion,parse } from 'gift-pegjs';
|
||||
|
||||
const mockSocket: Socket = {
|
||||
on: jest.fn(),
|
||||
off: jest.fn(),
|
||||
emit: jest.fn(),
|
||||
connect: jest.fn(),
|
||||
disconnect: jest.fn(),
|
||||
} as unknown as Socket;
|
||||
|
||||
const mockGiftQuestions = parse(
|
||||
`::Sample Question 1:: Question stem
|
||||
{
|
||||
=Choice 1
|
||||
~Choice 2
|
||||
}`);
|
||||
|
||||
const mockQuestions: QuestionType[] = mockGiftQuestions.map((question, index) => {
|
||||
if (question.type !== "Category")
|
||||
question.id = (index + 1).toString();
|
||||
const newMockQuestion = question;
|
||||
return {question : newMockQuestion as BaseQuestion};
|
||||
});
|
||||
|
||||
const mockStudents: StudentType[] = [
|
||||
{ id: '1', name: 'Student 1', answers: [{ idQuestion: 1, answer: 'Choice 1', isCorrect: true }] },
|
||||
{ id: '2', name: 'Student 2', answers: [{ idQuestion: 1, answer: 'Choice 2', isCorrect: false }] },
|
||||
];
|
||||
|
||||
describe('LiveResults', () => {
|
||||
test('renders the component with questions and students', () => {
|
||||
render(
|
||||
<LiveResults
|
||||
socket={mockSocket}
|
||||
questions={mockQuestions}
|
||||
showSelectedQuestion={jest.fn()}
|
||||
quizMode="teacher"
|
||||
students={mockStudents}
|
||||
/>
|
||||
);
|
||||
expect(screen.getByText(`Q${1}`)).toBeInTheDocument();
|
||||
|
||||
// Toggle the display of usernames
|
||||
const toggleUsernamesSwitch = screen.getByLabelText('Afficher les noms');
|
||||
|
||||
// Toggle the display of usernames back
|
||||
fireEvent.click(toggleUsernamesSwitch);
|
||||
|
||||
// Check if the component renders the students
|
||||
mockStudents.forEach((student) => {
|
||||
expect(screen.getByText(student.name)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
test('toggles the display of usernames', () => {
|
||||
render(
|
||||
<LiveResults
|
||||
socket={mockSocket}
|
||||
questions={mockQuestions}
|
||||
showSelectedQuestion={jest.fn()}
|
||||
quizMode="teacher"
|
||||
students={mockStudents}
|
||||
/>
|
||||
);
|
||||
|
||||
// Toggle the display of usernames
|
||||
const toggleUsernamesSwitch = screen.getByLabelText('Afficher les noms');
|
||||
|
||||
// Toggle the display of usernames back
|
||||
fireEvent.click(toggleUsernamesSwitch);
|
||||
|
||||
// Check if the usernames are shown again
|
||||
mockStudents.forEach((student) => {
|
||||
expect(screen.getByText(student.name)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
test('calculates and displays the correct student grades', () => {
|
||||
render(
|
||||
<LiveResults
|
||||
socket={mockSocket}
|
||||
questions={mockQuestions}
|
||||
showSelectedQuestion={jest.fn()}
|
||||
quizMode="teacher"
|
||||
students={mockStudents}
|
||||
/>
|
||||
);
|
||||
|
||||
|
||||
// Toggle the display of usernames
|
||||
const toggleUsernamesSwitch = screen.getByLabelText('Afficher les noms');
|
||||
|
||||
// Toggle the display of usernames back
|
||||
fireEvent.click(toggleUsernamesSwitch);
|
||||
|
||||
// Check if the student grades are calculated and displayed correctly
|
||||
mockStudents.forEach((student) => {
|
||||
const grade = student.answers.filter(answer => answer.isCorrect).length / mockQuestions.length * 100;
|
||||
expect(screen.getByText(`${grade.toFixed()} %`)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
test('calculates and displays the class average', () => {
|
||||
render(
|
||||
<LiveResults
|
||||
socket={mockSocket}
|
||||
questions={mockQuestions}
|
||||
showSelectedQuestion={jest.fn()}
|
||||
quizMode="teacher"
|
||||
students={mockStudents}
|
||||
/>
|
||||
);
|
||||
|
||||
// Toggle the display of usernames
|
||||
const toggleUsernamesSwitch = screen.getByLabelText('Afficher les noms');
|
||||
|
||||
// Toggle the display of usernames back
|
||||
fireEvent.click(toggleUsernamesSwitch);
|
||||
|
||||
// Calculate the class average
|
||||
const totalGrades = mockStudents.reduce((total, student) => {
|
||||
return total + (student.answers.filter(answer => answer.isCorrect).length / mockQuestions.length * 100);
|
||||
}, 0);
|
||||
const classAverage = totalGrades / mockStudents.length;
|
||||
|
||||
// Check if the class average is displayed correctly
|
||||
const classAverageElements = screen.getAllByText(`${classAverage.toFixed()} %`);
|
||||
const classAverageElement = classAverageElements.find((element) => {
|
||||
return element.closest('td')?.classList.contains('MuiTableCell-footer');
|
||||
});
|
||||
expect(classAverageElement).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('displays the correct answers per question', () => {
|
||||
render(
|
||||
<LiveResults
|
||||
socket={mockSocket}
|
||||
questions={mockQuestions}
|
||||
showSelectedQuestion={jest.fn()}
|
||||
quizMode="teacher"
|
||||
students={mockStudents}
|
||||
/>
|
||||
);
|
||||
|
||||
// Check if the correct answers per question are displayed correctly
|
||||
mockQuestions.forEach((_, index) => {
|
||||
const correctAnswers = mockStudents.filter(student => student.answers.some(answer => answer.idQuestion === index + 1 && answer.isCorrect)).length;
|
||||
const correctAnswersPercentage = (correctAnswers / mockStudents.length) * 100;
|
||||
const correctAnswersElements = screen.getAllByText(`${correctAnswersPercentage.toFixed()} %`);
|
||||
const correctAnswersElement = correctAnswersElements.find((element) => {
|
||||
return element.closest('td')?.classList.contains('MuiTableCell-root');
|
||||
});
|
||||
expect(correctAnswersElement).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
|
@ -20,7 +20,7 @@ import {
|
|||
TableRow
|
||||
} from '@mui/material';
|
||||
import { StudentType } from '../../Types/StudentType';
|
||||
import { formatLatex } from '../GiftTemplate/templates/TextTypeTemplate';
|
||||
import { FormattedTextTemplate } from '../GiftTemplate/templates/TextTypeTemplate';
|
||||
|
||||
interface LiveResultsProps {
|
||||
socket: Socket | null;
|
||||
|
|
@ -30,117 +30,13 @@ interface LiveResultsProps {
|
|||
students: StudentType[]
|
||||
}
|
||||
|
||||
// interface Answer {
|
||||
// answer: string | number | boolean;
|
||||
// isCorrect: boolean;
|
||||
// idQuestion: number;
|
||||
// }
|
||||
|
||||
// interface StudentResult {
|
||||
// username: string;
|
||||
// idUser: string;
|
||||
// answers: Answer[];
|
||||
// }
|
||||
|
||||
const LiveResults: React.FC<LiveResultsProps> = ({ questions, showSelectedQuestion, students }) => {
|
||||
const [showUsernames, setShowUsernames] = useState<boolean>(false);
|
||||
const [showCorrectAnswers, setShowCorrectAnswers] = useState<boolean>(false);
|
||||
// const [students, setStudents] = useState<StudentType[]>(initialStudents);
|
||||
// const [studentResultsMap, setStudentResultsMap] = useState<Map<string, StudentResult>>(new Map());
|
||||
|
||||
const maxQuestions = questions.length;
|
||||
|
||||
// useEffect(() => {
|
||||
// // Initialize the map with the current students
|
||||
// const newStudentResultsMap = new Map<string, StudentResult>();
|
||||
|
||||
// for (const student of students) {
|
||||
// newStudentResultsMap.set(student.id, { username: student.name, idUser: student.id, answers: [] });
|
||||
// }
|
||||
|
||||
// setStudentResultsMap(newStudentResultsMap);
|
||||
// }, [])
|
||||
|
||||
// update when students change
|
||||
// useEffect(() => {
|
||||
// // studentResultsMap is inconsistent with students -- need to update
|
||||
|
||||
// for (const student of students as StudentType[]) {
|
||||
// }
|
||||
|
||||
// }, [students])
|
||||
|
||||
// useEffect(() => {
|
||||
// if (socket) {
|
||||
// const submitAnswerHandler = ({
|
||||
// idUser,
|
||||
// answer,
|
||||
// idQuestion
|
||||
// }: {
|
||||
// idUser: string;
|
||||
// username: string;
|
||||
// answer: string | number | boolean;
|
||||
// idQuestion: number;
|
||||
// }) => {
|
||||
// console.log(`Received answer from ${idUser} for question ${idQuestion}: ${answer}`);
|
||||
|
||||
// // print the list of current student names
|
||||
// console.log('Current students:');
|
||||
// students.forEach((student) => {
|
||||
// console.log(student.name);
|
||||
// });
|
||||
|
||||
// // Update the students state using the functional form of setStudents
|
||||
// setStudents((prevStudents) => {
|
||||
// let foundStudent = false;
|
||||
// const updatedStudents = prevStudents.map((student) => {
|
||||
// if (student.id === idUser) {
|
||||
// foundStudent = true;
|
||||
// const updatedAnswers = student.answers.map((ans) => {
|
||||
// const newAnswer: Answer = { answer, isCorrect: checkIfIsCorrect(answer, idQuestion), idQuestion };
|
||||
// console.log(`Updating answer for ${student.name} for question ${idQuestion} to ${answer}`);
|
||||
// return (ans.idQuestion === idQuestion ? { ...ans, newAnswer } : ans);
|
||||
// }
|
||||
// );
|
||||
// return { ...student, answers: updatedAnswers };
|
||||
// }
|
||||
// return student;
|
||||
// });
|
||||
// if (!foundStudent) {
|
||||
// console.log(`Student ${idUser} not found in the list of students in LiveResults`);
|
||||
// }
|
||||
// return updatedStudents;
|
||||
// });
|
||||
|
||||
|
||||
// // make a copy of the students array so we can update it
|
||||
// // const updatedStudents = [...students];
|
||||
|
||||
// // const student = updatedStudents.find((student) => student.id === idUser);
|
||||
// // if (!student) {
|
||||
// // // this is a bad thing if an answer was submitted but the student isn't in the list
|
||||
// // console.log(`Student ${idUser} not found in the list of students in LiveResults`);
|
||||
// // return;
|
||||
// // }
|
||||
|
||||
// // const isCorrect = checkIfIsCorrect(answer, idQuestion);
|
||||
// // const newAnswer: Answer = { answer, isCorrect, idQuestion };
|
||||
// // student.answers.push(newAnswer);
|
||||
// // // print list of answers
|
||||
// // console.log('Answers:');
|
||||
// // student.answers.forEach((answer) => {
|
||||
// // console.log(answer.answer);
|
||||
// // });
|
||||
// // setStudents(updatedStudents); // update the state
|
||||
// };
|
||||
|
||||
// socket.on('submit-answer', submitAnswerHandler);
|
||||
// return () => {
|
||||
// socket.off('submit-answer');
|
||||
// };
|
||||
// }
|
||||
// }, [socket]);
|
||||
|
||||
const getStudentGrade = (student: StudentType): number => {
|
||||
if (student.answers.length === 0) {
|
||||
return 0;
|
||||
|
|
@ -185,85 +81,6 @@ const LiveResults: React.FC<LiveResultsProps> = ({ questions, showSelectedQuesti
|
|||
);
|
||||
};
|
||||
|
||||
// (studentResults.filter((student) =>
|
||||
// student.answers.some(
|
||||
// (answer) =>
|
||||
// parseInt(answer.idQuestion.toString()) === index + 1 && answer.isCorrect
|
||||
// )
|
||||
// ).length /
|
||||
// studentResults.length) *
|
||||
// 100
|
||||
// );
|
||||
// };
|
||||
|
||||
// function checkIfIsCorrect(answer: string | number | boolean, idQuestion: number): boolean {
|
||||
// const questionInfo = questions.find((q) =>
|
||||
// q.question.id ? q.question.id === idQuestion.toString() : false
|
||||
// ) as QuestionType | undefined;
|
||||
|
||||
// const answerText = answer.toString();
|
||||
// if (questionInfo) {
|
||||
// const question = questionInfo.question as GIFTQuestion;
|
||||
// if (question.type === 'TF') {
|
||||
// return (
|
||||
// (question.isTrue && answerText == 'true') ||
|
||||
// (!question.isTrue && answerText == 'false')
|
||||
// );
|
||||
// } else if (question.type === 'MC') {
|
||||
// return question.choices.some(
|
||||
// (choice) => choice.isCorrect && choice.text.text === answerText
|
||||
// );
|
||||
// } else if (question.type === 'Numerical') {
|
||||
// if (question.choices && !Array.isArray(question.choices)) {
|
||||
// if (
|
||||
// question.choices.type === 'high-low' &&
|
||||
// question.choices.numberHigh &&
|
||||
// question.choices.numberLow
|
||||
// ) {
|
||||
// const answerNumber = parseFloat(answerText);
|
||||
// if (!isNaN(answerNumber)) {
|
||||
// return (
|
||||
// answerNumber <= question.choices.numberHigh &&
|
||||
// answerNumber >= question.choices.numberLow
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// if (question.choices && Array.isArray(question.choices)) {
|
||||
// if (
|
||||
// question.choices[0].text.type === 'range' &&
|
||||
// question.choices[0].text.number &&
|
||||
// question.choices[0].text.range
|
||||
// ) {
|
||||
// const answerNumber = parseFloat(answerText);
|
||||
// const range = question.choices[0].text.range;
|
||||
// const correctAnswer = question.choices[0].text.number;
|
||||
// if (!isNaN(answerNumber)) {
|
||||
// return (
|
||||
// answerNumber <= correctAnswer + range &&
|
||||
// answerNumber >= correctAnswer - range
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
// if (
|
||||
// question.choices[0].text.type === 'simple' &&
|
||||
// question.choices[0].text.number
|
||||
// ) {
|
||||
// const answerNumber = parseFloat(answerText);
|
||||
// if (!isNaN(answerNumber)) {
|
||||
// return answerNumber === question.choices[0].text.number;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// } else if (question.type === 'Short') {
|
||||
// return question.choices.some(
|
||||
// (choice) => choice.text.text.toUpperCase() === answerText.toUpperCase()
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
// return false;
|
||||
// }
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="action-bar mb-1">
|
||||
|
|
@ -370,7 +187,7 @@ const LiveResults: React.FC<LiveResultsProps> = ({ questions, showSelectedQuesti
|
|||
}
|
||||
>
|
||||
{showCorrectAnswers ? (
|
||||
<div>{formatLatex(answerText)}</div>
|
||||
<div dangerouslySetInnerHTML={{ __html:FormattedTextTemplate({ format: '', text: answerText }) }}></div>
|
||||
) : isCorrect ? (
|
||||
<FontAwesomeIcon icon={faCheck} />
|
||||
) : (
|
||||
|
|
|
|||
Loading…
Reference in a new issue