mirror of
https://github.com/bec-project/bec_widgets.git
synced 2026-03-04 16:02:51 +01:00
feat: add feedback dialog
This commit is contained in:
@@ -3,6 +3,7 @@ from __future__ import annotations
|
||||
import os
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from bec_lib import messages
|
||||
from bec_lib.endpoints import MessageEndpoints
|
||||
from qtpy.QtCore import QEvent, QSize, Qt, QTimer
|
||||
from qtpy.QtGui import QAction, QActionGroup, QIcon
|
||||
@@ -31,6 +32,7 @@ from bec_widgets.widgets.containers.main_window.addons.notification_center.notif
|
||||
from bec_widgets.widgets.containers.main_window.addons.scroll_label import ScrollLabel
|
||||
from bec_widgets.widgets.containers.main_window.addons.web_links import BECWebLinksMixin
|
||||
from bec_widgets.widgets.progress.scan_progressbar.scan_progressbar import ScanProgressBar
|
||||
from bec_widgets.widgets.utility.feedback_dialog.feedback_dialog import FeedbackDialog
|
||||
|
||||
MODULE_PATH = os.path.dirname(bec_widgets.__file__)
|
||||
|
||||
@@ -342,6 +344,34 @@ class BECMainWindow(BECWidget, QMainWindow):
|
||||
help_menu.addAction(widgets_docs)
|
||||
help_menu.addAction(bug_report)
|
||||
|
||||
# Add separator before feedback
|
||||
help_menu.addSeparator()
|
||||
|
||||
# Feedback action
|
||||
feedback_icon = QApplication.style().standardIcon(
|
||||
QStyle.StandardPixmap.SP_MessageBoxQuestion
|
||||
)
|
||||
feedback_action = QAction("Feedback", self)
|
||||
feedback_action.setIcon(feedback_icon)
|
||||
feedback_action.triggered.connect(self._show_feedback_dialog)
|
||||
help_menu.addAction(feedback_action)
|
||||
|
||||
def _show_feedback_dialog(self):
|
||||
"""Show the feedback dialog and handle the submitted feedback."""
|
||||
dialog = FeedbackDialog(self)
|
||||
|
||||
def on_feedback_submitted(rating: int, comment: str, email: str):
|
||||
rating = max(1, min(rating, 5)) # Ensure rating is between 1 and 5
|
||||
username = os.getlogin()
|
||||
|
||||
message = messages.FeedbackMessage(
|
||||
feedback=comment, rating=rating, contact=email, username=username
|
||||
)
|
||||
self.bec_dispatcher.client.connector.send(MessageEndpoints.submit_feedback(), message)
|
||||
|
||||
dialog.feedback_submitted.connect(on_feedback_submitted)
|
||||
dialog.exec()
|
||||
|
||||
################################################################################
|
||||
# Status Bar Addons
|
||||
################################################################################
|
||||
|
||||
294
bec_widgets/widgets/utility/feedback_dialog/feedback_dialog.py
Normal file
294
bec_widgets/widgets/utility/feedback_dialog/feedback_dialog.py
Normal file
@@ -0,0 +1,294 @@
|
||||
from qtpy.QtCore import Qt, Signal
|
||||
from qtpy.QtGui import QColor, QFont
|
||||
from qtpy.QtWidgets import (
|
||||
QApplication,
|
||||
QDialog,
|
||||
QHBoxLayout,
|
||||
QLabel,
|
||||
QLineEdit,
|
||||
QPushButton,
|
||||
QTextEdit,
|
||||
QVBoxLayout,
|
||||
QWidget,
|
||||
)
|
||||
|
||||
from bec_widgets.utils.error_popups import SafeConnect, SafeSlot
|
||||
|
||||
|
||||
class StarRating(QWidget):
|
||||
"""
|
||||
A star rating widget that allows users to rate from 1 to 5 stars.
|
||||
"""
|
||||
|
||||
rating_changed = Signal(int)
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self._rating = 0
|
||||
self._hovered_star = 0
|
||||
self._star_buttons = []
|
||||
|
||||
# Get theme colors
|
||||
theme = getattr(QApplication.instance(), "theme", None)
|
||||
if theme:
|
||||
SafeConnect(self, theme.theme_changed, self._update_theme_colors)
|
||||
self._update_theme_colors()
|
||||
|
||||
# Enable mouse tracking to handle hover across the entire widget
|
||||
self.setMouseTracking(True)
|
||||
|
||||
layout = QHBoxLayout()
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
layout.setSpacing(5)
|
||||
|
||||
for i in range(5):
|
||||
btn = QPushButton("★")
|
||||
btn.setFixedSize(30, 30)
|
||||
btn.setFlat(True)
|
||||
btn.setCursor(Qt.CursorShape.PointingHandCursor)
|
||||
btn.clicked.connect(lambda checked=False, idx=i + 1: self._set_rating(idx))
|
||||
layout.addWidget(btn)
|
||||
self._star_buttons.append(btn)
|
||||
|
||||
self.setLayout(layout)
|
||||
self._update_display()
|
||||
|
||||
@SafeSlot(str)
|
||||
def _update_theme_colors(self, _theme: str | None = None):
|
||||
"""Update colors based on theme."""
|
||||
theme = getattr(QApplication.instance(), "theme", None)
|
||||
colors = theme.colors if theme else {}
|
||||
|
||||
self._inactive_color = colors.get("SEPARATOR", QColor(200, 200, 200))
|
||||
self._active_color = colors.get("ACCENT_WARNING", QColor(255, 193, 7))
|
||||
|
||||
# Update display if already initialized
|
||||
if hasattr(self, "_star_buttons") and self._star_buttons:
|
||||
self._update_display()
|
||||
|
||||
def _set_rating(self, rating: int):
|
||||
"""Set the rating and emit the signal."""
|
||||
if self._rating != rating:
|
||||
self._rating = rating
|
||||
self.rating_changed.emit(rating)
|
||||
self._update_display()
|
||||
|
||||
def mouseMoveEvent(self, event):
|
||||
"""Handle mouse movement to update hovered star."""
|
||||
# Calculate which star is being hovered based on mouse position
|
||||
x_pos = event.pos().x()
|
||||
star_idx = 0
|
||||
|
||||
# Find which star region we're in (including gaps between stars)
|
||||
for i, btn in enumerate(self._star_buttons):
|
||||
btn_geometry = btn.geometry()
|
||||
# If we're to the right of this button's left edge, this is the current star
|
||||
# (including the gap before the next button)
|
||||
if x_pos >= btn_geometry.left():
|
||||
star_idx = i + 1
|
||||
else:
|
||||
break
|
||||
|
||||
if star_idx != self._hovered_star:
|
||||
self._hovered_star = star_idx
|
||||
self._update_display()
|
||||
|
||||
super().mouseMoveEvent(event)
|
||||
|
||||
def leaveEvent(self, event):
|
||||
"""Handle mouse leaving the widget."""
|
||||
self._hovered_star = 0
|
||||
self._update_display()
|
||||
super().leaveEvent(event)
|
||||
|
||||
def _update_display(self):
|
||||
"""Update the visual display of stars."""
|
||||
display_rating = self._hovered_star if self._hovered_star > 0 else self._rating
|
||||
inactive_color_name = self._inactive_color.name()
|
||||
active_color_name = self._active_color.name()
|
||||
|
||||
for i, btn in enumerate(self._star_buttons):
|
||||
if i < display_rating:
|
||||
btn.setStyleSheet(
|
||||
f"""
|
||||
QPushButton {{
|
||||
border: none;
|
||||
background: transparent;
|
||||
font-size: 24px;
|
||||
color: {active_color_name};
|
||||
}}
|
||||
"""
|
||||
)
|
||||
else:
|
||||
btn.setStyleSheet(
|
||||
f"""
|
||||
QPushButton {{
|
||||
border: none;
|
||||
background: transparent;
|
||||
font-size: 24px;
|
||||
color: {inactive_color_name};
|
||||
}}
|
||||
QPushButton:hover {{
|
||||
color: {active_color_name};
|
||||
}}
|
||||
"""
|
||||
)
|
||||
|
||||
def rating(self) -> int:
|
||||
"""Get the current rating."""
|
||||
return self._rating
|
||||
|
||||
def set_rating(self, rating: int):
|
||||
"""Set the rating programmatically."""
|
||||
if 0 <= rating <= 5:
|
||||
self._set_rating(rating)
|
||||
|
||||
|
||||
class FeedbackDialog(QDialog):
|
||||
"""
|
||||
A feedback dialog widget containing a comment field, star rating, and optional email field.
|
||||
|
||||
Signals:
|
||||
feedbackSubmitted: Emitted when feedback is submitted (rating: int, comment: str, email: str)
|
||||
"""
|
||||
|
||||
feedback_submitted = Signal(int, str, str)
|
||||
ICON_NAME = "feedback"
|
||||
PLUGIN = True
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.setWindowTitle("Feedback")
|
||||
self.setModal(True)
|
||||
self.setMinimumWidth(400)
|
||||
self.setMinimumHeight(300)
|
||||
|
||||
self._setup_ui()
|
||||
|
||||
def _setup_ui(self):
|
||||
"""Set up the user interface."""
|
||||
layout = QVBoxLayout()
|
||||
layout.setSpacing(15)
|
||||
|
||||
# Title
|
||||
title_label = QLabel("We'd love to hear your feedback!")
|
||||
title_font = QFont()
|
||||
title_font.setPointSize(12)
|
||||
title_font.setBold(True)
|
||||
title_label.setFont(title_font)
|
||||
layout.addWidget(title_label)
|
||||
|
||||
# Star rating section
|
||||
rating_layout = QVBoxLayout()
|
||||
rating_label = QLabel("Rating:")
|
||||
rating_layout.addWidget(rating_label)
|
||||
|
||||
self._star_rating = StarRating()
|
||||
rating_layout.addWidget(self._star_rating)
|
||||
layout.addLayout(rating_layout)
|
||||
|
||||
# Comment section
|
||||
comment_label = QLabel("Comments:")
|
||||
layout.addWidget(comment_label)
|
||||
|
||||
self._comment_field = QTextEdit()
|
||||
self._comment_field.setPlaceholderText("Please share your thoughts...")
|
||||
self._comment_field.setMaximumHeight(150)
|
||||
layout.addWidget(self._comment_field)
|
||||
|
||||
# Email section (optional)
|
||||
email_label = QLabel("Email (optional, for follow-up):")
|
||||
layout.addWidget(email_label)
|
||||
|
||||
self._email_field = QLineEdit()
|
||||
self._email_field.setPlaceholderText("your.email@example.com")
|
||||
layout.addWidget(self._email_field)
|
||||
|
||||
# Buttons
|
||||
button_layout = QHBoxLayout()
|
||||
button_layout.addStretch()
|
||||
|
||||
self._cancel_button = QPushButton("Cancel")
|
||||
self._cancel_button.clicked.connect(self.reject)
|
||||
button_layout.addWidget(self._cancel_button)
|
||||
|
||||
self._submit_button = QPushButton("Submit")
|
||||
self._submit_button.setDefault(True)
|
||||
self._submit_button.clicked.connect(self._on_submit)
|
||||
button_layout.addWidget(self._submit_button)
|
||||
|
||||
layout.addLayout(button_layout)
|
||||
|
||||
self.setLayout(layout)
|
||||
|
||||
def _on_submit(self):
|
||||
"""Handle submit button click."""
|
||||
rating = self._star_rating.rating()
|
||||
comment = self._comment_field.toPlainText().strip()
|
||||
email = self._email_field.text().strip()
|
||||
|
||||
# Emit the feedback signal
|
||||
self.feedback_submitted.emit(rating, comment, email)
|
||||
|
||||
# Accept the dialog
|
||||
self.accept()
|
||||
|
||||
def get_feedback(self) -> tuple[int, str, str]:
|
||||
"""
|
||||
Get the current feedback values.
|
||||
|
||||
Returns:
|
||||
tuple: (rating, comment, email)
|
||||
"""
|
||||
return (
|
||||
self._star_rating.rating(),
|
||||
self._comment_field.toPlainText().strip(),
|
||||
self._email_field.text().strip(),
|
||||
)
|
||||
|
||||
def set_rating(self, rating: int):
|
||||
"""Set the star rating."""
|
||||
self._star_rating.set_rating(rating)
|
||||
|
||||
def set_comment(self, comment: str):
|
||||
"""Set the comment text."""
|
||||
self._comment_field.setPlainText(comment)
|
||||
|
||||
def set_email(self, email: str):
|
||||
"""Set the email text."""
|
||||
self._email_field.setText(email)
|
||||
|
||||
@staticmethod
|
||||
def show_feedback_dialog(parent=None) -> tuple[int, str, str] | None:
|
||||
"""
|
||||
Show the feedback dialog and return the feedback if submitted.
|
||||
|
||||
Args:
|
||||
parent: Parent widget
|
||||
|
||||
Returns:
|
||||
tuple: (rating, comment, email) if submitted, None if cancelled
|
||||
"""
|
||||
dialog = FeedbackDialog(parent)
|
||||
if dialog.exec() == QDialog.DialogCode.Accepted:
|
||||
return dialog.get_feedback()
|
||||
return None
|
||||
|
||||
|
||||
if __name__ == "__main__": # pragma: no cover
|
||||
import sys
|
||||
|
||||
from bec_widgets.utils.colors import apply_theme
|
||||
|
||||
app = QApplication(sys.argv)
|
||||
apply_theme("dark")
|
||||
dialog = FeedbackDialog()
|
||||
|
||||
def on_feedback(rating, comment, email):
|
||||
print(f"Rating: {rating}")
|
||||
print(f"Comment: {comment}")
|
||||
print(f"Email: {email}")
|
||||
|
||||
dialog.feedback_submitted.connect(on_feedback)
|
||||
dialog.exec()
|
||||
sys.exit(app.exec())
|
||||
262
tests/unit_tests/test_feedback_dialog.py
Normal file
262
tests/unit_tests/test_feedback_dialog.py
Normal file
@@ -0,0 +1,262 @@
|
||||
import pytest
|
||||
from qtpy.QtCore import Qt
|
||||
from qtpy.QtWidgets import QDialog
|
||||
|
||||
from bec_widgets.utils.colors import apply_theme
|
||||
from bec_widgets.widgets.utility.feedback_dialog.feedback_dialog import FeedbackDialog, StarRating
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def star_rating(qtbot):
|
||||
"""Create a StarRating widget for testing."""
|
||||
widget = StarRating()
|
||||
qtbot.addWidget(widget)
|
||||
qtbot.waitExposed(widget)
|
||||
yield widget
|
||||
widget.close()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def feedback_dialog(qtbot):
|
||||
"""Create a FeedbackDialog for testing."""
|
||||
dialog = FeedbackDialog()
|
||||
qtbot.addWidget(dialog)
|
||||
qtbot.waitExposed(dialog)
|
||||
yield dialog
|
||||
dialog.close()
|
||||
|
||||
|
||||
class TestStarRating:
|
||||
"""Tests for the StarRating widget."""
|
||||
|
||||
def test_initial_state(self, star_rating):
|
||||
"""Test that StarRating initializes with rating 0."""
|
||||
assert star_rating.rating() == 0
|
||||
assert star_rating._hovered_star == 0
|
||||
assert len(star_rating._star_buttons) == 5
|
||||
|
||||
def test_set_rating_via_method(self, star_rating):
|
||||
"""Test setting rating programmatically."""
|
||||
star_rating.set_rating(3)
|
||||
assert star_rating.rating() == 3
|
||||
|
||||
star_rating.set_rating(5)
|
||||
assert star_rating.rating() == 5
|
||||
|
||||
def test_set_rating_bounds(self, star_rating):
|
||||
"""Test that rating is bounded between 0 and 5."""
|
||||
star_rating.set_rating(0)
|
||||
assert star_rating.rating() == 0
|
||||
|
||||
star_rating.set_rating(5)
|
||||
assert star_rating.rating() == 5
|
||||
|
||||
# Out of bounds should not change rating
|
||||
initial_rating = star_rating.rating()
|
||||
star_rating.set_rating(6)
|
||||
assert star_rating.rating() == initial_rating
|
||||
|
||||
star_rating.set_rating(-1)
|
||||
assert star_rating.rating() == initial_rating
|
||||
|
||||
def test_rating_signal_emission(self, star_rating, qtbot):
|
||||
"""Test that rating_changed signal is emitted when rating changes."""
|
||||
with qtbot.waitSignal(star_rating.rating_changed, timeout=1000) as blocker:
|
||||
star_rating.set_rating(4)
|
||||
|
||||
assert blocker.args == [4]
|
||||
|
||||
def test_rating_signal_not_emitted_on_same_value(self, star_rating, qtbot):
|
||||
"""Test that signal is not emitted when setting the same rating."""
|
||||
star_rating.set_rating(3)
|
||||
|
||||
# Should not emit signal when setting same value
|
||||
with qtbot.assertNotEmitted(star_rating.rating_changed, wait=100):
|
||||
star_rating.set_rating(3)
|
||||
|
||||
def test_click_star_button(self, star_rating, qtbot):
|
||||
"""Test clicking on star buttons."""
|
||||
# Click the third star (index 2)
|
||||
with qtbot.waitSignal(star_rating.rating_changed, timeout=1000):
|
||||
qtbot.mouseClick(star_rating._star_buttons[2], Qt.LeftButton)
|
||||
|
||||
assert star_rating.rating() == 3
|
||||
|
||||
# Click the first star
|
||||
with qtbot.waitSignal(star_rating.rating_changed, timeout=1000):
|
||||
qtbot.mouseClick(star_rating._star_buttons[0], Qt.LeftButton)
|
||||
|
||||
assert star_rating.rating() == 1
|
||||
|
||||
def test_mouse_hover(self, star_rating, qtbot):
|
||||
"""Test mouse hover behavior."""
|
||||
# Set initial rating
|
||||
star_rating.set_rating(2)
|
||||
assert star_rating._hovered_star == 0
|
||||
|
||||
# Simulate mouse move over the fourth button
|
||||
btn = star_rating._star_buttons[3]
|
||||
btn_center = btn.geometry().center()
|
||||
event = qtbot.mouseMove(star_rating, pos=btn_center)
|
||||
|
||||
# Note: _hovered_star should be updated by mouseMoveEvent
|
||||
# This is a bit tricky to test directly, so we verify the method exists
|
||||
assert hasattr(star_rating, "mouseMoveEvent")
|
||||
assert hasattr(star_rating, "leaveEvent")
|
||||
|
||||
def test_leave_event(self, star_rating, qtbot):
|
||||
"""Test that leaving the widget clears hover state."""
|
||||
star_rating.set_rating(2)
|
||||
star_rating._hovered_star = 4 # Simulate hover
|
||||
|
||||
# Trigger leave event
|
||||
star_rating.leaveEvent(None)
|
||||
|
||||
assert star_rating._hovered_star == 0
|
||||
assert star_rating.rating() == 2 # Rating should remain unchanged
|
||||
|
||||
def test_update_theme_colors(self, star_rating):
|
||||
"""Test that theme colors are applied correctly."""
|
||||
assert hasattr(star_rating, "_inactive_color")
|
||||
assert hasattr(star_rating, "_active_color")
|
||||
|
||||
# Colors should be initialized
|
||||
assert star_rating._inactive_color is not None
|
||||
assert star_rating._active_color is not None
|
||||
|
||||
def test_display_update(self, star_rating):
|
||||
"""Test that display updates when rating changes."""
|
||||
star_rating.set_rating(3)
|
||||
# If this doesn't raise an exception, the display was updated successfully
|
||||
star_rating._update_display()
|
||||
|
||||
|
||||
class TestFeedbackDialog:
|
||||
"""Tests for the FeedbackDialog widget."""
|
||||
|
||||
def test_initial_state(self, feedback_dialog):
|
||||
"""Test that FeedbackDialog initializes correctly."""
|
||||
assert feedback_dialog.windowTitle() == "Feedback"
|
||||
assert feedback_dialog.isModal() is True
|
||||
assert feedback_dialog._star_rating is not None
|
||||
assert feedback_dialog._comment_field is not None
|
||||
assert feedback_dialog._email_field is not None
|
||||
assert feedback_dialog._submit_button is not None
|
||||
assert feedback_dialog._cancel_button is not None
|
||||
|
||||
def test_get_feedback_initial(self, feedback_dialog):
|
||||
"""Test getting feedback from unmodified dialog."""
|
||||
rating, comment, email = feedback_dialog.get_feedback()
|
||||
assert rating == 0
|
||||
assert comment == ""
|
||||
assert email == ""
|
||||
|
||||
def test_set_and_get_rating(self, feedback_dialog):
|
||||
"""Test setting and getting rating."""
|
||||
feedback_dialog.set_rating(4)
|
||||
rating, _, _ = feedback_dialog.get_feedback()
|
||||
assert rating == 4
|
||||
|
||||
def test_set_and_get_comment(self, feedback_dialog):
|
||||
"""Test setting and getting comment."""
|
||||
test_comment = "This is a test comment"
|
||||
feedback_dialog.set_comment(test_comment)
|
||||
_, comment, _ = feedback_dialog.get_feedback()
|
||||
assert comment == test_comment
|
||||
|
||||
def test_set_and_get_email(self, feedback_dialog):
|
||||
"""Test setting and getting email."""
|
||||
test_email = "test@example.com"
|
||||
feedback_dialog.set_email(test_email)
|
||||
_, _, email = feedback_dialog.get_feedback()
|
||||
assert email == test_email
|
||||
|
||||
def test_set_all_feedback(self, feedback_dialog):
|
||||
"""Test setting all feedback fields."""
|
||||
feedback_dialog.set_rating(5)
|
||||
feedback_dialog.set_comment("Great widget!")
|
||||
feedback_dialog.set_email("user@example.com")
|
||||
|
||||
rating, comment, email = feedback_dialog.get_feedback()
|
||||
assert rating == 5
|
||||
assert comment == "Great widget!"
|
||||
assert email == "user@example.com"
|
||||
|
||||
def test_submit_button_emits_signal(self, feedback_dialog, qtbot):
|
||||
"""Test that clicking submit emits feedback_submitted signal."""
|
||||
feedback_dialog.set_rating(3)
|
||||
feedback_dialog.set_comment("Test feedback")
|
||||
feedback_dialog.set_email("test@test.com")
|
||||
|
||||
with qtbot.waitSignal(feedback_dialog.feedback_submitted, timeout=1000) as blocker:
|
||||
qtbot.mouseClick(feedback_dialog._submit_button, Qt.LeftButton)
|
||||
|
||||
assert blocker.args == [3, "Test feedback", "test@test.com"]
|
||||
|
||||
def test_submit_button_accepts_dialog(self, feedback_dialog, qtbot):
|
||||
"""Test that clicking submit accepts the dialog."""
|
||||
feedback_dialog.set_rating(4)
|
||||
|
||||
qtbot.mouseClick(feedback_dialog._submit_button, Qt.LeftButton)
|
||||
qtbot.wait(100)
|
||||
|
||||
# Dialog should be accepted
|
||||
assert feedback_dialog.result() == QDialog.DialogCode.Accepted
|
||||
|
||||
def test_cancel_button_rejects_dialog(self, feedback_dialog, qtbot):
|
||||
"""Test that clicking cancel rejects the dialog."""
|
||||
qtbot.mouseClick(feedback_dialog._cancel_button, Qt.LeftButton)
|
||||
qtbot.wait(100)
|
||||
|
||||
# Dialog should be rejected
|
||||
assert feedback_dialog.result() == QDialog.DialogCode.Rejected
|
||||
|
||||
def test_submit_with_empty_fields(self, feedback_dialog, qtbot):
|
||||
"""Test submitting with empty fields."""
|
||||
# Don't set any values
|
||||
with qtbot.waitSignal(feedback_dialog.feedback_submitted, timeout=1000) as blocker:
|
||||
qtbot.mouseClick(feedback_dialog._submit_button, Qt.LeftButton)
|
||||
|
||||
# Should emit with empty values
|
||||
assert blocker.args == [0, "", ""]
|
||||
|
||||
def test_submit_strips_whitespace(self, feedback_dialog, qtbot):
|
||||
"""Test that whitespace is stripped from comment and email."""
|
||||
feedback_dialog.set_comment(" Test comment ")
|
||||
feedback_dialog.set_email(" test@example.com ")
|
||||
|
||||
with qtbot.waitSignal(feedback_dialog.feedback_submitted, timeout=1000) as blocker:
|
||||
qtbot.mouseClick(feedback_dialog._submit_button, Qt.LeftButton)
|
||||
|
||||
rating, comment, email = blocker.args
|
||||
assert comment == "Test comment"
|
||||
assert email == "test@example.com"
|
||||
|
||||
def test_dialog_has_correct_properties(self, feedback_dialog):
|
||||
"""Test that dialog has correct class properties."""
|
||||
assert hasattr(FeedbackDialog, "ICON_NAME")
|
||||
assert FeedbackDialog.ICON_NAME == "feedback"
|
||||
assert hasattr(FeedbackDialog, "PLUGIN")
|
||||
assert FeedbackDialog.PLUGIN is True
|
||||
|
||||
def test_comment_field_placeholder(self, feedback_dialog):
|
||||
"""Test that comment field has placeholder text."""
|
||||
assert feedback_dialog._comment_field.placeholderText() != ""
|
||||
|
||||
def test_email_field_placeholder(self, feedback_dialog):
|
||||
"""Test that email field has placeholder text."""
|
||||
assert feedback_dialog._email_field.placeholderText() != ""
|
||||
|
||||
def test_submit_button_is_default(self, feedback_dialog):
|
||||
"""Test that submit button is set as default."""
|
||||
assert feedback_dialog._submit_button.isDefault() is True
|
||||
|
||||
def test_star_rating_embedded_correctly(self, feedback_dialog, qtbot):
|
||||
"""Test that StarRating widget is properly embedded."""
|
||||
# Verify we can interact with the embedded star rating
|
||||
feedback_dialog._star_rating.set_rating(5)
|
||||
assert feedback_dialog._star_rating.rating() == 5
|
||||
|
||||
# Verify rating is reflected in feedback
|
||||
rating, _, _ = feedback_dialog.get_feedback()
|
||||
assert rating == 5
|
||||
Reference in New Issue
Block a user