| // Copyright 2017 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. |
| |
| #include "ash/login/ui/login_password_view.h" |
| |
| #include <algorithm> |
| #include <memory> |
| #include <set> |
| #include <vector> |
| |
| #include "ash/login/ui/login_pin_view.h" |
| #include "ash/login/ui/login_test_base.h" |
| #include "base/bind.h" |
| #include "base/timer/mock_timer.h" |
| #include "ui/events/test/event_generator.h" |
| #include "ui/views/widget/widget.h" |
| |
| namespace ash { |
| |
| namespace { |
| |
| class LoginPinViewTest : public LoginTestBase { |
| public: |
| LoginPinViewTest(const LoginPinViewTest&) = delete; |
| LoginPinViewTest& operator=(const LoginPinViewTest&) = delete; |
| |
| protected: |
| LoginPinViewTest() = default; |
| ~LoginPinViewTest() override = default; |
| |
| // Creates login pin view with the specified keyboard |style| and sets it up |
| // in a widget. |
| void CreateLoginPinViewWithStyle(LoginPinView::Style style) { |
| view_ = |
| new LoginPinView(style, CreateDefaultLoginPalette(), |
| base::BindRepeating(&LoginPinViewTest::OnPinKey, |
| base::Unretained(this)), |
| base::BindRepeating(&LoginPinViewTest::OnPinBackspace, |
| base::Unretained(this)), |
| base::BindRepeating(&LoginPinViewTest::OnPinSubmit, |
| base::Unretained(this))); |
| |
| SetWidget(CreateWidgetWithContent(view_)); |
| } |
| |
| // Called when a password is submitted. |
| void OnPinKey(int value) { value_ = value; } |
| void OnPinBackspace() { ++backspace_; } |
| void OnPinSubmit() { ++submit_; } |
| |
| LoginPinView* view_ = nullptr; // Owned by test widget view hierarchy. |
| absl::optional<int> value_; |
| // Number of times the backspace event has been fired. |
| int backspace_ = 0; |
| // Number of times the submit event has been fired. |
| int submit_ = 0; |
| }; |
| |
| } // namespace |
| |
| // Verifies that PIN submit works with 'Enter'. |
| TEST_F(LoginPinViewTest, ButtonsFireEvents) { |
| CreateLoginPinViewWithStyle(LoginPinView::Style::kAlphanumeric); |
| ui::test::EventGenerator* generator = GetEventGenerator(); |
| LoginPinView::TestApi test_api(view_); |
| |
| // Verify pin button events are emitted with the correct value. |
| for (int i = 0; i <= 9; ++i) { |
| test_api.GetButton(i)->GetFocusManager()->SetFocusedView( |
| test_api.GetButton(i)); |
| generator->PressKey(ui::KeyboardCode::VKEY_RETURN, ui::EF_NONE); |
| ASSERT_TRUE(value_.has_value()); |
| EXPECT_EQ(*value_, i); |
| value_.reset(); |
| } |
| |
| // Verify backspace events are emitted. |
| EXPECT_EQ(0, backspace_); |
| test_api.GetBackspaceButton()->SetEnabled(true); |
| test_api.GetBackspaceButton()->GetFocusManager()->SetFocusedView( |
| test_api.GetBackspaceButton()); |
| generator->PressKey(ui::KeyboardCode::VKEY_RETURN, ui::EF_NONE); |
| EXPECT_EQ(1, backspace_); |
| |
| // Verify backspace events are emitted. |
| EXPECT_EQ(0, submit_); |
| test_api.GetSubmitButton()->SetEnabled(true); |
| test_api.GetSubmitButton()->GetFocusManager()->SetFocusedView( |
| test_api.GetSubmitButton()); |
| generator->PressKey(ui::KeyboardCode::VKEY_RETURN, ui::EF_NONE); |
| EXPECT_EQ(1, submit_); |
| } |
| |
| // Validates buttons have the correct spacing for alphanumeric PIN keyboard |
| // style. |
| TEST_F(LoginPinViewTest, AlphanumericKeyboardButtonSpacingAndSize) { |
| CreateLoginPinViewWithStyle(LoginPinView::Style::kAlphanumeric); |
| LoginPinView::TestApi test_api(view_); |
| |
| const gfx::Size expected_button_size = |
| LoginPinView::TestApi::GetButtonSize(LoginPinView::Style::kAlphanumeric); |
| |
| // Validate pin button size. |
| for (int i = 0; i <= 9; ++i) { |
| DCHECK_EQ(test_api.GetButton(i)->size().width(), |
| expected_button_size.width()); |
| DCHECK_EQ(test_api.GetButton(i)->size().height(), |
| expected_button_size.height()); |
| } |
| |
| // Validate backspace button size. |
| DCHECK_EQ(test_api.GetBackspaceButton()->size().width(), |
| expected_button_size.width()); |
| DCHECK_EQ(test_api.GetBackspaceButton()->size().height(), |
| expected_button_size.height()); |
| |
| // Validate submit button size. |
| DCHECK_EQ(test_api.GetSubmitButton()->size().width(), |
| expected_button_size.width()); |
| DCHECK_EQ(test_api.GetSubmitButton()->size().height(), |
| expected_button_size.height()); |
| |
| // Record all the x/y coordinates of the buttons. |
| std::set<int> seen_x; |
| std::set<int> seen_y; |
| for (int i = 0; i <= 9; ++i) { |
| gfx::Rect screen_bounds = test_api.GetButton(i)->GetBoundsInScreen(); |
| seen_x.insert(screen_bounds.x()); |
| seen_y.insert(screen_bounds.y()); |
| } |
| seen_x.insert(test_api.GetBackspaceButton()->GetBoundsInScreen().x()); |
| seen_y.insert(test_api.GetBackspaceButton()->GetBoundsInScreen().y()); |
| seen_x.insert(test_api.GetSubmitButton()->GetBoundsInScreen().x()); |
| seen_y.insert(test_api.GetSubmitButton()->GetBoundsInScreen().y()); |
| |
| // Sort the coordinates so we can easily check the distance between them. |
| std::vector<int> sorted_x(seen_x.begin(), seen_x.end()); |
| std::vector<int> sorted_y(seen_y.begin(), seen_y.end()); |
| std::sort(sorted_x.begin(), sorted_x.end()); |
| std::sort(sorted_y.begin(), sorted_y.end()); |
| |
| // Validate each x or y coordinate has the correct distance between it and the |
| // next one. This is correct because we have already validated button size. |
| EXPECT_EQ(3u, sorted_x.size()); |
| for (size_t i = 0; i < sorted_x.size() - 1; ++i) |
| EXPECT_EQ(sorted_x[i] + expected_button_size.width(), sorted_x[i + 1]); |
| |
| EXPECT_EQ(4u, sorted_y.size()); |
| for (size_t i = 0; i < sorted_y.size() - 1; ++i) |
| EXPECT_EQ(sorted_y[i] + expected_button_size.height(), sorted_y[i + 1]); |
| } |
| |
| // Validates buttons have the correct spacing for numeric PIN keyboard style. |
| TEST_F(LoginPinViewTest, NumericKeyboardButtonSpacingAndSize) { |
| CreateLoginPinViewWithStyle(LoginPinView::Style::kNumeric); |
| LoginPinView::TestApi test_api(view_); |
| |
| const gfx::Size expected_button_size = |
| LoginPinView::TestApi::GetButtonSize(LoginPinView::Style::kNumeric); |
| |
| // Validate pin button size. |
| for (int i = 0; i <= 9; ++i) { |
| DCHECK_EQ(test_api.GetButton(i)->size().width(), |
| expected_button_size.width()); |
| DCHECK_EQ(test_api.GetButton(i)->size().height(), |
| expected_button_size.height()); |
| } |
| |
| // Validate backspace button size. |
| DCHECK_EQ(test_api.GetBackspaceButton()->size().width(), |
| expected_button_size.width()); |
| DCHECK_EQ(test_api.GetBackspaceButton()->size().height(), |
| expected_button_size.height()); |
| |
| // Validate submit button size. |
| DCHECK_EQ(test_api.GetSubmitButton()->size().width(), |
| expected_button_size.width()); |
| DCHECK_EQ(test_api.GetSubmitButton()->size().height(), |
| expected_button_size.height()); |
| |
| // Record all the x/y coordinates of the buttons. |
| std::set<int> seen_x; |
| std::set<int> seen_y; |
| for (int i = 0; i <= 9; ++i) { |
| gfx::Rect screen_bounds = test_api.GetButton(i)->GetBoundsInScreen(); |
| seen_x.insert(screen_bounds.x()); |
| seen_y.insert(screen_bounds.y()); |
| } |
| seen_x.insert(test_api.GetBackspaceButton()->GetBoundsInScreen().x()); |
| seen_y.insert(test_api.GetBackspaceButton()->GetBoundsInScreen().y()); |
| seen_x.insert(test_api.GetSubmitButton()->GetBoundsInScreen().x()); |
| seen_y.insert(test_api.GetSubmitButton()->GetBoundsInScreen().y()); |
| |
| // Sort the coordinates so we can easily check the distance between them. |
| std::vector<int> sorted_x(seen_x.begin(), seen_x.end()); |
| std::vector<int> sorted_y(seen_y.begin(), seen_y.end()); |
| std::sort(sorted_x.begin(), sorted_x.end()); |
| std::sort(sorted_y.begin(), sorted_y.end()); |
| |
| // Validate each x or y coordinate has the correct distance between it and the |
| // next one. This is correct because we have already validated button size. |
| EXPECT_EQ(3u, sorted_x.size()); |
| for (size_t i = 0; i < sorted_x.size() - 1; ++i) |
| EXPECT_EQ(sorted_x[i] + expected_button_size.width(), sorted_x[i + 1]); |
| |
| EXPECT_EQ(4u, sorted_y.size()); |
| for (size_t i = 0; i < sorted_y.size() - 1; ++i) |
| EXPECT_EQ(sorted_y[i] + expected_button_size.height(), sorted_y[i + 1]); |
| } |
| |
| // Verifies that holding the backspace button automatically triggers and begins |
| // repeating if it is held down. |
| TEST_F(LoginPinViewTest, BackspaceAutoSubmitsAndRepeats) { |
| CreateLoginPinViewWithStyle(LoginPinView::Style::kAlphanumeric); |
| ui::test::EventGenerator* generator = GetEventGenerator(); |
| LoginPinView::TestApi test_api(view_); |
| |
| // Install mock timers into the PIN view. |
| auto delay_timer0 = std::make_unique<base::MockOneShotTimer>(); |
| auto repeat_timer0 = std::make_unique<base::MockRepeatingTimer>(); |
| base::MockOneShotTimer* delay_timer = delay_timer0.get(); |
| base::MockRepeatingTimer* repeat_timer = repeat_timer0.get(); |
| test_api.SetBackspaceTimers(std::move(delay_timer0), |
| std::move(repeat_timer0)); |
| |
| // Verify backspace events are emitted. |
| EXPECT_EQ(0, backspace_); |
| test_api.GetBackspaceButton()->SetEnabled(true); |
| generator->MoveMouseTo( |
| test_api.GetBackspaceButton()->GetBoundsInScreen().CenterPoint()); |
| generator->PressLeftButton(); |
| |
| // Backspace event triggers after delay timer fires. |
| delay_timer->Fire(); |
| EXPECT_EQ(1, backspace_); |
| |
| // Backspace event triggers after repeat timer fires. |
| backspace_ = 0; |
| for (int i = 0; i < 5; ++i) { |
| repeat_timer->Fire(); |
| EXPECT_EQ(i + 1, backspace_); |
| } |
| |
| // Backspace does not trigger after releasing the mouse. |
| backspace_ = 0; |
| generator->ReleaseLeftButton(); |
| EXPECT_EQ(0, backspace_); |
| } |
| |
| // Verifies that clicking on the submit button fires the corresponding event. |
| TEST_F(LoginPinViewTest, SubmitButtonClick) { |
| CreateLoginPinViewWithStyle(LoginPinView::Style::kAlphanumeric); |
| ui::test::EventGenerator* generator = GetEventGenerator(); |
| LoginPinView::TestApi test_api(view_); |
| |
| test_api.GetSubmitButton()->SetEnabled(true); |
| generator->MoveMouseTo( |
| test_api.GetSubmitButton()->GetBoundsInScreen().CenterPoint()); |
| |
| EXPECT_EQ(0, submit_); |
| generator->PressLeftButton(); |
| EXPECT_EQ(1, submit_); |
| } |
| |
| } // namespace ash |