"""UX tests for Jira Project Knowledge help tooltips and validation. Verifies that the JiraConnectDialog shows contextual help icons for Project ID and Jira Key, renders correct help text, supports keyboard accessibility, and validates common user mistakes (e.g. entering ABC-123 instead of ABC). """ from __future__ import annotations import pytest class _Config: """Minimal config stub for JiraConnectDialog.""" def __init__(self): self.data = { "jira": {"base_url": "", "email": "", "api_token": ""}, "jira_knowledge": {"enabled": False, "projects": {}}, } def save(self): pass class _Ctx: def __init__(self): self.config = _Config() def save(self): self.config.save() @pytest.fixture def dialog(qapp): from cowork_local.ui.connectors_panel import JiraConnectDialog ctx = _Ctx() dlg = JiraConnectDialog(ctx) yield dlg dlg.deleteLater() # ---- Help icon presence --------------------------------------------------- def test_help_icon_exists(dialog): """The mapping label row must contain a help button (?).""" from PySide6.QtWidgets import QToolButton dlg = dialog help_icons = dlg.findChildren(QToolButton) assert len(help_icons) >= 1, "Help button (?) not found in JiraConnectDialog" def test_help_icon_has_tooltip(dialog): """Help icon must have a non-empty tooltip with both Project ID and Jira Key explanations.""" from PySide6.QtWidgets import QToolButton dlg = dialog help_icons = dlg.findChildren(QToolButton) assert help_icons, "No help button found" tooltip = help_icons[0].toolTip() assert tooltip, "Help button has no tooltip" assert "Project ID" in tooltip, "Tooltip missing Project ID explanation" assert "Jira Key" in tooltip, "Tooltip missing Jira Key explanation" # ---- Help text content ---------------------------------------------------- def test_project_id_help_content(dialog): """Project ID help must explain what it is, where to find it, example, and common mistake.""" from cowork_local.i18n import tr text = tr("connectors.jira_kb_project_id_help") assert "cowork-local" in text.lower() or "Cowork" in text, "Missing example" # Must warn against entering Jira keys lower = text.lower() assert "jira" in lower and ("key" in lower or "issue" in lower), \ "Missing common mistake warning about Jira keys" def test_jira_key_help_content(dialog): """Jira Key help must include the ABC-123 → ABC example.""" from cowork_local.i18n import tr text = tr("connectors.jira_kb_jira_key_help") assert "ABC-123" in text, "Missing ABC-123 example" assert "ABC" in text, "Missing ABC extraction example" def test_jira_key_help_warns_against_issue_key(dialog): """Jira Key help must explicitly warn not to enter ABC-123.""" from cowork_local.i18n import tr text = tr("connectors.jira_kb_jira_key_help") lower = text.lower() # Should contain a warning like "Do not enter ABC-123" or "Không nhập ABC-123" assert "abc-123" in lower, "Missing warning about entering issue key format" # ---- Validation ----------------------------------------------------------- def test_validation_shows_on_issue_key_pattern(dialog): """Typing 'proj:ABC-123' should show the validation warning.""" dlg = dialog dlg.show() dlg.project_mapping.setText("myproject:ABC-123") assert not dlg.mapping_validation.isHidden(), \ "Validation hint should be shown when Issue Key pattern detected" assert dlg.mapping_validation.text(), "Validation hint should have text" def test_validation_hides_on_correct_input(dialog): """Typing 'proj:ABC' should NOT show the validation warning.""" dlg = dialog dlg.show() dlg.project_mapping.setText("myproject:ABC") assert dlg.mapping_validation.isHidden(), \ "Validation hint should be hidden for correct Jira Key format" def test_validation_hides_on_empty(dialog): """Empty input should not show validation warning.""" dlg = dialog dlg.show() dlg.project_mapping.setText("") assert dlg.mapping_validation.isHidden(), \ "Validation hint should be hidden on empty input" def test_validation_multiple_mappings(dialog): """Validation should detect issue key pattern even in multi-mapping strings.""" dlg = dialog dlg.show() dlg.project_mapping.setText("proj-a:ALPHA, proj-b:DEF-456") assert not dlg.mapping_validation.isHidden(), \ "Validation should trigger when any mapping contains an Issue Key pattern" # ---- Existing behavior unchanged ------------------------------------------ def test_save_still_works(dialog): """Saving with valid mapping still produces correct config structure.""" dlg = dialog dlg.url.setText("https://example.atlassian.net") dlg.email.setText("user@example.com") dlg.token.setText("test-token") dlg.project_mapping.setText("myproject:MYKEY") dlg.kb_enabled.setChecked(True) dlg._save() jira_kb = dlg.ctx.config.data.get("jira_knowledge", {}) assert jira_kb["enabled"] is True assert jira_kb["projects"] == {"myproject": "MYKEY"} def test_sync_button_present(dialog): """Sync Now button must still exist and be functional.""" dlg = dialog assert dlg.sync_btn is not None assert dlg.sync_btn.text(), "Sync button should have text" # ---- Accessibility -------------------------------------------------------- def test_help_icon_cursor(dialog): """Help button should be a QToolButton (clickable by nature).""" from PySide6.QtWidgets import QToolButton dlg = dialog help_icons = dlg.findChildren(QToolButton) assert help_icons, "No help button found" # QToolButton is inherently clickable, no need for cursor check assert help_icons[0].text() == "?", "Help button should display '?' text" # ---- i18n keys exist for all three languages ------------------------------ @pytest.mark.parametrize("lang", ["en", "vi", "ja"]) def test_i18n_keys_exist(lang): """All Jira KB help keys must have translations for en, vi, ja.""" from cowork_local.i18n import STRINGS required_keys = [ "connectors.jira_kb_section", "connectors.jira_kb_enable", "connectors.jira_kb_mapping_label", "connectors.jira_kb_project_id_title", "connectors.jira_kb_jira_key_title", "connectors.jira_kb_project_id_help", "connectors.jira_kb_jira_key_help", "connectors.jira_kb_validation_issue_key", "connectors.jira_kb_sync_now", "connectors.jira_kb_not_configured", "connectors.jira_kb_disabled", "connectors.jira_kb_syncing", ] for key in required_keys: assert key in STRINGS, f"Missing i18n key: {key}" entry = STRINGS[key] assert lang in entry, f"Missing '{lang}' translation for key: {key}" assert entry[lang], f"Empty '{lang}' translation for key: {key}"