// Guided-setup wizard — the three explained answer steps. Verifies // that options render as localized cards with their one-line // explanation (no English enum humanization) and that Weiter/Zurück // walk the steps. The review step calls the CLI (a subprocess) and is // out of scope for a widget test. import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:chain_studio/l10n/app_localizations.dart'; import 'package:chain_studio/widgets/guided_setup_dialog.dart'; Widget _host() => MaterialApp( locale: const Locale('de'), localizationsDelegates: AppLocalizations.localizationsDelegates, supportedLocales: AppLocalizations.supportedLocales, home: Builder( builder: (context) => Scaffold( body: Center( child: ElevatedButton( onPressed: () => GuidedSetupDialog.show(context), child: const Text('open'), ), ), ), ), ); /// Host that opens the dialog pre-seeded on the review / applied /// step with [plan] (the parsed `--plan-json` shape), bypassing the /// CLI subprocess. Widget _seededHost(Map plan, {bool applied = false}) => MaterialApp( locale: const Locale('de'), localizationsDelegates: AppLocalizations.localizationsDelegates, supportedLocales: AppLocalizations.supportedLocales, home: Builder( builder: (context) => Scaffold( body: Center( child: ElevatedButton( onPressed: () => showDialog( context: context, builder: (_) => GuidedSetupDialog(debugPlan: plan, debugApplied: applied), ), child: const Text('open'), ), ), ), ), ); Map _regulatedPlan({bool signed = true}) => { 'profile': 'enterprise', 'modules': ['text.extract', 'text.summarize'], 'starter_flow': 'extract-summarize', 'runbook': 'service', 'curated_docs': [], 'require_signatures': signed, 'worm_audit': false, 'approval_step': true, }; void main() { testWidgets('step 1 shows scenario options in German with explanations', ( tester, ) async { await tester.pumpWidget(_host()); await tester.tap(find.text('open')); await tester.pumpAndSettle(); // Step counter + localized (German) scenario labels, not the // English enum humanization. expect(find.text('Schritt 1 von 3'), findsOneWidget); expect(find.text('Regulierter Produktivbetrieb'), findsOneWidget); expect(find.text('Erst mal ausprobieren'), findsOneWidget); // Each option carries its one-line explanation. expect( find.textContaining('menschliche Freigabe vor jedem KI-Schritt'), findsOneWidget, ); // No leaked English enum labels. expect(find.text('Regulated Production'), findsNothing); expect(find.text('Trying Out'), findsNothing); }); testWidgets('Weiter/Zurück walk the three answer steps', (tester) async { await tester.pumpWidget(_host()); await tester.tap(find.text('open')); await tester.pumpAndSettle(); // Step 1 → 2 (task). await tester.tap(find.text('Weiter')); await tester.pumpAndSettle(); expect(find.text('Schritt 2 von 3'), findsOneWidget); expect(find.text('Text aus Dokumenten extrahieren'), findsOneWidget); // Step 2 → 3 (environment) shows the two plain-language toggles. await tester.tap(find.text('Weiter')); await tester.pumpAndSettle(); expect(find.text('Schritt 3 von 3'), findsOneWidget); expect(find.text('Dieser Rechner'), findsOneWidget); expect( find.text('Vor jedem KI-Schritt einen Menschen um Freigabe bitten'), findsOneWidget, ); // Zurück returns to step 2. await tester.tap(find.text('Zurück')); await tester.pumpAndSettle(); expect(find.text('Schritt 2 von 3'), findsOneWidget); }); testWidgets( 'review of a regulated plan explains the signature dead end and offers the relaxation switch', (tester) async { await tester.pumpWidget(_seededHost(_regulatedPlan())); await tester.tap(find.text('open')); await tester.pumpAndSettle(); // The plain-language notice + the deliberate one-click switch. expect( find.textContaining('Der öffentliche Store liefert zurzeit unsignierte Pakete'), findsOneWidget, ); expect( find.text('Installation aus dem öffentlichen Store erlauben'), findsOneWidget, ); // No raw CLI prose anywhere. expect(find.textContaining('chain install'), findsNothing); }, ); testWidgets( 'applied state renders clickable next steps — start hub, per-module install, open flow', (tester) async { await tester.pumpWidget( _seededHost(_regulatedPlan(signed: false), applied: true), ); await tester.tap(find.text('open')); await tester.pumpAndSettle(); expect(find.text('Fertig — Ch∆In ist eingerichtet.'), findsOneWidget); // Hub not probed up in tests → the start action + the // install-needs-hub hint show, and install buttons exist // (disabled) for each plan module. expect(find.text('Hub starten'), findsOneWidget); expect( find.textContaining('Starten Sie zuerst den Hub'), findsOneWidget, ); expect(find.text('text.extract installieren'), findsOneWidget); expect(find.text('text.summarize installieren'), findsOneWidget); expect( find.text('Beispiel-Flow „extract-summarize“ öffnen'), findsOneWidget, ); // No CLI text in the GUI path. expect(find.textContaining('chain serve'), findsNothing); expect(find.textContaining('chain install'), findsNothing); }, ); testWidgets( 'applied regulated (signed) state guides to the signed source instead of dead-end install buttons', (tester) async { await tester.pumpWidget(_seededHost(_regulatedPlan(), applied: true)); await tester.tap(find.text('open')); await tester.pumpAndSettle(); expect( find.textContaining('signierten Quelle'), findsOneWidget, ); expect(find.text('text.extract installieren'), findsNothing); }, ); }