mirror of
https://github.com/ets-cfuhrman-pfe/EvalueTonSavoir.git
synced 2025-08-11 21:23:54 -04:00
Merge pull request #97 from ets-cfuhrman-pfe/fuhrmanator/issue64
Fuhrmanator/issue64 Supporter le format HTML, mises à jour des tests du frontend
This commit is contained in:
commit
3c699a3379
23 changed files with 1260 additions and 1809 deletions
6
.github/workflows/frontend-tests.yml
vendored
6
.github/workflows/frontend-tests.yml
vendored
|
|
@ -14,15 +14,15 @@ jobs:
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Check Out Repo
|
- name: Check Out Repo
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Set up Node.js
|
- name: Set up Node.js
|
||||||
uses: actions/setup-node@v2
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: '18'
|
node-version: '18'
|
||||||
|
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
run: npm install
|
run: npm ci
|
||||||
working-directory: ./client
|
working-directory: ./client
|
||||||
|
|
||||||
- name: Run Tests
|
- name: Run Tests
|
||||||
|
|
|
||||||
2539
client/package-lock.json
generated
2539
client/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -18,51 +18,51 @@
|
||||||
"@fortawesome/fontawesome-svg-core": "^6.6.0",
|
"@fortawesome/fontawesome-svg-core": "^6.6.0",
|
||||||
"@fortawesome/free-solid-svg-icons": "^6.4.2",
|
"@fortawesome/free-solid-svg-icons": "^6.4.2",
|
||||||
"@fortawesome/react-fontawesome": "^0.2.0",
|
"@fortawesome/react-fontawesome": "^0.2.0",
|
||||||
"@mui/icons-material": "^5.14.18",
|
"@mui/icons-material": "^6.1.0",
|
||||||
"@mui/lab": "^5.0.0-alpha.153",
|
"@mui/lab": "^5.0.0-alpha.153",
|
||||||
"@mui/material": "^5.15.11",
|
"@mui/material": "^6.1.0",
|
||||||
"@types/uuid": "^9.0.7",
|
"@types/uuid": "^9.0.7",
|
||||||
"axios": "^1.6.7",
|
"axios": "^1.6.7",
|
||||||
"esbuild": "^0.20.2",
|
"esbuild": "^0.23.1",
|
||||||
"gift-pegjs": "^0.2.1",
|
"gift-pegjs": "^1.0.2",
|
||||||
"jest-environment-jsdom": "^29.7.0",
|
"jest-environment-jsdom": "^29.7.0",
|
||||||
"katex": "^0.16.9",
|
"katex": "^0.16.11",
|
||||||
"marked": "^9.1.2",
|
"marked": "^14.1.2",
|
||||||
"nanoid": "^5.0.2",
|
"nanoid": "^5.0.2",
|
||||||
"react": "^18.2.0",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.3.1",
|
||||||
"react-latex": "^1.2.0",
|
"react-latex": "^2.0.0",
|
||||||
"react-modal": "^3.16.1",
|
"react-modal": "^3.16.1",
|
||||||
"react-router-dom": "^6.16.0",
|
"react-router-dom": "^6.26.2",
|
||||||
"remark-math": "^6.0.0",
|
"remark-math": "^6.0.0",
|
||||||
"socket.io-client": "^4.7.2",
|
"socket.io-client": "^4.7.2",
|
||||||
"ts-node": "^10.9.1",
|
"ts-node": "^10.9.1",
|
||||||
"uuid": "^9.0.1",
|
"uuid": "^9.0.1",
|
||||||
"vite-plugin-checker": "^0.6.2"
|
"vite-plugin-checker": "^0.8.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/preset-env": "^7.23.3",
|
"@babel/preset-env": "^7.23.3",
|
||||||
"@babel/preset-react": "^7.23.3",
|
"@babel/preset-react": "^7.23.3",
|
||||||
"@babel/preset-typescript": "^7.23.3",
|
"@babel/preset-typescript": "^7.23.3",
|
||||||
"@testing-library/jest-dom": "^6.1.4",
|
"@testing-library/dom": "^10.4.0",
|
||||||
"@testing-library/react": "^14.1.0",
|
"@testing-library/jest-dom": "^6.5.0",
|
||||||
"@types/jest": "^29.5.8",
|
"@testing-library/react": "^16.0.1",
|
||||||
"@types/node": "^20.8.8",
|
"@types/jest": "^29.5.13",
|
||||||
|
"@types/node": "^22.5.5",
|
||||||
"@types/react": "^18.2.15",
|
"@types/react": "^18.2.15",
|
||||||
"@types/react-dom": "^18.2.7",
|
"@types/react-dom": "^18.2.7",
|
||||||
"@types/react-latex": "^2.0.3",
|
"@types/react-latex": "^2.0.3",
|
||||||
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
"@typescript-eslint/eslint-plugin": "^8.5.0",
|
||||||
"@typescript-eslint/parser": "^6.0.0",
|
"@typescript-eslint/parser": "^8.5.0",
|
||||||
"@vitejs/plugin-react-swc": "^3.3.2",
|
"@vitejs/plugin-react-swc": "^3.3.2",
|
||||||
"eslint": "^8.45.0",
|
"eslint": "^9.10.0",
|
||||||
"eslint-plugin-react-hooks": "^4.6.0",
|
"eslint-plugin-react-hooks": "^5.1.0-rc-206df66e-20240912",
|
||||||
"eslint-plugin-react-refresh": "^0.4.3",
|
"eslint-plugin-react-refresh": "^0.4.12",
|
||||||
"identity-obj-proxy": "^3.0.0",
|
"identity-obj-proxy": "^3.0.0",
|
||||||
"jest": "^29.7.0",
|
"jest": "^29.7.0",
|
||||||
"ts-jest": "^29.1.1",
|
"ts-jest": "^29.1.1",
|
||||||
"typescript": "^5.0.2",
|
"typescript": "^5.6.2",
|
||||||
"vite": "^4.4.5",
|
"vite": "^5.4.5",
|
||||||
"vite-plugin-environment": "^1.1.3",
|
"vite-plugin-environment": "^1.1.3"
|
||||||
"vite-plugin-rewrite-all": "^1.0.1"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ const handleLogout = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const isLoggedIn = () => {
|
const isLoggedIn = () => {
|
||||||
return ApiService.isLogedIn();
|
return ApiService.isLoggedIn();
|
||||||
}
|
}
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
|
|
|
||||||
|
|
@ -63,8 +63,18 @@ describe('TextType', () => {
|
||||||
text: '**Bold**',
|
text: '**Bold**',
|
||||||
format: 'markdown'
|
format: 'markdown'
|
||||||
};
|
};
|
||||||
const expectedOutput = '<strong>Bold</strong>';
|
// TODO: investigate why the output has an extra newline
|
||||||
expect(TextType({ text: input })).toContain(expectedOutput);
|
const expectedOutput = '<strong>Bold</strong>\n';
|
||||||
|
expect(TextType({ text: input })).toBe(expectedOutput);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should format text with HTML correctly', () => {
|
||||||
|
const input: TextFormat = {
|
||||||
|
text: '<em>yes</em>',
|
||||||
|
format: 'html'
|
||||||
|
};
|
||||||
|
const expectedOutput = '<em>yes</em>';
|
||||||
|
expect(TextType({ text: input })).toBe(expectedOutput);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should format plain text correctly', () => {
|
it('should format plain text correctly', () => {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
import { render, screen, fireEvent } from '@testing-library/react';
|
import { render, screen, fireEvent } from '@testing-library/react';
|
||||||
import '@testing-library/jest-dom';
|
import '@testing-library/jest-dom';
|
||||||
import MultipleChoiceQuestion from '../../../../components/Questions/MultipleChoiceQuestion/MultipleChoiceQuestion';
|
import MultipleChoiceQuestion from '../../../../components/Questions/MultipleChoiceQuestion/MultipleChoiceQuestion';
|
||||||
|
import { act } from 'react';
|
||||||
|
import { MemoryRouter } from 'react-router-dom';
|
||||||
|
|
||||||
const questionStem = 'Question stem';
|
const questionStem = 'Question stem';
|
||||||
const sampleFeedback = 'Feedback';
|
const sampleFeedback = 'Feedback';
|
||||||
|
|
@ -14,11 +16,13 @@ describe('MultipleChoiceQuestion', () => {
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
render(
|
render(
|
||||||
<MultipleChoiceQuestion
|
<MemoryRouter>
|
||||||
globalFeedback={sampleFeedback}
|
<MultipleChoiceQuestion
|
||||||
choices={choices}
|
globalFeedback={sampleFeedback}
|
||||||
handleOnSubmitAnswer={mockHandleOnSubmitAnswer} questionContent={{text: questionStem, format: 'plain'}} />
|
choices={choices}
|
||||||
);
|
handleOnSubmitAnswer={mockHandleOnSubmitAnswer}
|
||||||
|
questionStem={{ text: questionStem, format: 'plain' }} />
|
||||||
|
</MemoryRouter>);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('renders the question and choices', () => {
|
test('renders the question and choices', () => {
|
||||||
|
|
@ -30,16 +34,24 @@ describe('MultipleChoiceQuestion', () => {
|
||||||
|
|
||||||
test('does not submit when no answer is selected', () => {
|
test('does not submit when no answer is selected', () => {
|
||||||
const submitButton = screen.getByText('Répondre');
|
const submitButton = screen.getByText('Répondre');
|
||||||
fireEvent.click(submitButton);
|
act(() => {
|
||||||
|
fireEvent.click(submitButton);
|
||||||
|
});
|
||||||
expect(mockHandleOnSubmitAnswer).not.toHaveBeenCalled();
|
expect(mockHandleOnSubmitAnswer).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('submits the selected answer', () => {
|
test('submits the selected answer', () => {
|
||||||
const choiceButton = screen.getByText('Choice 1').closest('button');
|
const choiceButton = screen.getByText('Choice 1').closest('button');
|
||||||
if (!choiceButton) throw new Error('Choice button not found');
|
if (!choiceButton) throw new Error('Choice button not found');
|
||||||
fireEvent.click(choiceButton);
|
act(() => {
|
||||||
|
fireEvent.click(choiceButton);
|
||||||
|
});
|
||||||
|
|
||||||
const submitButton = screen.getByText('Répondre');
|
const submitButton = screen.getByText('Répondre');
|
||||||
fireEvent.click(submitButton);
|
act(() => {
|
||||||
|
fireEvent.click(submitButton);
|
||||||
|
});
|
||||||
|
|
||||||
expect(mockHandleOnSubmitAnswer).toHaveBeenCalledWith('Choice 1');
|
expect(mockHandleOnSubmitAnswer).toHaveBeenCalledWith('Choice 1');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,8 @@ describe('Questions Component', () => {
|
||||||
type: 'TF',
|
type: 'TF',
|
||||||
stem: { format: 'plain', text: 'Sample True/False Question' },
|
stem: { format: 'plain', text: 'Sample True/False Question' },
|
||||||
isTrue: true,
|
isTrue: true,
|
||||||
incorrectFeedback: null,
|
falseFeedback: null,
|
||||||
correctFeedback: null,
|
trueFeedback: null,
|
||||||
title: 'True/False Question',
|
title: 'True/False Question',
|
||||||
hasEmbeddedAnswers: false,
|
hasEmbeddedAnswers: false,
|
||||||
globalFeedback: null,
|
globalFeedback: null,
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
// TrueFalseQuestion.test.tsx
|
// TrueFalseQuestion.test.tsx
|
||||||
import { render, fireEvent, screen } from '@testing-library/react';
|
import { render, fireEvent, screen, act } from '@testing-library/react';
|
||||||
import '@testing-library/jest-dom';
|
import '@testing-library/jest-dom';
|
||||||
import TrueFalseQuestion from '../../../../components/Questions/TrueFalseQuestion/TrueFalseQuestion';
|
import TrueFalseQuestion from '../../../../components/Questions/TrueFalseQuestion/TrueFalseQuestion';
|
||||||
|
import { MemoryRouter } from 'react-router-dom';
|
||||||
|
|
||||||
describe('TrueFalseQuestion Component', () => {
|
describe('TrueFalseQuestion Component', () => {
|
||||||
const mockHandleSubmitAnswer = jest.fn();
|
const mockHandleSubmitAnswer = jest.fn();
|
||||||
|
|
@ -15,7 +16,10 @@ describe('TrueFalseQuestion Component', () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
render(<TrueFalseQuestion questionContent={{text: sampleStem, format: 'plain'}} {...sampleProps} />);
|
render(
|
||||||
|
<MemoryRouter>
|
||||||
|
<TrueFalseQuestion questionContent={{ text: sampleStem, format: 'plain' }} {...sampleProps} />
|
||||||
|
</MemoryRouter>);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders correctly', () => {
|
it('renders correctly', () => {
|
||||||
|
|
@ -27,14 +31,14 @@ describe('TrueFalseQuestion Component', () => {
|
||||||
|
|
||||||
it('Submit button should be disabled if no option is selected', () => {
|
it('Submit button should be disabled if no option is selected', () => {
|
||||||
const submitButton = screen.getByText('Répondre');
|
const submitButton = screen.getByText('Répondre');
|
||||||
|
|
||||||
expect(submitButton).toBeDisabled();
|
expect(submitButton).toBeDisabled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('not submit answer if no option is selected', () => {
|
it('not submit answer if no option is selected', () => {
|
||||||
const submitButton = screen.getByText('Répondre');
|
const submitButton = screen.getByText('Répondre');
|
||||||
|
act(() => {
|
||||||
fireEvent.click(submitButton);
|
fireEvent.click(submitButton);
|
||||||
|
});
|
||||||
|
|
||||||
expect(mockHandleSubmitAnswer).not.toHaveBeenCalled();
|
expect(mockHandleSubmitAnswer).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
@ -43,9 +47,13 @@ describe('TrueFalseQuestion Component', () => {
|
||||||
const trueButton = screen.getByText('Vrai');
|
const trueButton = screen.getByText('Vrai');
|
||||||
const submitButton = screen.getByText('Répondre');
|
const submitButton = screen.getByText('Répondre');
|
||||||
|
|
||||||
fireEvent.click(trueButton);
|
act(() => {
|
||||||
|
fireEvent.click(trueButton);
|
||||||
|
});
|
||||||
|
|
||||||
fireEvent.click(submitButton);
|
act(() => {
|
||||||
|
fireEvent.click(submitButton);
|
||||||
|
});
|
||||||
|
|
||||||
expect(mockHandleSubmitAnswer).toHaveBeenCalledWith(true);
|
expect(mockHandleSubmitAnswer).toHaveBeenCalledWith(true);
|
||||||
});
|
});
|
||||||
|
|
@ -53,10 +61,12 @@ describe('TrueFalseQuestion Component', () => {
|
||||||
it('submits answer correctly for False', () => {
|
it('submits answer correctly for False', () => {
|
||||||
const falseButton = screen.getByText('Faux');
|
const falseButton = screen.getByText('Faux');
|
||||||
const submitButton = screen.getByText('Répondre');
|
const submitButton = screen.getByText('Répondre');
|
||||||
|
act(() => {
|
||||||
fireEvent.click(falseButton);
|
fireEvent.click(falseButton);
|
||||||
|
});
|
||||||
fireEvent.click(submitButton);
|
act(() => {
|
||||||
|
fireEvent.click(submitButton);
|
||||||
|
});
|
||||||
|
|
||||||
expect(mockHandleSubmitAnswer).toHaveBeenCalledWith(false);
|
expect(mockHandleSubmitAnswer).toHaveBeenCalledWith(false);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,141 +1,95 @@
|
||||||
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
|
import { render, screen, fireEvent, act } from '@testing-library/react';
|
||||||
import '@testing-library/jest-dom';
|
import '@testing-library/jest-dom';
|
||||||
|
import { parse } from 'gift-pegjs';
|
||||||
import { MemoryRouter } from 'react-router-dom';
|
import { MemoryRouter } from 'react-router-dom';
|
||||||
import { QuestionType } from '../../../../Types/QuestionType';
|
import { QuestionType } from '../../../../Types/QuestionType';
|
||||||
import StudentModeQuiz from '../../../../components/StudentModeQuiz/StudentModeQuiz';
|
import StudentModeQuiz from '../../../../components/StudentModeQuiz/StudentModeQuiz';
|
||||||
|
|
||||||
const mockQuestions: QuestionType[] = [
|
const mockGiftQuestions = parse(
|
||||||
{
|
`::Sample Question 1:: Sample Question 1 {=Option A ~Option B}
|
||||||
question: {
|
|
||||||
id: '1',
|
::Sample Question 2:: Sample Question 2 {T}`);
|
||||||
type: 'MC',
|
|
||||||
stem: { format: 'plain', text: 'Sample Question 1' },
|
const mockQuestions: QuestionType[] = mockGiftQuestions.map((question, index) => {
|
||||||
title: 'Sample Question 1',
|
question.id = (index + 1).toString();
|
||||||
hasEmbeddedAnswers: false,
|
const newMockQuestion: QuestionType = {
|
||||||
globalFeedback: null,
|
question: question,
|
||||||
choices: [
|
};
|
||||||
{ text: { format: 'plain', text: 'Option A' }, isCorrect: true, weight: 1, feedback: null },
|
return newMockQuestion;
|
||||||
{ text: { format: 'plain', text: 'Option B' }, isCorrect: false, weight: 0, feedback: null },
|
});
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
question: {
|
|
||||||
id: '2',
|
|
||||||
type: 'TF',
|
|
||||||
stem: { format: 'plain', text: 'Sample Question 2' },
|
|
||||||
isTrue: true,
|
|
||||||
incorrectFeedback: null,
|
|
||||||
correctFeedback: null,
|
|
||||||
title: 'Question 2',
|
|
||||||
hasEmbeddedAnswers: false,
|
|
||||||
globalFeedback: null,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const mockSubmitAnswer = jest.fn();
|
const mockSubmitAnswer = jest.fn();
|
||||||
const mockDisconnectWebSocket = jest.fn();
|
const mockDisconnectWebSocket = jest.fn();
|
||||||
|
|
||||||
describe('StudentModeQuiz', () => {
|
beforeEach(() => {
|
||||||
test('renders the initial question', async () => {
|
render(
|
||||||
render(
|
<MemoryRouter>
|
||||||
<MemoryRouter>
|
<StudentModeQuiz
|
||||||
<StudentModeQuiz
|
|
||||||
questions={mockQuestions}
|
|
||||||
submitAnswer={mockSubmitAnswer}
|
|
||||||
disconnectWebSocket={mockDisconnectWebSocket}
|
|
||||||
/>
|
|
||||||
</MemoryRouter>
|
|
||||||
);
|
|
||||||
|
|
||||||
// wait for the question to be rendered
|
|
||||||
await waitFor(() => {
|
|
||||||
expect(screen.getByText('Sample Question 1')).toBeInTheDocument();
|
|
||||||
expect(screen.getByText('Option A')).toBeInTheDocument();
|
|
||||||
expect(screen.getByText('Option B')).toBeInTheDocument();
|
|
||||||
expect(screen.getByText('Quitter')).toBeInTheDocument();
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
test('handles answer submission text', async () => {
|
|
||||||
|
|
||||||
render(
|
|
||||||
<MemoryRouter>
|
|
||||||
<StudentModeQuiz
|
|
||||||
questions={mockQuestions}
|
questions={mockQuestions}
|
||||||
submitAnswer={mockSubmitAnswer}
|
submitAnswer={mockSubmitAnswer}
|
||||||
disconnectWebSocket={mockDisconnectWebSocket}
|
disconnectWebSocket={mockDisconnectWebSocket}
|
||||||
/>
|
/>
|
||||||
</MemoryRouter>
|
</MemoryRouter>);
|
||||||
);
|
});
|
||||||
|
|
||||||
fireEvent.click(screen.getByText('Option A'));
|
describe('StudentModeQuiz', () => {
|
||||||
fireEvent.click(screen.getByText('Répondre'));
|
test('renders the initial question', async () => {
|
||||||
|
expect(screen.getByText('Sample Question 1')).toBeInTheDocument();
|
||||||
|
expect(screen.getByText('Option A')).toBeInTheDocument();
|
||||||
|
expect(screen.getByText('Option B')).toBeInTheDocument();
|
||||||
|
expect(screen.getByText('Quitter')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
await waitFor(() => {
|
test('handles answer submission text', async () => {
|
||||||
expect(mockSubmitAnswer).toHaveBeenCalledWith('Option A', '1');
|
act(() => {
|
||||||
|
fireEvent.click(screen.getByText('Option A'));
|
||||||
});
|
});
|
||||||
|
act(() => {
|
||||||
|
fireEvent.click(screen.getByText('Répondre'));
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(mockSubmitAnswer).toHaveBeenCalledWith('Option A', '1');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('handles quit button click', async () => {
|
test('handles quit button click', async () => {
|
||||||
render(
|
act(() => {
|
||||||
<MemoryRouter>
|
fireEvent.click(screen.getByText('Quitter'));
|
||||||
<StudentModeQuiz
|
|
||||||
questions={mockQuestions}
|
|
||||||
submitAnswer={mockSubmitAnswer}
|
|
||||||
disconnectWebSocket={mockDisconnectWebSocket}
|
|
||||||
/>
|
|
||||||
</MemoryRouter>);
|
|
||||||
fireEvent.click(screen.getByText('Quitter'));
|
|
||||||
|
|
||||||
await waitFor(() => {
|
|
||||||
expect(mockDisconnectWebSocket).toHaveBeenCalled();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
expect(mockDisconnectWebSocket).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('navigates to the next question', async () => {
|
test('navigates to the next question', async () => {
|
||||||
render(
|
act(() => {
|
||||||
<MemoryRouter>
|
fireEvent.click(screen.getByText('Option A'));
|
||||||
<StudentModeQuiz
|
|
||||||
questions={mockQuestions}
|
|
||||||
submitAnswer={mockSubmitAnswer}
|
|
||||||
disconnectWebSocket={mockDisconnectWebSocket}
|
|
||||||
/>
|
|
||||||
</MemoryRouter>);
|
|
||||||
|
|
||||||
fireEvent.click(screen.getByText('Option A'));
|
|
||||||
fireEvent.click(screen.getByText('Répondre'));
|
|
||||||
fireEvent.click(screen.getByText('Question suivante'));
|
|
||||||
|
|
||||||
await waitFor(() => {
|
|
||||||
const sampleQuestionElements = screen.queryAllByText(/Sample question 2/i);
|
|
||||||
expect(sampleQuestionElements.length).toBeGreaterThan(0);
|
|
||||||
expect(screen.getByText('V')).toBeInTheDocument();
|
|
||||||
});
|
});
|
||||||
|
act(() => {
|
||||||
|
fireEvent.click(screen.getByText('Répondre'));
|
||||||
|
});
|
||||||
|
act(() => {
|
||||||
|
fireEvent.click(screen.getByText('Question suivante'));
|
||||||
|
});
|
||||||
|
|
||||||
|
const sampleQuestionElements = screen.queryAllByText(/Sample question 2/i);
|
||||||
|
expect(sampleQuestionElements.length).toBeGreaterThan(0);
|
||||||
|
expect(screen.getByText('V')).toBeInTheDocument();
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('navigates to the previous question', async () => {
|
test('navigates to the previous question', async () => {
|
||||||
|
|
||||||
render(
|
act(() => {
|
||||||
<MemoryRouter>
|
fireEvent.click(screen.getByText('Option A'));
|
||||||
<StudentModeQuiz
|
|
||||||
questions={mockQuestions}
|
|
||||||
submitAnswer={mockSubmitAnswer}
|
|
||||||
disconnectWebSocket={mockDisconnectWebSocket}
|
|
||||||
/>
|
|
||||||
</MemoryRouter>);
|
|
||||||
|
|
||||||
fireEvent.click(screen.getByText('Option A'));
|
|
||||||
fireEvent.click(screen.getByText('Répondre'));
|
|
||||||
fireEvent.click(screen.getByText('Question précédente'));
|
|
||||||
|
|
||||||
await waitFor(() => {
|
|
||||||
expect(screen.getByText('Sample Question 1')).toBeInTheDocument();
|
|
||||||
expect(screen.getByText('Option B')).toBeInTheDocument();
|
|
||||||
});
|
});
|
||||||
|
act(() => {
|
||||||
|
fireEvent.click(screen.getByText('Répondre'));
|
||||||
|
});
|
||||||
|
act(() => {
|
||||||
|
fireEvent.click(screen.getByText('Question précédente'));
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(screen.getByText('Sample Question 1')).toBeInTheDocument();
|
||||||
|
expect(screen.getByText('Option B')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,36 +1,31 @@
|
||||||
//TeacherModeQuiz.test.tsx
|
//TeacherModeQuiz.test.tsx
|
||||||
import { render, screen, fireEvent } from '@testing-library/react';
|
import { render, fireEvent, act } from '@testing-library/react';
|
||||||
|
import { screen } from '@testing-library/dom';
|
||||||
import '@testing-library/jest-dom';
|
import '@testing-library/jest-dom';
|
||||||
import { GIFTQuestion } from 'gift-pegjs';
|
import { parse } from 'gift-pegjs';
|
||||||
|
|
||||||
import TeacherModeQuiz from '../../../../components/TeacherModeQuiz/TeacherModeQuiz';
|
import TeacherModeQuiz from '../../../../components/TeacherModeQuiz/TeacherModeQuiz';
|
||||||
import { MemoryRouter } from 'react-router-dom';
|
import { MemoryRouter } from 'react-router-dom';
|
||||||
|
// import { mock } from 'node:test';
|
||||||
|
|
||||||
|
const mockGiftQuestions = parse(
|
||||||
|
`::Sample Question:: Sample Question {=Option A ~Option B}`);
|
||||||
|
|
||||||
|
|
||||||
describe('TeacherModeQuiz', () => {
|
describe('TeacherModeQuiz', () => {
|
||||||
const mockQuestion: GIFTQuestion = {
|
const mockQuestion = mockGiftQuestions[0];
|
||||||
id: '1',
|
mockQuestion.id = '1';
|
||||||
type: 'MC',
|
|
||||||
stem: { format: 'plain', text: 'Sample Question' },
|
|
||||||
title: 'Sample Question',
|
|
||||||
hasEmbeddedAnswers: false,
|
|
||||||
globalFeedback: null,
|
|
||||||
choices: [
|
|
||||||
{ text: { format: 'plain', text: 'Option A' }, isCorrect: true, weight: 1, feedback: null },
|
|
||||||
{ text: { format: 'plain', text: 'Option B' }, isCorrect: false, weight: 0, feedback: null },
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
const mockSubmitAnswer = jest.fn();
|
const mockSubmitAnswer = jest.fn();
|
||||||
const mockDisconnectWebSocket = jest.fn();
|
const mockDisconnectWebSocket = jest.fn();
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(async () => {
|
||||||
render(
|
render(
|
||||||
<MemoryRouter>
|
<MemoryRouter>
|
||||||
<TeacherModeQuiz
|
<TeacherModeQuiz
|
||||||
questionInfos={{ question: mockQuestion }}
|
questionInfos={{ question: mockQuestion }}
|
||||||
submitAnswer={mockSubmitAnswer}
|
submitAnswer={mockSubmitAnswer}
|
||||||
disconnectWebSocket={mockDisconnectWebSocket}
|
disconnectWebSocket={mockDisconnectWebSocket} />
|
||||||
/>
|
|
||||||
</MemoryRouter>
|
</MemoryRouter>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
@ -45,16 +40,21 @@ describe('TeacherModeQuiz', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('handles answer submission and displays wait text', () => {
|
test('handles answer submission and displays wait text', () => {
|
||||||
fireEvent.click(screen.getByText('Option A'));
|
|
||||||
fireEvent.click(screen.getByText('Répondre'));
|
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
fireEvent.click(screen.getByText('Option A'));
|
||||||
|
});
|
||||||
|
act(() => {
|
||||||
|
fireEvent.click(screen.getByText('Répondre'));
|
||||||
|
});
|
||||||
expect(mockSubmitAnswer).toHaveBeenCalledWith('Option A', '1');
|
expect(mockSubmitAnswer).toHaveBeenCalledWith('Option A', '1');
|
||||||
expect(screen.getByText('En attente pour la prochaine question...')).toBeInTheDocument();
|
expect(screen.getByText('En attente pour la prochaine question...')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('handles disconnect button click', () => {
|
test('handles disconnect button click', () => {
|
||||||
fireEvent.click(screen.getByText('Quitter'));
|
act(() => {
|
||||||
|
fireEvent.click(screen.getByText('Quitter'));
|
||||||
|
});
|
||||||
expect(mockDisconnectWebSocket).toHaveBeenCalled();
|
expect(mockDisconnectWebSocket).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,28 +1,30 @@
|
||||||
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
|
import { render, screen, fireEvent } 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 Dashboard from '../../../../pages/Teacher/Dashboard/Dashboard';
|
import Dashboard from '../../../../pages/Teacher/Dashboard/Dashboard';
|
||||||
|
import { act } from 'react';
|
||||||
|
|
||||||
const localStorageMock = (() => {
|
// const localStorageMock = (() => {
|
||||||
let store: Record<string, string> = {};
|
// let store: Record<string, string> = {};
|
||||||
return {
|
// return {
|
||||||
getItem: (key: string) => store[key] || null,
|
// getItem: (key: string) => store[key] || null,
|
||||||
setItem: (key: string, value: string) => (store[key] = value.toString()),
|
// setItem: (key: string, value: string) => (store[key] = value.toString()),
|
||||||
clear: () => (store = {}),
|
// clear: () => (store = {}),
|
||||||
};
|
// };
|
||||||
})();
|
// })();
|
||||||
Object.defineProperty(window, 'localStorage', { value: localStorageMock });
|
// Object.defineProperty(window, 'localStorage', { value: localStorageMock });
|
||||||
|
|
||||||
jest.mock('react-router-dom', () => ({
|
jest.mock('react-router-dom', () => ({
|
||||||
...jest.requireActual('react-router-dom'),
|
...jest.requireActual('react-router-dom'),
|
||||||
useNavigate: jest.fn(),
|
useNavigate: jest.fn(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
// NOTE: these tests are for an old version that kept quizzes in local storage and had no login
|
||||||
|
|
||||||
describe.skip('Dashboard Component', () => {
|
describe.skip('Dashboard Component', () => {
|
||||||
beforeEach(() => {
|
// beforeEach(() => {
|
||||||
localStorage.setItem('quizzes', JSON.stringify([]));
|
// localStorage.setItem('quizzes', JSON.stringify([]));
|
||||||
});
|
// });
|
||||||
|
|
||||||
test('renders Dashboard with default state', () => {
|
test('renders Dashboard with default state', () => {
|
||||||
render(
|
render(
|
||||||
|
|
@ -33,7 +35,7 @@ describe.skip('Dashboard Component', () => {
|
||||||
expect(screen.getByText(/Tableau de bord/i)).toBeInTheDocument();
|
expect(screen.getByText(/Tableau de bord/i)).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('adds a quiz and checks if it is displayed', () => {
|
test.skip('adds a quiz and checks if it is displayed', () => {
|
||||||
const mockQuizzes = [
|
const mockQuizzes = [
|
||||||
{
|
{
|
||||||
id: '1',
|
id: '1',
|
||||||
|
|
@ -52,17 +54,18 @@ describe.skip('Dashboard Component', () => {
|
||||||
expect(screen.getByText(/Sample Quiz/i)).toBeInTheDocument();
|
expect(screen.getByText(/Sample Quiz/i)).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('opens ImportModal when "Importer" button is clicked', async () => {
|
test.skip('opens ImportModal when "Importer" button is clicked', async () => {
|
||||||
render(
|
render(
|
||||||
<MemoryRouter>
|
<MemoryRouter>
|
||||||
<Dashboard />
|
<Dashboard />
|
||||||
</MemoryRouter>
|
</MemoryRouter>
|
||||||
);
|
);
|
||||||
|
|
||||||
fireEvent.click(screen.getByText(/Importer/i));
|
act(() => {
|
||||||
await waitFor(() => {
|
fireEvent.click(screen.getByText(/Importer/i));
|
||||||
expect(screen.getByText(/Importation de quiz/i)).toBeInTheDocument();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
expect(screen.getByText(/Importation de quiz/i)).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ Object.defineProperty(window, 'localStorage', {
|
||||||
value: localStorageMock
|
value: localStorageMock
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// NOTE: this suite seems to be designed around local storage of quizzes (older version, before a database)
|
||||||
describe.skip('QuizService', () => {
|
describe.skip('QuizService', () => {
|
||||||
const mockQuizzes: QuizType[] = [
|
const mockQuizzes: QuizType[] = [
|
||||||
{ folderId: 'test', userId: 'user', _id: 'quiz1', title: 'Quiz One', content: ['Q1', 'Q2'], created_at: new Date('2024-09-15'), updated_at: new Date('2024-09-15') },
|
{ folderId: 'test', userId: 'user', _id: 'quiz1', title: 'Quiz One', content: ['Q1', 'Q2'], created_at: new Date('2024-09-15'), updated_at: new Date('2024-09-15') },
|
||||||
|
|
|
||||||
|
|
@ -67,8 +67,8 @@ const multiple: GIFTQuestion[] = [
|
||||||
hasEmbeddedAnswers: false,
|
hasEmbeddedAnswers: false,
|
||||||
globalFeedback: null,
|
globalFeedback: null,
|
||||||
isTrue: false,
|
isTrue: false,
|
||||||
incorrectFeedback: null,
|
falseFeedback: null,
|
||||||
correctFeedback: null
|
trueFeedback: null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'Short',
|
type: 'Short',
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ function formatLatex(text: string): string {
|
||||||
* @see marked
|
* @see marked
|
||||||
* @see katex
|
* @see katex
|
||||||
*/
|
*/
|
||||||
export default function TextType({ text }: TextTypeOptions): string {
|
export default function TextType({ text }: TextTypeOptions) {
|
||||||
const formatText = formatLatex(text.text.trim()); // latex needs pure "&", ">", etc. Must not be escaped
|
const formatText = formatLatex(text.text.trim()); // latex needs pure "&", ">", etc. Must not be escaped
|
||||||
|
|
||||||
switch (text.format) {
|
switch (text.format) {
|
||||||
|
|
@ -40,12 +40,8 @@ export default function TextType({ text }: TextTypeOptions): string {
|
||||||
// Strip outer paragraph tags (not a great approach with regex)
|
// Strip outer paragraph tags (not a great approach with regex)
|
||||||
return formatText.replace(/(^<p>)(.*?)(<\/p>)$/gm, '$2');
|
return formatText.replace(/(^<p>)(.*?)(<\/p>)$/gm, '$2');
|
||||||
case 'markdown':
|
case 'markdown':
|
||||||
return (
|
const parsedText = marked.parse(formatText, { breaks: true }) as string; // https://github.com/markedjs/marked/discussions/3219
|
||||||
marked
|
return parsedText.replace(/(^<p>)(.*?)(<\/p>)$/gm, '$2');
|
||||||
.parse(formatText, { breaks: true }) // call marked.parse instead of marked
|
|
||||||
// Strip outer paragraph tags
|
|
||||||
.replace(/(^<p>)(.*?)(<\/p>)$/gm, '$2')
|
|
||||||
);
|
|
||||||
default:
|
default:
|
||||||
throw new Error(`Unsupported text format: ${text.format}`);
|
throw new Error(`Unsupported text format: ${text.format}`);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,8 @@ export default function TrueFalse({
|
||||||
title,
|
title,
|
||||||
isTrue,
|
isTrue,
|
||||||
stem,
|
stem,
|
||||||
correctFeedback,
|
trueFeedback: trueFeedback,
|
||||||
incorrectFeedback,
|
falseFeedback: falseFeedback,
|
||||||
globalFeedback
|
globalFeedback
|
||||||
}: TrueFalseOptions): string {
|
}: TrueFalseOptions): string {
|
||||||
const choices: TextChoice[] = [
|
const choices: TextChoice[] = [
|
||||||
|
|
@ -25,7 +25,7 @@ export default function TrueFalse({
|
||||||
},
|
},
|
||||||
isCorrect: isTrue,
|
isCorrect: isTrue,
|
||||||
weight: null,
|
weight: null,
|
||||||
feedback: isTrue ? correctFeedback : incorrectFeedback
|
feedback: isTrue ? trueFeedback : falseFeedback
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: {
|
text: {
|
||||||
|
|
@ -34,7 +34,7 @@ export default function TrueFalse({
|
||||||
},
|
},
|
||||||
isCorrect: !isTrue,
|
isCorrect: !isTrue,
|
||||||
weight: null,
|
weight: null,
|
||||||
feedback: !isTrue ? correctFeedback : incorrectFeedback
|
feedback: !isTrue ? trueFeedback : falseFeedback
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,31 +12,9 @@ export interface DisplayOptions {
|
||||||
preview: boolean;
|
preview: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type QuestionType =
|
export {
|
||||||
| 'Description'
|
QuestionType, FormatType, NumericalType, TextFormat, NumericalFormat, TextChoice, NumericalChoice, Question, Description, Category, MultipleChoice, ShortAnswer, Numerical, Essay, TrueFalse,
|
||||||
| 'Category'
|
Matching, Match, GIFTQuestion } from 'gift-pegjs';
|
||||||
| 'MC'
|
|
||||||
| 'Numerical'
|
|
||||||
| 'Short'
|
|
||||||
| 'Essay'
|
|
||||||
| 'TF'
|
|
||||||
| 'Matching';
|
|
||||||
|
|
||||||
export type FormatType = 'moodle' | 'html' | 'markdown' | 'plain';
|
|
||||||
export type NumericalType = 'simple' | 'range' | 'high-low';
|
|
||||||
|
|
||||||
export interface TextFormat {
|
|
||||||
format: FormatType;
|
|
||||||
text: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface NumericalFormat {
|
|
||||||
type: NumericalType;
|
|
||||||
number?: number;
|
|
||||||
range?: number;
|
|
||||||
numberHigh?: number;
|
|
||||||
numberLow?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Choice {
|
export interface Choice {
|
||||||
isCorrect: boolean;
|
isCorrect: boolean;
|
||||||
|
|
@ -44,77 +22,3 @@ export interface Choice {
|
||||||
text: TextFormat | NumericalFormat;
|
text: TextFormat | NumericalFormat;
|
||||||
feedback: TextFormat | null;
|
feedback: TextFormat | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TextChoice extends Choice {
|
|
||||||
text: TextFormat;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface NumericalChoice extends Choice {
|
|
||||||
text: NumericalFormat;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Question {
|
|
||||||
type: QuestionType;
|
|
||||||
title: string | null;
|
|
||||||
stem: TextFormat;
|
|
||||||
hasEmbeddedAnswers: boolean;
|
|
||||||
globalFeedback: TextFormat | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Description {
|
|
||||||
type: Extract<QuestionType, 'Description'>;
|
|
||||||
title: string | null;
|
|
||||||
stem: TextFormat;
|
|
||||||
hasEmbeddedAnswers: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Category {
|
|
||||||
type: Extract<QuestionType, 'Category'>;
|
|
||||||
title: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface MultipleChoice extends Question {
|
|
||||||
type: Extract<QuestionType, 'MC'>;
|
|
||||||
choices: TextChoice[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ShortAnswer extends Question {
|
|
||||||
type: Extract<QuestionType, 'Short'>;
|
|
||||||
choices: TextChoice[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Numerical extends Question {
|
|
||||||
type: Extract<QuestionType, 'Numerical'>;
|
|
||||||
choices: NumericalChoice[] | NumericalFormat;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Essay extends Question {
|
|
||||||
type: Extract<QuestionType, 'Essay'>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface TrueFalse extends Question {
|
|
||||||
type: Extract<QuestionType, 'TF'>;
|
|
||||||
isTrue: boolean;
|
|
||||||
incorrectFeedback: TextFormat | null;
|
|
||||||
correctFeedback: TextFormat | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Matching extends Question {
|
|
||||||
type: Extract<QuestionType, 'Matching'>;
|
|
||||||
matchPairs: Match[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Match {
|
|
||||||
subquestion: TextFormat;
|
|
||||||
subanswer: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type GIFTQuestion =
|
|
||||||
| Description
|
|
||||||
| Category
|
|
||||||
| MultipleChoice
|
|
||||||
| ShortAnswer
|
|
||||||
| Numerical
|
|
||||||
| Essay
|
|
||||||
| TrueFalse
|
|
||||||
| Matching;
|
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ type Choices = {
|
||||||
};
|
};
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
questionContent: TextFormat;
|
questionStem: TextFormat;
|
||||||
choices: Choices[];
|
choices: Choices[];
|
||||||
globalFeedback?: string | undefined;
|
globalFeedback?: string | undefined;
|
||||||
handleOnSubmitAnswer?: (answer: string) => void;
|
handleOnSubmitAnswer?: (answer: string) => void;
|
||||||
|
|
@ -22,7 +22,7 @@ interface Props {
|
||||||
}
|
}
|
||||||
|
|
||||||
const MultipleChoiceQuestion: React.FC<Props> = (props) => {
|
const MultipleChoiceQuestion: React.FC<Props> = (props) => {
|
||||||
const { questionContent, choices, showAnswer, handleOnSubmitAnswer, globalFeedback } = props;
|
const { questionStem: questionContent, choices, showAnswer, handleOnSubmitAnswer, globalFeedback } = props;
|
||||||
const [answer, setAnswer] = useState<string>();
|
const [answer, setAnswer] = useState<string>();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ const Question: React.FC<QuestionProps> = ({
|
||||||
case 'MC':
|
case 'MC':
|
||||||
questionTypeComponent = (
|
questionTypeComponent = (
|
||||||
<MultipleChoiceQuestion
|
<MultipleChoiceQuestion
|
||||||
questionContent={question.stem}
|
questionStem={question.stem}
|
||||||
choices={question.choices}
|
choices={question.choices}
|
||||||
handleOnSubmitAnswer={handleOnSubmitAnswer}
|
handleOnSubmitAnswer={handleOnSubmitAnswer}
|
||||||
showAnswer={showAnswer}
|
showAnswer={showAnswer}
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ const TrueFalseQuestion: React.FC<Props> = (props) => {
|
||||||
return (
|
return (
|
||||||
<div className="question-container">
|
<div className="question-container">
|
||||||
<div className="question content">
|
<div className="question content">
|
||||||
<div dangerouslySetInnerHTML={{ __html: TextType({text: questionContent}) }} />
|
<div dangerouslySetInnerHTML={{ __html: TextType({ text: questionContent }) }} />
|
||||||
</div>
|
</div>
|
||||||
<div className="choices-wrapper mb-1">
|
<div className="choices-wrapper mb-1">
|
||||||
<Button
|
<Button
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ const Dashboard: React.FC = () => {
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchData = async () => {
|
const fetchData = async () => {
|
||||||
if (!ApiService.isLogedIn()) {
|
if (!ApiService.isLoggedIn()) {
|
||||||
navigate("/teacher/login");
|
navigate("/teacher/login");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ const Share: React.FC = () => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ApiService.isLogedIn()) {
|
if (!ApiService.isLoggedIn()) {
|
||||||
window.alert(`Vous n'êtes pas connecté.\nVeuillez vous connecter et revenir à ce lien`);
|
window.alert(`Vous n'êtes pas connecté.\nVeuillez vous connecter et revenir à ce lien`);
|
||||||
navigate("/teacher/login");
|
navigate("/teacher/login");
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ class ApiService {
|
||||||
}
|
}
|
||||||
|
|
||||||
private constructRequestHeaders(): any {
|
private constructRequestHeaders(): any {
|
||||||
if (this.isLogedIn()) {
|
if (this.isLoggedIn()) {
|
||||||
return {
|
return {
|
||||||
Authorization: `Bearer ${this.getToken()}`,
|
Authorization: `Bearer ${this.getToken()}`,
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
|
|
@ -63,7 +63,7 @@ class ApiService {
|
||||||
return object.token;
|
return object.token;
|
||||||
}
|
}
|
||||||
|
|
||||||
public isLogedIn(): boolean {
|
public isLoggedIn(): boolean {
|
||||||
const token = this.getToken()
|
const token = this.getToken()
|
||||||
|
|
||||||
if (token == null) {
|
if (token == null) {
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ class WebSocketService {
|
||||||
private socket: Socket | null = null;
|
private socket: Socket | null = null;
|
||||||
|
|
||||||
connect(backendUrl: string): Socket {
|
connect(backendUrl: string): Socket {
|
||||||
console.log(backendUrl);
|
// console.log(backendUrl);
|
||||||
this.socket = io(`${backendUrl}`, {
|
this.socket = io(`${backendUrl}`, {
|
||||||
transports: ['websocket'],
|
transports: ['websocket'],
|
||||||
reconnectionAttempts: 1
|
reconnectionAttempts: 1
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue