Some checks failed
Security / Security check (push) Failing after 1s
The trust-gate dialog replaces its generic 'no per-entry status yet (alpha)' note with the hub's classified verification statement — pinned store key / trusted publishers (green), installs without signature checking (amber), install would be refused (red), bridge entry (neutral) — via one shared describeInstallVerification mapping. Against a pre-0.23 hub the field is empty and the old honest wording stays (pinned by test). Information architecture: policy-off is a GLOBAL fact, so it appears as ONE ChainInlineHelp notice above the store grid instead of a warning pill on every card (card noise); only 'blocked' — a genuine per-source anomaly — earns a card pill. Doctor's host services show the hub-classified network reach per endpoint (local only / private network / publicly reachable with a protect-it hint / reach unknown). Verified end-to-end against the live dev hub (guide harness): the wire field arrives, the store page shows exactly one policy notice and quiet cards; trust-gate variants captured via the dialog harness. Suite 123 green. Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
140 lines
4.8 KiB
Dart
140 lines
4.8 KiB
Dart
// Dialog capture harness — visual proof for dialogs the page-level
|
|
// guide harness doesn't reach. Renders the target dialog against
|
|
// the scriptable fake hub and writes a PNG per theme via a
|
|
// RepaintBoundary (driverless, headed macOS `flutter test`).
|
|
//
|
|
// flutter test integration_test/dialog_shots_test.dart -d macos
|
|
//
|
|
// Output: build/dialog-shots/<name>-<theme>.png (or $DIALOG_SHOTS_OUT).
|
|
|
|
import 'dart:io';
|
|
import 'dart:ui' as ui;
|
|
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter/rendering.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:integration_test/integration_test.dart';
|
|
import 'package:shared_preferences/shared_preferences.dart';
|
|
|
|
import 'package:chain_studio/data/hub.dart' show StoreItem;
|
|
import 'package:chain_studio/l10n/app_localizations.dart';
|
|
import 'package:chain_studio/pages/federation.dart';
|
|
import 'package:chain_studio/widgets/chain_install_confirm.dart';
|
|
|
|
import '../test/support/fake_hub.dart';
|
|
|
|
final GlobalKey _shotKey = GlobalKey();
|
|
|
|
String get _outDir =>
|
|
Platform.environment['DIALOG_SHOTS_OUT'] ?? 'build/dialog-shots';
|
|
|
|
Future<void> _shot(WidgetTester tester, String name) async {
|
|
await tester.pump(const Duration(milliseconds: 150));
|
|
await tester.pump(const Duration(milliseconds: 150));
|
|
final boundary =
|
|
_shotKey.currentContext!.findRenderObject() as RenderRepaintBoundary;
|
|
final image = await boundary.toImage(pixelRatio: 2.0);
|
|
final bytes = await image.toByteData(format: ui.ImageByteFormat.png);
|
|
image.dispose();
|
|
final file = File('$_outDir/$name.png');
|
|
file.parent.createSync(recursive: true);
|
|
file.writeAsBytesSync(bytes!.buffer.asUint8List());
|
|
// ignore: avoid_print
|
|
print('dialog-shot: ${file.path}');
|
|
}
|
|
|
|
StoreItem _storeItem(String verification) => StoreItem(
|
|
name: 'text.extract',
|
|
taglineEn: 'Extract clean text',
|
|
taglineDe: 'Reinen Text extrahieren',
|
|
descriptionEn: '',
|
|
descriptionDe: '',
|
|
category: 'text',
|
|
tags: const [],
|
|
requiresCapabilities: const [],
|
|
requiresServices: const [],
|
|
license: 'Apache-2.0',
|
|
repository: '',
|
|
bestVersion: '0.1.0',
|
|
status: 'alpha',
|
|
installed: false,
|
|
featured: false,
|
|
iconUrl: '',
|
|
screenshotUrls: const [],
|
|
docsUrl: '',
|
|
kind: 'native',
|
|
provider: '',
|
|
source: 'bundled',
|
|
installVerification: verification,
|
|
);
|
|
|
|
void main() {
|
|
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
|
|
|
for (final (themeName, mode) in [
|
|
('light', ThemeMode.light),
|
|
('dark', ThemeMode.dark),
|
|
]) {
|
|
testWidgets('add-satellite dialog — $themeName', (tester) async {
|
|
SharedPreferences.setMockInitialValues({});
|
|
installFakeHub();
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
debugShowCheckedModeBanner: false,
|
|
themeMode: mode,
|
|
theme: ThemeData.light(useMaterial3: true),
|
|
darkTheme: ThemeData.dark(useMaterial3: true),
|
|
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
|
supportedLocales: AppLocalizations.supportedLocales,
|
|
locale: const Locale('de'),
|
|
// Wrap the whole navigator (incl. the overlay dialogs
|
|
// render into) so the capture catches the dialog, not
|
|
// just the page beneath it.
|
|
builder: (context, child) =>
|
|
RepaintBoundary(key: _shotKey, child: child),
|
|
home: const FederationPage(),
|
|
),
|
|
);
|
|
await tester.pump(const Duration(milliseconds: 200));
|
|
await tester.tap(find.text('Satellit hinzufügen').first);
|
|
await tester.pumpAndSettle();
|
|
await _shot(tester, 'add-satellite-$themeName');
|
|
expect(find.textContaining('Ein Satellit ist ein weiterer Hub'),
|
|
findsOneWidget);
|
|
});
|
|
}
|
|
|
|
// Trust gate with the hub-reported verification statement — the
|
|
// pinned-key (good) and policy-off (warning) variants.
|
|
for (final (name, verification) in [
|
|
('trust-gate-pinned', 'pinned-key'),
|
|
('trust-gate-unverified', 'unverified'),
|
|
]) {
|
|
testWidgets('$name — dark', (tester) async {
|
|
SharedPreferences.setMockInitialValues({});
|
|
installFakeHub();
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
debugShowCheckedModeBanner: false,
|
|
themeMode: ThemeMode.dark,
|
|
theme: ThemeData.light(useMaterial3: true),
|
|
darkTheme: ThemeData.dark(useMaterial3: true),
|
|
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
|
supportedLocales: AppLocalizations.supportedLocales,
|
|
locale: const Locale('de'),
|
|
builder: (context, child) =>
|
|
RepaintBoundary(key: _shotKey, child: child),
|
|
home: Scaffold(
|
|
body: Center(
|
|
child: ChainInstallConfirmDialog(
|
|
item: _storeItem(verification),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
await tester.pump(const Duration(milliseconds: 200));
|
|
await _shot(tester, '$name-dark');
|
|
});
|
|
}
|
|
}
|