date_picker_test.dart 10.6 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
2 3 4 5 6 7
// 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_localizations/flutter_localizations.dart';
import 'package:flutter_test/flutter_test.dart';
8
import 'package:intl/intl.dart' as intl;
9 10

void main() {
11 12 13
  late DateTime firstDate;
  late DateTime lastDate;
  late DateTime initialDate;
14 15

  setUp(() {
16
    firstDate = DateTime(2001);
17 18
    lastDate = DateTime(2031, DateTime.december, 31);
    initialDate = DateTime(2016, DateTime.january, 15);
19 20
  });

21
  group(CalendarDatePicker, () {
22
    final intl.NumberFormat arabicNumbers = intl.NumberFormat('0', 'ar');
23 24 25 26 27
    final Map<Locale, Map<String, dynamic>> testLocales = <Locale, Map<String, dynamic>>{
      // Tests the default.
      const Locale('en', 'US'): <String, dynamic>{
        'textDirection': TextDirection.ltr,
        'expectedDaysOfWeek': <String>['S', 'M', 'T', 'W', 'T', 'F', 'S'],
28
        'expectedDaysOfMonth': List<String>.generate(30, (int i) => '${i + 1}'),
29 30 31 32 33 34
        'expectedMonthYearHeader': 'September 2017',
      },
      // Tests a different first day of week.
      const Locale('ru', 'RU'): <String, dynamic>{
        'textDirection': TextDirection.ltr,
        'expectedDaysOfWeek': <String>['пн', 'вт', 'ср', 'чт', 'пт', 'сб', 'вс'],
35
        'expectedDaysOfMonth': List<String>.generate(30, (int i) => '${i + 1}'),
36 37
        'expectedMonthYearHeader': 'сентябрь 2017 г.',
      },
38 39 40
      const Locale('ro', 'RO'): <String, dynamic>{
        'textDirection': TextDirection.ltr,
        'expectedDaysOfWeek': <String>['D', 'L', 'M', 'M', 'J', 'V', 'S'],
41
        'expectedDaysOfMonth': List<String>.generate(30, (int i) => '${i + 1}'),
42 43
        'expectedMonthYearHeader': 'septembrie 2017',
      },
44 45 46 47
      // Tests RTL.
      const Locale('ar', 'AR'): <String, dynamic>{
        'textDirection': TextDirection.rtl,
        'expectedDaysOfWeek': <String>['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'],
48
        'expectedDaysOfMonth': List<String>.generate(30, (int i) => arabicNumbers.format(i + 1)),
49
        'expectedMonthYearHeader': 'سبتمبر ٢٠١٧',
50 51 52
      },
    };

53
    for (final Locale locale in testLocales.keys) {
54
      testWidgets('shows dates for $locale', (WidgetTester tester) async {
55 56 57 58
        final List<String> expectedDaysOfWeek = testLocales[locale]!['expectedDaysOfWeek'] as List<String>;
        final List<String> expectedDaysOfMonth = testLocales[locale]!['expectedDaysOfMonth'] as List<String>;
        final String expectedMonthYearHeader = testLocales[locale]!['expectedMonthYearHeader'] as String;
        final TextDirection textDirection = testLocales[locale]!['textDirection'] as TextDirection;
59
        final DateTime baseDate = DateTime(2017, 9, 27);
60

61 62
        await _pumpBoilerplate(tester, CalendarDatePicker(
          initialDate: baseDate,
63 64
          firstDate: baseDate.subtract(const Duration(days: 90)),
          lastDate: baseDate.add(const Duration(days: 90)),
65
          onDateChanged: (DateTime newValue) {},
66 67 68 69
        ), locale: locale, textDirection: textDirection);

        expect(find.text(expectedMonthYearHeader), findsOneWidget);

70
        for (final String dayOfWeek in expectedDaysOfWeek) {
71
          expect(find.text(dayOfWeek), findsWidgets);
72
        }
73

74
        Offset? previousCellOffset;
75
        for (final String dayOfMonth in expectedDaysOfMonth) {
76 77 78 79 80 81 82 83 84 85 86 87 88 89
          final Finder dayCell = find.descendant(of: find.byType(GridView), matching: find.text(dayOfMonth));
          expect(dayCell, findsOneWidget);

          // Check that cells are correctly positioned relative to each other,
          // taking text direction into account.
          final Offset offset = tester.getCenter(dayCell);
          if (previousCellOffset != null) {
            if (textDirection == TextDirection.ltr) {
              expect(offset.dx > previousCellOffset.dx && offset.dy == previousCellOffset.dy || offset.dy > previousCellOffset.dy, true);
            } else {
              expect(offset.dx < previousCellOffset.dx && offset.dy == previousCellOffset.dy || offset.dy > previousCellOffset.dy, true);
            }
          }
          previousCellOffset = offset;
90
        }
91 92 93 94 95
      });
    }
  });

  testWidgets('locale parameter overrides ambient locale', (WidgetTester tester) async {
96
    await tester.pumpWidget(MaterialApp(
97 98
      locale: const Locale('en', 'US'),
      supportedLocales: const <Locale>[
99 100
        Locale('en', 'US'),
        Locale('fr', 'CA'),
101 102
      ],
      localizationsDelegates: GlobalMaterialLocalizations.delegates,
103 104
      home: Material(
        child: Builder(
105
          builder: (BuildContext context) {
106
            return TextButton(
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
              onPressed: () async {
                await showDatePicker(
                  context: context,
                  initialDate: initialDate,
                  firstDate: firstDate,
                  lastDate: lastDate,
                  locale: const Locale('fr', 'CA'),
                );
              },
              child: const Text('X'),
            );
          },
        ),
      ),
    ));

    await tester.tap(find.text('X'));
    await tester.pumpAndSettle(const Duration(seconds: 1));

126
    final Element picker = tester.element(find.byType(CalendarDatePicker));
127
    expect(
128
      Localizations.localeOf(picker),
129 130 131 132
      const Locale('fr', 'CA'),
    );

    expect(
133
      Directionality.of(picker),
134 135 136 137 138 139 140
      TextDirection.ltr,
    );

    await tester.tap(find.text('ANNULER'));
  });

  testWidgets('textDirection parameter overrides ambient textDirection', (WidgetTester tester) async {
141
    await tester.pumpWidget(MaterialApp(
142
      locale: const Locale('en', 'US'),
143 144
      home: Material(
        child: Builder(
145
          builder: (BuildContext context) {
146
            return TextButton(
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
              onPressed: () async {
                await showDatePicker(
                  context: context,
                  initialDate: initialDate,
                  firstDate: firstDate,
                  lastDate: lastDate,
                  textDirection: TextDirection.rtl,
                );
              },
              child: const Text('X'),
            );
          },
        ),
      ),
    ));

    await tester.tap(find.text('X'));
    await tester.pumpAndSettle(const Duration(seconds: 1));

166
    final Element picker = tester.element(find.byType(CalendarDatePicker));
167
    expect(
168
      Directionality.of(picker),
169 170 171 172 173 174
      TextDirection.rtl,
    );

    await tester.tap(find.text('CANCEL'));
  });

Josh Soref's avatar
Josh Soref committed
175
  testWidgets('textDirection parameter takes precedence over locale parameter', (WidgetTester tester) async {
176
    await tester.pumpWidget(MaterialApp(
177 178
      locale: const Locale('en', 'US'),
      supportedLocales: const <Locale>[
179 180
        Locale('en', 'US'),
        Locale('fr', 'CA'),
181 182
      ],
      localizationsDelegates: GlobalMaterialLocalizations.delegates,
183 184
      home: Material(
        child: Builder(
185
          builder: (BuildContext context) {
186
            return TextButton(
187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206
              onPressed: () async {
                await showDatePicker(
                  context: context,
                  initialDate: initialDate,
                  firstDate: firstDate,
                  lastDate: lastDate,
                  locale: const Locale('fr', 'CA'),
                  textDirection: TextDirection.rtl,
                );
              },
              child: const Text('X'),
            );
          },
        ),
      ),
    ));

    await tester.tap(find.text('X'));
    await tester.pumpAndSettle(const Duration(seconds: 1));

207
    final Element picker = tester.element(find.byType(CalendarDatePicker));
208
    expect(
209
      Localizations.localeOf(picker),
210 211 212 213
      const Locale('fr', 'CA'),
    );

    expect(
214
      Directionality.of(picker),
215 216 217 218 219
      TextDirection.rtl,
    );

    await tester.tap(find.text('ANNULER'));
  });
220

221
  group("locale fonts don't overflow layout", () {
222 223 224 225 226 227 228
    // Test screen layouts in various locales to ensure the fonts used
    // don't overflow the layout

    // Common screen size roughly based on a Pixel 1
    const Size kCommonScreenSizePortrait = Size(1070, 1770);
    const Size kCommonScreenSizeLandscape = Size(1770, 1070);

229
    Future<void> showPicker(WidgetTester tester, Locale locale, Size size) async {
230
      tester.binding.window.physicalSizeTestValue = size;
231
      addTearDown(tester.binding.window.clearPhysicalSizeTestValue);
232
      tester.binding.window.devicePixelRatioTestValue = 1.0;
233
      addTearDown(tester.binding.window.clearDevicePixelRatioTestValue);
234 235 236 237 238 239 240
      await tester.pumpWidget(
        MaterialApp(
          home: Builder(
            builder: (BuildContext context) {
              return Localizations(
                locale: locale,
                delegates: GlobalMaterialLocalizations.delegates,
241
                child: TextButton(
242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262
                  child: const Text('X'),
                  onPressed: () {
                    showDatePicker(
                      context: context,
                      initialDate: initialDate,
                      firstDate: firstDate,
                      lastDate: lastDate,
                    );
                  },
                ),
              );
            },
          ),
        )
      );
      await tester.tap(find.text('X'));
      await tester.pumpAndSettle();
    }

    // Regression test for https://github.com/flutter/flutter/issues/20171
    testWidgets('common screen size - portrait - Chinese', (WidgetTester tester) async {
263
      await showPicker(tester, const Locale('zh', 'CN'), kCommonScreenSizePortrait);
264 265 266 267
      expect(tester.takeException(), isNull);
    });

    testWidgets('common screen size - landscape - Chinese', (WidgetTester tester) async {
268
      await showPicker(tester, const Locale('zh', 'CN'), kCommonScreenSizeLandscape);
269 270 271 272
      expect(tester.takeException(), isNull);
    });

    testWidgets('common screen size - portrait - Japanese', (WidgetTester tester) async {
273
      await showPicker(tester, const Locale('ja', 'JA'), kCommonScreenSizePortrait);
274 275 276 277
      expect(tester.takeException(), isNull);
    });

    testWidgets('common screen size - landscape - Japanese', (WidgetTester tester) async {
278
      await showPicker(tester, const Locale('ja', 'JA'), kCommonScreenSizeLandscape);
279 280 281 282
      expect(tester.takeException(), isNull);
    });
  });

283 284
}

285
Future<void> _pumpBoilerplate(
286 287 288
  WidgetTester tester,
  Widget child, {
  Locale locale = const Locale('en', 'US'),
289
  TextDirection textDirection = TextDirection.ltr,
290
}) async {
291 292 293 294 295 296 297 298 299 300
  await tester.pumpWidget(MaterialApp(
    home: Directionality(
      textDirection: TextDirection.ltr,
      child: Localizations(
        locale: locale,
        delegates: GlobalMaterialLocalizations.delegates,
        child: Material(
          child: child,
        ),
      ),
301 302 303
    ),
  ));
}