Files
planflow/tests/unit/navigation.test.ts
MichaelandClaude Opus 5 98b289784a Séparer les absences par état
Le calendrier portait tout : les demandes en attente, les décisions rendues et
celles qui avaient expiré, distinguées par un simple badge. Une file d'attente
se lit mal dans un calendrier — elle se lit par ancienneté, et le calendrier
range par date d'absence.

Quatre routes désormais : le calendrier, la file à traiter, les traitées et les
expirées. Le calendrier a déménagé sous /absences plutôt que d'être dupliqué, et
/conges redirige en conservant le mois : un lien partagé dans un message
continue d'ouvrir la bonne page.

Les trois files partagent une requête et un rendu. C'est délibéré : la règle qui
masque le motif d'une absence Sécurité sociale vaut pour les trois, et trois
copies de cette règle garantiraient qu'une finisse par diverger. Une donnée de
santé qui ne fuit que sur l'écran « traitées » reste une fuite.

La file à traiter classe par ancienneté de demande, les files closes par date
d'absence décroissante : on cherche d'un côté ce qui attend depuis trop
longtemps, de l'autre ce qui vient d'être décidé.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-11 12:23:16 +02:00

74 lines
2.4 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { describe, expect, it } from 'vitest';
import {
activeItem,
isActive,
matches,
NAVIGATION,
sectionForPath,
} from '@/components/shell/navigation';
describe('matches', () => {
it('reconnaît la racine sans capturer tout le site', () => {
expect(matches('/', '/')).toBe(true);
expect(matches('/', '/equipe')).toBe(false);
});
it('couvre les sous-routes', () => {
expect(matches('/equipe', '/equipe')).toBe(true);
expect(matches('/equipe', '/equipe/camille-ferrand')).toBe(true);
expect(matches('/equipe', '/equipements')).toBe(false);
});
it('écarte les liens d’ancre', () => {
expect(
matches('/absences/calendrier#compteurs', '/absences/calendrier'),
).toBe(false);
});
});
describe('activeItem', () => {
it('retient la correspondance la plus spécifique', () => {
// Une fiche salarié est un détail de « Membres » : c'est cette entrée qui
// reste allumée, et non un lien codé en dur vers un salarié particulier.
expect(activeItem('/equipe/cm123abc')?.id).toBe('membres');
expect(activeItem('/equipe')?.id).toBe('membres');
expect(activeItem('/reglages/registre')?.id).toBe('registre');
});
it('ne renvoie rien pour une route hors navigation', () => {
expect(activeItem('/inconnu')).toBeUndefined();
});
});
describe('isActive', () => {
it('n’allume qu’une entrée par écran', () => {
const targets = NAVIGATION.flatMap((section) => section.items)
.map((item) => item.href)
.filter(
(href): href is string =>
typeof href === 'string' && !href.includes('#'),
);
for (const pathname of [...targets, '/equipe/cm123abc']) {
const lit = NAVIGATION.flatMap((section) => section.items).filter(
(item) => item.href && isActive(item.href, pathname),
);
expect(lit.length, `${pathname} allume ${lit.length} entrées`).toBe(1);
}
});
});
describe('sectionForPath', () => {
it('trouve la section portant la route courante', () => {
expect(sectionForPath('/planning/semaine').id).toBe('plannings');
expect(sectionForPath('/equipe/camille-ferrand').id).toBe('equipe');
expect(sectionForPath('/absences/calendrier').id).toBe('conges');
expect(sectionForPath('/absences/a-traiter').id).toBe('conges');
});
it('retombe sur la première section pour une route inconnue', () => {
expect(sectionForPath('/inconnu').id).toBe('apercu');
});
});