""" 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 . """ 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 from novelwriter.constants import nwUnicode from novelwriter.dialogs.preferences import GuiPreferences from novelwriter.dialogs.quotes import GuiQuoteSelect 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.guiTheme.itemData(i) for i in range(prefs.guiTheme.count())] assert len(themes) >= 5 assert DEF_GUI in themes # Check GUI Syntax syntax = [prefs.guiSyntax.itemData(i) for i in range(prefs.guiSyntax.count())] assert len(syntax) >= 10 assert "default_dark" in syntax assert "default_light" in syntax # 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 signal.args == [False, False, False, False] # 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 prefs = GuiPreferences(nwGUI) with qtbot.waitExposed(prefs): prefs.show() # Appearance prefs.guiLocale.setCurrentIndex(prefs.guiLocale.findData("en_US")) prefs.guiTheme.setCurrentIndex(prefs.guiTheme.findData("default_dark")) 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.guiTheme != "default_dark" assert CONFIG.guiFont.family() != "" assert CONFIG.hideVScroll is False assert CONFIG.hideHScroll is False assert CONFIG.useCharCount is False # Document Style prefs.guiSyntax.setCurrentIndex(prefs.guiSyntax.findData("default_dark")) 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.guiSyntax != "default_dark" 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 == "theme" 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.showTabsNSpaces.setChecked(True) prefs.showLineEndings.setChecked(True) assert CONFIG.spellLanguage != "de" assert CONFIG.autoSelect is True assert CONFIG.cursorWidth == 1 assert CONFIG.showTabsNSpaces is False assert CONFIG.showLineEndings is False # Editor Scrolling prefs.scrollPastEnd.setChecked(False) prefs.autoScroll.setChecked(True) prefs.autoScrollPos.stepUp() assert CONFIG.scrollPastEnd is True assert CONFIG.autoScroll is False 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(False) 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 True # 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.guiTheme == "default_dark" assert CONFIG.guiFont == QFont() assert CONFIG.hideVScroll is True assert CONFIG.hideHScroll is True assert CONFIG.useCharCount is True # Document Style assert CONFIG.guiSyntax == "default_dark" 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.showTabsNSpaces is True assert CONFIG.showLineEndings is True # Editor Scrolling assert CONFIG.scrollPastEnd is False assert CONFIG.autoScroll is True 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 False # 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()