date_picker_test.dart 14.2 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1 2 3 4 5 6
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
7
import 'package:intl/intl.dart';
Ian Hickson's avatar
Ian Hickson committed
8

9 10
import 'feedback_tester.dart';

Ian Hickson's avatar
Ian Hickson committed
11
void main() {
12 13 14
  DateTime firstDate;
  DateTime lastDate;
  DateTime initialDate;
15
  SelectableDayPredicate selectableDayPredicate;
16
  DatePickerMode initialDatePickerMode;
17 18 19 20 21

  setUp(() {
    firstDate = new DateTime(2001, DateTime.JANUARY, 1);
    lastDate = new DateTime(2031, DateTime.DECEMBER, 31);
    initialDate = new DateTime(2016, DateTime.JANUARY, 15);
22 23
    selectableDayPredicate = null;
    initialDatePickerMode = null;
24 25
  });

26
  testWidgets('tap-select a day', (WidgetTester tester) async {
27
    final Key _datePickerKey = new UniqueKey();
Ian Hickson's avatar
Ian Hickson committed
28 29 30
    DateTime _selectedDate = new DateTime(2016, DateTime.JULY, 26);

    await tester.pumpWidget(
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
      new MaterialApp(
        home: new StatefulBuilder(
          builder: (BuildContext context, StateSetter setState) {
            return new Container(
              width: 400.0,
              child: new SingleChildScrollView(
                child: new Material(
                  child: new MonthPicker(
                    firstDate: new DateTime(0),
                    lastDate: new DateTime(9999),
                    key: _datePickerKey,
                    selectedDate: _selectedDate,
                    onChanged: (DateTime value) {
                      setState(() {
                        _selectedDate = value;
                      });
                    },
48
                  ),
49 50 51 52 53 54
                ),
              ),
            );
          },
        ),
      )
Ian Hickson's avatar
Ian Hickson committed
55
    );
56

57
    expect(_selectedDate, equals(new DateTime(2016, DateTime.JULY, 26)));
Ian Hickson's avatar
Ian Hickson committed
58

59
    await tester.tapAt(const Offset(50.0, 100.0));
60
    await tester.pumpAndSettle();
Ian Hickson's avatar
Ian Hickson committed
61 62
    await tester.pump(const Duration(seconds: 2));

63 64
    await tester.tap(find.text('1'));
    await tester.pumpAndSettle();
Ian Hickson's avatar
Ian Hickson committed
65 66
    expect(_selectedDate, equals(new DateTime(2016, DateTime.JULY, 1)));

67 68
    await tester.tap(find.byTooltip('Next month'));
    await tester.pumpAndSettle();
Ian Hickson's avatar
Ian Hickson committed
69 70
    expect(_selectedDate, equals(new DateTime(2016, DateTime.JULY, 1)));

71 72
    await tester.tap(find.text('5'));
    await tester.pumpAndSettle();
Ian Hickson's avatar
Ian Hickson committed
73 74
    expect(_selectedDate, equals(new DateTime(2016, DateTime.AUGUST, 5)));

75 76
    await tester.drag(find.byKey(_datePickerKey), const Offset(-400.0, 0.0));
    await tester.pumpAndSettle();
Ian Hickson's avatar
Ian Hickson committed
77 78
    expect(_selectedDate, equals(new DateTime(2016, DateTime.AUGUST, 5)));

79 80
    await tester.tap(find.text('25'));
    await tester.pumpAndSettle();
Ian Hickson's avatar
Ian Hickson committed
81 82
    expect(_selectedDate, equals(new DateTime(2016, DateTime.SEPTEMBER, 25)));

83 84
    await tester.drag(find.byKey(_datePickerKey), const Offset(800.0, 0.0));
    await tester.pumpAndSettle();
Ian Hickson's avatar
Ian Hickson committed
85 86
    expect(_selectedDate, equals(new DateTime(2016, DateTime.SEPTEMBER, 25)));

87 88
    await tester.tap(find.text('17'));
    await tester.pumpAndSettle();
Ian Hickson's avatar
Ian Hickson committed
89 90 91 92 93
    expect(_selectedDate, equals(new DateTime(2016, DateTime.AUGUST, 17)));
  });

  testWidgets('render picker with intrinsic dimensions', (WidgetTester tester) async {
    await tester.pumpWidget(
94 95 96 97 98 99 100 101 102 103 104 105
      new MaterialApp(
        home: new StatefulBuilder(
          builder: (BuildContext context, StateSetter setState) {
            return new IntrinsicWidth(
              child: new IntrinsicHeight(
                child: new Material(
                  child: new SingleChildScrollView(
                    child: new MonthPicker(
                      firstDate: new DateTime(0),
                      lastDate: new DateTime(9999),
                      onChanged: (DateTime value) { },
                      selectedDate: new DateTime(2000, DateTime.JANUARY, 1),
106 107
                    ),
                  ),
108 109 110 111 112
                ),
              ),
            );
          },
        ),
113
      ),
Ian Hickson's avatar
Ian Hickson committed
114 115 116 117
    );
    await tester.pump(const Duration(seconds: 5));
  });

118 119 120 121 122 123 124 125 126 127
  Future<Null> preparePicker(WidgetTester tester, Future<Null> callback(Future<DateTime> date)) async {
    BuildContext buttonContext;
    await tester.pumpWidget(new MaterialApp(
      home: new Material(
        child: new Builder(
          builder: (BuildContext context) {
            return new RaisedButton(
              onPressed: () {
                buttonContext = context;
              },
128
              child: const Text('Go'),
129 130 131 132 133 134 135 136 137
            );
          },
        ),
      ),
    ));

    await tester.tap(find.text('Go'));
    expect(buttonContext, isNotNull);

138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
    final Future<DateTime> date = initialDatePickerMode == null
        // Exercise the argument default for initialDatePickerMode.
        ?
            showDatePicker(
              context: buttonContext,
              initialDate: initialDate,
              firstDate: firstDate,
              lastDate: lastDate,
              selectableDayPredicate: selectableDayPredicate,
            )
        :
            showDatePicker(
              context: buttonContext,
              initialDate: initialDate,
              firstDate: firstDate,
              lastDate: lastDate,
              selectableDayPredicate: selectableDayPredicate,
              initialDatePickerMode: initialDatePickerMode,
            );
157

158
    await tester.pumpAndSettle(const Duration(seconds: 1));
159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186
    await callback(date);
  }

  testWidgets('Initial date is the default', (WidgetTester tester) async {
    await preparePicker(tester, (Future<DateTime> date) async {
      await tester.tap(find.text('OK'));
      expect(await date, equals(new DateTime(2016, DateTime.JANUARY, 15)));
    });
  });

  testWidgets('Can cancel', (WidgetTester tester) async {
    await preparePicker(tester, (Future<DateTime> date) async {
      await tester.tap(find.text('CANCEL'));
      expect(await date, isNull);
    });
  });

  testWidgets('Can select a day', (WidgetTester tester) async {
    await preparePicker(tester, (Future<DateTime> date) async {
      await tester.tap(find.text('12'));
      await tester.tap(find.text('OK'));
      expect(await date, equals(new DateTime(2016, DateTime.JANUARY, 12)));
    });
  });

  testWidgets('Can select a month', (WidgetTester tester) async {
    await preparePicker(tester, (Future<DateTime> date) async {
      await tester.tap(find.byTooltip('Previous month'));
187
      await tester.pumpAndSettle(const Duration(seconds: 1));
188 189 190 191 192 193 194 195 196 197
      await tester.tap(find.text('25'));
      await tester.tap(find.text('OK'));
      expect(await date, equals(new DateTime(2015, DateTime.DECEMBER, 25)));
    });
  });

  testWidgets('Can select a year', (WidgetTester tester) async {
    await preparePicker(tester, (Future<DateTime> date) async {
      await tester.tap(find.text('2016'));
      await tester.pump();
198
      await tester.tap(find.text('2018'));
199
      await tester.tap(find.text('OK'));
200
      expect(await date, equals(new DateTime(2018, DateTime.JANUARY, 15)));
201 202 203 204 205 206 207
    });
  });

  testWidgets('Can select a year and then a day', (WidgetTester tester) async {
    await preparePicker(tester, (Future<DateTime> date) async {
      await tester.tap(find.text('2016'));
      await tester.pump();
208
      await tester.tap(find.text('2017'));
209
      await tester.pump();
210
      final String dayLabel = new DateFormat('E, MMM\u00a0d').format(new DateTime(2017, DateTime.JANUARY, 15));
211 212 213 214
      await tester.tap(find.text(dayLabel));
      await tester.pump();
      await tester.tap(find.text('19'));
      await tester.tap(find.text('OK'));
215 216 217 218 219 220 221 222 223 224 225 226
      expect(await date, equals(new DateTime(2017, DateTime.JANUARY, 19)));
    });
  });

  testWidgets('Current year is initially visible in year picker', (WidgetTester tester) async {
    initialDate = new DateTime(2000);
    firstDate = new DateTime(1900);
    lastDate = new DateTime(2100);
    await preparePicker(tester, (Future<DateTime> date) async {
      await tester.tap(find.text('2000'));
      await tester.pump();
      expect(find.text('2000'), findsNWidgets(2));
227 228
    });
  });
229 230 231 232 233 234 235 236 237

  testWidgets('Cannot select a day outside bounds', (WidgetTester tester) async {
    initialDate = new DateTime(2017, DateTime.JANUARY, 15);
    firstDate = initialDate;
    lastDate = initialDate;
    await preparePicker(tester, (Future<DateTime> date) async {
      await tester.tap(find.text('10')); // Earlier than firstDate. Should be ignored.
      await tester.tap(find.text('20')); // Later than lastDate. Should be ignored.
      await tester.tap(find.text('OK'));
238
      // We should still be on the initial date.
239 240 241 242 243 244 245 246 247 248
      expect(await date, equals(initialDate));
    });
  });

  testWidgets('Cannot select a month past last date', (WidgetTester tester) async {
    initialDate = new DateTime(2017, DateTime.JANUARY, 15);
    firstDate = initialDate;
    lastDate = new DateTime(2017, DateTime.FEBRUARY, 20);
    await preparePicker(tester, (Future<DateTime> date) async {
      await tester.tap(find.byTooltip('Next month'));
249
      await tester.pumpAndSettle(const Duration(seconds: 1));
250 251
      // Shouldn't be possible to keep going into March.
      await tester.tap(find.byTooltip('Next month'));
252
      await tester.pumpAndSettle(const Duration(seconds: 1));
253 254 255 256 257 258 259 260 261 262 263 264 265 266 267
      // We're still in February
      await tester.tap(find.text('20'));
      // Days outside bound for new month pages also disabled.
      await tester.tap(find.text('25'));
      await tester.tap(find.text('OK'));
      expect(await date, equals(new DateTime(2017, DateTime.FEBRUARY, 20)));
    });
  });

  testWidgets('Cannot select a month before first date', (WidgetTester tester) async {
    initialDate = new DateTime(2017, DateTime.JANUARY, 15);
    firstDate = new DateTime(2016, DateTime.DECEMBER, 10);
    lastDate = initialDate;
    await preparePicker(tester, (Future<DateTime> date) async {
      await tester.tap(find.byTooltip('Previous month'));
268
      await tester.pumpAndSettle(const Duration(seconds: 1));
269 270
      // Shouldn't be possible to keep going into November.
      await tester.tap(find.byTooltip('Previous month'));
271
      await tester.pumpAndSettle(const Duration(seconds: 1));
272 273 274 275 276 277 278 279
      // We're still in December
      await tester.tap(find.text('10'));
      // Days outside bound for new month pages also disabled.
      await tester.tap(find.text('5'));
      await tester.tap(find.text('OK'));
      expect(await date, equals(new DateTime(2016, DateTime.DECEMBER, 10)));
    });
  });
280 281 282 283 284 285 286 287 288 289 290 291 292 293

  testWidgets('Only predicate days are selectable', (WidgetTester tester) async {
    initialDate = new DateTime(2017, DateTime.JANUARY, 16);
    firstDate = new DateTime(2017, DateTime.JANUARY, 10);
    lastDate = new DateTime(2017, DateTime.JANUARY, 20);
    selectableDayPredicate = (DateTime day) => day.day.isEven;
    await preparePicker(tester, (Future<DateTime> date) async {
      await tester.tap(find.text('10')); // Even, works.
      await tester.tap(find.text('13')); // Odd, doesn't work.
      await tester.tap(find.text('17')); // Odd, doesn't work.
      await tester.tap(find.text('OK'));
      expect(await date, equals(new DateTime(2017, DateTime.JANUARY, 10)));
    });
  });
294

295 296 297 298 299 300 301 302 303 304 305 306 307
  testWidgets('Can select initial date picker mode', (WidgetTester tester) async {
    initialDate = new DateTime(2014, DateTime.JANUARY, 15);
    initialDatePickerMode = DatePickerMode.year;
    await preparePicker(tester, (Future<DateTime> date) async {
      await tester.pump();
      // 2018 wouldn't be available if the year picker wasn't showing.
      // The initial current year is 2014.
      await tester.tap(find.text('2018'));
      await tester.tap(find.text('OK'));
      expect(await date, equals(new DateTime(2018, DateTime.JANUARY, 15)));
    });
  });

308 309
  group('haptic feedback', () {
    const Duration kHapticFeedbackInterval = const Duration(milliseconds: 10);
310
    FeedbackTester feedback;
311 312

    setUp(() {
313
      feedback = new FeedbackTester();
314 315 316 317 318 319
      initialDate = new DateTime(2017, DateTime.JANUARY, 16);
      firstDate = new DateTime(2017, DateTime.JANUARY, 10);
      lastDate = new DateTime(2018, DateTime.JANUARY, 20);
      selectableDayPredicate = (DateTime date) => date.day.isEven;
    });

320 321 322 323
    tearDown(() {
      feedback?.dispose();
    });

324 325 326 327
    testWidgets('tap-select date vibrates', (WidgetTester tester) async {
      await preparePicker(tester, (Future<DateTime> date) async {
        await tester.tap(find.text('10'));
        await tester.pump(kHapticFeedbackInterval);
328
        expect(feedback.hapticCount, 1);
329 330
        await tester.tap(find.text('12'));
        await tester.pump(kHapticFeedbackInterval);
331
        expect(feedback.hapticCount, 2);
332 333
        await tester.tap(find.text('14'));
        await tester.pump(kHapticFeedbackInterval);
334
        expect(feedback.hapticCount, 3);
335 336 337 338 339 340 341
      });
    });

    testWidgets('tap-select unselectable date does not vibrate', (WidgetTester tester) async {
      await preparePicker(tester, (Future<DateTime> date) async {
        await tester.tap(find.text('11'));
        await tester.pump(kHapticFeedbackInterval);
342
        expect(feedback.hapticCount, 0);
343 344
        await tester.tap(find.text('13'));
        await tester.pump(kHapticFeedbackInterval);
345
        expect(feedback.hapticCount, 0);
346 347
        await tester.tap(find.text('15'));
        await tester.pump(kHapticFeedbackInterval);
348
        expect(feedback.hapticCount, 0);
349 350 351 352 353 354 355
      });
    });

    testWidgets('mode, year change vibrates', (WidgetTester tester) async {
      await preparePicker(tester, (Future<DateTime> date) async {
        await tester.tap(find.text('2017'));
        await tester.pump(kHapticFeedbackInterval);
356
        expect(feedback.hapticCount, 1);
357 358
        await tester.tap(find.text('2018'));
        await tester.pump(kHapticFeedbackInterval);
359
        expect(feedback.hapticCount, 2);
360 361
      });
    });
362

363
  });
364 365 366 367 368 369 370 371 372

  test('days in month', () {
    expect(DayPicker.getDaysInMonth(2017, 10), 31);
    expect(DayPicker.getDaysInMonth(2017, 6), 30);
    expect(DayPicker.getDaysInMonth(2017, 2), 28);
    expect(DayPicker.getDaysInMonth(2016, 2), 29);
    expect(DayPicker.getDaysInMonth(2000, 2), 29);
    expect(DayPicker.getDaysInMonth(1900, 2), 28);
  });
373 374 375 376 377 378 379 380 381 382 383 384 385

  testWidgets('month header tap', (WidgetTester tester) async {
    selectableDayPredicate = null;
    await preparePicker(tester, (Future<DateTime> date) async {
      // Switch into the year selector.
      await tester.tap(find.text('January 2016'));
      await tester.pump();
      expect(find.text('2020'), isNotNull);

      await tester.tap(find.text('CANCEL'));
      expect(await date, isNull);
    });
  });
Ian Hickson's avatar
Ian Hickson committed
386
}