Doctor findings used to be dead-end statements — 'approvals waiting for review' left the operator to find the approvals inbox on their own. Every finding with a dedicated surface is now one tap away from it: - summary tiles: modules -> store, approvals -> approvals inbox, audit chain -> audit log (chevron affordance, tooltip + semantics button; the services tile stays plain — no dedicated page) - modules/approvals panel rows link the same way - the event-log headline opens the audit page next to the existing verify button - host-services empty state gains a 'view the configuration' button opening the in-Studio config viewer the hint refers to - the update banner's release-notes URL is now an underlined, clickable link instead of dead text Tiles and the panel are public callback-driven widgets so the widget tests pump them without a live hub. New DE+EN link labels. Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
122 lines
3.4 KiB
Dart
122 lines
3.4 KiB
Dart
// Doctor deep links — findings with a dedicated page must be one
|
|
// tap away from it (usertest follow-up: "approvals waiting for
|
|
// review" was a dead-end statement). The tiles and panel rows are
|
|
// public callback-driven widgets so these tests pump each variant
|
|
// without a live hub.
|
|
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
|
|
import 'package:chain_studio/data/hub.dart';
|
|
import 'package:chain_studio/l10n/app_localizations.dart';
|
|
import 'package:chain_studio/pages/doctor.dart';
|
|
|
|
const _snapshot = DoctorSnapshot(
|
|
moduleCount: 2,
|
|
capabilityCount: 3,
|
|
pendingApprovals: 1,
|
|
eventChainTotal: 5,
|
|
eventChainVerified: 5,
|
|
eventChainTamperedAt: null,
|
|
services: [],
|
|
update: UpdateStatus(
|
|
channel: 'stable',
|
|
localVersion: '0.22.0',
|
|
latestVersion: '0.22.0',
|
|
updateAvailable: false,
|
|
manifestReachable: true,
|
|
),
|
|
paths: DaemonPathsSnapshot(
|
|
logPath: '',
|
|
dbPath: '',
|
|
modulesDir: '',
|
|
flowsDir: '',
|
|
configPath: '',
|
|
pidPath: '',
|
|
),
|
|
);
|
|
|
|
Widget _host(Widget child) => MaterialApp(
|
|
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
|
supportedLocales: AppLocalizations.supportedLocales,
|
|
locale: const Locale('de'),
|
|
home: Scaffold(body: child),
|
|
);
|
|
|
|
void main() {
|
|
testWidgets('a linked stat tile is a labelled button that fires its target', (
|
|
tester,
|
|
) async {
|
|
var opened = false;
|
|
await tester.pumpWidget(
|
|
_host(
|
|
Row(
|
|
children: [
|
|
DoctorStatTile(
|
|
label: 'Freigaben',
|
|
value: '1',
|
|
subtitle: 'warten auf Prüfung',
|
|
icon: Icons.inbox_outlined,
|
|
onTap: () => opened = true,
|
|
linkLabel: 'Freigaben öffnen',
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
await tester.pumpAndSettle();
|
|
// Affordance: chevron + semantics button with the link label.
|
|
expect(find.byIcon(Icons.chevron_right), findsOneWidget);
|
|
expect(
|
|
find.bySemanticsLabel(RegExp('Freigaben öffnen')),
|
|
findsOneWidget,
|
|
);
|
|
await tester.tap(find.text('1'));
|
|
expect(opened, isTrue);
|
|
});
|
|
|
|
testWidgets('an unlinked stat tile stays a plain card', (tester) async {
|
|
await tester.pumpWidget(
|
|
_host(
|
|
const Row(
|
|
children: [
|
|
DoctorStatTile(
|
|
label: 'Dienste',
|
|
value: '0',
|
|
subtitle: 'deklariert',
|
|
icon: Icons.dns_outlined,
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
await tester.pumpAndSettle();
|
|
expect(find.byIcon(Icons.chevron_right), findsNothing);
|
|
expect(find.byType(InkWell), findsNothing);
|
|
});
|
|
|
|
testWidgets(
|
|
'the approvals row links to approvals, the modules row to the store',
|
|
(tester) async {
|
|
var storeOpened = false;
|
|
var approvalsOpened = false;
|
|
await tester.pumpWidget(
|
|
_host(
|
|
DoctorModulesPanel(
|
|
snapshot: _snapshot,
|
|
onOpenStore: () => storeOpened = true,
|
|
onOpenApprovals: () => approvalsOpened = true,
|
|
),
|
|
),
|
|
);
|
|
await tester.pumpAndSettle();
|
|
// The pending-approvals finding must be tappable ("1 Freigabe
|
|
// wartet auf Prüfung" -> approvals inbox).
|
|
await tester.tap(find.textContaining('wartet auf Prüfung'));
|
|
expect(approvalsOpened, isTrue);
|
|
expect(storeOpened, isFalse);
|
|
await tester.tap(find.textContaining('2 Module'));
|
|
expect(storeOpened, isTrue);
|
|
},
|
|
);
|
|
}
|