// La carte locataire est la vue où l'on vérifie une extraction avant de // l'enregistrer : elle ne doit afficher que des champs extraits, tous modifiables, // et ne jamais substituer un calcul à ce que le compte rendu porte. import { describe, expect, it } from 'vitest' import { createSSRApp, h } from 'vue' import { renderToString } from 'vue/server-renderer' import LocataireCard from '../src/components/LocataireCard.vue' // Lot 11 de M_33670000_2025-09-22, avec le règlement mal extrait observé sur un // compte rendu ultérieur : le PDF annonce 7 878,00 € réglés pour 867,45 € dus. function lotCharlot(surcharges = {}) { return { lot: { numero: '11', type: 'Appartement T3' }, locataire: { nom: 'CHARLOT ANDREE' }, lignes: [ { type: 'loyer', periode: { debut: '2025-09-01', fin: '2025-09-30' }, loyers: 798.45, taxes: 0, provisions: 69, divers: { montant: 0, libelle: null }, total: 867.45, regles: 867.45, impayes: 0, }, ], totaux: { solde_anterieur: 0, loyers: 798.45, taxes: 0, provisions: 69, divers: 0, total: 867.45, regles: 867.45, impayes: 0, }, ...surcharges, } } // Les montants sont formatés avec des espaces insécables (séparateur de milliers, // espace avant €) et le template en insère aux sauts de ligne : tous les blancs // sont ramenés à un espace simple pour pouvoir chercher un montant. async function rendre(locataire) { const app = createSSRApp({ render: () => h(LocataireCard, { locataire }), }) const html = await renderToString(app) return html.replace(/\s+/g, ' ') } /** Les lignes seules, sans la ligne « Totaux » qui porte les valeurs déduites. */ function corpsDuTableau(html) { return html.match(/(.*)<\/tbody>/)[1] } describe('LocataireCard', () => { it('reprend les colonnes du compte rendu, ligne « Totaux » comprise', async () => { const html = await rendre(lotCharlot()) for (const colonne of ['Loyers', 'Taxes', 'Provisions', 'Divers', 'Total', 'Regles', 'Impaye']) { expect(html).toContain(colonne) } expect(html).toContain('Totaux') expect(html).toContain('Solde ant.') }) it('laisse la ligne porter le total du compte rendu, sans le recalculer', async () => { // Lot 13 de M_33670000_2025-08-26 : colonne « total » vide sur cette ligne, // le compte rendu la porte sur une autre ligne du même bloc. const lot = lotCharlot() lot.lignes[0].loyers = 997.02 lot.lignes[0].provisions = 64 lot.lignes[0].total = 0 const lignes = corpsDuTableau(await rendre(lot)) expect(lignes).toContain('997,02 €') expect(lignes).toContain('64,00 €') // La somme des colonnes n'a pas à apparaître : la ligne n'est pas déduite. expect(lignes).not.toContain('1 061,02 €') }) it('n’affiche aucune valeur déduite quand l’extraction se recoupe', async () => { const html = await rendre(lotCharlot()) expect(html).not.toContain('calc.') expect(html).not.toContain('A verifier') }) it('signale la colonne qui ne somme pas, sans toucher au montant extrait', async () => { // Cas du lot 07 de S_33680000_2025-11-25 : la colonne Total des lignes reste // à 0 alors que le compte rendu annonce 707,29 € pour le lot. const lot = lotCharlot() lot.lignes[0].total = 0 lot.totaux.total = 707.29 const html = await rendre(lot) expect(html).toContain('A verifier') expect(html).toContain('calculé : 0,00 €') // Le montant du compte rendu reste affiché tel quel. expect(html).toContain('707,29 €') }) it('met la colonne en défaut en évidence', async () => { const lot = lotCharlot() lot.totaux.regles = 707.29 const html = await rendre(lot) expect(html).toContain('bg-amber-500/10') }) it('dit au survol ce qui est extrait et ce qui est calculé', async () => { const lot = lotCharlot() lot.totaux.regles = 707.29 const html = await rendre(lot) expect(html).toContain('Valeur extraite du compte rendu — cliquer pour la modifier') expect(html).toContain('Extrait du compte rendu : 707,29 €') expect(html).toContain('Calculé sur les 1 ligne(s) : 867,45 €') }) it('signale un lot dont aucune ligne n’a été extraite', async () => { const lot = lotCharlot({ lignes: [] }) const html = await rendre(lot) expect(html).toContain('Aucune ligne extraite') expect(html).toContain('A verifier') }) it('montre l’impayé du compte rendu dans le bandeau replié', async () => { const lot = lotCharlot() lot.totaux.impayes = 67.45 const html = await rendre(lot) expect(html).toContain('Impayes: 67,45 €') }) it('distingue un trop-perçu d’un impayé', async () => { const lot = lotCharlot() lot.totaux.impayes = -32.55 const html = await rendre(lot) expect(html).toContain('Trop-percu: 32,55 €') }) })