Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
Front-End
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
abdullh.alsoleman
Front-End
Commits
285b4751
Unverified
Commit
285b4751
authored
Apr 22, 2021
by
Ian Hickson
Committed by
GitHub
Apr 22, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Refactor text editing test APIs (Mark III) (#80003)
parent
4f3ec01d
Changes
8
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
222 additions
and
133 deletions
+222
-133
text_editing_integration.dart
...s/web_e2e_tests/test_driver/text_editing_integration.dart
+0
-18
system_channels.dart
packages/flutter/lib/src/services/system_channels.dart
+16
-4
text_input.dart
packages/flutter/lib/src/services/text_input.dart
+19
-4
editable_text.dart
packages/flutter/lib/src/widgets/editable_text.dart
+1
-1
binding.dart
packages/flutter_test/lib/src/binding.dart
+34
-7
test_text_input.dart
packages/flutter_test/lib/src/test_text_input.dart
+115
-84
widget_tester.dart
packages/flutter_test/lib/src/widget_tester.dart
+15
-12
bindings_test.dart
packages/flutter_test/test/bindings_test.dart
+22
-3
No files found.
dev/integration_tests/web_e2e_tests/test_driver/text_editing_integration.dart
View file @
285b4751
...
...
@@ -20,9 +20,6 @@ void main() {
app
.
main
();
await
tester
.
pumpAndSettle
();
// TODO(nurhan): https://github.com/flutter/flutter/issues/51885
SystemChannels
.
textInput
.
setMockMethodCallHandler
(
null
);
// Focus on a TextFormField.
final
Finder
finder
=
find
.
byKey
(
const
Key
(
'input'
));
expect
(
finder
,
findsOneWidget
);
...
...
@@ -48,9 +45,6 @@ void main() {
app
.
main
();
await
tester
.
pumpAndSettle
();
// TODO(nurhan): https://github.com/flutter/flutter/issues/51885
SystemChannels
.
textInput
.
setMockMethodCallHandler
(
null
);
// Focus on a TextFormField.
final
Finder
finder
=
find
.
byKey
(
const
Key
(
'empty-input'
));
expect
(
finder
,
findsOneWidget
);
...
...
@@ -76,9 +70,6 @@ void main() {
app
.
main
();
await
tester
.
pumpAndSettle
();
// TODO(nurhan): https://github.com/flutter/flutter/issues/51885
SystemChannels
.
textInput
.
setMockMethodCallHandler
(
null
);
// This text will show no-enter initially. It will have 'enter-pressed'
// after `onFieldSubmitted` of TextField is triggered.
final
Finder
textFinder
=
find
.
byKey
(
const
Key
(
'text'
));
...
...
@@ -112,9 +103,6 @@ void main() {
app
.
main
();
await
tester
.
pumpAndSettle
();
// TODO(nurhan): https://github.com/flutter/flutter/issues/51885
SystemChannels
.
textInput
.
setMockMethodCallHandler
(
null
);
// Focus on a TextFormField.
final
Finder
finder
=
find
.
byKey
(
const
Key
(
'input'
));
expect
(
finder
,
findsOneWidget
);
...
...
@@ -147,9 +135,6 @@ void main() {
app
.
main
();
await
tester
.
pumpAndSettle
();
// TODO(nurhan): https://github.com/flutter/flutter/issues/51885
SystemChannels
.
textInput
.
setMockMethodCallHandler
(
null
);
// Focus on a TextFormField.
final
Finder
finder
=
find
.
byKey
(
const
Key
(
'input'
));
expect
(
finder
,
findsOneWidget
);
...
...
@@ -197,9 +182,6 @@ void main() {
app
.
main
();
await
tester
.
pumpAndSettle
();
// TODO(nurhan): https://github.com/flutter/flutter/issues/51885
SystemChannels
.
textInput
.
setMockMethodCallHandler
(
null
);
// Select something from the selectable text.
final
Finder
finder
=
find
.
byKey
(
const
Key
(
'selectable'
));
expect
(
finder
,
findsOneWidget
);
...
...
packages/flutter/lib/src/services/system_channels.dart
View file @
285b4751
...
...
@@ -120,6 +120,11 @@ class SystemChannels {
/// they apply, so that stale messages referencing past transactions can be
/// ignored.
///
/// In debug builds, messages sent with a client ID of -1 are always accepted.
/// This allows tests to smuggle messages without having to mock the engine's
/// text handling (for example, allowing the engine to still handle the text
/// input messages in an integration test).
///
/// The methods described below are wrapped in a more convenient form by the
/// [TextInput] and [TextInputConnection] class.
///
...
...
@@ -152,9 +157,15 @@ class SystemChannels {
/// is a transaction identifier. Calls for stale transactions should be ignored.
///
/// * `TextInputClient.updateEditingState`: The user has changed the contents
/// of the text control. The second argument is a [String] containing a
/// JSON-encoded object with seven keys, in the form expected by
/// [TextEditingValue.fromJSON].
/// of the text control. The second argument is an object with seven keys,
/// in the form expected by [TextEditingValue.fromJSON].
///
/// * `TextInputClient.updateEditingStateWithTag`: One or more text controls
/// were autofilled by the platform's autofill service. The first argument
/// (the client ID) is ignored, the second argument is a map of tags to
/// objects in the form expected by [TextEditingValue.fromJSON]. See
/// [AutofillScope.getAutofillClient] for details on the interpretation of
/// the tag.
///
/// * `TextInputClient.performAction`: The user has triggered an action. The
/// second argument is a [String] consisting of the stringification of one
...
...
@@ -165,7 +176,8 @@ class SystemChannels {
/// one. The framework should call `TextInput.setClient` and
/// `TextInput.setEditingState` again with its most recent information. If
/// there is no existing state on the framework side, the call should
/// fizzle.
/// fizzle. (This call is made without a client ID; indeed, without any
/// arguments at all.)
///
/// * `TextInputClient.onConnectionClosed`: The text input connection closed
/// on the platform side. For example the application is moved to
...
...
packages/flutter/lib/src/services/text_input.dart
View file @
285b4751
...
...
@@ -1327,9 +1327,11 @@ class TextInput {
final
List
<
dynamic
>
args
=
methodCall
.
arguments
as
List
<
dynamic
>;
// The updateEditingStateWithTag request (autofill) can come up even to a
// text field that doesn't have a connection.
if
(
method
==
'TextInputClient.updateEditingStateWithTag'
)
{
assert
(
_currentConnection
!.
_client
!=
null
);
final
TextInputClient
client
=
_currentConnection
!.
_client
;
assert
(
client
!=
null
);
final
AutofillScope
?
scope
=
client
.
currentAutofillScope
;
final
Map
<
String
,
dynamic
>
editingValue
=
args
[
1
]
as
Map
<
String
,
dynamic
>;
for
(
final
String
tag
in
editingValue
.
keys
)
{
...
...
@@ -1343,9 +1345,22 @@ class TextInput {
}
final
int
client
=
args
[
0
]
as
int
;
// The incoming message was for a different client.
if
(
client
!=
_currentConnection
!.
_id
)
return
;
if
(
client
!=
_currentConnection
!.
_id
)
{
// If the client IDs don't match, the incoming message was for a different
// client.
bool
debugAllowAnyway
=
false
;
assert
(()
{
// In debug builds we allow "-1" as a magical client ID that ignores
// this verification step so that tests can always get through, even
// when they are not mocking the engine side of text input.
if
(
client
==
-
1
)
debugAllowAnyway
=
true
;
return
true
;
}());
if
(!
debugAllowAnyway
)
return
;
}
switch
(
method
)
{
case
'TextInputClient.updateEditingState'
:
_currentConnection
!.
_client
.
updateEditingValue
(
TextEditingValue
.
fromJSON
(
args
[
1
]
as
Map
<
String
,
dynamic
>));
...
...
packages/flutter/lib/src/widgets/editable_text.dart
View file @
285b4751
...
...
@@ -2112,7 +2112,7 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
if
(
_hasFocus
)
{
_openInputConnection
();
}
else
{
widget
.
focusNode
.
requestFocus
();
widget
.
focusNode
.
requestFocus
();
// This eventually calls _openInputConnection also, see _handleFocusChanged.
}
}
...
...
packages/flutter_test/lib/src/binding.dart
View file @
285b4751
...
...
@@ -195,9 +195,15 @@ abstract class TestWidgetsFlutterBinding extends BindingBase
/// Called by the test framework at the beginning of a widget test to
/// prepare the binding for the next test.
///
/// If [registerTestTextInput] returns true when this method is called,
/// the [testTextInput] is configured to simulate the keyboard.
void
reset
()
{
_restorationManager
=
null
;
resetGestureBinding
();
testTextInput
.
reset
();
if
(
registerTestTextInput
)
_testTextInput
.
register
();
}
@override
...
...
@@ -237,7 +243,8 @@ abstract class TestWidgetsFlutterBinding extends BindingBase
@protected
bool
get
overrideHttpClient
=>
true
;
/// Determines whether the binding automatically registers [testTextInput].
/// Determines whether the binding automatically registers [testTextInput] as
/// a fake keyboard implementation.
///
/// Unit tests make use of this to mock out text input communication for
/// widgets. An integration test would set this to false, to test real IME
...
...
@@ -245,6 +252,19 @@ abstract class TestWidgetsFlutterBinding extends BindingBase
///
/// [TestTextInput.isRegistered] reports whether the text input mock is
/// registered or not.
///
/// Some of the properties and methods on [testTextInput] are only valid if
/// [registerTestTextInput] returns true when a test starts. If those
/// members are accessed when using a binding that sets this flag to false,
/// they will throw.
///
/// If this property returns true when a test ends, the [testTextInput] is
/// unregistered.
///
/// This property should not change the value it returns during the lifetime
/// of the binding. Changing the value of this property risks very confusing
/// behavior as the [TestTextInput] may be inconsistently registered or
/// unregistered.
@protected
bool
get
registerTestTextInput
=>
true
;
...
...
@@ -319,9 +339,6 @@ abstract class TestWidgetsFlutterBinding extends BindingBase
binding
.
setupHttpOverrides
();
}
_testTextInput
=
TestTextInput
(
onCleared:
_resetFocusedEditable
);
if
(
registerTestTextInput
)
{
_testTextInput
.
register
();
}
}
@override
...
...
@@ -515,12 +532,20 @@ abstract class TestWidgetsFlutterBinding extends BindingBase
TestTextInput
get
testTextInput
=>
_testTextInput
;
late
TestTextInput
_testTextInput
;
/// The current client of the onscreen keyboard. Callers must pump
/// an additional frame after setting this property to complete the
/// focus change.
/// The [State] of the current [EditableText] client of the onscreen keyboard.
///
/// Setting this property to a new value causes the given [EditableTextState]
/// to focus itself and request the keyboard to establish a
/// [TextInputConnection].
///
/// Callers must pump an additional frame after setting this property to
/// complete the focus change.
///
/// Instead of setting this directly, consider using
/// [WidgetTester.showKeyboard].
//
// TODO(ianh): We should just remove this property and move the call to
// requestKeyboard to the WidgetTester.showKeyboard method.
EditableTextState
?
get
focusedEditable
=>
_focusedEditable
;
EditableTextState
?
_focusedEditable
;
set
focusedEditable
(
EditableTextState
?
value
)
{
...
...
@@ -799,6 +824,8 @@ abstract class TestWidgetsFlutterBinding extends BindingBase
// alone so that we don't cause more spurious errors.
runApp
(
Container
(
key:
UniqueKey
(),
child:
_postTestMessage
));
// Unmount any remaining widgets.
await
pump
();
if
(
registerTestTextInput
)
_testTextInput
.
unregister
();
invariantTester
();
_verifyAutoUpdateGoldensUnset
(
autoUpdateGoldensBeforeTest
&&
!
isBrowser
);
_verifyReportTestExceptionUnset
(
reportTestExceptionBeforeTest
);
...
...
packages/flutter_test/lib/src/test_text_input.dart
View file @
285b4751
...
...
@@ -14,6 +14,18 @@ export 'package:flutter/services.dart' show TextEditingValue, TextInputAction;
///
/// Typical app tests will not need to use this class directly.
///
/// The [TestWidgetsFlutterBinding] class registers a [TestTextInput] instance
/// ([TestWidgetsFlutterBinding.testTextInput]) as a stub keyboard
/// implementation if its [TestWidgetsFlutterBinding.registerTestTextInput]
/// property returns true when a test starts, and unregisters it when the test
/// ends (unless it ends with a failure).
///
/// See [register], [unregister], and [isRegistered] for details.
///
/// The [enterText], [updateEditingValue], [receiveAction], and
/// [closeConnection] methods can be used even when the [TestTextInput] is not
/// registered. All other methods will assert if [isRegistered] is false.
///
/// See also:
///
/// * [WidgetTester.enterText], which uses this class to simulate keyboard input.
...
...
@@ -36,58 +48,76 @@ class TestTextInput {
/// The messenger which sends the bytes for this channel, not null.
BinaryMessenger
get
_binaryMessenger
=>
ServicesBinding
.
instance
!.
defaultBinaryMessenger
;
///
Resets any internal state of this object and calls [register]
.
///
Log for method calls
.
///
/// This method is invoked by the testing framework between tests. It should
/// not ordinarily be called by tests directly.
void
resetAndRegister
()
{
log
.
clear
();
editingState
=
null
;
setClientArgs
=
null
;
_client
=
0
;
_isVisible
=
false
;
register
();
}
/// For all registered channels, handled calls are added to the list. Can
/// be cleaned using `log.clear()`.
final
List
<
MethodCall
>
log
=
<
MethodCall
>[];
/// Installs this object as a mock handler for [SystemChannels.textInput].
///
/// Called by the binding at the top of a test when
/// [TestWidgetsFlutterBinding.registerTestTextInput] is true.
void
register
()
=>
SystemChannels
.
textInput
.
setMockMethodCallHandler
(
_handleTextInputCall
);
/// Removes this object as a mock handler for [SystemChannels.textInput].
///
/// After calling this method, the channel will exchange messages with the
/// Flutter engine. Use this with [FlutterDriver] tests that need to display
/// on-screen keyboard provided by the operating system.
void
unregister
()
=>
SystemChannels
.
textInput
.
setMockMethodCallHandler
(
null
);
/// Log for method calls.
/// Flutter engine instead of the stub.
///
///
For all registered channels, handled calls are added to the list. Ca
n
///
be cleaned using `log.clear()`
.
final
List
<
MethodCall
>
log
=
<
MethodCall
>[]
;
///
Called by the binding at the end of a (successful) test whe
n
///
[TestWidgetsFlutterBinding.registerTestTextInput] is true
.
void
unregister
()
=>
SystemChannels
.
textInput
.
setMockMethodCallHandler
(
null
)
;
/// Whether this [TestTextInput] is registered with [SystemChannels.textInput].
///
/// Use [register] and [unregister] methods to control this value.
/// The binding uses the [register] and [unregister] methods to control this
/// value when [TestWidgetsFlutterBinding.registerTestTextInput] is true.
bool
get
isRegistered
=>
SystemChannels
.
textInput
.
checkMockMethodCallHandler
(
_handleTextInputCall
);
int
?
_client
;
/// Whether there are any active clients listening to text input.
bool
get
hasAnyClients
{
assert
(
isRegistered
);
return
_client
>
0
;
return
_client
!=
null
&&
_client
!
>
0
;
}
int
_client
=
0
;
/// Arguments supplied to the TextInput.setClient method call.
/// The last set of arguments supplied to the `TextInput.setClient` and
/// `TextInput.updateConfig` methods of this stub implementation.
Map
<
String
,
dynamic
>?
setClientArgs
;
/// The last set of arguments that [TextInputConnection.setEditingState] sent
/// to the embedder.
/// to this stub implementation (i.e. the arguments set to
/// `TextInput.setEditingState`).
///
/// This is a map representation of a [TextEditingValue] object. For example,
/// it will have a `text` entry whose value matches the most recent
/// [TextEditingValue.text] that was sent to the embedder.
Map
<
String
,
dynamic
>?
editingState
;
/// Whether the onscreen keyboard is visible to the user.
///
/// Specifically, this reflects the last call to `TextInput.show` or
/// `TextInput.hide` received by the stub implementation.
bool
get
isVisible
{
assert
(
isRegistered
);
return
_isVisible
;
}
bool
_isVisible
=
false
;
/// Resets any internal state of this object.
///
/// This method is invoked by the testing framework between tests. It should
/// not ordinarily be called by tests directly.
void
reset
()
{
log
.
clear
();
_client
=
null
;
setClientArgs
=
null
;
editingState
=
null
;
_isVisible
=
false
;
}
Future
<
dynamic
>
_handleTextInputCall
(
MethodCall
methodCall
)
async
{
log
.
add
(
methodCall
);
switch
(
methodCall
.
method
)
{
...
...
@@ -99,7 +129,7 @@ class TestTextInput {
setClientArgs
=
methodCall
.
arguments
as
Map
<
String
,
dynamic
>;
break
;
case
'TextInput.clearClient'
:
_client
=
0
;
_client
=
null
;
_isVisible
=
false
;
onCleared
?.
call
();
break
;
...
...
@@ -115,87 +145,69 @@ class TestTextInput {
}
}
/// Whether the onscreen keyboard is visible to the user.
bool
get
isVisible
{
/// Simulates the user hiding the onscreen keyboard.
///
/// This does nothing but set the internal flag.
void
hide
()
{
assert
(
isRegistered
);
return
_isVisibl
e
;
_isVisible
=
fals
e
;
}
bool
_isVisible
=
false
;
/// Simulates the user changing the [TextEditingValue] to the given value.
void
updateEditingValue
(
TextEditingValue
value
)
{
assert
(
isRegistered
);
// Not using the `expect` function because in the case of a FlutterDriver
// test this code does not run in a package:test test zone.
if
(
_client
==
0
)
throw
TestFailure
(
'Tried to use TestTextInput with no keyboard attached. You must use WidgetTester.showKeyboard() first.'
);
_binaryMessenger
.
handlePlatformMessage
(
SystemChannels
.
textInput
.
name
,
SystemChannels
.
textInput
.
codec
.
encodeMethodCall
(
MethodCall
(
'TextInputClient.updateEditingState'
,
<
dynamic
>[
_client
,
value
.
toJSON
()],
),
),
(
ByteData
?
data
)
{
/* response from framework is discarded */
},
);
/// Simulates the user typing the given text.
///
/// Calling this method replaces the content of the connected input field with
/// `text`, and places the caret at the end of the text.
///
/// This can be called even if the [TestTextInput] has not been [register]ed.
///
/// If this is used to inject text when there is a real IME connection, for
/// example when using the [integration_test] library, there is a risk that
/// the real IME will become confused as to the current state of input.
void
enterText
(
String
text
)
{
updateEditingValue
(
TextEditingValue
(
text:
text
,
selection:
TextSelection
.
collapsed
(
offset:
text
.
length
),
));
}
/// Simulates the user c
losing the text input connection
.
/// Simulates the user c
hanging the [TextEditingValue] to the given value
.
///
/// For example:
/// - User pressed the home button and sent the application to background.
/// - User closed the virtual keyboard.
void
closeConnection
()
{
assert
(
isRegistered
);
// Not using the `expect` function because in the case of a FlutterDriver
// test this code does not run in a package:test test zone.
if
(
_client
==
0
)
throw
TestFailure
(
'Tried to use TestTextInput with no keyboard attached. You must use WidgetTester.showKeyboard() first.'
);
/// This can be called even if the [TestTextInput] has not been [register]ed.
///
/// If this is used to inject text when there is a real IME connection, for
/// example when using the [integration_test] library, there is a risk that
/// the real IME will become confused as to the current state of input.
void
updateEditingValue
(
TextEditingValue
value
)
{
_binaryMessenger
.
handlePlatformMessage
(
SystemChannels
.
textInput
.
name
,
SystemChannels
.
textInput
.
codec
.
encodeMethodCall
(
MethodCall
(
'TextInputClient.
onConnectionClosed
'
,
<
dynamic
>[
_client
,]
'TextInputClient.
updateEditingState
'
,
<
dynamic
>[
_client
??
-
1
,
value
.
toJSON
()],
),
),
(
ByteData
?
data
)
{
/* response from framework is discarded */
},
);
}
/// Simulates the user typing the given text.
///
/// Calling this method replaces the content of the connected input field with
/// `text`, and places the caret at the end of the text.
void
enterText
(
String
text
)
{
assert
(
isRegistered
);
updateEditingValue
(
TextEditingValue
(
text:
text
,
selection:
TextSelection
.
collapsed
(
offset:
text
.
length
),
));
}
/// Simulates the user pressing one of the [TextInputAction] buttons.
/// Does not check that the [TextInputAction] performed is an acceptable one
/// based on the `inputAction` [setClientArgs].
///
/// This can be called even if the [TestTextInput] has not been [register]ed.
///
/// If this is used to inject an action when there is a real IME connection,
/// for example when using the [integration_test] library, there is a risk
/// that the real IME will become confused as to the current state of input.
Future
<
void
>
receiveAction
(
TextInputAction
action
)
async
{
assert
(
isRegistered
);
return
TestAsyncUtils
.
guard
(()
{
// Not using the `expect` function because in the case of a FlutterDriver
// test this code does not run in a package:test test zone.
if
(
_client
==
0
)
{
throw
TestFailure
(
'Tried to use TestTextInput with no keyboard attached. You must use WidgetTester.showKeyboard() first.'
);
}
final
Completer
<
void
>
completer
=
Completer
<
void
>();
_binaryMessenger
.
handlePlatformMessage
(
SystemChannels
.
textInput
.
name
,
SystemChannels
.
textInput
.
codec
.
encodeMethodCall
(
MethodCall
(
'TextInputClient.performAction'
,
<
dynamic
>[
_client
,
action
.
toString
()],
<
dynamic
>[
_client
??
-
1
,
action
.
toString
()],
),
),
(
ByteData
?
data
)
{
...
...
@@ -219,9 +231,28 @@ class TestTextInput {
});
}
/// Simulates the user hiding the onscreen keyboard.
void
hide
()
{
assert
(
isRegistered
);
_isVisible
=
false
;
/// Simulates the user closing the text input connection.
///
/// For example:
///
/// * User pressed the home button and sent the application to background.
/// * User closed the virtual keyboard.
///
/// This can be called even if the [TestTextInput] has not been [register]ed.
///
/// If this is used to inject text when there is a real IME connection, for
/// example when using the [integration_test] library, there is a risk that
/// the real IME will become confused as to the current state of input.
void
closeConnection
()
{
_binaryMessenger
.
handlePlatformMessage
(
SystemChannels
.
textInput
.
name
,
SystemChannels
.
textInput
.
codec
.
encodeMethodCall
(
MethodCall
(
'TextInputClient.onConnectionClosed'
,
<
dynamic
>[
_client
??
-
1
],
),
),
(
ByteData
?
data
)
{
/* response from framework is discarded */
},
);
}
}
packages/flutter_test/lib/src/widget_tester.dart
View file @
285b4751
...
...
@@ -149,7 +149,6 @@ void testWidgets(
()
async
{
binding
.
reset
();
debugResetSemanticsIdCounter
();
tester
.
resetTestTextInput
();
Object
?
memento
;
try
{
memento
=
await
variant
.
setUp
(
value
);
...
...
@@ -1002,18 +1001,13 @@ class WidgetTester extends WidgetController implements HitTestDispatcher, Ticker
///
/// Typical app tests will not need to use this value. To add text to widgets
/// like [TextField] or [TextFormField], call [enterText].
TestTextInput
get
testTextInput
=>
binding
.
testTextInput
;
/// Ensures that [testTextInput] is registered and [TestTextInput.log] is
/// reset.
///
/// This is called by the testing framework before test runs, so that if a
/// previous test has set its own handler on [SystemChannels.textInput], the
/// [testTextInput] regains control and the log is fresh for the new test.
/// It should not typically need to be called by tests.
void
resetTestTextInput
()
{
testTextInput
.
resetAndRegister
();
}
/// Some of the properties and methods on this value are only valid if the
/// binding's [TestWidgetsFlutterBinding.registerTestTextInput] flag is set to
/// true as a test is starting (meaning that the keyboard is to be simulated
/// by the test framework). If those members are accessed when using a binding
/// that sets this flag to false, they will throw.
TestTextInput
get
testTextInput
=>
binding
.
testTextInput
;
/// Give the text input widget specified by [finder] the focus, as if the
/// onscreen keyboard had appeared.
...
...
@@ -1035,6 +1029,9 @@ class WidgetTester extends WidgetController implements HitTestDispatcher, Ticker
matchRoot:
true
,
),
);
// Setting focusedEditable causes the binding to call requestKeyboard()
// on the EditableTextState, which itself eventually calls TextInput.attach
// to establish the connection.
binding
.
focusedEditable
=
editable
;
await
pump
();
});
...
...
@@ -1052,6 +1049,12 @@ class WidgetTester extends WidgetController implements HitTestDispatcher, Ticker
///
/// To just give [finder] the focus without entering any text,
/// see [showKeyboard].
///
/// To enter text into other widgets (e.g. a custom widget that maintains a
/// TextInputConnection the way that a [EditableText] does), first ensure that
/// that widget has an open connection (e.g. by using [tap] to to focus it),
/// then call `testTextInput.enterText` directly (see
/// [TestTextInput.enterText]).
Future
<
void
>
enterText
(
Finder
finder
,
String
text
)
async
{
return
TestAsyncUtils
.
guard
<
void
>(()
async
{
await
showKeyboard
(
finder
);
...
...
packages/flutter_test/test/bindings_test.dart
View file @
285b4751
...
...
@@ -11,6 +11,8 @@ import 'package:flutter_test/flutter_test.dart';
import
'package:test_api/test_api.dart'
as
test_package
;
void
main
(
)
{
final
AutomatedTestWidgetsFlutterBinding
binding
=
AutomatedTestWidgetsFlutterBinding
();
group
(
TestViewConfiguration
,
()
{
test
(
'is initialized with top-level window if one is not provided'
,
()
{
// The code below will throw without the default.
...
...
@@ -20,15 +22,32 @@ void main() {
group
(
AutomatedTestWidgetsFlutterBinding
,
()
{
test
(
'allows setting defaultTestTimeout to 5 minutes'
,
()
{
final
AutomatedTestWidgetsFlutterBinding
binding
=
AutomatedTestWidgetsFlutterBinding
();
binding
.
defaultTestTimeout
=
const
test_package
.
Timeout
(
Duration
(
minutes:
5
));
expect
(
binding
.
defaultTestTimeout
.
duration
,
const
Duration
(
minutes:
5
));
});
});
// The next three tests must run in order -- first using `test`, then `testWidgets`, then `test` again.
int
order
=
0
;
test
(
'Initializes httpOverrides and testTextInput'
,
()
async
{
final
TestWidgetsFlutterBinding
binding
=
TestWidgetsFlutterBinding
.
ensureInitialized
()
as
TestWidgetsFlutterBinding
;
expect
(
binding
.
testTextInput
.
isRegistered
,
true
);
assert
(
order
==
0
);
expect
(
binding
.
testTextInput
,
isNotNull
);
expect
(
binding
.
testTextInput
.
isRegistered
,
isFalse
);
expect
(
HttpOverrides
.
current
,
isNotNull
);
order
+=
1
;
});
testWidgets
(
'Registers testTextInput'
,
(
WidgetTester
tester
)
async
{
assert
(
order
==
1
);
expect
(
tester
.
testTextInput
.
isRegistered
,
isTrue
);
order
+=
1
;
});
test
(
'Unregisters testTextInput'
,
()
async
{
assert
(
order
==
2
);
expect
(
binding
.
testTextInput
.
isRegistered
,
isFalse
);
order
+=
1
;
});
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment