From 80b2c1732af07952caaa438c4fc580e38ffcaa0b Mon Sep 17 00:00:00 2001 From: Jason Simmons Date: Thu, 17 Sep 2015 15:39:54 -0700 Subject: [PATCH] In EditableText, wrap the placeholder text in a Row so it will expand to its parent's width This will ensure that the width of an empty Input is consistent with the width of an Input that contains text. Also add a unit test for the Input widget and a way for tests to provide mock implementations of Mojo services such as the keyboard. --- packages/flutter/lib/src/services/shell.dart | 20 ++++++- .../lib/src/widgets/editable_text.dart | 2 +- .../unit/test/services/mock_services.dart | 31 ++++++++++ packages/unit/test/widget/input_test.dart | 59 +++++++++++++++++++ 4 files changed, 110 insertions(+), 2 deletions(-) create mode 100644 packages/unit/test/services/mock_services.dart create mode 100644 packages/unit/test/widget/input_test.dart diff --git a/packages/flutter/lib/src/services/shell.dart b/packages/flutter/lib/src/services/shell.dart index 88b21403379..3f3f4112dff 100644 --- a/packages/flutter/lib/src/services/shell.dart +++ b/packages/flutter/lib/src/services/shell.dart @@ -18,15 +18,33 @@ ApplicationConnection _initConnection() { return new ApplicationConnection(null, serviceProvider); } +// A replacement for requestService. Implementations should return true +// if they handled the request, or false if the request should fall through +// to the default requestService. +typedef bool OverrideRequestService(String url, Object proxy); + +// Set this to intercept calls to requestService and supply an alternative +// implementation of a service (for example, a mock for testing). +OverrideRequestService overrideRequestService; + class _ShellImpl { _ShellImpl._(); final ApplicationConnection _connection = _initConnection(); - void requestService(String url, Object proxy) { + void _requestService(String url, Object proxy) { if (embedder.shell == null) _connection.requestService(proxy); else embedder.connectToService(url, proxy); } + + void requestService(String url, Object proxy) { + if (overrideRequestService != null) { + if (overrideRequestService(url, proxy)) + return; + } + + _requestService(url, proxy); + } } final _ShellImpl shell = new _ShellImpl._(); diff --git a/packages/flutter/lib/src/widgets/editable_text.dart b/packages/flutter/lib/src/widgets/editable_text.dart index 82794c92678..3280da4c235 100644 --- a/packages/flutter/lib/src/widgets/editable_text.dart +++ b/packages/flutter/lib/src/widgets/editable_text.dart @@ -205,7 +205,7 @@ class EditableText extends StatefulComponent { if (!value.composing.isValid) { // TODO(eseidel): This is the wrong height if empty! - return new Text(value.text, style: style); + return new Row([new Text(value.text, style: style)]); } TextStyle composingStyle = style.merge(const TextStyle(decoration: underline)); diff --git a/packages/unit/test/services/mock_services.dart b/packages/unit/test/services/mock_services.dart new file mode 100644 index 00000000000..264d597f55f --- /dev/null +++ b/packages/unit/test/services/mock_services.dart @@ -0,0 +1,31 @@ +import 'package:sky/src/services/shell.dart' as shell; + +// Tests can use ServiceMocker to register replacement implementations +// of Mojo services. +class _ServiceMocker { + _ServiceMocker() { + shell.overrideRequestService = _requestService; + } + + // Map of interface names to mock implementations. + Map _interfaceMock = new Map(); + + bool _requestService(String url, Object proxy) { + Object mock = _interfaceMock[proxy.impl.name]; + if (mock != null) { + // Replace the proxy's implementation of the service interface with the + // mock. + proxy.ptr = mock; + return true; + } else { + return false; + } + } + + // Provide a mock implementation for a Mojo interface. + void registerMockService(String interfaceName, Object mock) { + _interfaceMock[interfaceName] = mock; + } +} + +final _ServiceMocker serviceMocker = new _ServiceMocker(); diff --git a/packages/unit/test/widget/input_test.dart b/packages/unit/test/widget/input_test.dart new file mode 100644 index 00000000000..7628ccd1aa5 --- /dev/null +++ b/packages/unit/test/widget/input_test.dart @@ -0,0 +1,59 @@ +import 'package:mojo_services/keyboard/keyboard.mojom.dart'; +import 'package:sky/services.dart'; +import 'package:sky/widgets.dart'; +import 'package:test/test.dart'; + +import 'widget_tester.dart'; +import '../services/mock_services.dart'; + +class MockKeyboard implements KeyboardService { + KeyboardClient client; + + void show(Object client, int type) { + this.client = client.impl; + } + + void showByRequest() {} + + void hide() {} +} + +void main() { + test('Editable text has consistent width', () { + WidgetTester tester = new WidgetTester(); + + MockKeyboard mockKeyboard = new MockKeyboard(); + serviceMocker.registerMockService(KeyboardServiceName, mockKeyboard); + + GlobalKey inputKey = new GlobalKey(); + String inputValue; + + Widget builder() { + return new Center( + child: new Input( + key: inputKey, + placeholder: 'Placeholder', + onChanged: (value) { inputValue = value; } + ) + ); + } + + tester.pumpFrame(builder); + + Input input = tester.findWidget((Widget widget) => widget.key == inputKey); + Size emptyInputSize = input.renderObject.size; + + // Simulate entry of text through the keyboard. + expect(mockKeyboard.client, isNotNull); + const String testValue = 'Test'; + mockKeyboard.client.setComposingText(testValue, testValue.length); + + // Check that the onChanged event handler fired. + expect(inputValue, equals(testValue)); + + tester.pumpFrame(builder); + + // Check that the Input with text has the same size as the empty Input. + expect(input.renderObject.size, equals(emptyInputSize)); + }); +}