Design system - MilerSurface ladder (canvas → working → raised → floating) with MilerPanel as layer 1; canvas moved to #DEE3EA so white separates at 1.290:1. - Visible vocabulary applied across Home, Deliveries, Activity, Account and the sheets: hero heads (tabular numeral + small caption, clamped at 1.3x), canvas wells for anything that opens, small filled tags for shelf labels, demoted placeholders. Recorded in DESIGN_SYSTEM.md §6. - One icon family: 222 Material glyphs migrated to Lucide; none left outside lib/xpress. - Colour semantics corrected: amber only for what is genuinely owed, brand red reserved for the live stop, disabled primaries go neutral rather than pale. Data and lifecycle - lib/data/lifecycle.dart reads mutations for what they prove; route_order.dart makes admin sequence the single ordering authority; service_day.dart, and stop_area.dart rewritten against live Coimbatore addresses (digit-token stripping, city stoplist, street suffixes, stammer collapse). - countLabel states the load once, in bags. Testing - 1440 tests passing; golden shot harnesses for Home, Deliveries, Activity, sheets and verify, with test/failures/ now gitignored (diff debris). - New pins: home_gutter_test, stop_area_test, plus updated structural bounds. Note: this commit also carries pre-existing working-tree deletions that were present before this work (API_SPEC.md, README.md, demo test fixtures). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
95 lines
3.5 KiB
Dart
95 lines
3.5 KiB
Dart
import 'dart:io';
|
|
|
|
import 'package:flutter/services.dart';
|
|
import 'package:flutter/widgets.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
|
|
import 'package:miler/views/helpers/constants/Font_constant.dart';
|
|
|
|
/// Fonts a reference shot needs, loaded once per suite.
|
|
///
|
|
/// ── Why every shot harness must call this ──
|
|
///
|
|
/// A golden rendered without a font is a picture of the fallback, and the
|
|
/// fallback for an icon font is an empty box. That is not a loud failure — the
|
|
/// test passes, the image is written, and a whole page of missing glyphs
|
|
/// becomes the reference every future run is compared against.
|
|
///
|
|
/// It has happened twice. Poppins ships as separate weight files and had to be
|
|
/// registered under one family before `w800` headings stopped rendering as
|
|
/// smeared SemiBold; then Lucide arrived as a package-declared font the test
|
|
/// binding does not resolve at all, and every icon on Activity came out as a
|
|
/// hollow square. One loader, so the next family added to the app is added
|
|
/// here rather than in five harnesses that will drift apart.
|
|
Future<void> loadGoldenFonts() async {
|
|
final poppins = FontLoader(FontConstants.fontFamily);
|
|
for (final e in const <(String, FontWeight)>[
|
|
('Light', FontWeight.w300),
|
|
('Regular', FontWeight.w400),
|
|
('Medium', FontWeight.w500),
|
|
('SemiBold', FontWeight.w600),
|
|
('Bold', FontWeight.w700),
|
|
('ExtraBold', FontWeight.w800),
|
|
('Black', FontWeight.w900),
|
|
]) {
|
|
final f = File('assets/fonts/Poppins/Poppins-${e.$1}.ttf');
|
|
if (f.existsSync()) {
|
|
poppins.addFont(f.readAsBytes().then((b) => ByteData.view(b.buffer)));
|
|
}
|
|
}
|
|
await poppins.load();
|
|
|
|
await _loadIfPresent('MaterialIcons', const [
|
|
'/opt/homebrew/share/flutter/bin/cache/artifacts/material_fonts/'
|
|
'MaterialIcons-Regular.otf',
|
|
]);
|
|
|
|
// ── The family name is not `Lucide` ──
|
|
//
|
|
// Its `IconData` carries `fontPackage: 'lucide_icons_flutter'`, and Flutter
|
|
// resolves a packaged font under `packages/<package>/<family>`. Registering
|
|
// it as plain `Lucide` loads a font nothing ever asks for, which looks
|
|
// exactly like not loading it at all: every glyph renders as a hollow box
|
|
// and the golden is written anyway.
|
|
//
|
|
// Both names are registered — the prefixed one is what the widgets resolve,
|
|
// and the bare one costs nothing and covers any direct use.
|
|
final lucide = _lucideCandidates();
|
|
await _loadIfPresent('packages/lucide_icons_flutter/Lucide', lucide);
|
|
await _loadIfPresent('Lucide', lucide, quiet: true);
|
|
}
|
|
|
|
Future<void> _loadIfPresent(
|
|
String family,
|
|
List<String> paths, {
|
|
bool quiet = false,
|
|
}) async {
|
|
for (final p in paths) {
|
|
final f = File(p);
|
|
if (!f.existsSync()) continue;
|
|
final loader = FontLoader(family)
|
|
..addFont(f.readAsBytes().then((b) => ByteData.view(b.buffer)));
|
|
await loader.load();
|
|
return;
|
|
}
|
|
// Absent is reported, never silent: the alternative is a reference image
|
|
// full of empty boxes that nothing ever flags.
|
|
if (quiet) return;
|
|
// ignore: avoid_print
|
|
print('[GOLDEN] $family not found — its glyphs will render as boxes');
|
|
}
|
|
|
|
List<String> _lucideCandidates() {
|
|
final home = Platform.environment['HOME'] ?? '';
|
|
final root = Directory('$home/.pub-cache/hosted/pub.dev');
|
|
if (!root.existsSync()) return const [];
|
|
return [
|
|
for (final d in root.listSync().whereType<Directory>())
|
|
if (d.path
|
|
.split(Platform.pathSeparator)
|
|
.last
|
|
.startsWith('lucide_icons_flutter-'))
|
|
'${d.path}/assets/lucide.ttf',
|
|
];
|
|
}
|