Flutter Driver select date from date picker
Asked Answered
P

2

7

I would like to select a date through date picker as part of a flutter driver test. however, I can't seem to figure out exactly how I would do this?

I've tried using a find.textandfind.bySemanticsLabel, and have tried but have had no luck thus far.

my code :

Future<void> executeStep() async {
  await FlutterDriverUtils.waitForFlutter(world.driver);
  NewOrderForm newOrderForm = NewOrderForm(world.driver);
  await newOrderForm.setFieldKontrak();
  //Open Date Picker
  await newOrderForm.setDateKontrak();
  //Select date 24
  await driver.waitFor(find.bySemanticsLabel('24'));
  await driver.tap(find.text('24'),timeout: Duration(seconds: 15));
  await driver.waitFor(find.text('OK'));
  await driver.tap(find.text('OK'));
  await newOrderForm.setProyekField();

}

Screen capture :

enter image description here

Pren answered 8/10, 2019 at 7:48 Comment(0)
U
10

I ran a sample test that selects a date from datepicker and it worked well. Below is what I did:

main.dart has a simple RaisedButton upon clicking on which DatePicker opens:

return Scaffold(
      body: Center(
        child: RaisedButton(
          onPressed: () {
            _showDatePicker();
          },
          child: Text('Click')
        )
      )
    );

void _showDatePicker() async {
    DateTime picked = await showDatePicker(
        context: context,
        initialDate: new DateTime.now(),
        firstDate: new DateTime(2019),
        lastDate: new DateTime(2021)
    );
    if(picked != null) setState(() => _value = picked.toString());
  }

Below is the flutter driver test that first identifies the RaisedButton -> taps on it -> finds the date to be selected -> taps on it -> taps OK

test('datepicker test', () async {
        final dateButton = find.text('Click');
        await driver.waitFor(dateButton);
        await driver.tap(dateButton);
        await driver.tap(find.text('15'));
        await driver.tap(find.text('OK'));
        print('October 15 selected and tapped OK');
      });

Test result:

enter image description here

In the code you provided, you may try below snippet, ie, tap on 24 and directly tap on OK instead of telling driver to wait for OK button to find.

await driver.tap(find.text('24'));
  await driver.tap(find.text('OK'));

Hope this helps you to resolve the issue.

Underwent answered 11/10, 2019 at 13:48 Comment(2)
note that this does not work well with the Time picker. Tapping a text will result in another time being selected. Flutter needs to make integration testing more developer friendly when using those type of flutter widgets. It's a shame because 80% of testing is fast on flutter but the last 20% is really annoying to do.Interstellar
Here I'm again, apparently. It also won't work with DateRangePicker as multiple days with the same text can be shown on the same pageInterstellar
M
0

This might get tricky due to datePicker implementation and design changes that go in time.

Sometimes Material Design specification can change and the underlying Widgets for datePicker can be modified too.

We want to extract the current Widget so we can manipulate them.

The current date picker looks like this:

enter image description here

We clearly see that we can try couple approaches here for setting dates:

  • either click the edit button and go to the input mode
  • try < > arrows to change months -1/+1
  • select a day
  • click April 2022 and pick a year

For the current state of knowledge the datePicker code looks like this: date picker test source code.

What is interesting for us are those parts of code (Finders) that Flutter team uses to manipulate this datePicker. Couple of them are:

final Finder nextMonthIcon = find.byWidgetPredicate((Widget w) => w is IconButton && (w.tooltip?.startsWith('Next month') ?? false));
final Finder previousMonthIcon = find.byWidgetPredicate((Widget w) => w is IconButton && (w.tooltip?.startsWith('Previous month') ?? false));
final Finder switchToInputIcon = find.byIcon(Icons.edit);

So the Flutter team uses those Finders to manipulate this date picker and we should find an approach to set the whole date (day, month and year) in a predictable and reliable manner.

Warning! Do remember that your instrumented test can be in a different language so you might localise your 'Next month' or 'Previous month'. Or you can search for those texts in the test debug console with the help of: find.byWidgetPredicate((Widget w) => w is IconButton). You will get couple results and you can browse them.

Another warning! Using month change arrows for setting the date (or setting only a part of the date i.e. just a day) is dangerous as the widget shows the current date. If you were to run this test a day/month/year after, you can land into wrong results, especially when your tests have code like: Make two taps on "<" arrow. Hence, I am suggesting switching to the input mode and hardcoding the date so you get predictable test runs in the future.

To sum it up, here's the whole example. I am using edit mode and hardcoding the date.

Full code for PL locale:

// switch to edit mode. Using Icons.edit did not work
// hence I manually found it with the help of tooltip text
final Finder switchToInputIcon = find.byWidgetPredicate((Widget w) =>
        w is IconButton && (w.tooltip == 'Przełącz na wpisywanie'));
await tester.tap(switchToInputIcon);
await tester.pumpAndSettle();

// I know that the widget starts with the current date
// so I obtain the EditableText with the help of current year value finder
var currentDt = DateTime.now();
final EditableText input = tester.widget<EditableText>(find.textContaining(currentDt.year.toString()).first);

// I am hardcoding the date.
// This is for PL locale. For example for US you would need something like: 06/21/2024
input.controller.text = "21.06.2024";
await tester.pumpAndSettle();

// Confirm the date dialog and close it 
// "OK" might be different in other languages
await tester.tap(find.text('OK'));
await tester.pumpAndSettle();

Mho answered 22/6 at 13:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.