Files
doormile_milderapp/test/support/golden_fonts.dart
2026-09-09 12:55:23 +05:30

90 lines
3.5 KiB
Dart

import 'dart:io';
import 'package:flutter/services.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.
///
/// ── Load the family the app actually reads ──
///
/// This registers `FontConstants.fontFamily`, so it must be fed that family's
/// files. When the app moved off Poppins and back to Manrope, the loader still
/// pointed at `assets/fonts/Poppins/*` — which does not fail, it registers the
/// wrong glyphs under the right name and every reference shot becomes a
/// picture of a typeface the app does not ship. Manrope is a single variable
/// font, so one file covers the whole weight axis.
Future<void> loadGoldenFonts() async {
await _loadIfPresent(FontConstants.fontFamily, const [
'assets/fonts/Manrope/Manrope-VF.ttf',
]);
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',
];
}