Boulangerie_Website/frontend/tests/weeklyExport.test.js
Louis Mazin e360b0b684 nickel
2026-10-10 20:16:43 +02:00

113 lines
5.9 KiB
JavaScript

import test from 'node:test';
import assert from 'node:assert/strict';
import { buildWeeklyPdfPages, validateExportPeriod } from '../src/lib/weeklyExport.js';
import { createWeeklyPdfBytes } from '../src/lib/weeklyPdf.js';
const planning = {
days: [
{ date: '2026-10-05', schedule_mode: 'precise', label: 'Habituel', kind: 'work' },
{ date: '2026-10-06', schedule_mode: 'precise', label: 'Repos', kind: 'rest' },
{ date: '2026-10-07', schedule_mode: 'daily_hours', target_minutes: 420, label: 'Heures à effectuer', kind: 'work' },
{ date: '2026-10-08', schedule_mode: 'precise', label: 'Absence', kind: 'absence' },
],
segments: [
{ date: '2026-10-05', start_time: '08:00', end_time: '12:00', label: 'Habituel' },
{ date: '2026-10-05', start_time: '13:00', end_time: '17:00', label: 'Habituel' },
],
};
const attendances = [
{ start_date: '2026-10-05', end_date: '2026-10-05', start_time: '08:03:00', end_time: '12:00:00', duration: 3.95 },
{ start_date: '2026-10-05', end_date: '2026-10-05', start_time: '13:00:00', end_time: '17:00:00', duration: 4 },
{ start_date: '2026-10-06', end_date: '2026-10-06', start_time: '08:00:00', end_time: null, duration: null },
];
const base = { start: '2026-10-05', end: '2026-10-07', name: 'Émilie',
withPlanning: true, withPointage: true, planning, attendances };
const pdfString = (pages) => Buffer.from(createWeeklyPdfBytes(pages, new Date('2026-10-10T12:00:00Z'))).toString('latin1');
test('vérifie les bornes et la limite API de 366 jours', () => {
assert.equal(validateExportPeriod('2026-10-05', '2026-10-11'), '');
assert.match(validateExportPeriod('2026-10-11', '2026-10-05'), /postérieure/);
assert.match(validateExportPeriod('2026-02-30', '2026-03-01'), /valides/);
assert.match(validateExportPeriod('2026-01-01', '2027-01-02'), /366 jours/);
assert.equal(validateExportPeriod('2024-01-01', '2024-12-31'), '');
assert.throws(() => buildWeeklyPdfPages({ ...base, start: '2026-13-01' }), /date/);
assert.throws(() => buildWeeklyPdfPages({ ...base, withPlanning: false, withPointage: false }), /élément/);
});
test('export combiné : heures, objectifs, pointages et pointage incomplet', () => {
const [page] = buildWeeklyPdfPages({ ...base, end: '2026-10-08' });
assert.equal(page.days.length, 7);
assert.equal(page.weekStart, '2026-10-05');
assert.equal(page.weekEnd, '2026-10-11');
assert.equal(page.plannedMinutes, 900); // 8h précises + 7h journalières
assert.equal(page.pointedMinutes, 477);
assert.equal(page.incompleteCount, 1);
assert.deepEqual(page.days[0].planLines, ['08:00 - 12:00', '13:00 - 17:00']);
assert.match(page.days[2].planLines[0], /Heures à effectuer/);
assert.match(page.days[3].planLines[0], /Absence/);
assert.match(page.days[1].clockLines[0], /incomplet/);
});
test('1 page par semaine, y compris quand les dates ne sont pas des lundis et dimanches', () => {
const pages = buildWeeklyPdfPages({ ...base, start: '2026-10-07', end: '2026-10-22' });
assert.equal(pages.length, 3);
assert.deepEqual(pages.map((page) => page.weekStart), ['2026-10-05', '2026-10-12', '2026-10-19']);
assert.equal(pages[0].includedDays, 5);
assert.equal(pages[1].includedDays, 7);
assert.equal(pages[2].includedDays, 4);
assert.equal(pages[0].days[0].included, false);
assert.equal(pages[0].days[1].included, false);
assert.equal(pages[2].days[4].included, false);
assert.equal(pages[0].plannedMinutes, 420); // le 5 octobre est hors période
assert.equal(pages[0].pointedMinutes, 0);
});
test('choix planning seul ou pointages seuls ne mixe pas les données', () => {
const [onlyPlan] = buildWeeklyPdfPages({ ...base, withPointage: false });
const [onlyClock] = buildWeeklyPdfPages({ ...base, withPlanning: false });
assert.equal(onlyPlan.pointedMinutes, 0);
assert.equal(onlyClock.plannedMinutes, 0);
assert.equal(onlyClock.pointedMinutes, 477);
assert.equal(onlyPlan.days[0].clockLines.length, 0);
assert.equal(onlyClock.days[0].planLines.length, 0);
assert.match(pdfString([onlyPlan]), /PLANNING HEBDOMADAIRE/);
assert.match(pdfString([onlyClock]), /POINTAGES HEBDOMADAIRES/);
});
test('objectif hebdomadaire calculé une seule fois par semaine (même partielle)', () => {
const days = ['2026-10-05', '2026-10-06', '2026-10-07'].map((date) => ({
date, schedule_mode: 'weekly_hours', week_target_minutes: 2100, kind: 'weekly_target',
}));
const [page] = buildWeeklyPdfPages({ ...base, start: '2026-10-06',
withPointage: false, planning: { days, segments: [] } });
assert.equal(page.weeklyTarget, 2100);
assert.equal(page.plannedMinutes, 2100);
assert.equal(page.days[0].included, false);
assert.match(pdfString([page]), /objectif hebdomadaire concerne/);
});
test('pointage nocturne avec fin le lendemain et durée transmise', () => {
const [page] = buildWeeklyPdfPages({ ...base, withPlanning: false,
attendances: [{ start_date: '2026-10-05', end_date: '2026-10-06', start_time: '22:00:00', end_time: '03:00:00', duration: 5 }] });
assert.equal(page.pointedMinutes, 300);
assert.equal(page.days[0].pointedMinutes, 300);
assert.match(page.days[0].clockLines[0], /\(\+1j\)/);
assert.equal(page.days[1].pointedMinutes, null);
});
test('PDF A4 paysage valide : vrai PDF vectoriel, 1 page par semaine, texte français', () => {
const pages = buildWeeklyPdfPages({ ...base, start: '2026-10-07', end: '2026-10-19' });
const bytes = createWeeklyPdfBytes(pages);
const pdf = Buffer.from(bytes).toString('latin1');
assert.ok(bytes.length > 4000);
assert.match(pdf, /^%PDF-1\.4/);
assert.equal((pdf.match(/\/Type \/Page \/Parent/g) || []).length, 3);
assert.match(pdf, /\/MediaBox \[0 0 841\.89 595\.28\]/);
assert.match(pdf, /\/Count 3/);
assert.match(pdf, /PLANNING & POINTAGES/);
assert.match(pdf, /-7h00/); // écart négatif affiché sans caractère non pris en charge
assert.match(pdf, /\/Encoding \/WinAnsiEncoding/);
assert.match(pdf, /Page 3 \/ 3/);
});