| // 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. |
| |
| // TODO(camsim99): Revert this timeout change after effects are investigated. |
| @Timeout(Duration(seconds: 60)) |
| library; |
| |
| import 'package:flutter/material.dart'; |
| import 'package:flutter/rendering.dart'; |
| import 'package:flutter/services.dart'; |
| import 'package:flutter_test/flutter_test.dart'; |
| import 'package:integration_test/integration_test.dart'; |
| import 'package:spell_check/main.dart'; |
| |
| late DefaultSpellCheckService defaultSpellCheckService; |
| late Locale locale; |
| |
| /// Waits to find [EditableText] that displays text with misspelled |
| /// words marked the same as the [TextSpan] provided and returns |
| /// true if it is found before timing out at 20 seconds. |
| Future<bool> findTextSpanTree( |
| WidgetTester tester, |
| TextSpan inlineSpan, |
| ) async { |
| final RenderObject root = tester.renderObject(find.byType(EditableText)); |
| expect(root, isNotNull); |
| |
| RenderEditable? renderEditable; |
| void recursiveFinder(RenderObject child) { |
| if (child is RenderEditable && child.text == inlineSpan) { |
| renderEditable = child; |
| return; |
| } |
| child.visitChildren(recursiveFinder); |
| } |
| |
| final DateTime endTime = tester.binding.clock.now().add(const Duration(seconds: 20)); |
| do { |
| if (tester.binding.clock.now().isAfter(endTime)) { |
| return false; |
| } |
| await tester.pump(const Duration(seconds: 1)); |
| root.visitChildren(recursiveFinder); |
| } while (renderEditable == null); |
| |
| return true; |
| } |
| |
| void main() { |
| IntegrationTestWidgetsFlutterBinding.ensureInitialized(); |
| |
| setUp(() { |
| defaultSpellCheckService = DefaultSpellCheckService(); |
| locale = const Locale('en', 'us'); |
| }); |
| |
| test( |
| 'fetchSpellCheckSuggestions returns null with no misspelled words', |
| () async { |
| const String text = 'Hello, world!'; |
| |
| final List<SuggestionSpan>? spellCheckSuggestionSpans = |
| await defaultSpellCheckService.fetchSpellCheckSuggestions(locale, text); |
| |
| expect(spellCheckSuggestionSpans!.length, equals(0)); |
| expect( |
| defaultSpellCheckService.lastSavedResults!.spellCheckedText, |
| equals(text) |
| ); |
| expect( |
| defaultSpellCheckService.lastSavedResults!.suggestionSpans, |
| equals(spellCheckSuggestionSpans) |
| ); |
| }); |
| |
| test( |
| 'fetchSpellCheckSuggestions returns correct ranges with misspelled words', |
| () async { |
| const String text = 'Hlelo, world! Yuou are magnificente'; |
| const List<TextRange> misspelledWordRanges = <TextRange>[ |
| TextRange(start: 0, end: 5), |
| TextRange(start: 14, end: 18), |
| TextRange(start: 23, end: 35) |
| ]; |
| |
| final List<SuggestionSpan>? spellCheckSuggestionSpans = |
| await defaultSpellCheckService.fetchSpellCheckSuggestions(locale, text); |
| |
| expect(spellCheckSuggestionSpans, isNotNull); |
| expect( |
| spellCheckSuggestionSpans!.length, |
| equals(misspelledWordRanges.length) |
| ); |
| |
| for (int i = 0; i < misspelledWordRanges.length; i += 1) { |
| expect( |
| spellCheckSuggestionSpans[i].range, |
| equals(misspelledWordRanges[i]) |
| ); |
| } |
| |
| expect( |
| defaultSpellCheckService.lastSavedResults!.spellCheckedText, |
| equals(text) |
| ); |
| expect( |
| defaultSpellCheckService.lastSavedResults!.suggestionSpans, |
| equals(spellCheckSuggestionSpans) |
| ); |
| }); |
| |
| test( |
| 'fetchSpellCheckSuggestions does not correct results when Gboard not ignoring composing region', |
| () async { |
| const String text = 'Wwow, whaaett a beautiful day it is!'; |
| |
| final List<SuggestionSpan>? spellCheckSpansWithComposingRegion = |
| await defaultSpellCheckService.fetchSpellCheckSuggestions(locale, text); |
| |
| expect(spellCheckSpansWithComposingRegion, isNotNull); |
| expect(spellCheckSpansWithComposingRegion!.length, equals(2)); |
| |
| final List<SuggestionSpan>? spellCheckSuggestionSpans = |
| await defaultSpellCheckService.fetchSpellCheckSuggestions(locale, text); |
| |
| expect( |
| spellCheckSuggestionSpans, |
| equals(spellCheckSpansWithComposingRegion) |
| ); |
| }); |
| |
| test( |
| 'fetchSpellCheckSuggestions merges results when Gboard ignoring composing region', |
| () async { |
| const String text = 'Wooahha it is an amazzinng dayyebf!'; |
| |
| final List<SuggestionSpan>? modifiedSpellCheckSuggestionSpans = |
| await defaultSpellCheckService.fetchSpellCheckSuggestions(locale, text); |
| final List<SuggestionSpan> expectedSpellCheckSuggestionSpans = |
| List<SuggestionSpan>.from(modifiedSpellCheckSuggestionSpans!); |
| expect(modifiedSpellCheckSuggestionSpans, isNotNull); |
| expect(modifiedSpellCheckSuggestionSpans.length, equals(3)); |
| |
| // Remove one span to simulate Gboard attempting to un-ignore the composing region, after tapping away from "Yuou". |
| modifiedSpellCheckSuggestionSpans.removeAt(1); |
| |
| defaultSpellCheckService.lastSavedResults = |
| SpellCheckResults(text, modifiedSpellCheckSuggestionSpans); |
| |
| final List<SuggestionSpan>? spellCheckSuggestionSpans = |
| await defaultSpellCheckService.fetchSpellCheckSuggestions(locale, text); |
| |
| expect(spellCheckSuggestionSpans, isNotNull); |
| expect( |
| spellCheckSuggestionSpans, |
| equals(expectedSpellCheckSuggestionSpans) |
| ); |
| }); |
| |
| testWidgets('EditableText spell checks when text is entered and spell check enabled', (WidgetTester tester) async { |
| const TextStyle style = TextStyle(); |
| const TextStyle misspelledTextStyle = TextField.materialMisspelledTextStyle; |
| |
| await tester.pumpWidget(const MyApp()); |
| |
| await tester.enterText(find.byType(EditableText), 'Hey cfabiueq qocnakoef! Hey!'); |
| |
| const TextSpan expectedTextSpanTree = TextSpan( |
| style: style, |
| children: <TextSpan>[ |
| TextSpan(style: style, text: 'Hey '), |
| TextSpan(style: misspelledTextStyle, text: 'cfabiueq'), |
| TextSpan(style: style, text: ' '), |
| TextSpan(style: misspelledTextStyle, text: 'qocnakoef'), |
| TextSpan(style: style, text: '! Hey!'), |
| ]); |
| |
| final bool expectedTextSpanTreeFound = await findTextSpanTree(tester, expectedTextSpanTree); |
| |
| expect(expectedTextSpanTreeFound, isTrue); |
| }); |
| } |