Files
novelWriter/tests/test_dialogs/test_dlg_preferences.py
T
Veronica Berglyd Olsen 5bdbd7c195 Update linting for tests
2025-08-27 21:04:32 +02:00

435 lines
15 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
novelWriter Preferences Dialog Class Tester
=============================================
This file is a part of novelWriter
Copyright (C) 2020 Veronica Berglyd Olsen and novelWriter contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
""" # noqa
from __future__ import annotations
import pytest
from PyQt6.QtCore import QEvent, Qt
from PyQt6.QtGui import QAction, QFont, QFontDatabase, QKeyEvent
from PyQt6.QtWidgets import QFileDialog, QFontDialog
from novelwriter import CONFIG, SHARED
from novelwriter.config import DEF_GUI_DARK, DEF_GUI_LIGHT, DEF_TREECOL
from novelwriter.constants import nwUnicode
from novelwriter.dialogs.preferences import GuiPreferences
from novelwriter.dialogs.quotes import GuiQuoteSelect
from novelwriter.gui.theme import ThemeEntry
from novelwriter.types import QtDialogCancel, QtDialogSave, QtModNone
KEY_DELAY = 1
@pytest.mark.gui
def testDlgPreferences_Main(qtbot, monkeypatch, nwGUI, tstPaths):
"""Test the preferences dialog loading."""
monkeypatch.setattr(SHARED._spelling, "listDictionaries", lambda: [("en", "English [en]")])
monkeypatch.setattr(GuiPreferences, "exec", lambda *a: None)
# Load GUI with standard values
nwGUI.mainMenu.aPreferences.activate(QAction.ActionEvent.Trigger)
qtbot.waitUntil(lambda: SHARED.findTopLevelWidget(GuiPreferences) is not None, timeout=1000)
prefs = SHARED.findTopLevelWidget(GuiPreferences)
assert isinstance(prefs, GuiPreferences)
prefs.show()
# Check Languages
languages = [prefs.guiLocale.itemData(i) for i in range(prefs.guiLocale.count())]
assert len(languages) > 0
assert "en_GB" in languages
# Check GUI Themes
themes = [prefs.lightTheme.itemData(i) for i in range(prefs.lightTheme.count())]
assert DEF_GUI_LIGHT in themes
themes = [prefs.darkTheme.itemData(i) for i in range(prefs.darkTheme.count())]
assert DEF_GUI_DARK in themes
# Check Spell Checking
spelling = [prefs.spellLanguage.itemData(i) for i in range(prefs.spellLanguage.count())]
assert len(spelling) == 1
assert spelling == ["en"]
prefs.close()
# Check Fallback Values
CONFIG.hasEnchant = False
prefs = GuiPreferences(nwGUI)
prefs.show()
# Check Spell Checking
spelling = [prefs.spellLanguage.itemData(i) for i in range(prefs.spellLanguage.count())]
assert len(spelling) == 1
assert spelling == [""]
prefs.close()
# qtbot.stop()
@pytest.mark.gui
def testDlgPreferences_Actions(qtbot, monkeypatch, nwGUI):
"""Test the preferences dialog actions."""
monkeypatch.setattr(SHARED._spelling, "listDictionaries", lambda: [("en", "English [en]")])
prefs = GuiPreferences(nwGUI)
with qtbot.waitExposed(prefs):
prefs.show()
# Check Navigation
vBar = prefs.mainForm.verticalScrollBar()
assert vBar is not None
old = -1
with qtbot.waitSignal(vBar.valueChanged) as value:
prefs.sidebar.button(1).click()
assert value.args[0] > old
old = value.args[0]
with qtbot.waitSignal(vBar.valueChanged) as value:
prefs.sidebar.button(2).click()
assert value.args[0] > old
old = value.args[0]
with qtbot.waitSignal(vBar.valueChanged) as value:
prefs.sidebar.button(3).click()
assert value.args[0] > old
old = value.args[0]
# Check Search
prefs.searchText.setText("Display language")
with qtbot.waitSignal(vBar.valueChanged) as value:
prefs._gotoSearch()
assert value.args[0] < old
# Check Save Button
prefs.show()
with qtbot.waitSignal(prefs.newPreferencesReady) as signal:
button = prefs.buttonBox.button(QtDialogSave)
assert button is not None
button.click()
assert len(signal.args) == 4
# Check Close Button
prefs.show()
button = prefs.buttonBox.button(QtDialogCancel)
assert button is not None
button.click()
assert prefs.isHidden() is True
# Close Using Escape Key
prefs.show()
event = QKeyEvent(QEvent.Type.KeyPress, Qt.Key.Key_Escape, QtModNone)
prefs.keyPressEvent(event)
assert prefs.isHidden() is True
# qtbot.stop()
@pytest.mark.gui
def testDlgPreferences_Settings(qtbot, monkeypatch, nwGUI, fncPath, tstPaths):
"""Test the preferences dialog settings."""
spelling = [("en", "English [en]"), ("de", "Deutch [de]")]
monkeypatch.setattr(SHARED._spelling, "listDictionaries", lambda: spelling)
(fncPath / "nw_en_US.qm").touch()
(fncPath / "project_en_US.json").touch()
CONFIG._nwLangPath = fncPath
SHARED.theme._allThemes = {
"theme1": ThemeEntry(name="Theme 1", dark=False, path=fncPath),
"theme2": ThemeEntry(name="Theme 2", dark=False, path=fncPath),
"theme3": ThemeEntry(name="Theme 3", dark=True, path=fncPath),
"theme4": ThemeEntry(name="Theme 4", dark=True, path=fncPath),
}
prefs = GuiPreferences(nwGUI)
with qtbot.waitExposed(prefs):
prefs.show()
# Appearance
prefs.guiLocale.setCurrentIndex(prefs.guiLocale.findData("en_US"))
prefs.lightTheme.setCurrentIndex(prefs.lightTheme.findData("theme1"))
prefs.darkTheme.setCurrentIndex(prefs.darkTheme.findData("theme3"))
with monkeypatch.context() as mp:
mp.setattr(QFontDialog, "getFont", lambda *a, **k: (QFont(), True))
prefs.nativeFont.setChecked(True) # Use OS font dialog
prefs.guiFontButton.click()
prefs.hideVScroll.setChecked(True)
prefs.hideHScroll.setChecked(True)
prefs.useCharCount.setChecked(True)
assert CONFIG.guiLocale != "en_US"
assert CONFIG.lightTheme == "default_light"
assert CONFIG.darkTheme == "default_dark"
assert CONFIG.guiFont.family() != ""
assert CONFIG.hideVScroll is False
assert CONFIG.hideHScroll is False
assert CONFIG.useCharCount is False
# Document Style
with monkeypatch.context() as mp:
mp.setattr(QFontDialog, "getFont", lambda *a, **k: (QFont(), True))
prefs.nativeFont.setChecked(False) # Use Qt font dialog
prefs.textFontButton.click()
prefs.showFullPath.setChecked(False)
prefs.incNotesWCount.setChecked(False)
assert CONFIG.textFont.family() != ""
assert CONFIG.showFullPath is True
assert CONFIG.incNotesWCount is True
# Project View
prefs.iconColTree.setCurrentData("faded", "default")
prefs.iconColDocs.setChecked(True)
prefs.emphLabels.setChecked(False)
assert CONFIG.iconColTree == DEF_TREECOL
assert CONFIG.iconColDocs is False
assert CONFIG.emphLabels is True
# Behaviour
prefs.autoSaveDoc.stepUp()
prefs.autoSaveProj.stepUp()
prefs.askBeforeExit.setChecked(False)
assert CONFIG.autoSaveDoc == 30
assert CONFIG.autoSaveProj == 60
assert CONFIG.askBeforeExit is True
# Project Backup
with monkeypatch.context() as mp:
mp.setattr(QFileDialog, "getExistingDirectory", lambda *a, **k: str(tstPaths.testDir))
prefs.backupGetPath.click()
assert prefs.backupPath == str(tstPaths.testDir)
prefs.backupOnClose.setChecked(True)
assert prefs.askBeforeBackup.isEnabled() is True
prefs.askBeforeBackup.setChecked(False)
assert CONFIG._backupPath != tstPaths.testDir
assert CONFIG.backupOnClose is False
assert CONFIG.askBeforeBackup is True
# Session Timer
prefs.stopWhenIdle.setChecked(False)
prefs.userIdleTime.stepUp()
assert CONFIG.stopWhenIdle is True
assert CONFIG.userIdleTime == 300
# Text Flow
prefs.textWidth.stepDown()
prefs.focusWidth.stepDown()
prefs.hideFocusFooter.setChecked(True)
prefs.doJustify.setChecked(True)
prefs.textMargin.stepUp()
prefs.tabWidth.stepUp()
assert CONFIG.textWidth == 700
assert CONFIG.focusWidth == 800
assert CONFIG.hideFocusFooter is False
assert CONFIG.doJustify is False
assert CONFIG.textMargin == 40
assert CONFIG.tabWidth == 40
# Text Editing
prefs.spellLanguage.setCurrentIndex(prefs.spellLanguage.findData("de"))
prefs.autoSelect.setChecked(False)
prefs.cursorWidth.setValue(5)
prefs.lineHighlight.setChecked(False)
prefs.showTabsNSpaces.setChecked(True)
prefs.showLineEndings.setChecked(True)
assert CONFIG.spellLanguage != "de"
assert CONFIG.autoSelect is True
assert CONFIG.cursorWidth == 1
assert CONFIG.lineHighlight is True
assert CONFIG.showTabsNSpaces is False
assert CONFIG.showLineEndings is False
# Editor Scrolling
prefs.scrollPastEnd.setChecked(False)
prefs.autoScroll.setChecked(False)
prefs.autoScrollPos.stepUp()
assert CONFIG.scrollPastEnd is True
assert CONFIG.autoScroll is True
assert CONFIG.autoScrollPos == 30
# Text Highlighting
prefs.dialogStyle.setCurrentData(3, 0)
prefs.allowOpenDial.setChecked(False)
prefs.dialogLine.setText(nwUnicode.U_EMDASH)
prefs.narratorBreak.setCurrentData(nwUnicode.U_EMDASH, "")
prefs.narratorDialog.setCurrentData(nwUnicode.U_EMDASH, "")
prefs.altDialogOpen.setText("%") # Symbol also tests for #2455
prefs.altDialogClose.setText("%") # Symbol also tests for #2455
prefs.highlightEmph.setChecked(False)
prefs.showMultiSpaces.setChecked(True)
prefs._insertDialogLineSymbol(nwUnicode.U_ENDASH)
assert prefs.dialogLine.text() == f"{nwUnicode.U_ENDASH} {nwUnicode.U_EMDASH}"
assert CONFIG.dialogStyle == 2
assert CONFIG.allowOpenDial is True
assert CONFIG.dialogLine == ""
assert CONFIG.narratorBreak == ""
assert CONFIG.narratorDialog == ""
assert CONFIG.altDialogOpen == ""
assert CONFIG.altDialogClose == ""
assert CONFIG.highlightEmph is True
assert CONFIG.showMultiSpaces is False
# Text Automation
prefs.doReplaceSQuote.setChecked(False)
prefs.doReplaceDQuote.setChecked(False)
prefs.doReplaceDash.setChecked(False)
prefs.doReplaceDots.setChecked(False)
prefs.doReplace.setChecked(False)
prefs.fmtPadBefore.setText("!?:")
prefs.fmtPadAfter.setText("¡¿")
prefs.fmtPadThin.setChecked(True)
assert prefs.doReplaceSQuote.isEnabled() is False
assert prefs.doReplaceDQuote.isEnabled() is False
assert prefs.doReplaceDash.isEnabled() is False
assert prefs.doReplaceDots.isEnabled() is False
assert prefs.fmtPadThin.isEnabled() is False
assert CONFIG.doReplace is True
assert CONFIG.doReplaceSQuote is True
assert CONFIG.doReplaceDQuote is True
assert CONFIG.doReplaceDash is True
assert CONFIG.doReplaceDots is True
assert CONFIG.fmtPadBefore == ""
assert CONFIG.fmtPadAfter == ""
assert CONFIG.fmtPadThin is False
# Quotation Style
with monkeypatch.context() as mp:
mp.setattr(GuiQuoteSelect, "getQuote", lambda *a, **k: (nwUnicode.U_LSAQUO, True))
prefs.btnSQuoteOpen.click()
with monkeypatch.context() as mp:
mp.setattr(GuiQuoteSelect, "getQuote", lambda *a, **k: (nwUnicode.U_RSAQUO, True))
prefs.btnSQuoteClose.click()
with monkeypatch.context() as mp:
mp.setattr(GuiQuoteSelect, "getQuote", lambda *a, **k: (nwUnicode.U_LAQUO, True))
prefs.btnDQuoteOpen.click()
with monkeypatch.context() as mp:
mp.setattr(GuiQuoteSelect, "getQuote", lambda *a, **k: (nwUnicode.U_RAQUO, True))
prefs.btnDQuoteClose.click()
assert CONFIG.fmtSQuoteOpen == nwUnicode.U_LSQUO
assert CONFIG.fmtSQuoteClose == nwUnicode.U_RSQUO
assert CONFIG.fmtDQuoteOpen == nwUnicode.U_LDQUO
assert CONFIG.fmtDQuoteClose == nwUnicode.U_RDQUO
# Save Settings
with monkeypatch.context() as mp:
mp.setattr(QFontDatabase, "families", lambda *a: ["TestFont"])
with qtbot.waitSignal(prefs.newPreferencesReady) as signal:
button = prefs.buttonBox.button(QtDialogSave)
assert button is not None
button.click()
assert signal.args == [True, True, True, True]
# Check Settings
# ==============
# Appearance
assert CONFIG.guiLocale == "en_US"
assert CONFIG.lightTheme == "theme1"
assert CONFIG.darkTheme == "theme3"
assert CONFIG.guiFont == QFont()
assert CONFIG.hideVScroll is True
assert CONFIG.hideHScroll is True
assert CONFIG.useCharCount is True
# Document Style
assert CONFIG.textFont == QFont()
assert CONFIG.showFullPath is False
assert CONFIG.incNotesWCount is False
# Project View
assert CONFIG.iconColTree == "faded"
assert CONFIG.iconColDocs is True
assert CONFIG.emphLabels is False
# Behaviour
assert CONFIG.autoSaveDoc == 31
assert CONFIG.autoSaveProj == 61
assert CONFIG.askBeforeExit is False
# Project Backup
assert CONFIG._backupPath == tstPaths.testDir
assert CONFIG.backupOnClose is True
assert CONFIG.askBeforeBackup is False
# Session Timer
assert CONFIG.stopWhenIdle is False
assert CONFIG.userIdleTime == 330
# Text Flow
assert CONFIG.textWidth == 690
assert CONFIG.focusWidth == 790
assert CONFIG.hideFocusFooter is True
assert CONFIG.doJustify is True
assert CONFIG.textMargin == 41
assert CONFIG.tabWidth == 41
# Text Editing
assert CONFIG.spellLanguage == "de"
assert CONFIG.autoSelect is False
assert CONFIG.cursorWidth == 5
assert CONFIG.lineHighlight is False
assert CONFIG.showTabsNSpaces is True
assert CONFIG.showLineEndings is True
# Editor Scrolling
assert CONFIG.scrollPastEnd is False
assert CONFIG.autoScroll is False
assert CONFIG.autoScrollPos == 31
# Text Highlighting
assert CONFIG.dialogStyle == 3
assert CONFIG.allowOpenDial is False
assert CONFIG.dialogLine == f"{nwUnicode.U_ENDASH}{nwUnicode.U_EMDASH}"
assert CONFIG.narratorBreak == nwUnicode.U_EMDASH
assert CONFIG.narratorDialog == nwUnicode.U_EMDASH
assert CONFIG.altDialogOpen == "%"
assert CONFIG.altDialogClose == "%"
assert CONFIG.highlightEmph is False
assert CONFIG.showMultiSpaces is True
# Text Automation
assert CONFIG.doReplace is False
assert CONFIG.doReplaceSQuote is False
assert CONFIG.doReplaceDQuote is False
assert CONFIG.doReplaceDash is False
assert CONFIG.doReplaceDots is False
assert CONFIG.fmtPadBefore == "!:?"
assert CONFIG.fmtPadAfter == "¡¿"
assert CONFIG.fmtPadThin is True
# Quotation Style
assert CONFIG.fmtSQuoteOpen == nwUnicode.U_LSAQUO
assert CONFIG.fmtSQuoteClose == nwUnicode.U_RSAQUO
assert CONFIG.fmtDQuoteOpen == nwUnicode.U_LAQUO
assert CONFIG.fmtDQuoteClose == nwUnicode.U_RAQUO
# qtbot.stop()