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(