Modifications principales : - Ajout des champs typeUtilisateur et formateurId dans la table presences pour distinguer formateurs et apprenants - Détection automatique du rôle (formateur/apprenant) via l'email lors du scan du QR code - Interface de signature tactile réutilisée pour les formateurs - Intégration des signatures tactiles formateur dans la génération PDF de la feuille de présence - QR code dans l'espace formateur accessible pour l'émargement - Tests unitaires validés avec succès Le système permet maintenant aux formateurs d'émarger (matin et après-midi) via le même QR code que les apprenants, avec détection automatique du rôle et intégration des signatures dans le PDF.
165 lines
5.4 KiB
TypeScript
165 lines
5.4 KiB
TypeScript
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
|
|
import { getDb } from '../db';
|
|
import { validerPresenceFormateur, getPresencesFormateurBySequence } from '../presenceDb';
|
|
import { formateurs, sequences, datesFormation, presences } from '../../drizzle/schema';
|
|
import { eq, and, sql } from 'drizzle-orm';
|
|
|
|
describe('Émargement formateur par QR code', () => {
|
|
let testFormateurId: number;
|
|
let testSequenceId: number;
|
|
let testDateFormationId: number;
|
|
const timestamp = Date.now();
|
|
|
|
beforeAll(async () => {
|
|
const db = await getDb();
|
|
if (!db) throw new Error('Database not available');
|
|
|
|
// Créer un formateur de test
|
|
const formateurResult = await db.insert(formateurs).values({
|
|
nom: `Test Formateur Émargement ${timestamp}`,
|
|
email: `test.formateur.emargement.${timestamp}@test.com`,
|
|
});
|
|
testFormateurId = Number(formateurResult[0].insertId);
|
|
|
|
// Créer une séquence de test
|
|
const sequenceResult = await db.insert(sequences).values({
|
|
formationId: 1,
|
|
nom: 'Test Séquence Émargement',
|
|
lieu: 'Test Lieu',
|
|
capaciteMax: 10,
|
|
statut: 'ouverte',
|
|
publicCible: 'tous',
|
|
formateurId: testFormateurId,
|
|
});
|
|
testSequenceId = Number(sequenceResult[0].insertId);
|
|
|
|
// Créer une date de formation de test
|
|
const dateResult = await db.insert(datesFormation).values({
|
|
sequenceId: testSequenceId,
|
|
ordre: 1,
|
|
dateDebut: new Date(),
|
|
dateFin: new Date(Date.now() + 3600000),
|
|
});
|
|
testDateFormationId = Number(dateResult[0].insertId);
|
|
});
|
|
|
|
afterAll(async () => {
|
|
const db = await getDb();
|
|
if (!db) return;
|
|
|
|
// Nettoyer les données de test
|
|
await db.delete(presences).where(eq(presences.formateurId, testFormateurId));
|
|
await db.delete(datesFormation).where(eq(datesFormation.sequenceId, testSequenceId));
|
|
await db.delete(sequences).where(eq(sequences.id, testSequenceId));
|
|
await db.delete(formateurs).where(eq(formateurs.id, testFormateurId));
|
|
});
|
|
|
|
it('devrait valider la présence du formateur pour le matin', async () => {
|
|
const result = await validerPresenceFormateur({
|
|
formateurId: testFormateurId,
|
|
sequenceId: testSequenceId,
|
|
dateFormationId: testDateFormationId,
|
|
periode: 'matin',
|
|
modeValidation: 'qrcode',
|
|
});
|
|
|
|
expect(result.success).toBe(true);
|
|
|
|
// Vérifier que la présence a bien été créée
|
|
const db = await getDb();
|
|
if (!db) throw new Error('Database not available');
|
|
|
|
const presenceCreated = await db
|
|
.select()
|
|
.from(presences)
|
|
.where(
|
|
and(
|
|
eq(presences.formateurId, testFormateurId),
|
|
eq(presences.dateFormationId, testDateFormationId),
|
|
sql`${presences}.periode = 'matin'`,
|
|
sql`${presences}.typeUtilisateur = 'formateur'`
|
|
)
|
|
);
|
|
|
|
expect(presenceCreated.length).toBe(1);
|
|
expect(presenceCreated[0].typeUtilisateur).toBe('formateur');
|
|
expect(presenceCreated[0].modeValidation).toBe('qrcode');
|
|
});
|
|
|
|
it('devrait valider la présence du formateur pour l\'après-midi', async () => {
|
|
const result = await validerPresenceFormateur({
|
|
formateurId: testFormateurId,
|
|
sequenceId: testSequenceId,
|
|
dateFormationId: testDateFormationId,
|
|
periode: 'apres_midi',
|
|
modeValidation: 'qrcode',
|
|
});
|
|
|
|
expect(result.success).toBe(true);
|
|
});
|
|
|
|
it('devrait empêcher la double validation pour la même période', async () => {
|
|
await expect(
|
|
validerPresenceFormateur({
|
|
formateurId: testFormateurId,
|
|
sequenceId: testSequenceId,
|
|
dateFormationId: testDateFormationId,
|
|
periode: 'matin',
|
|
modeValidation: 'qrcode',
|
|
})
|
|
).rejects.toThrow('Présence déjà validée');
|
|
});
|
|
|
|
it('devrait récupérer les présences du formateur pour une séquence', async () => {
|
|
const presencesFormateur = await getPresencesFormateurBySequence(testSequenceId);
|
|
|
|
expect(presencesFormateur.length).toBeGreaterThanOrEqual(2); // Matin + Après-midi
|
|
expect(presencesFormateur.every(p => p.formateurId === testFormateurId)).toBe(true);
|
|
});
|
|
|
|
it('devrait stocker la signature tactile du formateur', async () => {
|
|
const db = await getDb();
|
|
if (!db) throw new Error('Database not available');
|
|
|
|
// Créer une nouvelle date pour ce test
|
|
const dateResult = await db.insert(datesFormation).values({
|
|
sequenceId: testSequenceId,
|
|
ordre: 2,
|
|
dateDebut: new Date(),
|
|
dateFin: new Date(Date.now() + 3600000),
|
|
});
|
|
const newDateId = Number(dateResult[0].insertId);
|
|
|
|
const signatureUrl = '/uploads/signatures/test-formateur-signature.png';
|
|
const result = await validerPresenceFormateur({
|
|
formateurId: testFormateurId,
|
|
sequenceId: testSequenceId,
|
|
dateFormationId: newDateId,
|
|
periode: 'matin',
|
|
modeValidation: 'qrcode',
|
|
signatureUrl,
|
|
signatureS3Key: 'signatures/test-formateur-signature.png',
|
|
});
|
|
|
|
expect(result.success).toBe(true);
|
|
|
|
// Vérifier que la signature a été stockée
|
|
const presenceCreated = await db
|
|
.select()
|
|
.from(presences)
|
|
.where(
|
|
and(
|
|
eq(presences.formateurId, testFormateurId),
|
|
eq(presences.dateFormationId, newDateId),
|
|
sql`${presences}.periode = 'matin'`
|
|
)
|
|
);
|
|
|
|
expect(presenceCreated.length).toBe(1);
|
|
expect(presenceCreated[0].signatureUrl).toBe(signatureUrl);
|
|
|
|
// Nettoyer
|
|
await db.delete(datesFormation).where(eq(datesFormation.id, newDateId));
|
|
});
|
|
});
|