import { describe, expect, it } from "vitest"; import { extractPayrollBulletins } from "./payrollParser"; const SAMPLE_BULLETIN = ` ##BULLETIN##05-2026##009999##DUPONT##TEST## Matricule : 009999 Emploi : RESPONSABLE APPLICATIF Monsieur TEST DUPONT Salaire de base (Forfait 218 jours) 3 650.00 Prime d'astreinte 200.00 Prime d'astreinte 200.00 Prime d'activité 150.00 Salaire brut 4 200.00 N° SS : ne-pas-extraire Iban / Rib : ne-pas-extraire Net à payer avant impôt sur le revenu 2 000.00 `; describe("extractPayrollBulletins", () => { it("n'extrait que les champs métier autorisés et additionne les astreintes", () => { const result = extractPayrollBulletins(SAMPLE_BULLETIN); expect(result).toMatchObject({ statutExtraction: "ready", erreurExtraction: null }); expect(result.bulletins).toEqual([ expect.objectContaining({ matricule: "009999", nom: "DUPONT", prenom: "TEST", poste: "RESPONSABLE APPLICATIF", brutMensuelCents: 365000, brutAvecPrimesCents: 420000, primeAstreinteCents: 40000, }), ]); expect(Object.keys(result.bulletins[0]!)).not.toContain("iban"); expect(JSON.stringify(result)).not.toContain("ne-pas-extraire"); }); it("signale une liasse non indexable sans prétendre perdre le PDF", () => { expect(extractPayrollBulletins("contenu PDF non reconnu")).toEqual({ bulletins: [], statutExtraction: "a_controler", erreurExtraction: "Aucun bulletin n’a pu être indexé automatiquement. Le PDF reste conservé et consultable.", }); }); it("indexe chaque salarié lorsque le texte PDF concatène les pages sans séparateur", () => { const secondBulletin = SAMPLE_BULLETIN .replace("##009999##DUPONT##TEST##", "##008888##MARTIN##EXEMPLE##") .replaceAll("009999", "008888") .replaceAll("TEST DUPONT", "EXEMPLE MARTIN"); const result = extractPayrollBulletins(`${SAMPLE_BULLETIN}\n${secondBulletin}`); expect(result.bulletins).toHaveLength(2); expect(result.bulletins.map((bulletin) => bulletin.matricule)).toEqual(["009999", "008888"]); }); it("accepte un emploi suivi de la civilité par un seul espace, comme certains PDF extraits", () => { const compacted = SAMPLE_BULLETIN.replace(" Monsieur", " Monsieur"); expect(extractPayrollBulletins(compacted).bulletins).toHaveLength(1); }); it("lit un poste sur sa seule ligne lorsque la civilité est rendue à la ligne suivante", () => { const separateCivilite = SAMPLE_BULLETIN.replace(" Monsieur TEST DUPONT", "\nMonsieur TEST DUPONT"); expect(extractPayrollBulletins(separateCivilite).bulletins).toHaveLength(1); }); it("refuse une extraction d'emploi répartie sur plusieurs lignes pour ne jamais persister de donnée personnelle", () => { const unsafeLayout = SAMPLE_BULLETIN.replace( /Emploi\s*:[^\n]*/, "Emploi :\nAdresse personnelle à ignorer\nRESPONSABLE APPLICATIF\nMonsieur TEST DUPONT", ); expect(extractPayrollBulletins(unsafeLayout)).toMatchObject({ bulletins: [], statutExtraction: "a_controler", }); }); });