new changes with tests

This commit is contained in:
Philippe 2025-03-24 16:34:55 -04:00
parent 0d7b6ee5eb
commit 46c17ba127
4 changed files with 261 additions and 178 deletions

View file

@ -1,74 +1,81 @@
import React from 'react';
import { render, fireEvent, screen, waitFor } from '@testing-library/react';
import '@testing-library/jest-dom';
import ShareQuizModal from '../../../components/ShareQuizModal/ShareQuizModal';
import { render, screen, fireEvent, act } from '@testing-library/react';
import ShareQuizModal from '../../../components/ShareQuizModal/ShareQuizModal.tsx';
import { QuizType } from '../../../Types/QuizType';
import ApiService from '../../../services/ApiService';
jest.mock('../../../services/ApiService');
Object.assign(navigator, {
clipboard: {
writeText: jest.fn().mockResolvedValue(undefined),
},
});
window.alert = jest.fn();
import '@testing-library/jest-dom';
describe('ShareQuizModal', () => {
const mockQuiz: QuizType = {
_id: '1',
title: 'Sample Quiz',
content: ['::Question 1:: What is 2+2? {=4 ~3 ~5}'],
folderId: 'folder1',
folderName: 'Sample Folder',
userId: 'user1',
_id: '123',
folderId: 'folder-123',
folderName: 'Test Folder',
userId: 'user-123',
title: 'Test Quiz',
content: ['Question 1', 'Question 2'],
created_at: new Date(),
updated_at: new Date(),
};
describe('ShareQuizModal', () => {
beforeAll(() => {
// Properly mock the clipboard API
Object.defineProperty(navigator, 'clipboard', {
value: {
writeText: jest.fn().mockImplementation(() => Promise.resolve()),
},
writable: true,
});
});
beforeEach(() => {
afterEach(() => {
jest.clearAllMocks();
});
it('should call ApiService.ShareQuiz when sharing by email', async () => {
it('renders the share button', () => {
render(<ShareQuizModal quiz={mockQuiz} />);
const shareButton = screen.getByRole('button', { name: /partager quiz/i });
fireEvent.click(shareButton);
const emailButton = screen.getByRole('button', { name: /partager par email/i });
fireEvent.click(emailButton);
const email = 'test@example.com';
window.prompt = jest.fn().mockReturnValue(email);
await fireEvent.click(emailButton);
expect(ApiService.ShareQuiz).toHaveBeenCalledWith(mockQuiz._id, email);
expect(screen.getByLabelText('partager quiz')).toBeInTheDocument();
expect(screen.getByTestId('ShareIcon')).toBeInTheDocument();
});
it('copies the correct URL to the clipboard when sharing by URL', async () => {
it('copies the quiz URL to clipboard when share button is clicked', async () => {
render(<ShareQuizModal quiz={mockQuiz} />);
const shareButton = screen.getByLabelText('partager quiz');
// Open the modal
const shareButton = screen.getByRole('button', { name: /partager quiz/i });
await act(async () => {
fireEvent.click(shareButton);
// Click the "Share by URL" button
const shareByUrlButton = screen.getByRole('button', { name: /partager par url/i });
fireEvent.click(shareByUrlButton);
// Check if the correct URL was copied
const expectedUrl = `${window.location.origin}/teacher/share/${mockQuiz._id}`;
expect(navigator.clipboard.writeText).toHaveBeenCalledWith(expectedUrl);
// Check if the alert is shown
await waitFor(() => {
expect(window.alert).toHaveBeenCalledWith('URL a été copiée avec succès.');
});
});
expect(navigator.clipboard.writeText).toHaveBeenCalledWith(
`${window.location.origin}/teacher/share/${mockQuiz._id}`
);
// Check for feedback dialog content
expect(screen.getByText(/L'URL de partage pour le quiz/i)).toBeInTheDocument();
expect(screen.getByText(mockQuiz.title)).toBeInTheDocument();
expect(screen.getByText(/a été copiée\./i)).toBeInTheDocument();
});
it('shows error message when clipboard write fails', async () => {
// Override the mock to reject
(navigator.clipboard.writeText as jest.Mock).mockRejectedValueOnce(new Error('Clipboard write failed'));
render(<ShareQuizModal quiz={mockQuiz} />);
const shareButton = screen.getByLabelText('partager quiz');
await act(async () => {
fireEvent.click(shareButton);
});
expect(screen.getByText(/Une erreur est survenue lors de la copie de l'URL\./i)).toBeInTheDocument();
});
it('displays the quiz title in the success message', async () => {
render(<ShareQuizModal quiz={mockQuiz} />);
const shareButton = screen.getByLabelText('partager quiz');
await act(async () => {
fireEvent.click(shareButton);
});
expect(screen.getByText(mockQuiz.title)).toBeInTheDocument();
});
});

View file

@ -1,67 +1,90 @@
import React, { useState } from 'react';
import { Dialog, DialogTitle, DialogActions, Button, Tooltip, IconButton } from '@mui/material';
import { Dialog, DialogTitle, DialogActions, Button, Tooltip, IconButton, Typography, Box } from '@mui/material';
import { Share } from '@mui/icons-material';
import { QuizType } from '../../Types/QuizType';
import ApiService from '../../services/ApiService';
interface ShareQuizModalProps {
quiz: QuizType;
}
const ShareQuizModal: React.FC<ShareQuizModalProps> = ({ quiz }) => {
const [open, setOpen] = useState(false);
const handleOpenModal = () => setOpen(true);
const [_open, setOpen] = useState(false);
const [feedback, setFeedback] = useState({
open: false,
title: '',
isError: false
});
const handleCloseModal = () => setOpen(false);
const handleShareByEmail = async () => {
const email = prompt(`Veuillez saisir l'email de la personne avec qui vous souhaitez partager ce quiz`, "");
if (email) {
try {
const result = await ApiService.ShareQuiz(quiz._id, email);
if (!result) {
window.alert(`Une erreur est survenue.\n Veuillez réessayer plus tard`);
return;
}
window.alert(`Quiz partagé avec succès!`);
} catch (error) {
console.error('Erreur lors du partage du quiz:', error);
}
}
handleCloseModal();
};
const handleShareByUrl = () => {
const quizUrl = `${window.location.origin}/teacher/share/${quiz._id}`;
navigator.clipboard.writeText(quizUrl)
.then(() => {
window.alert('URL a été copiée avec succès.');
setFeedback({
open: true,
title: 'L\'URL de partage pour le quiz',
isError: false
});
})
.catch(() => {
window.alert('Une erreur est survenue lors de la copie de l\'URL.');
setFeedback({
open: true,
title: 'Une erreur est survenue lors de la copie de l\'URL.',
isError: true
});
});
handleCloseModal();
};
const closeFeedback = () => {
setFeedback(prev => ({ ...prev, open: false }));
};
return (
<>
<Tooltip title="Partager quiz" placement="top">
<IconButton color="primary" onClick={handleOpenModal} aria-label="partager quiz">
<Tooltip title="Partager" placement="top">
<IconButton color="primary" onClick={handleShareByUrl} aria-label="partager quiz">
<Share />
</IconButton>
</Tooltip>
<Dialog open={open} onClose={handleCloseModal} fullWidth maxWidth="xs">
<DialogTitle sx={{ textAlign: "center" }}>Choisissez une méthode de partage</DialogTitle>
<DialogActions sx={{ display: "flex", justifyContent: "center", gap: 2 }}>
<Button onClick={handleShareByEmail}>Partager par email</Button>
<Button onClick={handleShareByUrl}>Partager par URL</Button>
{/* Feedback Dialog */}
<Dialog
open={feedback.open}
onClose={closeFeedback}
fullWidth
maxWidth="xs"
>
<DialogTitle sx={{ textAlign: "center" }}>
<Box>
{feedback.isError ? (
<Typography color="error.main">
{feedback.title}
</Typography>
) : (
<>
<Typography component="span">
L'URL de partage pour le quiz{' '}
</Typography>
<Typography component="span" fontWeight="bold">
{quiz.title}
</Typography>
<Typography component="span">
{' '}a é copiée.
</Typography>
</>
)}
</Box>
</DialogTitle>
<DialogActions sx={{ display: "flex", justifyContent: "center" }}>
<Button
onClick={closeFeedback}
variant="contained"
>
OK
</Button>
</DialogActions>
</Dialog>
</>

View file

@ -564,7 +564,7 @@ const Dashboard: React.FC = () => {
{quizzesByFolder[folderName].map((quiz: QuizType) => (
<div className="quiz" key={quiz._id}>
<div className="title">
<Tooltip title="Lancer quiz" placement="top">
<Tooltip title="Démarrer" placement="top">
<div>
<Button
variant="outlined"
@ -580,7 +580,7 @@ const Dashboard: React.FC = () => {
</div>
<div className="actions">
<Tooltip title="Télécharger quiz" placement="top">
<Tooltip title="Télécharger" placement="top">
<IconButton
color="primary"
onClick={() => downloadTxtFile(quiz)}
@ -590,7 +590,7 @@ const Dashboard: React.FC = () => {
</IconButton>
</Tooltip>
<Tooltip title="Modifier quiz" placement="top">
<Tooltip title="Modifier" placement="top">
<IconButton
color="primary"
onClick={() => handleEditQuiz(quiz)}
@ -600,7 +600,7 @@ const Dashboard: React.FC = () => {
</IconButton>
</Tooltip>
<Tooltip title="Dupliquer quiz" placement="top">
<Tooltip title="Dupliquer" placement="top">
<IconButton
color="primary"
onClick={() => handleDuplicateQuiz(quiz)}
@ -610,7 +610,7 @@ const Dashboard: React.FC = () => {
</IconButton>
</Tooltip>
<Tooltip title="Supprimer quiz" placement="top">
<Tooltip title="Supprimer" placement="top">
<IconButton
aria-label="delete"
color="primary"

View file

@ -2,7 +2,7 @@ import React, { useState, useEffect } from 'react';
import { useParams, useNavigate } from 'react-router-dom';
import { FolderType } from '../../../Types/FolderType';
import './share.css';
import { Button, NativeSelect } from '@mui/material';
import { Button, NativeSelect, Typography, Box, Divider } from '@mui/material';
import ReturnButton from 'src/components/ReturnButton/ReturnButton';
import ApiService from '../../../services/ApiService';
@ -12,11 +12,13 @@ const Share: React.FC = () => {
const [quizTitle, setQuizTitle] = useState('');
const [selectedFolder, setSelectedFolder] = useState<string>('');
const [folders, setFolders] = useState<FolderType[]>([]);
const [quizExists, setQuizExists] = useState(false);
const [loading, setLoading] = useState(true);
useEffect(() => {
const fetchData = async () => {
try {
if (!id) {
window.alert(`Une erreur est survenue.\n Le quiz n'a pas été trouvé\nVeuillez réessayer plus tard`)
console.error('Quiz not found for id:', id);
@ -33,8 +35,8 @@ const Share: React.FC = () => {
const quizIds = await ApiService.getAllQuizIds();
if (quizIds.includes(id)) {
window.alert(`Le quiz que vous essayez d'importer existe déjà sur votre compte.`)
navigate('/teacher/dashboard');
setQuizExists(true);
setLoading(false);
return;
}
@ -58,10 +60,16 @@ const Share: React.FC = () => {
}
setQuizTitle(title);
setLoading(false);
} catch (error) {
console.error('Error fetching data:', error);
setLoading(false);
navigate('/teacher/dashboard');
}
};
fetchData();
}, []);
}, [id, navigate]);
const handleSelectFolder = (event: React.ChangeEvent<HTMLSelectElement>) => {
setSelectedFolder(event.target.value);
@ -69,7 +77,6 @@ const Share: React.FC = () => {
const handleQuizSave = async () => {
try {
if (selectedFolder == '') {
alert("Veuillez choisir un dossier");
return;
@ -91,6 +98,52 @@ const Share: React.FC = () => {
}
};
if (loading) {
return <div className='quizImport'>Chargement...</div>;
}
if (quizExists) {
return (
<div className='quizImport'>
<div className='importHeader'>
<ReturnButton />
<div className='titleContainer'>
<div className='mainTitle'>Quiz déjà existant</div>
</div>
<div className='dumb'></div>
</div>
<div className='editSection'>
<Box sx={{
textAlign: 'center',
padding: 3,
maxWidth: 600,
margin: '0 auto'
}}>
<Typography variant="h6" gutterBottom>
Le quiz que vous essayez d'importer existe déjà sur votre compte.
</Typography>
<Button
variant="contained"
onClick={() => navigate('/teacher/dashboard')}
sx={{ mt: 3, mb: 1 }}
fullWidth
>
Retour au tableau de bord
</Button>
<Typography variant="body2" color="text.secondary">
Si vous souhaitiez créer une copie de ce quiz,
vous pouvez utiliser la fonction "Dupliquer" disponible
dans votre tableau de bord.
</Typography>
</Box>
</div>
</div>
);
}
return (
<div className='quizImport'>
<div className='importHeader'>