Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
changelog: |
- Adds tests for Indonesian, Korean and Thai locales tests in TimePicker i10n tests
version: patch
142 changes: 136 additions & 6 deletions packages/material_ui/test/l10n/time_picker_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,16 @@ void main() {
(Widget w) => '${w.runtimeType}' == '_DayPeriodControl',
);

// TODO(yjbanov): also test `HH.mm` (in_ID), `a h:mm` (ko_KR) and `HH:mm น.` (th_TH) when we have .arb files for them
final locales = <Locale>[
const Locale('en', 'US'), //'h:mm a'
const Locale('en', 'GB'), //'HH:mm'
const Locale('es', 'ES'), //'H:mm'
const Locale('fr', 'CA'), //'HH \'h\' mm'
const Locale('zh', 'ZH'), //'ah:mm'
const Locale('fa', 'IR'), //'H:mm' but RTL
const Locale('in', 'ID'), //'HH.mm'
const Locale('ko', 'KR'), //'a h:mm`
const Locale('th', 'TH'), //`HH:mm น.`
];

for (final locale in locales) {
Expand Down Expand Up @@ -90,6 +92,22 @@ void main() {
expect(hourLeftOffset, lessThan(stringFragmentLeftOffset));
expect(stringFragmentLeftOffset, lessThan(minuteLeftOffset));
expect(dayPeriodControlFinder, findsNothing);
} else if (locale == const Locale('in', 'ID')) {
// This is an LTR locale, it uses a dot separator instead of a standard colon.
expect(stringFragmentText.data, '.');
expect(hourLeftOffset, lessThan(stringFragmentLeftOffset));
expect(stringFragmentLeftOffset, lessThan(minuteLeftOffset));
expect(dayPeriodControlFinder, findsNothing);
} else if (locale == const Locale('ko', 'KR')) {
expect(stringFragmentText.data, ':');
expect(hourLeftOffset, lessThan(stringFragmentLeftOffset));
expect(stringFragmentLeftOffset, lessThan(minuteLeftOffset));
expect(dayPeriodControlFinder, findsOne);
} else if (locale == const Locale('th', 'TH')) {
expect(stringFragmentText.data, ':');
expect(hourLeftOffset, lessThan(stringFragmentLeftOffset));
expect(stringFragmentLeftOffset, lessThan(minuteLeftOffset));
expect(dayPeriodControlFinder, findsOne);
}
await tester.tapAt(Offset(center.dx, center.dy - 50.0));
await finishPicker(tester);
Expand Down Expand Up @@ -120,14 +138,16 @@ void main() {
(Widget w) => '${w.runtimeType}' == '_DayPeriodControl',
);

// TODO(yjbanov): also test `HH.mm` (in_ID), `a h:mm` (ko_KR) and `HH:mm น.` (th_TH) when we have .arb files for them
final locales = <Locale>[
const Locale('en', 'US'), //'h:mm a'
const Locale('en', 'GB'), //'HH:mm'
const Locale('es', 'ES'), //'H:mm'
const Locale('fr', 'CA'), //'HH \'h\' mm'
const Locale('zh', 'ZH'), //'ah:mm'
const Locale('fa', 'IR'), //'H:mm' but RTL
const Locale('in', 'ID'), //'HH.mm'
const Locale('ko', 'KR'), //'a h:mm`
const Locale('th', 'TH'), //`HH:mm น.`
];

for (final locale in locales) {
Expand Down Expand Up @@ -170,6 +190,22 @@ void main() {
expect(hourLeftOffset, lessThan(stringFragmentLeftOffset));
expect(stringFragmentLeftOffset, lessThan(minuteLeftOffset));
expect(dayPeriodControlFinder, findsNothing);
} else if (locale == const Locale('in', 'ID')) {
// This is an LTR locale, it uses a dot separator instead of a standard colon.
expect(stringFragmentText.data, '.');
expect(hourLeftOffset, lessThan(stringFragmentLeftOffset));
expect(stringFragmentLeftOffset, lessThan(minuteLeftOffset));
expect(dayPeriodControlFinder, findsNothing);
} else if (locale == const Locale('ko', 'KR')) {
expect(stringFragmentText.data, ':');
expect(hourLeftOffset, lessThan(stringFragmentLeftOffset));
expect(stringFragmentLeftOffset, lessThan(minuteLeftOffset));
expect(dayPeriodControlFinder, findsOne);
} else if (locale == const Locale('th', 'TH')) {
expect(stringFragmentText.data, ':');
expect(hourLeftOffset, lessThan(stringFragmentLeftOffset));
expect(stringFragmentLeftOffset, lessThan(minuteLeftOffset));
expect(dayPeriodControlFinder, findsOne);
}
await tester.tapAt(Offset(center.dx, center.dy - 50.0));
await finishPicker(tester);
Expand Down Expand Up @@ -200,14 +236,16 @@ void main() {
(Widget w) => '${w.runtimeType}' == '_DayPeriodControl',
);

// TODO(yjbanov): also test `HH.mm` (in_ID), `a h:mm` (ko_KR) and `HH:mm น.` (th_TH) when we have .arb files for them
final locales = <Locale>[
const Locale('en', 'US'), //'h:mm a'
const Locale('en', 'GB'), //'HH:mm'
const Locale('es', 'ES'), //'H:mm'
const Locale('fr', 'CA'), //'HH \'h\' mm'
const Locale('zh', 'ZH'), //'ah:mm'
const Locale('fa', 'IR'), //'H:mm' but RTL
const Locale('in', 'ID'), //'HH.mm'
const Locale('ko', 'KR'), //'a h:mm`
const Locale('th', 'TH'), //`HH:mm น.`
];

for (final locale in locales) {
Expand Down Expand Up @@ -260,6 +298,22 @@ void main() {
expect(hourLeftOffset, lessThan(stringFragmentLeftOffset));
expect(stringFragmentLeftOffset, lessThan(minuteLeftOffset));
expect(dayPeriodControlFinder, findsNothing);
} else if (locale == const Locale('in', 'ID')) {
// This is an LTR locale, it uses a dot separator instead of a standard colon.
expect(stringFragmentText.data, '.');
expect(hourLeftOffset, lessThan(stringFragmentLeftOffset));
expect(stringFragmentLeftOffset, lessThan(minuteLeftOffset));
expect(dayPeriodControlFinder, findsNothing);
} else if (locale == const Locale('ko', 'KR')) {
expect(stringFragmentText.data, ':');
expect(hourLeftOffset, lessThan(stringFragmentLeftOffset));
expect(stringFragmentLeftOffset, lessThan(minuteLeftOffset));
expect(dayPeriodControlFinder, findsOne);
} else if (locale == const Locale('th', 'TH')) {
expect(stringFragmentText.data, ':');
expect(hourLeftOffset, lessThan(stringFragmentLeftOffset));
expect(stringFragmentLeftOffset, lessThan(minuteLeftOffset));
expect(dayPeriodControlFinder, findsOne);
}
await tester.tapAt(Offset(center.dx, center.dy - 50.0));
await finishPicker(tester);
Expand Down Expand Up @@ -290,14 +344,16 @@ void main() {
(Widget w) => '${w.runtimeType}' == '_DayPeriodControl',
);

// TODO(yjbanov): also test `HH.mm` (in_ID), `a h:mm` (ko_KR) and `HH:mm น.` (th_TH) when we have .arb files for them
final locales = <Locale>[
const Locale('en', 'US'), //'h:mm a'
const Locale('en', 'GB'), //'HH:mm'
const Locale('es', 'ES'), //'H:mm'
const Locale('fr', 'CA'), //'HH \'h\' mm'
const Locale('zh', 'ZH'), //'ah:mm'
const Locale('fa', 'IR'), //'H:mm' but RTL
const Locale('in', 'ID'), //'HH.mm'
const Locale('ko', 'KR'), //'a h:mm`
const Locale('th', 'TH'), //`HH:mm น.`
];

for (final locale in locales) {
Expand Down Expand Up @@ -345,6 +401,38 @@ void main() {
expect(hourLeftOffset, lessThan(stringFragmentLeftOffset));
expect(stringFragmentLeftOffset, lessThan(minuteLeftOffset));
expect(dayPeriodControlFinder, findsNothing);
} else if (locale == const Locale('in', 'ID')) {
// This is an LTR locale, it uses a dot separator instead of a standard colon.
expect(stringFragmentText.data, '.');
expect(hourLeftOffset, lessThan(stringFragmentLeftOffset));
expect(stringFragmentLeftOffset, lessThan(minuteLeftOffset));
expect(dayPeriodControlFinder, findsNothing);
} else if (locale == const Locale('ko', 'KR')) {
expect(stringFragmentText.data, ':');
expect(hourLeftOffset, lessThan(stringFragmentLeftOffset));
expect(stringFragmentLeftOffset, lessThan(minuteLeftOffset));
expect(dayPeriodControlFinder, findsOne);
} else if (locale == const Locale('th', 'TH')) {
expect(stringFragmentText.data, ':');
expect(hourLeftOffset, lessThan(stringFragmentLeftOffset));
expect(stringFragmentLeftOffset, lessThan(minuteLeftOffset));
expect(dayPeriodControlFinder, findsOne);
} else if (locale == const Locale('in', 'ID')) {
// This is an LTR locale, it uses a dot separator instead of a standard colon.
expect(stringFragmentText.data, '.');
expect(hourLeftOffset, lessThan(stringFragmentLeftOffset));
expect(stringFragmentLeftOffset, lessThan(minuteLeftOffset));
expect(dayPeriodControlFinder, findsNothing);
} else if (locale == const Locale('ko', 'KR')) {
expect(stringFragmentText.data, ':');
expect(hourLeftOffset, lessThan(stringFragmentLeftOffset));
expect(stringFragmentLeftOffset, lessThan(minuteLeftOffset));
expect(dayPeriodControlFinder, findsOne);
} else if (locale == const Locale('th', 'TH')) {
expect(stringFragmentText.data, ':');
expect(hourLeftOffset, lessThan(stringFragmentLeftOffset));
expect(stringFragmentLeftOffset, lessThan(minuteLeftOffset));
expect(dayPeriodControlFinder, findsOne);
}
await tester.tapAt(Offset(center.dx, center.dy - 50.0));
await finishPicker(tester);
Expand All @@ -370,17 +458,22 @@ void main() {
)
.first;

// TODO(yjbanov): also test `HH.mm` (in_ID), `a h:mm` (ko_KR) and `HH:mm น.` (th_TH) when we have .arb files for them
final locales = <Locale>[
const Locale('en', 'US'), //'h:mm a'
const Locale('en', 'GB'), //'HH:mm'
const Locale('es', 'ES'), //'H:mm'
const Locale('fr', 'CA'), //'HH \'h\' mm'
const Locale('zh', 'ZH'), //'ah:mm'
const Locale('fa', 'IR'), //'H:mm' but RTL
const Locale('in', 'ID'), //'HH.mm'
const Locale('ko', 'KR'), //'a h:mm`
const Locale('th', 'TH'), //`HH:mm น.`
];

for (final locale in locales) {
await tester.pumpWidget(
const SizedBox(),
); // this is needed to reset the widget tree and prevent state/Directionality leaks between loop iterations.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so this is necessary because using const Locale('fa', 'IR'), //'H:mm' but RTL, the x button will go to the other side which causes the hit testing to not hit test on the specified widget in the next iterations, and the following warning will appear:


Warning: A call to tap() with finder "Found 1 widget with text "X": [
  Text("X", dependencies: [DefaultSelectionStyle, DefaultTextStyle, MediaQuery]),
]" derived an Offset (Offset(400.0, 300.0)) that would not hit test on the specified widget.
Maybe the widget is actually off-screen, or another widget is obscuring it, or the widget cannot receive pointer events.
The finder corresponds to this RenderBox: RenderParagraph#4315d relayoutBoundary=up13
The hit test result at that offset is: HitTestResult(RenderPointerListener#53575@Offset(124.0, 108.0), RenderSemanticsGestureHandler#e488f@Offset(124.0, 108.0), RenderPointerListener#10b37@Offset(124.0, 108.0), _RenderScrollSemantics#5bd15@Offset(124.0, 108.0), RenderClipRect#b4b84@Offset(124.0, 108.0), _RenderSingleChildViewport#1766f@Offset(124.0, 108.0), RenderIgnorePointer#c357a@Offset(124.0, 108.0), RenderSemanticsAnnotations#c6d85@Offset(124.0, 108.0), RenderPointerListener#f33fd@Offset(124.0, 108.0), RenderSemanticsGestureHandler#d85b7@Offset(124.0, 108.0), RenderPointerListener#8d4cb@Offset(124.0, 108.0), _RenderScrollSemantics#65c57@Offset(124.0, 108.0), RenderClipRect#c7224@Offset(124.0, 108.0), _RenderLayoutBuilder#e9b18@Offset(124.0, 108.0), RenderPadding#0920f@Offset(148.0, 132.0), _RenderInkFeatures#8ee06@Offset(148.0, 132.0), RenderCustomPaint#ec0c5@Offset(148.0, 132.0), RenderPhysicalShape#68177@Offset(148.0, 132.0), RenderConstrainedBox#a622e@Offset(148.0, 132.0), RenderPositionedBox#f0706@Offset(384.0, 300.0), RenderPadding#1abd3@Offset(400.0, 300.0), RenderSemanticsAnnotations#e5163@Offset(400.0, 300.0), RenderPadding#526c7@Offset(400.0, 300.0), RenderSemanticsAnnotations#29bdc@Offset(400.0, 300.0), RenderPadding#3d688@Offset(400.0, 300.0), RenderSemanticsAnnotations#b694c@Offset(400.0, 300.0), RenderRepaintBoundary#880f7@Offset(400.0, 300.0), RenderIgnorePointer#e2358@Offset(400.0, 300.0), RenderAnimatedOpacity#a7c38@Offset(400.0, 300.0), RenderRepaintBoundary#3935d@Offset(400.0, 300.0), RenderSemanticsAnnotations#516f0@Offset(400.0, 300.0), RenderOffstage#b471e@Offset(400.0, 300.0), RenderSemanticsAnnotations#f5af2@Offset(400.0, 300.0), _RenderTheater#4bd23@Offset(400.0, 300.0), RenderAbsorbPointer#4c1cf@Offset(400.0, 300.0), RenderPointerListener#1cee2@Offset(400.0, 300.0), RenderSemanticsAnnotations#2e617@Offset(400.0, 300.0), RenderCustomPaint#f4bcb@Offset(400.0, 300.0), RenderSemanticsAnnotations#60900@Offset(400.0, 300.0), RenderSemanticsAnnotations#d4d73@Offset(400.0, 300.0), RenderSemanticsAnnotations#d4f6b@Offset(400.0, 300.0), RenderTapRegionSurface#2ba9e@Offset(400.0, 300.0), RenderSemanticsAnnotations#3169a@Offset(400.0, 300.0), RenderSemanticsAnnotations#2c6d3@Offset(400.0, 300.0), HitTestEntry<HitTestTarget>#4b488(_ReusableRenderView#5b5d7), HitTestEntry<HitTestTarget>#ca173(<AutomatedTestWidgetsFlutterBinding>))

it is also possible to repro the bug with moving const Locale('fa', 'IR'), //'H:mm' but RTL as the first locale to see the same warning, not sure if this is a known bug or the expected behavior.

await tester.pumpWidget(
_TimePickerLauncher(
onChanged: (TimeOfDay? time) {},
Expand Down Expand Up @@ -430,6 +523,22 @@ void main() {
expect(hourLeftOffset, lessThan(stringFragmentLeftOffset));
expect(stringFragmentLeftOffset, lessThan(minuteLeftOffset));
expect(dayPeriodControlFinder, findsNothing);
} else if (locale == const Locale('in', 'ID')) {
// This is an LTR locale, it uses a dot separator instead of a standard colon.
expect(stringFragmentText.data, '.');
expect(hourLeftOffset, lessThan(stringFragmentLeftOffset));
expect(stringFragmentLeftOffset, lessThan(minuteLeftOffset));
expect(dayPeriodControlFinder, findsNothing);
} else if (locale == const Locale('ko', 'KR')) {
expect(stringFragmentText.data, ':');
expect(hourLeftOffset, lessThan(stringFragmentLeftOffset));
expect(stringFragmentLeftOffset, lessThan(minuteLeftOffset));
expect(dayPeriodControlFinder, findsOne);
} else if (locale == const Locale('th', 'TH')) {
expect(stringFragmentText.data, ':');
expect(hourLeftOffset, lessThan(stringFragmentLeftOffset));
expect(stringFragmentLeftOffset, lessThan(minuteLeftOffset));
expect(dayPeriodControlFinder, findsOne);
}
await finishPicker(tester);
expect(tester.takeException(), isNot(throwsFlutterError));
Expand All @@ -455,17 +564,22 @@ void main() {
)
.first;

// TODO(yjbanov): also test `HH.mm` (in_ID), `a h:mm` (ko_KR) and `HH:mm น.` (th_TH) when we have .arb files for them
final locales = <Locale>[
const Locale('en', 'US'), //'h:mm a'
const Locale('en', 'GB'), //'HH:mm'
const Locale('es', 'ES'), //'H:mm'
const Locale('fr', 'CA'), //'HH \'h\' mm'
const Locale('zh', 'ZH'), //'ah:mm'
const Locale('fa', 'IR'), //'H:mm' but RTL
const Locale('in', 'ID'), //'HH.mm'
const Locale('ko', 'KR'), //'a h:mm`
const Locale('th', 'TH'), //`HH:mm น.`
];

for (final locale in locales) {
await tester.pumpWidget(
const SizedBox(),
); // this is needed to reset the widget tree and prevent state/Directionality leaks between loop iterations.
await tester.pumpWidget(
_TimePickerLauncher(
onChanged: (TimeOfDay? time) {},
Expand Down Expand Up @@ -514,6 +628,22 @@ void main() {
expect(hourLeftOffset, lessThan(stringFragmentLeftOffset));
expect(stringFragmentLeftOffset, lessThan(minuteLeftOffset));
expect(dayPeriodControlFinder, findsNothing);
} else if (locale == const Locale('in', 'ID')) {
// This is an LTR locale, it uses a dot separator instead of a standard colon.
expect(stringFragmentText.data, '.');
expect(hourLeftOffset, lessThan(stringFragmentLeftOffset));
expect(stringFragmentLeftOffset, lessThan(minuteLeftOffset));
expect(dayPeriodControlFinder, findsNothing);
} else if (locale == const Locale('ko', 'KR')) {
expect(stringFragmentText.data, ':');
expect(hourLeftOffset, lessThan(stringFragmentLeftOffset));
expect(stringFragmentLeftOffset, lessThan(minuteLeftOffset));
expect(dayPeriodControlFinder, findsOne);
} else if (locale == const Locale('th', 'TH')) {
expect(stringFragmentText.data, ':');
expect(hourLeftOffset, lessThan(stringFragmentLeftOffset));
expect(stringFragmentLeftOffset, lessThan(minuteLeftOffset));
expect(dayPeriodControlFinder, findsOne);
}
await finishPicker(tester);
expect(tester.takeException(), isNot(throwsFlutterError));
Expand Down
Loading