6 #include <rapidjson/document.h>
10 #include "flutter/fml/macros.h"
13 #include "flutter/shell/platform/windows/testing/test_binary_messenger.h"
15 #include "gmock/gmock.h"
16 #include "gtest/gtest.h"
22 static constexpr
char kScanCodeKey[] =
"scanCode";
23 static constexpr
int kHandledScanCode = 20;
24 static constexpr
int kUnhandledScanCode = 21;
26 static constexpr
int kDefaultClientId = 42;
28 static constexpr
char kChannelName[] =
"flutter/textinput";
32 static constexpr
char kTextKey[] =
"text";
40 "TextInputClient.updateEditingState";
42 static std::unique_ptr<std::vector<uint8_t>> CreateResponse(
bool handled) {
44 std::make_unique<rapidjson::Document>(rapidjson::kObjectType);
45 auto& allocator = response_doc->GetAllocator();
46 response_doc->AddMember(
"handled", handled, allocator);
50 static std::unique_ptr<rapidjson::Document> EncodedClientConfig(
51 std::string type_name,
52 std::string input_action) {
53 auto arguments = std::make_unique<rapidjson::Document>(rapidjson::kArrayType);
54 auto& allocator = arguments->GetAllocator();
55 arguments->PushBack(kDefaultClientId, allocator);
57 rapidjson::Value config(rapidjson::kObjectType);
58 config.AddMember(
"inputAction", input_action, allocator);
60 rapidjson::Value type_info(rapidjson::kObjectType);
61 type_info.AddMember(
"name", type_name, allocator);
62 config.AddMember(
"inputType", type_info, allocator);
63 arguments->PushBack(config, allocator);
68 static std::unique_ptr<rapidjson::Document> EncodedEditingState(
70 TextRange selection) {
71 auto model = std::make_unique<TextInputModel>();
73 model->SetSelection(selection);
75 auto arguments = std::make_unique<rapidjson::Document>(rapidjson::kArrayType);
76 auto& allocator = arguments->GetAllocator();
77 arguments->PushBack(kDefaultClientId, allocator);
79 rapidjson::Value editing_state(rapidjson::kObjectType);
87 model->composing() ? model->composing_range().base() : -1;
88 int composing_extent =
89 model->composing() ? model->composing_range().extent() : -1;
93 rapidjson::Value(model->GetText(), allocator).Move(),
95 arguments->PushBack(editing_state, allocator);
100 class MockTextInputPluginDelegate :
public TextInputPluginDelegate {
102 MockTextInputPluginDelegate() {}
103 virtual ~MockTextInputPluginDelegate() =
default;
105 MOCK_METHOD(
void, OnCursorRectUpdated, (
const Rect&), (
override));
106 MOCK_METHOD(
void, OnResetImeComposing, (), (
override));
109 FML_DISALLOW_COPY_AND_ASSIGN(MockTextInputPluginDelegate);
114 TEST(TextInputPluginTest, TextMethodsWorksWithEmptyModel) {
115 auto handled_message = CreateResponse(
true);
116 auto unhandled_message = CreateResponse(
false);
117 int received_scancode = 0;
119 TestBinaryMessenger messenger(
120 [&received_scancode, &handled_message, &unhandled_message](
121 const std::string& channel,
const uint8_t*
message,
123 MockTextInputPluginDelegate delegate;
125 int redispatch_scancode = 0;
128 handler.
KeyboardHook(VK_RETURN, 100, WM_KEYDOWN,
'\n',
false,
false);
131 text.push_back(
'\n');
138 TEST(TextInputPluginTest, ClearClientResetsComposing) {
139 TestBinaryMessenger messenger([](
const std::string& channel,
140 const uint8_t*
message,
size_t message_size,
142 BinaryReply reply_handler = [](
const uint8_t* reply,
size_t reply_size) {};
144 MockTextInputPluginDelegate delegate;
147 EXPECT_CALL(delegate, OnResetImeComposing());
150 auto message = codec.EncodeMethodCall({
"TextInput.clearClient",
nullptr});
152 message->size(), reply_handler);
157 TEST(TextInputPluginTest, VerifyComposingSendStateUpdate) {
158 bool sent_message =
false;
159 TestBinaryMessenger messenger(
160 [&sent_message](
const std::string& channel,
const uint8_t*
message,
163 BinaryReply reply_handler = [](
const uint8_t* reply,
size_t reply_size) {};
165 MockTextInputPluginDelegate delegate;
171 auto arguments = std::make_unique<rapidjson::Document>(rapidjson::kArrayType);
172 auto& allocator = arguments->GetAllocator();
173 arguments->PushBack(kDefaultClientId, allocator);
174 rapidjson::Value config(rapidjson::kObjectType);
175 config.AddMember(
"inputAction",
"done", allocator);
176 config.AddMember(
"inputType",
"text", allocator);
178 arguments->PushBack(config, allocator);
180 codec.EncodeMethodCall({
"TextInput.setClient", std::move(arguments)});
181 messenger.SimulateEngineMessage(
"flutter/textinput",
message->data(),
182 message->size(), reply_handler);
185 sent_message =
false;
187 EXPECT_TRUE(sent_message);
190 sent_message =
false;
192 EXPECT_TRUE(sent_message);
202 sent_message =
false;
204 EXPECT_FALSE(sent_message);
207 sent_message =
false;
209 EXPECT_TRUE(sent_message);
212 TEST(TextInputPluginTest, VerifyInputActionNewlineInsertNewLine) {
214 std::vector<std::string> messages;
216 TestBinaryMessenger messenger(
217 [&messages](
const std::string& channel,
const uint8_t*
message,
219 std::string last_message(
reinterpret_cast<const char*
>(
message),
221 messages.push_back(last_message);
223 BinaryReply reply_handler = [](
const uint8_t* reply,
size_t reply_size) {};
225 MockTextInputPluginDelegate delegate;
231 auto set_client_arguments =
232 EncodedClientConfig(
"TextInputType.multiline",
"TextInputAction.newline");
233 auto message = codec.EncodeMethodCall(
234 {
"TextInput.setClient", std::move(set_client_arguments)});
235 messenger.SimulateEngineMessage(
"flutter/textinput",
message->data(),
236 message->size(), reply_handler);
239 handler.
KeyboardHook(VK_RETURN, 100, WM_KEYDOWN,
'\n',
false,
false);
243 EXPECT_EQ(messages.size(), 2);
246 auto encoded_arguments = EncodedEditingState(
"\n",
TextRange(1));
247 auto update_state_message = codec.EncodeMethodCall(
250 EXPECT_TRUE(std::equal(update_state_message->begin(),
251 update_state_message->end(),
252 messages.front().begin()));
255 auto arguments = std::make_unique<rapidjson::Document>(rapidjson::kArrayType);
256 auto& allocator = arguments->GetAllocator();
257 arguments->PushBack(kDefaultClientId, allocator);
259 rapidjson::Value(
"TextInputAction.newline", allocator).Move(), allocator);
260 auto invoke_action_message = codec.EncodeMethodCall(
261 {
"TextInputClient.performAction", std::move(arguments)});
263 EXPECT_TRUE(std::equal(invoke_action_message->begin(),
264 invoke_action_message->end(),
265 messages.back().begin()));
269 TEST(TextInputPluginTest, VerifyInputActionSendDoesNotInsertNewLine) {
270 std::vector<std::vector<uint8_t>> messages;
272 TestBinaryMessenger messenger(
273 [&messages](
const std::string& channel,
const uint8_t*
message,
275 int length =
static_cast<int>(message_size);
276 std::vector<uint8_t> last_message(length);
277 memcpy(&last_message[0], &
message[0], length *
sizeof(uint8_t));
278 messages.push_back(last_message);
280 BinaryReply reply_handler = [](
const uint8_t* reply,
size_t reply_size) {};
282 MockTextInputPluginDelegate delegate;
288 auto set_client_arguments =
289 EncodedClientConfig(
"TextInputType.multiline",
"TextInputAction.send");
290 auto message = codec.EncodeMethodCall(
291 {
"TextInput.setClient", std::move(set_client_arguments)});
292 messenger.SimulateEngineMessage(
"flutter/textinput",
message->data(),
293 message->size(), reply_handler);
296 handler.
KeyboardHook(VK_RETURN, 100, WM_KEYDOWN,
'\n',
false,
false);
299 EXPECT_EQ(messages.size(), 1);
302 auto arguments = std::make_unique<rapidjson::Document>(rapidjson::kArrayType);
303 auto& allocator = arguments->GetAllocator();
304 arguments->PushBack(kDefaultClientId, allocator);
306 rapidjson::Value(
"TextInputAction.send", allocator).Move(), allocator);
307 auto invoke_action_message = codec.EncodeMethodCall(
308 {
"TextInputClient.performAction", std::move(arguments)});
310 EXPECT_TRUE(std::equal(invoke_action_message->begin(),
311 invoke_action_message->end(),
312 messages.front().begin()));
315 TEST(TextInputPluginTest, TextEditingWorksWithDeltaModel) {
316 auto handled_message = CreateResponse(
true);
317 auto unhandled_message = CreateResponse(
false);
318 int received_scancode = 0;
320 TestBinaryMessenger messenger(
321 [&received_scancode, &handled_message, &unhandled_message](
322 const std::string& channel,
const uint8_t*
message,
324 MockTextInputPluginDelegate delegate;
326 int redispatch_scancode = 0;
329 auto args = std::make_unique<rapidjson::Document>(rapidjson::kArrayType);
330 auto& allocator = args->GetAllocator();
331 args->PushBack(123, allocator);
333 rapidjson::Value client_config(rapidjson::kObjectType);
336 args->PushBack(client_config, allocator);
340 EXPECT_TRUE(messenger.SimulateEngineMessage(
342 [](
const uint8_t* reply,
size_t reply_size) {}));
344 handler.
KeyboardHook(VK_RETURN, 100, WM_KEYDOWN,
'\n',
false,
false);
347 text.push_back(
'\n');
351 handler.
KeyboardHook(0x4E, 100, WM_KEYDOWN,
'n',
false,
false);
353 std::u16string textN;
356 handler.
KeyboardHook(0x49, 100, WM_KEYDOWN,
'i',
false,
false);
357 std::u16string textNi;
361 handler.
KeyboardHook(VK_RETURN, 100, WM_KEYDOWN,
'\n',
false,
false);
362 std::u16string textChineseCharacter;
363 text.push_back(u
'\u4F60');
372 TEST(TextInputPluginTest, CompositionCursorPos) {
373 int selection_base = -1;
374 TestBinaryMessenger messenger([&](
const std::string& channel,
375 const uint8_t*
message,
size_t size,
380 const auto& args = *method->arguments();
381 const auto& editing_state = args[1];
384 ASSERT_NE(base, editing_state.MemberEnd());
385 ASSERT_TRUE(base->value.IsInt());
386 ASSERT_NE(extent, editing_state.MemberEnd());
387 ASSERT_TRUE(extent->value.IsInt());
388 selection_base = base->value.GetInt();
389 EXPECT_EQ(extent->value.GetInt(), selection_base);
392 MockTextInputPluginDelegate delegate;
396 auto args = std::make_unique<rapidjson::Document>(rapidjson::kArrayType);
397 auto& allocator = args->GetAllocator();
398 args->PushBack(123, allocator);
399 rapidjson::Value client_config(rapidjson::kObjectType);
400 args->PushBack(client_config, allocator);
403 EXPECT_TRUE(messenger.SimulateEngineMessage(
405 [](
const uint8_t* reply,
size_t reply_size) {}));
408 EXPECT_EQ(selection_base, 0);
410 EXPECT_EQ(selection_base, 3);
414 EXPECT_EQ(selection_base, 3);
418 EXPECT_EQ(selection_base, 4);
421 EXPECT_EQ(selection_base, 5);
424 EXPECT_EQ(selection_base, 4);
427 EXPECT_EQ(selection_base, 5);
430 TEST(TextInputPluginTest, TransformCursorRect) {
438 double ime_width = 50;
439 double ime_height = 60;
442 std::array<std::array<double, 4>, 4> editabletext_transform = {
443 1.0, 0.0, 0.0, view_x,
444 0.0, 1.0, 0.0, view_y,
448 TestBinaryMessenger messenger([](
const std::string& channel,
449 const uint8_t*
message,
size_t message_size,
451 BinaryReply reply_handler = [](
const uint8_t* reply,
size_t reply_size) {};
453 MockTextInputPluginDelegate delegate;
458 EXPECT_CALL(delegate, OnCursorRectUpdated(
Rect{{view_x, view_y}, {0, 0}}));
462 std::make_unique<rapidjson::Document>(rapidjson::kObjectType);
463 auto& allocator = arguments->GetAllocator();
465 rapidjson::Value transoform(rapidjson::kArrayType);
466 for (
int i = 0; i < 4 * 4; i++) {
468 transoform.PushBack(editabletext_transform[i % 4][i / 4], allocator);
471 arguments->AddMember(
"transform", transoform, allocator);
473 auto message = codec.EncodeMethodCall(
474 {
"TextInput.setEditableSizeAndTransform", std::move(arguments)});
476 message->size(), reply_handler);
479 EXPECT_CALL(delegate,
480 OnCursorRectUpdated(
Rect{{view_x + ime_x, view_y + ime_y},
481 {ime_width, ime_height}}));
485 std::make_unique<rapidjson::Document>(rapidjson::kObjectType);
486 auto& allocator = arguments->GetAllocator();
488 arguments->AddMember(
"x", ime_x, allocator);
489 arguments->AddMember(
"y", ime_y, allocator);
490 arguments->AddMember(
"width", ime_width, allocator);
491 arguments->AddMember(
"height", ime_height, allocator);
493 auto message = codec.EncodeMethodCall(
494 {
"TextInput.setMarkedTextRect", std::move(arguments)});
496 message->size(), reply_handler);