Je @Person eine Pille mit Anteil an der offenen Pfad-Arbeit (D71-Maß), dazu ein Eintrag für Unzugewiesenes — die Anteile summieren auf 100 %. Ein Tipp filtert das Diagramm auf die Knoten der Person: alles andere faltet, ihre Pakete stehen als ▸ n, die Pillen färben petrol. Die Linse ist rein Ansicht und Sitzungssache — in den Text wird nichts geschrieben, in geteilten Dokumenten ginge der persönliche Filter sonst an alle. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
73 lines
2.7 KiB
JavaScript
73 lines
2.7 KiB
JavaScript
import { describe, it, expect } from 'vitest';
|
|
import { parse } from '../src/parser.js';
|
|
import { personFoldSet, allTags } from '../src/model.js';
|
|
|
|
/* Personen-Linse (SPEC §9, D87): personFoldSet liefert einen VOLLSTÄNDIGEN
|
|
Faltzustand — offen bleiben nur die Vorfahren der getaggten Knoten, die
|
|
getaggten selbst klappen zu; tag === null ist die Unzugewiesen-Linse. */
|
|
|
|
const PLAN = `Projekt
|
|
- Backend @anna
|
|
- Auth (S)
|
|
- API (M) @ben
|
|
- Frontend @ben
|
|
- Views (S)
|
|
- Detail (XS)
|
|
- Infra
|
|
- CI (S) @anna
|
|
- Deploy (S)
|
|
- Doku (S)
|
|
`;
|
|
|
|
function byLabel(roots){
|
|
const map = new Map();
|
|
const walk = ns => { for(const n of ns){ map.set(n.label, n); walk(n.children); } };
|
|
walk(roots);
|
|
return map;
|
|
}
|
|
function labels(set){ return [...set].map(n => n.label).sort(); }
|
|
|
|
describe('personFoldSet — Personen-Linse (D87)', () => {
|
|
const roots = parse(PLAN).roots;
|
|
const N = byLabel(roots);
|
|
|
|
it('getaggte Knoten klappen zu, ihre Vorfahren bleiben offen', () => {
|
|
const set = personFoldSet(roots, 'anna');
|
|
expect(set.has(N.get('Backend'))).toBe(true); /* @anna, zu — ▸ vertritt das Paket */
|
|
expect(set.has(N.get('Projekt'))).toBe(false); /* Vorfahr von annas Knoten */
|
|
expect(set.has(N.get('Infra'))).toBe(false); /* Vorfahr von annas CI */
|
|
});
|
|
|
|
it('Teilbäume ohne den Tag klappen an ihrer Wurzel zu', () => {
|
|
const set = personFoldSet(roots, 'anna');
|
|
expect(set.has(N.get('Frontend'))).toBe(true); /* @ben — nichts von anna darin */
|
|
expect(labels(set)).toContain('Views'); /* auch tiefer: vollständiger Zustand */
|
|
});
|
|
|
|
it('ein getaggtes Blatt ist nicht in der Menge — es gibt nichts zu falten', () => {
|
|
const set = personFoldSet(roots, 'anna');
|
|
expect(set.has(N.get('CI'))).toBe(false);
|
|
});
|
|
|
|
it('Vorfahr mit fremdem Tag bleibt offen, wenn der gesuchte Tag darunter liegt', () => {
|
|
const set = personFoldSet(roots, 'ben');
|
|
expect(set.has(N.get('Backend'))).toBe(false); /* enthält bens API */
|
|
expect(set.has(N.get('Frontend'))).toBe(true); /* @ben selbst — zu */
|
|
expect(set.has(N.get('Infra'))).toBe(true); /* nichts von ben */
|
|
});
|
|
|
|
it('Unzugewiesen-Linse: jeder Knoten mit eigenen Tags klappt zu', () => {
|
|
const set = personFoldSet(roots, null);
|
|
expect(labels(set)).toEqual(['Backend', 'Frontend']); /* CI ist Blatt, Infra untagged */
|
|
});
|
|
});
|
|
|
|
describe('allTags — Einträge der Personen-Leiste (D87)', () => {
|
|
it('liefert die Tags in Dokumentreihenfolge, ohne Doppelte', () => {
|
|
expect(allTags(parse(PLAN).roots)).toEqual(['anna', 'ben']);
|
|
});
|
|
it('leer ohne Tags', () => {
|
|
expect(allTags(parse('- A\n - B').roots)).toEqual([]);
|
|
});
|
|
});
|