// 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';

// The emphasis and strong emphasis section of the GitHub Flavored Markdown
// specification (https://github.github.com/gfm/#emphasis-and-strong-emphasis)
// is extensive covering over 130 example cases. The tests in this file cover
// all of the GFM tests; example 360 through 490.

void main() => defineTests();

void defineTests() {
  group(
    'Emphasis',
    () {
      group(
        'Rule 1',
        () {
          // Rule 1 tests check the single '*' can open emphasis.
          testWidgets(
            // Example 360 from GFM.
            'italic text using asterisk tags',
            (WidgetTester tester) async {
              const String data = '*foo bar*';
              await tester.pumpWidget(
                boilerplate(
                  const MarkdownBody(data: data),
                ),
              );

              final Finder textFinder = find.byType(Text);
              expect(textFinder, findsOneWidget);

              final Text textWidget = textFinder.evaluate().first.widget as Text;
              final String text = textWidget.textSpan!.toPlainText();
              expect(text, 'foo bar');

              expectTextSpanStyle(
                textWidget.textSpan! as TextSpan,
                FontStyle.italic,
                FontWeight.normal,
              );
            },
          );

          testWidgets(
            // Example 361 from GFM.
            'invalid left-flanking delimiter run because * is followed by whitespace',
            (WidgetTester tester) async {
              const String data = 'a * foo bar*';
              await tester.pumpWidget(
                boilerplate(
                  const MarkdownBody(data: data),
                ),
              );

              final Finder textFinder = find.byType(Text);
              expect(textFinder, findsOneWidget);

              // Expect text to be unchanged from original data string.
              final Text textWidget = textFinder.evaluate().first.widget as Text;
              final String text = textWidget.textSpan!.toPlainText();
              expect(text, data);

              expectTextSpanStyle(
                textWidget.textSpan! as TextSpan,
                null,
                FontWeight.normal,
              );
            },
          );

          testWidgets(
            // Example 362 from GFM.
            'invalid left-flanking delimiter run because * preceded by alphanumeric followed by punctuation',
            (WidgetTester tester) async {
              const String data = 'a*"foo bar"*';
              await tester.pumpWidget(
                boilerplate(
                  const MarkdownBody(data: data),
                ),
              );

              final Finder textFinder = find.byType(Text);
              expect(textFinder, findsOneWidget);

              // Expect text to be unchanged from original data string.
              final Text textWidget = textFinder.evaluate().first.widget as Text;
              final String text = textWidget.textSpan!.toPlainText();
              expect(text, data);

              expectTextSpanStyle(
                textWidget.textSpan! as TextSpan,
                null,
                FontWeight.normal,
              );
            },
          );

          // NOTE: Example 363 is not included. The test is "Unicode nonbreaking
          // spaces count as whitespace, too: '* a *' The Markdown parse sees
          // this as a unordered list item." https://github.github.com/gfm/#example-363

          testWidgets(
            // Example 364 from GFM.
            'intraword emphasis with * is permitted alpha characters',
            (WidgetTester tester) async {
              const String data = 'foo*bar*';
              await tester.pumpWidget(
                boilerplate(
                  const MarkdownBody(data: data),
                ),
              );

              final Finder textFinder = find.byType(Text);
              expect(textFinder, findsOneWidget);

              final Text textWidget = textFinder.evaluate().first.widget as Text;
              expect(textWidget, isNotNull);
              final String text = textWidget.textSpan!.toPlainText();
              expect(text, 'foobar');

              // There should be two spans of text.
              final TextSpan textSpan = textWidget.textSpan! as TextSpan;
              expect(textSpan, isNotNull);
              expect(textSpan.children!.length == 2, isTrue);

              // First text span is normal text with no emphasis.
              final InlineSpan firstSpan = textSpan.children![0];
              expectTextSpanStyle(
                firstSpan as TextSpan,
                null,
                FontWeight.normal,
              );

              // Second span has italic style with normal weight.
              final InlineSpan secondSpan = textSpan.children![1];
              expectTextSpanStyle(
                secondSpan as TextSpan,
                FontStyle.italic,
                FontWeight.normal,
              );
            },
          );

          testWidgets(
            // Example 365 from GFM.
            'intraword emphasis with * is permitted numeric characters',
            (WidgetTester tester) async {
              const String data = '5*6*78';
              await tester.pumpWidget(
                boilerplate(
                  const MarkdownBody(data: data),
                ),
              );

              final Finder textFinder = find.byType(Text);
              expect(textFinder, findsOneWidget);

              final Text textWidget = textFinder.evaluate().first.widget as Text;
              expect(textWidget, isNotNull);
              final String text = textWidget.textSpan!.toPlainText();
              expect(text, '5678');

              // There should be three spans of text.
              final TextSpan textSpan = textWidget.textSpan! as TextSpan;
              expect(textSpan, isNotNull);
              expect(textSpan.children!.length == 3, isTrue);

              // First text span is normal text with no emphasis.
              final InlineSpan firstSpan = textSpan.children![0];
              expectTextSpanStyle(
                firstSpan as TextSpan,
                null,
                FontWeight.normal,
              );

              // Second span has italic style with normal weight.
              final InlineSpan secondSpan = textSpan.children![1];
              expectTextSpanStyle(
                secondSpan as TextSpan,
                FontStyle.italic,
                FontWeight.normal,
              );

              // Third text span is normal text with no emphasis.
              final InlineSpan thirdSpan = textSpan.children![2];
              expectTextSpanStyle(
                thirdSpan as TextSpan,
                null,
                FontWeight.normal,
              );
            },
          );
        },
      );

      group('Rule 2', () {
        testWidgets(
          // Example 366 from GFM.
          'italic text using underscore tags',
          (WidgetTester tester) async {
            const String data = '_foo bar_';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, 'foo bar');

            expectTextSpanStyle(
              textWidget.textSpan! as TextSpan,
              FontStyle.italic,
              FontWeight.normal,
            );
          },
        );

        testWidgets(
          // Example 367 from GFM.
          'invalid left-flanking delimiter run because _ is followed by whitespace',
          (WidgetTester tester) async {
            const String data = '_ foo bar_';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            // Expect text to be unchanged from original data string.
            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, data);

            expectTextSpanStyle(
              textWidget.textSpan! as TextSpan,
              null,
              FontWeight.normal,
            );
          },
        );

        testWidgets(
          // Example 368 from GFM.
          'invalid left-flanking delimiter run because _ preceded by alphanumeric followed by punctuation',
          (WidgetTester tester) async {
            const String data = 'a_"foo bar"_';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            // Expect text to be unchanged from original data string.
            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, data);

            expectTextSpanStyle(
              textWidget.textSpan! as TextSpan,
              null,
              FontWeight.normal,
            );
          },
        );

        testWidgets(
          // Example 369 from GFM.
          'emphasis with _ is not allowed inside words alpha characters',
          (WidgetTester tester) async {
            const String data = 'foo_bar_';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            // Expect text to be unchanged from original data string.
            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, data);

            expectTextSpanStyle(
              textWidget.textSpan! as TextSpan,
              null,
              FontWeight.normal,
            );
          },
        );

        testWidgets(
          // Example 370 from GFM.
          'emphasis with _ is not allowed inside words numeric characters',
          (WidgetTester tester) async {
            const String data = '5_6_78';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            // Expect text to be unchanged from original data string.
            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, data);

            expectTextSpanStyle(
              textWidget.textSpan! as TextSpan,
              null,
              FontWeight.normal,
            );
          },
        );

        testWidgets(
          // Example 371 from GFM.
          'emphasis with _ is not allowed inside words unicode characters',
          (WidgetTester tester) async {
            const String data = 'пристаням_стремятся_';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            // Expect text to be unchanged from original data string.
            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, data);

            expectTextSpanStyle(
              textWidget.textSpan! as TextSpan,
              null,
              FontWeight.normal,
            );
          },
        );

        testWidgets(
          // Example 372 from GFM.
          'invalid first delimiter right-flanking followed by second delimiter left-flanking',
          (WidgetTester tester) async {
            const String data = 'aa_"bb"_cc';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            // Expect text to be unchanged from original data string.
            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, data);

            expectTextSpanStyle(
              textWidget.textSpan! as TextSpan,
              null,
              FontWeight.normal,
            );
          },
        );

        testWidgets(
          // Example 373 from GFM.
          'valid open delimiter left- and right-flanking preceded by punctuation',
          (WidgetTester tester) async {
            const String data = 'foo-_(bar)_';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, 'foo-(bar)');

            // There should be two spans of text.
            final TextSpan textSpan = textWidget.textSpan! as TextSpan;
            expect(textSpan, isNotNull);
            expect(textSpan.children!.length == 2, isTrue);

            // First text span is normal text with no emphasis.
            final InlineSpan firstSpan = textSpan.children![0];
            expectTextSpanStyle(
              firstSpan as TextSpan,
              null,
              FontWeight.normal,
            );

            // Second span has italic style with normal weight.
            final InlineSpan secondSpan = textSpan.children![1];
            expectTextSpanStyle(
              secondSpan as TextSpan,
              FontStyle.italic,
              FontWeight.normal,
            );
          },
        );
      });

      group('Rule 3', () {
        testWidgets(
          // Example 374 from GFM.
          'invalid emphasis - closing delimiter does not match opening delimiter',
          (WidgetTester tester) async {
            const String data = '_foo*';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            // Expect text to be unchanged from original data string.
            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, data);

            expectTextSpanStyle(
              textWidget.textSpan! as TextSpan,
              null,
              FontWeight.normal,
            );
          },
        );

        testWidgets(
          // Example 375 from GFM.
          'invalid emphasis - closing * is preceded by whitespace',
          (WidgetTester tester) async {
            const String data = '*foo bar *';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            // Expect text to be unchanged from original data string.
            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, data);

            expectTextSpanStyle(
              textWidget.textSpan! as TextSpan,
              null,
              FontWeight.normal,
            );
          },
        );

        testWidgets(
          // Example 376 from GFM.
          'invalid emphasis - closing * is preceded by newline',
          (WidgetTester tester) async {
            const String data = '*foo bar\n*';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, '*foo bar *');

            expectTextSpanStyle(
              textWidget.textSpan! as TextSpan,
              null,
              FontWeight.normal,
            );
          },
        );

        testWidgets(
          // Example 377 from GFM.
          'invalid emphasis - second * is preceded by punctuation followed by alphanumeric',
          (WidgetTester tester) async {
            const String data = '*(*foo)';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            // Expect text to be unchanged from original data string.
            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, data);

            expectTextSpanStyle(
              textWidget.textSpan! as TextSpan,
              null,
              FontWeight.normal,
            );
          },
        );

        testWidgets(
          // Example 378 from GFM.
          'nested * emphasis',
          (WidgetTester tester) async {
            const String data = '*(*foo*)*';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, '(foo)');

            expectTextSpanStyle(
              textWidget.textSpan! as TextSpan,
              FontStyle.italic,
              FontWeight.normal,
            );
          },
        );

        testWidgets(
          // Example 379 from GFM.
          'intraword emphasis with * is allowed',
          (WidgetTester tester) async {
            const String data = '*foo*bar';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, 'foobar');

            // There should be two spans of text.
            final TextSpan textSpan = textWidget.textSpan! as TextSpan;
            expect(textSpan, isNotNull);
            expect(textSpan.children!.length == 2, isTrue);

            // First text span has italic style with normal weight.
            final InlineSpan firstSpan = textSpan.children![0];
            expectTextSpanStyle(
              firstSpan as TextSpan,
              FontStyle.italic,
              FontWeight.normal,
            );

            // Second span is normal text with no emphasis.
            final InlineSpan secondSpan = textSpan.children![1];
            expectTextSpanStyle(
              secondSpan as TextSpan,
              null,
              FontWeight.normal,
            );
          },
        );
      });

      group('Rule 4', () {
        testWidgets(
          // Example 380 from GFM.
          'invalid emphasis because closing _ is preceded by whitespace',
          (WidgetTester tester) async {
            const String data = '_foo bar _';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            // Expect text to be unchanged from original data string.
            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, data);

            expectTextSpanStyle(
              textWidget.textSpan! as TextSpan,
              null,
              FontWeight.normal,
            );
          },
        );

        testWidgets(
          // Example 381 from GFM.
          'invalid emphasis because second _ is preceded by punctuation and followed by an alphanumeric',
          (WidgetTester tester) async {
            const String data = '_(_foo)';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            // Expect text to be unchanged from original data string.
            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, data);

            expectTextSpanStyle(
              textWidget.textSpan! as TextSpan,
              null,
              FontWeight.normal,
            );
          },
        );

        testWidgets(
          // Example 382 from GFM.
          'nested _ emphasis',
          (WidgetTester tester) async {
            const String data = '_(_foo_)_';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, '(foo)');

            expectTextSpanStyle(
              textWidget.textSpan! as TextSpan,
              FontStyle.italic,
              FontWeight.normal,
            );
          },
        );

        testWidgets(
          // Example 383 from GFM.
          'intraword emphasis with _ is disallowed - alpha characters',
          (WidgetTester tester) async {
            const String data = '_foo_bar';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            // Expect text to be unchanged from original data string.
            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, data);

            expectTextSpanStyle(
              textWidget.textSpan! as TextSpan,
              null,
              FontWeight.normal,
            );
          },
        );

        testWidgets(
          // Example 384 from GFM.
          'intraword emphasis with _ is disallowed - unicode characters',
          (WidgetTester tester) async {
            const String data = '_пристаням_стремятся';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            // Expect text to be unchanged from original data string.
            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, data);

            expectTextSpanStyle(
              textWidget.textSpan! as TextSpan,
              null,
              FontWeight.normal,
            );
          },
        );

        testWidgets(
          // Example 385 from GFM.
          'intraword emphasis with _ is disallowed - nested emphasis tags',
          (WidgetTester tester) async {
            const String data = '_foo_bar_baz_';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, 'foo_bar_baz');

            expectTextSpanStyle(
              textWidget.textSpan! as TextSpan,
              FontStyle.italic,
              FontWeight.normal,
            );
          },
        );

        testWidgets(
          // Example 386 from GFM.
          'valid emphasis closing delimiter is both left- and right-flanking followed by punctuation',
          (WidgetTester tester) async {
            const String data = '_(bar)_.';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, '(bar).');

            // There should be two spans of text.
            final TextSpan textSpan = textWidget.textSpan! as TextSpan;
            expect(textSpan, isNotNull);
            expect(textSpan.children!.length == 2, isTrue);

            // First text span has italic style with normal weight.
            final InlineSpan firstSpan = textSpan.children![0];
            expectTextSpanStyle(
              firstSpan as TextSpan,
              FontStyle.italic,
              FontWeight.normal,
            );

            // Second span is normal text with no emphasis.
            final InlineSpan secondSpan = textSpan.children![1];
            expectTextSpanStyle(
              secondSpan as TextSpan,
              null,
              FontWeight.normal,
            );
          },
        );
      });

      group('Rule 5', () {
        testWidgets(
          // Example 387 from GFM.
          'strong emphasis using ** emphasis tags',
          (WidgetTester tester) async {
            const String data = '**foo bar**';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, 'foo bar');

            expectTextSpanStyle(
              textWidget.textSpan! as TextSpan,
              null,
              FontWeight.bold,
            );
          },
        );

        testWidgets(
          // Example 388 from GFM.
          'invalid strong emphasis - opening delimiter followed by whitespace',
          (WidgetTester tester) async {
            const String data = '** foo bar**';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            // Expect text to be unchanged from original data string.
            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, data);

            expectTextSpanStyle(
              textWidget.textSpan! as TextSpan,
              null,
              FontWeight.normal,
            );
          },
        );

        testWidgets(
          // Example 389 from GFM.
          'invalid strong emphasis - opening ** is preceded by an alphanumeric and followed by punctuation',
          (WidgetTester tester) async {
            const String data = 'a**"foo"**';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            // Expect text to be unchanged from original data string.
            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, data);

            expectTextSpanStyle(
              textWidget.textSpan! as TextSpan,
              null,
              FontWeight.normal,
            );
          },
        );

        testWidgets(
          // Example 390 from GFM.
          'intraword strong emphasis with ** is permitted',
          (WidgetTester tester) async {
            const String data = 'foo**bar**';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, 'foobar');

            // There should be two spans of text.
            final TextSpan textSpan = textWidget.textSpan! as TextSpan;
            expect(textSpan, isNotNull);
            expect(textSpan.children!.length == 2, isTrue);

            // First text span is normal text with no emphasis.
            final InlineSpan firstSpan = textSpan.children![0];
            expectTextSpanStyle(
              firstSpan as TextSpan,
              null,
              FontWeight.normal,
            );

            // Second span has bold weight.
            final InlineSpan secondSpan = textSpan.children![1];
            expectTextSpanStyle(
              secondSpan as TextSpan,
              null,
              FontWeight.bold,
            );
          },
        );
      });

      group('Rule 6', () {
        testWidgets(
          // Example 391 from GFM.
          'strong emphasis using __ emphasis tags',
          (WidgetTester tester) async {
            const String data = '__foo bar__';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, 'foo bar');

            expectTextSpanStyle(
              textWidget.textSpan! as TextSpan,
              null,
              FontWeight.bold,
            );
          },
        );

        testWidgets(
          // Example 392 from GFM.
          'invalid strong emphasis - opening delimiter followed by whitespace',
          (WidgetTester tester) async {
            const String data = '__ foo bar__';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            // Expect text to be unchanged from original data string.
            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, data);

            expectTextSpanStyle(
              textWidget.textSpan! as TextSpan,
              null,
              FontWeight.normal,
            );
          },
        );

        testWidgets(
          // Example 393 from GFM.
          'invalid strong emphasis - opening delimiter followed by newline',
          (WidgetTester tester) async {
            const String data = '__\nfoo bar__';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, '__ foo bar__');

            expectTextSpanStyle(
              textWidget.textSpan! as TextSpan,
              null,
              FontWeight.normal,
            );
          },
        );

        testWidgets(
          // Example 394 from GFM.
          'invalid strong emphasis - opening __ is preceded by an alphanumeric and followed by punctuation',
          (WidgetTester tester) async {
            const String data = 'a__"foo"__';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            // Expect text to be unchanged from original data string.
            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, data);

            expectTextSpanStyle(
              textWidget.textSpan! as TextSpan,
              null,
              FontWeight.normal,
            );
          },
        );

        testWidgets(
          // Example 395 from GFM.
          'intraword strong emphasis is forbidden with __ - alpha characters',
          (WidgetTester tester) async {
            const String data = 'foo__bar__';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            // Expect text to be unchanged from original data string.
            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, data);

            expectTextSpanStyle(
              textWidget.textSpan! as TextSpan,
              null,
              FontWeight.normal,
            );
          },
        );

        testWidgets(
          // Example 396 from GFM.
          'intraword strong emphasis is forbidden with __ - numeric characters',
          (WidgetTester tester) async {
            const String data = '5__6__78';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            // Expect text to be unchanged from original data string.
            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, data);

            expectTextSpanStyle(
              textWidget.textSpan! as TextSpan,
              null,
              FontWeight.normal,
            );
          },
        );

        testWidgets(
          // Example 397 from GFM.
          'intraword strong emphasis is forbidden with __ - unicode characters',
          (WidgetTester tester) async {
            const String data = 'пристаням__стремятся__';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            // Expect text to be unchanged from original data string.
            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, data);

            expectTextSpanStyle(
              textWidget.textSpan! as TextSpan,
              null,
              FontWeight.normal,
            );
          },
        );

        testWidgets(
          // Example 398 from GFM.
          'intraword strong emphasis is forbidden with __ - nested strong emphasis',
          (WidgetTester tester) async {
            const String data = '__foo, __bar__, baz__';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            // Expect text to be unchanged from original data string.
            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, 'foo, bar, baz');

            expectTextSpanStyle(
              textWidget.textSpan! as TextSpan,
              null,
              FontWeight.bold,
            );
          },
        );

        testWidgets(
          // Example 399 from GFM.
          'valid strong emphasis because opening delimiter is both left- and right-flanking preceded by punctuation',
          (WidgetTester tester) async {
            const String data = 'foo-__(bar)__';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            // Expect text to be unchanged from original data string.
            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, 'foo-(bar)');

            // There should be two spans of text.
            final TextSpan textSpan = textWidget.textSpan! as TextSpan;
            expect(textSpan, isNotNull);
            expect(textSpan.children!.length == 2, isTrue);

            // First text span is normal text with no emphasis.
            final InlineSpan firstSpan = textSpan.children![0];
            expectTextSpanStyle(
              firstSpan as TextSpan,
              null,
              FontWeight.normal,
            );

            // Second span has bold weight.
            final InlineSpan secondSpan = textSpan.children![1];
            expectTextSpanStyle(
              secondSpan as TextSpan,
              null,
              FontWeight.bold,
            );
          },
        );
      });

      group('Rule 7', () {
        testWidgets(
          // Example 400 from GFM.
          'invalid strong emphasis - closing delimiter is preceded by whitespace',
          (WidgetTester tester) async {
            const String data = '**foo bar **';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            // Expect text to be unchanged from original data string.
            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, data);

            expectTextSpanStyle(
              textWidget.textSpan! as TextSpan,
              null,
              FontWeight.normal,
            );
          },
        );

        testWidgets(
          // Example 401 from GFM.
          'invalid strong emphasis - second ** is preceded by punctuation and followed by an alphanumeric',
          (WidgetTester tester) async {
            const String data = '**(**foo)';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            // Expect text to be unchanged from original data string.
            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, data);

            expectTextSpanStyle(
              textWidget.textSpan! as TextSpan,
              null,
              FontWeight.normal,
            );
          },
        );

        testWidgets(
          // Example 402 from GFM.
          'emphasis with nested strong emphasis',
          (WidgetTester tester) async {
            const String data = '*(**foo**)*';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            // Expect text to be unchanged from original data string.
            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, '(foo)');

            // There should be three spans of text.
            final TextSpan textSpan = textWidget.textSpan! as TextSpan;
            expect(textSpan, isNotNull);
            expect(textSpan.children!.length == 3, isTrue);

            // First text span has italic style with normal weight.
            final InlineSpan firstSpan = textSpan.children![0];
            expectTextSpanStyle(
              firstSpan as TextSpan,
              FontStyle.italic,
              FontWeight.normal,
            );

            // Second span has italic style with bold weight.
            final InlineSpan secondSpan = textSpan.children![1];
            expectTextSpanStyle(
              secondSpan as TextSpan,
              FontStyle.italic,
              FontWeight.bold,
            );

            // Third text span has italic style with normal weight.
            final InlineSpan thirdSpan = textSpan.children![2];
            expectTextSpanStyle(
              thirdSpan as TextSpan,
              FontStyle.italic,
              FontWeight.normal,
            );
          },
        );

        testWidgets(
          // Example 403 from GFM.
          'strong emphasis with multiple nested emphasis',
          (WidgetTester tester) async {
            const String data = '**Gomphocarpus (*Gomphocarpus physocarpus*, syn. *Asclepias physocarpa*)**';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            // Expect text to be unchanged from original data string.
            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, 'Gomphocarpus (Gomphocarpus physocarpus, syn. Asclepias physocarpa)');

            // There should be five spans of text.
            final TextSpan textSpan = textWidget.textSpan! as TextSpan;
            expect(textSpan, isNotNull);
            expect(textSpan.children!.length == 5, isTrue);

            // First text span has bold weight.
            final InlineSpan firstSpan = textSpan.children![0];
            expectTextSpanStyle(
              firstSpan as TextSpan,
              null,
              FontWeight.bold,
            );

            // Second span has both italic style with bold weight.
            final InlineSpan secondSpan = textSpan.children![1];
            expectTextSpanStyle(
              secondSpan as TextSpan,
              FontStyle.italic,
              FontWeight.bold,
            );

            // Third text span has bold weight.
            final InlineSpan thirdSpan = textSpan.children![2];
            expectTextSpanStyle(
              thirdSpan as TextSpan,
              null,
              FontWeight.bold,
            );

            // Fourth text span has both italic style with bold weight.
            final InlineSpan fourthSpan = textSpan.children![3];
            expectTextSpanStyle(
              fourthSpan as TextSpan,
              FontStyle.italic,
              FontWeight.bold,
            );

            // Fifth text span has bold weight.
            final InlineSpan fifthSpan = textSpan.children![4];
            expectTextSpanStyle(
              fifthSpan as TextSpan,
              null,
              FontWeight.bold,
            );
          },
        );

        testWidgets(
          // Example 404 from GFM.
          'strong emphasis with nested emphasis',
          (WidgetTester tester) async {
            const String data = '**foo "*bar*" foo**';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            // Expect text to be unchanged from original data string.
            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, 'foo "bar" foo');

            // There should be three spans of text.
            final TextSpan textSpan = textWidget.textSpan! as TextSpan;
            expect(textSpan, isNotNull);
            expect(textSpan.children!.length == 3, isTrue);

            // First text span has bold weight.
            final InlineSpan firstSpan = textSpan.children![0];
            expectTextSpanStyle(
              firstSpan as TextSpan,
              null,
              FontWeight.bold,
            );

            // Second span has both italic style with bold weight.
            final InlineSpan secondSpan = textSpan.children![1];
            expectTextSpanStyle(
              secondSpan as TextSpan,
              FontStyle.italic,
              FontWeight.bold,
            );

            // Third text span has bold weight.
            final InlineSpan thirdSpan = textSpan.children![2];
            expectTextSpanStyle(
              thirdSpan as TextSpan,
              null,
              FontWeight.bold,
            );
          },
        );

        testWidgets(
          // Example 405 from GFM.
          'intraword strong emphasis',
          (WidgetTester tester) async {
            const String data = '**foo**bar';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            // Expect text to be unchanged from original data string.
            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, 'foobar');

            // There should be two spans of text.
            final TextSpan textSpan = textWidget.textSpan! as TextSpan;
            expect(textSpan, isNotNull);
            expect(textSpan.children!.length == 2, isTrue);

            // First text span is normal text with strong emphasis.
            final InlineSpan firstSpan = textSpan.children![0];
            expectTextSpanStyle(
              firstSpan as TextSpan,
              null,
              FontWeight.bold,
            );

            // Second span is normal text with no emphasis.
            final InlineSpan secondSpan = textSpan.children![1];
            expectTextSpanStyle(
              secondSpan as TextSpan,
              null,
              FontWeight.normal,
            );
          },
        );
      });

      group('Rule 8', () {
        testWidgets(
          // Example 406 from GFM.
          'invalid strong emphasis - closing delimiter is preceded by whitespace',
          (WidgetTester tester) async {
            const String data = '__foo bar __';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            // Expect text to be unchanged from original data string.
            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, data);

            expectTextSpanStyle(
              textWidget.textSpan! as TextSpan,
              null,
              FontWeight.normal,
            );
          },
        );

        testWidgets(
          // Example 407 from GFM.
          'invalid strong emphasis - second __ is preceded by punctuation followed by alphanumeric',
          (WidgetTester tester) async {
            const String data = '__(__foo)';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            // Expect text to be unchanged from original data string.
            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, data);

            expectTextSpanStyle(
              textWidget.textSpan! as TextSpan,
              null,
              FontWeight.normal,
            );
          },
        );

        testWidgets(
          // Example 408 from GFM.
          'strong emphasis nested in emphasis',
          (WidgetTester tester) async {
            const String data = '_(__foo__)_';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, '(foo)');

            // There should be three spans of text.
            final TextSpan textSpan = textWidget.textSpan! as TextSpan;
            expect(textSpan, isNotNull);
            expect(textSpan.children!.length == 3, isTrue);

            // First text span has italic style with normal weight.
            final InlineSpan firstSpan = textSpan.children![0];
            expectTextSpanStyle(
              firstSpan as TextSpan,
              FontStyle.italic,
              FontWeight.normal,
            );

            // Second span has italic style with bold weight.
            final InlineSpan secondSpan = textSpan.children![1];
            expectTextSpanStyle(
              secondSpan as TextSpan,
              FontStyle.italic,
              FontWeight.bold,
            );

            // Third text span has italic style with normal weight.
            final InlineSpan thirdSpan = textSpan.children![2];
            expectTextSpanStyle(
              thirdSpan as TextSpan,
              FontStyle.italic,
              FontWeight.normal,
            );
          },
        );

        testWidgets(
          // Example 409 from GFM.
          'intraword strong emphasis is forbidden with __ - alpha characters',
          (WidgetTester tester) async {
            const String data = '__foo__bar';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            // Expect text to be unchanged from original data string.
            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, data);

            expectTextSpanStyle(
              textWidget.textSpan! as TextSpan,
              null,
              FontWeight.normal,
            );
          },
        );

        testWidgets(
          // Example 410 from GFM.
          'intraword strong emphasis is forbidden with __ - unicode characters',
          (WidgetTester tester) async {
            const String data = '__пристаням__стремятся';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            // Expect text to be unchanged from original data string.
            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, data);

            expectTextSpanStyle(
              textWidget.textSpan! as TextSpan,
              null,
              FontWeight.normal,
            );
          },
        );

        testWidgets(
          // Example 411 from GFM.
          'intraword nested strong emphasis is forbidden with __',
          (WidgetTester tester) async {
            const String data = '__foo__bar__baz__';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, 'foo__bar__baz');

            expectTextSpanStyle(
              textWidget.textSpan! as TextSpan,
              null,
              FontWeight.bold,
            );
          },
        );

        testWidgets(
          // Example 412 from GFM.
          'strong emphasis because closing delimiter is both left- and right-flanking is followed by punctuation',
          (WidgetTester tester) async {
            const String data = '__(bar)__.';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, '(bar).');

            // There should be two spans of text.
            final TextSpan textSpan = textWidget.textSpan! as TextSpan;
            expect(textSpan, isNotNull);
            expect(textSpan.children!.length == 2, isTrue);

            // First text span is normal text with strong emphasis.
            final InlineSpan firstSpan = textSpan.children![0];
            expectTextSpanStyle(
              firstSpan as TextSpan,
              null,
              FontWeight.bold,
            );

            // Second span has italic style with normal weight.
            final InlineSpan secondSpan = textSpan.children![1];
            expectTextSpanStyle(
              secondSpan as TextSpan,
              null,
              FontWeight.normal,
            );
          },
        );
      });

      group('Rule 9', () {
        testWidgets(
          // Example 413 from GFM.
          'nonempty sequence emphasis span - text followed by link',
          (WidgetTester tester) async {
            const String data = '*foo [bar](/url)*';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, 'foo bar');

            // There should be two spans of text.
            final TextSpan textSpan = textWidget.textSpan! as TextSpan;
            expect(textSpan, isNotNull);
            expect(textSpan.children!.length == 2, isTrue);

            // First text span is plain text and has italic style with normal weight.
            final TextSpan firstSpan = textSpan.children![0] as TextSpan;
            expect(firstSpan.recognizer, isNull);
            expectTextSpanStyle(
              firstSpan,
              FontStyle.italic,
              FontWeight.normal,
            );

            // Second span has italic style with normal weight.
            final TextSpan secondSpan = textSpan.children![1] as TextSpan;
            expect(secondSpan.recognizer, isNotNull);
            expect(secondSpan.recognizer is GestureRecognizer, isTrue);
            expectTextSpanStyle(
              secondSpan,
              FontStyle.italic,
              FontWeight.normal,
            );
          },
        );

        testWidgets(
          // Example 414 from GFM.
          'nonempty sequence emphasis span - two lines of text',
          (WidgetTester tester) async {
            const String data = '*foo\nbar*';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, 'foo bar');

            expectTextSpanStyle(
              textWidget.textSpan! as TextSpan,
              FontStyle.italic,
              FontWeight.normal,
            );
          },
        );

        testWidgets(
          // Example 415 from GFM.
          'strong emphasis nested inside emphasis - _ delimiter',
          (WidgetTester tester) async {
            const String data = '_foo __bar__ baz_';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, 'foo bar baz');

            // There should be three spans of text.
            final TextSpan textSpan = textWidget.textSpan! as TextSpan;
            expect(textSpan, isNotNull);
            expect(textSpan.children!.length == 3, isTrue);

            // First text span has italic style with normal weight.
            final InlineSpan firstSpan = textSpan.children![0];
            expectTextSpanStyle(
              firstSpan as TextSpan,
              FontStyle.italic,
              FontWeight.normal,
            );

            // Second span has italic style with bold weight.
            final InlineSpan secondSpan = textSpan.children![1];
            expectTextSpanStyle(
              secondSpan as TextSpan,
              FontStyle.italic,
              FontWeight.bold,
            );

            // Third text span has italic style with normal weight.
            final InlineSpan thirdSpan = textSpan.children![2];
            expectTextSpanStyle(
              thirdSpan as TextSpan,
              FontStyle.italic,
              FontWeight.normal,
            );
          },
        );

        testWidgets(
          // Example 416 from GFM.
          'emphasis nested inside emphasis',
          (WidgetTester tester) async {
            const String data = '_foo _bar_ baz_';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, 'foo bar baz');

            expectTextSpanStyle(
              textWidget.textSpan! as TextSpan,
              FontStyle.italic,
              FontWeight.normal,
            );
          },
        );

        testWidgets(
          // Example 417 from GFM.
          'intraword emphasis nested inside emphasis - _ delimiter',
          (WidgetTester tester) async {
            const String data = '__foo_ bar_';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, 'foo bar');

            expectTextSpanStyle(
              textWidget.textSpan! as TextSpan,
              FontStyle.italic,
              FontWeight.normal,
            );
          },
        );

        testWidgets(
          // Example 418 from GFM.
          'intraword emphasis nested inside emphasis - * delimiter',
          (WidgetTester tester) async {
            const String data = '*foo *bar**';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, 'foo bar');

            expectTextSpanStyle(
              textWidget.textSpan! as TextSpan,
              FontStyle.italic,
              FontWeight.normal,
            );
          },
        );

        testWidgets(
          // Example 419 from GFM.
          'strong emphasis nested inside emphasis - * delimiter',
          (WidgetTester tester) async {
            const String data = '*foo **bar** baz*';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, 'foo bar baz');

            // There should be three spans of text.
            final TextSpan textSpan = textWidget.textSpan! as TextSpan;
            expect(textSpan, isNotNull);
            expect(textSpan.children!.length == 3, isTrue);

            // First text span has italic style with normal weight.
            final InlineSpan firstSpan = textSpan.children![0];
            expectTextSpanStyle(
              firstSpan as TextSpan,
              FontStyle.italic,
              FontWeight.normal,
            );

            // Second span has italic style with bold weight.
            final InlineSpan secondSpan = textSpan.children![1];
            expectTextSpanStyle(
              secondSpan as TextSpan,
              FontStyle.italic,
              FontWeight.bold,
            );

            // Third text span has italic style with normal weight.
            final InlineSpan thirdSpan = textSpan.children![2];
            expectTextSpanStyle(
              thirdSpan as TextSpan,
              FontStyle.italic,
              FontWeight.normal,
            );
          },
        );

        testWidgets(
          // Example 418 from GFM.
          'intraword strong emphasis nested inside emphasis - * delimiter',
          (WidgetTester tester) async {
            const String data = '*foo**bar**baz*';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, 'foobarbaz');

            // There should be three spans of text.
            final TextSpan textSpan = textWidget.textSpan! as TextSpan;
            expect(textSpan, isNotNull);
            expect(textSpan.children!.length == 3, isTrue);

            // First text span has italic style with normal weight.
            final InlineSpan firstSpan = textSpan.children![0];
            expectTextSpanStyle(
              firstSpan as TextSpan,
              FontStyle.italic,
              FontWeight.normal,
            );

            // Second span has italic style with bold weight.
            final InlineSpan secondSpan = textSpan.children![1];
            expectTextSpanStyle(
              secondSpan as TextSpan,
              FontStyle.italic,
              FontWeight.bold,
            );

            // Third text span has italic style with normal weight.
            final InlineSpan thirdSpan = textSpan.children![2];
            expectTextSpanStyle(
              thirdSpan as TextSpan,
              FontStyle.italic,
              FontWeight.normal,
            );
          },
        );

        testWidgets(
          // Example 421 from GFM.
          'consecutive emphasis sections are not allowed',
          (WidgetTester tester) async {
            const String data = '*foo**bar*';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, 'foo**bar');

            expectTextSpanStyle(
              textWidget.textSpan! as TextSpan,
              FontStyle.italic,
              FontWeight.normal,
            );
          },
        );

        testWidgets(
          // Example 422 from GFM.
          'strong emphasis nested inside emphasis - space after first word',
          (WidgetTester tester) async {
            const String data = '***foo** bar*';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, 'foo bar');

            // There should be two spans of text.
            final TextSpan textSpan = textWidget.textSpan! as TextSpan;
            expect(textSpan, isNotNull);
            expect(textSpan.children!.length == 2, isTrue);

            // First text span has italic style with bold weight.
            final InlineSpan firstSpan = textSpan.children![0];
            expectTextSpanStyle(
              firstSpan as TextSpan,
              FontStyle.italic,
              FontWeight.bold,
            );

            // Second span has italic style with normal weight.
            final InlineSpan secondSpan = textSpan.children![1];
            expectTextSpanStyle(
              secondSpan as TextSpan,
              FontStyle.italic,
              FontWeight.normal,
            );
          },
        );

        testWidgets(
          // Example 423 from GFM.
          'strong emphasis nested inside emphasis - space before second word',
          (WidgetTester tester) async {
            const String data = '*foo **bar***';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, 'foo bar');

            // There should be two spans of text.
            final TextSpan textSpan = textWidget.textSpan! as TextSpan;
            expect(textSpan, isNotNull);
            expect(textSpan.children!.length == 2, isTrue);

            // First text span has italic style with normal weight.
            final InlineSpan firstSpan = textSpan.children![0];
            expectTextSpanStyle(
              firstSpan as TextSpan,
              FontStyle.italic,
              FontWeight.normal,
            );

            // Second span has italic style with bold weight.
            final InlineSpan secondSpan = textSpan.children![1];
            expectTextSpanStyle(
              secondSpan as TextSpan,
              FontStyle.italic,
              FontWeight.bold,
            );
          },
        );

        testWidgets(
          // Example 424 from GFM.
          'intraword strong emphasis nested inside emphasis',
          (WidgetTester tester) async {
            const String data = '*foo**bar***';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, 'foobar');

            // There should be two spans of text.
            final TextSpan textSpan = textWidget.textSpan! as TextSpan;
            expect(textSpan, isNotNull);
            expect(textSpan.children!.length == 2, isTrue);

            // First text span has italic style with normal weight.
            final InlineSpan firstSpan = textSpan.children![0];
            expectTextSpanStyle(
              firstSpan as TextSpan,
              FontStyle.italic,
              FontWeight.normal,
            );

            // Second span has italic style with bold weight.
            final InlineSpan secondSpan = textSpan.children![1];
            expectTextSpanStyle(
              secondSpan as TextSpan,
              FontStyle.italic,
              FontWeight.bold,
            );
          },
        );

        testWidgets(
          // Example 425 from GFM.
          'intraword emphasis and strong emphasis',
          (WidgetTester tester) async {
            const String data = 'foo***bar***baz';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, 'foobarbaz');

            // There should be three spans of text.
            final TextSpan textSpan = textWidget.textSpan! as TextSpan;
            expect(textSpan, isNotNull);
            expect(textSpan.children!.length == 3, isTrue);

            // First text span is plain text with normal weight.
            final InlineSpan firstSpan = textSpan.children![0];
            expectTextSpanStyle(
              firstSpan as TextSpan,
              null,
              FontWeight.normal,
            );

            // Second span has italic style with bold weight.
            final InlineSpan secondSpan = textSpan.children![1];
            expectTextSpanStyle(
              secondSpan as TextSpan,
              FontStyle.italic,
              FontWeight.bold,
            );

            // Third text span is plain text with normal weight.
            final InlineSpan thirdSpan = textSpan.children![2];
            expectTextSpanStyle(
              thirdSpan as TextSpan,
              null,
              FontWeight.normal,
            );
          },
        );

        testWidgets(
          // Example 426 from GFM.
          'intraword emphasis and strong emphasis - multiples of 3',
          (WidgetTester tester) async {
            const String data = 'foo******bar*********baz';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, 'foobar***baz');

            // There should be three spans of text.
            final TextSpan textSpan = textWidget.textSpan! as TextSpan;
            expect(textSpan, isNotNull);
            expect(textSpan.children!.length == 3, isTrue);

            // First text span is plain text with normal weight.
            final InlineSpan firstSpan = textSpan.children![0];
            expectTextSpanStyle(
              firstSpan as TextSpan,
              null,
              FontWeight.normal,
            );

            // Second span is plain text with bold weight.
            final InlineSpan secondSpan = textSpan.children![1];
            expectTextSpanStyle(
              secondSpan as TextSpan,
              null,
              FontWeight.bold,
            );

            // Third text span is plain text with normal weight.
            final InlineSpan thirdSpan = textSpan.children![2];
            expectTextSpanStyle(
              thirdSpan as TextSpan,
              null,
              FontWeight.normal,
            );
          },
        );

        testWidgets(
          // Example 427 from GFM.
          'infinite levels of nesting are possible within emphasis',
          (WidgetTester tester) async {
            const String data = '*foo **bar *baz*\nbim** bop*';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, 'foo bar baz bim bop');

            // There should be five spans of text.
            final TextSpan textSpan = textWidget.textSpan! as TextSpan;
            expect(textSpan, isNotNull);
            expect(textSpan.children!.length, 3);

            // First text span has italic style and normal weight.
            final InlineSpan firstSpan = textSpan.children![0];
            expectTextSpanStyle(
              firstSpan as TextSpan,
              FontStyle.italic,
              FontWeight.normal,
            );

            // Second span has both italic style with bold weight.
            final InlineSpan secondSpan = textSpan.children![1];
            expectTextSpanStyle(
              secondSpan as TextSpan,
              FontStyle.italic,
              FontWeight.bold,
            );

            // Third text span has bold weight.
            final InlineSpan thirdSpan = textSpan.children![2];
            expectTextSpanStyle(
              thirdSpan as TextSpan,
              FontStyle.italic,
              FontWeight.normal,
            );
          },
        );

        testWidgets(
          // Example 428 from GFM.
          'infinite levels of nesting are possible within emphasis - text and a link',
          (WidgetTester tester) async {
            const String data = '*foo [*bar*](/url)*';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, 'foo bar');

            // There should be two spans of text.
            final TextSpan textSpan = textWidget.textSpan! as TextSpan;
            expect(textSpan, isNotNull);
            expect(textSpan.children!.length == 2, isTrue);

            // First text span has italic style and normal weight.
            final TextSpan firstSpan = textSpan.children![0] as TextSpan;
            expect(firstSpan.recognizer, isNull);
            expectTextSpanStyle(
              firstSpan,
              FontStyle.italic,
              FontWeight.normal,
            );

            // Second span has italic style with normal weight.
            final TextSpan secondSpan = textSpan.children![1] as TextSpan;
            expect(secondSpan.recognizer, isNotNull);
            expect(secondSpan.recognizer is GestureRecognizer, isTrue);
            expectTextSpanStyle(
              secondSpan,
              FontStyle.italic,
              FontWeight.normal,
            );
          },
        );

        testWidgets(
          // Example 429 from GFM.
          'there can be no empty emphasis * delimiter',
          (WidgetTester tester) async {
            const String data = '** is not an empty emphasis';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            // Expect text to be unchanged from original data string.
            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, data);

            expectTextSpanStyle(
              textWidget.textSpan! as TextSpan,
              null,
              FontWeight.normal,
            );
          },
        );

        testWidgets(
          // Example 430 from GFM.
          'there can be no empty strong emphasis * delimiter',
          (WidgetTester tester) async {
            const String data = '**** is not an empty strong emphasis';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            // Expect text to be unchanged from original data string.
            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, data);

            expectTextSpanStyle(
              textWidget.textSpan! as TextSpan,
              null,
              FontWeight.normal,
            );
          },
        );
      });

      group('Rule 10', () {
        testWidgets(
          // Example 431 from GFM.
          'nonempty sequence of inline elements with strong emphasis - text and a link',
          (WidgetTester tester) async {
            const String data = '**foo [bar](/url)**';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, 'foo bar');

            // There should be two spans of text.
            final TextSpan textSpan = textWidget.textSpan! as TextSpan;
            expect(textSpan, isNotNull);
            expect(textSpan.children!.length == 2, isTrue);

            // First text span is normal text with bold weight.
            final TextSpan firstSpan = textSpan.children![0] as TextSpan;
            expect(firstSpan.recognizer, isNull);
            expectTextSpanStyle(
              firstSpan,
              null,
              FontWeight.bold,
            );

            // Second span is a link with bold weight.
            final TextSpan secondSpan = textSpan.children![1] as TextSpan;
            expect(secondSpan.recognizer, isNotNull);
            expect(secondSpan.recognizer is GestureRecognizer, isTrue);
            expectTextSpanStyle(
              secondSpan,
              null,
              FontWeight.bold,
            );
          },
        );

        testWidgets(
          // Example 432 from GFM.
          'nonempty sequence of inline elements with strong emphasis - two lines of texts',
          (WidgetTester tester) async {
            const String data = '**foo\nbar**';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, 'foo bar');

            expectTextSpanStyle(
              textWidget.textSpan! as TextSpan,
              null,
              FontWeight.bold,
            );
          },
        );

        testWidgets(
          // Example 433 from GFM.
          'emphasis and strong emphasis nested inside strong emphasis - nested emphasis',
          (WidgetTester tester) async {
            const String data = '__foo _bar_ baz__';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, 'foo bar baz');

            // There should be three spans of text.
            final TextSpan textSpan = textWidget.textSpan! as TextSpan;
            expect(textSpan, isNotNull);
            expect(textSpan.children!.length == 3, isTrue);

            // First text span is plain text with bold weight.
            final InlineSpan firstSpan = textSpan.children![0];
            expectTextSpanStyle(
              firstSpan as TextSpan,
              null,
              FontWeight.bold,
            );

            // Second span has italic style with bold weight.
            final InlineSpan secondSpan = textSpan.children![1];
            expectTextSpanStyle(
              secondSpan as TextSpan,
              FontStyle.italic,
              FontWeight.bold,
            );

            // Third text span is plain text with bold weight.
            final InlineSpan thirdSpan = textSpan.children![2];
            expectTextSpanStyle(
              thirdSpan as TextSpan,
              null,
              FontWeight.bold,
            );
          },
        );

        testWidgets(
          // Example 434 from GFM.
          'emphasis and strong emphasis nested inside strong emphasis - nested strong emphasis',
          (WidgetTester tester) async {
            const String data = '__foo __bar__ baz__';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, 'foo bar baz');

            expectTextSpanStyle(
              textWidget.textSpan! as TextSpan,
              null,
              FontWeight.bold,
            );
          },
        );

        testWidgets(
          // Example 435 from GFM.
          'emphasis and strong emphasis nested inside strong emphasis - nested strong emphasis',
          (WidgetTester tester) async {
            const String data = '____foo__ bar__';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, 'foo bar');

            expectTextSpanStyle(
              textWidget.textSpan! as TextSpan,
              null,
              FontWeight.bold,
            );
          },
        );

        testWidgets(
          // Example 436 from GFM.
          'emphasis and strong emphasis nested inside strong emphasis - nested strong emphasis',
          (WidgetTester tester) async {
            const String data = '**foo **bar****';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, 'foo bar');

            expectTextSpanStyle(
              textWidget.textSpan! as TextSpan,
              null,
              FontWeight.bold,
            );
          },
        );

        testWidgets(
          // Example 437 from GFM.
          'emphasis and strong emphasis nested inside strong emphasis - nested emphasis',
          (WidgetTester tester) async {
            const String data = '**foo *bar* baz**';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, 'foo bar baz');

            // There should be three spans of text.
            final TextSpan textSpan = textWidget.textSpan! as TextSpan;
            expect(textSpan, isNotNull);
            expect(textSpan.children!.length == 3, isTrue);

            // First text span is plain text with bold weight.
            final InlineSpan firstSpan = textSpan.children![0];
            expectTextSpanStyle(
              firstSpan as TextSpan,
              null,
              FontWeight.bold,
            );

            // Second span has italic style with bold weight.
            final InlineSpan secondSpan = textSpan.children![1];
            expectTextSpanStyle(
              secondSpan as TextSpan,
              FontStyle.italic,
              FontWeight.bold,
            );

            // Third text span is plain text with bold weight.
            final InlineSpan thirdSpan = textSpan.children![2];
            expectTextSpanStyle(
              thirdSpan as TextSpan,
              null,
              FontWeight.bold,
            );
          },
        );

        testWidgets(
          // Example 438 from GFM.
          'emphasis and strong emphasis nested inside strong emphasis - intraword nested emphasis',
          (WidgetTester tester) async {
            const String data = '**foo*bar*baz**';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, 'foobarbaz');

            // There should be three spans of text.
            final TextSpan textSpan = textWidget.textSpan! as TextSpan;
            expect(textSpan, isNotNull);
            expect(textSpan.children!.length == 3, isTrue);

            // First text span is plain text with bold weight.
            final InlineSpan firstSpan = textSpan.children![0];
            expectTextSpanStyle(
              firstSpan as TextSpan,
              null,
              FontWeight.bold,
            );

            // Second span has italic style with bold weight.
            final InlineSpan secondSpan = textSpan.children![1];
            expectTextSpanStyle(
              secondSpan as TextSpan,
              FontStyle.italic,
              FontWeight.bold,
            );

            // Third text span is plain text with bold weight.
            final InlineSpan thirdSpan = textSpan.children![2];
            expectTextSpanStyle(
              thirdSpan as TextSpan,
              null,
              FontWeight.bold,
            );
          },
        );

        testWidgets(
          // Example 439 from GFM.
          'emphasis and strong emphasis nested inside strong emphasis - nested emphasis on first word',
          (WidgetTester tester) async {
            const String data = '***foo* bar**';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, 'foo bar');

            // There should be two spans of text.
            final TextSpan textSpan = textWidget.textSpan! as TextSpan;
            expect(textSpan, isNotNull);
            expect(textSpan.children!.length == 2, isTrue);

            // First text span has italic style with bold weight.
            final InlineSpan firstSpan = textSpan.children![0];
            expectTextSpanStyle(
              firstSpan as TextSpan,
              FontStyle.italic,
              FontWeight.bold,
            );

            // Second span is plain text with bold weight.
            final InlineSpan secondSpan = textSpan.children![1];
            expectTextSpanStyle(
              secondSpan as TextSpan,
              null,
              FontWeight.bold,
            );
          },
        );

        testWidgets(
          // Example 440 from GFM.
          'emphasis and strong emphasis nested inside strong emphasis - nested emphasis on second word',
          (WidgetTester tester) async {
            const String data = '**foo *bar***';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, 'foo bar');

            // There should be two spans of text.
            final TextSpan textSpan = textWidget.textSpan! as TextSpan;
            expect(textSpan, isNotNull);
            expect(textSpan.children!.length == 2, isTrue);

            // First text span is plain text with bold weight.
            final InlineSpan firstSpan = textSpan.children![0];
            expectTextSpanStyle(
              firstSpan as TextSpan,
              null,
              FontWeight.bold,
            );

            // Second span has italic style with bold weight.
            final InlineSpan secondSpan = textSpan.children![1];
            expectTextSpanStyle(
              secondSpan as TextSpan,
              FontStyle.italic,
              FontWeight.bold,
            );
          },
        );

        testWidgets(
          // Example 441 from GFM.
          'infinite levels of nesting are possible within strong emphasis',
          (WidgetTester tester) async {
            const String data = '**foo *bar **baz**\nbim* bop**';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, 'foo bar baz bim bop');

            // There should be five spans of text.
            final TextSpan textSpan = textWidget.textSpan! as TextSpan;
            expect(textSpan, isNotNull);
            expect(textSpan.children!.length, 3);

            // First text span is plain text with bold weight.
            final InlineSpan firstSpan = textSpan.children![0];
            expectTextSpanStyle(
              firstSpan as TextSpan,
              null,
              FontWeight.bold,
            );

            // Second span has both italic style with bold weight.
            final InlineSpan secondSpan = textSpan.children![1];
            expectTextSpanStyle(
              secondSpan as TextSpan,
              FontStyle.italic,
              FontWeight.bold,
            );

            // Third text span has both italic style with bold weight.
            final InlineSpan thirdSpan = textSpan.children![2];
            expectTextSpanStyle(
              thirdSpan as TextSpan,
              null,
              FontWeight.bold,
            );
          },
        );

        testWidgets(
          // Example 442 from GFM.
          'infinite levels of nesting are possible within strong emphasis - text and a link',
          (WidgetTester tester) async {
            const String data = '**foo [*bar*](/url)**';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, 'foo bar');

            // There should be two spans of text.
            final TextSpan textSpan = textWidget.textSpan! as TextSpan;
            expect(textSpan, isNotNull);
            expect(textSpan.children!.length == 2, isTrue);

            // First text span is plain text and bold weight.
            final TextSpan firstSpan = textSpan.children![0] as TextSpan;
            expect(firstSpan.recognizer, isNull);
            expectTextSpanStyle(
              firstSpan,
              null,
              FontWeight.bold,
            );

            // Second span has both italic style with normal weight.
            final TextSpan secondSpan = textSpan.children![1] as TextSpan;
            expect(secondSpan.recognizer, isNotNull);
            expect(secondSpan.recognizer is GestureRecognizer, isTrue);
            expectTextSpanStyle(
              secondSpan,
              FontStyle.italic,
              FontWeight.bold,
            );
          },
        );

        testWidgets(
          // Example 443 from GFM.
          'there can be no empty emphasis _ delimiter',
          (WidgetTester tester) async {
            const String data = '__ is not an empty emphasis';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            // Expect text to be unchanged from original data string.
            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, data);

            expectTextSpanStyle(
              textWidget.textSpan! as TextSpan,
              null,
              FontWeight.normal,
            );
          },
        );

        testWidgets(
          // Example 444 from GFM.
          'there can be no empty strong emphasis _ delimiter',
          (WidgetTester tester) async {
            const String data = '____ is not an empty strong emphasis';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            // Expect text to be unchanged from original data string.
            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, data);

            expectTextSpanStyle(
              textWidget.textSpan! as TextSpan,
              null,
              FontWeight.normal,
            );
          },
        );
      });

      group('Rule 11', () {
        testWidgets(
          // Example 445 from GFM.
          'an * cannot occur at the beginning or end of * delimited emphasis',
          (WidgetTester tester) async {
            const String data = 'foo ***';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            // Expect text to be unchanged from original data string.
            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, data);

            expectTextSpanStyle(
              textWidget.textSpan! as TextSpan,
              null,
              FontWeight.normal,
            );
          },
        );

        testWidgets(
          // Example 446 from GFM.
          'an escaped * can occur inside * delimited emphasis',
          (WidgetTester tester) async {
            const String data = r'foo *\**';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            // Expect text to be unchanged from original data string.
            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, 'foo *');

            // There should be two spans of text.
            final TextSpan textSpan = textWidget.textSpan! as TextSpan;
            expect(textSpan, isNotNull);
            expect(textSpan.children!.length == 2, isTrue);

            // First text span is normal text with normal weight.
            final InlineSpan firstSpan = textSpan.children![0];
            expectTextSpanStyle(
              firstSpan as TextSpan,
              null,
              FontWeight.normal,
            );

            // Second span has italic style with normal weight.
            final InlineSpan secondSpan = textSpan.children![1];
            expectTextSpanStyle(
              secondSpan as TextSpan,
              FontStyle.italic,
              FontWeight.normal,
            );
          },
        );

        testWidgets(
          // Example 446 from GFM.
          'an _ can occur inside * delimited emphasis',
          (WidgetTester tester) async {
            const String data = 'foo *_*';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            // Expect text to be unchanged from original data string.
            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, 'foo _');

            // There should be two spans of text.
            final TextSpan textSpan = textWidget.textSpan! as TextSpan;
            expect(textSpan, isNotNull);
            expect(textSpan.children!.length == 2, isTrue);

            // First text span is normal text with normal weight.
            final InlineSpan firstSpan = textSpan.children![0];
            expectTextSpanStyle(
              firstSpan as TextSpan,
              null,
              FontWeight.normal,
            );

            // Second span has italic style with normal weight.
            final InlineSpan secondSpan = textSpan.children![1];
            expectTextSpanStyle(
              secondSpan as TextSpan,
              FontStyle.italic,
              FontWeight.normal,
            );
          },
        );

        testWidgets(
          // Example 448 from GFM.
          'an * cannot occur at the beginning or end of ** delimited strong emphasis',
          (WidgetTester tester) async {
            const String data = 'foo *****';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            // Expect text to be unchanged from original data string.
            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, data);

            expectTextSpanStyle(
              textWidget.textSpan! as TextSpan,
              null,
              FontWeight.normal,
            );
          },
        );

        testWidgets(
          // Example 449 from GFM.
          'an escaped * can occur inside ** delimited strong emphasis',
          (WidgetTester tester) async {
            const String data = r'foo **\***';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            // Expect text to be unchanged from original data string.
            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, 'foo *');

            // There should be two spans of text.
            final TextSpan textSpan = textWidget.textSpan! as TextSpan;
            expect(textSpan, isNotNull);
            expect(textSpan.children!.length == 2, isTrue);

            // First text span is normal text with normal weight.
            final InlineSpan firstSpan = textSpan.children![0];
            expectTextSpanStyle(
              firstSpan as TextSpan,
              null,
              FontWeight.normal,
            );

            // Second span is normal text with bold weight.
            final InlineSpan secondSpan = textSpan.children![1];
            expectTextSpanStyle(
              secondSpan as TextSpan,
              null,
              FontWeight.bold,
            );
          },
        );

        testWidgets(
          // Example 450 from GFM.
          'an _ can occur inside ** delimited strong emphasis',
          (WidgetTester tester) async {
            const String data = 'foo **_**';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            // Expect text to be unchanged from original data string.
            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, 'foo _');

            // There should be two spans of text.
            final TextSpan textSpan = textWidget.textSpan! as TextSpan;
            expect(textSpan, isNotNull);
            expect(textSpan.children!.length == 2, isTrue);

            // First text span is normal text with normal weight.
            final InlineSpan firstSpan = textSpan.children![0];
            expectTextSpanStyle(
              firstSpan as TextSpan,
              null,
              FontWeight.normal,
            );

            // Second span is normal text with bold weight.
            final InlineSpan secondSpan = textSpan.children![1];
            expectTextSpanStyle(
              secondSpan as TextSpan,
              null,
              FontWeight.bold,
            );
          },
        );

        testWidgets(
          // Example 451 from GFM.
          'unmatched emphasis delimiters excess * at beginning',
          (WidgetTester tester) async {
            const String data = '**foo*';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            // Expect text to be unchanged from original data string.
            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, '*foo');

            // There should be two spans of text.
            final TextSpan textSpan = textWidget.textSpan! as TextSpan;
            expect(textSpan, isNotNull);
            expect(textSpan.children!.length == 2, isTrue);

            // First text span is normal text with normal weight.
            final InlineSpan firstSpan = textSpan.children![0];
            expectTextSpanStyle(
              firstSpan as TextSpan,
              null,
              FontWeight.normal,
            );

            // Second span has italic style with normal weight.
            final InlineSpan secondSpan = textSpan.children![1];
            expectTextSpanStyle(
              secondSpan as TextSpan,
              FontStyle.italic,
              FontWeight.normal,
            );
          },
        );

        testWidgets(
          // Example 452 from GFM.
          'unmatched emphasis delimiters excess * at end',
          (WidgetTester tester) async {
            const String data = '*foo**';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            // Expect text to be unchanged from original data string.
            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, 'foo*');

            // There should be two spans of text.
            final TextSpan textSpan = textWidget.textSpan! as TextSpan;
            expect(textSpan, isNotNull);
            expect(textSpan.children!.length == 2, isTrue);

            // First text span has italic style with normal weight.
            final InlineSpan firstSpan = textSpan.children![0];
            expectTextSpanStyle(
              firstSpan as TextSpan,
              FontStyle.italic,
              FontWeight.normal,
            );

            // Second span is normal text with normal weight.
            final InlineSpan secondSpan = textSpan.children![1];
            expectTextSpanStyle(
              secondSpan as TextSpan,
              null,
              FontWeight.normal,
            );
          },
        );

        testWidgets(
          // Example 453 from GFM.
          'unmatched strong emphasis delimiters excess * at beginning',
          (WidgetTester tester) async {
            const String data = '***foo**';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            // Expect text to be unchanged from original data string.
            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, '*foo');

            // There should be two spans of text.
            final TextSpan textSpan = textWidget.textSpan! as TextSpan;
            expect(textSpan, isNotNull);
            expect(textSpan.children!.length == 2, isTrue);

            // First text span is normal text with normal weight.
            final InlineSpan firstSpan = textSpan.children![0];
            expectTextSpanStyle(
              firstSpan as TextSpan,
              null,
              FontWeight.normal,
            );

            // Second span is normal text with bold weight.
            final InlineSpan secondSpan = textSpan.children![1];
            expectTextSpanStyle(
              secondSpan as TextSpan,
              null,
              FontWeight.bold,
            );
          },
        );

        testWidgets(
          // Example 454 from GFM.
          'unmatched strong emphasis delimiters excess * at beginning',
          (WidgetTester tester) async {
            const String data = '****foo*';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            // Expect text to be unchanged from original data string.
            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, '***foo');

            // There should be two spans of text.
            final TextSpan textSpan = textWidget.textSpan! as TextSpan;
            expect(textSpan, isNotNull);
            expect(textSpan.children!.length == 2, isTrue);

            // First text span is normal text with normal weight.
            final InlineSpan firstSpan = textSpan.children![0];
            expectTextSpanStyle(
              firstSpan as TextSpan,
              null,
              FontWeight.normal,
            );

            // Second span has italic style with normal weight.
            final InlineSpan secondSpan = textSpan.children![1];
            expectTextSpanStyle(
              secondSpan as TextSpan,
              FontStyle.italic,
              FontWeight.normal,
            );
          },
        );

        testWidgets(
          // Example 455 from GFM.
          'unmatched strong emphasis delimiters excess * at end',
          (WidgetTester tester) async {
            const String data = '**foo***';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            // Expect text to be unchanged from original data string.
            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, 'foo*');

            // There should be two spans of text.
            final TextSpan textSpan = textWidget.textSpan! as TextSpan;
            expect(textSpan, isNotNull);
            expect(textSpan.children!.length == 2, isTrue);

            // First text span is normal text with bold weight.
            final InlineSpan firstSpan = textSpan.children![0];
            expectTextSpanStyle(
              firstSpan as TextSpan,
              null,
              FontWeight.bold,
            );

            // Second span is plain text with normal weight.
            final InlineSpan secondSpan = textSpan.children![1];
            expectTextSpanStyle(
              secondSpan as TextSpan,
              null,
              FontWeight.normal,
            );
          },
        );

        testWidgets(
          // Example 456 from GFM.
          'unmatched strong emphasis delimiters excess * at end',
          (WidgetTester tester) async {
            const String data = '*foo****';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            // Expect text to be unchanged from original data string.
            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, 'foo***');

            // There should be two spans of text.
            final TextSpan textSpan = textWidget.textSpan! as TextSpan;
            expect(textSpan, isNotNull);
            expect(textSpan.children!.length == 2, isTrue);

            // First text span has italic style with normal weight.
            final InlineSpan firstSpan = textSpan.children![0];
            expectTextSpanStyle(
              firstSpan as TextSpan,
              FontStyle.italic,
              FontWeight.normal,
            );

            // Second span is plain text with normal weight.
            final InlineSpan secondSpan = textSpan.children![1];
            expectTextSpanStyle(
              secondSpan as TextSpan,
              null,
              FontWeight.normal,
            );
          },
        );
      });

      group('Rule 12', () {
        testWidgets(
          // Example 457 from GFM.
          'an _ cannot occur at the beginning or end of _ delimited emphasis',
          (WidgetTester tester) async {
            const String data = 'foo ___';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            // Expect text to be unchanged from original data string.
            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, data);

            expectTextSpanStyle(
              textWidget.textSpan! as TextSpan,
              null,
              FontWeight.normal,
            );
          },
        );

        testWidgets(
          // Example 458 from GFM.
          'an escaped _ can occur inside _ delimited emphasis',
          (WidgetTester tester) async {
            const String data = r'foo _\__';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            // Expect text to be unchanged from original data string.
            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, 'foo _');

            // There should be two spans of text.
            final TextSpan textSpan = textWidget.textSpan! as TextSpan;
            expect(textSpan, isNotNull);
            expect(textSpan.children!.length == 2, isTrue);

            // First text span is normal text with normal weight.
            final InlineSpan firstSpan = textSpan.children![0];
            expectTextSpanStyle(
              firstSpan as TextSpan,
              null,
              FontWeight.normal,
            );

            // Second span has italic style with normal weight.
            final InlineSpan secondSpan = textSpan.children![1];
            expectTextSpanStyle(
              secondSpan as TextSpan,
              FontStyle.italic,
              FontWeight.normal,
            );
          },
        );

        testWidgets(
          // Example 459 from GFM.
          'an * can occur inside _ delimited emphasis',
          (WidgetTester tester) async {
            const String data = 'foo _*_';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            // Expect text to be unchanged from original data string.
            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, 'foo *');

            // There should be two spans of text.
            final TextSpan textSpan = textWidget.textSpan! as TextSpan;
            expect(textSpan, isNotNull);
            expect(textSpan.children!.length == 2, isTrue);

            // First text span is normal text with normal weight.
            final InlineSpan firstSpan = textSpan.children![0];
            expectTextSpanStyle(
              firstSpan as TextSpan,
              null,
              FontWeight.normal,
            );

            // Second span has italic style with normal weight.
            final InlineSpan secondSpan = textSpan.children![1];
            expectTextSpanStyle(
              secondSpan as TextSpan,
              FontStyle.italic,
              FontWeight.normal,
            );
          },
        );

        testWidgets(
          // Example 460 from GFM.
          'an _ cannot occur at the beginning or end of __ delimited strong emphasis',
          (WidgetTester tester) async {
            const String data = 'foo _____';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            // Expect text to be unchanged from original data string.
            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, data);

            expectTextSpanStyle(
              textWidget.textSpan! as TextSpan,
              null,
              FontWeight.normal,
            );
          },
        );

        testWidgets(
          // Example 461 from GFM.
          'an escaped _ can occur inside __ delimited strong emphasis',
          (WidgetTester tester) async {
            const String data = r'foo __\___';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            // Expect text to be unchanged from original data string.
            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, 'foo _');

            // There should be two spans of text.
            final TextSpan textSpan = textWidget.textSpan! as TextSpan;
            expect(textSpan, isNotNull);
            expect(textSpan.children!.length == 2, isTrue);

            // First text span is normal text with normal weight.
            final InlineSpan firstSpan = textSpan.children![0];
            expectTextSpanStyle(
              firstSpan as TextSpan,
              null,
              FontWeight.normal,
            );

            // Second span is normal text with bold weight.
            final InlineSpan secondSpan = textSpan.children![1];
            expectTextSpanStyle(
              secondSpan as TextSpan,
              null,
              FontWeight.bold,
            );
          },
        );

        testWidgets(
          // Example 462 from GFM.
          'an * can occur inside __ delimited strong emphasis',
          (WidgetTester tester) async {
            const String data = 'foo __*__';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            // Expect text to be unchanged from original data string.
            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, 'foo *');

            // There should be two spans of text.
            final TextSpan textSpan = textWidget.textSpan! as TextSpan;
            expect(textSpan, isNotNull);
            expect(textSpan.children!.length == 2, isTrue);

            // First text span is normal text with normal weight.
            final InlineSpan firstSpan = textSpan.children![0];
            expectTextSpanStyle(
              firstSpan as TextSpan,
              null,
              FontWeight.normal,
            );

            // Second span is normal text with bold weight.
            final InlineSpan secondSpan = textSpan.children![1];
            expectTextSpanStyle(
              secondSpan as TextSpan,
              null,
              FontWeight.bold,
            );
          },
        );

        testWidgets(
          // Example 463 from GFM.
          'unmatched emphasis delimiters excess _ at beginning',
          (WidgetTester tester) async {
            const String data = '__foo_';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            // Expect text to be unchanged from original data string.
            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, '_foo');

            // There should be two spans of text.
            final TextSpan textSpan = textWidget.textSpan! as TextSpan;
            expect(textSpan, isNotNull);
            expect(textSpan.children!.length == 2, isTrue);

            // First text span is normal text with normal weight.
            final InlineSpan firstSpan = textSpan.children![0];
            expectTextSpanStyle(
              firstSpan as TextSpan,
              null,
              FontWeight.normal,
            );

            // Second span has italic style with normal weight.
            final InlineSpan secondSpan = textSpan.children![1];
            expectTextSpanStyle(
              secondSpan as TextSpan,
              FontStyle.italic,
              FontWeight.normal,
            );
          },
        );

        testWidgets(
          // Example 464 from GFM.
          'unmatched emphasis delimiters excess _ at end',
          (WidgetTester tester) async {
            const String data = '_foo__';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            // Expect text to be unchanged from original data string.
            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, 'foo_');

            // There should be two spans of text.
            final TextSpan textSpan = textWidget.textSpan! as TextSpan;
            expect(textSpan, isNotNull);
            expect(textSpan.children!.length == 2, isTrue);

            // First text span has italic style with normal weight.
            final InlineSpan firstSpan = textSpan.children![0];
            expectTextSpanStyle(
              firstSpan as TextSpan,
              FontStyle.italic,
              FontWeight.normal,
            );

            // Second span is normal text with normal weight.
            final InlineSpan secondSpan = textSpan.children![1];
            expectTextSpanStyle(
              secondSpan as TextSpan,
              null,
              FontWeight.normal,
            );
          },
        );

        testWidgets(
          // Example 465 from GFM.
          'unmatched strong emphasis delimiters excess _ at beginning',
          (WidgetTester tester) async {
            const String data = '___foo__';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            // Expect text to be unchanged from original data string.
            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, '_foo');

            // There should be two spans of text.
            final TextSpan textSpan = textWidget.textSpan! as TextSpan;
            expect(textSpan, isNotNull);
            expect(textSpan.children!.length == 2, isTrue);

            // First text span is normal text with normal weight.
            final InlineSpan firstSpan = textSpan.children![0];
            expectTextSpanStyle(
              firstSpan as TextSpan,
              null,
              FontWeight.normal,
            );

            // Second span is normal text with bold weight.
            final InlineSpan secondSpan = textSpan.children![1];
            expectTextSpanStyle(
              secondSpan as TextSpan,
              null,
              FontWeight.bold,
            );
          },
        );

        testWidgets(
          // Example 466 from GFM.
          'unmatched strong emphasis delimiters excess _ at beginning',
          (WidgetTester tester) async {
            const String data = '____foo_';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            // Expect text to be unchanged from original data string.
            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, '___foo');

            // There should be two spans of text.
            final TextSpan textSpan = textWidget.textSpan! as TextSpan;
            expect(textSpan, isNotNull);
            expect(textSpan.children!.length == 2, isTrue);

            // First text span is normal text with normal weight.
            final InlineSpan firstSpan = textSpan.children![0];
            expectTextSpanStyle(
              firstSpan as TextSpan,
              null,
              FontWeight.normal,
            );

            // Second span has italic style with normal weight.
            final InlineSpan secondSpan = textSpan.children![1];
            expectTextSpanStyle(
              secondSpan as TextSpan,
              FontStyle.italic,
              FontWeight.normal,
            );
          },
        );

        testWidgets(
          // Example 467 from GFM.
          'unmatched strong emphasis delimiters excess _ at end',
          (WidgetTester tester) async {
            const String data = '__foo___';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            // Expect text to be unchanged from original data string.
            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, 'foo_');

            // There should be two spans of text.
            final TextSpan textSpan = textWidget.textSpan! as TextSpan;
            expect(textSpan, isNotNull);
            expect(textSpan.children!.length == 2, isTrue);

            // First text span is normal text with bold weight.
            final InlineSpan firstSpan = textSpan.children![0];
            expectTextSpanStyle(
              firstSpan as TextSpan,
              null,
              FontWeight.bold,
            );

            // Second span is plain text with normal weight.
            final InlineSpan secondSpan = textSpan.children![1];
            expectTextSpanStyle(
              secondSpan as TextSpan,
              null,
              FontWeight.normal,
            );
          },
        );

        testWidgets(
          // Example 468 from GFM.
          'unmatched strong emphasis delimiters excess _ at end',
          (WidgetTester tester) async {
            const String data = '_foo____';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            // Expect text to be unchanged from original data string.
            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, 'foo___');

            // There should be two spans of text.
            final TextSpan textSpan = textWidget.textSpan! as TextSpan;
            expect(textSpan, isNotNull);
            expect(textSpan.children!.length == 2, isTrue);

            // First text span has italic style with normal weight.
            final InlineSpan firstSpan = textSpan.children![0];
            expectTextSpanStyle(
              firstSpan as TextSpan,
              FontStyle.italic,
              FontWeight.normal,
            );

            // Second span is plain text with normal weight.
            final InlineSpan secondSpan = textSpan.children![1];
            expectTextSpanStyle(
              secondSpan as TextSpan,
              null,
              FontWeight.normal,
            );
          },
        );
      });

      group('Rule 13', () {
        testWidgets(
          // Example 469 from GFM.
          'nested delimiters must be different - nested * is strong emphasis',
          (WidgetTester tester) async {
            const String data = '**foo**';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, 'foo');

            expectTextSpanStyle(
              textWidget.textSpan! as TextSpan,
              null,
              FontWeight.bold,
            );
          },
        );

        testWidgets(
          // Example 470 from GFM.
          'nested delimiters must be different - nest _ in * emphasis',
          (WidgetTester tester) async {
            const String data = '*_foo_*';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, 'foo');

            expectTextSpanStyle(
              textWidget.textSpan! as TextSpan,
              FontStyle.italic,
              FontWeight.normal,
            );
          },
        );

        testWidgets(
          // Example 471 from GFM.
          'nested delimiters must be different - nested _ is strong emphasis',
          (WidgetTester tester) async {
            const String data = '__foo__';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, 'foo');

            expectTextSpanStyle(
              textWidget.textSpan! as TextSpan,
              null,
              FontWeight.bold,
            );
          },
        );

        testWidgets(
          // Example 472 from GFM.
          'nested delimiters must be different - nest * in _ emphasis',
          (WidgetTester tester) async {
            const String data = '_*foo*_';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, 'foo');

            expectTextSpanStyle(
              textWidget.textSpan! as TextSpan,
              FontStyle.italic,
              FontWeight.normal,
            );
          },
        );

        testWidgets(
          // Example 473 from GFM.
          'nested delimiters must be different - nested * strong emphasis',
          (WidgetTester tester) async {
            const String data = '****foo****';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, 'foo');

            expectTextSpanStyle(
              textWidget.textSpan! as TextSpan,
              null,
              FontWeight.bold,
            );
          },
        );

        testWidgets(
          // Example 474 from GFM.
          'nested delimiters must be different - nested _ strong emphasis',
          (WidgetTester tester) async {
            const String data = '____foo____';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, 'foo');

            expectTextSpanStyle(
              textWidget.textSpan! as TextSpan,
              null,
              FontWeight.bold,
            );
          },
        );

        testWidgets(
          // Example 475 from GFM.
          'nested delimiters must be different - long sequence of * delimiters',
          (WidgetTester tester) async {
            const String data = '******foo******';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, 'foo');

            expectTextSpanStyle(
              textWidget.textSpan! as TextSpan,
              null,
              FontWeight.bold,
            );
          },
        );
      });

      // Rule 14 doesn't make any difference to flutter_markdown_plus but tests for
      // rule 14 are included here for completeness.
      group('Rule 14', () {
        testWidgets(
          // Example 476 from GFM.
          'font style and weight order * delimiter',
          (WidgetTester tester) async {
            const String data = '***foo***';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, 'foo');

            expectTextSpanStyle(
              textWidget.textSpan! as TextSpan,
              FontStyle.italic,
              FontWeight.bold,
            );
          },
        );

        testWidgets(
          // Example 476 from GFM.
          'font style and weight order _ delimiter',
          (WidgetTester tester) async {
            const String data = '_____foo_____';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, 'foo');

            expectTextSpanStyle(
              textWidget.textSpan! as TextSpan,
              FontStyle.italic,
              FontWeight.bold,
            );
          },
        );
      });

      group('Rule 15', () {
        testWidgets(
          // Example 478 from GFM.
          'overlapping * and _ emphasis delimiters',
          (WidgetTester tester) async {
            const String data = '*foo _bar* baz_';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, 'foo _bar baz_');

            // There should be two spans of text.
            final TextSpan textSpan = textWidget.textSpan! as TextSpan;
            expect(textSpan, isNotNull);
            expect(textSpan.children!.length == 2, isTrue);

            // First text span has italic style with normal weight.
            final InlineSpan firstSpan = textSpan.children![0];
            expectTextSpanStyle(
              firstSpan as TextSpan,
              FontStyle.italic,
              FontWeight.normal,
            );

            // Second span is plain text with normal weight.
            final InlineSpan secondSpan = textSpan.children![1];
            expectTextSpanStyle(
              secondSpan as TextSpan,
              null,
              FontWeight.normal,
            );
          },
        );

        testWidgets(
          // Example 479 from GFM.
          'overlapping * and __ emphasis delimiters',
          (WidgetTester tester) async {
            const String data = '*foo __bar *baz bim__ bam*';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, 'foo bar *baz bim bam');

            // There should be three spans of text.
            final TextSpan textSpan = textWidget.textSpan! as TextSpan;
            expect(textSpan, isNotNull);
            expect(textSpan.children!.length == 3, isTrue);

            // First text span has italic style with normal weight.
            final InlineSpan firstSpan = textSpan.children![0];
            expectTextSpanStyle(
              firstSpan as TextSpan,
              FontStyle.italic,
              FontWeight.normal,
            );

            // Second span has italic style with bold weight.
            final InlineSpan secondSpan = textSpan.children![1];
            expectTextSpanStyle(
              secondSpan as TextSpan,
              FontStyle.italic,
              FontWeight.bold,
            );

            // Third text span has italic style with normal weight.
            final InlineSpan thirdSpan = textSpan.children![2];
            expectTextSpanStyle(
              thirdSpan as TextSpan,
              FontStyle.italic,
              FontWeight.normal,
            );
          },
        );
      });

      group('Rule 16', () {
        testWidgets(
          // Example 480 from GFM.
          'overlapping ** strong emphasis delimiters',
          (WidgetTester tester) async {
            const String data = '**foo **bar baz**';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, '**foo bar baz');

            // There should be two spans of text.
            final TextSpan textSpan = textWidget.textSpan! as TextSpan;
            expect(textSpan, isNotNull);
            expect(textSpan.children!.length == 2, isTrue);

            // First text span is plain text with normal weight.
            final InlineSpan firstSpan = textSpan.children![0];
            expectTextSpanStyle(
              firstSpan as TextSpan,
              null,
              FontWeight.normal,
            );

            // Second span is plain text with bold weight.
            final InlineSpan secondSpan = textSpan.children![1];
            expectTextSpanStyle(
              secondSpan as TextSpan,
              null,
              FontWeight.bold,
            );
          },
        );

        testWidgets(
          // Example 479 from GFM.
          'overlapping * emphasis delimiters',
          (WidgetTester tester) async {
            const String data = '*foo *bar baz*';
            await tester.pumpWidget(
              boilerplate(
                const MarkdownBody(data: data),
              ),
            );

            final Finder textFinder = find.byType(Text);
            expect(textFinder, findsOneWidget);

            final Text textWidget = textFinder.evaluate().first.widget as Text;
            final String text = textWidget.textSpan!.toPlainText();
            expect(text, '*foo bar baz');

            // There should be two spans of text.
            final TextSpan textSpan = textWidget.textSpan! as TextSpan;
            expect(textSpan, isNotNull);
            expect(textSpan.children!.length == 2, isTrue);

            // First text span is plain text with normal weight.
            final InlineSpan firstSpan = textSpan.children![0];
            expectTextSpanStyle(
              firstSpan as TextSpan,
              null,
              FontWeight.normal,
            );

            // Second span has italic style with normal weight.
            final InlineSpan secondSpan = textSpan.children![1];
            expectTextSpanStyle(
              secondSpan as TextSpan,
              FontStyle.italic,
              FontWeight.normal,
            );
          },
        );
      });

      group('Rule 17', () {
        // The markdown package does not follow rule 17. Sam Rawlins made the
        // following comment on issue #280 on March 7, 2020:
        //
        // In terms of the spec, we are not following Rule 17 of "Emphasis and
        // strong emphasis." Inline code spans, links, images, and HTML tags
        // group more tightly than emphasis. Currently the Dart package respects
        // the broader rule that any time we can close a tag, we do, attempting
        // in the order of most recent openings first. I don't think this is
        // terribly hard to correct.
        // https://github.com/dart-lang/markdown/issues/280
        //
        // Test for rule 17 are not included since markdown package is not
        // following the rule.
      }, skip: 'No Rule 17 tests implemented');
    },
  );
}
