test: deterministic operator-guide screenshot harness

integration_test/guide_shots_test.dart boots a hermetic hub
(HubFixture), seeds demo projects (open Bürgeramt; sealed
Ratsinformation and a setup.applied audit event only when the
runner confirms an isolated $HOME), launches the app in German +
dark mode, walks every sidebar page in Cmd order with
content-aware waits, opens the workspace switcher and the setup
wizard, and writes the guide PNGs via a driverless RepaintBoundary
capture. Driven by the platform repo's scripts/regen-studio-guide.sh.

Also fixes the hub fixture's binary resolution, dead since the
rename (it looked for 'fai' and ../chain_platform/): now $CHAIN_BIN,
'chain' on PATH, then ../fai_chain/target/{release,debug}/chain —
the integration tests actually run again instead of silently
skipping.

Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
This commit is contained in:
flemming-it 2026-07-13 00:47:41 +02:00
parent ed2c5ace26
commit cf4024a4e2
6 changed files with 346 additions and 16 deletions

View file

@ -71,13 +71,13 @@ class HubFixture {
if (binary == null) {
if (skipIfBinaryMissing) {
markTestSkipped(
'Hub fixture skipped: no `fai` binary found on PATH or in '
'../chain_platform/target/release/fai. Build it with '
'`cargo build --release` and re-run.',
'Hub fixture skipped: no `chain` binary found via \$CHAIN_BIN, '
'PATH, or ../fai_chain/target/{release,debug}/chain. Build it '
'with `cargo build` in the platform repo and re-run.',
);
return null;
}
throw StateError('fai binary not found');
throw StateError('chain binary not found');
}
final tempDir = await Directory.systemTemp.createTemp('chain_studio_test_');
@ -157,26 +157,36 @@ class HubFixture {
}
}
/// Look for `fai` first on PATH (production-ish), then in
/// the standard `target/release/` build output relative to
/// the platform checkout (developer flow). Returns null when
/// neither path resolves.
/// Resolved hub binary path, for tests that also drive the CLI
/// against the fixture's data dir (e.g. seeding projects).
static Future<String?> binaryPath() => _resolveBinary();
/// Look for the hub binary: $CHAIN_BIN override first, then
/// `chain` on PATH (production-ish), then the release/debug
/// build outputs relative to the platform checkout (developer
/// flow, repo dir `fai_chain`). Returns null when nothing
/// resolves.
static Future<String?> _resolveBinary() async {
final fromEnv = Platform.environment['CHAIN_BIN'];
if (fromEnv != null && fromEnv.isNotEmpty && File(fromEnv).existsSync()) {
return fromEnv;
}
final pathResult = await Process.run(
Platform.isWindows ? 'where' : 'which',
['fai'],
[Platform.isWindows ? 'chain.exe' : 'chain'],
);
if (pathResult.exitCode == 0) {
final s = (pathResult.stdout as String).trim();
if (s.isNotEmpty) return s.split('\n').first.trim();
}
final candidate = Platform.isWindows
? '../chain_platform/target/release/fai.exe'
: '../chain_platform/target/release/fai';
final f = File(candidate);
if (await f.exists()) {
return f.absolute.path;
final exe = Platform.isWindows ? 'chain.exe' : 'chain';
for (final dir in ['release', 'debug']) {
final f = File('../fai_chain/target/$dir/$exe');
if (await f.exists()) {
return f.absolute.path;
}
}
return null;
}