diff --git a/.gitignore b/.gitignore index fdfe7e87..57dc50a9 100644 --- a/.gitignore +++ b/.gitignore @@ -8,11 +8,12 @@ # Documentation /docs/build/ -novelWriter.qch -novelWriter.qhc +*.qch +*.qhc # Python Temp __pycache__ +*.pyc # Sample Project /nw/assets/sample.zip @@ -27,6 +28,9 @@ __pycache__ /tests/temp /tests/lipsum/cache /tests/lipsum/meta +/tests/minimal/cache +/tests/minimal/meta +/tests/oldproj/cache /.pytest_cache /pytestdebug.log diff --git a/tests/conftest.py b/tests/conftest.py index bafa1fbb..8967ca5d 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -185,7 +185,7 @@ def nwOldProj(nwTemp): @pytest.fixture(scope="function") def yesToAll(monkeypatch): - """Make the message boxes/questions always say yes to the dress! + """Make the message boxes/questions always say yes. """ monkeypatch.setattr( QMessageBox, "question", lambda *args, **kwargs: QMessageBox.Yes diff --git a/tests/minimal/meta/guiOptions.json b/tests/minimal/meta/guiOptions.json deleted file mode 100644 index 9e26dfee..00000000 --- a/tests/minimal/meta/guiOptions.json +++ /dev/null @@ -1 +0,0 @@ -{} \ No newline at end of file diff --git a/tests/minimal/meta/sessionStats.log b/tests/minimal/meta/sessionStats.log deleted file mode 100644 index 38cbde3d..00000000 --- a/tests/minimal/meta/sessionStats.log +++ /dev/null @@ -1,2 +0,0 @@ -# Start Time End Time Novel Notes -2020-08-28 11:36:36 2020-08-28 11:36:48 10 0 diff --git a/tests/minimal/meta/tagsIndex.json b/tests/minimal/meta/tagsIndex.json deleted file mode 100644 index ec600a1c..00000000 --- a/tests/minimal/meta/tagsIndex.json +++ /dev/null @@ -1,91 +0,0 @@ -{ - "tagIndex": {}, - "refIndex": { - "a35baf2e93843": { - "T000000": { - "tags": [], - "updated": 1598607396 - }, - "T000001": { - "tags": [], - "updated": 1598607396 - } - }, - "f5ab3e30151e1": { - "T000000": { - "tags": [], - "updated": 1598607396 - }, - "T000001": { - "tags": [], - "updated": 1598607396 - } - }, - "8c659a11cd429": { - "T000000": { - "tags": [], - "updated": 1598607396 - }, - "T000001": { - "tags": [], - "updated": 1598607396 - } - } - }, - "novelIndex": { - "a35baf2e93843": { - "T000001": { - "level": "H1", - "title": "Minimal", - "layout": "TITLE", - "synopsis": "", - "cCount": 7, - "wCount": 1, - "pCount": 0, - "updated": 1598607396 - } - }, - "f5ab3e30151e1": { - "T000001": { - "level": "H2", - "title": "New Chapter", - "layout": "CHAPTER", - "synopsis": "", - "cCount": 11, - "wCount": 2, - "pCount": 0, - "updated": 1598607396 - } - }, - "8c659a11cd429": { - "T000001": { - "level": "H3", - "title": "New Scene", - "layout": "SCENE", - "synopsis": "", - "cCount": 9, - "wCount": 2, - "pCount": 0, - "updated": 1598607396 - } - } - }, - "noteIndex": {}, - "textCounts": { - "a35baf2e93843": [ - 28, - 6, - 1 - ], - "f5ab3e30151e1": [ - 11, - 2, - 0 - ], - "8c659a11cd429": [ - 9, - 2, - 0 - ] - } -} \ No newline at end of file diff --git a/tests/nwtools.py b/tests/nwtools.py index 0a9516aa..0b8896a3 100644 --- a/tests/nwtools.py +++ b/tests/nwtools.py @@ -2,18 +2,13 @@ """novelWriter Test Tools """ -from os import path, mkdir from itertools import chain from PyQt5.QtWidgets import qApp -def ensureDir(theDir): - if not path.isdir(theDir): - mkdir(theDir) - return - def cmpFiles(fileOne, fileTwo, ignoreLines=[]): - + """Compare two files, but optionally ignore lines given by a list. + """ try: foOne = open(fileOne, mode="r", encoding="utf8") except Exception as e: @@ -54,6 +49,8 @@ def cmpFiles(fileOne, fileTwo, ignoreLines=[]): return not diffFound def cmpList(listOne, listTwo): + """Compare two iterable objects. + """ flatOne = list(chain.from_iterable([listOne])) flatTwo = list(chain.from_iterable([listTwo])) if len(flatOne) != len(flatTwo): @@ -64,6 +61,8 @@ def cmpList(listOne, listTwo): return True def getGuiItem(theName): + """Returns a QtWidget based on its objectName. + """ for qWidget in qApp.topLevelWidgets(): if qWidget.objectName() == theName: return qWidget diff --git a/tests/test_dialogs.py b/tests/test_dialogs.py index 0076e181..40970d2f 100644 --- a/tests/test_dialogs.py +++ b/tests/test_dialogs.py @@ -26,6 +26,7 @@ from nw.gui.custom import QuotesDialog from nw.constants import nwItemType, nwItemLayout, nwItemClass keyDelay = 2 +typeDelay = 1 stepDelay = 20 @pytest.mark.gui @@ -60,14 +61,14 @@ def testProjectSettings(qtbot, monkeypatch, yesToAll, nwFuncTemp, nwTempGUI, nwR qtbot.wait(stepDelay) projEdit.tabMain.editName.setText("") for c in "Project Name": - qtbot.keyClick(projEdit.tabMain.editName, c, delay=keyDelay) + qtbot.keyClick(projEdit.tabMain.editName, c, delay=typeDelay) for c in "Project Title": - qtbot.keyClick(projEdit.tabMain.editTitle, c, delay=keyDelay) + qtbot.keyClick(projEdit.tabMain.editTitle, c, delay=typeDelay) for c in "Jane Doe": - qtbot.keyClick(projEdit.tabMain.editAuthors, c, delay=keyDelay) + qtbot.keyClick(projEdit.tabMain.editAuthors, c, delay=typeDelay) qtbot.keyClick(projEdit.tabMain.editAuthors, Qt.Key_Return, delay=keyDelay) for c in "John Doh": - qtbot.keyClick(projEdit.tabMain.editAuthors, c, delay=keyDelay) + qtbot.keyClick(projEdit.tabMain.editAuthors, c, delay=typeDelay) # Test Status Tab qtbot.wait(stepDelay) @@ -77,9 +78,9 @@ def testProjectSettings(qtbot, monkeypatch, yesToAll, nwFuncTemp, nwTempGUI, nwR qtbot.mouseClick(projEdit.tabStatus.newButton, Qt.LeftButton) projEdit.tabStatus.listBox.item(3).setSelected(True) for n in range(8): - qtbot.keyClick(projEdit.tabStatus.editName, Qt.Key_Backspace, delay=keyDelay) + qtbot.keyClick(projEdit.tabStatus.editName, Qt.Key_Backspace, delay=typeDelay) for c in "Final": - qtbot.keyClick(projEdit.tabStatus.editName, c, delay=keyDelay) + qtbot.keyClick(projEdit.tabStatus.editName, c, delay=typeDelay) qtbot.mouseClick(projEdit.tabStatus.saveButton, Qt.LeftButton) # Auto-Replace Tab @@ -89,9 +90,9 @@ def testProjectSettings(qtbot, monkeypatch, yesToAll, nwFuncTemp, nwTempGUI, nwR qtbot.mouseClick(projEdit.tabReplace.addButton, Qt.LeftButton) projEdit.tabReplace.listBox.topLevelItem(0).setSelected(True) for c in "Th is ": - qtbot.keyClick(projEdit.tabReplace.editKey, c, delay=keyDelay) + qtbot.keyClick(projEdit.tabReplace.editKey, c, delay=typeDelay) for c in "With This Stuff ": - qtbot.keyClick(projEdit.tabReplace.editValue, c, delay=keyDelay) + qtbot.keyClick(projEdit.tabReplace.editValue, c, delay=typeDelay) qtbot.mouseClick(projEdit.tabReplace.saveButton, Qt.LeftButton) qtbot.wait(stepDelay) @@ -159,7 +160,7 @@ def testItemEditor(qtbot, yesToAll, nwFuncTemp, nwTempGUI, nwRef, nwTemp): assert itemEdit.editLayout.currentData() == nwItemLayout.SCENE for c in "Just a Page": - qtbot.keyClick(itemEdit.editName, c, delay=keyDelay) + qtbot.keyClick(itemEdit.editName, c, delay=typeDelay) itemEdit.editStatus.setCurrentIndex(1) layoutIdx = itemEdit.editLayout.findData(nwItemLayout.PAGE) itemEdit.editLayout.setCurrentIndex(layoutIdx) @@ -747,8 +748,11 @@ def testNewProjectWizard(qtbot, monkeypatch, yesToAll, nwMinimal, nwTemp): nwGUI.show() qtbot.waitForWindowShown(nwGUI) qtbot.wait(stepDelay) + nwGUI.mainConf.lastPath = " " - for wStep in range(3): + for wStep in range(4): + # This does not actually create the project, it just generates the + # dictionary that defines it. # The Wizard nwWiz = GuiProjectWizard(nwGUI) @@ -762,15 +766,15 @@ def testNewProjectWizard(qtbot, monkeypatch, yesToAll, nwMinimal, nwTemp): qtbot.wait(stepDelay) for c in "Test Minimal": - qtbot.keyClick(introPage.projName, c, delay=keyDelay) + qtbot.keyClick(introPage.projName, c, delay=typeDelay) qtbot.wait(stepDelay) for c in "Minimal Novel": - qtbot.keyClick(introPage.projTitle, c, delay=keyDelay) + qtbot.keyClick(introPage.projTitle, c, delay=typeDelay) qtbot.wait(stepDelay) for c in "Jane Doe": - qtbot.keyClick(introPage.projAuthors, c, delay=keyDelay) + qtbot.keyClick(introPage.projAuthors, c, delay=typeDelay) # Setting projName should activate the button assert nwWiz.button(QWizard.NextButton).isEnabled() @@ -783,10 +787,20 @@ def testNewProjectWizard(qtbot, monkeypatch, yesToAll, nwMinimal, nwTemp): assert isinstance(storagePage, ProjWizardFolderPage) assert not nwWiz.button(QWizard.NextButton).isEnabled() + if wStep == 0: + # Check invalid path first, the first time we reach here + monkeypatch.setattr(QFileDialog, "getExistingDirectory", lambda *a, **kw: "") + qtbot.wait(stepDelay) + qtbot.mouseClick(storagePage.browseButton, Qt.LeftButton, delay=100) + assert storagePage.projPath.text() == "" + + # Then, we always return nwMinimal as path + monkeypatch.setattr(QFileDialog, "getExistingDirectory", lambda *a, **kw: nwMinimal) + qtbot.wait(stepDelay) - projPath = path.join(nwTemp, "dummy") - for c in projPath: - qtbot.keyClick(storagePage.projPath, c, delay=keyDelay) + qtbot.mouseClick(storagePage.browseButton, Qt.LeftButton, delay=100) + projPath = path.join(nwMinimal, "Test Minimal") + assert storagePage.projPath.text() == projPath # Setting projPath should activate the button assert nwWiz.button(QWizard.NextButton).isEnabled() @@ -805,13 +819,15 @@ def testNewProjectWizard(qtbot, monkeypatch, yesToAll, nwMinimal, nwTemp): elif wStep == 1: popPage.popCustom.setChecked(True) elif wStep == 2: + popPage.popCustom.setChecked(True) + elif wStep == 3: popPage.popSample.setChecked(True) qtbot.wait(stepDelay) qtbot.mouseClick(nwWiz.button(QWizard.NextButton), Qt.LeftButton) # Custom Page - if wStep == 1: + if wStep == 1 or wStep == 2: customPage = nwWiz.currentPage() assert isinstance(customPage, ProjWizardCustomPage) assert nwWiz.button(QWizard.NextButton).isEnabled() @@ -823,6 +839,11 @@ def testNewProjectWizard(qtbot, monkeypatch, yesToAll, nwMinimal, nwTemp): customPage.addObject.setChecked(True) customPage.addEntity.setChecked(True) + if wStep == 2: + customPage.numChapters.setValue(0) + customPage.numScenes.setValue(10) + customPage.chFolders.setChecked(False) + qtbot.wait(stepDelay) qtbot.mouseClick(nwWiz.button(QWizard.NextButton), Qt.LeftButton) @@ -839,9 +860,9 @@ def testNewProjectWizard(qtbot, monkeypatch, yesToAll, nwMinimal, nwTemp): assert projData["projAuthors"] == "Jane Doe" assert projData["projPath"] == projPath assert projData["popMinimal"] == (wStep == 0) - assert projData["popCustom"] == (wStep == 1) - assert projData["popSample"] == (wStep == 2) - if wStep == 1: + assert projData["popCustom"] == (wStep == 1 or wStep == 2) + assert projData["popSample"] == (wStep == 3) + if wStep == 1 or wStep == 2: assert projData["addRoots"] == [ nwItemClass.PLOT, nwItemClass.CHARACTER, @@ -850,9 +871,14 @@ def testNewProjectWizard(qtbot, monkeypatch, yesToAll, nwMinimal, nwTemp): nwItemClass.OBJECT, nwItemClass.ENTITY, ] - assert projData["numChapters"] == 5 - assert projData["numScenes"] == 5 - assert projData["chFolders"] + if wStep == 1: + assert projData["numChapters"] == 5 + assert projData["numScenes"] == 5 + assert projData["chFolders"] + else: + assert projData["numChapters"] == 0 + assert projData["numScenes"] == 10 + assert not projData["chFolders"] else: assert projData["addRoots"] == [] assert projData["numChapters"] == 0 diff --git a/tests/test_gui.py b/tests/test_gui.py index 5289b187..85c09982 100644 --- a/tests/test_gui.py +++ b/tests/test_gui.py @@ -21,6 +21,7 @@ from nw.constants import ( ) keyDelay = 2 +typeDelay = 1 stepDelay = 20 @pytest.mark.gui @@ -165,15 +166,15 @@ def testDocEditor(qtbot, yesToAll, nwFuncTemp, nwTempGUI, nwRef, nwTemp): nwGUI.setFocus(2) qtbot.keyClick(nwGUI.docEditor, "a", modifier=Qt.ControlModifier, delay=keyDelay) for c in "# Jane Doe": - qtbot.keyClick(nwGUI.docEditor, c, delay=keyDelay) + qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) for c in "@tag: Jane": - qtbot.keyClick(nwGUI.docEditor, c, delay=keyDelay) + qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) for c in "This is a file about Jane.": - qtbot.keyClick(nwGUI.docEditor, c, delay=keyDelay) + qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) # Add a Plot File @@ -187,15 +188,15 @@ def testDocEditor(qtbot, yesToAll, nwFuncTemp, nwTempGUI, nwRef, nwTemp): nwGUI.setFocus(2) qtbot.keyClick(nwGUI.docEditor, "a", modifier=Qt.ControlModifier, delay=keyDelay) for c in "# Main Plot": - qtbot.keyClick(nwGUI.docEditor, c, delay=keyDelay) + qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) for c in "@tag: MainPlot": - qtbot.keyClick(nwGUI.docEditor, c, delay=keyDelay) + qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) for c in "This is a file detailing the main plot.": - qtbot.keyClick(nwGUI.docEditor, c, delay=keyDelay) + qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) # Add a World File @@ -214,15 +215,15 @@ def testDocEditor(qtbot, yesToAll, nwFuncTemp, nwTempGUI, nwRef, nwTemp): nwGUI.setFocus(2) qtbot.keyClick(nwGUI.docEditor, "a", modifier=Qt.ControlModifier, delay=keyDelay) for c in "# Main Location": - qtbot.keyClick(nwGUI.docEditor, c, delay=keyDelay) + qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) for c in "@tag: Home": - qtbot.keyClick(nwGUI.docEditor, c, delay=keyDelay) + qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) for c in "This is a file describing Jane's home.": - qtbot.keyClick(nwGUI.docEditor, c, delay=keyDelay) + qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) # Trigger autosaves before making more changes @@ -241,54 +242,54 @@ def testDocEditor(qtbot, yesToAll, nwFuncTemp, nwTempGUI, nwRef, nwTemp): nwGUI.setFocus(2) qtbot.keyClick(nwGUI.docEditor, "a", modifier=Qt.ControlModifier, delay=keyDelay) for c in "# Novel": - qtbot.keyClick(nwGUI.docEditor, c, delay=keyDelay) + qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) for c in "## Chapter": - qtbot.keyClick(nwGUI.docEditor, c, delay=keyDelay) + qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) for c in "@pov: Jane": - qtbot.keyClick(nwGUI.docEditor, c, delay=keyDelay) + qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) for c in "@plot: MainPlot": - qtbot.keyClick(nwGUI.docEditor, c, delay=keyDelay) + qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) for c in "### Scene": - qtbot.keyClick(nwGUI.docEditor, c, delay=keyDelay) + qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) for c in "% How about a comment?": - qtbot.keyClick(nwGUI.docEditor, c, delay=keyDelay) + qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) for c in "@pov: Jane": - qtbot.keyClick(nwGUI.docEditor, c, delay=keyDelay) + qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) for c in "@plot: MainPlot": - qtbot.keyClick(nwGUI.docEditor, c, delay=keyDelay) + qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) for c in "@location: Home": - qtbot.keyClick(nwGUI.docEditor, c, delay=keyDelay) + qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) for c in "#### Some Section": - qtbot.keyClick(nwGUI.docEditor, c, delay=keyDelay) + qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) for c in "@char: Jane": - qtbot.keyClick(nwGUI.docEditor, c, delay=keyDelay) + qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) for c in "This is a paragraph of dummy text.": - qtbot.keyClick(nwGUI.docEditor, c, delay=keyDelay) + qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) @@ -296,32 +297,32 @@ def testDocEditor(qtbot, yesToAll, nwFuncTemp, nwTempGUI, nwRef, nwTemp): "This is another paragraph of much longer dummy text. " "It is in fact very very dumb dummy text! " ): - qtbot.keyClick(nwGUI.docEditor, c, delay=keyDelay) + qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) for c in "We can also try replacing \"quotes\", even single 'quotes' are replaced. ": - qtbot.keyClick(nwGUI.docEditor, c, delay=keyDelay) + qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) for c in "Isn't that nice? ": - qtbot.keyClick(nwGUI.docEditor, c, delay=keyDelay) + qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) for c in "We can hyphen-ate, make dashes -- and even longer dashes --- if we want. ": - qtbot.keyClick(nwGUI.docEditor, c, delay=keyDelay) + qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) for c in "Ellipsis? Not a problem either ... ": - qtbot.keyClick(nwGUI.docEditor, c, delay=keyDelay) + qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) for c in "How about three hyphens - -": - qtbot.keyClick(nwGUI.docEditor, c, delay=keyDelay) + qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Left, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Backspace, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Right, delay=keyDelay) for c in "- for long dash? It works too.": - qtbot.keyClick(nwGUI.docEditor, c, delay=keyDelay) + qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) for c in "\"Full line double quoted text.\"": - qtbot.keyClick(nwGUI.docEditor, c, delay=keyDelay) + qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) for c in "'Full line single quoted text.'": - qtbot.keyClick(nwGUI.docEditor, c, delay=keyDelay) + qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) diff --git a/tests/test_index.py b/tests/test_index.py new file mode 100644 index 00000000..ff3e9260 --- /dev/null +++ b/tests/test_index.py @@ -0,0 +1,226 @@ +# -*- coding: utf-8 -*- +"""novelWriter Project Class Tester +""" + +import pytest + +from nw.core.project import NWProject +from nw.core.index import NWIndex +from nw.constants import nwItemClass + +@pytest.mark.project +def testIndexScanThis(nwMinimal, nwDummy): + + theProject = NWProject(nwDummy) + theProject.projTree.setSeed(42) + assert theProject.openProject(nwMinimal) + + theIndex = NWIndex(theProject, nwDummy) + + isValid, theBits, thePos = theIndex.scanThis("tag: this, and this") + assert not isValid + + isValid, theBits, thePos = theIndex.scanThis("@") + assert not isValid + + isValid, theBits, thePos = theIndex.scanThis("@:") + assert not isValid + + isValid, theBits, thePos = theIndex.scanThis(" @a: b") + assert not isValid + + isValid, theBits, thePos = theIndex.scanThis("@a:") + assert isValid + assert str(theBits) == "['@a']" + assert str(thePos) == "[0]" + + isValid, theBits, thePos = theIndex.scanThis("@a:b") + assert isValid + assert str(theBits) == "['@a', 'b']" + assert str(thePos) == "[0, 3]" + + isValid, theBits, thePos = theIndex.scanThis("@a:b,c,d") + assert isValid + assert str(theBits) == "['@a', 'b', 'c', 'd']" + assert str(thePos) == "[0, 3, 5, 7]" + + isValid, theBits, thePos = theIndex.scanThis("@a : b , c , d") + assert isValid + assert str(theBits) == "['@a', 'b', 'c', 'd']" + assert str(thePos) == "[0, 5, 9, 13]" + + isValid, theBits, thePos = theIndex.scanThis("@tag: this, and this") + assert isValid + assert str(theBits) == "['@tag', 'this', 'and this']" + assert str(thePos) == "[0, 6, 12]" + + assert theProject.closeProject() + +@pytest.mark.project +def testIndexCheckThese(nwMinimal, nwDummy): + + theProject = NWProject(nwDummy) + theProject.projTree.setSeed(42) + assert theProject.openProject(nwMinimal) + + theIndex = NWIndex(theProject, nwDummy) + nHandle = theProject.newFile("Hello", nwItemClass.NOVEL, "a508bb932959c") + cHandle = theProject.newFile("Jane", nwItemClass.CHARACTER, "afb3043c7b2b3") + nItem = theProject.projTree[nHandle] + cItem = theProject.projTree[cHandle] + + assert theIndex.scanText(cHandle, ( + "# Jane Smith\n" + "@tag: Jane" + )) + assert theIndex.scanText(nHandle, ( + "# Hello World!\n" + "@pov: Jane" + )) + assert str(theIndex.tagIndex) == "{'Jane': [2, '%s', 'CHARACTER', 'T000001']}" % cHandle + assert theIndex.novelIndex[nHandle]["T000001"]["title"] == "Hello World!" + + assert str(theIndex.checkThese(["@tag", "Jane"], cItem)) == "[True, True]" + assert str(theIndex.checkThese(["@tag", "John"], cItem)) == "[True, True]" + assert str(theIndex.checkThese(["@tag", "Jane"], nItem)) == "[True, False]" + assert str(theIndex.checkThese(["@tag", "John"], nItem)) == "[True, True]" + assert str(theIndex.checkThese(["@pov", "John"], nItem)) == "[True, False]" + assert str(theIndex.checkThese(["@pov", "Jane"], nItem)) == "[True, True]" + assert str(theIndex.checkThese(["@ pov", "Jane"], nItem)) == "[False, False]" + assert str(theIndex.checkThese(["@what", "Jane"], nItem)) == "[False, False]" + + assert theProject.closeProject() + +@pytest.mark.project +def testIndexMeta(nwMinimal, nwDummy): + + theProject = NWProject(nwDummy) + theProject.projTree.setSeed(42) + assert theProject.openProject(nwMinimal) + + theIndex = NWIndex(theProject, nwDummy) + nHandle = theProject.newFile("Hello", nwItemClass.NOVEL, "a508bb932959c") + cHandle = theProject.newFile("Jane", nwItemClass.CHARACTER, "afb3043c7b2b3") + + assert theIndex.scanText(cHandle, ( + "# Jane Smith\n" + "@tag: Jane\n" + )) + assert theIndex.scanText(nHandle, ( + "# Hello World!\n" + "@pov: Jane\n" + "@char: Jane\n" + "\n" + "% this is a comment\n" + "\n" + "This is a story about Jane Smith.\n" + "\n" + "Well, not really.\n" + )) + assert str(theIndex.tagIndex) == "{'Jane': [2, '%s', 'CHARACTER', 'T000001']}" % cHandle + assert theIndex.novelIndex[nHandle]["T000001"]["title"] == "Hello World!" + + # The novel structure should contain the pointer to the novel file header + assert str(theIndex.getNovelStructure()) == "['%s:T000001']" % nHandle + + # The novel file should have the correct counts + cC, wC, pC = theIndex.getCounts(nHandle) + assert cC == 62 # Characters in text and title only + assert wC == 12 # Words in text and title only + assert pC == 2 # Paragraphs in text only + + # Look up an ivalid handle + theRefs = theIndex.getReferences("Not a handle") + assert theRefs["@pov"] == [] + assert theRefs["@char"] == [] + + # The novel file should now refer to Jane as @pov and @char + theRefs = theIndex.getReferences(nHandle) + assert str(theRefs["@pov"]) == "['Jane']" + assert str(theRefs["@char"]) == "['Jane']" + + # The character file should have a record of the reference from the novel file + theRefs = theIndex.getBackReferenceList(cHandle) + assert str(theRefs) == "{'%s': 'T000001'}" % nHandle + + # Get section counts for a novel file + assert theIndex.scanText(nHandle, ( + "# Hello World!\n" + "@pov: Jane\n" + "@char: Jane\n" + "\n" + "% this is a comment\n" + "\n" + "This is a story about Jane Smith.\n" + "\n" + "Well, not really.\n" + "\n" + "# Hello World!\n" + "@pov: Jane\n" + "@char: Jane\n" + "\n" + "% this is a comment\n" + "\n" + "This is a story about Jane Smith.\n" + "\n" + "Well, not really.\n" + )) + # Whole document + cC, wC, pC = theIndex.getCounts(nHandle) + assert cC == 124 + assert wC == 24 + assert pC == 4 + + # First part + cC, wC, pC = theIndex.getCounts(nHandle, "T000001") + assert cC == 62 + assert wC == 12 + assert pC == 2 + + # First part + cC, wC, pC = theIndex.getCounts(nHandle, "T000011") + assert cC == 62 + assert wC == 12 + assert pC == 2 + + # Get section counts for a note file + assert theIndex.scanText(cHandle, ( + "# Hello World!\n" + "@pov: Jane\n" + "@char: Jane\n" + "\n" + "% this is a comment\n" + "\n" + "This is a story about Jane Smith.\n" + "\n" + "Well, not really.\n" + "\n" + "# Hello World!\n" + "@pov: Jane\n" + "@char: Jane\n" + "\n" + "% this is a comment\n" + "\n" + "This is a story about Jane Smith.\n" + "\n" + "Well, not really.\n" + )) + # Whole document + cC, wC, pC = theIndex.getCounts(cHandle) + assert cC == 124 + assert wC == 24 + assert pC == 4 + + # First part + cC, wC, pC = theIndex.getCounts(cHandle, "T000001") + assert cC == 62 + assert wC == 12 + assert pC == 2 + + # First part + cC, wC, pC = theIndex.getCounts(cHandle, "T000011") + assert cC == 62 + assert wC == 12 + assert pC == 2 + + assert theProject.closeProject() diff --git a/tests/test_project.py b/tests/test_project.py index 9ee7324c..8656ad89 100644 --- a/tests/test_project.py +++ b/tests/test_project.py @@ -11,7 +11,6 @@ from nwtools import cmpFiles from nw.core.project import NWProject from nw.core.document import NWDoc -from nw.core.index import NWIndex from nw.core.spellcheck import NWSpellEnchant, NWSpellSimple from nw.constants import nwItemClass, nwItemType, nwItemLayout, nwFiles @@ -109,223 +108,6 @@ def testProjectNewFile(nwFuncTemp, nwTempProj, nwRef, nwDummy): assert cmpFiles(testFile, refFile, [2, 6, 7, 8]) assert not theProject.projChanged -@pytest.mark.project -def testIndexScanThis(nwMinimal, nwDummy): - - theProject = NWProject(nwDummy) - theProject.projTree.setSeed(42) - assert theProject.openProject(nwMinimal) - - theIndex = NWIndex(theProject, nwDummy) - - isValid, theBits, thePos = theIndex.scanThis("tag: this, and this") - assert not isValid - - isValid, theBits, thePos = theIndex.scanThis("@") - assert not isValid - - isValid, theBits, thePos = theIndex.scanThis("@:") - assert not isValid - - isValid, theBits, thePos = theIndex.scanThis(" @a: b") - assert not isValid - - isValid, theBits, thePos = theIndex.scanThis("@a:") - assert isValid - assert str(theBits) == "['@a']" - assert str(thePos) == "[0]" - - isValid, theBits, thePos = theIndex.scanThis("@a:b") - assert isValid - assert str(theBits) == "['@a', 'b']" - assert str(thePos) == "[0, 3]" - - isValid, theBits, thePos = theIndex.scanThis("@a:b,c,d") - assert isValid - assert str(theBits) == "['@a', 'b', 'c', 'd']" - assert str(thePos) == "[0, 3, 5, 7]" - - isValid, theBits, thePos = theIndex.scanThis("@a : b , c , d") - assert isValid - assert str(theBits) == "['@a', 'b', 'c', 'd']" - assert str(thePos) == "[0, 5, 9, 13]" - - isValid, theBits, thePos = theIndex.scanThis("@tag: this, and this") - assert isValid - assert str(theBits) == "['@tag', 'this', 'and this']" - assert str(thePos) == "[0, 6, 12]" - - assert theProject.closeProject() - -@pytest.mark.project -def testIndexCheckThese(nwMinimal, nwDummy): - - theProject = NWProject(nwDummy) - theProject.projTree.setSeed(42) - assert theProject.openProject(nwMinimal) - - theIndex = NWIndex(theProject, nwDummy) - nHandle = theProject.newFile("Hello", nwItemClass.NOVEL, "a508bb932959c") - cHandle = theProject.newFile("Jane", nwItemClass.CHARACTER, "afb3043c7b2b3") - nItem = theProject.projTree[nHandle] - cItem = theProject.projTree[cHandle] - - assert theIndex.scanText(cHandle, ( - "# Jane Smith\n" - "@tag: Jane" - )) - assert theIndex.scanText(nHandle, ( - "# Hello World!\n" - "@pov: Jane" - )) - assert str(theIndex.tagIndex) == "{'Jane': [2, '%s', 'CHARACTER', 'T000001']}" % cHandle - assert theIndex.novelIndex[nHandle]["T000001"]["title"] == "Hello World!" - - assert str(theIndex.checkThese(["@tag", "Jane"], cItem)) == "[True, True]" - assert str(theIndex.checkThese(["@tag", "John"], cItem)) == "[True, True]" - assert str(theIndex.checkThese(["@tag", "Jane"], nItem)) == "[True, False]" - assert str(theIndex.checkThese(["@tag", "John"], nItem)) == "[True, True]" - assert str(theIndex.checkThese(["@pov", "John"], nItem)) == "[True, False]" - assert str(theIndex.checkThese(["@pov", "Jane"], nItem)) == "[True, True]" - assert str(theIndex.checkThese(["@ pov", "Jane"], nItem)) == "[False, False]" - assert str(theIndex.checkThese(["@what", "Jane"], nItem)) == "[False, False]" - - assert theProject.closeProject() - -@pytest.mark.project -def testIndexMeta(nwMinimal, nwDummy): - - theProject = NWProject(nwDummy) - theProject.projTree.setSeed(42) - assert theProject.openProject(nwMinimal) - - theIndex = NWIndex(theProject, nwDummy) - nHandle = theProject.newFile("Hello", nwItemClass.NOVEL, "a508bb932959c") - cHandle = theProject.newFile("Jane", nwItemClass.CHARACTER, "afb3043c7b2b3") - - assert theIndex.scanText(cHandle, ( - "# Jane Smith\n" - "@tag: Jane\n" - )) - assert theIndex.scanText(nHandle, ( - "# Hello World!\n" - "@pov: Jane\n" - "@char: Jane\n" - "\n" - "% this is a comment\n" - "\n" - "This is a story about Jane Smith.\n" - "\n" - "Well, not really.\n" - )) - assert str(theIndex.tagIndex) == "{'Jane': [2, '%s', 'CHARACTER', 'T000001']}" % cHandle - assert theIndex.novelIndex[nHandle]["T000001"]["title"] == "Hello World!" - - # The novel structure should contain the pointer to the novel file header - assert str(theIndex.getNovelStructure()) == "['%s:T000001']" % nHandle - - # The novel file should have the correct counts - cC, wC, pC = theIndex.getCounts(nHandle) - assert cC == 62 # Characters in text and title only - assert wC == 12 # Words in text and title only - assert pC == 2 # Paragraphs in text only - - # Look up an ivalid handle - theRefs = theIndex.getReferences("Not a handle") - assert theRefs["@pov"] == [] - assert theRefs["@char"] == [] - - # The novel file should now refer to Jane as @pov and @char - theRefs = theIndex.getReferences(nHandle) - assert str(theRefs["@pov"]) == "['Jane']" - assert str(theRefs["@char"]) == "['Jane']" - - # The character file should have a record of the reference from the novel file - theRefs = theIndex.getBackReferenceList(cHandle) - assert str(theRefs) == "{'%s': 'T000001'}" % nHandle - - # Get section counts for a novel file - assert theIndex.scanText(nHandle, ( - "# Hello World!\n" - "@pov: Jane\n" - "@char: Jane\n" - "\n" - "% this is a comment\n" - "\n" - "This is a story about Jane Smith.\n" - "\n" - "Well, not really.\n" - "\n" - "# Hello World!\n" - "@pov: Jane\n" - "@char: Jane\n" - "\n" - "% this is a comment\n" - "\n" - "This is a story about Jane Smith.\n" - "\n" - "Well, not really.\n" - )) - # Whole document - cC, wC, pC = theIndex.getCounts(nHandle) - assert cC == 124 - assert wC == 24 - assert pC == 4 - - # First part - cC, wC, pC = theIndex.getCounts(nHandle, "T000001") - assert cC == 62 - assert wC == 12 - assert pC == 2 - - # First part - cC, wC, pC = theIndex.getCounts(nHandle, "T000011") - assert cC == 62 - assert wC == 12 - assert pC == 2 - - # Get section counts for a note file - assert theIndex.scanText(cHandle, ( - "# Hello World!\n" - "@pov: Jane\n" - "@char: Jane\n" - "\n" - "% this is a comment\n" - "\n" - "This is a story about Jane Smith.\n" - "\n" - "Well, not really.\n" - "\n" - "# Hello World!\n" - "@pov: Jane\n" - "@char: Jane\n" - "\n" - "% this is a comment\n" - "\n" - "This is a story about Jane Smith.\n" - "\n" - "Well, not really.\n" - )) - # Whole document - cC, wC, pC = theIndex.getCounts(cHandle) - assert cC == 124 - assert wC == 24 - assert pC == 4 - - # First part - cC, wC, pC = theIndex.getCounts(cHandle, "T000001") - assert cC == 62 - assert wC == 12 - assert pC == 2 - - # First part - cC, wC, pC = theIndex.getCounts(cHandle, "T000011") - assert cC == 62 - assert wC == 12 - assert pC == 2 - - assert theProject.closeProject() - @pytest.mark.project def testProjectNewCustom(nwFuncTemp, nwTempProj, nwRef, nwDummy):