mirror of
https://github.com/ets-cfuhrman-pfe/EvalueTonSavoir.git
synced 2025-08-11 21:23:54 -04:00
Merge pull request #248 from ets-cfuhrman-pfe/fuhrmanator/issue212
Some checks failed
CI/CD Pipeline for Backend / build_and_push_backend (push) Failing after 1m15s
CI/CD Pipeline for Nginx Router / build_and_push_nginx (push) Failing after 56s
CI/CD Pipeline for Frontend / build_and_push_frontend (push) Failing after 18s
Tests / tests (client) (push) Failing after 1m29s
Tests / tests (server) (push) Failing after 52s
Some checks failed
CI/CD Pipeline for Backend / build_and_push_backend (push) Failing after 1m15s
CI/CD Pipeline for Nginx Router / build_and_push_nginx (push) Failing after 56s
CI/CD Pipeline for Frontend / build_and_push_frontend (push) Failing after 18s
Tests / tests (client) (push) Failing after 1m29s
Tests / tests (server) (push) Failing after 52s
Fuhrmanator/issue212
This commit is contained in:
commit
8240de0a44
10 changed files with 186 additions and 141 deletions
|
|
@ -3,49 +3,87 @@ import { render, screen } from '@testing-library/react';
|
||||||
import '@testing-library/jest-dom';
|
import '@testing-library/jest-dom';
|
||||||
import GIFTTemplatePreview from 'src/components/GiftTemplate/GIFTTemplatePreview';
|
import GIFTTemplatePreview from 'src/components/GiftTemplate/GIFTTemplatePreview';
|
||||||
|
|
||||||
|
const validQuestions = [
|
||||||
|
'::TFTitle::[markdown]Troo statement {TRUE}',
|
||||||
|
'::SATitle::[markdown]What is the answer? {=ShortAnswerOne =ShortAnswerTwo}',
|
||||||
|
'::MCQTitle::[markdown]MultiChoice question? {=MQAnswerOne ~MQAnswerTwo#feedback####Gen feedback}',
|
||||||
|
];
|
||||||
|
|
||||||
|
const unsupportedQuestions = [
|
||||||
|
'::Title::[markdown]Essay {}',
|
||||||
|
'::Title::[markdown]Matching {}',
|
||||||
|
'::Title::[markdown]Description',
|
||||||
|
'$CATEGORY a/b/c'
|
||||||
|
];
|
||||||
|
|
||||||
describe('GIFTTemplatePreview Component', () => {
|
describe('GIFTTemplatePreview Component', () => {
|
||||||
test('renders error message when questions contain invalid syntax', () => {
|
it('renders error message when questions contain invalid syntax', () => {
|
||||||
render(<GIFTTemplatePreview questions={['Invalid GIFT syntax']} />);
|
render(<GIFTTemplatePreview questions={['T{']} hideAnswers={false} />);
|
||||||
const errorMessage = screen.findByText(/Erreur inconnue/i, {}, { timeout: 5000 });
|
|
||||||
expect(errorMessage).resolves.toBeInTheDocument();
|
|
||||||
});
|
|
||||||
test('renders preview when valid questions are provided', () => {
|
|
||||||
const questions = [
|
|
||||||
'Question 1 { A | B | C }',
|
|
||||||
'Question 2 { D | E | F }',
|
|
||||||
];
|
|
||||||
render(<GIFTTemplatePreview questions={questions} />);
|
|
||||||
const previewContainer = screen.getByTestId('preview-container');
|
const previewContainer = screen.getByTestId('preview-container');
|
||||||
expect(previewContainer).toBeInTheDocument();
|
expect(previewContainer).toBeInTheDocument();
|
||||||
|
const errorMessage = previewContainer.querySelector('div[label="error-message"]');
|
||||||
|
expect(errorMessage).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
test('hides answers when hideAnswers prop is true', () => {
|
|
||||||
const questions = [
|
it('renders preview when valid questions are provided, including answers, has no errors', () => {
|
||||||
'Question 1 { A | B | C }',
|
render(<GIFTTemplatePreview questions={validQuestions} hideAnswers={false} />);
|
||||||
'Question 2 { D | E | F }',
|
|
||||||
];
|
|
||||||
render(<GIFTTemplatePreview questions={questions} hideAnswers />);
|
|
||||||
const previewContainer = screen.getByTestId('preview-container');
|
const previewContainer = screen.getByTestId('preview-container');
|
||||||
expect(previewContainer).toBeInTheDocument();
|
expect(previewContainer).toBeInTheDocument();
|
||||||
|
// Check that all question titles are rendered inside the previewContainer
|
||||||
|
validQuestions.forEach((question) => {
|
||||||
|
const title = question.split('::')[1].split('::')[0];
|
||||||
|
expect(previewContainer).toHaveTextContent(title);
|
||||||
});
|
});
|
||||||
// it('renders images correctly', () => {
|
// There should be no errors
|
||||||
// const questions = [
|
const errorMessage = previewContainer.querySelector('div[label="error-message"]');
|
||||||
// 'Question 1',
|
expect(errorMessage).not.toBeInTheDocument();
|
||||||
// '<img src="image1.jpg" alt="Image 1">',
|
// Check that some stems and answers are rendered inside the previewContainer
|
||||||
// 'Question 2',
|
expect(previewContainer).toHaveTextContent('Troo statement');
|
||||||
// '<img src="image2.jpg" alt="Image 2">',
|
expect(previewContainer).toHaveTextContent('What is the answer?');
|
||||||
// ];
|
expect(previewContainer).toHaveTextContent('MultiChoice question?');
|
||||||
// const { getByAltText } = render(<GIFTTemplatePreview questions={questions} />);
|
expect(previewContainer).toHaveTextContent('Vrai');
|
||||||
// const image1 = getByAltText('Image 1');
|
// short answers are stored in a textbox
|
||||||
// const image2 = getByAltText('Image 2');
|
const answerInputElements = screen.getAllByRole('textbox');
|
||||||
// expect(image1).toBeInTheDocument();
|
const giftInputElements = answerInputElements.filter(element => element.classList.contains('gift-input'));
|
||||||
// expect(image2).toBeInTheDocument();
|
|
||||||
// });
|
expect(giftInputElements).toHaveLength(1);
|
||||||
// it('renders non-images correctly', () => {
|
expect(giftInputElements[0]).toHaveAttribute('placeholder', 'ShortAnswerOne, ShortAnswerTwo');
|
||||||
// const questions = ['Question 1', 'Question 2'];
|
|
||||||
// const { queryByAltText } = render(<GIFTTemplatePreview questions={questions} />);
|
// Check for correct answer icon just after MQAnswerOne
|
||||||
// const image1 = queryByAltText('Image 1');
|
const mqAnswerOneElement = screen.getByText('MQAnswerOne');
|
||||||
// const image2 = queryByAltText('Image 2');
|
const correctAnswerIcon = mqAnswerOneElement.parentElement?.querySelector('[data-testid="correct-icon"]');
|
||||||
// expect(image1).toBeNull();
|
expect(correctAnswerIcon).toBeInTheDocument();
|
||||||
// expect(image2).toBeNull();
|
|
||||||
// });
|
// Check for incorrect answer icon just after MQAnswerTwo
|
||||||
|
const mqAnswerTwoElement = screen.getByText('MQAnswerTwo');
|
||||||
|
const incorrectAnswerIcon = mqAnswerTwoElement.parentElement?.querySelector('[data-testid="incorrect-icon"]');
|
||||||
|
expect(incorrectAnswerIcon).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('hides answers when hideAnswers prop is true', () => {
|
||||||
|
render(<GIFTTemplatePreview questions={validQuestions} hideAnswers={true} />);
|
||||||
|
const previewContainer = screen.getByTestId('preview-container');
|
||||||
|
expect(previewContainer).toBeInTheDocument();
|
||||||
|
expect(previewContainer).toHaveTextContent('Troo statement');
|
||||||
|
expect(previewContainer).toHaveTextContent('What is the answer?');
|
||||||
|
expect(previewContainer).toHaveTextContent('MultiChoice question?');
|
||||||
|
expect(previewContainer).toHaveTextContent('Vrai');
|
||||||
|
expect(previewContainer).not.toHaveTextContent('ShortAnswerOne');
|
||||||
|
expect(previewContainer).not.toHaveTextContent('ShortAnswerTwo');
|
||||||
|
// shouldn't have correct/incorrect icons
|
||||||
|
const correctAnswerIcon = screen.queryByTestId('correct-icon');
|
||||||
|
expect(correctAnswerIcon).not.toBeInTheDocument();
|
||||||
|
const incorrectAnswerIcon = screen.queryByTestId('incorrect-icon');
|
||||||
|
expect(incorrectAnswerIcon).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should indicate in the preview that unsupported GIFT questions are not supported', () => {
|
||||||
|
render(<GIFTTemplatePreview questions={unsupportedQuestions} hideAnswers={false} />);
|
||||||
|
const previewContainer = screen.getByTestId('preview-container');
|
||||||
|
expect(previewContainer).toBeInTheDocument();
|
||||||
|
// find all unsupported errors (should be 4)
|
||||||
|
const unsupportedMessages = previewContainer.querySelectorAll('div[label="error-message"]');
|
||||||
|
expect(unsupportedMessages).toHaveLength(4);
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@ exports[`MultipleChoice snapshot test 1`] = `
|
||||||
" for="idmocked-id">
|
" for="idmocked-id">
|
||||||
Choice 1
|
Choice 1
|
||||||
</label>
|
</label>
|
||||||
<svg style="
|
<svg data-testid="correct-icon" style="
|
||||||
vertical-align: text-bottom;
|
vertical-align: text-bottom;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin-left: 0.1rem;
|
margin-left: 0.1rem;
|
||||||
|
|
@ -88,7 +88,7 @@ exports[`MultipleChoice snapshot test 1`] = `
|
||||||
" for="idmocked-id">
|
" for="idmocked-id">
|
||||||
Choice 2
|
Choice 2
|
||||||
</label>
|
</label>
|
||||||
<svg style="
|
<svg data-testid="incorrect-icon" style="
|
||||||
vertical-align: text-bottom;
|
vertical-align: text-bottom;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin-left: 0.1rem;
|
margin-left: 0.1rem;
|
||||||
|
|
@ -180,7 +180,7 @@ exports[`MultipleChoice snapshot test with 2 images using markdown text format 1
|
||||||
Choice 1
|
Choice 1
|
||||||
|
|
||||||
</label>
|
</label>
|
||||||
<svg style="
|
<svg data-testid="correct-icon" style="
|
||||||
vertical-align: text-bottom;
|
vertical-align: text-bottom;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin-left: 0.1rem;
|
margin-left: 0.1rem;
|
||||||
|
|
@ -205,7 +205,7 @@ exports[`MultipleChoice snapshot test with 2 images using markdown text format 1
|
||||||
Choice 2
|
Choice 2
|
||||||
|
|
||||||
</label>
|
</label>
|
||||||
<svg style="
|
<svg data-testid="incorrect-icon" style="
|
||||||
vertical-align: text-bottom;
|
vertical-align: text-bottom;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin-left: 0.1rem;
|
margin-left: 0.1rem;
|
||||||
|
|
@ -229,7 +229,7 @@ exports[`MultipleChoice snapshot test with 2 images using markdown text format 1
|
||||||
" for="idmocked-id">
|
" for="idmocked-id">
|
||||||
<img alt="Sample Image" src="https://via.placeholder.com/150">
|
<img alt="Sample Image" src="https://via.placeholder.com/150">
|
||||||
</label>
|
</label>
|
||||||
<svg style="
|
<svg data-testid="incorrect-icon" style="
|
||||||
vertical-align: text-bottom;
|
vertical-align: text-bottom;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin-left: 0.1rem;
|
margin-left: 0.1rem;
|
||||||
|
|
@ -321,7 +321,7 @@ exports[`MultipleChoice snapshot test with Moodle text format 1`] = `
|
||||||
" for="idmocked-id">
|
" for="idmocked-id">
|
||||||
Choice 1
|
Choice 1
|
||||||
</label>
|
</label>
|
||||||
<svg style="
|
<svg data-testid="correct-icon" style="
|
||||||
vertical-align: text-bottom;
|
vertical-align: text-bottom;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin-left: 0.1rem;
|
margin-left: 0.1rem;
|
||||||
|
|
@ -345,7 +345,7 @@ exports[`MultipleChoice snapshot test with Moodle text format 1`] = `
|
||||||
" for="idmocked-id">
|
" for="idmocked-id">
|
||||||
Choice 2
|
Choice 2
|
||||||
</label>
|
</label>
|
||||||
<svg style="
|
<svg data-testid="incorrect-icon" style="
|
||||||
vertical-align: text-bottom;
|
vertical-align: text-bottom;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin-left: 0.1rem;
|
margin-left: 0.1rem;
|
||||||
|
|
@ -436,7 +436,7 @@ exports[`MultipleChoice snapshot test with image 1`] = `
|
||||||
" for="idmocked-id">
|
" for="idmocked-id">
|
||||||
Choice 1
|
Choice 1
|
||||||
</label>
|
</label>
|
||||||
<svg style="
|
<svg data-testid="correct-icon" style="
|
||||||
vertical-align: text-bottom;
|
vertical-align: text-bottom;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin-left: 0.1rem;
|
margin-left: 0.1rem;
|
||||||
|
|
@ -460,7 +460,7 @@ exports[`MultipleChoice snapshot test with image 1`] = `
|
||||||
" for="idmocked-id">
|
" for="idmocked-id">
|
||||||
Choice 2
|
Choice 2
|
||||||
</label>
|
</label>
|
||||||
<svg style="
|
<svg data-testid="incorrect-icon" style="
|
||||||
vertical-align: text-bottom;
|
vertical-align: text-bottom;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin-left: 0.1rem;
|
margin-left: 0.1rem;
|
||||||
|
|
@ -484,7 +484,7 @@ exports[`MultipleChoice snapshot test with image 1`] = `
|
||||||
" for="idmocked-id">
|
" for="idmocked-id">
|
||||||
<img alt="Sample Image" src="https://via.placeholder.com/150">
|
<img alt="Sample Image" src="https://via.placeholder.com/150">
|
||||||
</label>
|
</label>
|
||||||
<svg style="
|
<svg data-testid="incorrect-icon" style="
|
||||||
vertical-align: text-bottom;
|
vertical-align: text-bottom;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin-left: 0.1rem;
|
margin-left: 0.1rem;
|
||||||
|
|
@ -577,7 +577,7 @@ exports[`MultipleChoice snapshot test with image using markdown text format 1`]
|
||||||
Choice 1
|
Choice 1
|
||||||
|
|
||||||
</label>
|
</label>
|
||||||
<svg style="
|
<svg data-testid="correct-icon" style="
|
||||||
vertical-align: text-bottom;
|
vertical-align: text-bottom;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin-left: 0.1rem;
|
margin-left: 0.1rem;
|
||||||
|
|
@ -602,7 +602,7 @@ exports[`MultipleChoice snapshot test with image using markdown text format 1`]
|
||||||
Choice 2
|
Choice 2
|
||||||
|
|
||||||
</label>
|
</label>
|
||||||
<svg style="
|
<svg data-testid="incorrect-icon" style="
|
||||||
vertical-align: text-bottom;
|
vertical-align: text-bottom;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin-left: 0.1rem;
|
margin-left: 0.1rem;
|
||||||
|
|
@ -626,7 +626,7 @@ exports[`MultipleChoice snapshot test with image using markdown text format 1`]
|
||||||
" for="idmocked-id">
|
" for="idmocked-id">
|
||||||
<img alt="Sample Image" src="https://via.placeholder.com/150">
|
<img alt="Sample Image" src="https://via.placeholder.com/150">
|
||||||
</label>
|
</label>
|
||||||
<svg style="
|
<svg data-testid="incorrect-icon" style="
|
||||||
vertical-align: text-bottom;
|
vertical-align: text-bottom;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin-left: 0.1rem;
|
margin-left: 0.1rem;
|
||||||
|
|
@ -718,7 +718,7 @@ exports[`MultipleChoice snapshot test with katex 1`] = `
|
||||||
" for="idmocked-id">
|
" for="idmocked-id">
|
||||||
Choice 1
|
Choice 1
|
||||||
</label>
|
</label>
|
||||||
<svg style="
|
<svg data-testid="correct-icon" style="
|
||||||
vertical-align: text-bottom;
|
vertical-align: text-bottom;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin-left: 0.1rem;
|
margin-left: 0.1rem;
|
||||||
|
|
@ -742,7 +742,7 @@ exports[`MultipleChoice snapshot test with katex 1`] = `
|
||||||
" for="idmocked-id">
|
" for="idmocked-id">
|
||||||
Choice 2
|
Choice 2
|
||||||
</label>
|
</label>
|
||||||
<svg style="
|
<svg data-testid="correct-icon" style="
|
||||||
vertical-align: text-bottom;
|
vertical-align: text-bottom;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin-left: 0.1rem;
|
margin-left: 0.1rem;
|
||||||
|
|
@ -833,7 +833,7 @@ exports[`MultipleChoice snapshot test with katex, using html text format 1`] = `
|
||||||
" for="idmocked-id">
|
" for="idmocked-id">
|
||||||
Choice 1
|
Choice 1
|
||||||
</label>
|
</label>
|
||||||
<svg style="
|
<svg data-testid="correct-icon" style="
|
||||||
vertical-align: text-bottom;
|
vertical-align: text-bottom;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin-left: 0.1rem;
|
margin-left: 0.1rem;
|
||||||
|
|
@ -857,7 +857,7 @@ exports[`MultipleChoice snapshot test with katex, using html text format 1`] = `
|
||||||
" for="idmocked-id">
|
" for="idmocked-id">
|
||||||
Choice 2
|
Choice 2
|
||||||
</label>
|
</label>
|
||||||
<svg style="
|
<svg data-testid="incorrect-icon" style="
|
||||||
vertical-align: text-bottom;
|
vertical-align: text-bottom;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin-left: 0.1rem;
|
margin-left: 0.1rem;
|
||||||
|
|
|
||||||
|
|
@ -150,7 +150,7 @@ exports[`TrueFalse snapshot test with katex 1`] = `
|
||||||
" for="idmocked-id">
|
" for="idmocked-id">
|
||||||
Vrai
|
Vrai
|
||||||
</label>
|
</label>
|
||||||
<svg style="
|
<svg data-testid="correct-icon" style="
|
||||||
vertical-align: text-bottom;
|
vertical-align: text-bottom;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin-left: 0.1rem;
|
margin-left: 0.1rem;
|
||||||
|
|
@ -174,7 +174,7 @@ exports[`TrueFalse snapshot test with katex 1`] = `
|
||||||
" for="idmocked-id">
|
" for="idmocked-id">
|
||||||
Faux
|
Faux
|
||||||
</label>
|
</label>
|
||||||
<svg style="
|
<svg data-testid="incorrect-icon" style="
|
||||||
vertical-align: text-bottom;
|
vertical-align: text-bottom;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin-left: 0.1rem;
|
margin-left: 0.1rem;
|
||||||
|
|
@ -265,7 +265,7 @@ exports[`TrueFalse snapshot test with moodle 1`] = `
|
||||||
" for="idmocked-id">
|
" for="idmocked-id">
|
||||||
Vrai
|
Vrai
|
||||||
</label>
|
</label>
|
||||||
<svg style="
|
<svg data-testid="correct-icon" style="
|
||||||
vertical-align: text-bottom;
|
vertical-align: text-bottom;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin-left: 0.1rem;
|
margin-left: 0.1rem;
|
||||||
|
|
@ -289,7 +289,7 @@ exports[`TrueFalse snapshot test with moodle 1`] = `
|
||||||
" for="idmocked-id">
|
" for="idmocked-id">
|
||||||
Faux
|
Faux
|
||||||
</label>
|
</label>
|
||||||
<svg style="
|
<svg data-testid="incorrect-icon" style="
|
||||||
vertical-align: text-bottom;
|
vertical-align: text-bottom;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin-left: 0.1rem;
|
margin-left: 0.1rem;
|
||||||
|
|
@ -380,7 +380,7 @@ exports[`TrueFalse snapshot test with plain text 1`] = `
|
||||||
" for="idmocked-id">
|
" for="idmocked-id">
|
||||||
Vrai
|
Vrai
|
||||||
</label>
|
</label>
|
||||||
<svg style="
|
<svg data-testid="correct-icon" style="
|
||||||
vertical-align: text-bottom;
|
vertical-align: text-bottom;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin-left: 0.1rem;
|
margin-left: 0.1rem;
|
||||||
|
|
@ -404,7 +404,7 @@ exports[`TrueFalse snapshot test with plain text 1`] = `
|
||||||
" for="idmocked-id">
|
" for="idmocked-id">
|
||||||
Faux
|
Faux
|
||||||
</label>
|
</label>
|
||||||
<svg style="
|
<svg data-testid="incorrect-icon" style="
|
||||||
vertical-align: text-bottom;
|
vertical-align: text-bottom;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin-left: 0.1rem;
|
margin-left: 0.1rem;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
// GIFTTemplatePreview.tsx
|
// GIFTTemplatePreview.tsx
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import Template, { ErrorTemplate } from './templates';
|
import Template, { ErrorTemplate, UnsupportedQuestionTypeError } from './templates';
|
||||||
import { parse } from 'gift-pegjs';
|
import { parse } from 'gift-pegjs';
|
||||||
import './styles.css';
|
import './styles.css';
|
||||||
import { FormattedTextTemplate } from './templates/TextTypeTemplate';
|
import { FormattedTextTemplate } from './templates/TextTypeTemplate';
|
||||||
|
|
@ -22,19 +22,6 @@ const GIFTTemplatePreview: React.FC<GIFTTemplatePreviewProps> = ({
|
||||||
try {
|
try {
|
||||||
let previewHTML = '';
|
let previewHTML = '';
|
||||||
questions.forEach((giftQuestion) => {
|
questions.forEach((giftQuestion) => {
|
||||||
// TODO : afficher un message que les images spécifiées par <img> sont dépréciées et qu'il faut utiliser [markdown] et la syntaxe 
|
|
||||||
|
|
||||||
// const isImage = item.includes('<img');
|
|
||||||
// if (isImage) {
|
|
||||||
// const imageUrlMatch = item.match(/<img[^>]+>/i);
|
|
||||||
// if (imageUrlMatch) {
|
|
||||||
// let imageUrl = imageUrlMatch[0];
|
|
||||||
// imageUrl = imageUrl.replace('img', 'img style="width:10vw;" src=');
|
|
||||||
// item = item.replace(imageUrlMatch[0], '');
|
|
||||||
// previewHTML += `${imageUrl}`;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const question = parse(giftQuestion);
|
const question = parse(giftQuestion);
|
||||||
previewHTML += Template(question[0], {
|
previewHTML += Template(question[0], {
|
||||||
|
|
@ -42,11 +29,15 @@ const GIFTTemplatePreview: React.FC<GIFTTemplatePreviewProps> = ({
|
||||||
theme: 'light'
|
theme: 'light'
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof Error) {
|
let errorMsg: string;
|
||||||
previewHTML += ErrorTemplate(giftQuestion + '\n' + error.message);
|
if (error instanceof UnsupportedQuestionTypeError) {
|
||||||
|
errorMsg = ErrorTemplate(giftQuestion, `Erreur: ${error.message}`);
|
||||||
|
} else if (error instanceof Error) {
|
||||||
|
errorMsg = ErrorTemplate(giftQuestion, `Erreur GIFT: ${error.message}`);
|
||||||
} else {
|
} else {
|
||||||
previewHTML += ErrorTemplate(giftQuestion + '\n' + 'Erreur inconnue');
|
errorMsg = ErrorTemplate(giftQuestion, 'Erreur inconnue');
|
||||||
}
|
}
|
||||||
|
previewHTML += `<div label="error-message">${errorMsg}</div>`;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,11 +18,11 @@ What is the capital of Canada? {=Canada -> Ottawa =Italy -> Rome =Japan -> Tokyo
|
||||||
|
|
||||||
|
|
||||||
const items = multiple.map((item) => Template(item, { theme: 'dark' })).join('');
|
const items = multiple.map((item) => Template(item, { theme: 'dark' })).join('');
|
||||||
const errorItemDark = ErrorTemplate('Hello');
|
const errorItemDark = ErrorTemplate('Hello', 'Error');
|
||||||
|
|
||||||
const lightItems = multiple.map((item) => Template(item, { theme: 'light' })).join('');
|
const lightItems = multiple.map((item) => Template(item, { theme: 'light' })).join('');
|
||||||
|
|
||||||
const errorItem = ErrorTemplate('Hello');
|
const errorItem = ErrorTemplate('Hello', 'Error');
|
||||||
|
|
||||||
const app = document.getElementById('app');
|
const app = document.getElementById('app');
|
||||||
if (app) app.innerHTML = items + errorItemDark + lightItems + errorItem;
|
if (app) app.innerHTML = items + errorItemDark + lightItems + errorItem;
|
||||||
|
|
|
||||||
|
|
@ -25,11 +25,11 @@ export default function AnswerIcon({ correct }: AnswerIconOptions): string {
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const CorrectIcon = (): string => {
|
const CorrectIcon = (): string => {
|
||||||
return `<svg style="${Icon} ${Correct}" role="img" aria-hidden="true" focusable="false" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z"></path></svg>`;
|
return `<svg data-testid="correct-icon" style="${Icon} ${Correct}" role="img" aria-hidden="true" focusable="false" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z"></path></svg>`;
|
||||||
};
|
};
|
||||||
|
|
||||||
const IncorrectIcon = (): string => {
|
const IncorrectIcon = (): string => {
|
||||||
return `<svg style="${Icon} ${Incorrect}" role="img" aria-hidden="true" focusable="false" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 352 512"><path fill="currentColor" d="M242.72 256l100.07-100.07c12.28-12.28 12.28-32.19 0-44.48l-22.24-22.24c-12.28-12.28-32.19-12.28-44.48 0L176 189.28 75.93 89.21c-12.28-12.28-32.19-12.28-44.48 0L9.21 111.45c-12.28 12.28-12.28 32.19 0 44.48L109.28 256 9.21 356.07c-12.28 12.28-12.28 32.19 0 44.48l22.24 22.24c12.28 12.28 32.2 12.28 44.48 0L176 322.72l100.07 100.07c12.28 12.28 32.2 12.28 44.48 0l22.24-22.24c12.28-12.28 12.28-32.19 0-44.48L242.72 256z"></path></svg>`;
|
return `<svg data-testid="incorrect-icon" style="${Icon} ${Incorrect}" role="img" aria-hidden="true" focusable="false" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 352 512"><path fill="currentColor" d="M242.72 256l100.07-100.07c12.28-12.28 12.28-32.19 0-44.48l-22.24-22.24c-12.28-12.28-32.19-12.28-44.48 0L176 189.28 75.93 89.21c-12.28-12.28-32.19-12.28-44.48 0L9.21 111.45c-12.28 12.28-12.28 32.19 0 44.48L109.28 256 9.21 356.07c-12.28 12.28-12.28 32.19 0 44.48l22.24 22.24c12.28 12.28 32.2 12.28 44.48 0L176 322.72l100.07 100.07c12.28 12.28 32.2 12.28 44.48 0l22.24-22.24c12.28-12.28 12.28-32.19 0-44.48L242.72 256z"></path></svg>`;
|
||||||
};
|
};
|
||||||
|
|
||||||
return correct ? CorrectIcon() : IncorrectIcon();
|
return correct ? CorrectIcon() : IncorrectIcon();
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { theme, ParagraphStyle } from '../constants';
|
import { theme, ParagraphStyle } from '../constants';
|
||||||
import { state } from '.';
|
import { state } from '.';
|
||||||
|
|
||||||
export default function (text: string): string {
|
export default function (questionText: string, errorText: string): string {
|
||||||
const Container = `
|
const Container = `
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
@ -13,47 +13,49 @@ export default function (text: string): string {
|
||||||
box-shadow: 0px 1px 3px ${theme(state.theme, 'gray400', 'black900')};
|
box-shadow: 0px 1px 3px ${theme(state.theme, 'gray400', 'black900')};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const document = removeBackslash(lineRegex(documentRegex(text))).split(/\r?\n/);
|
// const document = removeBackslash(lineRegex(documentRegex(text))).split(/\r?\n/);
|
||||||
return document[0] !== ``
|
// return document[0] !== ``
|
||||||
? `<section style="${Container}">${document
|
// ? `<section style="${Container}">${document
|
||||||
.map((i) => `<p style="${ParagraphStyle(state.theme)}">${i}</p>`)
|
// .map((i) => `<p style="${ParagraphStyle(state.theme)}">${i}</p>`)
|
||||||
.join('')}</section>`
|
// .join('')}</section>`
|
||||||
: ``;
|
// : ``;
|
||||||
|
return `<section style="${Container}"><p style="${ParagraphStyle(state.theme)}">${questionText}<br><em>${errorText}</em></p></section>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function documentRegex(text: string): string {
|
// function documentRegex(text: string): string {
|
||||||
const newText = text
|
// const newText = text
|
||||||
.split(/\r?\n/)
|
// .split(/\r?\n/)
|
||||||
.map((comment) => comment.replace(/(^[ \\t]+)?(^)((\/\/))(.*)/gm, ''))
|
// .map((comment) => comment.replace(/(^[ \\t]+)?(^)((\/\/))(.*)/gm, ''))
|
||||||
.join('');
|
// .join('');
|
||||||
|
|
||||||
const newLineAnswer = /([^\\]|[^\S\r\n][^=])(=|~)/g;
|
// const newLineAnswer = /([^\\]|[^\S\r\n][^=])(=|~)/g;
|
||||||
const correctAnswer = /([^\\]|^{)(([^\\]|^|\\s*)=(.*)(?=[=~}]|\\n))/g;
|
// const correctAnswer = /([^\\]|^{)(([^\\]|^|\\s*)=(.*)(?=[=~}]|\\n))/g;
|
||||||
const incorrectAnswer = /([^\\]|^{)(([^\\]|^|\\s*)~(.*)(?=[=~}]|\\n))/g;
|
// const incorrectAnswer = /([^\\]|^{)(([^\\]|^|\\s*)~(.*)(?=[=~}]|\\n))/g;
|
||||||
|
|
||||||
return newText
|
// return newText
|
||||||
.replace(newLineAnswer, `\n$2`)
|
// .replace(newLineAnswer, `\n$2`)
|
||||||
.replace(correctAnswer, `$1<li>$4</li>`)
|
// .replace(correctAnswer, `$1<li>$4</li>`)
|
||||||
.replace(incorrectAnswer, `$1<li>$4</li>`);
|
// .replace(incorrectAnswer, `$1<li>$4</li>`);
|
||||||
}
|
// }
|
||||||
|
|
||||||
function lineRegex(text: string): string {
|
// function lineRegex(text: string): string {
|
||||||
return text
|
// return text
|
||||||
.split(/\r?\n/)
|
// // CPF: disabled the following regex because it's not clear what it's supposed to do
|
||||||
.map((category) =>
|
// // .split(/\r?\n/)
|
||||||
category.replace(/(^[ \\t]+)?(((^|\n)\s*[$]CATEGORY:))(.+)/g, `<br><b>$5</b><br>`)
|
// // .map((category) =>
|
||||||
)
|
// // category.replace(/(^[ \\t]+)?(((^|\n)\s*[$]CATEGORY:))(.+)/g, `<br><b>$5</b><br>`)
|
||||||
.map((title) => title.replace(/\s*(::)\s*(.*?)(::)/g, `<br><b>$2</b><br>`))
|
// // )
|
||||||
.map((openBracket) => openBracket.replace(/([^\\]|^){([#])?/g, `$1<br>`))
|
// // .map((title) => title.replace(/\s*(::)\s*(.*?)(::)/g, `<br><b>$2</b><br>`))
|
||||||
.map((closeBracket) => closeBracket.replace(/([^\\]|^)}/g, `$1<br>`))
|
// // .map((openBracket) => openBracket.replace(/([^\\]|^){([#])?/g, `$1<br>`))
|
||||||
.join('');
|
// // .map((closeBracket) => closeBracket.replace(/([^\\]|^)}/g, `$1<br>`))
|
||||||
}
|
// // .join('');
|
||||||
|
// }
|
||||||
|
|
||||||
function removeBackslash(text: string): string {
|
// function removeBackslash(text: string): string {
|
||||||
return text
|
// return text
|
||||||
.split(/\r?\n/)
|
// .split(/\r?\n/)
|
||||||
.map((colon) => colon.replace(/[\\]:/g, ':'))
|
// .map((colon) => colon.replace(/[\\]:/g, ':'))
|
||||||
.map((openBracket) => openBracket.replace(/[\\]{/g, '{'))
|
// .map((openBracket) => openBracket.replace(/[\\]{/g, '{'))
|
||||||
.map((closeBracket) => closeBracket.replace(/[\\]}/g, '}'))
|
// .map((closeBracket) => closeBracket.replace(/[\\]}/g, '}'))
|
||||||
.join('');
|
// .join('');
|
||||||
}
|
// }
|
||||||
|
|
|
||||||
|
|
@ -6,20 +6,32 @@ import {
|
||||||
MultipleChoiceQuestion as MultipleChoiceType,
|
MultipleChoiceQuestion as MultipleChoiceType,
|
||||||
NumericalQuestion as NumericalType,
|
NumericalQuestion as NumericalType,
|
||||||
ShortAnswerQuestion as ShortAnswerType,
|
ShortAnswerQuestion as ShortAnswerType,
|
||||||
// Essay as EssayType,
|
// EssayQuestion as EssayType,
|
||||||
TrueFalseQuestion as TrueFalseType,
|
TrueFalseQuestion as TrueFalseType,
|
||||||
// MatchingQuestion as MatchingType,
|
// MatchingQuestion as MatchingType,
|
||||||
} from 'gift-pegjs';
|
} from 'gift-pegjs';
|
||||||
import { DisplayOptions } from './types';
|
import { DisplayOptions } from './types';
|
||||||
import DescriptionTemplate from './DescriptionTemplate';
|
// import DescriptionTemplate from './DescriptionTemplate';
|
||||||
import EssayTemplate from './EssayTemplate';
|
// import EssayTemplate from './EssayTemplate';
|
||||||
import MatchingTemplate from './MatchingTemplate';
|
// import MatchingTemplate from './MatchingTemplate';
|
||||||
import MultipleChoiceTemplate from './MultipleChoiceTemplate';
|
import MultipleChoiceTemplate from './MultipleChoiceTemplate';
|
||||||
import NumericalTemplate from './NumericalTemplate';
|
import NumericalTemplate from './NumericalTemplate';
|
||||||
import ShortAnswerTemplate from './ShortAnswerTemplate';
|
import ShortAnswerTemplate from './ShortAnswerTemplate';
|
||||||
import TrueFalseTemplate from './TrueFalseTemplate';
|
import TrueFalseTemplate from './TrueFalseTemplate';
|
||||||
import Error from './ErrorTemplate';
|
import Error from './ErrorTemplate';
|
||||||
import CategoryTemplate from './CategoryTemplate';
|
// import CategoryTemplate from './CategoryTemplate';
|
||||||
|
|
||||||
|
export class UnsupportedQuestionTypeError extends globalThis.Error {
|
||||||
|
constructor(type: string) {
|
||||||
|
const userFriendlyType = (type === 'Essay') ? 'Réponse longue (Essay)'
|
||||||
|
: (type === 'Matching') ? 'Association (Matching)'
|
||||||
|
: (type === 'Category') ? 'Catégorie (Category)'
|
||||||
|
: type;
|
||||||
|
super(`Les questions du type ${userFriendlyType} ne sont pas supportées.`);
|
||||||
|
this.name = 'UnsupportedQuestionTypeError';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
export const state: DisplayOptions = { preview: true, theme: 'light' };
|
export const state: DisplayOptions = { preview: true, theme: 'light' };
|
||||||
|
|
||||||
|
|
@ -54,23 +66,21 @@ export default function Template(
|
||||||
// case 'Matching':
|
// case 'Matching':
|
||||||
// return Matching({ ...(keys as MatchingType) });
|
// return Matching({ ...(keys as MatchingType) });
|
||||||
default:
|
default:
|
||||||
// TODO: throw error for unsupported question types?
|
// convert type to human-readable string
|
||||||
// throw new Error(`Unsupported question type: ${type}`);
|
throw new UnsupportedQuestionTypeError(type); }
|
||||||
return ``;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ErrorTemplate(text: string, options?: Partial<DisplayOptions>): string {
|
export function ErrorTemplate(questionText: string, errorText: string, options?: Partial<DisplayOptions>): string {
|
||||||
Object.assign(state, options);
|
Object.assign(state, options);
|
||||||
|
|
||||||
return Error(text);
|
return Error(questionText, errorText);
|
||||||
}
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
CategoryTemplate,
|
// CategoryTemplate,
|
||||||
DescriptionTemplate as Description,
|
// DescriptionTemplate as Description,
|
||||||
EssayTemplate as Essay,
|
// EssayTemplate as Essay,
|
||||||
MatchingTemplate as Matching,
|
// MatchingTemplate as Matching,
|
||||||
MultipleChoiceTemplate as MultipleChoice,
|
MultipleChoiceTemplate as MultipleChoice,
|
||||||
NumericalTemplate as Numerical,
|
NumericalTemplate as Numerical,
|
||||||
ShortAnswerTemplate as ShortAnswer,
|
ShortAnswerTemplate as ShortAnswer,
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
import { TableCell, TableHead, TableRow } from "@mui/material";
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
import { TableCell, TableHead, TableRow } from "@mui/material";
|
||||||
|
|
||||||
interface LiveResultsFooterProps {
|
interface LiveResultsHeaderProps {
|
||||||
maxQuestions: number;
|
maxQuestions: number;
|
||||||
showSelectedQuestion: (index: number) => void;
|
showSelectedQuestion: (index: number) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const LiveResultsTableFooter: React.FC<LiveResultsFooterProps> = ({
|
const LiveResultsTableHeader: React.FC<LiveResultsHeaderProps> = ({
|
||||||
maxQuestions,
|
maxQuestions,
|
||||||
showSelectedQuestion,
|
showSelectedQuestion,
|
||||||
}) => {
|
}) => {
|
||||||
|
|
@ -47,4 +47,4 @@ const LiveResultsTableFooter: React.FC<LiveResultsFooterProps> = ({
|
||||||
</TableHead>
|
</TableHead>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
export default LiveResultsTableFooter;
|
export default LiveResultsTableHeader;
|
||||||
|
|
|
||||||
|
|
@ -118,7 +118,11 @@ const QuizForm: React.FC = () => {
|
||||||
setValue(value);
|
setValue(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
const linesArray = value.split(/(?<=^|[^\\]}.*)[\n]+/);
|
// split value when there is at least one blank line
|
||||||
|
const linesArray = value.split(/\n{2,}/);
|
||||||
|
|
||||||
|
// if the first item in linesArray is blank, remove it
|
||||||
|
if (linesArray[0] === '') linesArray.shift();
|
||||||
|
|
||||||
if (linesArray[linesArray.length - 1] === '') linesArray.pop();
|
if (linesArray[linesArray.length - 1] === '') linesArray.pop();
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue