blob: e8bebca1038cec2e2edecffe440e57e7d2696c58 [file] [log] [blame]
// 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 'dart:typed_data';
import 'package:flutter/services.dart';
import 'package:flutter/foundation.dart';
import 'widget_tester.dart';
export 'package:flutter/services.dart' show TextEditingValue, TextInputAction;
/// A testing stub for the system's onscreen keyboard.
///
/// Typical app tests will not need to use this class directly.
///
/// See also:
///
/// * [WidgetTester.enterText], which uses this class to simulate keyboard input.
/// * [WidgetTester.showKeyboard], which uses this class to simulate showing the
/// popup keyboard and initializing its text.
class TestTextInput {
/// Create a fake keyboard backend.
///
/// The [onCleared] argument may be set to be notified of when the keyboard
/// is dismissed.
TestTextInput({ this.onCleared });
/// Called when the keyboard goes away.
///
/// To use the methods on this API that send fake keyboard messages (such as
/// [updateEditingValue], [enterText], or [receiveAction]), the keyboard must
/// first be requested, e.g. using [WidgetTester.showKeyboard].
final VoidCallback onCleared;
/// Installs this object as a mock handler for [SystemChannels.textInput].
void register() {
SystemChannels.textInput.setMockMethodCallHandler(_handleTextInputCall);
_isRegistered = true;
}
/// 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);
_isRegistered = false;
}
/// Whether this [TestTextInput] is registered with [SystemChannels.textInput].
///
/// Use [register] and [unregister] methods to control this value.
bool get isRegistered => _isRegistered;
bool _isRegistered = false;
int _client = 0;
/// Arguments supplied to the TextInput.setClient method call.
Map<String, dynamic> setClientArgs;
/// The last set of arguments that [TextInputConnection.setEditingState] sent
/// to the embedder.
///
/// 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;
Future<dynamic> _handleTextInputCall(MethodCall methodCall) async {
switch (methodCall.method) {
case 'TextInput.setClient':
_client = methodCall.arguments[0];
setClientArgs = methodCall.arguments[1];
break;
case 'TextInput.clearClient':
_client = 0;
_isVisible = false;
if (onCleared != null)
onCleared();
break;
case 'TextInput.setEditingState':
editingState = methodCall.arguments;
break;
case 'TextInput.show':
_isVisible = true;
break;
case 'TextInput.hide':
_isVisible = false;
break;
}
}
/// Whether the onscreen keyboard is visible to the user.
bool get isVisible => _isVisible;
bool _isVisible = false;
/// Simulates the user changing the [TextEditingValue] to the given value.
void updateEditingValue(TextEditingValue value) {
// 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 new TestFailure('Tried to use TestTextInput with no keyboard attached. You must use WidgetTester.showKeyboard() first.');
BinaryMessages.handlePlatformMessage(
SystemChannels.textInput.name,
SystemChannels.textInput.codec.encodeMethodCall(
new MethodCall(
'TextInputClient.updateEditingState',
<dynamic>[_client, value.toJSON()],
),
),
(ByteData data) { /* response from framework is discarded */ },
);
}
/// Simulates the user typing the given text.
void enterText(String text) {
updateEditingValue(new TextEditingValue(
text: text,
));
}
/// 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].
void receiveAction(TextInputAction action) {
// 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 new TestFailure('Tried to use TestTextInput with no keyboard attached. You must use WidgetTester.showKeyboard() first.');
BinaryMessages.handlePlatformMessage(
SystemChannels.textInput.name,
SystemChannels.textInput.codec.encodeMethodCall(
new MethodCall(
'TextInputClient.performAction',
<dynamic>[_client, action.toString()],
),
),
(ByteData data) { /* response from framework is discarded */ },
);
}
/// Simulates the user hiding the onscreen keyboard.
void hide() {
_isVisible = false;
}
}