spell_check_test.dart 14.8 KB
// Copyright 2014 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/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';

late TextStyle composingStyle;
late TextStyle misspelledTextStyle;

void main() {
  setUp(() {
    composingStyle = const TextStyle(decoration: TextDecoration.underline);

    // Using Android handling for testing.
    misspelledTextStyle = TextField.materialMisspelledTextStyle;
  });

  testWidgets(
  'buildTextSpanWithSpellCheckSuggestions ignores composing region when composing region out of range',
      (WidgetTester tester) async {
    const String text = 'Hello, wrold! Hey';
    const TextEditingValue value = TextEditingValue(text: text);
    const bool composingRegionOutOfRange = true;
    const SpellCheckResults spellCheckResults =
      SpellCheckResults(text, <SuggestionSpan>[
        SuggestionSpan(
            TextRange(start: 7, end: 12), <String>['world', 'word', 'old'])
    ]);

    final TextSpan expectedTextSpanTree = TextSpan(children: <TextSpan>[
      const TextSpan(text: 'Hello, '),
      TextSpan(style: misspelledTextStyle, text: 'wrold'),
      const TextSpan(text: '! Hey')
    ]);
    final TextSpan textSpanTree =
      buildTextSpanWithSpellCheckSuggestions(
        value,
        composingRegionOutOfRange,
        null,
        misspelledTextStyle,
        spellCheckResults,
    );

    expect(textSpanTree, equals(expectedTextSpanTree));
    }, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.android, TargetPlatform.iOS }));

  testWidgets(
    'buildTextSpanWithSpellCheckSuggestions, isolated misspelled word with separate composing region example',
      (WidgetTester tester) async {
    const String text = 'Hello, wrold! Hey';
    const TextEditingValue value = TextEditingValue(
        text: text, composing: TextRange(start: 14, end: 17));
    const bool composingRegionOutOfRange = false;
    const SpellCheckResults spellCheckResults =
      SpellCheckResults(text, <SuggestionSpan>[
        SuggestionSpan(
            TextRange(start: 7, end: 12), <String>['world', 'word', 'old'])
    ]);

    final TextSpan expectedTextSpanTree = TextSpan(children: <TextSpan>[
      const TextSpan(text: 'Hello, '),
      TextSpan(style: misspelledTextStyle, text: 'wrold'),
      const TextSpan(text: '! '),
      TextSpan(style: composingStyle, text: 'Hey')
    ]);
    final TextSpan textSpanTree =
      buildTextSpanWithSpellCheckSuggestions(
        value,
        composingRegionOutOfRange,
        null,
        misspelledTextStyle,
        spellCheckResults,
    );

    expect(textSpanTree, equals(expectedTextSpanTree));
  }, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.android }));

  testWidgets(
    'buildTextSpanWithSpellCheckSuggestions, composing region and misspelled words overlap example',
      (WidgetTester tester) async {
    const String text = 'Right worng worng right';
    const TextEditingValue value = TextEditingValue(
        text: text, composing: TextRange(start: 12, end: 17));
    const bool composingRegionOutOfRange = false;
    const SpellCheckResults spellCheckResults =
      SpellCheckResults(text, <SuggestionSpan>[
        SuggestionSpan(
            TextRange(start: 6, end: 11), <String>['wrong', 'worn', 'wrung']),
        SuggestionSpan(
            TextRange(start: 12, end: 17), <String>['wrong', 'worn', 'wrung'])
    ]);

    final TextSpan expectedTextSpanTree = TextSpan(children: <TextSpan>[
      const TextSpan(text: 'Right '),
      TextSpan(style: misspelledTextStyle, text: 'worng'),
      const TextSpan(text: ' '),
      TextSpan(style: composingStyle, text: 'worng'),
      const TextSpan(text: ' right'),
    ]);
    final TextSpan textSpanTree =
      buildTextSpanWithSpellCheckSuggestions(
        value,
        composingRegionOutOfRange,
        null,
        misspelledTextStyle,
        spellCheckResults,
    );

    expect(textSpanTree, equals(expectedTextSpanTree));
  }, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.android }));

  testWidgets(
    'buildTextSpanWithSpellCheckSuggestions, consecutive misspelled words example',
      (WidgetTester tester) async {
    const String text = 'Right worng worng right';
    const TextEditingValue value = TextEditingValue(text: text);
    const bool composingRegionOutOfRange = true;
    const SpellCheckResults spellCheckResults =
      SpellCheckResults(text, <SuggestionSpan>[
        SuggestionSpan(
            TextRange(start: 6, end: 11), <String>['wrong', 'worn', 'wrung']),
        SuggestionSpan(
            TextRange(start: 12, end: 17), <String>['wrong', 'worn', 'wrung'])
    ]);

    final TextSpan expectedTextSpanTree = TextSpan(children: <TextSpan>[
      const TextSpan(text: 'Right '),
      TextSpan(style: misspelledTextStyle, text: 'worng'),
      const TextSpan(text: ' '),
      TextSpan(style: misspelledTextStyle, text: 'worng'),
      const TextSpan(text: ' right'),
    ]);
    final TextSpan textSpanTree =
      buildTextSpanWithSpellCheckSuggestions(
        value,
        composingRegionOutOfRange,
        null,
        misspelledTextStyle,
        spellCheckResults,
    );

    expect(textSpanTree, equals(expectedTextSpanTree));
  }, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.android, TargetPlatform.iOS }));

  testWidgets(
    'buildTextSpanWithSpellCheckSuggestions corrects results when they lag, results text shorter than actual text example',
      (WidgetTester tester) async {
    const String text = 'Hello, wrold! Hey';
    const String resultsText = 'Hello, wrold!';
    const TextEditingValue value = TextEditingValue(text: text);
    const bool composingRegionOutOfRange = false;
    const SpellCheckResults spellCheckResults =
      SpellCheckResults(resultsText, <SuggestionSpan>[
        SuggestionSpan(
            TextRange(start: 7, end: 12), <String>['world', 'word', 'old'])
    ]);

    final TextSpan expectedTextSpanTree = TextSpan(children: <TextSpan>[
      const TextSpan(text: 'Hello, '),
      TextSpan(style: misspelledTextStyle, text: 'wrold'),
      const TextSpan(text: '! Hey'),
    ]);
    final TextSpan textSpanTree =
      buildTextSpanWithSpellCheckSuggestions(
        value,
        composingRegionOutOfRange,
        null,
        misspelledTextStyle,
        spellCheckResults,
    );

    expect(textSpanTree, equals(expectedTextSpanTree));
  }, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.android, TargetPlatform.iOS }));

  testWidgets(
    'buildTextSpanWithSpellCheckSuggestions corrects results when they lag, results text longer with more misspelled words than actual text example',
      (WidgetTester tester) async {
    const String text = 'Hello, wrold! Hey';
    const String resultsText = 'Hello, wrold Hey feirnd!';
    const TextEditingValue value = TextEditingValue(text: text);
    const bool composingRegionOutOfRange = false;
    const SpellCheckResults spellCheckResults =
      SpellCheckResults(resultsText, <SuggestionSpan>[
        SuggestionSpan(
            TextRange(start: 7, end: 12), <String>['world', 'word', 'old']),
        SuggestionSpan(
            TextRange(start: 17, end: 23), <String>['friend', 'fiend', 'fern'])
    ]);

    final TextSpan expectedTextSpanTree = TextSpan(children: <TextSpan>[
      const TextSpan(text: 'Hello, '),
      TextSpan(style: misspelledTextStyle, text: 'wrold'),
      const TextSpan(text: '! Hey'),
    ]);
    final TextSpan textSpanTree =
      buildTextSpanWithSpellCheckSuggestions(
        value,
        composingRegionOutOfRange,
        null,
        misspelledTextStyle,
        spellCheckResults,
    );

    expect(textSpanTree, equals(expectedTextSpanTree));
  }, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.android, TargetPlatform.iOS }));

  testWidgets(
    'buildTextSpanWithSpellCheckSuggestions corrects results when they lag, results text mismatched example',
      (WidgetTester tester) async {
    const String text = 'Hello, wrold! Hey';
    const String resultsText = 'Hello, wrild! Hey';
    const TextEditingValue value = TextEditingValue(text: text);
    const bool composingRegionOutOfRange = false;
    const SpellCheckResults spellCheckResults =
      SpellCheckResults(resultsText, <SuggestionSpan>[
        SuggestionSpan(TextRange(start: 7, end: 12), <String>['wild', 'world']),
    ]);

    const TextSpan expectedTextSpanTree = TextSpan(children: <TextSpan>[
      TextSpan(text: 'Hello, wrold! Hey'),
    ]);
    final TextSpan textSpanTree =
      buildTextSpanWithSpellCheckSuggestions(
        value,
        composingRegionOutOfRange,
        null,
        misspelledTextStyle,
        spellCheckResults,
    );

    expect(textSpanTree, equals(expectedTextSpanTree));
  }, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.android, TargetPlatform.iOS }));

  testWidgets(
    'buildTextSpanWithSpellCheckSuggestions corrects results when they lag, results shifted forward example',
      (WidgetTester tester) async {
    const String text = 'Hello, there wrold! Hey';
    const String resultsText = 'Hello, wrold! Hey';
    const TextEditingValue value = TextEditingValue(text: text);
    const bool composingRegionOutOfRange = false;
    const SpellCheckResults spellCheckResults =
      SpellCheckResults(resultsText, <SuggestionSpan>[
        SuggestionSpan(
            TextRange(start: 7, end: 12), <String>['world', 'word', 'old']),
    ]);

    final TextSpan expectedTextSpanTree = TextSpan(children: <TextSpan>[
      const TextSpan(text: 'Hello, there '),
      TextSpan(style: misspelledTextStyle, text: 'wrold'),
      const TextSpan(text: '! Hey'),
    ]);
    final TextSpan textSpanTree =
      buildTextSpanWithSpellCheckSuggestions(
        value,
        composingRegionOutOfRange,
        null,
        misspelledTextStyle,
        spellCheckResults,
    );

    expect(textSpanTree, equals(expectedTextSpanTree));
  }, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.android, TargetPlatform.iOS }));

  testWidgets(
    'buildTextSpanWithSpellCheckSuggestions corrects results when they lag, results shifted backwards example',
      (WidgetTester tester) async {
    const String text = 'Hello, wrold! Hey';
    const String resultsText = 'Hello, great wrold! Hey';
    const TextEditingValue value = TextEditingValue(text: text);
    const bool composingRegionOutOfRange = false;
    const SpellCheckResults spellCheckResults =
      SpellCheckResults(resultsText, <SuggestionSpan>[
        SuggestionSpan(
            TextRange(start: 13, end: 18), <String>['world', 'word', 'old']),
    ]);

    final TextSpan expectedTextSpanTree = TextSpan(children: <TextSpan>[
      const TextSpan(text: 'Hello, '),
      TextSpan(style: misspelledTextStyle, text: 'wrold'),
      const TextSpan(text: '! Hey'),
    ]);
    final TextSpan textSpanTree =
      buildTextSpanWithSpellCheckSuggestions(
        value,
        composingRegionOutOfRange,
        null,
        misspelledTextStyle,
        spellCheckResults,
    );

    expect(textSpanTree, equals(expectedTextSpanTree));
  }, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.android, TargetPlatform.iOS }));

  testWidgets(
    'buildTextSpanWithSpellCheckSuggestions corrects results when they lag, results shifted backwards and forwards example',
      (WidgetTester tester) async {
    const String text = 'Hello, wrold! And Hye!';
    const String resultsText = 'Hello, great wrold! Hye!';
    const TextEditingValue value = TextEditingValue(text: text);
    const bool composingRegionOutOfRange = false;
    const SpellCheckResults spellCheckResults =
      SpellCheckResults(resultsText, <SuggestionSpan>[
        SuggestionSpan(
            TextRange(start: 13, end: 18), <String>['world', 'word', 'old']),
        SuggestionSpan(TextRange(start: 20, end: 23), <String>['Hey', 'He'])
    ]);

    final TextSpan expectedTextSpanTree = TextSpan(children: <TextSpan>[
      const TextSpan(text: 'Hello, '),
      TextSpan(style: misspelledTextStyle, text: 'wrold'),
      const TextSpan(text: '! And '),
      TextSpan(style: misspelledTextStyle, text: 'Hye'),
      const TextSpan(text: '!')
    ]);
    final TextSpan textSpanTree =
      buildTextSpanWithSpellCheckSuggestions(
        value,
        composingRegionOutOfRange,
        null,
        misspelledTextStyle,
        spellCheckResults,
    );

    expect(textSpanTree, equals(expectedTextSpanTree));
  }, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.android, TargetPlatform.iOS }));

  testWidgets(
    'buildTextSpanWithSpellCheckSuggestions discards result when additions are made to misspelled word example',
      (WidgetTester tester) async {
    const String text = 'Hello, wroldd!';
    const String resultsText = 'Hello, wrold!';
    const TextEditingValue value = TextEditingValue(text: text);
    const bool composingRegionOutOfRange = false;
    const SpellCheckResults spellCheckResults =
      SpellCheckResults(resultsText, <SuggestionSpan>[
        SuggestionSpan(
            TextRange(start: 7, end: 12), <String>['world', 'word', 'old']),
    ]);

    const TextSpan expectedTextSpanTree = TextSpan(children: <TextSpan>[
      TextSpan(text: 'Hello, wroldd!'),
    ]);
    final TextSpan textSpanTree =
      buildTextSpanWithSpellCheckSuggestions(
        value,
        composingRegionOutOfRange,
        null,
        misspelledTextStyle,
        spellCheckResults,
    );

    expect(textSpanTree, equals(expectedTextSpanTree));
  }, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.android, TargetPlatform.iOS }));

  testWidgets(
    'buildTextSpanWithSpellCheckSuggestions does not throw when text contains regex reserved characters',
    (WidgetTester tester) async {
      // Regression test for https://github.com/flutter/flutter/issues/136032.
      const String text = 'Hello, *ãresaa';
      const String resultsText = 'Hello, *ãresa';
      const TextEditingValue value = TextEditingValue(text: text);
      const bool composingRegionOutOfRange = false;
      const SpellCheckResults spellCheckResults = SpellCheckResults(
        resultsText,
        <SuggestionSpan>[
          SuggestionSpan(TextRange(start: 7, end: 12), <String>['*rangesa']),
        ],
      );

      const TextSpan expectedTextSpanTree = TextSpan(children: <TextSpan>[
        TextSpan(text: 'Hello, *ãresaa'),
      ]);
      final TextSpan textSpanTree = buildTextSpanWithSpellCheckSuggestions(
        value,
        composingRegionOutOfRange,
        null,
        misspelledTextStyle,
        spellCheckResults,
      );

      expect(tester.takeException(), null);
      expect(textSpanTree, equals(expectedTextSpanTree));
    }, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.android, TargetPlatform.iOS }),
  );
}