// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. import 'package:flutter/gestures.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_markdown_plus/flutter_markdown_plus.dart'; import 'package:flutter_test/flutter_test.dart'; import 'utils.dart'; void main() => defineTests(); void defineTests() { group( 'structure', () { testWidgets( 'footnote is detected and handle correctly', (WidgetTester tester) async { const String data = 'Foo[^a]\n[^a]: Bar'; await tester.pumpWidget( boilerplate( const MarkdownBody( data: data, ), ), ); final Iterable widgets = tester.allWidgets; expectTextStrings(widgets, [ 'Foo1', '1.', 'Bar ↩', ]); }, ); testWidgets( 'footnote is detected and handle correctly for selectable markdown', (WidgetTester tester) async { const String data = 'Foo[^a]\n[^a]: Bar'; await tester.pumpWidget( boilerplate( const MarkdownBody( data: data, selectable: true, ), ), ); final Iterable widgets = tester.allWidgets; expectTextStrings(widgets, [ 'Foo1', '1.', 'Bar ↩', ]); }, ); testWidgets( 'ignore footnotes without description', (WidgetTester tester) async { const String data = 'Foo[^1] Bar[^2]\n[^1]: Bar'; await tester.pumpWidget( boilerplate( const MarkdownBody( data: data, ), ), ); final Iterable widgets = tester.allWidgets; expectTextStrings(widgets, [ 'Foo1 Bar[^2]', '1.', 'Bar ↩', ]); }, ); testWidgets( 'ignore superscripts and footnotes order', (WidgetTester tester) async { const String data = '[^2]: Bar \n [^1]: Foo \n Foo[^f] Bar[^b]'; await tester.pumpWidget( boilerplate( const MarkdownBody( data: data, ), ), ); final Iterable widgets = tester.allWidgets; expectTextStrings(widgets, [ 'Foo1 Bar2', '1.', 'Foo ↩', '2.', 'Bar ↩', ]); }, ); testWidgets( 'handle two digits superscript', (WidgetTester tester) async { const String data = ''' 1[^1] 2[^2] 3[^3] 4[^4] 5[^5] 6[^6] 7[^7] 8[^8] 9[^9] 10[^10] [^1]:1 [^2]:2 [^3]:3 [^4]:4 [^5]:5 [^6]:6 [^7]:7 [^8]:8 [^9]:9 [^10]:10 '''; await tester.pumpWidget( boilerplate( const MarkdownBody( data: data, ), ), ); final Iterable widgets = tester.allWidgets; expectTextStrings(widgets, [ '11 22 33 44 55 66 77 88 99 1010', '1.', '1 ↩', '2.', '2 ↩', '3.', '3 ↩', '4.', '4 ↩', '5.', '5 ↩', '6.', '6 ↩', '7.', '7 ↩', '8.', '8 ↩', '9.', '9 ↩', '10.', '10 ↩', ]); }, ); }, ); group( 'superscript textstyle replacing', () { testWidgets( 'superscript has correct default fontfeature', (WidgetTester tester) async { const String data = 'Foo[^a]\n[^a]: Bar'; await tester.pumpWidget( boilerplate( const MarkdownBody( data: data, ), ), ); final Iterable widgets = tester.allWidgets; final Text text = widgets.firstWhere((Widget widget) => widget is Text) as Text; final TextSpan span = text.textSpan! as TextSpan; final List? children = span.children; expect(children, isNotNull); expect(children!.length, 2); expect(children[1].style, isNotNull); expect(children[1].style!.fontFeatures?.length, 1); expect(children[1].style!.fontFeatures?.first.feature, 'sups'); }, ); testWidgets( 'superscript has correct custom fontfeature', (WidgetTester tester) async { const String data = 'Foo[^a]\n[^a]: Bar'; await tester.pumpWidget( boilerplate( MarkdownBody( data: data, styleSheet: MarkdownStyleSheet(superscriptFontFeatureTag: 'numr'), ), ), ); final Iterable widgets = tester.allWidgets; final Text text = widgets.firstWhere((Widget widget) => widget is Text) as Text; final TextSpan span = text.textSpan! as TextSpan; final List? children = span.children; expect(children, isNotNull); expect(children!.length, 2); expect(children[1].style, isNotNull); expect(children[1].style!.fontFeatures?.length, 2); expect(children[1].style!.fontFeatures?[1].feature, 'numr'); }, ); testWidgets( 'superscript index has the same font style like text', (WidgetTester tester) async { const String data = '# Foo[^a]\n[^a]: Bar'; await tester.pumpWidget( boilerplate( const MarkdownBody( data: data, ), ), ); final Iterable widgets = tester.allWidgets; final Text text = widgets.firstWhere((Widget widget) => widget is Text) as Text; final TextSpan span = text.textSpan! as TextSpan; final List? children = span.children; expect(children![0].style, isNotNull); expect(children[1].style!.fontSize, children[0].style!.fontSize); expect(children[1].style!.fontFamily, children[0].style!.fontFamily); expect(children[1].style!.fontStyle, children[0].style!.fontStyle); expect(children[1].style!.fontSize, children[0].style!.fontSize); }, ); testWidgets( 'link is correctly copied to new superscript index', (WidgetTester tester) async { final List linkTapResults = []; const String data = 'Foo[^a]\n[^a]: Bar'; await tester.pumpWidget( boilerplate( MarkdownBody( data: data, onTapLink: (String text, String? href, String title) => linkTapResults.add(MarkdownLink(text, href, title)), ), ), ); final Iterable widgets = tester.allWidgets; final Text text = widgets.firstWhere((Widget widget) => widget is Text) as Text; final TextSpan span = text.textSpan! as TextSpan; final List gestureRecognizerTypes = []; span.visitChildren((InlineSpan inlineSpan) { if (inlineSpan is TextSpan) { final TapGestureRecognizer? recognizer = inlineSpan.recognizer as TapGestureRecognizer?; gestureRecognizerTypes.add(recognizer?.runtimeType ?? Null); if (recognizer != null) { recognizer.onTap!(); } } return true; }); expect(span.children!.length, 2); expect( gestureRecognizerTypes, orderedEquals([Null, TapGestureRecognizer]), ); expectLinkTap(linkTapResults[0], const MarkdownLink('1', '#fn-a')); }, ); }, ); }