From e059fd6697ffdf55d8fc792596bdb124c5b07c70 Mon Sep 17 00:00:00 2001 From: Jakub Fidler <31575114+RisingOrange@users.noreply.github.com> Date: Fri, 5 Jan 2024 19:27:22 +0100 Subject: [PATCH] feat: update addon login window (#842) --- ankihub/gui/menu.py | 46 ++++++++++++++++++++++++++++++---- tests/addon/test_unit.py | 53 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 92 insertions(+), 7 deletions(-) diff --git a/ankihub/gui/menu.py b/ankihub/gui/menu.py index 53c96c31a..107e574df 100644 --- a/ankihub/gui/menu.py +++ b/ankihub/gui/menu.py @@ -100,23 +100,51 @@ def __init__(self): # Password self.password_box = QHBoxLayout() + self.password_box_label = QLabel("Password:") + self.password_box_text = QLineEdit("", self) self.password_box_text.setEchoMode(QLineEdit.EchoMode.Password) self.password_box_text.setMinimumWidth(300) qconnect(self.password_box_text.returnPressed, self.login) + + self.toggle_button = QPushButton("Show") + self.toggle_button.setCheckable(True) + self.toggle_button.setFixedHeight(30) + qconnect(self.toggle_button.toggled, self.refresh_password_visibility) + self.password_box.addWidget(self.password_box_label) self.password_box.addWidget(self.password_box_text) + self.password_box.addWidget(self.toggle_button) self.box_left.addLayout(self.password_box) - # Login - self.login_button = QPushButton("Login", self) + # Sign in button + self.login_button = QPushButton("Sign in", self) self.bottom_box_section.addWidget(self.login_button) qconnect(self.login_button.clicked, self.login) self.login_button.setDefault(True) - + self.bottom_box_section.setContentsMargins(0, 12, 0, 12) self.box_left.addLayout(self.bottom_box_section) + # Sign up / forgot password text + self.sign_up_and_recover_password_container = QVBoxLayout() + self.sign_up_and_recover_password_container.setSpacing(8) + self.sign_up_and_recover_password_container.setContentsMargins(0, 0, 0, 5) + self.login_button.setDefault(True) + self.sign_up_help_text = QLabel( + 'Don\'t have a AnkiHub account? Register now' + ) + self.sign_up_help_text.setOpenExternalLinks(True) + self.recover_password_help_text = QLabel( + 'Forgot password?' + ) + self.recover_password_help_text.setOpenExternalLinks(True) + self.sign_up_and_recover_password_container.addWidget(self.sign_up_help_text) + self.sign_up_and_recover_password_container.addWidget( + self.recover_password_help_text + ) + self.box_left.addLayout(self.sign_up_and_recover_password_container) + # Add left and right layouts to upper self.box_upper.addLayout(self.box_left) self.box_upper.addSpacing(20) @@ -127,12 +155,20 @@ def __init__(self): self.box_top.addStretch(1) self.setLayout(self.box_top) - self.setMinimumWidth(500) + self.setContentsMargins(20, 5, 0, 5) self.setSizePolicy(QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Minimum) - self.setWindowTitle("Login to AnkiHub.") + self.setWindowTitle("Sign in to AnkiHub.") self.setWindowFlags(self.windowFlags() | Qt.WindowType.WindowStaysOnTopHint) # type: ignore self.show() + def refresh_password_visibility(self) -> None: + if self.toggle_button.isChecked(): + self.password_box_text.setEchoMode(QLineEdit.EchoMode.Normal) + self.toggle_button.setText("Hide") + else: + self.password_box_text.setEchoMode(QLineEdit.EchoMode.Password) + self.toggle_button.setText("Show") + def login(self): username_or_email = self.username_or_email_box_text.text() password = self.password_box_text.text() diff --git a/tests/addon/test_unit.py b/tests/addon/test_unit.py index 46d3f3fb3..471669529 100644 --- a/tests/addon/test_unit.py +++ b/tests/addon/test_unit.py @@ -10,14 +10,14 @@ from textwrap import dedent from time import sleep from typing import Callable, ContextManager, Generator, List, Optional, Protocol, Tuple -from unittest.mock import Mock +from unittest.mock import Mock, patch import aqt import pytest from anki.decks import DeckId from anki.models import NotetypeDict from anki.notes import Note, NoteId -from aqt import QMenu +from aqt import QLineEdit, QMenu from aqt.qt import QDialog, QDialogButtonBox, Qt, QTimer, QWidget from pytest import MonkeyPatch from pytest_anki import AnkiSession @@ -590,6 +590,55 @@ def test_login( assert config.user() == username assert config.token() == token + @patch("ankihub.gui.menu.AnkiHubClient.login") + def test_password_visibility_toggle(self, login_mock, qtbot: QtBot): + password = "test_password" + + AnkiHubLogin.display_login() + + window: AnkiHubLogin = AnkiHubLogin._window + window.password_box_text.setText(password) + + # assert password is not visible and toggle button is at the initial state + assert window.password_box_text.echoMode() == QLineEdit.EchoMode.Password + assert window.toggle_button.isChecked() is False + + window.toggle_button.click() + qtbot.wait_until( + lambda: window.password_box_text.echoMode() == QLineEdit.EchoMode.Normal + ) + + assert window.password_box_text.echoMode() == QLineEdit.EchoMode.Normal + assert window.toggle_button.isChecked() is True + + window.toggle_button.click() + qtbot.wait_until( + lambda: window.password_box_text.echoMode() == QLineEdit.EchoMode.Password + ) + + assert window.password_box_text.echoMode() == QLineEdit.EchoMode.Password + assert window.toggle_button.isChecked() is False + + @patch("ankihub.gui.menu.AnkiHubClient.login") + def test_forgot_password_and_sign_up_links_are_present( + self, login_mock, qtbot: QtBot + ): + AnkiHubLogin.display_login() + + window: AnkiHubLogin = AnkiHubLogin._window + + assert window.sign_up_help_text.openExternalLinks() is True + assert ( + window.sign_up_help_text.text() + == 'Don\'t have a AnkiHub account? Register now' + ) + + assert window.recover_password_help_text.openExternalLinks() is True + assert ( + window.recover_password_help_text.text() + == 'Forgot password?' + ) + class TestSuggestionDialog: @pytest.mark.parametrize(