feat(doctor): deep-link findings to the page where they are actioned
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>
This commit is contained in:
parent
c18bb7f357
commit
e67e0c9e73
7 changed files with 525 additions and 139 deletions
122
test/doctor_deeplinks_test.dart
Normal file
122
test/doctor_deeplinks_test.dart
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
// 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);
|
||||
},
|
||||
);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue