aissat / easy_localization

Easy and Fast internationalizing your Flutter Apps
https://pub.dev/packages/easy_localization
MIT License
923 stars 332 forks source link

Localization delegate and provider initialization does not run for each widget test #359

Open glavigno opened 3 years ago

glavigno commented 3 years ago

I am using the version 2.3.0 of the package and wrote a test file containing multiple widget tests for a localized widget. I noticed that EasyLocalization logs does not run localization delegate and provider initialization after the first test.

Here is my test. TestableAddCardScreen function in setUp hook is in charge of wrapping widget in EasyLocalization.

void main() {
  Widget testedWidget;

  setUp(() {
    testedWidget = testableAddCardScreen(AddCardFormScreen());
  });

  testWidgets('Form is submittable if inputs are valid', (
    WidgetTester tester,
  ) async {
    final validCardNumber = '1234123412341234';
    final validExpirationDate = '11/25';
    final validCvv = '123';

    await tester.pumpWidget(testedWidget);
    await tester.pumpAndSettle();

    final inputs = find.byType(TextInput);

    await tester.enterText(inputs.at(0), validCardNumber);
    await tester.enterText(inputs.at(1), validExpirationDate);
    await tester.enterText(inputs.at(2), validCvv);
    await tester.pumpAndSettle();

    final button = evaluateWidget<Button>(find.byType(Button));

    expect(button.isDisabled, false);
  });
}

I ran the same test twice in a row on purpose and here are the logs.

[Easy Localization] Start
[Easy Localization] Init state
[Easy Localization] Build
[Easy Localization] Device locale en_US
[log] easy localization loader: load yaml file assets/translations/fr.yaml
[Easy Localization] Init Localization Delegate
[Easy Localization] Init provider
✓ Form is submittable if inputs are valid
[Easy Localization] Start
[Easy Localization] Init state
[Easy Localization] Build
[Easy Localization] Device locale en_US
[log] easy localization loader: load yaml file assets/translations/fr.yaml

══╡ EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK ╞════════════════════════════════════════════════════
The following IndexError was thrown running a test:
RangeError (index): Index out of range: no indices are valid: 0

When the exception was thrown, this was the stack:
#0      Iterable.elementAt (dart:core/iterable.dart:653:5)
#1      _IndexFinder.filter.<anonymous closure> (package:flutter_test/src/finders.dart:489:28)
#2      _SyncIterator.moveNext (dart:core-patch/core_patch.dart:165:25)
#3      ExpandIterator.moveNext (dart:_internal/iterable.dart:483:21)
#4      SetMixin.addAll (dart:collection/set.dart:57:23)
#5      new LinkedHashSet.of (dart:collection/linked_hash_set.dart:111:27)
#6      Iterable.toSet (dart:core/iterable.dart:416:21)
#7      _DescendantFinder.allCandidates (package:flutter_test/src/finders.dart:693:7)
#8      Finder.evaluate (package:flutter_test/src/finders.dart:377:61)
#9      WidgetController.state (package:flutter_test/src/controller.dart:155:31)
#10     WidgetTester.showKeyboard.<anonymous closure> (package:flutter_test/src/widget_tester.dart:1066:42)
#13     TestAsyncUtils.guard (package:flutter_test/src/test_async_utils.dart:72:41)
#14     WidgetTester.showKeyboard (package:flutter_test/src/widget_tester.dart:1065:27)
#15     WidgetTester.enterText.<anonymous closure> (package:flutter_test/src/widget_tester.dart:1089:13)
#18     TestAsyncUtils.guard (package:flutter_test/src/test_async_utils.dart:72:41)
#19     WidgetTester.enterText (package:flutter_test/src/widget_tester.dart:1088:27)
#20     main.<anonymous closure> (file:///Users/guillaumelavignotte/Code/Work/Moneytrack/moneytrack_sdk/test/screens/add_card_form_screen_valid_test.dart:50:18)
<asynchronous suspension>
#21     main.<anonymous closure> (file:///Users/guillaumelavignotte/Code/Work/Moneytrack/moneytrack_sdk/test/screens/add_card_form_screen_valid_test.dart)
#22     testWidgets.<anonymous closure>.<anonymous closure> (package:flutter_test/src/widget_tester.dart:146:29)
<asynchronous suspension>
#23     testWidgets.<anonymous closure>.<anonymous closure> (package:flutter_test/src/widget_tester.dart)
#24     TestWidgetsFlutterBinding._runTestBody (package:flutter_test/src/binding.dart:784:19)
<asynchronous suspension>
#27     TestWidgetsFlutterBinding._runTest (package:flutter_test/src/binding.dart:764:14)
#28     AutomatedTestWidgetsFlutterBinding.runTest.<anonymous closure> (package:flutter_test/src/binding.dart:1173:24)
#29     FakeAsync.run.<anonymous closure>.<anonymous closure> (package:fake_async/fake_async.dart:178:54)
#34     withClock (package:clock/src/default.dart:48:10)
#35     FakeAsync.run.<anonymous closure> (package:fake_async/fake_async.dart:178:22)
#40     FakeAsync.run (package:fake_async/fake_async.dart:178:7)
#41     AutomatedTestWidgetsFlutterBinding.runTest (package:flutter_test/src/binding.dart:1170:15)
#42     testWidgets.<anonymous closure> (package:flutter_test/src/widget_tester.dart:138:24)
#43     Declarer.test.<anonymous closure>.<anonymous closure> (package:test_api/src/backend/declarer.dart:175:19)
<asynchronous suspension>
#44     Declarer.test.<anonymous closure>.<anonymous closure> (package:test_api/src/backend/declarer.dart)
#49     Declarer.test.<anonymous closure> (package:test_api/src/backend/declarer.dart:173:13)
#50     Invoker.waitForOutstandingCallbacks.<anonymous closure> (package:test_api/src/backend/invoker.dart:231:15)
#55     Invoker.waitForOutstandingCallbacks (package:test_api/src/backend/invoker.dart:228:5)
#56     Invoker._onRun.<anonymous closure>.<anonymous closure>.<anonymous closure> (package:test_api/src/backend/invoker.dart:383:17)
<asynchronous suspension>
#57     Invoker._onRun.<anonymous closure>.<anonymous closure>.<anonymous closure> (package:test_api/src/backend/invoker.dart)
#62     Invoker._onRun.<anonymous closure>.<anonymous closure> (package:test_api/src/backend/invoker.dart:370:9)
#63     Invoker._guardIfGuarded (package:test_api/src/backend/invoker.dart:415:15)
#64     Invoker._onRun.<anonymous closure> (package:test_api/src/backend/invoker.dart:369:7)
#71     Invoker._onRun (package:test_api/src/backend/invoker.dart:368:11)
#72     LiveTestController.run (package:test_api/src/backend/live_test_controller.dart:153:11)
#73     RemoteListener._runLiveTest.<anonymous closure> (package:test_api/src/remote_listener.dart:256:16)
#78     RemoteListener._runLiveTest (package:test_api/src/remote_listener.dart:255:5)
#79     RemoteListener._serializeTest.<anonymous closure> (package:test_api/src/remote_listener.dart:208:7)
#97     _GuaranteeSink.add (package:stream_channel/src/guarantee_channel.dart:125:12)
#98     new _MultiChannel.<anonymous closure> (package:stream_channel/src/multi_channel.dart:159:31)
#102    CastStreamSubscription._onData (dart:_internal/async_cast.dart:85:11)
#136    new _WebSocketImpl._fromSocket.<anonymous closure> (dart:_http/websocket_impl.dart:1145:21)
#144    _WebSocketProtocolTransformer._messageFrameEnd (dart:_http/websocket_impl.dart:338:23)
#145    _WebSocketProtocolTransformer.add (dart:_http/websocket_impl.dart:232:46)
#155    _Socket._onData (dart:io-patch/socket_patch.dart:2044:41)
#164    new _RawSocket.<anonymous closure> (dart:io-patch/socket_patch.dart:1580:33)
#165    _NativeSocket.issueReadEvent.issue (dart:io-patch/socket_patch.dart:1076:14)
(elided 115 frames from dart:async and package:stack_trace)

The test description was:
  Form is submittable if inputs are valid
════════════════════════════════════════════════════════════════════════════════════════════════════
Test failed. See exception logs above.
The test description was: Form is submittable if inputs are valid

✖ Form is submittable if inputs are valid

Thanks for your help.

sanjidbillah commented 3 years ago

yes i am also faced this

VitaliBov commented 2 years ago

I have the same problem. You can override AssetLoader to change the localization source:

class MockAssetLoader extends AssetLoader {
  @override
  Future<Map<String, dynamic>?> load(String path, Locale locale) async {
    return <String, dynamic>{
      'aaa': <String, dynamic>{
        'bbb': <String, dynamic>{
          'ccc': <String, dynamic>{
            'ddd': 'ddd',
            'eee': 'eee',
          },
          'fff': 'fff',
        },
      },
    };
  }
}

Then add a new AssetLoader to the EasyLocalization constructor:

EasyLocalization(
  assetLoader: assetLoader,
  child: child,
);