Post-apply the wizard now renders real Studio actions instead of CLI
text: a start-hub button that polls until the daemon answers,
per-module install buttons (capability-name install via the hub's
store index) with done/progress states, and an open-the-starter-flow
button that navigates to the Flows page. Regulated plans explain in
plain language that modules come from a signed source; the preview
offers 'allow installing from the public store' as one deliberate,
reversible switch that re-assembles the plan (allow_unsigned_modules).
Fresh installs (no config, no setup-plan.yaml) auto-open the wizard
once per run — the wizard IS the onboarding — and it steps back once
a setup exists. The welcome CTA is framed honestly ('get started in
3 questions'), and after the wizard closes the onboarding checklist
remounts, re-probes, and says what the assistant already covered
(profile line from setup-plan.yaml) instead of acting as a second,
disconnected onboarding surface.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
179 lines
6.1 KiB
Dart
179 lines
6.1 KiB
Dart
// 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<String, dynamic> 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<void>(
|
|
context: context,
|
|
builder: (_) =>
|
|
GuidedSetupDialog(debugPlan: plan, debugApplied: applied),
|
|
),
|
|
child: const Text('open'),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
Map<String, dynamic> _regulatedPlan({bool signed = true}) => {
|
|
'profile': 'enterprise',
|
|
'modules': ['text.extract', 'text.summarize'],
|
|
'starter_flow': 'extract-summarize',
|
|
'runbook': 'service',
|
|
'curated_docs': <String>[],
|
|
'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);
|
|
},
|
|
);
|
|
}
|