// AUDIT REPRO (not app code): "the field loses focus after one character". import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:provider/provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:verbomatique/routes/verb_indicative_study_cover_mode_route.dart'; import 'package:verbomatique/routes/verb_indicative_study_write_mode_route.dart'; import 'package:verbomatique/routes/verb_indicative_test_route.dart'; import 'package:verbomatique/settings/settings_controller.dart'; import 'package:verbomatique/settings/settings_service.dart'; import 'package:verbomatique/src/model/study_mode.dart'; import 'package:verbomatique/src/model/study_mode_state.dart'; import 'package:verbomatique/src/model/verb_test_model.dart'; Future _pumpVia(WidgetTester tester, {required bool viaWrite}) async { SharedPreferences.setMockInitialValues({}); final prefs = await SharedPreferences.getInstance(); final settings = SettingsController(SettingsService(prefs), ThemeMode.light, prefs); await settings.loadSettings(); final model = VerbTestModel() ..initialise(['ai', 'as', 'a', 'avons', 'avez', 'ont'], ['je', 'tu', 'il/elle', 'nous', 'vous', 'ils/elles'], 'avoir', 'fr', 'hebben'); await tester.pumpWidget(MultiProvider( providers: [ ChangeNotifierProvider.value(value: model), ChangeNotifierProvider( create: (_) => StudyModeState(viaWrite ? StudyMode.writtenMode : StudyMode.coverMode)), ChangeNotifierProvider.value(value: settings.cardinalitySelection), ], child: MaterialApp( home: viaWrite ? VerbIndicativeStudyWriteModeRoute(settings) : VerbIndicativeStudyCoverModeRoute(settings, key: UniqueKey()), onGenerateRoute: (s) => MaterialPageRoute( settings: s, builder: (_) => VerbIndicativeTestRoute(settings))))); await tester.pumpAndSettle(); await tester.tap(find.byKey(Key(viaWrite ? 'write_forward_button' : 'forward_button'))); await tester.pumpAndSettle(); } EditableText _et(WidgetTester tester) => tester.widget(find.descendant( of: find.byKey(VerbIndicativeTestRoute.testFieldKey('avoir', 0)), matching: find.byType(EditableText))); Future _typeTwoChars(WidgetTester tester) async { await tester.showKeyboard(find.byKey(VerbIndicativeTestRoute.testFieldKey('avoir', 0))); tester.testTextInput.enterText('a'); // like a real keyboard: no re-focus per char await tester.pump(); await tester.pump(); final focusAfterFirst = _et(tester).focusNode.hasFocus; final hidden = find.descendant(of: find.byKey(const Key('actualVerb_0'), skipOffstage: false), matching: find.byType(EditableText, skipOffstage: false), skipOffstage: false); if (hidden.evaluate().isNotEmpty) { debugPrint('AUDIT hidden write-mode field actualVerb_0 has focus: ${tester.widget(hidden).focusNode.hasFocus}, text: "${tester.widget(hidden).controller.text}"'); } debugPrint('AUDIT focus after 1st char: $focusAfterFirst; primaryFocus: ${FocusManager.instance.primaryFocus}'); tester.testTextInput.enterText('ai'); await tester.pump(); debugPrint('AUDIT text after 2nd char: "${_et(tester).controller.text}"'); expect(focusAfterFirst, isTrue, reason: 'focus left the test field after one character'); expect(_et(tester).focusNode.hasFocus, isTrue); expect(_et(tester).controller.text, 'ai'); } void main() { testWidgets('REPRO: test field reached from WRITE mode keeps focus after 2 chars', (tester) async { await _pumpVia(tester, viaWrite: true); await _typeTwoChars(tester); }, variant: TargetPlatformVariant.only(TargetPlatform.iOS)); testWidgets('REPRO (Android): same, TargetPlatform.android', (tester) async { await _pumpVia(tester, viaWrite: true); await _typeTwoChars(tester); }, variant: TargetPlatformVariant.only(TargetPlatform.android)); testWidgets('CONTROL: test field reached from COVER mode keeps focus', (tester) async { await _pumpVia(tester, viaWrite: false); await _typeTwoChars(tester); }, variant: TargetPlatformVariant.only(TargetPlatform.iOS)); testWidgets('WRITE MODE itself: answer field keeps focus after 2 chars', (tester) async { SharedPreferences.setMockInitialValues({}); final prefs = await SharedPreferences.getInstance(); final settings = SettingsController(SettingsService(prefs), ThemeMode.light, prefs); await settings.loadSettings(); final model = VerbTestModel() ..initialise(['ai', 'as', 'a', 'avons', 'avez', 'ont'], ['je', 'tu', 'il/elle', 'nous', 'vous', 'ils/elles'], 'avoir', 'fr', 'hebben'); await tester.pumpWidget(MultiProvider(providers: [ ChangeNotifierProvider.value(value: model), ChangeNotifierProvider(create: (_) => StudyModeState(StudyMode.writtenMode)), ChangeNotifierProvider.value(value: settings.cardinalitySelection), ], child: MaterialApp(home: VerbIndicativeStudyWriteModeRoute(settings)))); await tester.pumpAndSettle(); final f = find.byKey(const Key('actualVerb_0')); await tester.showKeyboard(f); tester.testTextInput.enterText('a'); await tester.pump(); await tester.pump(); EditableText et() => tester.widget(find.descendant(of: f, matching: find.byType(EditableText))); debugPrint('AUDIT write-mode focus after 1st char: ${et().focusNode.hasFocus}'); tester.testTextInput.enterText('ai'); await tester.pump(); debugPrint('AUDIT write-mode text after 2nd char: "${et().controller.text}"'); expect(et().focusNode.hasFocus, isTrue); expect(et().controller.text, 'ai'); }, variant: TargetPlatformVariant.only(TargetPlatform.iOS)); }