90 lines
3.5 KiB
Dart
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',
|
|
];
|
|
}
|