Skip to content

Commit b38301f

Browse files
rajveermalviyagnprice
authored andcommitted
content test: Add offset and size based widget tests for KaTeX content
And remove font and fontsize based tests, as the newer rect offset/size based tests are more accurate anyway.
1 parent 5677317 commit b38301f

File tree

2 files changed

+106
-94
lines changed

2 files changed

+106
-94
lines changed

lib/widgets/content.dart

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -827,7 +827,7 @@ class MathBlock extends StatelessWidget {
827827
textDirection: TextDirection.ltr,
828828
child: SingleChildScrollViewWithScrollbar(
829829
scrollDirection: Axis.horizontal,
830-
child: _Katex(
830+
child: KatexWidget(
831831
nodes: nodes))));
832832
}
833833
}
@@ -839,8 +839,10 @@ const kBaseKatexTextStyle = TextStyle(
839839
fontFamily: 'KaTeX_Main',
840840
height: 1.2);
841841

842-
class _Katex extends StatelessWidget {
843-
const _Katex({
842+
@visibleForTesting
843+
class KatexWidget extends StatelessWidget {
844+
const KatexWidget({
845+
super.key,
844846
required this.nodes,
845847
});
846848

@@ -1271,7 +1273,7 @@ class _InlineContentBuilder {
12711273
: WidgetSpan(
12721274
alignment: PlaceholderAlignment.baseline,
12731275
baseline: TextBaseline.alphabetic,
1274-
child: _Katex(nodes: nodes));
1276+
child: KatexWidget(nodes: nodes));
12751277

12761278
case GlobalTimeNode():
12771279
return WidgetSpan(alignment: PlaceholderAlignment.middle,

test/widgets/content_test.dart

Lines changed: 100 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import 'package:flutter/cupertino.dart';
33
import 'package:flutter/foundation.dart';
44
import 'package:flutter/material.dart';
55
import 'package:flutter/rendering.dart';
6+
import 'package:flutter/services.dart';
67
import 'package:flutter_checks/flutter_checks.dart';
78
import 'package:flutter_test/flutter_test.dart';
89
import 'package:url_launcher/url_launcher.dart';
@@ -572,98 +573,65 @@ void main() {
572573
tester.widget(find.text('λ', findRichText: true));
573574
});
574575

575-
void checkKatexText(
576-
WidgetTester tester,
577-
String text, {
578-
required String fontFamily,
579-
required double fontSize,
580-
required double fontHeight,
581-
}) {
582-
check(mergedStyleOf(tester, text)).isNotNull()
583-
..fontFamily.equals(fontFamily)
584-
..fontSize.equals(fontSize);
585-
check(tester.getSize(find.text(text)))
586-
.height.isCloseTo(fontSize * fontHeight, 0.5);
587-
}
588-
589-
testWidgets('displays KaTeX content with different sizing', (tester) async {
590-
addTearDown(testBinding.reset);
591-
final globalSettings = testBinding.globalStore.settings;
592-
await globalSettings.setBool(BoolGlobalSetting.renderKatex, true);
593-
check(globalSettings).getBool(BoolGlobalSetting.renderKatex).isTrue();
594-
595-
final content = ContentExample.mathBlockKatexSizing;
596-
await prepareContent(tester, plainContent(content.html));
597-
598-
final mathBlockNode = content.expectedNodes.single as MathBlockNode;
599-
final baseNode = mathBlockNode.nodes!.single as KatexSpanNode;
600-
final nodes = baseNode.nodes!.skip(1); // Skip .strut node.
601-
for (var katexNode in nodes) {
602-
katexNode = katexNode as KatexSpanNode;
603-
final fontSize = katexNode.styles.fontSizeEm! * kBaseKatexTextStyle.fontSize!;
604-
checkKatexText(tester, katexNode.text!,
605-
fontFamily: 'KaTeX_Main',
606-
fontSize: fontSize,
607-
fontHeight: kBaseKatexTextStyle.height!);
576+
group('characters render at specific offsets with specific size', () {
577+
const testCases = <(ContentExample, List<(String, Offset, Size)>, {bool? skip})>[
578+
(ContentExample.mathBlockKatexSizing, skip: false, [
579+
('1', Offset(0.00, 2.24), Size(25.59, 61.00)),
580+
('2', Offset(25.59, 9.90), Size(21.33, 51.00)),
581+
('3', Offset(46.91, 16.30), Size(17.77, 43.00)),
582+
('4', Offset(64.68, 21.63), Size(14.80, 36.00)),
583+
('5', Offset(79.48, 26.07), Size(12.34, 30.00)),
584+
('6', Offset(91.82, 29.77), Size(10.28, 25.00)),
585+
('7', Offset(102.10, 31.62), Size(9.25, 22.00)),
586+
('8', Offset(111.35, 33.47), Size(8.23, 20.00)),
587+
('9', Offset(119.58, 35.32), Size(7.20, 17.00)),
588+
('0', Offset(126.77, 39.02), Size(5.14, 12.00)),
589+
]),
590+
(ContentExample.mathBlockKatexNestedSizing, skip: false, [
591+
('1', Offset(0.00, 39.58), Size(5.14, 12.00)),
592+
('2', Offset(5.14, 2.80), Size(25.59, 61.00)),
593+
]),
594+
// TODO: Re-enable this test after adding support for parsing
595+
// `vertical-align` in inline styles. Currently it fails
596+
// because `strut` span has `vertical-align`.
597+
(ContentExample.mathBlockKatexDelimSizing, skip: true, [
598+
('(', Offset(8.00, 46.36), Size(9.42, 25.00)),
599+
('[', Offset(17.42, 48.36), Size(9.71, 25.00)),
600+
('⌈', Offset(27.12, 49.36), Size(11.99, 25.00)),
601+
('⌊', Offset(39.11, 49.36), Size(13.14, 25.00)),
602+
]),
603+
];
604+
605+
for (final testCase in testCases) {
606+
testWidgets(testCase.$1.description, (tester) async {
607+
await _loadKatexFonts();
608+
609+
addTearDown(testBinding.reset);
610+
final globalSettings = testBinding.globalStore.settings;
611+
await globalSettings.setBool(BoolGlobalSetting.renderKatex, true);
612+
check(globalSettings).getBool(BoolGlobalSetting.renderKatex).isTrue();
613+
614+
await prepareContent(tester, plainContent(testCase.$1.html));
615+
616+
final baseRect = tester.getRect(find.byType(KatexWidget));
617+
618+
for (final characterData in testCase.$2) {
619+
final character = characterData.$1;
620+
final expectedTopLeftOffset = characterData.$2;
621+
final expectedSize = characterData.$3;
622+
623+
final rect = tester.getRect(find.text(character));
624+
final topLeftOffset = rect.topLeft - baseRect.topLeft;
625+
final size = rect.size;
626+
627+
check(topLeftOffset)
628+
.within(distance: 0.05, from: expectedTopLeftOffset);
629+
check(size)
630+
.within(distance: 0.05, from: expectedSize);
631+
}
632+
}, skip: testCase.skip);
608633
}
609634
});
610-
611-
testWidgets('displays KaTeX content with nested sizing', (tester) async {
612-
addTearDown(testBinding.reset);
613-
final globalSettings = testBinding.globalStore.settings;
614-
await globalSettings.setBool(BoolGlobalSetting.renderKatex, true);
615-
check(globalSettings).getBool(BoolGlobalSetting.renderKatex).isTrue();
616-
617-
final content = ContentExample.mathBlockKatexNestedSizing;
618-
await prepareContent(tester, plainContent(content.html));
619-
620-
var fontSize = 0.5 * kBaseKatexTextStyle.fontSize!;
621-
checkKatexText(tester, '1',
622-
fontFamily: 'KaTeX_Main',
623-
fontSize: fontSize,
624-
fontHeight: kBaseKatexTextStyle.height!);
625-
626-
fontSize = 4.976 * fontSize;
627-
checkKatexText(tester, '2',
628-
fontFamily: 'KaTeX_Main',
629-
fontSize: fontSize,
630-
fontHeight: kBaseKatexTextStyle.height!);
631-
});
632-
633-
testWidgets('displays KaTeX content with different delimiter sizing', (tester) async {
634-
addTearDown(testBinding.reset);
635-
final globalSettings = testBinding.globalStore.settings;
636-
await globalSettings.setBool(BoolGlobalSetting.renderKatex, true);
637-
check(globalSettings).getBool(BoolGlobalSetting.renderKatex).isTrue();
638-
639-
final content = ContentExample.mathBlockKatexDelimSizing;
640-
await prepareContent(tester, plainContent(content.html));
641-
642-
final mathBlockNode = content.expectedNodes.single as MathBlockNode;
643-
final baseNode = mathBlockNode.nodes!.single as KatexSpanNode;
644-
var nodes = baseNode.nodes!.skip(1); // Skip .strut node.
645-
646-
final fontSize = kBaseKatexTextStyle.fontSize!;
647-
648-
final firstNode = nodes.first as KatexSpanNode;
649-
checkKatexText(tester, firstNode.text!,
650-
fontFamily: 'KaTeX_Main',
651-
fontSize: fontSize,
652-
fontHeight: kBaseKatexTextStyle.height!);
653-
nodes = nodes.skip(1);
654-
655-
for (var katexNode in nodes) {
656-
katexNode = katexNode as KatexSpanNode;
657-
katexNode = katexNode.nodes!.single as KatexSpanNode; // Skip empty .mord parent.
658-
final fontFamily = katexNode.styles.fontFamily!;
659-
checkKatexText(tester, katexNode.text!,
660-
fontFamily: fontFamily,
661-
fontSize: fontSize,
662-
fontHeight: kBaseKatexTextStyle.height!);
663-
}
664-
}, skip: true); // TODO: Re-enable this test after adding support for parsing
665-
// `vertical-align` in inline styles. Currently it fails
666-
// because `strut` span has `vertical-align`.
667635
});
668636

669637
/// Make a [TargetFontSizeFinder] to pass to [checkFontSizeRatio],
@@ -1432,3 +1400,45 @@ void main() {
14321400
});
14331401
});
14341402
}
1403+
1404+
Future<void> _loadKatexFonts() async {
1405+
const fonts = {
1406+
'KaTeX_AMS': ['KaTeX_AMS-Regular.ttf'],
1407+
'KaTeX_Caligraphic': [
1408+
'KaTeX_Caligraphic-Regular.ttf',
1409+
'KaTeX_Caligraphic-Bold.ttf',
1410+
],
1411+
'KaTeX_Fraktur': [
1412+
'KaTeX_Fraktur-Regular.ttf',
1413+
'KaTeX_Fraktur-Bold.ttf',
1414+
],
1415+
'KaTeX_Main': [
1416+
'KaTeX_Main-Regular.ttf',
1417+
'KaTeX_Main-Bold.ttf',
1418+
'KaTeX_Main-Italic.ttf',
1419+
'KaTeX_Main-BoldItalic.ttf',
1420+
],
1421+
'KaTeX_Math': [
1422+
'KaTeX_Math-Italic.ttf',
1423+
'KaTeX_Math-BoldItalic.ttf',
1424+
],
1425+
'KaTeX_SansSerif': [
1426+
'KaTeX_SansSerif-Regular.ttf',
1427+
'KaTeX_SansSerif-Bold.ttf',
1428+
'KaTeX_SansSerif-Italic.ttf',
1429+
],
1430+
'KaTeX_Script': ['KaTeX_Script-Regular.ttf'],
1431+
'KaTeX_Size1': ['KaTeX_Size1-Regular.ttf'],
1432+
'KaTeX_Size2': ['KaTeX_Size2-Regular.ttf'],
1433+
'KaTeX_Size3': ['KaTeX_Size3-Regular.ttf'],
1434+
'KaTeX_Size4': ['KaTeX_Size4-Regular.ttf'],
1435+
'KaTeX_Typewriter': ['KaTeX_Typewriter-Regular.ttf'],
1436+
};
1437+
for (final MapEntry(key: fontFamily, value: fontFiles) in fonts.entries) {
1438+
final fontLoader = FontLoader(fontFamily);
1439+
for (final fontFile in fontFiles) {
1440+
fontLoader.addFont(rootBundle.load('assets/KaTeX/$fontFile'));
1441+
}
1442+
await fontLoader.load();
1443+
}
1444+
}

0 commit comments

Comments
 (0)