Add test coverage of GuiBuildSettings tool
This commit is contained in:
@@ -59,8 +59,7 @@ class NPagedSideBar(QToolBar):
|
||||
return
|
||||
|
||||
def setLabelColor(self, color):
|
||||
"""Set the text color for the labels.
|
||||
"""
|
||||
"""Set the text color for the labels."""
|
||||
if isinstance(color, list):
|
||||
self._labelCol = QColor(*color)
|
||||
elif isinstance(color, QColor):
|
||||
@@ -68,8 +67,7 @@ class NPagedSideBar(QToolBar):
|
||||
return
|
||||
|
||||
def addSeparator(self):
|
||||
"""Add a spacer widget.
|
||||
"""
|
||||
"""Add a spacer widget."""
|
||||
spacer = QWidget(self)
|
||||
spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
|
||||
spacer.setFixedHeight(self._spacerHeight)
|
||||
@@ -77,16 +75,14 @@ class NPagedSideBar(QToolBar):
|
||||
return
|
||||
|
||||
def addLabel(self, text):
|
||||
"""Add a new label to the toolbar.
|
||||
"""
|
||||
"""Add a new label to the toolbar."""
|
||||
label = NPagedToolLabel(self, self._labelCol)
|
||||
label.setText(text)
|
||||
self.insertWidget(self._stretchAction, label)
|
||||
return
|
||||
|
||||
def addButton(self, text, buttonId=-1):
|
||||
"""Add a new button to the toolbar.
|
||||
"""
|
||||
"""Add a new button to the toolbar."""
|
||||
button = NPagedToolButton(self)
|
||||
button.setText(text)
|
||||
|
||||
@@ -99,8 +95,7 @@ class NPagedSideBar(QToolBar):
|
||||
return action
|
||||
|
||||
def setSelected(self, buttonId):
|
||||
"""Set the selected button.
|
||||
"""
|
||||
"""Set the selected button."""
|
||||
self._group.button(buttonId).setChecked(True)
|
||||
return
|
||||
|
||||
@@ -110,8 +105,7 @@ class NPagedSideBar(QToolBar):
|
||||
|
||||
@pyqtSlot("QAbstractButton*")
|
||||
def _buttonClicked(self, button):
|
||||
"""A button was clicked in the group, emit its id.
|
||||
"""
|
||||
"""A button was clicked in the group, emit its id."""
|
||||
buttonId = self._group.id(button)
|
||||
if buttonId != -1:
|
||||
self.buttonClicked.emit(buttonId)
|
||||
@@ -155,7 +149,7 @@ class NPagedToolButton(QToolButton):
|
||||
height = self.height()
|
||||
palette = self.palette()
|
||||
|
||||
if opt.state & QStyle.State_MouseOver:
|
||||
if opt.state & QStyle.State_MouseOver == QStyle.State_MouseOver:
|
||||
backCol = palette.alternateBase()
|
||||
paint.setBrush(backCol)
|
||||
paint.setOpacity(0.5)
|
||||
|
||||
@@ -45,11 +45,15 @@ class NSwitchBox(QScrollArea):
|
||||
self._hSwitch = baseSize
|
||||
self._wSwitch = 2*self._hSwitch
|
||||
self._sIcon = baseSize
|
||||
self._widgets = []
|
||||
self.clear()
|
||||
return
|
||||
|
||||
def clear(self):
|
||||
"""Rebuild the content of the core widget."""
|
||||
self._index = 0
|
||||
self._widgets = []
|
||||
|
||||
self._content = QGridLayout()
|
||||
self._content.setColumnStretch(1, 1)
|
||||
|
||||
@@ -69,6 +73,7 @@ class NSwitchBox(QScrollArea):
|
||||
font.setBold(True)
|
||||
label.setFont(font)
|
||||
self._content.addWidget(label, self._index, 0, 1, 3, Qt.AlignLeft)
|
||||
self._widgets.append(label)
|
||||
self._bumpIndex()
|
||||
return
|
||||
|
||||
@@ -87,16 +92,17 @@ class NSwitchBox(QScrollArea):
|
||||
switch.toggled.connect(lambda state: self._emitSwitchSignal(identifier, state))
|
||||
self._content.addWidget(switch, self._index, 2, Qt.AlignRight)
|
||||
|
||||
self._widgets.append(switch)
|
||||
self._bumpIndex()
|
||||
|
||||
return
|
||||
|
||||
def addSeparator(self):
|
||||
"""Add a blank entry in the content box.
|
||||
"""
|
||||
"""Add a blank entry in the content box."""
|
||||
spacer = QWidget()
|
||||
spacer.setFixedHeight(int(0.5*self._sIcon))
|
||||
self._content.addWidget(spacer, self._index, 0, 1, 3, Qt.AlignLeft)
|
||||
self._widgets.append(spacer)
|
||||
self._bumpIndex()
|
||||
return
|
||||
|
||||
|
||||
@@ -118,8 +118,8 @@ class GuiBuildSettings(QDialog):
|
||||
# Create Tabs
|
||||
self.optTabSelect = _FilterTab(self, self._build)
|
||||
self.optTabHeadings = _HeadingsTab(self, self._build)
|
||||
self.optTabFormat = _FormatTab(self, self._build)
|
||||
self.optTabContent = _ContentTab(self, self._build)
|
||||
self.optTabFormat = _FormatTab(self, self._build)
|
||||
self.optTabOutput = _OutputTab(self, self._build)
|
||||
|
||||
# Add Tabs
|
||||
@@ -408,9 +408,8 @@ class _FilterTab(QWidget):
|
||||
def mainSplitSizes(self) -> tuple[int, int]:
|
||||
"""Extract the sizes of the main splitter."""
|
||||
sizes = self.mainSplit.sizes()
|
||||
if len(sizes) < 2:
|
||||
return 0, 0
|
||||
return CONFIG.rpxInt(sizes[0]), CONFIG.rpxInt(sizes[1])
|
||||
m, n = (sizes[0], sizes[1]) if len(sizes) >= 2 else (0, 0)
|
||||
return CONFIG.rpxInt(m), CONFIG.rpxInt(n)
|
||||
|
||||
##
|
||||
# Slots
|
||||
@@ -436,7 +435,7 @@ class _FilterTab(QWidget):
|
||||
logger.debug("Building project tree")
|
||||
self._treeMap = {}
|
||||
self.optTree.clear()
|
||||
for nwItem in self.theProject.tree:
|
||||
for nwItem in self.theProject.getProjectItems():
|
||||
|
||||
tHandle = nwItem.itemHandle
|
||||
pHandle = nwItem.itemParent
|
||||
@@ -512,14 +511,14 @@ class _FilterTab(QWidget):
|
||||
|
||||
# Root Classes
|
||||
self.filterOpt.addLabel(self.tr("Root Folders"))
|
||||
for nwItem in self.theProject.getProjectItems():
|
||||
for tHandle, nwItem in self.theProject.tree.iterRoots(None):
|
||||
if not nwItem.isInactiveClass():
|
||||
itemIcon = self.mainTheme.getItemIcon(
|
||||
nwItem.itemType, nwItem.itemClass, nwItem.itemLayout
|
||||
)
|
||||
self.filterOpt.addItem(
|
||||
itemIcon, nwItem.itemName, f"root:{nwItem.itemHandle}",
|
||||
default=self._build.isRootAllowed(nwItem.itemHandle)
|
||||
itemIcon, nwItem.itemName, f"root:{tHandle}",
|
||||
default=self._build.isRootAllowed(tHandle)
|
||||
)
|
||||
|
||||
return
|
||||
@@ -527,18 +526,16 @@ class _FilterTab(QWidget):
|
||||
def _setSelectedMode(self, mode: int):
|
||||
"""Set the mode for the selected items."""
|
||||
for item in self.optTree.selectedItems():
|
||||
if not isinstance(item, QTreeWidgetItem):
|
||||
continue
|
||||
|
||||
tHandle = item.data(self.C_DATA, self.D_HANDLE)
|
||||
isFile = item.data(self.C_DATA, self.D_FILE)
|
||||
if isFile:
|
||||
if mode == self.F_FILTERED:
|
||||
self._build.setFiltered(tHandle)
|
||||
elif mode == self.F_INCLUDED:
|
||||
self._build.setIncluded(tHandle)
|
||||
elif mode == self.F_EXCLUDED:
|
||||
self._build.setExcluded(tHandle)
|
||||
if isinstance(item, QTreeWidgetItem):
|
||||
tHandle = item.data(self.C_DATA, self.D_HANDLE)
|
||||
isFile = item.data(self.C_DATA, self.D_FILE)
|
||||
if isFile:
|
||||
if mode == self.F_FILTERED:
|
||||
self._build.setFiltered(tHandle)
|
||||
elif mode == self.F_INCLUDED:
|
||||
self._build.setIncluded(tHandle)
|
||||
elif mode == self.F_EXCLUDED:
|
||||
self._build.setExcluded(tHandle)
|
||||
|
||||
self._setTreeItemMode()
|
||||
|
||||
@@ -592,7 +589,7 @@ class _HeadingsTab(QWidget):
|
||||
# Title Heading
|
||||
self.lblTitle = QLabel(self._build.getLabel("headings.fmtTitle"))
|
||||
self.fmtTitle = QLineEdit("")
|
||||
self.fmtTitle.setEnabled(False)
|
||||
self.fmtTitle.setReadOnly(True)
|
||||
self.btnTitle = QToolButton()
|
||||
self.btnTitle.setIcon(self.mainTheme.getIcon("edit"))
|
||||
self.btnTitle.clicked.connect(lambda: self._editHeading(self.EDIT_TITLE))
|
||||
@@ -608,7 +605,7 @@ class _HeadingsTab(QWidget):
|
||||
# Chapter Heading
|
||||
self.lblChapter = QLabel(self._build.getLabel("headings.fmtChapter"))
|
||||
self.fmtChapter = QLineEdit("")
|
||||
self.fmtChapter.setEnabled(False)
|
||||
self.fmtChapter.setReadOnly(True)
|
||||
self.btnChapter = QToolButton()
|
||||
self.btnChapter.setIcon(self.mainTheme.getIcon("edit"))
|
||||
self.btnChapter.clicked.connect(lambda: self._editHeading(self.EDIT_CHAPTER))
|
||||
@@ -624,7 +621,7 @@ class _HeadingsTab(QWidget):
|
||||
# Unnumbered Chapter Heading
|
||||
self.lblUnnumbered = QLabel(self._build.getLabel("headings.fmtUnnumbered"))
|
||||
self.fmtUnnumbered = QLineEdit("")
|
||||
self.fmtUnnumbered.setEnabled(False)
|
||||
self.fmtUnnumbered.setReadOnly(True)
|
||||
self.btnUnnumbered = QToolButton()
|
||||
self.btnUnnumbered.setIcon(self.mainTheme.getIcon("edit"))
|
||||
self.btnUnnumbered.clicked.connect(lambda: self._editHeading(self.EDIT_UNNUM))
|
||||
@@ -641,7 +638,7 @@ class _HeadingsTab(QWidget):
|
||||
sceneHideTip = self._build.getLabel("headings.hideScene")
|
||||
self.lblScene = QLabel(self._build.getLabel("headings.fmtScene"))
|
||||
self.fmtScene = QLineEdit("")
|
||||
self.fmtScene.setEnabled(False)
|
||||
self.fmtScene.setReadOnly(True)
|
||||
self.btnScene = QToolButton()
|
||||
self.btnScene.setIcon(self.mainTheme.getIcon("edit"))
|
||||
self.btnScene.clicked.connect(lambda: self._editHeading(self.EDIT_SCENE))
|
||||
@@ -668,7 +665,7 @@ class _HeadingsTab(QWidget):
|
||||
sectionHideTip = self._build.getLabel("headings.hideSection")
|
||||
self.lblSection = QLabel(self._build.getLabel("headings.fmtSection"))
|
||||
self.fmtSection = QLineEdit("")
|
||||
self.fmtSection.setEnabled(False)
|
||||
self.fmtSection.setReadOnly(True)
|
||||
self.btnSection = QToolButton()
|
||||
self.btnSection.setIcon(self.mainTheme.getIcon("edit"))
|
||||
self.btnSection.clicked.connect(lambda: self._editHeading(self.EDIT_SECTION))
|
||||
@@ -814,7 +811,7 @@ class _HeadingsTab(QWidget):
|
||||
def _saveFormat(self):
|
||||
"""Save the format from the edit text box."""
|
||||
heading = self._editing
|
||||
text = self.editTextBox.toPlainText().replace("\n", "//")
|
||||
text = self.editTextBox.toPlainText().strip().replace("\n", "//")
|
||||
if heading == self.EDIT_TITLE:
|
||||
self.fmtTitle.setText(text)
|
||||
self._build.setValue("headings.fmtTitle", text)
|
||||
|
||||
@@ -153,6 +153,7 @@ def nwGUI(qtbot, monkeypatch, functionFixture):
|
||||
nwGUI = main(["--testmode", f"--config={_TMP_CONF}", f"--data={_TMP_CONF}"])
|
||||
qtbot.addWidget(nwGUI)
|
||||
resetConfigVars()
|
||||
nwGUI.docEditor.initEditor()
|
||||
|
||||
nwGUI.show()
|
||||
qtbot.wait(20)
|
||||
|
||||
@@ -0,0 +1,617 @@
|
||||
"""
|
||||
novelWriter – Manuscript Build Settings Dialog Tester
|
||||
=====================================================
|
||||
|
||||
This file is a part of novelWriter
|
||||
Copyright 2018–2023, Veronica Berglyd Olsen
|
||||
|
||||
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/>.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
|
||||
from tools import C, buildTestProject
|
||||
|
||||
from pathlib import Path
|
||||
from pytestqt.qtbot import QtBot
|
||||
|
||||
from PyQt5.QtGui import QFont
|
||||
from PyQt5.QtCore import pyqtSlot
|
||||
from PyQt5.QtWidgets import QDialogButtonBox, QFontDialog
|
||||
|
||||
from novelwriter import CONFIG
|
||||
from novelwriter.guimain import GuiMain
|
||||
from novelwriter.constants import nwHeadFmt
|
||||
from novelwriter.core.buildsettings import BuildSettings, FilterMode
|
||||
from novelwriter.tools.manussettings import (
|
||||
GuiBuildSettings, _OutputTab, _FormatTab, _ContentTab, _HeadingsTab, _FilterTab
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.gui
|
||||
def testBuildSettings_Init(qtbot: QtBot, nwGUI: GuiMain, projPath: Path, mockRnd):
|
||||
"""Test the initialisation of the GuiBuildSettings dialog."""
|
||||
buildTestProject(nwGUI, projPath)
|
||||
nwGUI.openProject(projPath)
|
||||
build = BuildSettings()
|
||||
|
||||
# Create the dialog and populate it
|
||||
bSettings = GuiBuildSettings(nwGUI, nwGUI, build)
|
||||
bSettings.show()
|
||||
bSettings.loadContent()
|
||||
|
||||
# Flip through pages
|
||||
bSettings.optSideBar._group.button(bSettings.OPT_OUTPUT).click()
|
||||
assert isinstance(bSettings.toolStack.currentWidget(), _OutputTab)
|
||||
|
||||
bSettings.optSideBar._group.button(bSettings.OPT_FORMAT).click()
|
||||
assert isinstance(bSettings.toolStack.currentWidget(), _FormatTab)
|
||||
|
||||
bSettings.optSideBar._group.button(bSettings.OPT_CONTENT).click()
|
||||
assert isinstance(bSettings.toolStack.currentWidget(), _ContentTab)
|
||||
|
||||
bSettings.optSideBar._group.button(bSettings.OPT_HEADINGS).click()
|
||||
assert isinstance(bSettings.toolStack.currentWidget(), _HeadingsTab)
|
||||
|
||||
bSettings.optSideBar._group.button(bSettings.OPT_FILTERS).click()
|
||||
assert isinstance(bSettings.toolStack.currentWidget(), _FilterTab)
|
||||
|
||||
# Check dialog buttons
|
||||
triggered = False
|
||||
|
||||
@pyqtSlot(BuildSettings)
|
||||
def _testNewSettingsReady(new: BuildSettings):
|
||||
nonlocal build, triggered
|
||||
assert new is build
|
||||
triggered = True
|
||||
|
||||
# Capture Apply button
|
||||
with qtbot.waitSignal(bSettings.newSettingsReady, timeout=5000):
|
||||
bSettings.newSettingsReady.connect(_testNewSettingsReady)
|
||||
bSettings._dialogButtonClicked(bSettings.dlgButtons.button(QDialogButtonBox.Apply))
|
||||
|
||||
assert triggered
|
||||
|
||||
# Capture Save button
|
||||
triggered = False
|
||||
|
||||
with qtbot.waitSignal(bSettings.newSettingsReady, timeout=5000):
|
||||
bSettings.newSettingsReady.connect(_testNewSettingsReady)
|
||||
bSettings._dialogButtonClicked(bSettings.dlgButtons.button(QDialogButtonBox.Save))
|
||||
|
||||
assert triggered
|
||||
|
||||
# Close manually
|
||||
# This pops the ask to save dialog, which is automatically handled
|
||||
triggered = False
|
||||
build._changed = True
|
||||
bSettings.show()
|
||||
|
||||
with qtbot.waitSignal(bSettings.newSettingsReady, timeout=5000):
|
||||
bSettings.newSettingsReady.connect(_testNewSettingsReady)
|
||||
bSettings.close()
|
||||
|
||||
assert triggered
|
||||
|
||||
# Finish
|
||||
bSettings._dialogButtonClicked(bSettings.dlgButtons.button(QDialogButtonBox.Close))
|
||||
# qtbot.stop()
|
||||
|
||||
# END Test testBuildSettings_Init
|
||||
|
||||
|
||||
@pytest.mark.gui
|
||||
def testBuildSettings_Filter(qtbot: QtBot, nwGUI: GuiMain, projPath: Path, mockRnd):
|
||||
"""Test the Filter Tab of the GuiBuildSettings dialog."""
|
||||
buildTestProject(nwGUI, projPath)
|
||||
nwGUI.openProject(projPath)
|
||||
build = BuildSettings()
|
||||
|
||||
switchMap = {
|
||||
"incNovel": 1,
|
||||
"incNotes": 2,
|
||||
"incInactive": 3,
|
||||
"novelRoot": 6,
|
||||
"plotRoot": 7,
|
||||
"charRoot": 8,
|
||||
"worldRoot": 9,
|
||||
}
|
||||
|
||||
hPlotDoc = nwGUI.theProject.newFile("Main Plot", C.hPlotRoot)
|
||||
hCharDoc = nwGUI.theProject.newFile("Jane Doe", C.hCharRoot)
|
||||
nwGUI.projView.projTree.revealNewTreeItem(hPlotDoc)
|
||||
nwGUI.projView.projTree.revealNewTreeItem(hCharDoc)
|
||||
nwGUI.theProject.tree[hPlotDoc].setActive(False)
|
||||
|
||||
# Create the dialog and populate it
|
||||
bSettings = GuiBuildSettings(nwGUI, nwGUI, build)
|
||||
bSettings.show()
|
||||
bSettings.loadContent()
|
||||
|
||||
filterTab = bSettings.optTabSelect
|
||||
bSettings.optSideBar._group.button(bSettings.OPT_FILTERS).click()
|
||||
assert bSettings.toolStack.currentWidget() is filterTab
|
||||
|
||||
# Check content
|
||||
assert filterTab.optTree.topLevelItemCount() == 4 # The 4 root folders
|
||||
assert filterTab.filterOpt._index == 10 # 2 headers, 1 sep, 3 opt and 4 roots
|
||||
|
||||
# Untoggle note folders
|
||||
filterTab.filterOpt._widgets[switchMap["worldRoot"]].setChecked(False) # World Root
|
||||
assert filterTab.optTree.topLevelItemCount() == 3
|
||||
assert C.hWorldRoot in build._skipRoot
|
||||
|
||||
filterTab.filterOpt._widgets[switchMap["charRoot"]].setChecked(False) # Char Root
|
||||
assert filterTab.optTree.topLevelItemCount() == 2
|
||||
assert C.hCharRoot in build._skipRoot
|
||||
|
||||
filterTab.filterOpt._widgets[switchMap["plotRoot"]].setChecked(False) # Plot Root
|
||||
assert filterTab.optTree.topLevelItemCount() == 1
|
||||
assert C.hPlotRoot in build._skipRoot
|
||||
|
||||
# Reset Plot and Char
|
||||
filterTab.filterOpt._widgets[switchMap["plotRoot"]].setChecked(True)
|
||||
filterTab.filterOpt._widgets[switchMap["charRoot"]].setChecked(True)
|
||||
assert filterTab.optTree.topLevelItemCount() == 3
|
||||
|
||||
# Switch off novel docs
|
||||
filterTab.filterOpt._widgets[switchMap["incNovel"]].setChecked(False)
|
||||
assert build.buildItemFilter(nwGUI.theProject) == {
|
||||
C.hNovelRoot: (False, FilterMode.SKIPPED),
|
||||
C.hTitlePage: (False, FilterMode.FILTERED),
|
||||
C.hChapterDir: (False, FilterMode.SKIPPED),
|
||||
C.hChapterDoc: (False, FilterMode.FILTERED),
|
||||
C.hSceneDoc: (False, FilterMode.FILTERED),
|
||||
C.hPlotRoot: (False, FilterMode.SKIPPED),
|
||||
hPlotDoc: (False, FilterMode.FILTERED),
|
||||
C.hCharRoot: (False, FilterMode.SKIPPED),
|
||||
hCharDoc: (False, FilterMode.FILTERED),
|
||||
C.hWorldRoot: (False, FilterMode.SKIPPED),
|
||||
}
|
||||
|
||||
# Switch on note docs
|
||||
filterTab.filterOpt._widgets[switchMap["incNotes"]].setChecked(True)
|
||||
assert build.buildItemFilter(nwGUI.theProject) == {
|
||||
C.hNovelRoot: (False, FilterMode.SKIPPED),
|
||||
C.hTitlePage: (False, FilterMode.FILTERED),
|
||||
C.hChapterDir: (False, FilterMode.SKIPPED),
|
||||
C.hChapterDoc: (False, FilterMode.FILTERED),
|
||||
C.hSceneDoc: (False, FilterMode.FILTERED),
|
||||
C.hPlotRoot: (False, FilterMode.SKIPPED),
|
||||
hPlotDoc: (False, FilterMode.FILTERED), # Set to inactive
|
||||
C.hCharRoot: (False, FilterMode.SKIPPED),
|
||||
hCharDoc: (True, FilterMode.FILTERED), # Now enabled
|
||||
C.hWorldRoot: (False, FilterMode.SKIPPED),
|
||||
}
|
||||
|
||||
# Switch on inactive docs
|
||||
filterTab.filterOpt._widgets[switchMap["incInactive"]].setChecked(True)
|
||||
assert build.buildItemFilter(nwGUI.theProject) == {
|
||||
C.hNovelRoot: (False, FilterMode.SKIPPED),
|
||||
C.hTitlePage: (False, FilterMode.FILTERED),
|
||||
C.hChapterDir: (False, FilterMode.SKIPPED),
|
||||
C.hChapterDoc: (False, FilterMode.FILTERED),
|
||||
C.hSceneDoc: (False, FilterMode.FILTERED),
|
||||
C.hPlotRoot: (False, FilterMode.SKIPPED),
|
||||
hPlotDoc: (True, FilterMode.FILTERED), # Now enabled
|
||||
C.hCharRoot: (False, FilterMode.SKIPPED),
|
||||
hCharDoc: (True, FilterMode.FILTERED),
|
||||
C.hWorldRoot: (False, FilterMode.SKIPPED),
|
||||
}
|
||||
|
||||
# Set chapter and scene docs to included
|
||||
filterTab._treeMap[C.hChapterDoc].setSelected(True)
|
||||
filterTab._treeMap[C.hSceneDoc].setSelected(True)
|
||||
filterTab.includedButton.click()
|
||||
assert build.buildItemFilter(nwGUI.theProject) == {
|
||||
C.hNovelRoot: (False, FilterMode.SKIPPED),
|
||||
C.hTitlePage: (False, FilterMode.FILTERED),
|
||||
C.hChapterDir: (False, FilterMode.SKIPPED),
|
||||
C.hChapterDoc: (True, FilterMode.INCLUDED), # Now included
|
||||
C.hSceneDoc: (True, FilterMode.INCLUDED), # Now included
|
||||
C.hPlotRoot: (False, FilterMode.SKIPPED),
|
||||
hPlotDoc: (True, FilterMode.FILTERED),
|
||||
C.hCharRoot: (False, FilterMode.SKIPPED),
|
||||
hCharDoc: (True, FilterMode.FILTERED),
|
||||
C.hWorldRoot: (False, FilterMode.SKIPPED),
|
||||
}
|
||||
|
||||
# Set char and plot docs to excluded
|
||||
filterTab.optTree.clearSelection()
|
||||
filterTab._treeMap[hPlotDoc].setSelected(True)
|
||||
filterTab._treeMap[hCharDoc].setSelected(True)
|
||||
filterTab.excludedButton.click()
|
||||
assert build.buildItemFilter(nwGUI.theProject) == {
|
||||
C.hNovelRoot: (False, FilterMode.SKIPPED),
|
||||
C.hTitlePage: (False, FilterMode.FILTERED),
|
||||
C.hChapterDir: (False, FilterMode.SKIPPED),
|
||||
C.hChapterDoc: (True, FilterMode.INCLUDED),
|
||||
C.hSceneDoc: (True, FilterMode.INCLUDED),
|
||||
C.hPlotRoot: (False, FilterMode.SKIPPED),
|
||||
hPlotDoc: (False, FilterMode.EXCLUDED), # Now excluded
|
||||
C.hCharRoot: (False, FilterMode.SKIPPED),
|
||||
hCharDoc: (False, FilterMode.EXCLUDED), # Now excluded
|
||||
C.hWorldRoot: (False, FilterMode.SKIPPED),
|
||||
}
|
||||
|
||||
# Switch on novel docs
|
||||
filterTab.filterOpt._widgets[switchMap["incNovel"]].setChecked(True)
|
||||
assert build.buildItemFilter(nwGUI.theProject) == {
|
||||
C.hNovelRoot: (False, FilterMode.SKIPPED),
|
||||
C.hTitlePage: (True, FilterMode.FILTERED), # Now enabled
|
||||
C.hChapterDir: (False, FilterMode.SKIPPED),
|
||||
C.hChapterDoc: (True, FilterMode.INCLUDED),
|
||||
C.hSceneDoc: (True, FilterMode.INCLUDED),
|
||||
C.hPlotRoot: (False, FilterMode.SKIPPED),
|
||||
hPlotDoc: (False, FilterMode.EXCLUDED),
|
||||
C.hCharRoot: (False, FilterMode.SKIPPED),
|
||||
hCharDoc: (False, FilterMode.EXCLUDED),
|
||||
C.hWorldRoot: (False, FilterMode.SKIPPED),
|
||||
}
|
||||
|
||||
# Set everything back to filtered
|
||||
filterTab.optTree.clearSelection()
|
||||
filterTab._treeMap[C.hChapterDoc].setSelected(True)
|
||||
filterTab._treeMap[C.hSceneDoc].setSelected(True)
|
||||
filterTab._treeMap[hPlotDoc].setSelected(True)
|
||||
filterTab._treeMap[hCharDoc].setSelected(True)
|
||||
filterTab.filteredButton.click()
|
||||
assert build.buildItemFilter(nwGUI.theProject) == {
|
||||
C.hNovelRoot: (False, FilterMode.SKIPPED),
|
||||
C.hTitlePage: (True, FilterMode.FILTERED),
|
||||
C.hChapterDir: (False, FilterMode.SKIPPED),
|
||||
C.hChapterDoc: (True, FilterMode.FILTERED),
|
||||
C.hSceneDoc: (True, FilterMode.FILTERED),
|
||||
C.hPlotRoot: (False, FilterMode.SKIPPED),
|
||||
hPlotDoc: (True, FilterMode.FILTERED),
|
||||
C.hCharRoot: (False, FilterMode.SKIPPED),
|
||||
hCharDoc: (True, FilterMode.FILTERED),
|
||||
C.hWorldRoot: (False, FilterMode.SKIPPED),
|
||||
}
|
||||
|
||||
# Check handling of invalid project items
|
||||
assert list(filterTab._treeMap.keys()) == [
|
||||
C.hNovelRoot, C.hTitlePage, C.hChapterDir, C.hChapterDoc, C.hSceneDoc,
|
||||
C.hPlotRoot, hPlotDoc, C.hCharRoot, hCharDoc,
|
||||
]
|
||||
nwGUI.theProject.tree[hCharDoc].setRoot(None) # Char doc has no root handle
|
||||
nwGUI.theProject.tree[hPlotDoc].setParent(None) # Plot doc has no parent handle
|
||||
filterTab._populateTree()
|
||||
assert list(filterTab._treeMap.keys()) == [
|
||||
C.hNovelRoot, C.hTitlePage, C.hChapterDir, C.hChapterDoc, C.hSceneDoc,
|
||||
C.hPlotRoot, C.hCharRoot
|
||||
]
|
||||
|
||||
# Finish
|
||||
bSettings._dialogButtonClicked(bSettings.dlgButtons.button(QDialogButtonBox.Close))
|
||||
# qtbot.stop()
|
||||
|
||||
# END Test testBuildSettings_Filter
|
||||
|
||||
|
||||
@pytest.mark.gui
|
||||
def testBuildSettings_Headings(qtbot: QtBot, nwGUI: GuiMain):
|
||||
"""Test the Headings Tab of the GuiBuildSettings dialog."""
|
||||
build = BuildSettings()
|
||||
|
||||
ttTitle = f"Title: {nwHeadFmt.TITLE}"
|
||||
chTitle = f"Chapter: {nwHeadFmt.TITLE}"
|
||||
unTitle = f"Interlude: {nwHeadFmt.TITLE}"
|
||||
scTitle = f"Scene: {nwHeadFmt.TITLE}"
|
||||
sxTitle = f"Section: {nwHeadFmt.TITLE}"
|
||||
|
||||
build.setValue("headings.fmtTitle", ttTitle)
|
||||
build.setValue("headings.fmtChapter", chTitle)
|
||||
build.setValue("headings.fmtUnnumbered", unTitle)
|
||||
build.setValue("headings.fmtScene", scTitle)
|
||||
build.setValue("headings.fmtSection", sxTitle)
|
||||
build.setValue("headings.hideScene", False)
|
||||
build.setValue("headings.hideSection", False)
|
||||
|
||||
# Create the dialog and populate it
|
||||
bSettings = GuiBuildSettings(nwGUI, nwGUI, build)
|
||||
bSettings.show()
|
||||
bSettings.loadContent()
|
||||
|
||||
headTab = bSettings.optTabHeadings
|
||||
bSettings.optSideBar._group.button(bSettings.OPT_HEADINGS).click()
|
||||
assert bSettings.toolStack.currentWidget() is headTab
|
||||
|
||||
# Check initial values
|
||||
assert headTab.fmtTitle.text() == ttTitle
|
||||
assert headTab.fmtChapter.text() == chTitle
|
||||
assert headTab.fmtUnnumbered.text() == unTitle
|
||||
assert headTab.fmtScene.text() == scTitle
|
||||
assert headTab.fmtSection.text() == sxTitle
|
||||
assert headTab.swtScene.isChecked() is False
|
||||
assert headTab.swtSection.isChecked() is False
|
||||
|
||||
# Edit in Turn
|
||||
# ============
|
||||
|
||||
# Nothing to apply
|
||||
assert headTab._editing == 0
|
||||
assert headTab.editTextBox.isEnabled() is False
|
||||
headTab.btnApply.click()
|
||||
assert headTab.editTextBox.isEnabled() is False
|
||||
|
||||
# Title
|
||||
headTab.btnTitle.click()
|
||||
assert headTab._editing == headTab.EDIT_TITLE
|
||||
assert headTab.editTextBox.isEnabled() is True
|
||||
assert headTab.editTextBox.toPlainText() == ttTitle
|
||||
headTab.btnApply.click()
|
||||
assert headTab.editTextBox.isEnabled() is False
|
||||
|
||||
# Chapter
|
||||
headTab.btnChapter.click()
|
||||
assert headTab._editing == headTab.EDIT_CHAPTER
|
||||
assert headTab.editTextBox.isEnabled() is True
|
||||
assert headTab.editTextBox.toPlainText() == chTitle
|
||||
headTab.btnApply.click()
|
||||
assert headTab.editTextBox.isEnabled() is False
|
||||
|
||||
# Unnumbered
|
||||
headTab.btnUnnumbered.click()
|
||||
assert headTab._editing == headTab.EDIT_UNNUM
|
||||
assert headTab.editTextBox.isEnabled() is True
|
||||
assert headTab.editTextBox.toPlainText() == unTitle
|
||||
headTab.btnApply.click()
|
||||
assert headTab.editTextBox.isEnabled() is False
|
||||
|
||||
# Scene
|
||||
headTab.btnScene.click()
|
||||
assert headTab._editing == headTab.EDIT_SCENE
|
||||
assert headTab.editTextBox.isEnabled() is True
|
||||
assert headTab.editTextBox.toPlainText() == scTitle
|
||||
headTab.btnApply.click()
|
||||
assert headTab.editTextBox.isEnabled() is False
|
||||
|
||||
# Section
|
||||
headTab.btnSection.click()
|
||||
assert headTab._editing == headTab.EDIT_SECTION
|
||||
assert headTab.editTextBox.isEnabled() is True
|
||||
assert headTab.editTextBox.toPlainText() == sxTitle
|
||||
headTab.btnApply.click()
|
||||
assert headTab.editTextBox.isEnabled() is False
|
||||
|
||||
# Edit a Heading
|
||||
# ==============
|
||||
|
||||
# Create new format of all bits
|
||||
headTab.btnChapter.click()
|
||||
allFmt = (
|
||||
f"{nwHeadFmt.TITLE}{nwHeadFmt.CH_NUM}{nwHeadFmt.CH_WORD}{nwHeadFmt.CH_ROMU}"
|
||||
f"{nwHeadFmt.CH_ROML}{nwHeadFmt.SC_NUM}{nwHeadFmt.SC_ABS}"
|
||||
)
|
||||
headTab.editTextBox.clear()
|
||||
assert headTab.editTextBox.toPlainText() == ""
|
||||
headTab.aInsTitle.trigger()
|
||||
headTab.aInsChNum.trigger()
|
||||
headTab.aInsChWord.trigger()
|
||||
headTab.aInsChRomU.trigger()
|
||||
headTab.aInsChRomL.trigger()
|
||||
headTab.aInsScNum.trigger()
|
||||
headTab.aInsScAbs.trigger()
|
||||
assert headTab.editTextBox.toPlainText() == allFmt
|
||||
headTab.btnApply.click()
|
||||
assert build.getStr("headings.fmtChapter") == allFmt
|
||||
|
||||
# Check complex format
|
||||
headTab.btnChapter.click()
|
||||
headTab.editTextBox.setPlainText(f"Chapter {nwHeadFmt.CH_NUM}\n{nwHeadFmt.TITLE}\n")
|
||||
headTab.btnApply.click()
|
||||
assert build.getStr("headings.fmtChapter") == f"Chapter {nwHeadFmt.CH_NUM}//{nwHeadFmt.TITLE}"
|
||||
|
||||
# Set all to plain title
|
||||
headTab.btnTitle.click()
|
||||
headTab.editTextBox.setPlainText(nwHeadFmt.TITLE)
|
||||
headTab.btnApply.click()
|
||||
assert build.getStr("headings.fmtTitle") == nwHeadFmt.TITLE
|
||||
|
||||
headTab.btnChapter.click()
|
||||
headTab.editTextBox.setPlainText(nwHeadFmt.TITLE)
|
||||
headTab.btnApply.click()
|
||||
assert build.getStr("headings.fmtChapter") == nwHeadFmt.TITLE
|
||||
|
||||
headTab.btnUnnumbered.click()
|
||||
headTab.editTextBox.setPlainText(nwHeadFmt.TITLE)
|
||||
headTab.btnApply.click()
|
||||
assert build.getStr("headings.fmtUnnumbered") == nwHeadFmt.TITLE
|
||||
|
||||
headTab.btnScene.click()
|
||||
headTab.editTextBox.setPlainText(nwHeadFmt.TITLE)
|
||||
headTab.btnApply.click()
|
||||
assert build.getStr("headings.fmtScene") == nwHeadFmt.TITLE
|
||||
|
||||
headTab.btnSection.click()
|
||||
headTab.editTextBox.setPlainText(nwHeadFmt.TITLE)
|
||||
headTab.btnApply.click()
|
||||
assert build.getStr("headings.fmtSection") == nwHeadFmt.TITLE
|
||||
|
||||
# Check hide switches
|
||||
headTab.swtScene.setChecked(True)
|
||||
headTab.swtSection.setChecked(True)
|
||||
headTab.saveContent()
|
||||
assert build.getBool("headings.hideScene") is True
|
||||
assert build.getBool("headings.hideSection") is True
|
||||
|
||||
# Finish
|
||||
bSettings._dialogButtonClicked(bSettings.dlgButtons.button(QDialogButtonBox.Close))
|
||||
# qtbot.stop()
|
||||
|
||||
# END Test testBuildSettings_Headings
|
||||
|
||||
|
||||
@pytest.mark.gui
|
||||
def testBuildSettings_Content(qtbot: QtBot, nwGUI: GuiMain):
|
||||
"""Test the Content Tab of the GuiBuildSettings dialog."""
|
||||
build = BuildSettings()
|
||||
|
||||
build.setValue("text.includeSynopsis", False)
|
||||
build.setValue("text.includeComments", False)
|
||||
build.setValue("text.includeKeywords", False)
|
||||
build.setValue("text.includeBodyText", False)
|
||||
|
||||
build.setValue("text.addNoteHeadings", False)
|
||||
|
||||
# Create the dialog and populate it
|
||||
bSettings = GuiBuildSettings(nwGUI, nwGUI, build)
|
||||
bSettings.show()
|
||||
bSettings.loadContent()
|
||||
|
||||
contTab = bSettings.optTabContent
|
||||
bSettings.optSideBar._group.button(bSettings.OPT_CONTENT).click()
|
||||
assert bSettings.toolStack.currentWidget() is contTab
|
||||
|
||||
# Check initial values
|
||||
assert contTab.incSynopsis.isChecked() is False
|
||||
assert contTab.incComments.isChecked() is False
|
||||
assert contTab.incKeywords.isChecked() is False
|
||||
assert contTab.incBodyText.isChecked() is False
|
||||
|
||||
assert contTab.addNoteHead.isChecked() is False
|
||||
|
||||
# Toggle all
|
||||
contTab.incSynopsis.setChecked(True)
|
||||
contTab.incComments.setChecked(True)
|
||||
contTab.incKeywords.setChecked(True)
|
||||
contTab.incBodyText.setChecked(True)
|
||||
|
||||
contTab.addNoteHead.setChecked(True)
|
||||
|
||||
# Save values
|
||||
contTab.saveContent()
|
||||
|
||||
assert build.getBool("text.includeSynopsis") is True
|
||||
assert build.getBool("text.includeComments") is True
|
||||
assert build.getBool("text.includeKeywords") is True
|
||||
assert build.getBool("text.includeBodyText") is True
|
||||
|
||||
assert build.getBool("text.addNoteHeadings") is True
|
||||
|
||||
# Finish
|
||||
bSettings._dialogButtonClicked(bSettings.dlgButtons.button(QDialogButtonBox.Close))
|
||||
# qtbot.stop()
|
||||
|
||||
# END Test testBuildSettings_Content
|
||||
|
||||
|
||||
@pytest.mark.gui
|
||||
def testBuildSettings_Format(monkeypatch, qtbot: QtBot, nwGUI: GuiMain):
|
||||
"""Test the Format Tab of the GuiBuildSettings dialog."""
|
||||
build = BuildSettings()
|
||||
|
||||
textFont = str(CONFIG.textFont)
|
||||
|
||||
build.setValue("format.buildLang", "en_US")
|
||||
build.setValue("format.textFont", "") # Will fall back to config value
|
||||
build.setValue("format.textSize", 12)
|
||||
build.setValue("format.lineHeight", 1.2)
|
||||
build.setValue("format.justifyText", False)
|
||||
build.setValue("format.stripUnicode", False)
|
||||
build.setValue("format.replaceTabs", False)
|
||||
|
||||
# Create the dialog and populate it
|
||||
bSettings = GuiBuildSettings(nwGUI, nwGUI, build)
|
||||
bSettings.show()
|
||||
bSettings.loadContent()
|
||||
|
||||
fmtTab = bSettings.optTabFormat
|
||||
bSettings.optSideBar._group.button(bSettings.OPT_FORMAT).click()
|
||||
assert bSettings.toolStack.currentWidget() is fmtTab
|
||||
|
||||
# Check initial values
|
||||
assert fmtTab.buildLang.currentData() == "en_US"
|
||||
assert fmtTab.textFont.text() == textFont
|
||||
assert fmtTab.textSize.value() == 12
|
||||
assert fmtTab.lineHeight.value() == 1.2
|
||||
assert fmtTab.justifyText.isChecked() is False
|
||||
assert fmtTab.stripUnicode.isChecked() is False
|
||||
assert fmtTab.replaceTabs.isChecked() is False
|
||||
|
||||
# Change values
|
||||
fmtTab.buildLang.setCurrentIndex(fmtTab.buildLang.findData("en_GB"))
|
||||
fmtTab.textFont.setText("Arial")
|
||||
fmtTab.textSize.setValue(11)
|
||||
fmtTab.lineHeight.setValue(1.15)
|
||||
fmtTab.justifyText.setChecked(True)
|
||||
fmtTab.stripUnicode.setChecked(True)
|
||||
fmtTab.replaceTabs.setChecked(True)
|
||||
|
||||
# Save values
|
||||
fmtTab.saveContent()
|
||||
|
||||
assert build.getStr("format.buildLang") == "en_GB"
|
||||
assert build.getStr("format.textFont") == "Arial"
|
||||
assert build.getInt("format.textSize") == 11
|
||||
assert build.getFloat("format.lineHeight") == 1.15
|
||||
assert build.getBool("format.justifyText") is True
|
||||
assert build.getBool("format.stripUnicode") is True
|
||||
assert build.getBool("format.replaceTabs") is True
|
||||
|
||||
# Check that the font dialog doesn't fail
|
||||
with monkeypatch.context() as mp:
|
||||
font = QFont()
|
||||
font.setFamily("Times")
|
||||
font.setPointSize(10)
|
||||
mp.setattr(QFontDialog, "getFont", lambda *a: (font, True))
|
||||
|
||||
fmtTab.btnTextFont.click()
|
||||
assert fmtTab.textFont.text() == "Times"
|
||||
assert fmtTab.textSize.value() == 10
|
||||
|
||||
# Finish
|
||||
bSettings._dialogButtonClicked(bSettings.dlgButtons.button(QDialogButtonBox.Close))
|
||||
# qtbot.stop()
|
||||
|
||||
# END Test testBuildSettings_Format
|
||||
|
||||
|
||||
@pytest.mark.gui
|
||||
def testBuildSettings_Output(qtbot: QtBot, nwGUI: GuiMain):
|
||||
"""Test the Output Tab of the GuiBuildSettings dialog."""
|
||||
build = BuildSettings()
|
||||
|
||||
build.setValue("odt.addColours", False)
|
||||
build.setValue("html.addStyles", False)
|
||||
|
||||
# Create the dialog and populate it
|
||||
bSettings = GuiBuildSettings(nwGUI, nwGUI, build)
|
||||
bSettings.show()
|
||||
bSettings.loadContent()
|
||||
|
||||
outTab = bSettings.optTabOutput
|
||||
bSettings.optSideBar._group.button(bSettings.OPT_OUTPUT).click()
|
||||
assert bSettings.toolStack.currentWidget() is outTab
|
||||
|
||||
# Check initial values
|
||||
assert outTab.odtAddColours.isChecked() is False
|
||||
assert outTab.htmlAddStyles.isChecked() is False
|
||||
|
||||
# Toggle all
|
||||
outTab.odtAddColours.setChecked(True)
|
||||
outTab.htmlAddStyles.setChecked(True)
|
||||
|
||||
# Save values
|
||||
outTab.saveContent()
|
||||
|
||||
assert build.getBool("odt.addColours") is True
|
||||
assert build.getBool("html.addStyles") is True
|
||||
|
||||
# Finish
|
||||
bSettings._dialogButtonClicked(bSettings.dlgButtons.button(QDialogButtonBox.Close))
|
||||
# qtbot.stop()
|
||||
|
||||
# END Test testBuildSettings_Output
|
||||
Reference in New Issue
Block a user