diff --git a/tests/files/all_icons.json b/tests/files/all_icons.json new file mode 100644 index 00000000..3d85f818 --- /dev/null +++ b/tests/files/all_icons.json @@ -0,0 +1,114 @@ +[ + "alert_error", + "alert_info", + "alert_question", + "alert_warn", + + "cls_archive", + "cls_character", + "cls_custom", + "cls_entity", + "cls_none", + "cls_novel", + "cls_object", + "cls_plot", + "cls_template", + "cls_timeline", + "cls_trash", + "cls_world", + + "prj_folder", + "prj_document", + "prj_title", + "prj_chapter", + "prj_scene", + "prj_note", + + "fmt_bold", + "fmt_italic", + "fmt_mark", + "fmt_strike", + "fmt_subscript", + "fmt_superscript", + "fmt_underline", + "fmt_toolbar", + + "search", + "search_cancel", + "search_case", + "search_loop", + "search_preserve", + "search_project", + "search_regex", + "search_replace", + "search_word", + + "bullet-off", + "bullet-on", + "unfold-hide", + "unfold-show", + + "sb_build", + "sb_details", + "sb_novel", + "sb_outline", + "sb_project", + "sb_search", + "sb_stats", + + "theme_light", + "theme_dark", + "theme_auto", + + "add", + "bookmarks", + "browse", + "build_settings", + "cancel", + "checked", + "chevron_down", + "chevron_left", + "chevron_right", + "chevron_up", + "close", + "copy", + "document_add", + "document", + "edit", + "exclude", + "export", + "filter", + "fit_height", + "fit_width", + "folder", + "font", + "import", + "language", + "lines", + "list", + "margin_bottom", + "margin_left", + "margin_right", + "margin_top", + "maximise", + "minimise", + "more_arrow", + "more_vertical", + "noncheckable", + "open", + "panel", + "pin", + "project_copy", + "quote", + "refresh", + "remove", + "revert", + "settings", + "star", + "stats", + "text", + "timer_off", + "timer", + "unchecked", + "view" +] diff --git a/tests/test_gui/test_gui_theme.py b/tests/test_gui/test_gui_theme.py index 107fd125..d14304d1 100644 --- a/tests/test_gui/test_gui_theme.py +++ b/tests/test_gui/test_gui_theme.py @@ -20,6 +20,8 @@ along with this program. If not, see . """ from __future__ import annotations +import json + from configparser import ConfigParser from pathlib import Path from unittest.mock import MagicMock, Mock @@ -563,3 +565,114 @@ def testGuiTheme_LoadDecorations(monkeypatch): assert iconCache.getHeaderDecorationNarrow(4) == iconCache._headerDecNarrow[4] assert iconCache.getHeaderDecorationNarrow(5) == iconCache._headerDecNarrow[5] assert iconCache.getHeaderDecorationNarrow(6) == iconCache._headerDecNarrow[5] + + +THEMES = [] +_listContent(THEMES, CONFIG.assetPath("themes"), ".conf") + + +@pytest.mark.gui +@pytest.mark.parametrize("theme", [a.stem for a in THEMES]) +def testGuiTheme_CheckTheme(theme): + """Test loading all themes.""" + themes = GuiTheme() + themes.iconCache = MagicMock() + themes._scanThemes(THEMES) + + assert theme in themes.colourThemes + current = themes.colourThemes[theme] + if current.dark: + CONFIG.darkTheme = theme + CONFIG.themeMode = nwTheme.DARK + else: + CONFIG.lightTheme = theme + CONFIG.themeMode = nwTheme.LIGHT + + # Check loading + themes.loadTheme() + assert themes._meta.name == current.name + assert themes.isDarkTheme == current.dark + + # Check completeness + parser = ConfigParser() + parser.read(current.path, encoding="utf-8") + + sections = ["Main", "Base", "Project", "Palette", "GUI", "Syntax"] + assert sorted(parser.sections()) == sorted(sections) + + structure = { + "Main": [ + "name", "mode", # The rest are not required + ], + "Base": [ + "default", "faded", "red", "orange", "yellow", "green", "cyan", + "blue", "purple", + ], + "Project": [ + "root", "folder", "file", "title", "chapter", "scene", "note", + ], + "Palette": [ + "window", "windowtext", "base", "alternatebase", "text", + "tooltipbase", "tooltiptext", "button", "buttontext", "brighttext", + "highlight", "highlightedtext", "link", "linkvisited", + ], + "GUI": [ + "helptext", "fadedtext", "errortext", + ], + "Syntax": [ + "background", "text", "link", "headertext", "headertag", + "emphasis", "dialog", "altdialog", "hidden", "note", "shortcode", + "keyword", "tag", "value", "optional", "spellcheckline", + "errorline", "replacetag", "modifier", "texthighlight", + ], + } + optional = ["description", "author", "credit", "url", "license", "licenseurl"] + missing = [] + for section, options in structure.items(): + missing.extend(opt for opt in options if opt not in parser[section]) + assert missing == [], "Missing options in theme file" + + # Check deprecated + deprecated = [] + for section in sections: + deprecated.extend( + opt for opt in parser[section] + if opt not in structure[section] and opt not in optional + ) + assert deprecated == [], "Deprecated options in theme file" + + +ICONS = [] +_listContent(ICONS, CONFIG.assetPath("icons"), ".icons") + + +@pytest.mark.gui +@pytest.mark.parametrize("icons", [a.stem for a in ICONS]) +def testGuiTheme_CheckIcons(icons, tstPaths): + """Test loading all icons.""" + keysFile: Path = tstPaths.filesDir / "all_icons.json" + iconKeys = json.loads(keysFile.read_text(encoding="utf-8")) + assert isinstance(iconKeys, list) + + CONFIG.lightTheme = DEF_GUI_LIGHT + CONFIG.themeMode = nwTheme.LIGHT + + themes = GuiTheme() + themes.initThemes() + iconCache = themes.iconCache + + assert icons in iconCache.iconThemes + current = iconCache.iconThemes[icons] + CONFIG.iconTheme = icons + + # Check loading + iconCache.loadTheme(icons) + assert iconCache._meta.name == current.name + + # Check completeness + missing = [key for key in iconKeys if key not in iconCache._svgData] + assert missing == [], "Missing keys in icons file" + + # Check deprecated + deprecated = [key for key in iconCache._svgData if key not in iconKeys] + assert deprecated == [], "Deprecated keys in icons file" diff --git a/utils/icon_themes.py b/utils/icon_themes.py index 752b29e5..acb2e230 100644 --- a/utils/icon_themes.py +++ b/utils/icon_themes.py @@ -33,6 +33,7 @@ from utils.common import ROOT_DIR UTILS = Path(__file__).parent ET.register_namespace("", "http://www.w3.org/2000/svg") ICONS = [ + # Remember to also update tests/files/all_icons.json for test coverage "alert_error", "alert_info", "alert_question",