366 lines
13 KiB
Dart
366 lines
13 KiB
Dart
// Copyright 2014 The Flutter Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
@Tags(<String>['reduced-test-set'])
|
|
library;
|
|
|
|
import 'package:flutter/cupertino.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
|
|
void main() {
|
|
final basicOffset = Offset(
|
|
CupertinoMagnifier.kDefaultSize.width / 2,
|
|
CupertinoMagnifier.kDefaultSize.height - CupertinoMagnifier.kMagnifierAboveFocalPoint,
|
|
);
|
|
const reasonableTextField = Rect.fromLTRB(0, 100, 200, 200);
|
|
final magnifierController = MagnifierController();
|
|
|
|
// Make sure that your gesture in magnifierInfo is within the line in magnifierInfo,
|
|
// or else the magnifier status will stay hidden and this will not complete.
|
|
Future<void> showCupertinoMagnifier(
|
|
BuildContext context,
|
|
WidgetTester tester,
|
|
ValueNotifier<MagnifierInfo> magnifierInfo,
|
|
) async {
|
|
final Future<void> magnifierShown = magnifierController.show(
|
|
context: context,
|
|
builder: (BuildContext context) =>
|
|
CupertinoTextMagnifier(controller: magnifierController, magnifierInfo: magnifierInfo),
|
|
);
|
|
await tester.pump();
|
|
await tester.pump(const Duration(seconds: 2));
|
|
await magnifierShown;
|
|
}
|
|
|
|
tearDown(() async {
|
|
magnifierController.removeFromOverlay();
|
|
});
|
|
|
|
group('CupertinoTextEditingMagnifier', () {
|
|
testWidgets('Magnifier border color inherits from parent CupertinoTheme', (
|
|
WidgetTester tester,
|
|
) async {
|
|
final Key fakeTextFieldKey = UniqueKey();
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: SizedBox.square(
|
|
key: fakeTextFieldKey,
|
|
dimension: 10,
|
|
child: CupertinoTheme(
|
|
data: const CupertinoThemeData(primaryColor: Colors.green),
|
|
child: Builder(
|
|
builder: (BuildContext context) {
|
|
return const Placeholder();
|
|
},
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
final BuildContext context = tester.element(find.byType(Placeholder));
|
|
|
|
// Magnifier should be positioned directly over the red square.
|
|
final tapPointRenderBox = tester.firstRenderObject(find.byKey(fakeTextFieldKey)) as RenderBox;
|
|
final Rect fakeTextFieldRect =
|
|
tapPointRenderBox.localToGlobal(Offset.zero) & tapPointRenderBox.size;
|
|
|
|
final magnifier = ValueNotifier<MagnifierInfo>(
|
|
MagnifierInfo(
|
|
currentLineBoundaries: fakeTextFieldRect,
|
|
fieldBounds: fakeTextFieldRect,
|
|
caretRect: fakeTextFieldRect,
|
|
// The tap position is dragBelow units below the text field.
|
|
globalGesturePosition: fakeTextFieldRect.center,
|
|
),
|
|
);
|
|
addTearDown(magnifier.dispose);
|
|
|
|
await showCupertinoMagnifier(context, tester, magnifier);
|
|
|
|
// Magnifier border color should inherit from CupertinoTheme.of(context).primaryColor.
|
|
final Color magnifierBorderColor = tester
|
|
.widget<CupertinoMagnifier>(find.byType(CupertinoMagnifier))
|
|
.borderSide
|
|
.color;
|
|
expect(magnifierBorderColor, equals(Colors.green));
|
|
});
|
|
|
|
group('position', () {
|
|
Offset getMagnifierPosition(WidgetTester tester) {
|
|
final AnimatedPositioned animatedPositioned = tester.firstWidget(
|
|
find.byType(AnimatedPositioned),
|
|
);
|
|
return Offset(animatedPositioned.left ?? 0, animatedPositioned.top ?? 0);
|
|
}
|
|
|
|
testWidgets('should be at gesture position if does not violate any positioning rules', (
|
|
WidgetTester tester,
|
|
) async {
|
|
final Key fakeTextFieldKey = UniqueKey();
|
|
final Key outerKey = UniqueKey();
|
|
|
|
await tester.pumpWidget(
|
|
ColoredBox(
|
|
key: outerKey,
|
|
color: const Color.fromARGB(255, 0, 255, 179),
|
|
child: MaterialApp(
|
|
home: Center(
|
|
child: Container(
|
|
key: fakeTextFieldKey,
|
|
width: 10,
|
|
height: 10,
|
|
color: Colors.red,
|
|
child: const Placeholder(),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
final BuildContext context = tester.element(find.byType(Placeholder));
|
|
|
|
// Magnifier should be positioned directly over the red square.
|
|
final tapPointRenderBox =
|
|
tester.firstRenderObject(find.byKey(fakeTextFieldKey)) as RenderBox;
|
|
final Rect fakeTextFieldRect =
|
|
tapPointRenderBox.localToGlobal(Offset.zero) & tapPointRenderBox.size;
|
|
|
|
final magnifier = ValueNotifier<MagnifierInfo>(
|
|
MagnifierInfo(
|
|
currentLineBoundaries: fakeTextFieldRect,
|
|
fieldBounds: fakeTextFieldRect,
|
|
caretRect: fakeTextFieldRect,
|
|
// The tap position is dragBelow units below the text field.
|
|
globalGesturePosition: fakeTextFieldRect.center,
|
|
),
|
|
);
|
|
addTearDown(magnifier.dispose);
|
|
|
|
await showCupertinoMagnifier(context, tester, magnifier);
|
|
|
|
// Should show two red squares; original, and one in the magnifier,
|
|
// directly ontop of one another.
|
|
await expectLater(
|
|
find.byKey(outerKey),
|
|
matchesGoldenFile('cupertino_magnifier.position.default.png'),
|
|
);
|
|
});
|
|
|
|
testWidgets('should never horizontally be outside of Screen Padding', (
|
|
WidgetTester tester,
|
|
) async {
|
|
await tester.pumpWidget(
|
|
const MaterialApp(color: Color.fromARGB(7, 0, 129, 90), home: Placeholder()),
|
|
);
|
|
|
|
final BuildContext context = tester.firstElement(find.byType(Placeholder));
|
|
|
|
final magnifierInfo = ValueNotifier<MagnifierInfo>(
|
|
MagnifierInfo(
|
|
currentLineBoundaries: reasonableTextField,
|
|
fieldBounds: reasonableTextField,
|
|
caretRect: reasonableTextField,
|
|
// The tap position is far out of the right side of the app.
|
|
globalGesturePosition: Offset(MediaQuery.sizeOf(context).width + 100, 0),
|
|
),
|
|
);
|
|
addTearDown(magnifierInfo.dispose);
|
|
await showCupertinoMagnifier(context, tester, magnifierInfo);
|
|
|
|
// Should be less than the right edge, since we have padding.
|
|
expect(getMagnifierPosition(tester).dx, lessThan(MediaQuery.sizeOf(context).width));
|
|
});
|
|
|
|
testWidgets('should have some vertical drag', (WidgetTester tester) async {
|
|
final double dragPositionBelowTextField = reasonableTextField.center.dy + 30;
|
|
|
|
await tester.pumpWidget(
|
|
const MaterialApp(color: Color.fromARGB(7, 0, 129, 90), home: Placeholder()),
|
|
);
|
|
|
|
final BuildContext context = tester.firstElement(find.byType(Placeholder));
|
|
|
|
final magnifierInfo = ValueNotifier<MagnifierInfo>(
|
|
MagnifierInfo(
|
|
currentLineBoundaries: reasonableTextField,
|
|
fieldBounds: reasonableTextField,
|
|
caretRect: reasonableTextField,
|
|
// The tap position is dragBelow units below the text field.
|
|
globalGesturePosition: Offset(
|
|
MediaQuery.sizeOf(context).width / 2,
|
|
dragPositionBelowTextField,
|
|
),
|
|
),
|
|
);
|
|
addTearDown(magnifierInfo.dispose);
|
|
await showCupertinoMagnifier(context, tester, magnifierInfo);
|
|
|
|
// The magnifier Y should be greater than the text field, since we "dragged" it down.
|
|
expect(
|
|
getMagnifierPosition(tester).dy + basicOffset.dy,
|
|
greaterThan(reasonableTextField.center.dy),
|
|
);
|
|
expect(
|
|
getMagnifierPosition(tester).dy + basicOffset.dy,
|
|
lessThan(dragPositionBelowTextField),
|
|
);
|
|
});
|
|
});
|
|
|
|
group('status', () {
|
|
testWidgets('should hide if gesture is far below the text field', (
|
|
WidgetTester tester,
|
|
) async {
|
|
await tester.pumpWidget(
|
|
const MaterialApp(color: Color.fromARGB(7, 0, 129, 90), home: Placeholder()),
|
|
);
|
|
|
|
final BuildContext context = tester.firstElement(find.byType(Placeholder));
|
|
|
|
final magnifierInfo = ValueNotifier<MagnifierInfo>(
|
|
MagnifierInfo(
|
|
currentLineBoundaries: reasonableTextField,
|
|
fieldBounds: reasonableTextField,
|
|
caretRect: reasonableTextField,
|
|
// The tap position is dragBelow units below the text field.
|
|
globalGesturePosition: Offset(
|
|
MediaQuery.sizeOf(context).width / 2,
|
|
reasonableTextField.top,
|
|
),
|
|
),
|
|
);
|
|
addTearDown(magnifierInfo.dispose);
|
|
|
|
// Show the magnifier initially, so that we get it in a not hidden state.
|
|
await showCupertinoMagnifier(context, tester, magnifierInfo);
|
|
|
|
// Move the gesture to one that should hide it.
|
|
magnifierInfo.value = MagnifierInfo(
|
|
currentLineBoundaries: reasonableTextField,
|
|
fieldBounds: reasonableTextField,
|
|
caretRect: reasonableTextField,
|
|
globalGesturePosition: magnifierInfo.value.globalGesturePosition + const Offset(0, 100),
|
|
);
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(magnifierController.shown, false);
|
|
expect(magnifierController.overlayEntry, isNotNull);
|
|
});
|
|
|
|
testWidgets('should re-show if gesture moves back up', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
const MaterialApp(color: Color.fromARGB(7, 0, 129, 90), home: Placeholder()),
|
|
);
|
|
|
|
final BuildContext context = tester.firstElement(find.byType(Placeholder));
|
|
|
|
final magnifierInfo = ValueNotifier<MagnifierInfo>(
|
|
MagnifierInfo(
|
|
currentLineBoundaries: reasonableTextField,
|
|
fieldBounds: reasonableTextField,
|
|
caretRect: reasonableTextField,
|
|
// The tap position is dragBelow units below the text field.
|
|
globalGesturePosition: Offset(
|
|
MediaQuery.sizeOf(context).width / 2,
|
|
reasonableTextField.top,
|
|
),
|
|
),
|
|
);
|
|
addTearDown(magnifierInfo.dispose);
|
|
|
|
// Show the magnifier initially, so that we get it in a not hidden state.
|
|
await showCupertinoMagnifier(context, tester, magnifierInfo);
|
|
|
|
// Move the gesture to one that should hide it.
|
|
magnifierInfo.value = MagnifierInfo(
|
|
currentLineBoundaries: reasonableTextField,
|
|
fieldBounds: reasonableTextField,
|
|
caretRect: reasonableTextField,
|
|
globalGesturePosition: magnifierInfo.value.globalGesturePosition + const Offset(0, 100),
|
|
);
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(magnifierController.shown, false);
|
|
expect(magnifierController.overlayEntry, isNotNull);
|
|
|
|
// Return the gesture to one that shows it.
|
|
magnifierInfo.value = MagnifierInfo(
|
|
currentLineBoundaries: reasonableTextField,
|
|
fieldBounds: reasonableTextField,
|
|
caretRect: reasonableTextField,
|
|
globalGesturePosition: Offset(
|
|
MediaQuery.sizeOf(context).width / 2,
|
|
reasonableTextField.top,
|
|
),
|
|
);
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(magnifierController.shown, true);
|
|
expect(magnifierController.overlayEntry, isNotNull);
|
|
});
|
|
});
|
|
|
|
group('magnificationScale', () {
|
|
testWidgets('Throws assertion error when magnificationScale is zero', (
|
|
WidgetTester tester,
|
|
) async {
|
|
expect(
|
|
() => MaterialApp(home: Scaffold(body: CupertinoMagnifier(magnificationScale: 0))),
|
|
throwsAssertionError,
|
|
);
|
|
});
|
|
|
|
testWidgets('Throws assertion error when magnificationScale is negative', (
|
|
WidgetTester tester,
|
|
) async {
|
|
expect(
|
|
() => MaterialApp(home: Scaffold(body: CupertinoMagnifier(magnificationScale: -1))),
|
|
throwsAssertionError,
|
|
);
|
|
});
|
|
|
|
testWidgets('CupertinoMagnifier magnification scale defaults to 1', (
|
|
WidgetTester tester,
|
|
) async {
|
|
await tester.pumpWidget(const MaterialApp(home: Scaffold(body: CupertinoMagnifier())));
|
|
|
|
expect(
|
|
tester.widget(find.byType(RawMagnifier)),
|
|
isA<RawMagnifier>().having(
|
|
(RawMagnifier t) => t.magnificationScale,
|
|
'magnificationScale',
|
|
1,
|
|
),
|
|
);
|
|
});
|
|
|
|
testWidgets('Magnification scale argument is passed to the RawMagnifier', (
|
|
WidgetTester tester,
|
|
) async {
|
|
await tester.pumpWidget(
|
|
const MaterialApp(home: Scaffold(body: CupertinoMagnifier(magnificationScale: 2))),
|
|
);
|
|
|
|
expect(
|
|
tester.widget(find.byType(RawMagnifier)),
|
|
isA<RawMagnifier>().having(
|
|
(RawMagnifier t) => t.magnificationScale,
|
|
'magnificationScale',
|
|
2,
|
|
),
|
|
);
|
|
});
|
|
});
|
|
});
|
|
|
|
testWidgets('CupertinoMagnifier does not crash at zero area', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
const CupertinoApp(
|
|
home: Center(child: SizedBox.shrink(child: CupertinoMagnifier(magnificationScale: 2))),
|
|
),
|
|
);
|
|
expect(tester.getSize(find.byType(CupertinoMagnifier)), Size.zero);
|
|
});
|
|
}
|