Flutter test widgets, go to another screen failed - flutter

I'm writting my very first widget tests in dart. I have to check if clicking on a button bring us to the next screen.
What am I doing wrong here?
testWidgets('Tapping staring the meeting button leads to conference screen', (widgetTester) async {
await widgetTester.pumpWidget(const MaterialApp(home: WelcomeScreen()));
await widgetTester.enterText(find.byKey(const Key('nameInput')), 'User');
await widgetTester.enterText(find.byKey(const Key('titleInput')), 'Title');
await widgetTester.enterText(find.byKey(const Key('passwordInput')), 'Password');
expect(find.byType(CallToActionButton), findsOneWidget);
await widgetTester.tap(find.byType(CallToActionButton));
await widgetTester.pump();
expect(find.byType(ConferenceScreen), findsOneWidget);
});

Related

How to wait until the Finder is visible for next code execution in Flutter integration test?

Information:
I have created a sample Flutter unit test to test the login screen where I have email & password as input field and a login button.
Requirement:
Need to test false cases and for that, I have written code as per the below steps.
Open main.dart
Filled the email & password field
onTap event is done on the login button. Over here API will be called and loader is displayed on the screen until API gets a success or failure response.
Need to check if failure dialog is displayed with a message.
Issue/Query:
Now when the API is calling I want to wait when the loader is visible until the loader is gone. So, as of now I just put a manual delay to execute the next code but I want to make it dynamic. So, let me know how we can put dynamic delay based on the loader visible?
Code:
void main() {
group('App Test', () {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
testWidgets('Login Fail Test', (WidgetTester tester) async {
await app.main();
await tester.pumpAndSettle();
await tester.pump(new Duration(seconds: 2));
final emailField = find.byType(TextFormField).first;
final passwordField = find.byType(TextFormField).last;
final loginButton = find.byType(RaisedButton).first;
await tester.enterText(emailField, 'Test');
await tester.pumpAndSettle();
await tester.pump(new Duration(seconds: 1));
await tester.enterText(passwordField, 'Test123');
await tester.pumpAndSettle();
await tester.pump(new Duration(seconds: 1));
await tester.tap(loginButton);
await tester.pumpAndSettle();
await tester.pump(new Duration(seconds: 3));
final dialog = find.byType(AlertDialog).first;
await tester.element(dialog);
await tester.pumpAndSettle();
await tester.pump(new Duration(seconds: 1));
final dialogButton = find.byType(FlatButton).first;
await tester.tap(dialogButton);
await tester.pumpAndSettle();
await tester.pump(new Duration(seconds: 2));
});
}
I have a file called utils.dart for functionality like this. In this case I use the following function which will basically poll until the finder is valid
// utils.dart
Future<void> pumpUntilFound(
WidgetTester tester,
Finder finder, {
Duration timeout = const Duration(seconds: 10),
}) async {
bool timerDone = false;
final timer = Timer(timeout, () => timerDone = true);
while (timerDone != true) {
await tester.pump();
final found = tester.any(finder);
if (found) {
timerDone = true;
}
}
timer.cancel();
}
You can also make it throw an exception if it times out, but the error messages aren't helpful, so I usually follow it up with an expect
It would look like
// my_test.dart
final fab = find.byKey(const ValueKey('fab'));
await pumpUntilFound(widgetTester, fab);
expect(fab, findsOneWidget);
Try wrapping like this:
testWidgets('test',
(WidgetTester tester) async {
await tester.runAsync(() async {
// test code here
});
});
If you use:
await tester.pumpAndSettle();
And then:
final widget = find.byKey(Key('whatever'));
It will find dinamically

mockObserver.didPush can not using type any in didPush

Hi I'm trying to verify mockObserver to check push to other screen. But when didPush(any,any), it's show err from type any.
I'm using mockito: ^5.0.13
(tester) async {
await tester.pumpWidget(widget);
await tester.tap(find.widgetWithText(TextButton, 'Join with us'));
await tester.pumpAndSettle();
verify(mockObserver.didPush(any, any));
});

Tap() in widget test showing warning in flutter

await widgetTester.tap(find.byType(ElevatedButton));
shows warning :
Maybe the widget is actually off-screen, or another widget is obscuring it, or the widget cannot receive pointer events.
Try this:
await widgetTester.ensureVisible(find.byType(ElevatedButton));
await widgetTester.pumpAndSettle();
await widgetTester.tap(find.byType(ElevatedButton));
I just replace await tester.pump(); by await tester.pumpAndSettle(); before tap
It works for me.
I suggest creating a safeTapBy function logic. This just ensures that the button is visible (keyboard showing, off screen etc). Here is an example using find.byKey:
Future safeTapByKey(WidgetTester tester, String key) async {
await tester.ensureVisible(find.byKey(Key(key)));
await tester.pumpAndSettle();
await tester.tap(find.byKey(Key(key)));
}

How to generate keyboard Event ENTER on a TextField in flutter_driver Tests

I am writing an integration test using flutter_driver for searching using text field I have to enter(Keyboard Event) after entering text, I cannot find any solution to generate keyboard event in flutter_Driver, is there any solution to this?
test('search tests, search a user by name', () async {
print('Waiting for join now button');
print('search test started');
await driver.clearTimeline();
print('pause for 5 sec');
await Future.delayed(Duration(seconds: 5));
print('Tapping search field');
await driver.waitFor(searchbar.searchFieldFinder);
await driver.tap(searchbar.searchFieldFinder);
print('enter user searching');
await driver.enterText('Test user');
//**Enter Keyboard Event here**
}, timeout: Timeout(Duration(minutes: 2)));
For keyboard event ENTER in integration tests you can use this code:
await tester.tap(find.byType(TextField));
await tester.enterText(find.byType(TextField), text);
await tester.testTextInput.receiveAction(TextInputAction.done);
await tester.pumpAndSettle(Duration(seconds: 1));
I don't know if it applies to driver tests, but in widget tests you can use the tester.sendKeyDownEvent() function like so:
await tester.sendKeyDownEvent(LogicalKeyboardKey.enter);
await tester.pumpAndSettle();
I would imagine that it would translate over to driver tests like this:
await driver.sendKeyDownEvent(LogicalKeyboardKey.enter);
await driver.pumpAndSettle();

Test onTap behaviour

I would like to test that when I am tapping a button, the user is routed to the next page. This works in the UI. I introduced the test but I get the following error:
The following TestFailure object was thrown running a test: Expected: exactly one matching node in the widget tree Actual: _WidgetTypeFinder:<zero widgets with type "MyNextView" (ignoring offstage widgets)> Which: means none were found but one was expected
What am I doing wrong?
testWidgets('Button tap routes to next page', (WidgetTester tester) async {
final button = createButton();
await tester.pumpWidget(button);
await tester.tap(find.byWidget(button));
expect(find.byType(MyNextView), findsOneWidget);
});
After >
await tester.tap(find.byWidget(button));
Put this line >
await tester.pumpAndSettle(); // Wait for screen to update
testWidgets('Button tap routes to next page', (WidgetTester tester) async {
final button = createButton();
await tester.pumpWidget(button);
await tester.tap(find.byWidget(button));
await tester.pumpAndSettle(); // Wait for screen to update
expect(find.byType(MyNextView), findsOneWidget);
});