fix(ci): embarquer les fixtures de dialogue dans le paquet de test

Les tests de dialog-detection lisaient leurs captures dans
spikes/s3-tui/captures/, dossier exclu du suivi git (.gitignore :
spikes/**/captures/ + la règle globale *.log). Verts en local mais
absents au checkout CI → ENOENT sur 9 tests.

Les 5 fixtures réellement utilisées sont déplacées dans
packages/server/test/fixtures/dialogs/ et renommées *.raw (sans .log)
pour échapper aux deux règles d'ignore. Le test pointe désormais ce
dossier. Suite hermétique : 175/175 verts, fixtures versionnées.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-17 12:11:17 +02:00
parent 3aadcbec43
commit 0ffc57ddd1
6 changed files with 2012 additions and 10 deletions

View File

@@ -6,7 +6,7 @@ import type { DialogKind } from '@arboretum/shared';
import { ScreenReader } from '../src/core/screen-reader.js';
import { classifyDialog, parseOptions, type ClassifiedDialog } from '../src/core/dialog-classifier.js';
const capturesDir = join(dirname(fileURLToPath(import.meta.url)), '..', '..', '..', 'spikes', 's3-tui', 'captures');
const capturesDir = join(dirname(fileURLToPath(import.meta.url)), 'fixtures', 'dialogs');
/** Rejoue un flux PTY brut par segments (comme l'adapter en prod) et collecte écran+dialogue détecté. */
async function replay(fixture: string): Promise<Array<{ dialog: ClassifiedDialog; text: string }>> {
@@ -26,7 +26,7 @@ async function replay(fixture: string): Promise<Array<{ dialog: ClassifiedDialog
describe('ScreenReader (@xterm/headless) — anti strip-ANSI naïf', () => {
it('préserve les espaces du dialogue (« Do you want to create », pas « Doyouwant »)', async () => {
const seen = await replay('perm-write2.raw.log');
const seen = await replay('perm-write2.raw');
expect(seen.length).toBeGreaterThan(0);
expect(seen.some((s) => /Do you want to create/.test(s.text))).toBe(true);
});
@@ -57,7 +57,7 @@ describe('classifyDialog — unitaire (lignes synthétiques)', () => {
describe('détection sur fixtures réelles S3 (replay headless)', () => {
it('perm-write2 → permission avec options Yes/…/No, option 1 sélectionnée', async () => {
const perms = (await replay('perm-write2.raw.log')).filter((s) => s.dialog.kind === 'permission');
const perms = (await replay('perm-write2.raw')).filter((s) => s.dialog.kind === 'permission');
expect(perms.length).toBeGreaterThan(0);
const withOpts = perms.find((s) => s.dialog.options.length >= 2);
expect(withOpts).toBeDefined();
@@ -68,13 +68,13 @@ describe('détection sur fixtures réelles S3 (replay headless)', () => {
});
it('ask2 → question avec une option « Rouge »', async () => {
const qs = (await replay('ask2.raw.log')).filter((s) => s.dialog.kind === 'question');
const qs = (await replay('ask2.raw')).filter((s) => s.dialog.kind === 'question');
expect(qs.length).toBeGreaterThan(0);
expect(qs.some((s) => s.dialog.options.some((o) => /Rouge/.test(o.label)))).toBe(true);
});
it('trust → dialogue trust détecté', async () => {
const seen = await replay('trust.raw.log');
const seen = await replay('trust.raw');
expect(seen.some((s) => s.dialog.kind === 'trust')).toBe(true);
});
});
@@ -83,10 +83,10 @@ describe('détection sur fixtures réelles S3 (replay headless)', () => {
// classifié de façon fiable (cf. « reste à faire P4 » du verdict S3 : couvrir le refus Esc et le plan).
describe('campagne de fiabilité P4-C — tous les types de dialogue', () => {
const realCaptures: Array<{ fixture: string; kind: DialogKind }> = [
{ fixture: 'trust.raw.log', kind: 'trust' },
{ fixture: 'perm-write2.raw.log', kind: 'permission' },
{ fixture: 'perm-bash2.raw.log', kind: 'permission' },
{ fixture: 'ask2.raw.log', kind: 'question' },
{ fixture: 'trust.raw', kind: 'trust' },
{ fixture: 'perm-write2.raw', kind: 'permission' },
{ fixture: 'perm-bash2.raw', kind: 'permission' },
{ fixture: 'ask2.raw', kind: 'question' },
];
for (const c of realCaptures) {
it(`${c.fixture}${c.kind} détecté (capture réelle)`, async () => {
@@ -97,7 +97,7 @@ describe('campagne de fiabilité P4-C — tous les types de dialogue', () => {
it('refus par Esc (deny-esc2) : un dialogue est bien affiché avant lannulation', async () => {
// un dialogue détecté = l'utilisateur peut répondre « deny » (Esc) depuis le mobile sans terminal.
const seen = await replay('deny-esc2.raw.log');
const seen = await replay('deny-esc2.raw');
expect(seen.length).toBeGreaterThan(0);
});