677 lines
23 KiB
Dart
677 lines
23 KiB
Dart
// Copyright 2015 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 'dart:async';
|
|
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter/rendering.dart';
|
|
import 'package:flutter/services.dart';
|
|
|
|
import 'mock_text_input.dart';
|
|
|
|
class MockClipboard {
|
|
Object _clipboardData = <String, dynamic>{
|
|
'text': null
|
|
};
|
|
|
|
Future<dynamic> handleJSONMessage(dynamic message) async {
|
|
final String method = message['method'];
|
|
final List<dynamic> args= message['args'];
|
|
switch (method) {
|
|
case 'Clipboard.getData':
|
|
return _clipboardData;
|
|
case 'Clipboard.setData':
|
|
_clipboardData = args[0];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void main() {
|
|
MockTextInput mockTextInput = new MockTextInput()..register();
|
|
MockClipboard mockClipboard = new MockClipboard();
|
|
PlatformMessages.setMockJSONMessageHandler('flutter/platform', mockClipboard.handleJSONMessage);
|
|
|
|
const String kThreeLines =
|
|
'First line of text is here abcdef ghijkl mnopqrst. ' +
|
|
'Second line of text goes until abcdef ghijkl mnopq. ' +
|
|
'Third line of stuff keeps going until abcdef ghijk. ';
|
|
const String kFourLines =
|
|
kThreeLines +
|
|
'Fourth line won\'t display and ends at abcdef ghi. ';
|
|
|
|
void updateEditingState(TextEditingState state) {
|
|
mockTextInput.updateEditingState(state);
|
|
}
|
|
|
|
void enterText(String text) {
|
|
mockTextInput.enterText(text);
|
|
}
|
|
|
|
// Returns the first RenderEditable.
|
|
RenderEditable findRenderEditable(WidgetTester tester) {
|
|
RenderObject root = tester.renderObject(find.byType(RawInput));
|
|
expect(root, isNotNull);
|
|
|
|
RenderEditable renderEditable;
|
|
void recursiveFinder(RenderObject child) {
|
|
if (child is RenderEditable) {
|
|
renderEditable = child;
|
|
return;
|
|
}
|
|
child.visitChildren(recursiveFinder);
|
|
}
|
|
root.visitChildren(recursiveFinder);
|
|
expect(renderEditable, isNotNull);
|
|
return renderEditable;
|
|
}
|
|
|
|
Point textOffsetToPosition(WidgetTester tester, int offset) {
|
|
RenderEditable renderEditable = findRenderEditable(tester);
|
|
List<TextSelectionPoint> endpoints = renderEditable.getEndpointsForSelection(
|
|
new TextSelection.collapsed(offset: offset));
|
|
expect(endpoints.length, 1);
|
|
return endpoints[0].point + new Offset(0.0, -2.0);
|
|
}
|
|
|
|
testWidgets('Editable text has consistent size', (WidgetTester tester) async {
|
|
GlobalKey inputKey = new GlobalKey();
|
|
InputValue inputValue = InputValue.empty;
|
|
|
|
Widget builder() {
|
|
return new Center(
|
|
child: new Material(
|
|
child: new Input(
|
|
value: inputValue,
|
|
key: inputKey,
|
|
hintText: 'Placeholder',
|
|
onChanged: (InputValue value) { inputValue = value; }
|
|
)
|
|
)
|
|
);
|
|
}
|
|
|
|
await tester.pumpWidget(builder());
|
|
|
|
RenderBox findInputBox() => tester.renderObject(find.byKey(inputKey));
|
|
|
|
RenderBox inputBox = findInputBox();
|
|
Size emptyInputSize = inputBox.size;
|
|
|
|
Future<Null> checkText(String testValue) async {
|
|
enterText(testValue);
|
|
await tester.idle();
|
|
|
|
// Check that the onChanged event handler fired.
|
|
expect(inputValue.text, equals(testValue));
|
|
|
|
return await tester.pumpWidget(builder());
|
|
}
|
|
|
|
await checkText(' ');
|
|
expect(findInputBox(), equals(inputBox));
|
|
expect(inputBox.size, equals(emptyInputSize));
|
|
|
|
await checkText('Test');
|
|
expect(findInputBox(), equals(inputBox));
|
|
expect(inputBox.size, equals(emptyInputSize));
|
|
});
|
|
|
|
testWidgets('Cursor blinks', (WidgetTester tester) async {
|
|
GlobalKey inputKey = new GlobalKey();
|
|
|
|
Widget builder() {
|
|
return new Center(
|
|
child: new Material(
|
|
child: new Input(
|
|
key: inputKey,
|
|
hintText: 'Placeholder'
|
|
)
|
|
)
|
|
);
|
|
}
|
|
|
|
await tester.pumpWidget(builder());
|
|
|
|
RawInputState editableText = tester.state(find.byType(RawInput));
|
|
|
|
// Check that the cursor visibility toggles after each blink interval.
|
|
Future<Null> checkCursorToggle() async {
|
|
bool initialShowCursor = editableText.cursorCurrentlyVisible;
|
|
await tester.pump(editableText.cursorBlinkInterval);
|
|
expect(editableText.cursorCurrentlyVisible, equals(!initialShowCursor));
|
|
await tester.pump(editableText.cursorBlinkInterval);
|
|
expect(editableText.cursorCurrentlyVisible, equals(initialShowCursor));
|
|
await tester.pump(editableText.cursorBlinkInterval ~/ 10);
|
|
expect(editableText.cursorCurrentlyVisible, equals(initialShowCursor));
|
|
await tester.pump(editableText.cursorBlinkInterval);
|
|
expect(editableText.cursorCurrentlyVisible, equals(!initialShowCursor));
|
|
await tester.pump(editableText.cursorBlinkInterval);
|
|
expect(editableText.cursorCurrentlyVisible, equals(initialShowCursor));
|
|
}
|
|
|
|
await checkCursorToggle();
|
|
|
|
// Try the test again with a nonempty EditableText.
|
|
updateEditingState(new TextEditingState(
|
|
text: 'X',
|
|
selectionBase: 1,
|
|
selectionExtent: 1,
|
|
));
|
|
await checkCursorToggle();
|
|
});
|
|
|
|
testWidgets('hideText control test', (WidgetTester tester) async {
|
|
GlobalKey inputKey = new GlobalKey();
|
|
|
|
Widget builder() {
|
|
return new Center(
|
|
child: new Material(
|
|
child: new Input(
|
|
key: inputKey,
|
|
hideText: true,
|
|
hintText: 'Placeholder'
|
|
)
|
|
)
|
|
);
|
|
}
|
|
|
|
await tester.pumpWidget(builder());
|
|
|
|
const String testValue = 'ABC';
|
|
updateEditingState(new TextEditingState(
|
|
text: testValue,
|
|
selectionBase: testValue.length,
|
|
selectionExtent: testValue.length,
|
|
));
|
|
|
|
await tester.pump();
|
|
});
|
|
|
|
testWidgets('Can long press to select', (WidgetTester tester) async {
|
|
GlobalKey inputKey = new GlobalKey();
|
|
InputValue inputValue = InputValue.empty;
|
|
|
|
Widget builder() {
|
|
return new Overlay(
|
|
initialEntries: <OverlayEntry>[
|
|
new OverlayEntry(
|
|
builder: (BuildContext context) {
|
|
return new Center(
|
|
child: new Material(
|
|
child: new Input(
|
|
value: inputValue,
|
|
key: inputKey,
|
|
onChanged: (InputValue value) { inputValue = value; }
|
|
)
|
|
)
|
|
);
|
|
}
|
|
)
|
|
]
|
|
);
|
|
}
|
|
|
|
await tester.pumpWidget(builder());
|
|
|
|
String testValue = 'abc def ghi';
|
|
enterText(testValue);
|
|
await tester.idle();
|
|
expect(inputValue.text, testValue);
|
|
|
|
await tester.pumpWidget(builder());
|
|
|
|
expect(inputValue.selection.isCollapsed, true);
|
|
|
|
// Long press the 'e' to select 'def'.
|
|
Point ePos = textOffsetToPosition(tester, testValue.indexOf('e'));
|
|
TestGesture gesture = await tester.startGesture(ePos, pointer: 7);
|
|
await tester.pump(const Duration(seconds: 2));
|
|
await gesture.up();
|
|
await tester.pump();
|
|
|
|
// 'def' is selected.
|
|
expect(inputValue.selection.baseOffset, testValue.indexOf('d'));
|
|
expect(inputValue.selection.extentOffset, testValue.indexOf('f')+1);
|
|
});
|
|
|
|
testWidgets('Can drag handles to change selection', (WidgetTester tester) async {
|
|
GlobalKey inputKey = new GlobalKey();
|
|
InputValue inputValue = InputValue.empty;
|
|
|
|
Widget builder() {
|
|
return new Overlay(
|
|
initialEntries: <OverlayEntry>[
|
|
new OverlayEntry(
|
|
builder: (BuildContext context) {
|
|
return new Center(
|
|
child: new Material(
|
|
child: new Input(
|
|
value: inputValue,
|
|
key: inputKey,
|
|
onChanged: (InputValue value) { inputValue = value; }
|
|
)
|
|
)
|
|
);
|
|
}
|
|
)
|
|
]
|
|
);
|
|
}
|
|
|
|
await tester.pumpWidget(builder());
|
|
|
|
String testValue = 'abc def ghi';
|
|
enterText(testValue);
|
|
await tester.idle();
|
|
|
|
await tester.pumpWidget(builder());
|
|
|
|
// Long press the 'e' to select 'def'.
|
|
Point ePos = textOffsetToPosition(tester, testValue.indexOf('e'));
|
|
TestGesture gesture = await tester.startGesture(ePos, pointer: 7);
|
|
await tester.pump(const Duration(seconds: 2));
|
|
await gesture.up();
|
|
await tester.pump();
|
|
|
|
TextSelection selection = inputValue.selection;
|
|
|
|
RenderEditable renderEditable = findRenderEditable(tester);
|
|
List<TextSelectionPoint> endpoints = renderEditable.getEndpointsForSelection(
|
|
selection);
|
|
expect(endpoints.length, 2);
|
|
|
|
// Drag the right handle 2 letters to the right.
|
|
// Note: use a small offset because the endpoint is on the very corner
|
|
// of the handle.
|
|
Point handlePos = endpoints[1].point + new Offset(1.0, 1.0);
|
|
Point newHandlePos = textOffsetToPosition(tester, selection.extentOffset+2);
|
|
gesture = await tester.startGesture(handlePos, pointer: 7);
|
|
await tester.pump();
|
|
await gesture.moveTo(newHandlePos);
|
|
await tester.pump();
|
|
await gesture.up();
|
|
await tester.pumpWidget(builder());
|
|
|
|
expect(inputValue.selection.baseOffset, selection.baseOffset);
|
|
expect(inputValue.selection.extentOffset, selection.extentOffset+2);
|
|
|
|
// Drag the left handle 2 letters to the left.
|
|
handlePos = endpoints[0].point + new Offset(-1.0, 1.0);
|
|
newHandlePos = textOffsetToPosition(tester, selection.baseOffset-2);
|
|
gesture = await tester.startGesture(handlePos, pointer: 7);
|
|
await tester.pump();
|
|
await gesture.moveTo(newHandlePos);
|
|
await tester.pump();
|
|
await gesture.up();
|
|
await tester.pumpWidget(builder());
|
|
|
|
expect(inputValue.selection.baseOffset, selection.baseOffset-2);
|
|
expect(inputValue.selection.extentOffset, selection.extentOffset+2);
|
|
});
|
|
|
|
testWidgets('Can use selection toolbar', (WidgetTester tester) async {
|
|
GlobalKey inputKey = new GlobalKey();
|
|
InputValue inputValue = InputValue.empty;
|
|
|
|
Widget builder() {
|
|
return new Overlay(
|
|
initialEntries: <OverlayEntry>[
|
|
new OverlayEntry(
|
|
builder: (BuildContext context) {
|
|
return new Center(
|
|
child: new Material(
|
|
child: new Input(
|
|
value: inputValue,
|
|
key: inputKey,
|
|
onChanged: (InputValue value) { inputValue = value; }
|
|
)
|
|
)
|
|
);
|
|
}
|
|
)
|
|
]
|
|
);
|
|
}
|
|
|
|
await tester.pumpWidget(builder());
|
|
|
|
String testValue = 'abc def ghi';
|
|
enterText(testValue);
|
|
await tester.idle();
|
|
await tester.pumpWidget(builder());
|
|
|
|
// Tap the selection handle to bring up the "paste / select all" menu.
|
|
await tester.tapAt(textOffsetToPosition(tester, testValue.indexOf('e')));
|
|
await tester.pumpWidget(builder());
|
|
RenderEditable renderEditable = findRenderEditable(tester);
|
|
List<TextSelectionPoint> endpoints = renderEditable.getEndpointsForSelection(
|
|
inputValue.selection);
|
|
await tester.tapAt(endpoints[0].point + new Offset(1.0, 1.0));
|
|
await tester.pumpWidget(builder());
|
|
|
|
// SELECT ALL should select all the text.
|
|
await tester.tap(find.text('SELECT ALL'));
|
|
await tester.pumpWidget(builder());
|
|
expect(inputValue.selection.baseOffset, 0);
|
|
expect(inputValue.selection.extentOffset, testValue.length);
|
|
|
|
// COPY should reset the selection.
|
|
await tester.tap(find.text('COPY'));
|
|
await tester.pumpWidget(builder());
|
|
expect(inputValue.selection.isCollapsed, true);
|
|
|
|
// Tap again to bring back the menu.
|
|
await tester.tapAt(textOffsetToPosition(tester, testValue.indexOf('e')));
|
|
await tester.pumpWidget(builder());
|
|
renderEditable = findRenderEditable(tester);
|
|
endpoints = renderEditable.getEndpointsForSelection(inputValue.selection);
|
|
await tester.tapAt(endpoints[0].point + new Offset(1.0, 1.0));
|
|
await tester.pumpWidget(builder());
|
|
|
|
// PASTE right before the 'e'.
|
|
await tester.tap(find.text('PASTE'));
|
|
await tester.pumpWidget(builder());
|
|
expect(inputValue.text, 'abc d${testValue}ef ghi');
|
|
});
|
|
|
|
testWidgets('Selection toolbar fades in', (WidgetTester tester) async {
|
|
GlobalKey inputKey = new GlobalKey();
|
|
InputValue inputValue = InputValue.empty;
|
|
|
|
Widget builder() {
|
|
return new Overlay(
|
|
initialEntries: <OverlayEntry>[
|
|
new OverlayEntry(
|
|
builder: (BuildContext context) {
|
|
return new Center(
|
|
child: new Material(
|
|
child: new Input(
|
|
value: inputValue,
|
|
key: inputKey,
|
|
onChanged: (InputValue value) { inputValue = value; }
|
|
)
|
|
)
|
|
);
|
|
}
|
|
)
|
|
]
|
|
);
|
|
}
|
|
|
|
await tester.pumpWidget(builder());
|
|
|
|
String testValue = 'abc def ghi';
|
|
enterText(testValue);
|
|
await tester.idle();
|
|
await tester.pumpWidget(builder());
|
|
|
|
// Tap the selection handle to bring up the "paste / select all" menu.
|
|
await tester.tapAt(textOffsetToPosition(tester, testValue.indexOf('e')));
|
|
await tester.pumpWidget(builder());
|
|
RenderEditable renderEditable = findRenderEditable(tester);
|
|
List<TextSelectionPoint> endpoints = renderEditable.getEndpointsForSelection(
|
|
inputValue.selection);
|
|
await tester.tapAt(endpoints[0].point + new Offset(1.0, 1.0));
|
|
await tester.pumpWidget(builder());
|
|
|
|
// Toolbar should fade in. Starting at 0% opacity.
|
|
Element target = tester.element(find.text('SELECT ALL'));
|
|
Opacity opacity = target.ancestorWidgetOfExactType(Opacity);
|
|
expect(opacity, isNotNull);
|
|
expect(opacity.opacity, equals(0.0));
|
|
|
|
// Still fading in.
|
|
await tester.pump(const Duration(milliseconds: 50));
|
|
opacity = target.ancestorWidgetOfExactType(Opacity);
|
|
expect(opacity.opacity, greaterThan(0.0));
|
|
expect(opacity.opacity, lessThan(1.0));
|
|
|
|
// End the test here to ensure the animation is properly disposed of.
|
|
});
|
|
|
|
testWidgets('Multiline text will wrap up to maxLines', (WidgetTester tester) async {
|
|
GlobalKey inputKey = new GlobalKey();
|
|
InputValue inputValue = InputValue.empty;
|
|
|
|
Widget builder(int maxLines) {
|
|
return new Center(
|
|
child: new Material(
|
|
child: new Input(
|
|
value: inputValue,
|
|
key: inputKey,
|
|
style: const TextStyle(color: Colors.black, fontSize: 34.0),
|
|
maxLines: maxLines,
|
|
hintText: 'Placeholder',
|
|
onChanged: (InputValue value) { inputValue = value; }
|
|
)
|
|
)
|
|
);
|
|
}
|
|
|
|
await tester.pumpWidget(builder(3));
|
|
|
|
RenderBox findInputBox() => tester.renderObject(find.byKey(inputKey));
|
|
|
|
RenderBox inputBox = findInputBox();
|
|
Size emptyInputSize = inputBox.size;
|
|
|
|
enterText('No wrapping here.');
|
|
await tester.idle();
|
|
await tester.pumpWidget(builder(3));
|
|
expect(findInputBox(), equals(inputBox));
|
|
expect(inputBox.size, equals(emptyInputSize));
|
|
|
|
enterText(kThreeLines);
|
|
await tester.idle();
|
|
await tester.pumpWidget(builder(3));
|
|
expect(findInputBox(), equals(inputBox));
|
|
expect(inputBox.size, greaterThan(emptyInputSize));
|
|
|
|
Size threeLineInputSize = inputBox.size;
|
|
|
|
// An extra line won't increase the size because we max at 3.
|
|
enterText(kFourLines);
|
|
await tester.idle();
|
|
await tester.pumpWidget(builder(3));
|
|
expect(findInputBox(), equals(inputBox));
|
|
expect(inputBox.size, threeLineInputSize);
|
|
|
|
// But now it will.
|
|
enterText(kFourLines);
|
|
await tester.idle();
|
|
await tester.pumpWidget(builder(4));
|
|
expect(findInputBox(), equals(inputBox));
|
|
expect(inputBox.size, greaterThan(threeLineInputSize));
|
|
});
|
|
|
|
testWidgets('Can drag handles to change selection in multiline', (WidgetTester tester) async {
|
|
GlobalKey inputKey = new GlobalKey();
|
|
InputValue inputValue = InputValue.empty;
|
|
|
|
Widget builder() {
|
|
return new Overlay(
|
|
initialEntries: <OverlayEntry>[
|
|
new OverlayEntry(
|
|
builder: (BuildContext context) {
|
|
return new Center(
|
|
child: new Material(
|
|
child: new Input(
|
|
value: inputValue,
|
|
key: inputKey,
|
|
style: const TextStyle(color: Colors.black, fontSize: 34.0),
|
|
maxLines: 3,
|
|
onChanged: (InputValue value) { inputValue = value; }
|
|
)
|
|
)
|
|
);
|
|
}
|
|
)
|
|
]
|
|
);
|
|
}
|
|
|
|
await tester.pumpWidget(builder());
|
|
|
|
String testValue = kThreeLines;
|
|
String cutValue = 'First line of stuff keeps going until abcdef ghijk. ';
|
|
enterText(testValue);
|
|
await tester.idle();
|
|
|
|
await tester.pumpWidget(builder());
|
|
|
|
// Check that the text spans multiple lines.
|
|
Point firstPos = textOffsetToPosition(tester, testValue.indexOf('First'));
|
|
Point secondPos = textOffsetToPosition(tester, testValue.indexOf('Second'));
|
|
Point thirdPos = textOffsetToPosition(tester, testValue.indexOf('Third'));
|
|
expect(firstPos.x, secondPos.x);
|
|
expect(firstPos.x, thirdPos.x);
|
|
expect(firstPos.y, lessThan(secondPos.y));
|
|
expect(secondPos.y, lessThan(thirdPos.y));
|
|
|
|
// Long press the 'n' in 'until' to select the word.
|
|
Point untilPos = textOffsetToPosition(tester, testValue.indexOf('until')+1);
|
|
TestGesture gesture = await tester.startGesture(untilPos, pointer: 7);
|
|
await tester.pump(const Duration(seconds: 2));
|
|
await gesture.up();
|
|
await tester.pump();
|
|
|
|
expect(inputValue.selection.baseOffset, 76);
|
|
expect(inputValue.selection.extentOffset, 81);
|
|
|
|
RenderEditable renderEditable = findRenderEditable(tester);
|
|
List<TextSelectionPoint> endpoints = renderEditable.getEndpointsForSelection(
|
|
inputValue.selection);
|
|
expect(endpoints.length, 2);
|
|
|
|
// Drag the right handle to the third line, just after 'Third'.
|
|
Point handlePos = endpoints[1].point + new Offset(1.0, 1.0);
|
|
Point newHandlePos = textOffsetToPosition(tester, testValue.indexOf('Third') + 5);
|
|
gesture = await tester.startGesture(handlePos, pointer: 7);
|
|
await tester.pump();
|
|
await gesture.moveTo(newHandlePos);
|
|
await tester.pump();
|
|
await gesture.up();
|
|
await tester.pumpWidget(builder());
|
|
|
|
expect(inputValue.selection.baseOffset, 76);
|
|
expect(inputValue.selection.extentOffset, 108);
|
|
|
|
// Drag the left handle to the first line, just after 'First'.
|
|
handlePos = endpoints[0].point + new Offset(-1.0, 1.0);
|
|
newHandlePos = textOffsetToPosition(tester, testValue.indexOf('First') + 5);
|
|
gesture = await tester.startGesture(handlePos, pointer: 7);
|
|
await tester.pump();
|
|
await gesture.moveTo(newHandlePos);
|
|
await tester.pump();
|
|
await gesture.up();
|
|
await tester.pumpWidget(builder());
|
|
|
|
expect(inputValue.selection.baseOffset, 5);
|
|
expect(inputValue.selection.extentOffset, 108);
|
|
|
|
await tester.tap(find.text('CUT'));
|
|
await tester.pumpWidget(builder());
|
|
expect(inputValue.selection.isCollapsed, true);
|
|
expect(inputValue.text, cutValue);
|
|
});
|
|
|
|
testWidgets('Can scroll multiline input', (WidgetTester tester) async {
|
|
GlobalKey inputKey = new GlobalKey();
|
|
InputValue inputValue = InputValue.empty;
|
|
|
|
Widget builder() {
|
|
return new Overlay(
|
|
initialEntries: <OverlayEntry>[
|
|
new OverlayEntry(
|
|
builder: (BuildContext context) {
|
|
return new Center(
|
|
child: new Material(
|
|
child: new Input(
|
|
value: inputValue,
|
|
key: inputKey,
|
|
style: const TextStyle(color: Colors.black, fontSize: 34.0),
|
|
maxLines: 2,
|
|
onChanged: (InputValue value) { inputValue = value; }
|
|
)
|
|
)
|
|
);
|
|
}
|
|
)
|
|
]
|
|
);
|
|
}
|
|
|
|
await tester.pumpWidget(builder());
|
|
|
|
enterText(kFourLines);
|
|
await tester.idle();
|
|
|
|
await tester.pumpWidget(builder());
|
|
|
|
RenderBox findInputBox() => tester.renderObject(find.byKey(inputKey));
|
|
RenderBox inputBox = findInputBox();
|
|
|
|
// Check that the last line of text is not displayed.
|
|
Point firstPos = textOffsetToPosition(tester, kFourLines.indexOf('First'));
|
|
Point fourthPos = textOffsetToPosition(tester, kFourLines.indexOf('Fourth'));
|
|
expect(firstPos.x, fourthPos.x);
|
|
expect(firstPos.y, lessThan(fourthPos.y));
|
|
expect(inputBox.hitTest(new HitTestResult(), position: inputBox.globalToLocal(firstPos)), isTrue);
|
|
expect(inputBox.hitTest(new HitTestResult(), position: inputBox.globalToLocal(fourthPos)), isFalse);
|
|
|
|
TestGesture gesture = await tester.startGesture(firstPos, pointer: 7);
|
|
await tester.pump();
|
|
await gesture.moveBy(new Offset(0.0, -1000.0));
|
|
await tester.pump(const Duration(seconds: 2));
|
|
// Wait and drag again to trigger https://github.com/flutter/flutter/issues/6329
|
|
// (No idea why this is necessary, but the bug wouldn't repro without it.)
|
|
await gesture.moveBy(new Offset(0.0, -1000.0));
|
|
await tester.pump(const Duration(seconds: 2));
|
|
await gesture.up();
|
|
await tester.pump();
|
|
|
|
// Now the first line is scrolled up, and the fourth line is visible.
|
|
Point newFirstPos = textOffsetToPosition(tester, kFourLines.indexOf('First'));
|
|
Point newFourthPos = textOffsetToPosition(tester, kFourLines.indexOf('Fourth'));
|
|
expect(newFirstPos.y, lessThan(firstPos.y));
|
|
expect(inputBox.hitTest(new HitTestResult(), position: inputBox.globalToLocal(newFirstPos)), isFalse);
|
|
expect(inputBox.hitTest(new HitTestResult(), position: inputBox.globalToLocal(newFourthPos)), isTrue);
|
|
|
|
// Now try scrolling by dragging the selection handle.
|
|
|
|
// Long press the 'i' in 'Fourth line' to select the word.
|
|
await tester.pump(const Duration(seconds: 2));
|
|
Point untilPos = textOffsetToPosition(tester, kFourLines.indexOf('Fourth line')+8);
|
|
gesture = await tester.startGesture(untilPos, pointer: 7);
|
|
await tester.pump(const Duration(seconds: 2));
|
|
await gesture.up();
|
|
await tester.pump();
|
|
|
|
RenderEditable renderEditable = findRenderEditable(tester);
|
|
List<TextSelectionPoint> endpoints = renderEditable.getEndpointsForSelection(
|
|
inputValue.selection);
|
|
expect(endpoints.length, 2);
|
|
|
|
// Drag the left handle to the first line, just after 'First'.
|
|
Point handlePos = endpoints[0].point + new Offset(-1.0, 1.0);
|
|
Point newHandlePos = textOffsetToPosition(tester, kFourLines.indexOf('First') + 5);
|
|
gesture = await tester.startGesture(handlePos, pointer: 7);
|
|
await tester.pump();
|
|
await gesture.moveTo(newHandlePos + new Offset(0.0, -10.0));
|
|
await tester.pump();
|
|
await gesture.up();
|
|
await tester.pump();
|
|
|
|
// The text should have scrolled up with the handle to keep the active
|
|
// cursor visible, back to its original position.
|
|
newFirstPos = textOffsetToPosition(tester, kFourLines.indexOf('First'));
|
|
newFourthPos = textOffsetToPosition(tester, kFourLines.indexOf('Fourth'));
|
|
expect(newFirstPos.y, firstPos.y);
|
|
expect(inputBox.hitTest(new HitTestResult(), position: inputBox.globalToLocal(newFirstPos)), isTrue);
|
|
expect(inputBox.hitTest(new HitTestResult(), position: inputBox.globalToLocal(newFourthPos)), isFalse);
|
|
});
|
|
}
|