halkyon/packages/halkyon_ui/test/theme/app_fonts_test.dart
Tizian 「ティツィアーン」 80fa7b9628
Halkyon's shared packages
A pub workspace of the Dart packages Halkyon's apps share: halkyon_ui,
Halkyon's look (the theme, fonts, layout and shared widgets, with texts
in English and German), halkyon_core's low-level primitives and
halkyon_interop's cross-app contracts. Dart 3.13 with LeanCode's lints,
configured once at the root; apps take a package by git, pinned to a
signed commit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-18 21:50:15 +02:00

89 lines
3.1 KiB
Dart

import 'dart:ui' as ui;
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:halkyon_ui/halkyon_ui.dart';
import 'package:material_ui/material_ui.dart';
/// How many pixels [text] inks in [family] at [weight]: heavier strokes ink
/// more.
Future<int> ink(String text, String family, FontWeight weight) async {
final painter = TextPainter(
text: TextSpan(
text: text,
style: TextStyle(
fontFamily: family,
fontSize: 40,
fontWeight: weight,
color: const Color(0xFF000000),
),
),
textDirection: TextDirection.ltr,
)..layout();
final recorder = ui.PictureRecorder();
painter.paint(Canvas(recorder), Offset.zero);
final image = await recorder.endRecording().toImage(
painter.width.ceil(),
painter.height.ceil(),
);
final pixels = (await image.toByteData())!;
var inked = 0;
for (var alpha = 3; alpha < pixels.lengthInBytes; alpha += 4) {
if (pixels.getUint8(alpha) > 128) {
inked++;
}
}
return inked;
}
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
test("the font is the package's own, as an app finds a package's font", () {
expect(appFontFamily, 'packages/halkyon_ui/Noto Sans');
});
test("the theme sets text in Noto Sans, with the apps' CJK fonts for what "
'it lacks, named as an app declares them', () {
for (final brightness in Brightness.values) {
final style = appTheme(brightness).textTheme.bodyLarge!;
expect(style.fontFamily, appFontFamily);
expect(style.fontFamilyFallback, [
'Noto Sans JP',
'Noto Sans SC',
'Noto Sans TC',
'Noto Sans KR',
]);
}
});
test("a language's own CJK font comes first, so shared Han characters "
'take its shapes', () {
expect(appFontFallbackFor(null).first, 'Noto Sans JP');
expect(appFontFallbackFor('en'), appFontFallback);
expect(appFontFallbackFor('ja').first, 'Noto Sans JP');
expect(appFontFallbackFor('zh-Hans').first, 'Noto Sans SC');
expect(appFontFallbackFor('zh').first, 'Noto Sans SC');
expect(appFontFallbackFor('zh-Hant').first, 'Noto Sans TC');
expect(appFontFallbackFor('zh-TW').first, 'Noto Sans TC');
expect(appFontFallbackFor('ko').first, 'Noto Sans KR');
expect(hasOwnFont('ko'), isTrue);
expect(hasOwnFont('de'), isFalse);
// Every font stays, once.
expect(appFontFallbackFor('ko').toSet(), appFontFallback.toSet());
expect(appFontFallbackFor('ko'), hasLength(4));
});
test('Noto Sans draws every weight from its one file', () async {
// In this package's own tests it is the root project, whose assets have
// no `packages/` prefix; an app's test of the name is in example/.
final loader = FontLoader(appFontFamily)
..addFont(rootBundle.load('assets/fonts/NotoSans-Variable.ttf'));
await loader.load();
const text = 'Ἀθῆναι Halkyon';
final regular = await ink(text, appFontFamily, FontWeight.w400);
final bold = await ink(text, appFontFamily, FontWeight.w700);
expect(bold, greaterThan(regular * 1.2));
});
}