test: generated nav manifest guards the sidebar truth
test/nav_manifest_test.dart derives docs/nav.generated.json from the _pages list (order = Cmd numbers), the labelOf switch, and both .arb files. On any nav change it regenerates the manifest and fails once with instructions to commit + mirror it to fai_chain/docs/studio/, where the platform repo's docs_consistency gate checks the operator guide against it — cross-repo nav drift becomes a red gate instead of quietly rotting docs. Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
This commit is contained in:
parent
09c901b75e
commit
ed2c5ace26
2 changed files with 171 additions and 0 deletions
53
docs/nav.generated.json
Normal file
53
docs/nav.generated.json
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
{
|
||||
"_generated": "by test/nav_manifest_test.dart — do not edit. Mirror to fai_chain/docs/studio/nav.generated.json (same content).",
|
||||
"pages": [
|
||||
{
|
||||
"id": "welcome",
|
||||
"cmd": 1,
|
||||
"label_de": "Willkommen",
|
||||
"label_en": "Welcome"
|
||||
},
|
||||
{
|
||||
"id": "store",
|
||||
"cmd": 2,
|
||||
"label_de": "Store",
|
||||
"label_en": "Store"
|
||||
},
|
||||
{
|
||||
"id": "doctor",
|
||||
"cmd": 3,
|
||||
"label_de": "Diagnose",
|
||||
"label_en": "Doctor"
|
||||
},
|
||||
{
|
||||
"id": "flows",
|
||||
"cmd": 4,
|
||||
"label_de": "Flows",
|
||||
"label_en": "Flows"
|
||||
},
|
||||
{
|
||||
"id": "audit",
|
||||
"cmd": 5,
|
||||
"label_de": "Protokoll",
|
||||
"label_en": "Audit"
|
||||
},
|
||||
{
|
||||
"id": "approvals",
|
||||
"cmd": 6,
|
||||
"label_de": "Freigaben",
|
||||
"label_en": "Approvals"
|
||||
},
|
||||
{
|
||||
"id": "runs",
|
||||
"cmd": 7,
|
||||
"label_de": "Läufe",
|
||||
"label_en": "Runs"
|
||||
},
|
||||
{
|
||||
"id": "federation",
|
||||
"cmd": 8,
|
||||
"label_de": "Föderation",
|
||||
"label_en": "Federation"
|
||||
}
|
||||
]
|
||||
}
|
||||
118
test/nav_manifest_test.dart
Normal file
118
test/nav_manifest_test.dart
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
// Nav-manifest guard. The sidebar truth lives in three places in
|
||||
// this repo: the `_pages` list (ids + order = Cmd numbers), the
|
||||
// `labelOf` switch (id → l10n key), and the two .arb files (key →
|
||||
// DE/EN label). The operator guide documenting that nav lives in
|
||||
// the PLATFORM repo (fai_chain/docs/studio/operator-guide.md),
|
||||
// which this repo's CI cannot see — so the truth is exported as a
|
||||
// committed manifest, `docs/nav.generated.json`, and the platform
|
||||
// repo's docs_consistency test checks the guide against it.
|
||||
//
|
||||
// This test derives the manifest from the sources. When it differs
|
||||
// from the committed file it REWRITES the file and fails once with
|
||||
// instructions — commit the regenerated manifest and mirror it to
|
||||
// fai_chain/docs/studio/nav.generated.json so the cross-repo guard
|
||||
// stays truthful. Add/rename/reorder a page and forget the guide:
|
||||
// the platform gate goes loudly red instead of the docs quietly
|
||||
// rotting.
|
||||
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
const _manifestPath = 'docs/nav.generated.json';
|
||||
const _mirrorHint =
|
||||
'fai_chain/docs/studio/nav.generated.json (same content)';
|
||||
|
||||
/// Extract the ordered page ids from the `_pages` list in main.dart.
|
||||
List<String> _pageIds(String mainDart) {
|
||||
final block = RegExp(
|
||||
r'static const _pages = <_NavPage>\[(.*?)\n \];',
|
||||
dotAll: true,
|
||||
).firstMatch(mainDart);
|
||||
if (block == null) {
|
||||
fail('could not locate the _pages block in lib/main.dart');
|
||||
}
|
||||
return RegExp(r"id: '([a-z-]+)'")
|
||||
.allMatches(block.group(1)!)
|
||||
.map((m) => m.group(1)!)
|
||||
.toList();
|
||||
}
|
||||
|
||||
/// Extract the id → l10n-key mapping from the `labelOf` switch.
|
||||
Map<String, String> _labelKeys(String mainDart) {
|
||||
final keys = <String, String>{};
|
||||
for (final m in RegExp(
|
||||
r"case '([a-z-]+)':\s*\n\s*return l\.(nav\w+);",
|
||||
).allMatches(mainDart)) {
|
||||
keys[m.group(1)!] = m.group(2)!;
|
||||
}
|
||||
if (keys.isEmpty) {
|
||||
fail('could not parse the labelOf switch in lib/main.dart');
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
Map<String, dynamic> _arb(String path) =>
|
||||
jsonDecode(File(path).readAsStringSync()) as Map<String, dynamic>;
|
||||
|
||||
void main() {
|
||||
test('docs/nav.generated.json matches the sidebar truth', () {
|
||||
final mainDart = File('lib/main.dart').readAsStringSync();
|
||||
final ids = _pageIds(mainDart);
|
||||
final labelKeys = _labelKeys(mainDart);
|
||||
final de = _arb('lib/l10n/app_de.arb');
|
||||
final en = _arb('lib/l10n/app_en.arb');
|
||||
|
||||
final pages = <Map<String, dynamic>>[];
|
||||
for (var i = 0; i < ids.length; i++) {
|
||||
final key = labelKeys[ids[i]];
|
||||
expect(
|
||||
key,
|
||||
isNotNull,
|
||||
reason:
|
||||
"page '${ids[i]}' has no labelOf case — add it so the "
|
||||
'sidebar can localize the label',
|
||||
);
|
||||
final labelDe = de[key] as String?;
|
||||
final labelEn = en[key] as String?;
|
||||
expect(
|
||||
labelDe,
|
||||
isNotNull,
|
||||
reason: "l10n key '$key' missing from app_de.arb",
|
||||
);
|
||||
expect(
|
||||
labelEn,
|
||||
isNotNull,
|
||||
reason: "l10n key '$key' missing from app_en.arb",
|
||||
);
|
||||
pages.add({
|
||||
'id': ids[i],
|
||||
'cmd': i + 1,
|
||||
'label_de': labelDe,
|
||||
'label_en': labelEn,
|
||||
});
|
||||
}
|
||||
|
||||
const encoder = JsonEncoder.withIndent(' ');
|
||||
final manifest =
|
||||
'${encoder.convert({
|
||||
'_generated': 'by test/nav_manifest_test.dart — do not edit. '
|
||||
'Mirror to $_mirrorHint.',
|
||||
'pages': pages,
|
||||
})}\n';
|
||||
|
||||
final file = File(_manifestPath);
|
||||
final current = file.existsSync() ? file.readAsStringSync() : null;
|
||||
if (current != manifest) {
|
||||
file.parent.createSync(recursive: true);
|
||||
file.writeAsStringSync(manifest);
|
||||
fail(
|
||||
'the sidebar nav changed: $_manifestPath was regenerated. '
|
||||
'Commit it, mirror it to $_mirrorHint, and update the '
|
||||
'operator guide — the platform repo\'s docs_consistency '
|
||||
'test checks the guide against this manifest.',
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue