blob: 6770e3b81c7062c5457f45834998605d4870fcde [file] [log] [blame]
// 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.
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'text_input_utils.dart';
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
group('AutofillClient', () {
late FakeTextChannel fakeTextChannel;
final scope = FakeAutofillScope();
setUp(() {
fakeTextChannel = FakeTextChannel((MethodCall call) async {});
TextInput.setChannel(fakeTextChannel);
scope.clients.clear();
});
tearDown(() {
TextInputConnection.debugResetId();
TextInput.setChannel(SystemChannels.textInput);
});
test('Does not throw if the hint list is empty', () async {
Object? exception;
try {
const AutofillConfiguration(
uniqueIdentifier: 'id',
autofillHints: <String>[],
currentEditingValue: TextEditingValue.empty,
);
} catch (e) {
exception = e;
}
expect(exception, isNull);
});
test(
'AutofillClients send the correct configuration to the platform and responds to updateEditingStateWithTag method correctly',
() async {
final client1 = FakeAutofillClient(const TextEditingValue(text: 'test1'));
final client2 = FakeAutofillClient(const TextEditingValue(text: 'test2'));
client1.textInputConfiguration = TextInputConfiguration(
autofillConfiguration: AutofillConfiguration(
uniqueIdentifier: client1.autofillId,
autofillHints: const <String>['client1'],
currentEditingValue: client1.currentTextEditingValue,
),
);
client2.textInputConfiguration = TextInputConfiguration(
autofillConfiguration: AutofillConfiguration(
uniqueIdentifier: client2.autofillId,
autofillHints: const <String>['client2'],
currentEditingValue: client2.currentTextEditingValue,
),
);
scope.register(client1);
scope.register(client2);
client1.currentAutofillScope = scope;
client2.currentAutofillScope = scope;
scope.attach(client1, client1.textInputConfiguration);
final Map<String, dynamic> expectedConfiguration = client1.textInputConfiguration.toJson();
expectedConfiguration['fields'] = <Map<String, dynamic>>[
client1.textInputConfiguration.toJson(),
client2.textInputConfiguration.toJson(),
];
fakeTextChannel.validateOutgoingMethodCalls(<MethodCall>[
MethodCall('TextInput.setClient', <dynamic>[1, expectedConfiguration]),
]);
const text2 = TextEditingValue(text: 'Text 2');
fakeTextChannel.incoming?.call(
MethodCall('TextInputClient.updateEditingStateWithTag', <dynamic>[
0,
<String, dynamic>{client2.autofillId: text2.toJSON()},
]),
);
expect(client2.currentTextEditingValue, text2);
},
);
});
group('AutoFillConfiguration', () {
late AutofillConfiguration fakeAutoFillConfiguration;
late AutofillConfiguration fakeAutoFillConfiguration2;
setUp(() {
// If you create two objects with `const` with the same values, the second object will be equal to the first one by reference.
// This means that even without overriding the `equals` method, the test will pass.
// ignore: prefer_const_constructors
fakeAutoFillConfiguration = AutofillConfiguration(
uniqueIdentifier: 'id1',
// ignore: prefer_const_literals_to_create_immutables
autofillHints: <String>['client1'],
currentEditingValue: TextEditingValue.empty,
hintText: 'hint',
);
// ignore: prefer_const_constructors
fakeAutoFillConfiguration2 = AutofillConfiguration(
uniqueIdentifier: 'id1',
// ignore: prefer_const_literals_to_create_immutables
autofillHints: <String>['client1'],
currentEditingValue: TextEditingValue.empty,
hintText: 'hint',
);
});
test('equality operator works correctly', () {
expect(fakeAutoFillConfiguration, equals(fakeAutoFillConfiguration2));
expect(fakeAutoFillConfiguration.enabled, equals(fakeAutoFillConfiguration2.enabled));
expect(
fakeAutoFillConfiguration.uniqueIdentifier,
equals(fakeAutoFillConfiguration2.uniqueIdentifier),
);
expect(
fakeAutoFillConfiguration.autofillHints,
equals(fakeAutoFillConfiguration2.autofillHints),
);
expect(
fakeAutoFillConfiguration.currentEditingValue,
equals(fakeAutoFillConfiguration2.currentEditingValue),
);
expect(fakeAutoFillConfiguration.hintText, equals(fakeAutoFillConfiguration2.hintText));
});
test('hashCode works correctly', () {
expect(fakeAutoFillConfiguration.hashCode, equals(fakeAutoFillConfiguration2.hashCode));
expect(
fakeAutoFillConfiguration.enabled.hashCode,
equals(fakeAutoFillConfiguration2.enabled.hashCode),
);
expect(
fakeAutoFillConfiguration.uniqueIdentifier.hashCode,
equals(fakeAutoFillConfiguration2.uniqueIdentifier.hashCode),
);
expect(
Object.hashAll(fakeAutoFillConfiguration.autofillHints),
equals(Object.hashAll(fakeAutoFillConfiguration2.autofillHints)),
);
expect(
fakeAutoFillConfiguration.currentEditingValue.hashCode,
equals(fakeAutoFillConfiguration2.currentEditingValue.hashCode),
);
expect(
fakeAutoFillConfiguration.hintText.hashCode,
equals(fakeAutoFillConfiguration2.hintText.hashCode),
);
});
});
}
class FakeAutofillClient implements TextInputClient, AutofillClient {
FakeAutofillClient(this.currentTextEditingValue);
@override
String get autofillId => hashCode.toString();
@override
late TextInputConfiguration textInputConfiguration;
@override
void updateEditingValue(TextEditingValue newEditingValue) {
currentTextEditingValue = newEditingValue;
latestMethodCall = 'updateEditingValue';
}
@override
AutofillScope? currentAutofillScope;
String latestMethodCall = '';
@override
TextEditingValue currentTextEditingValue;
@override
void performAction(TextInputAction action) {
latestMethodCall = 'performAction';
}
@override
void performPrivateCommand(String action, Map<String, dynamic> data) {
latestMethodCall = 'performPrivateCommand';
}
@override
void insertContent(KeyboardInsertedContent content) {
latestMethodCall = 'commitContent';
}
@override
void updateFloatingCursor(RawFloatingCursorPoint point) {
latestMethodCall = 'updateFloatingCursor';
}
@override
void connectionClosed() {
latestMethodCall = 'connectionClosed';
}
@override
void showAutocorrectionPromptRect(int start, int end) {
latestMethodCall = 'showAutocorrectionPromptRect';
}
@override
void didChangeInputControl(TextInputControl? oldControl, TextInputControl? newControl) {
latestMethodCall = 'didChangeInputControl';
}
@override
void autofill(TextEditingValue newEditingValue) => updateEditingValue(newEditingValue);
@override
void showToolbar() {
latestMethodCall = 'showToolbar';
}
@override
void insertTextPlaceholder(Size size) {
latestMethodCall = 'insertTextPlaceholder';
}
@override
void removeTextPlaceholder() {
latestMethodCall = 'removeTextPlaceholder';
}
@override
void performSelector(String selectorName) {
latestMethodCall = 'performSelector';
}
}
class FakeAutofillScope with AutofillScopeMixin implements AutofillScope {
final Map<String, AutofillClient> clients = <String, AutofillClient>{};
@override
Iterable<AutofillClient> get autofillClients => clients.values;
@override
AutofillClient getAutofillClient(String autofillId) => clients[autofillId]!;
void register(AutofillClient client) {
clients.putIfAbsent(client.autofillId, () => client);
}
}