Chromium Code Reviews| Index: ui/views/controls/textfield/textfield_unittest.cc |
| diff --git a/ui/views/controls/textfield/textfield_unittest.cc b/ui/views/controls/textfield/textfield_unittest.cc |
| index ca3839c18ed1cef77ff5352ee137d501e7202b35..fd57ce127d6a46032640e19bfe7c69f3ac64412c 100644 |
| --- a/ui/views/controls/textfield/textfield_unittest.cc |
| +++ b/ui/views/controls/textfield/textfield_unittest.cc |
| @@ -21,6 +21,7 @@ |
| #include "ui/base/ui_base_switches_util.h" |
| #include "ui/events/event.h" |
| #include "ui/events/keycodes/keyboard_codes.h" |
| +#include "ui/events/test/event_generator.h" |
| #include "ui/gfx/render_text.h" |
| #include "ui/strings/grit/ui_strings.h" |
| #include "ui/views/controls/textfield/textfield_controller.h" |
| @@ -56,6 +57,15 @@ namespace { |
| const base::char16 kHebrewLetterSamekh = 0x05E1; |
| +// The method used for dispatching key events. Either dispatch directly to the |
| +// MockInputMethod using the keycodes it expects internally, or dispatch to the |
| +// native window implementation using platform-specific keycodes, which should |
| +// be translated into the correct editing commands. |
| +enum EventDispatchMethod { |
| + DISPATCH_TO_INPUT_METHOD, |
| + DISPATCH_TO_WINDOW, |
| +}; |
| + |
| // A Textfield wrapper to intercept OnKey[Pressed|Released]() ressults. |
| class TestTextfield : public views::Textfield { |
| public: |
| @@ -159,6 +169,12 @@ class TextfieldTest : public ViewsTestBase, public TextfieldController { |
| copied_to_clipboard_(ui::CLIPBOARD_TYPE_LAST) { |
| } |
| + // Provide an implementation of GetParam which parameterized subclasses can |
| + // override using gtest's GetParam(). |
| + virtual EventDispatchMethod GetParamWithDefault() { |
| + return DISPATCH_TO_INPUT_METHOD; |
| + } |
| + |
| // ::testing::Test: |
| void TearDown() override { |
| if (widget_) |
| @@ -202,7 +218,12 @@ class TextfieldTest : public ViewsTestBase, public TextfieldController { |
| textfield_ = new TestTextfield(); |
| textfield_->set_controller(this); |
| widget_ = new Widget(); |
| - Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); |
| + |
| + // The widget type must be an activatable type, and we don't want to worry |
| + // about the non-client view, which leaves just TYPE_WINDOW_FRAMELESS. |
| + Widget::InitParams params = |
| + CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS); |
| + |
| params.bounds = gfx::Rect(100, 100, 100, 100); |
| widget_->Init(params); |
| View* container = new View(); |
| @@ -224,8 +245,11 @@ class TextfieldTest : public ViewsTestBase, public TextfieldController { |
| input_method_ = new MockInputMethod(); |
| widget_->ReplaceInputMethod(input_method_); |
| - // Activate the widget and focus the textfield for input handling. |
| - widget_->Activate(); |
| + // Since the window type is activatable, showing the widget will also |
| + // activate it. Calling Activate directly is insufficient, since that does |
| + // not also _focus_ an aura::Window (i.e. using the FocusClient). Both the |
| + // widget and the textfield must have focus to properly handle input. |
| + widget_->Show(); |
| textfield_->RequestFocus(); |
| // On Mac, activation is asynchronous since desktop widgets are used. We |
| @@ -233,6 +257,11 @@ class TextfieldTest : public ViewsTestBase, public TextfieldController { |
| #if defined(OS_MACOSX) && !defined(USE_AURA) |
| fake_activation_ = test::WidgetTest::FakeWidgetIsActiveAlways(); |
| #endif |
| + |
| + if (GetParamWithDefault() == DISPATCH_TO_WINDOW) { |
| + event_generator_.reset(new ui::test::EventGenerator( |
| + GetContext(), widget_->GetNativeWindow())); |
| + } |
| } |
| ui::MenuModel* GetContextMenuModel() { |
| @@ -240,22 +269,50 @@ class TextfieldTest : public ViewsTestBase, public TextfieldController { |
| return test_api_->context_menu_contents(); |
| } |
| + // On Mac, true if testing a views::TextField in an NSWindow using native |
| + // NSEvents. On other platforms, a helper function to avoid ifdef litter. |
| + bool TestingNativeMac() { |
| +#if defined(OS_MACOSX) |
| + return GetParamWithDefault() == DISPATCH_TO_WINDOW; |
| +#else |
| + return false; |
| +#endif |
| + } |
| + |
| protected: |
| void SendKeyEvent(ui::KeyboardCode key_code, |
| bool alt, |
| bool shift, |
| - bool control, |
| + bool control_or_command, |
| bool caps_lock) { |
| - int flags = (alt ? ui::EF_ALT_DOWN : 0) | |
| - (shift ? ui::EF_SHIFT_DOWN : 0) | |
| + bool control = control_or_command; |
| + bool command = false; |
| + |
| + // By default, swap control and command for native events on Mac. This |
| + // handles most cases. |
| + if (TestingNativeMac()) |
| + std::swap(control, command); |
| + |
| + int flags = (alt ? ui::EF_ALT_DOWN : 0) | (shift ? ui::EF_SHIFT_DOWN : 0) | |
| (control ? ui::EF_CONTROL_DOWN : 0) | |
| + (command ? ui::EF_COMMAND_DOWN : 0) | |
| (caps_lock ? ui::EF_CAPS_LOCK_DOWN : 0); |
| + |
| + // If dispatching to the window, ask the event generator to generate |
| + // events populated with native event information for each platform. |
| + if (GetParamWithDefault() == DISPATCH_TO_WINDOW) { |
| + event_generator_->PressKey(key_code, flags); |
| + return; |
| + } |
| + |
| ui::KeyEvent event(ui::ET_KEY_PRESSED, key_code, flags); |
| input_method_->DispatchKeyEvent(event); |
| } |
| - void SendKeyEvent(ui::KeyboardCode key_code, bool shift, bool control) { |
| - SendKeyEvent(key_code, false, shift, control, false); |
| + void SendKeyEvent(ui::KeyboardCode key_code, |
| + bool shift, |
| + bool control_or_command) { |
| + SendKeyEvent(key_code, false, shift, control_or_command, false); |
| } |
| void SendKeyEvent(ui::KeyboardCode key_code) { |
| @@ -269,6 +326,7 @@ class TextfieldTest : public ViewsTestBase, public TextfieldController { |
| static_cast<ui::KeyboardCode>(ui::VKEY_A + ch - 'a'); |
| SendKeyEvent(code); |
| } else { |
| + // TODO(tapted): Support DISPATCH_TO_WINDOW here. |
| ui::KeyEvent event(ch, ui::VKEY_UNKNOWN, ui::EF_NONE); |
| input_method_->DispatchKeyEvent(event); |
| } |
| @@ -357,10 +415,57 @@ class TextfieldTest : public ViewsTestBase, public TextfieldController { |
| private: |
| ui::ClipboardType copied_to_clipboard_; |
| scoped_ptr<test::WidgetTest::FakeActivation> fake_activation_; |
| + scoped_ptr<ui::test::EventGenerator> event_generator_; |
|
oshima
2015/01/30 17:44:17
I believe the test was updated to use MockInputMet
tapted
2015/02/02 12:46:37
Done.
Just a couple of surprises. And of course s
|
| DISALLOW_COPY_AND_ASSIGN(TextfieldTest); |
| }; |
| +// TextfieldTest that overrides GetParamWithDefault() to override the default |
| +// using GetParam() from the gtest interface. |
| +class TextfieldTestWithParam |
| + : public TextfieldTest, |
| + public ::testing::WithParamInterface<EventDispatchMethod> { |
| + public: |
| + TextfieldTestWithParam() {} |
| + |
| + void SendHomeEvent(bool shift) { |
| + if (TestingNativeMac()) { |
| + // Use Cmd+Left on native Mac. An RTL-agnostic "end" doesn't have a |
| + // default key-binding on Mac. |
| + SendKeyEvent(ui::VKEY_LEFT, shift /* shift */, true /* command */); |
| + return; |
| + } |
| + SendKeyEvent(ui::VKEY_HOME, shift /* shift */, false /* control */); |
| + } |
| + |
| + void SendEndEvent(bool shift) { |
| + if (TestingNativeMac()) { |
| + SendKeyEvent(ui::VKEY_RIGHT, shift, true); // Cmd+Right. |
| + return; |
| + } |
| + SendKeyEvent(ui::VKEY_END, shift, false); |
| + } |
| + |
| + // Sends {delete, move, select} word {forward, backward}. |
| + void SendWordEvent(ui::KeyboardCode key, bool shift) { |
| + bool alt = false; |
| + bool control = true; |
| + bool caps = false; |
| + if (TestingNativeMac()) { |
| + // Use Alt+Left/Right/Backspace on native Mac. |
| + alt = true; |
| + control = false; |
| + } |
| + SendKeyEvent(key, alt, shift, control, caps); |
| + } |
| + |
| + // Overridden from TextfieldTest: |
| + EventDispatchMethod GetParamWithDefault() override { return GetParam(); } |
| + |
| + private: |
| + DISALLOW_COPY_AND_ASSIGN(TextfieldTestWithParam); |
| +}; |
| + |
| TEST_F(TextfieldTest, ModelChangesTest) { |
| InitTextfield(); |
| @@ -398,11 +503,11 @@ TEST_F(TextfieldTest, KeyTest) { |
| EXPECT_STR_EQ("TexT!1!1", textfield_->text()); |
| } |
| -TEST_F(TextfieldTest, ControlAndSelectTest) { |
| +TEST_P(TextfieldTestWithParam, ControlAndSelectTest) { |
| // Insert a test string in a textfield. |
| InitTextfield(); |
| textfield_->SetText(ASCIIToUTF16("one two three")); |
| - SendKeyEvent(ui::VKEY_HOME, false /* shift */, false /* control */); |
| + SendHomeEvent(false); |
| SendKeyEvent(ui::VKEY_RIGHT, true, false); |
| SendKeyEvent(ui::VKEY_RIGHT, true, false); |
| SendKeyEvent(ui::VKEY_RIGHT, true, false); |
| @@ -410,13 +515,13 @@ TEST_F(TextfieldTest, ControlAndSelectTest) { |
| EXPECT_STR_EQ("one", textfield_->GetSelectedText()); |
| // Test word select. |
| - SendKeyEvent(ui::VKEY_RIGHT, true, true); |
| + SendWordEvent(ui::VKEY_RIGHT, true); |
| EXPECT_STR_EQ("one two", textfield_->GetSelectedText()); |
| - SendKeyEvent(ui::VKEY_RIGHT, true, true); |
| + SendWordEvent(ui::VKEY_RIGHT, true); |
| EXPECT_STR_EQ("one two three", textfield_->GetSelectedText()); |
| - SendKeyEvent(ui::VKEY_LEFT, true, true); |
| + SendWordEvent(ui::VKEY_LEFT, true); |
| EXPECT_STR_EQ("one two ", textfield_->GetSelectedText()); |
| - SendKeyEvent(ui::VKEY_LEFT, true, true); |
| + SendWordEvent(ui::VKEY_LEFT, true); |
| EXPECT_STR_EQ("one ", textfield_->GetSelectedText()); |
| // Replace the selected text. |
| @@ -427,13 +532,13 @@ TEST_F(TextfieldTest, ControlAndSelectTest) { |
| SendKeyEvent(ui::VKEY_SPACE, false, false); |
| EXPECT_STR_EQ("ZERO two three", textfield_->text()); |
| - SendKeyEvent(ui::VKEY_END, true, false); |
| + SendEndEvent(true); |
| EXPECT_STR_EQ("two three", textfield_->GetSelectedText()); |
| - SendKeyEvent(ui::VKEY_HOME, true, false); |
| + SendHomeEvent(true); |
| EXPECT_STR_EQ("ZERO ", textfield_->GetSelectedText()); |
| } |
| -TEST_F(TextfieldTest, InsertionDeletionTest) { |
| +TEST_P(TextfieldTestWithParam, InsertionDeletionTest) { |
| // Insert a test string in a textfield. |
| InitTextfield(); |
| for (size_t i = 0; i < 10; i++) |
| @@ -455,14 +560,17 @@ TEST_F(TextfieldTest, InsertionDeletionTest) { |
| EXPECT_STR_EQ("k", textfield_->text()); |
| // Delete the previous word from cursor. |
| + bool shift = false; |
| textfield_->SetText(ASCIIToUTF16("one two three four")); |
| - SendKeyEvent(ui::VKEY_END); |
| - SendKeyEvent(ui::VKEY_BACK, false, false, true, false); |
| + SendEndEvent(shift); |
| + SendWordEvent(ui::VKEY_BACK, shift); |
| EXPECT_STR_EQ("one two three ", textfield_->text()); |
| - // Delete to a line break on Linux and ChromeOS, to a word break on Windows. |
| - SendKeyEvent(ui::VKEY_LEFT, false, false, true, false); |
| - SendKeyEvent(ui::VKEY_BACK, false, true, true, false); |
| + // Delete to a line break on Linux and ChromeOS, to a word break on Windows |
| + // and Mac. |
| + SendWordEvent(ui::VKEY_LEFT, shift); |
| + shift = true; |
| + SendWordEvent(ui::VKEY_BACK, shift); |
| #if defined(OS_LINUX) |
| EXPECT_STR_EQ("three ", textfield_->text()); |
| #else |
| @@ -471,13 +579,16 @@ TEST_F(TextfieldTest, InsertionDeletionTest) { |
| // Delete the next word from cursor. |
| textfield_->SetText(ASCIIToUTF16("one two three four")); |
| - SendKeyEvent(ui::VKEY_HOME); |
| - SendKeyEvent(ui::VKEY_DELETE, false, false, true, false); |
| + shift = false; |
| + SendHomeEvent(shift); |
| + SendWordEvent(ui::VKEY_DELETE, shift); |
| EXPECT_STR_EQ(" two three four", textfield_->text()); |
| - // Delete to a line break on Linux and ChromeOS, to a word break on Windows. |
| - SendKeyEvent(ui::VKEY_RIGHT, false, false, true, false); |
| - SendKeyEvent(ui::VKEY_DELETE, false, true, true, false); |
| + // Delete to a line break on Linux and ChromeOS, to a word break on Windows |
| + // and Mac. |
| + SendWordEvent(ui::VKEY_RIGHT, shift); |
| + shift = true; |
| + SendWordEvent(ui::VKEY_DELETE, shift); |
| #if defined(OS_LINUX) |
| EXPECT_STR_EQ(" two", textfield_->text()); |
| #else |
| @@ -2115,4 +2226,12 @@ TEST_F(TextfieldTouchSelectionTest, TapOnSelection) { |
| EXPECT_EQ(tap_range, range); |
| } |
| +// Instantiate parameterized tests. These are just the ones above using "TEST_P" |
| +// rather than "TEST_F". |
| +INSTANTIATE_TEST_CASE_P( |
| + TextfieldTestWithParamInstance, |
| + TextfieldTestWithParam, |
| + ::testing::Values<EventDispatchMethod>(DISPATCH_TO_INPUT_METHOD, |
| + DISPATCH_TO_WINDOW)); |
| + |
| } // namespace views |