def testGuiMain_ProjectTreeItems(qtbot, monkeypatch, nwGUI, fncProj, mockRnd): """Test handling of project tree items based on GUI focus states. """ monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes) buildTestProject(nwGUI, fncProj) sHandle = "000000000000f" assert nwGUI.openSelectedItem() is False # Project Tree has focus nwGUI._changeView(nwView.PROJECT) nwGUI.switchFocus(nwWidget.TREE) nwGUI.projStack.setCurrentIndex(0) with monkeypatch.context() as mp: mp.setattr(GuiProjectTree, "hasFocus", lambda *a: True) assert nwGUI.docEditor.docHandle() is None nwGUI.projView.projTree._getTreeItem(sHandle).setSelected(True) nwGUI._keyPressReturn() assert nwGUI.docEditor.docHandle() == sHandle assert nwGUI.closeDocument() is True # Novel Tree has focus nwGUI._changeView(nwView.NOVEL) nwGUI.novelView.novelTree.refreshTree(rootHandle=None, overRide=True) with monkeypatch.context() as mp: mp.setattr(GuiNovelView, "treeHasFocus", lambda *a: True) assert nwGUI.docEditor.docHandle() is None selItem = nwGUI.novelView.novelTree.topLevelItem(2) nwGUI.novelView.novelTree.setCurrentItem(selItem) nwGUI._keyPressReturn() assert nwGUI.docEditor.docHandle() == sHandle assert nwGUI.closeDocument() is True # Project Outline has focus nwGUI._changeView(nwView.OUTLINE) nwGUI.switchFocus(nwWidget.OUTLINE) with monkeypatch.context() as mp: mp.setattr(GuiOutlineView, "treeHasFocus", lambda *a: True) assert nwGUI.docEditor.docHandle() is None actItem = nwGUI.outlineView.outlineTree.topLevelItem(0) chpItem = actItem.child(0) selItem = chpItem.child(0) nwGUI.outlineView.outlineTree.setCurrentItem(selItem) nwGUI._keyPressReturn() assert nwGUI.docEditor.docHandle() == sHandle assert nwGUI.closeDocument() is True
def testGuiMain_FocusFullMode(qtbot, monkeypatch, nwGUI, fncProj, mockRnd): """Test toggling focus mode in main window. """ monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes) buildTestProject(nwGUI, fncProj) assert nwGUI.isFocusMode is False # Focus Mode # ========== # No document open, so not allowing focus mode assert nwGUI.toggleFocusMode() is False # Open a file in editor and viewer assert nwGUI.openDocument("000000000000f") assert nwGUI.viewDocument("000000000000f") # Enable focus mode assert nwGUI.toggleFocusMode() is True assert nwGUI.treePane.isVisible() is False assert nwGUI.statusBar.isVisible() is False assert nwGUI.mainMenu.isVisible() is False assert nwGUI.viewsBar.isVisible() is False assert nwGUI.splitView.isVisible() is False # Disable focus mode assert nwGUI.toggleFocusMode() is True assert nwGUI.treePane.isVisible() is True assert nwGUI.statusBar.isVisible() is True assert nwGUI.mainMenu.isVisible() is True assert nwGUI.viewsBar.isVisible() is True assert nwGUI.splitView.isVisible() is True # Full Screen Mode # ================ assert nwGUI.mainConf.isFullScreen is False nwGUI.toggleFullScreenMode() assert nwGUI.mainConf.isFullScreen is True nwGUI.toggleFullScreenMode() assert nwGUI.mainConf.isFullScreen is False
def testToolLipsum_Main(qtbot, monkeypatch, nwGUI, fncProj, mockRnd): """Test the Lorem Ipsum tool. """ # Block message box monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes) # Check that we cannot open when there is no project nwGUI.mainMenu.aLipsumText.activate(QAction.Trigger) assert getGuiItem("GuiLipsum") is None # Create a new project buildTestProject(nwGUI, fncProj) assert nwGUI.openDocument("000000000000f") is True assert len(nwGUI.docEditor.getText()) == 15 # Open the tool nwGUI.mainMenu.aLipsumText.activate(QAction.Trigger) qtbot.waitUntil(lambda: getGuiItem("GuiLipsum") is not None, timeout=1000) nwLipsum = getGuiItem("GuiLipsum") assert isinstance(nwLipsum, GuiLipsum) # Insert paragraphs nwGUI.docEditor.setCursorPosition(100) # End of document nwLipsum.paraCount.setValue(2) nwLipsum._doInsert() theText = nwGUI.docEditor.getText() assert "Lorem ipsum" in theText assert len(theText) == 965 # Insert random paragraph nwGUI.docEditor.setCursorPosition(1000) # End of document nwLipsum.randSwitch.setChecked(True) nwLipsum.paraCount.setValue(1) nwLipsum._doInsert() theText = nwGUI.docEditor.getText() assert len(theText) > 965 # Close nwLipsum._doClose()
def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir, mockRnd): """Test the document editor. """ # Block message box monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes) monkeypatch.setattr(GuiProjectTree, "hasFocus", lambda *a: True) monkeypatch.setattr(GuiDocEditor, "hasFocus", lambda *a: True) monkeypatch.setattr(QInputDialog, "getText", lambda *a, text: (text, True)) monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True)) # Create new, save, close project buildTestProject(nwGUI, fncProj) assert nwGUI.saveProject() assert nwGUI.closeProject() assert len(nwGUI.theProject.tree) == 0 assert len(nwGUI.theProject.tree._treeOrder) == 0 assert len(nwGUI.theProject.tree._treeRoots) == 0 assert nwGUI.theProject.tree.trashRoot() is None assert nwGUI.theProject.projPath is None assert nwGUI.theProject.projMeta is None assert nwGUI.theProject.projFile == "nwProject.nwx" assert nwGUI.theProject.projName == "" assert nwGUI.theProject.bookTitle == "" assert len(nwGUI.theProject.bookAuthors) == 0 assert not nwGUI.theProject.spellCheck # Check the files projFile = os.path.join(fncProj, "nwProject.nwx") testFile = os.path.join(outDir, "guiEditor_Main_Initial_nwProject.nwx") compFile = os.path.join(refDir, "guiEditor_Main_Initial_nwProject.nwx") copyfile(projFile, testFile) assert cmpFiles(testFile, compFile, ignoreStart=XML_IGNORE) qtbot.wait(stepDelay) # qtbot.stopForInteraction() # Re-open project assert nwGUI.openProject(fncProj) qtbot.wait(stepDelay) # Check that we loaded the data assert len(nwGUI.theProject.tree) == 8 assert len(nwGUI.theProject.tree._treeOrder) == 8 assert len(nwGUI.theProject.tree._treeRoots) == 4 assert nwGUI.theProject.tree.trashRoot() is None assert nwGUI.theProject.projPath == fncProj assert nwGUI.theProject.projMeta == os.path.join(fncProj, "meta") assert nwGUI.theProject.projFile == "nwProject.nwx" assert nwGUI.theProject.projName == "New Project" assert nwGUI.theProject.bookTitle == "New Novel" assert len(nwGUI.theProject.bookAuthors) == 1 assert nwGUI.theProject.spellCheck is False # Check that tree items have been created assert nwGUI.projView.projTree._getTreeItem("0000000000008") is not None assert nwGUI.projView.projTree._getTreeItem("0000000000009") is not None assert nwGUI.projView.projTree._getTreeItem("000000000000a") is not None assert nwGUI.projView.projTree._getTreeItem("000000000000b") is not None assert nwGUI.projView.projTree._getTreeItem("000000000000c") is not None assert nwGUI.projView.projTree._getTreeItem("000000000000d") is not None assert nwGUI.projView.projTree._getTreeItem("000000000000e") is not None assert nwGUI.projView.projTree._getTreeItem("000000000000f") is not None nwGUI.mainMenu.aSpellCheck.setChecked(True) assert nwGUI.mainMenu._toggleSpellCheck() # Change some settings nwGUI.mainConf.hideHScroll = True nwGUI.mainConf.hideVScroll = True nwGUI.mainConf.autoScrollPos = 80 nwGUI.mainConf.autoScroll = True # Add a Character File nwGUI.switchFocus(nwWidget.TREE) nwGUI.projView.projTree.clearSelection() nwGUI.projView.projTree._getTreeItem("000000000000a").setSelected(True) nwGUI.projView.projTree.newTreeItem(nwItemType.FILE, None, isNote=True) assert nwGUI.openSelectedItem() # Type something into the document nwGUI.switchFocus(nwWidget.EDITOR) qtbot.keyClick(nwGUI.docEditor, "a", modifier=Qt.ControlModifier, delay=keyDelay) for c in "# Jane Doe": 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=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=typeDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) # Add a Plot File nwGUI.switchFocus(nwWidget.TREE) nwGUI.projView.projTree.clearSelection() nwGUI.projView.projTree._getTreeItem("0000000000009").setSelected(True) nwGUI.projView.projTree.newTreeItem(nwItemType.FILE, None, isNote=True) assert nwGUI.openSelectedItem() # Type something into the document nwGUI.switchFocus(nwWidget.EDITOR) qtbot.keyClick(nwGUI.docEditor, "a", modifier=Qt.ControlModifier, delay=keyDelay) for c in "# Main Plot": 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=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=typeDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) # Add a World File nwGUI.switchFocus(nwWidget.TREE) nwGUI.projView.projTree.clearSelection() nwGUI.projView.projTree._getTreeItem("000000000000b").setSelected(True) nwGUI.projView.projTree.newTreeItem(nwItemType.FILE, None, isNote=True) assert nwGUI.openSelectedItem() # Add Some Text nwGUI.docEditor.replaceText("Hello World!") assert nwGUI.docEditor.getText() == "Hello World!" nwGUI.docEditor.replaceText("") # Type something into the document nwGUI.switchFocus(nwWidget.EDITOR) qtbot.keyClick(nwGUI.docEditor, "a", modifier=Qt.ControlModifier, delay=keyDelay) for c in "# Main Location": 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=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=typeDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) # Trigger autosaves before making more changes nwGUI._autoSaveDocument() nwGUI._autoSaveProject() # Select the 'New Scene' file nwGUI.switchFocus(nwWidget.TREE) nwGUI.projView.projTree.clearSelection() nwGUI.projView.projTree._getTreeItem("0000000000008").setExpanded(True) nwGUI.projView.projTree._getTreeItem("000000000000d").setExpanded(True) nwGUI.projView.projTree._getTreeItem("000000000000f").setSelected(True) assert nwGUI.openSelectedItem() # Type something into the document nwGUI.switchFocus(nwWidget.EDITOR) qtbot.keyClick(nwGUI.docEditor, "a", modifier=Qt.ControlModifier, delay=keyDelay) for c in "# Novel": 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=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=typeDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) for c in "@plot: MainPlot": 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=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=typeDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) for c in "@pov: Jane": 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=typeDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) for c in "@location: Home": 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=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=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 nonsense text.": 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 another paragraph of much longer nonsense text. " "It is in fact 1 very very NONSENSICAL nonsense text! "): 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=typeDelay) for c in "Isn't that nice? ": 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=typeDelay) for c in "Ellipsis? Not a problem either ... ": qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) for c in "How about three hyphens - -": 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=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=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=typeDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) for c in "\t\"Tab-indented text\"": 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 ">\"Paragraph-indented text\"": 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 ">>\"Right-aligned text\"": 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 "\t'Tab-indented text'": 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 ">'Paragraph-indented text'": 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 ">>'Right-aligned text'": 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) qtbot.wait(stepDelay) nwGUI.docEditor.wCounterDoc.run() qtbot.wait(stepDelay) # Save the document assert nwGUI.docEditor.docChanged() assert nwGUI.saveDocument() assert not nwGUI.docEditor.docChanged() qtbot.wait(stepDelay) nwGUI.rebuildIndex() qtbot.wait(stepDelay) # Open and view the edited document nwGUI.switchFocus(nwWidget.VIEWER) assert nwGUI.openDocument("000000000000f") assert nwGUI.viewDocument("000000000000f") qtbot.wait(stepDelay) assert nwGUI.saveProject() assert nwGUI.closeDocViewer() qtbot.wait(stepDelay) # Check a Quick Create and Delete assert nwGUI.projView.projTree.newTreeItem(nwItemType.FILE, None) newHandle = nwGUI.projView.getSelectedHandle() assert nwGUI.theProject.tree["0000000000020"] is not None assert nwGUI.projView.deleteItem() assert nwGUI.projView.setSelectedHandle(newHandle) assert nwGUI.projView.deleteItem() assert nwGUI.theProject.tree["0000000000024"] is not None # Trash assert nwGUI.saveProject() # Check the files projFile = os.path.join(fncProj, "nwProject.nwx") testFile = os.path.join(outDir, "guiEditor_Main_Final_nwProject.nwx") compFile = os.path.join(refDir, "guiEditor_Main_Final_nwProject.nwx") copyfile(projFile, testFile) assert cmpFiles(testFile, compFile, ignoreStart=XML_IGNORE) projFile = os.path.join(fncProj, "content", "000000000000f.nwd") testFile = os.path.join(outDir, "guiEditor_Main_Final_000000000000f.nwd") compFile = os.path.join(refDir, "guiEditor_Main_Final_000000000000f.nwd") copyfile(projFile, testFile) assert cmpFiles(testFile, compFile) projFile = os.path.join(fncProj, "content", "0000000000020.nwd") testFile = os.path.join(outDir, "guiEditor_Main_Final_0000000000020.nwd") compFile = os.path.join(refDir, "guiEditor_Main_Final_0000000000020.nwd") copyfile(projFile, testFile) assert cmpFiles(testFile, compFile) projFile = os.path.join(fncProj, "content", "0000000000021.nwd") testFile = os.path.join(outDir, "guiEditor_Main_Final_0000000000021.nwd") compFile = os.path.join(refDir, "guiEditor_Main_Final_0000000000021.nwd") copyfile(projFile, testFile) assert cmpFiles(testFile, compFile) projFile = os.path.join(fncProj, "content", "0000000000022.nwd") testFile = os.path.join(outDir, "guiEditor_Main_Final_0000000000022.nwd") compFile = os.path.join(refDir, "guiEditor_Main_Final_0000000000022.nwd") copyfile(projFile, testFile) assert cmpFiles(testFile, compFile)
def testDlgSplit_Main(qtbot, monkeypatch, nwGUI, fncProj, mockRnd): """Test the split document tool. """ # Block message box monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Ok) monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True)) # Create a new project buildTestProject(nwGUI, fncProj) # Handles for new objects hNovelRoot = "0000000000008" hChapterDir = "000000000000d" hToSplit = "0000000000010" hNewFolder = "0000000000021" hPartition = "0000000000022" hChapterOne = "0000000000023" hSceneOne = "0000000000024" hSceneTwo = "0000000000025" hSceneThree = "0000000000026" hSceneFour = "0000000000027" hSceneFive = "0000000000028" # Add Project Content nwGUI.switchFocus(nwWidget.TREE) nwGUI.projView.projTree.clearSelection() nwGUI.projView.projTree._getTreeItem(hNovelRoot).setSelected(True) nwGUI.projView.projTree.newTreeItem(nwItemType.FILE) assert nwGUI.saveProject() is True assert nwGUI.closeProject() is True tPartition = "# Nantucket" tChapterOne = "## Chapter One\n\n% Chapter one comment" tSceneOne = "### Scene One\n\nThere once was a man from Nantucket" tSceneTwo = "### Scene Two\n\nWho kept all his cash in a bucket." tSceneThree = "### Scene Three\n\n\tBut his daughter, named Nan, \n\tRan away with a man" tSceneFour = "### Scene Four\n\nAnd as for the bucket, Nantucket." tSceneFive = "#### The End\n\nend" tToSplit = (f"{tPartition}\n\n{tChapterOne}\n\n" f"{tSceneOne}\n\n{tSceneTwo}\n\n" f"{tSceneThree}\n\n{tSceneFour}\n\n" f"{tSceneFive}\n\n") contentDir = os.path.join(fncProj, "content") writeFile(os.path.join(contentDir, hToSplit + ".nwd"), tToSplit) assert nwGUI.openProject(fncProj) is True # Open the Split tool nwGUI.switchFocus(nwWidget.TREE) nwGUI.projView.projTree.clearSelection() nwGUI.projView.projTree._getTreeItem(hToSplit).setSelected(True) monkeypatch.setattr(GuiDocSplit, "exec_", lambda *a: None) nwGUI.mainMenu.aSplitDoc.activate(QAction.Trigger) qtbot.waitUntil(lambda: getGuiItem("GuiDocSplit") is not None, timeout=1000) nwSplit = getGuiItem("GuiDocSplit") assert isinstance(nwSplit, GuiDocSplit) nwSplit.show() qtbot.wait(50) # Populate List # ============= nwSplit.listBox.clear() assert nwSplit.listBox.count() == 0 # No item selected nwSplit.sourceItem = None nwGUI.projView.projTree.clearSelection() assert nwSplit._populateList() is False assert nwSplit.listBox.count() == 0 # Non-existing item with monkeypatch.context() as mp: mp.setattr(NWTree, "__getitem__", lambda *a: None) nwSplit.sourceItem = None nwGUI.projView.projTree.clearSelection() nwGUI.projView.projTree._getTreeItem(hToSplit).setSelected(True) assert nwSplit._populateList() is False assert nwSplit.listBox.count() == 0 # Select a non-file nwSplit.sourceItem = None nwGUI.projView.projTree.clearSelection() nwGUI.projView.projTree._getTreeItem(hChapterDir).setSelected(True) assert nwSplit._populateList() is False assert nwSplit.listBox.count() == 0 # Error when reading documents with monkeypatch.context() as mp: mp.setattr(NWDoc, "readDocument", lambda *a: None) nwSplit.sourceItem = hToSplit assert nwSplit._populateList() is False assert nwSplit.listBox.count() == 0 # Read properly, and check split levels # Level 1 nwSplit.splitLevel.setCurrentIndex(0) nwSplit.sourceItem = hToSplit assert nwSplit._populateList() is True assert nwSplit.listBox.count() == 1 # Level 2 nwSplit.splitLevel.setCurrentIndex(1) nwSplit.sourceItem = hToSplit assert nwSplit._populateList() is True assert nwSplit.listBox.count() == 2 # Level 3 nwSplit.splitLevel.setCurrentIndex(2) nwSplit.sourceItem = hToSplit assert nwSplit._populateList() is True assert nwSplit.listBox.count() == 6 # Level 4 nwSplit.splitLevel.setCurrentIndex(3) nwSplit.sourceItem = hToSplit assert nwSplit._populateList() is True assert nwSplit.listBox.count() == 7 # Split Document # ============== # Test a proper split first with monkeypatch.context() as mp: mp.setattr(GuiDocSplit, "_doClose", lambda *a: None) assert nwSplit._doSplit() is True assert nwGUI.saveProject() assert readFile(os.path.join( contentDir, hPartition + ".nwd")) == ("%%%%~name: Nantucket\n" "%%%%~path: %s/%s\n" "%%%%~kind: NOVEL/DOCUMENT\n" "%s\n\n") % (hNewFolder, hPartition, tPartition) assert readFile(os.path.join( contentDir, hChapterOne + ".nwd")) == ("%%%%~name: Chapter One\n" "%%%%~path: %s/%s\n" "%%%%~kind: NOVEL/DOCUMENT\n" "%s\n\n") % (hNewFolder, hChapterOne, tChapterOne) assert readFile(os.path.join( contentDir, hSceneOne + ".nwd")) == ("%%%%~name: Scene One\n" "%%%%~path: %s/%s\n" "%%%%~kind: NOVEL/DOCUMENT\n" "%s\n\n") % (hNewFolder, hSceneOne, tSceneOne) assert readFile(os.path.join( contentDir, hSceneTwo + ".nwd")) == ("%%%%~name: Scene Two\n" "%%%%~path: %s/%s\n" "%%%%~kind: NOVEL/DOCUMENT\n" "%s\n\n") % (hNewFolder, hSceneTwo, tSceneTwo) assert readFile(os.path.join( contentDir, hSceneThree + ".nwd")) == ("%%%%~name: Scene Three\n" "%%%%~path: %s/%s\n" "%%%%~kind: NOVEL/DOCUMENT\n" "%s\n\n") % (hNewFolder, hSceneThree, tSceneThree) assert readFile(os.path.join( contentDir, hSceneFour + ".nwd")) == ("%%%%~name: Scene Four\n" "%%%%~path: %s/%s\n" "%%%%~kind: NOVEL/DOCUMENT\n" "%s\n\n") % (hNewFolder, hSceneFour, tSceneFour) assert readFile(os.path.join( contentDir, hSceneFive + ".nwd")) == ("%%%%~name: The End\n" "%%%%~path: %s/%s\n" "%%%%~kind: NOVEL/DOCUMENT\n" "%s\n\n") % (hNewFolder, hSceneFive, tSceneFive) # OS error with monkeypatch.context() as mp: mp.setattr("builtins.open", causeOSError) assert nwSplit._doSplit() is False # Select to not split with monkeypatch.context() as mp: mp.setattr(QMessageBox, "question", lambda *a: QMessageBox.No) assert nwSplit._doSplit() is False # Clear the list nwSplit.listBox.clear() assert nwSplit._doSplit() is False # Can't find sourcv item with monkeypatch.context() as mp: mp.setattr(NWTree, "__getitem__", lambda *a: None) assert nwSplit._doSplit() is False # No source item set nwSplit.sourceItem = None assert nwSplit._doSplit() is False # Close nwSplit._doClose()
def testGuiProjTree_NewItems(qtbot, caplog, monkeypatch, nwGUI, fncDir, mockRnd): """Test adding and removing items from the project tree. """ # Block message box monkeypatch.setattr(QMessageBox, "warning", lambda *a: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes) monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True)) nwTree = nwGUI.projView # Try to add item with no project assert nwTree.projTree.newTreeItem(nwItemType.FILE) is False # Create a project prjDir = os.path.join(fncDir, "project") buildTestProject(nwGUI, prjDir) # No itemType set nwTree.projTree.clearSelection() assert nwTree.projTree.newTreeItem(None) is False # Root Items # ========== # No class set assert nwTree.projTree.newTreeItem(nwItemType.ROOT) is False # Create root item assert nwTree.projTree.newTreeItem(nwItemType.ROOT, nwItemClass.WORLD) is True assert "0000000000010" in nwGUI.theProject.tree # File/Folder Items # ================= # No location selected for new item nwTree.projTree.clearSelection() caplog.clear() assert nwTree.projTree.newTreeItem(nwItemType.FILE) is False assert nwTree.projTree.newTreeItem(nwItemType.FOLDER) is False assert "Did not find anywhere" in caplog.text # Create new folder as child of Novel folder nwTree.setSelectedHandle("0000000000008") assert nwTree.projTree.newTreeItem(nwItemType.FOLDER) is True assert nwGUI.theProject.tree["0000000000011"].itemParent == "0000000000008" assert nwGUI.theProject.tree["0000000000011"].itemRoot == "0000000000008" assert nwGUI.theProject.tree["0000000000011"].itemClass == nwItemClass.NOVEL # Add a new file in the new folder nwTree.setSelectedHandle("0000000000011") assert nwTree.projTree.newTreeItem(nwItemType.FILE) is True assert nwGUI.theProject.tree["0000000000012"].itemParent == "0000000000011" assert nwGUI.theProject.tree["0000000000012"].itemRoot == "0000000000008" assert nwGUI.theProject.tree["0000000000012"].itemClass == nwItemClass.NOVEL # Add a new chapter next to the other new file nwTree.setSelectedHandle("0000000000012") assert nwTree.projTree.newTreeItem(nwItemType.FILE, hLevel=2) is True assert nwGUI.theProject.tree["0000000000013"].itemParent == "0000000000011" assert nwGUI.theProject.tree["0000000000013"].itemRoot == "0000000000008" assert nwGUI.theProject.tree["0000000000013"].itemClass == nwItemClass.NOVEL assert nwGUI.openDocument("0000000000013") assert nwGUI.docEditor.getText() == "## New Chapter\n\n" # Add a new scene next to the other new file nwTree.setSelectedHandle("0000000000012") assert nwTree.projTree.newTreeItem(nwItemType.FILE, hLevel=3) is True assert nwGUI.theProject.tree["0000000000014"].itemParent == "0000000000011" assert nwGUI.theProject.tree["0000000000014"].itemRoot == "0000000000008" assert nwGUI.theProject.tree["0000000000014"].itemClass == nwItemClass.NOVEL assert nwGUI.openDocument("0000000000014") assert nwGUI.docEditor.getText() == "### New Scene\n\n" # Add a new file to the characters folder nwTree.setSelectedHandle("000000000000a") assert nwTree.projTree.newTreeItem(nwItemType.FILE, hLevel=1, isNote=True) is True assert nwGUI.theProject.tree["0000000000015"].itemParent == "000000000000a" assert nwGUI.theProject.tree["0000000000015"].itemRoot == "000000000000a" assert nwGUI.theProject.tree["0000000000015"].itemClass == nwItemClass.CHARACTER assert nwGUI.openDocument("0000000000015") assert nwGUI.docEditor.getText() == "# New Note\n\n" # Make sure the sibling folder bug trap works nwTree.setSelectedHandle("0000000000013") nwGUI.theProject.tree["0000000000013"].setParent(None) # This should not happen caplog.clear() assert nwTree.projTree.newTreeItem(nwItemType.FILE) is False assert "Internal error" in caplog.text nwGUI.theProject.tree["0000000000013"].setParent("0000000000011") # Cancel during creation with monkeypatch.context() as mp: mp.setattr(GuiEditLabel, "getLabel", lambda *a, **k: ("", False)) nwTree.setSelectedHandle("0000000000013") assert nwTree.projTree.newTreeItem(nwItemType.FILE) is False # Get the trash folder nwTree.projTree._addTrashRoot() trashHandle = nwGUI.theProject.trashFolder() nwTree.setSelectedHandle(trashHandle) assert nwTree.projTree.newTreeItem(nwItemType.FILE) is False assert "Cannot add new files or folders to the Trash folder" in caplog.text # Other Checks # ============ # Also check error handling in reveal function assert nwTree.revealNewTreeItem("abc") is False # Add an item that cannot be displayed in the tree nHandle = nwGUI.theProject.newFile("Test", None) assert nwTree.revealNewTreeItem(nHandle) is False # Clean up # qtbot.stopForInteraction() nwGUI.closeProject()
def testGuiOutline_Main(qtbot, monkeypatch, nwGUI, fncDir): """Test the outline view. """ # Block message box monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes) # Create a project prjDir = os.path.join(fncDir, "project") buildTestProject(nwGUI, prjDir) nwGUI.rebuildIndex() nwGUI._changeView(nwView.OUTLINE) outlineView = nwGUI.outlineView outlineTree = outlineView.outlineTree outlineData = outlineView.outlineData outlineMenu = outlineView.outlineBar.mColumns # Toggle scrollbars nwGUI.mainConf.hideVScroll = True nwGUI.mainConf.hideHScroll = True outlineView.initOutline() assert outlineTree.verticalScrollBarPolicy() == Qt.ScrollBarAlwaysOff assert outlineTree.horizontalScrollBarPolicy() == Qt.ScrollBarAlwaysOff assert outlineData.verticalScrollBarPolicy() == Qt.ScrollBarAlwaysOff assert outlineData.horizontalScrollBarPolicy() == Qt.ScrollBarAlwaysOff nwGUI.mainConf.hideVScroll = False nwGUI.mainConf.hideHScroll = False outlineView.initOutline() assert outlineTree.verticalScrollBarPolicy() == Qt.ScrollBarAsNeeded assert outlineTree.horizontalScrollBarPolicy() == Qt.ScrollBarAsNeeded assert outlineData.verticalScrollBarPolicy() == Qt.ScrollBarAsNeeded assert outlineData.horizontalScrollBarPolicy() == Qt.ScrollBarAsNeeded # Check focus with monkeypatch.context() as mp: mp.setattr(QWidget, "hasFocus", lambda *a: True) assert outlineView.treeHasFocus() is True outlineView.setTreeFocus() # Can't check. just ensures that it doesn't error # Option State # ============ pOptions = nwGUI.theProject.options colNames = [h.name for h in nwOutline] colItems = [h for h in nwOutline] colWidth = {h: outlineTree.DEF_WIDTH[h] for h in nwOutline} colHidden = {h: outlineTree.DEF_HIDDEN[h] for h in nwOutline} assert outlineTree.topLevelItemCount() > 0 # Save header state not allowed outlineTree._lastBuild = 0 outlineTree._saveHeaderState() assert pOptions.getValue("GuiOutline", "headerOrder", []) == [] # Allow saving header state outlineTree._lastBuild = time.time() outlineTree._saveHeaderState() assert pOptions.getValue("GuiOutline", "headerOrder", []) == colNames assert outlineTree._treeOrder == colItems assert outlineTree._colWidth == colWidth assert outlineTree._colHidden == colHidden # Get default values optItems = pOptions.getValue("GuiOutline", "headerOrder", []) optWidth = pOptions.getValue("GuiOutline", "columnWidth", {}) optHidden = pOptions.getValue("GuiOutline", "columnHidden", {}) # Add invalid column name pOptions.setValue("GuiOutline", "headerOrder", optItems + ["blabla"]) outlineTree._loadHeaderState() assert outlineTree._treeOrder == colItems assert outlineTree._colHidden == colHidden # Add duplicate column name pOptions.setValue("GuiOutline", "headerOrder", optItems + [optItems[-1]]) outlineTree._loadHeaderState() assert outlineTree._treeOrder == colItems assert outlineTree._colHidden == colHidden # Invalid column width data pOptions.setValue("GuiOutline", "headerOrder", optItems) pOptions.setValue("GuiOutline", "columnWidth", {"blabla": None}) outlineTree._loadHeaderState() assert outlineTree._treeOrder == colItems assert outlineTree._colHidden == colHidden # Invalid column width data pOptions.setValue("GuiOutline", "headerOrder", optItems) pOptions.setValue("GuiOutline", "columnWidth", optWidth) pOptions.setValue("GuiOutline", "columnHidden", {"bloabla": None}) outlineTree._loadHeaderState() assert outlineTree._treeOrder == colItems assert outlineTree._colHidden == colHidden # Valid settings pOptions.setValue("GuiOutline", "headerOrder", optItems) pOptions.setValue("GuiOutline", "columnWidth", optWidth) pOptions.setValue("GuiOutline", "columnHidden", optHidden) outlineTree._loadHeaderState() assert outlineTree._treeOrder == colItems assert outlineTree._colHidden == colHidden # Header Menu # =========== # Trigger the menu entry for all hidden columns for hItem in nwOutline: if outlineTree.DEF_HIDDEN[hItem]: outlineMenu.actionMap[hItem].activate(QAction.Trigger) # Now no columns should be hidden outlineTree._saveHeaderState() assert not any(pOptions.getValue("GuiOutline", "columnHidden", None).values())
def testCoreIndex_ItemIndex(mockGUI, fncDir, mockRnd): """Check the ItemIndex class. """ theProject = NWProject(mockGUI) buildTestProject(theProject, fncDir) nHandle = "0000000000014" cHandle = "0000000000016" sHandle = "0000000000017" assert theProject.index.saveIndex() is True itemIndex = theProject.index._itemIndex # The index should be empty assert nHandle not in itemIndex assert cHandle not in itemIndex assert sHandle not in itemIndex # Add Items # ========= assert cHandle not in itemIndex # Add the novel chapter file itemIndex.add(cHandle, theProject.tree[cHandle]) assert cHandle in itemIndex assert itemIndex[cHandle].item == theProject.tree[cHandle] assert itemIndex.mainItemHeader(cHandle) == "H0" assert itemIndex.allItemTags(cHandle) == [] assert list(itemIndex.iterItemHeaders(cHandle))[0][0] == "T000000" # Add a heading to the item, which should replace the T000000 heading itemIndex.addItemHeading(cHandle, "T000001", "H2", "Chapter One") assert itemIndex.mainItemHeader(cHandle) == "H2" assert list(itemIndex.iterItemHeaders(cHandle))[0][0] == "T000001" # Set the remainig data values itemIndex.setHeadingCounts(cHandle, "T000001", 60, 10, 2) itemIndex.setHeadingSynopsis(cHandle, "T000001", "In the beginning ...") itemIndex.setHeadingTag(cHandle, "T000001", "One") itemIndex.addHeadingReferences(cHandle, "T000001", ["Jane"], "@pov") itemIndex.addHeadingReferences(cHandle, "T000001", ["Jane"], "@focus") itemIndex.addHeadingReferences(cHandle, "T000001", ["Jane", "John"], "@char") idxData = itemIndex.packData() assert idxData[cHandle]["level"] == "H2" assert idxData[cHandle]["headings"]["T000001"] == { "level": "H2", "title": "Chapter One", "tag": "One", "cCount": 60, "wCount": 10, "pCount": 2, "synopsis": "In the beginning ...", } assert "@pov" in idxData[cHandle]["references"]["T000001"]["Jane"] assert "@focus" in idxData[cHandle]["references"]["T000001"]["Jane"] assert "@char" in idxData[cHandle]["references"]["T000001"]["Jane"] assert "@char" in idxData[cHandle]["references"]["T000001"]["John"] # Add the other two files itemIndex.add(nHandle, theProject.tree[nHandle]) itemIndex.add(sHandle, theProject.tree[sHandle]) itemIndex.addItemHeading(nHandle, "T000001", "H1", "Novel") itemIndex.addItemHeading(sHandle, "T000001", "H3", "Scene One") # Check Item and Heading Direct Access # ==================================== # Check repr strings assert repr(itemIndex[nHandle]) == f"<IndexItem handle='{nHandle}'>" assert repr( itemIndex[nHandle]["T000001"]) == "<IndexHeading key='T000001'>" # Check content of a single item assert "T000001" in itemIndex[nHandle] assert itemIndex[cHandle].allTags() == ["One"] # Check the content of a single heading assert itemIndex[cHandle]["T000001"].key == "T000001" assert itemIndex[cHandle]["T000001"].level == "H2" assert itemIndex[cHandle]["T000001"].title == "Chapter One" assert itemIndex[cHandle]["T000001"].tag == "One" assert itemIndex[cHandle]["T000001"].charCount == 60 assert itemIndex[cHandle]["T000001"].wordCount == 10 assert itemIndex[cHandle]["T000001"].paraCount == 2 assert itemIndex[cHandle]["T000001"].synopsis == "In the beginning ..." assert "Jane" in itemIndex[cHandle]["T000001"].references assert "John" in itemIndex[cHandle]["T000001"].references # Check heading level setter itemIndex[cHandle]["T000001"].setLevel("H3") # Change it assert itemIndex[cHandle]["T000001"].level == "H3" itemIndex[cHandle]["T000001"].setLevel("H2") # Set it back assert itemIndex[cHandle]["T000001"].level == "H2" itemIndex[cHandle]["T000001"].setLevel("H5") # Invalid level assert itemIndex[cHandle]["T000001"].level == "H2" # Data Extraction # =============== # Get headers allHeads = list(itemIndex.iterAllHeaders()) assert allHeads[0][0] == cHandle assert allHeads[1][0] == nHandle assert allHeads[2][0] == sHandle assert allHeads[0][1] == "T000001" assert allHeads[1][1] == "T000001" assert allHeads[2][1] == "T000001" # Ask for stuff that doesn't exist assert itemIndex.mainItemHeader("blablabla") == "H0" assert itemIndex.allItemTags("blablabla") == [] # Novel Structure # =============== # Add a second novel mHandle = theProject.newRoot(nwItemClass.NOVEL) uHandle = theProject.newFile("Title Page", mHandle) itemIndex.add(uHandle, theProject.tree[uHandle]) itemIndex.addItemHeading(uHandle, "T000001", "H1", "Novel 2") assert uHandle in itemIndex # Structure of all novels nStruct = list(itemIndex.iterNovelStructure()) assert len(nStruct) == 4 assert nStruct[0][0] == nHandle assert nStruct[1][0] == cHandle assert nStruct[2][0] == sHandle assert nStruct[3][0] == uHandle # Novel structure with root handle set nStruct = list(itemIndex.iterNovelStructure(rootHandle="0000000000010")) assert len(nStruct) == 3 assert nStruct[0][0] == nHandle assert nStruct[1][0] == cHandle assert nStruct[2][0] == sHandle nStruct = list(itemIndex.iterNovelStructure(rootHandle=mHandle)) assert len(nStruct) == 1 assert nStruct[0][0] == uHandle # Inject garbage into tree theProject.tree._treeOrder.append("stuff") nStruct = list(itemIndex.iterNovelStructure()) assert len(nStruct) == 4 assert nStruct[0][0] == nHandle assert nStruct[1][0] == cHandle assert nStruct[2][0] == sHandle assert nStruct[3][0] == uHandle # Skip excluded theProject.tree[sHandle].setExported(False) nStruct = list(itemIndex.iterNovelStructure(skipExcl=True)) assert len(nStruct) == 3 assert nStruct[0][0] == nHandle assert nStruct[1][0] == cHandle assert nStruct[2][0] == uHandle # Delete new item del itemIndex[uHandle] assert uHandle not in itemIndex # Unpack Error Handling # ===================== # Pack/unpack should restore state content = itemIndex.packData() itemIndex.clear() itemIndex.unpackData(content) assert itemIndex.packData() == content itemIndex.clear() # Data must be dictionary with pytest.raises(ValueError): itemIndex.unpackData("stuff") # Keys must be valid handles with pytest.raises(ValueError): itemIndex.unpackData({"stuff": "more stuff"}) # Unknown keys should be skipped itemIndex.unpackData({"0000000000000": {}}) assert itemIndex._items == {} # Known keys can be added, even witout data itemIndex.unpackData({nHandle: {}}) assert nHandle in itemIndex # Title tags must be valid with pytest.raises(ValueError): itemIndex.unpackData({cHandle: {"headings": {"TTTTTTT": {}}}}) # Reference without a heading should be rejected itemIndex.unpackData({ cHandle: { "headings": { "T000001": {} }, "references": { "T000001": {}, "T000002": {} }, } }) assert "T000001" in itemIndex[cHandle] assert "T000002" not in itemIndex[cHandle] itemIndex.clear() # Tag keys must be strings with pytest.raises(ValueError): itemIndex.unpackData({ cHandle: { "headings": { "T000001": {} }, "references": { "T000001": { 1234: "@pov" } }, } }) # Type must be strings with pytest.raises(ValueError): itemIndex.unpackData({ cHandle: { "headings": { "T000001": {} }, "references": { "T000001": { "John": [] } }, } }) # Types must be valid with pytest.raises(ValueError): itemIndex.unpackData({ cHandle: { "headings": { "T000001": {} }, "references": { "T000001": { "John": "@pov,@char,@stuff" } }, } }) # This should pass itemIndex.unpackData({ cHandle: { "headings": { "T000001": {} }, "references": { "T000001": { "John": "@pov,@char" } }, } })
def testDlgMerge_Main(qtbot, monkeypatch, nwGUI, fncProj, mockRnd): """Test the merge documents tool. """ # Block message box monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Ok) monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True)) # Create a new project buildTestProject(nwGUI, fncProj) # Handles for new objects hNovelRoot = "0000000000008" hChapterDir = "000000000000d" hChapterOne = "000000000000e" hSceneOne = "000000000000f" hSceneTwo = "0000000000010" hSceneThree = "0000000000011" hSceneFour = "0000000000012" hMergedDoc = "0000000000023" # Add Project Content nwGUI.switchFocus(nwWidget.TREE) nwGUI.projView.projTree.clearSelection() nwGUI.projView.projTree._getTreeItem(hChapterDir).setSelected(True) nwGUI.projView.projTree.newTreeItem(nwItemType.FILE) nwGUI.projView.projTree.newTreeItem(nwItemType.FILE) nwGUI.projView.projTree.newTreeItem(nwItemType.FILE) assert nwGUI.saveProject() is True assert nwGUI.closeProject() is True tChapterOne = "## Chapter One\n\n% Chapter one comment\n" tSceneOne = "### Scene One\n\nThere once was a man from Nantucket" tSceneTwo = "### Scene Two\n\nWho kept all his cash in a bucket." tSceneThree = "### Scene Three\n\n\tBut his daughter, named Nan, \n\tRan away with a man" tSceneFour = "### Scene Four\n\nAnd as for the bucket, Nantucket." contentDir = os.path.join(fncProj, "content") writeFile(os.path.join(contentDir, hChapterOne+".nwd"), tChapterOne) writeFile(os.path.join(contentDir, hSceneOne+".nwd"), tSceneOne) writeFile(os.path.join(contentDir, hSceneTwo+".nwd"), tSceneTwo) writeFile(os.path.join(contentDir, hSceneThree+".nwd"), tSceneThree) writeFile(os.path.join(contentDir, hSceneFour+".nwd"), tSceneFour) assert nwGUI.openProject(fncProj) is True # Open the Merge tool nwGUI.switchFocus(nwWidget.TREE) nwGUI.projView.projTree.clearSelection() nwGUI.projView.projTree._getTreeItem(hChapterDir).setSelected(True) monkeypatch.setattr(GuiDocMerge, "exec_", lambda *a: None) nwGUI.mainMenu.aMergeDocs.activate(QAction.Trigger) qtbot.waitUntil(lambda: getGuiItem("GuiDocMerge") is not None, timeout=1000) nwMerge = getGuiItem("GuiDocMerge") assert isinstance(nwMerge, GuiDocMerge) nwMerge.show() qtbot.wait(50) # Populate List # ============= nwMerge.listBox.clear() assert nwMerge.listBox.count() == 0 # No item selected nwGUI.projView.projTree.clearSelection() assert nwMerge._populateList() is False assert nwMerge.listBox.count() == 0 # Non-existing item with monkeypatch.context() as mp: mp.setattr(NWTree, "__getitem__", lambda *a: None) nwGUI.projView.projTree.clearSelection() nwGUI.projView.projTree._getTreeItem(hChapterDir).setSelected(True) assert nwMerge._populateList() is False assert nwMerge.listBox.count() == 0 # Select a non-folder nwGUI.projView.projTree.clearSelection() nwGUI.projView.projTree._getTreeItem(hChapterOne).setSelected(True) assert nwMerge._populateList() is False assert nwMerge.listBox.count() == 0 # Select the chapter folder nwGUI.projView.projTree.clearSelection() nwGUI.projView.projTree._getTreeItem(hChapterDir).setSelected(True) assert nwMerge._populateList() is True assert nwMerge.listBox.count() == 5 # Merge Documents # =============== # First, a successful merge with monkeypatch.context() as mp: mp.setattr(GuiDocMerge, "_doClose", lambda *a: None) assert nwMerge._doMerge() is True assert nwGUI.saveProject() is True mergedFile = os.path.join(contentDir, hMergedDoc+".nwd") assert os.path.isfile(mergedFile) assert readFile(mergedFile) == ( "%%%%~name: New Chapter\n" "%%%%~path: %s/%s\n" "%%%%~kind: NOVEL/DOCUMENT\n" "%s\n\n" "%s\n\n" "%s\n\n" "%s\n\n" "%s\n\n" ) % ( hNovelRoot, hMergedDoc, tChapterOne.strip(), tSceneOne.strip(), tSceneTwo.strip(), tSceneThree.strip(), tSceneFour.strip(), ) # OS error with monkeypatch.context() as mp: mp.setattr("builtins.open", causeOSError) assert nwMerge._doMerge() is False # Can't find the source item with monkeypatch.context() as mp: mp.setattr(NWTree, "__getitem__", lambda *a: None) assert nwMerge._doMerge() is False # No source handle set nwMerge.sourceItem = None assert nwMerge._doMerge() is False # No documents to merge nwMerge.listBox.clear() assert nwMerge._doMerge() is False # Close up nwMerge._doClose()
def testCoreIndex_ScanText(mockGUI, fncDir, mockRnd): """Check the index text scanner. """ theProject = NWProject(mockGUI) buildTestProject(theProject, fncDir) theIndex = theProject.index # Some items for fail to scan tests dHandle = theProject.newFolder("Folder", "0000000000010") xHandle = theProject.newFile("No Layout", "0000000000010") xItem = theProject.tree[xHandle] xItem.setLayout(nwItemLayout.NO_LAYOUT) # Check invalid data assert theIndex.scanText(None, "Hello World!") is False assert theIndex.scanText(dHandle, "Hello World!") is False assert theIndex.scanText(xHandle, "Hello World!") is False xItem.setLayout(nwItemLayout.DOCUMENT) xItem.setParent(None) assert theIndex.scanText(xHandle, "Hello World!") is False # Create the trash folder tHandle = theProject.trashFolder() assert theProject.tree[tHandle] is not None xItem.setParent(tHandle) theProject.tree.updateItemData(xItem.itemHandle) assert xItem.itemRoot == tHandle assert xItem.itemClass == nwItemClass.TRASH assert theIndex.scanText(xHandle, "Hello World!") is False # Create the archive root aHandle = theProject.newRoot(nwItemClass.ARCHIVE) assert theProject.tree[aHandle] is not None xItem.setParent(aHandle) theProject.tree.updateItemData(xItem.itemHandle) assert theIndex.scanText(xHandle, "Hello World!") is False # Make some usable items tHandle = theProject.newFile("Title", "0000000000010") pHandle = theProject.newFile("Page", "0000000000010") nHandle = theProject.newFile("Hello", "0000000000010") cHandle = theProject.newFile("Jane", "0000000000012") sHandle = theProject.newFile("Scene", "0000000000010") # Text Indexing # ============= # Index correct text 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 theIndex._tagsIndex.tagHandle("Jane") == cHandle assert theIndex._tagsIndex.tagHeading("Jane") == "T000001" assert theIndex._tagsIndex.tagClass("Jane") == "CHARACTER" assert theIndex.getNovelData(nHandle, "T000001").title == "Hello World!" # Title Indexing # ============== # Document File assert theIndex.scanText( nHandle, ( "# Title One\n\n" "% synopsis: Synopsis One.\n\n" "Paragraph One.\n\n" "## Title Two\n\n" "% synopsis: Synopsis Two.\n\n" "Paragraph Two.\n\n" "### Title Three\n\n" "% synopsis: Synopsis Three.\n\n" "Paragraph Three.\n\n" "#### Title Four\n\n" "% synopsis: Synopsis Four.\n\n" "Paragraph Four.\n\n" "##### Title Five\n\n" # Not interpreted as a title, the hashes are counted as a word "Paragraph Five.\n\n")) assert theIndex._itemIndex[nHandle]["T000001"].references == {} assert theIndex._itemIndex[nHandle]["T000007"].references == {} assert theIndex._itemIndex[nHandle]["T000013"].references == {} assert theIndex._itemIndex[nHandle]["T000019"].references == {} assert theIndex._itemIndex[nHandle]["T000001"].level == "H1" assert theIndex._itemIndex[nHandle]["T000007"].level == "H2" assert theIndex._itemIndex[nHandle]["T000013"].level == "H3" assert theIndex._itemIndex[nHandle]["T000019"].level == "H4" assert theIndex._itemIndex[nHandle]["T000001"].title == "Title One" assert theIndex._itemIndex[nHandle]["T000007"].title == "Title Two" assert theIndex._itemIndex[nHandle]["T000013"].title == "Title Three" assert theIndex._itemIndex[nHandle]["T000019"].title == "Title Four" assert theIndex._itemIndex[nHandle]["T000001"].charCount == 23 assert theIndex._itemIndex[nHandle]["T000007"].charCount == 23 assert theIndex._itemIndex[nHandle]["T000013"].charCount == 27 assert theIndex._itemIndex[nHandle]["T000019"].charCount == 56 assert theIndex._itemIndex[nHandle]["T000001"].wordCount == 4 assert theIndex._itemIndex[nHandle]["T000007"].wordCount == 4 assert theIndex._itemIndex[nHandle]["T000013"].wordCount == 4 assert theIndex._itemIndex[nHandle]["T000019"].wordCount == 9 assert theIndex._itemIndex[nHandle]["T000001"].paraCount == 1 assert theIndex._itemIndex[nHandle]["T000007"].paraCount == 1 assert theIndex._itemIndex[nHandle]["T000013"].paraCount == 1 assert theIndex._itemIndex[nHandle]["T000019"].paraCount == 3 assert theIndex._itemIndex[nHandle]["T000001"].synopsis == "Synopsis One." assert theIndex._itemIndex[nHandle]["T000007"].synopsis == "Synopsis Two." assert theIndex._itemIndex[nHandle][ "T000013"].synopsis == "Synopsis Three." assert theIndex._itemIndex[nHandle]["T000019"].synopsis == "Synopsis Four." # Note File assert theIndex.scanText(cHandle, ("# Title One\n\n" "@tag: One\n\n" "% synopsis: Synopsis One.\n\n" "Paragraph One.\n\n")) assert theIndex._itemIndex[cHandle]["T000001"].references == {} assert theIndex._itemIndex[cHandle]["T000001"].level == "H1" assert theIndex._itemIndex[cHandle]["T000001"].title == "Title One" assert theIndex._itemIndex[cHandle]["T000001"].charCount == 23 assert theIndex._itemIndex[cHandle]["T000001"].wordCount == 4 assert theIndex._itemIndex[cHandle]["T000001"].paraCount == 1 assert theIndex._itemIndex[cHandle]["T000001"].synopsis == "Synopsis One." # Valid and Invalid References assert theIndex.scanText( sHandle, ( "# Title One\n\n" "@pov: One\n\n" # Valid "@char: Two\n\n" # Invalid tag "@:\n\n" # Invalid line "% synopsis: Synopsis One.\n\n" "Paragraph One.\n\n")) assert theIndex._itemIndex[sHandle]["T000001"].references == { "One": {"@pov"}, "Two": {"@char"} } # Special Titles # ============== assert theIndex.scanText(tHandle, ("#! My Project\n\n" ">> By Jane Doe <<\n\n")) assert theIndex._itemIndex[cHandle]["T000001"].references == {} assert theIndex._itemIndex[tHandle]["T000001"].level == "H1" assert theIndex._itemIndex[tHandle]["T000001"].title == "My Project" assert theIndex._itemIndex[tHandle]["T000001"].charCount == 21 assert theIndex._itemIndex[tHandle]["T000001"].wordCount == 5 assert theIndex._itemIndex[tHandle]["T000001"].paraCount == 1 assert theIndex._itemIndex[tHandle]["T000001"].synopsis == "" assert theIndex.scanText(tHandle, ("##! Prologue\n\n" "In the beginning there was time ...\n\n")) assert theIndex._itemIndex[cHandle]["T000001"].references == {} assert theIndex._itemIndex[tHandle]["T000001"].level == "H2" assert theIndex._itemIndex[tHandle]["T000001"].title == "Prologue" assert theIndex._itemIndex[tHandle]["T000001"].charCount == 43 assert theIndex._itemIndex[tHandle]["T000001"].wordCount == 8 assert theIndex._itemIndex[tHandle]["T000001"].paraCount == 1 assert theIndex._itemIndex[tHandle]["T000001"].synopsis == "" # Page wo/Title # ============= theProject.tree[pHandle]._layout = nwItemLayout.DOCUMENT assert theIndex.scanText(pHandle, ("This is a page with some text on it.\n\n")) assert theIndex._itemIndex[pHandle]["T000000"].references == {} assert theIndex._itemIndex[pHandle]["T000000"].level == "H0" assert theIndex._itemIndex[pHandle]["T000000"].title == "" assert theIndex._itemIndex[pHandle]["T000000"].charCount == 36 assert theIndex._itemIndex[pHandle]["T000000"].wordCount == 9 assert theIndex._itemIndex[pHandle]["T000000"].paraCount == 1 assert theIndex._itemIndex[pHandle]["T000000"].synopsis == "" theProject.tree[pHandle]._layout = nwItemLayout.NOTE assert theIndex.scanText(pHandle, ("This is a page with some text on it.\n\n")) assert theIndex._itemIndex[pHandle]["T000000"].references == {} assert theIndex._itemIndex[pHandle]["T000000"].level == "H0" assert theIndex._itemIndex[pHandle]["T000000"].title == "" assert theIndex._itemIndex[pHandle]["T000000"].charCount == 36 assert theIndex._itemIndex[pHandle]["T000000"].wordCount == 9 assert theIndex._itemIndex[pHandle]["T000000"].paraCount == 1 assert theIndex._itemIndex[pHandle]["T000000"].synopsis == "" assert theProject.closeProject() is True
def testCoreIndex_ExtractData(mockGUI, fncDir, mockRnd): """Check the index data extraction functions. """ theProject = NWProject(mockGUI) buildTestProject(theProject, fncDir) theIndex = theProject.index theIndex.reIndexHandle("0000000000010") theIndex.reIndexHandle("0000000000011") theIndex.reIndexHandle("0000000000012") theIndex.reIndexHandle("0000000000013") theIndex.reIndexHandle("0000000000014") theIndex.reIndexHandle("0000000000015") theIndex.reIndexHandle("0000000000016") theIndex.reIndexHandle("0000000000017") nHandle = theProject.newFile("Hello", "0000000000010") cHandle = theProject.newFile("Jane", "0000000000012") assert theIndex.getNovelData("", "") is None assert theIndex.getNovelData("0000000000010", "") is None 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")) # The novel structure should contain the pointer to the novel file header theKeys = [] for aKey, _, _, _ in theIndex.novelStructure(): theKeys.append(aKey) assert theKeys == [ "0000000000014:T000001", "0000000000016:T000001", "0000000000017:T000001", "%s:T000001" % nHandle, ] # Check that excluded files can be skipped theProject.tree[nHandle].setExported(False) theKeys = [] for aKey, _, _, _ in theIndex.novelStructure(skipExcl=False): theKeys.append(aKey) assert theKeys == [ "0000000000014:T000001", "0000000000016:T000001", "0000000000017:T000001", "%s:T000001" % nHandle, ] theKeys = [] for aKey, _, _, _ in theIndex.novelStructure(skipExcl=True): theKeys.append(aKey) assert theKeys == [ "0000000000014:T000001", "0000000000016:T000001", "0000000000017:T000001", ] # 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 # getReferences # ============= # 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 theRefs["@pov"] == ["Jane"] assert theRefs["@char"] == ["Jane"] # getBackReferenceList # ==================== # None handle should return an empty dict assert theIndex.getBackReferenceList(None) == {} # The Title Page file should have no references as it has no tag assert theIndex.getBackReferenceList("0000000000014") == {} # The character file should have a record of the reference from the novel file theRefs = theIndex.getBackReferenceList(cHandle) assert theRefs == {nHandle: "T000001"} # getTagSource # ============ assert theIndex.getTagSource("Jane") == (cHandle, "T000001") assert theIndex.getTagSource("John") == (None, "T000000") # getCounts # ========= # For whole text and sections # Invalid handle or title should return 0s assert theIndex.getCounts("stuff") == (0, 0, 0) assert theIndex.getCounts(nHandle, "stuff") == (0, 0, 0) # 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. She's still awesome though.\n")) # Whole document cC, wC, pC = theIndex.getCounts(nHandle) assert cC == 152 assert wC == 28 assert pC == 4 # First part cC, wC, pC = theIndex.getCounts(nHandle, "T000001") assert cC == 62 assert wC == 12 assert pC == 2 # Second part cC, wC, pC = theIndex.getCounts(nHandle, "T000011") assert cC == 90 assert wC == 16 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. She's still awesome though.\n")) # Whole document cC, wC, pC = theIndex.getCounts(cHandle) assert cC == 152 assert wC == 28 assert pC == 4 # First part cC, wC, pC = theIndex.getCounts(cHandle, "T000001") assert cC == 62 assert wC == 12 assert pC == 2 # Second part cC, wC, pC = theIndex.getCounts(cHandle, "T000011") assert cC == 90 assert wC == 16 assert pC == 2 # Novel Stats # =========== hHandle = theProject.newFile("Chapter", "0000000000010") sHandle = theProject.newFile("Scene One", "0000000000010") tHandle = theProject.newFile("Scene Two", "0000000000010") theProject.tree[hHandle].itemLayout == nwItemLayout.DOCUMENT theProject.tree[sHandle].itemLayout == nwItemLayout.DOCUMENT theProject.tree[tHandle].itemLayout == nwItemLayout.DOCUMENT assert theIndex.scanText(hHandle, "## Chapter One\n\n") assert theIndex.scanText(sHandle, "### Scene One\n\n") assert theIndex.scanText(tHandle, "### Scene Two\n\n") assert [ (h, t) for h, t, _ in theIndex._itemIndex.iterNovelStructure(skipExcl=False) ] == [ ("0000000000014", "T000001"), ("0000000000016", "T000001"), ("0000000000017", "T000001"), (nHandle, "T000001"), (nHandle, "T000011"), (hHandle, "T000001"), (sHandle, "T000001"), (tHandle, "T000001"), ] assert [ (h, t) for h, t, _ in theIndex._itemIndex.iterNovelStructure(skipExcl=True) ] == [ ("0000000000014", "T000001"), ("0000000000016", "T000001"), ("0000000000017", "T000001"), (hHandle, "T000001"), (sHandle, "T000001"), (tHandle, "T000001"), ] # Add a fake handle to the tree and check that it's ignored theProject.tree._treeOrder.append("0000000000000") assert [ (h, t) for h, t, _ in theIndex._itemIndex.iterNovelStructure(skipExcl=False) ] == [ ("0000000000014", "T000001"), ("0000000000016", "T000001"), ("0000000000017", "T000001"), (nHandle, "T000001"), (nHandle, "T000011"), (hHandle, "T000001"), (sHandle, "T000001"), (tHandle, "T000001"), ] theProject.tree._treeOrder.remove("0000000000000") # Extract stats assert theIndex.getNovelWordCount(skipExcl=False) == 43 assert theIndex.getNovelWordCount(skipExcl=True) == 15 assert theIndex.getNovelTitleCounts(skipExcl=False) == [0, 3, 2, 3, 0] assert theIndex.getNovelTitleCounts(skipExcl=True) == [0, 1, 2, 3, 0] # Table of Contents assert theIndex.getTableOfContents(0, skipExcl=True) == [] assert theIndex.getTableOfContents(1, skipExcl=True) == [ ("0000000000014:T000001", 1, "New Novel", 15), ] assert theIndex.getTableOfContents(2, skipExcl=True) == [ ("0000000000014:T000001", 1, "New Novel", 5), ("0000000000016:T000001", 2, "New Chapter", 4), ("%s:T000001" % hHandle, 2, "Chapter One", 6), ] assert theIndex.getTableOfContents(3, skipExcl=True) == [ ("0000000000014:T000001", 1, "New Novel", 5), ("0000000000016:T000001", 2, "New Chapter", 2), ("0000000000017:T000001", 3, "New Scene", 2), ("%s:T000001" % hHandle, 2, "Chapter One", 2), ("%s:T000001" % sHandle, 3, "Scene One", 2), ("%s:T000001" % tHandle, 3, "Scene Two", 2), ] assert theIndex.getTableOfContents(0, skipExcl=False) == [] assert theIndex.getTableOfContents(1, skipExcl=False) == [ ("0000000000014:T000001", 1, "New Novel", 9), ("%s:T000001" % nHandle, 1, "Hello World!", 12), ("%s:T000011" % nHandle, 1, "Hello World!", 22), ] # Header Word Counts bHandle = "0000000000000" assert theIndex.getHandleWordCounts(bHandle) == [] assert theIndex.getHandleWordCounts(hHandle) == [("%s:T000001" % hHandle, 2)] assert theIndex.getHandleWordCounts(sHandle) == [("%s:T000001" % sHandle, 2)] assert theIndex.getHandleWordCounts(tHandle) == [("%s:T000001" % tHandle, 2)] assert theIndex.getHandleWordCounts(nHandle) == [ ("%s:T000001" % nHandle, 12), ("%s:T000011" % nHandle, 16) ] assert theIndex.saveIndex() is True assert theProject.saveProject() is True assert theProject.closeProject() is True # Header Record bHandle = "0000000000000" assert theIndex.getHandleHeaders(bHandle) == [] assert theIndex.getHandleHeaders(hHandle) == [("T000001", "H2", "Chapter One")] assert theIndex.getHandleHeaders(sHandle) == [("T000001", "H3", "Scene One")] assert theIndex.getHandleHeaders(tHandle) == [("T000001", "H3", "Scene Two")] assert theIndex.getHandleHeaders(nHandle) == [ ("T000001", "H1", "Hello World!"), ("T000011", "H1", "Hello World!") ]
def testCoreIndex_CheckThese(mockGUI, fncDir, mockRnd): """Test the tag checker function checkThese. """ theProject = NWProject(mockGUI) buildTestProject(theProject, fncDir) theIndex = theProject.index nHandle = theProject.newFile("Hello", "0000000000010") cHandle = theProject.newFile("Jane", "0000000000012") nItem = theProject.tree[nHandle] cItem = theProject.tree[cHandle] assert theIndex.rootChangedSince("0000000000010", 0) is False assert theIndex.indexChangedSince(0) is False assert theIndex.scanText(cHandle, ("# Jane Smith\n" "@tag: Jane\n" "@tag:\n" "@:\n")) assert theIndex.scanText( nHandle, ( "# Hello World!\n" "@pov: Jane\n" "@invalid: John\n" # Checks for issue #688 )) assert theIndex._tagsIndex.tagHandle("Jane") == cHandle assert theIndex._tagsIndex.tagHeading("Jane") == "T000001" assert theIndex._tagsIndex.tagClass("Jane") == "CHARACTER" assert theIndex.getNovelData(nHandle, "T000001").title == "Hello World!" assert theIndex.getReferences(nHandle, "T000001") == { "@char": [], "@custom": [], "@entity": [], "@focus": [], "@location": [], "@object": [], "@plot": [], "@pov": ["Jane"], "@time": [] } assert theIndex.rootChangedSince("0000000000010", 0) is True assert theIndex.indexChangedSince(0) is True assert theIndex.getHandleHeaderLevel(cHandle) == "H1" assert theIndex.getHandleHeaderLevel(nHandle) == "H1" # Zero Items assert theIndex.checkThese([], cItem) == [] # One Item assert theIndex.checkThese(["@tag"], cItem) == [True] assert theIndex.checkThese(["@who"], cItem) == [False] # Two Items assert theIndex.checkThese(["@tag", "Jane"], cItem) == [True, True] assert theIndex.checkThese(["@tag", "John"], cItem) == [True, True] assert theIndex.checkThese(["@tag", "Jane"], nItem) == [True, False] assert theIndex.checkThese(["@tag", "John"], nItem) == [True, True] assert theIndex.checkThese(["@pov", "John"], nItem) == [True, False] assert theIndex.checkThese(["@pov", "Jane"], nItem) == [True, True] assert theIndex.checkThese(["@ pov", "Jane"], nItem) == [False, False] assert theIndex.checkThese(["@what", "Jane"], nItem) == [False, False] # Three Items assert theIndex.checkThese(["@tag", "Jane", "John"], cItem) == [True, True, False] assert theIndex.checkThese(["@who", "Jane", "John"], cItem) == [False, False, False] assert theIndex.checkThese(["@pov", "Jane", "John"], nItem) == [True, True, False] assert theProject.closeProject() is True
def testGuiStatusBar_Main(qtbot, monkeypatch, nwGUI, fncProj, mockRnd): """Test the the various features of the status bar. """ monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes) buildTestProject(nwGUI, fncProj) cHandle = nwGUI.theProject.newFile("A Note", "000000000000a") newDoc = NWDoc(nwGUI.theProject, cHandle) newDoc.writeDocument("# A Note\n\n") nwGUI.projView.revealNewTreeItem(cHandle) nwGUI.rebuildIndex(beQuiet=True) # Reference Time refTime = time.time() nwGUI.statusBar.setRefTime(refTime) assert nwGUI.statusBar.refTime == refTime # Project Status nwGUI.statusBar.setProjectStatus(nwState.NONE) assert nwGUI.statusBar.projIcon._theCol == nwGUI.statusBar.projIcon._colNone nwGUI.statusBar.setProjectStatus(nwState.BAD) assert nwGUI.statusBar.projIcon._theCol == nwGUI.statusBar.projIcon._colBad nwGUI.statusBar.setProjectStatus(nwState.GOOD) assert nwGUI.statusBar.projIcon._theCol == nwGUI.statusBar.projIcon._colGood # Document Status nwGUI.statusBar.setDocumentStatus(nwState.NONE) assert nwGUI.statusBar.docIcon._theCol == nwGUI.statusBar.docIcon._colNone nwGUI.statusBar.setDocumentStatus(nwState.BAD) assert nwGUI.statusBar.docIcon._theCol == nwGUI.statusBar.docIcon._colBad nwGUI.statusBar.setDocumentStatus(nwState.GOOD) assert nwGUI.statusBar.docIcon._theCol == nwGUI.statusBar.docIcon._colGood # Idle Status nwGUI.statusBar.mainConf.stopWhenIdle = False nwGUI.statusBar.setUserIdle(True) nwGUI.statusBar.updateTime() assert nwGUI.statusBar.userIdle is False assert nwGUI.statusBar.timeText.text() == "00:00:00" nwGUI.statusBar.mainConf.stopWhenIdle = True nwGUI.statusBar.setUserIdle(True) nwGUI.statusBar.updateTime(5) assert nwGUI.statusBar.userIdle is True assert nwGUI.statusBar.timeText.text() != "00:00:00" nwGUI.statusBar.setUserIdle(False) nwGUI.statusBar.updateTime(5) assert nwGUI.statusBar.userIdle is False assert nwGUI.statusBar.timeText.text() != "00:00:00" # Language nwGUI.statusBar.setLanguage("None", "None") assert nwGUI.statusBar.langText.text() == "None" nwGUI.statusBar.setLanguage("en", "None") assert nwGUI.statusBar.langText.text() == "American English" # Project Stats nwGUI.statusBar.mainConf.incNotesWCount = False nwGUI._updateStatusWordCount() assert nwGUI.statusBar.statsText.text() == "Words: 9 (+9)" nwGUI.statusBar.mainConf.incNotesWCount = True nwGUI._updateStatusWordCount() assert nwGUI.statusBar.statsText.text() == "Words: 11 (+11)"
def testDlgProjSettings_Dialog(qtbot, monkeypatch, nwGUI, fncDir, fncProj, outDir, refDir, mockRnd): """Test the full project settings dialog. """ projFile = os.path.join(fncProj, "nwProject.nwx") testFile = os.path.join(outDir, "guiProjSettings_Dialog_nwProject.nwx") compFile = os.path.join(refDir, "guiProjSettings_Dialog_nwProject.nwx") # Block message box monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes) # Check that we cannot open when there is no project nwGUI.mainMenu.aProjectSettings.activate(QAction.Trigger) assert getGuiItem("GuiProjectSettings") is None # Create new project buildTestProject(nwGUI, fncProj) nwGUI.mainConf.backupPath = fncDir nwGUI.theProject.setSpellLang("en") nwGUI.theProject.setBookAuthors("Jane Smith\nJohn Smith") nwGUI.theProject.setAutoReplace({"A": "B", "C": "D"}) # Get the dialog object monkeypatch.setattr(GuiProjectSettings, "exec_", lambda *a: None) monkeypatch.setattr(GuiProjectSettings, "result", lambda *a: QDialog.Accepted) monkeypatch.setattr(nwGUI.docEditor.spEnchant, "listDictionaries", lambda: [("en", "none")]) nwGUI.mainMenu.aProjectSettings.activate(QAction.Trigger) qtbot.waitUntil(lambda: getGuiItem("GuiProjectSettings") is not None, timeout=1000) projEdit = getGuiItem("GuiProjectSettings") assert isinstance(projEdit, GuiProjectSettings) projEdit.show() qtbot.addWidget(projEdit) # Settings Tab # ============ assert projEdit.tabMain.editName.text() == "New Project" assert projEdit.tabMain.editTitle.text() == "New Novel" assert projEdit.tabMain.editAuthors.toPlainText( ) == "Jane Smith\nJohn Smith" assert projEdit.tabMain.spellLang.currentData() == "en" assert projEdit.tabMain.doBackup.isChecked() is False qtbot.wait(stepDelay) projEdit.tabMain.editName.setText("") for c in "Project Name": qtbot.keyClick(projEdit.tabMain.editName, c, delay=typeDelay) projEdit.tabMain.editTitle.setText("") for c in "Project Title": qtbot.keyClick(projEdit.tabMain.editTitle, c, delay=typeDelay) projEdit.tabMain.editAuthors.clear() for c in "Jane Doe": 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=typeDelay) qtbot.keyClick(projEdit.tabMain.editAuthors, Qt.Key_Return, delay=keyDelay) qtbot.wait(stepDelay) assert projEdit.tabMain.editName.text() == "Project Name" assert projEdit.tabMain.editTitle.text() == "Project Title" assert projEdit.tabMain.editAuthors.toPlainText() == "Jane Doe\nJohn Doh\n" # Status Tab # ========== projEdit._tabBox.setCurrentWidget(projEdit.tabStatus) assert projEdit.tabStatus.colChanged is False assert projEdit.tabStatus.getNewList() == ([], []) assert projEdit.tabStatus.listBox.topLevelItemCount() == 4 # Can't delete the first item (it's in use) projEdit.tabStatus.listBox.clearSelection() projEdit.tabStatus.listBox.topLevelItem(0).setSelected(True) qtbot.mouseClick(projEdit.tabStatus.delButton, Qt.LeftButton) assert projEdit.tabStatus.listBox.topLevelItemCount() == 4 # Can delete the third item projEdit.tabStatus.listBox.clearSelection() projEdit.tabStatus.listBox.topLevelItem(2).setSelected(True) qtbot.mouseClick(projEdit.tabStatus.delButton, Qt.LeftButton) assert projEdit.tabStatus.listBox.topLevelItemCount() == 3 # Add a new item monkeypatch.setattr(QColorDialog, "getColor", lambda *a: QColor(20, 30, 40)) qtbot.mouseClick(projEdit.tabStatus.addButton, Qt.LeftButton) projEdit.tabStatus.listBox.topLevelItem(3).setSelected(True) for n in range(8): qtbot.keyClick(projEdit.tabStatus.editName, Qt.Key_Backspace, delay=typeDelay) for c in "Final": qtbot.keyClick(projEdit.tabStatus.editName, c, delay=typeDelay) qtbot.mouseClick(projEdit.tabStatus.colButton, Qt.LeftButton) qtbot.mouseClick(projEdit.tabStatus.saveButton, Qt.LeftButton) assert projEdit.tabStatus.listBox.topLevelItemCount() == 4 qtbot.wait(stepDelay) assert projEdit.tabStatus.colChanged is True assert projEdit.tabStatus.getNewList() == ( [{ "key": statusKeys[0], "name": "New", "cols": (100, 100, 100) }, { "key": statusKeys[1], "name": "Note", "cols": (200, 50, 0) }, { "key": statusKeys[3], "name": "Finished", "cols": (50, 200, 0) }, { "key": None, "name": "Final", "cols": (20, 30, 40) }], [ statusKeys[2] # Deleted item ]) # Move items projEdit.tabStatus.listBox.clearSelection() projEdit.tabStatus._moveItem(1) assert [x["key"] for x in projEdit.tabStatus.getNewList()[0] ] == [statusKeys[0], statusKeys[1], statusKeys[3], None] projEdit.tabStatus.listBox.clearSelection() projEdit.tabStatus.listBox.topLevelItem(0).setSelected(True) projEdit.tabStatus._moveItem(-1) assert [x["key"] for x in projEdit.tabStatus.getNewList()[0] ] == [statusKeys[0], statusKeys[1], statusKeys[3], None] projEdit.tabStatus.listBox.clearSelection() projEdit.tabStatus.listBox.topLevelItem(3).setSelected(True) projEdit.tabStatus._moveItem(-1) assert [x["key"] for x in projEdit.tabStatus.getNewList()[0] ] == [statusKeys[0], statusKeys[1], None, statusKeys[3]] projEdit.tabStatus._moveItem(1) assert [x["key"] for x in projEdit.tabStatus.getNewList()[0] ] == [statusKeys[0], statusKeys[1], statusKeys[3], None] # Importance Tab # ============== projEdit._tabBox.setCurrentWidget(projEdit.tabImport) projEdit.tabStatus.listBox.clearSelection() projEdit.tabImport.listBox.topLevelItem(3).setSelected(True) qtbot.mouseClick(projEdit.tabImport.delButton, Qt.LeftButton) qtbot.mouseClick(projEdit.tabImport.addButton, Qt.LeftButton) projEdit.tabStatus.listBox.clearSelection() projEdit.tabImport.listBox.topLevelItem(3).setSelected(True) for n in range(8): qtbot.keyClick(projEdit.tabImport.editName, Qt.Key_Backspace, delay=typeDelay) for c in "Final": qtbot.keyClick(projEdit.tabImport.editName, c, delay=typeDelay) qtbot.mouseClick(projEdit.tabImport.saveButton, Qt.LeftButton) qtbot.wait(stepDelay) # Auto-Replace Tab # ================ qtbot.wait(stepDelay) projEdit._tabBox.setCurrentWidget(projEdit.tabReplace) assert projEdit.tabReplace.listBox.topLevelItem(0).text(0) == "<A>" assert projEdit.tabReplace.listBox.topLevelItem(0).text(1) == "B" assert projEdit.tabReplace.listBox.topLevelItem(1).text(0) == "<C>" assert projEdit.tabReplace.listBox.topLevelItem(1).text(1) == "D" qtbot.mouseClick(projEdit.tabReplace.addButton, Qt.LeftButton) projEdit.tabReplace.listBox.topLevelItem(2).setSelected(True) projEdit.tabReplace.editKey.setText("") for c in "Th is ": qtbot.keyClick(projEdit.tabReplace.editKey, c, delay=typeDelay) projEdit.tabReplace.editValue.setText("") for c in "With This Stuff ": qtbot.keyClick(projEdit.tabReplace.editValue, c, delay=typeDelay) qtbot.mouseClick(projEdit.tabReplace.saveButton, Qt.LeftButton) qtbot.wait(stepDelay) projEdit.tabReplace.listBox.clearSelection() assert not projEdit.tabReplace._saveEntry() assert not projEdit.tabReplace._delEntry() qtbot.mouseClick(projEdit.tabReplace.addButton, Qt.LeftButton) newIdx = -1 for i in range(projEdit.tabReplace.listBox.topLevelItemCount()): if projEdit.tabReplace.listBox.topLevelItem(i).text(0) == "<keyword4>": newIdx = i break assert newIdx >= 0 newItem = projEdit.tabReplace.listBox.topLevelItem(newIdx) projEdit.tabReplace.listBox.setCurrentItem(newItem) qtbot.mouseClick(projEdit.tabReplace.delButton, Qt.LeftButton) qtbot.wait(stepDelay) # Save & Check # ============ projEdit._doSave() # Open again, and check project settings nwGUI.mainMenu.aProjectSettings.activate(QAction.Trigger) qtbot.waitUntil(lambda: getGuiItem("GuiProjectSettings") is not None, timeout=1000) projEdit = getGuiItem("GuiProjectSettings") assert isinstance(projEdit, GuiProjectSettings) qtbot.addWidget(projEdit) assert projEdit.tabMain.editName.text() == "Project Name" assert projEdit.tabMain.editTitle.text() == "Project Title" theAuth = projEdit.tabMain.editAuthors.toPlainText().strip().splitlines() assert len(theAuth) == 2 assert theAuth[0] == "Jane Doe" assert theAuth[1] == "John Doh" projEdit._doClose() qtbot.wait(stepDelay) assert nwGUI.saveProject() qtbot.wait(stepDelay) # Check the files copyfile(projFile, testFile) assert cmpFiles(testFile, compFile, [2, 8, 9, 10])
def testGuiProjTree_MoveItems(qtbot, monkeypatch, nwGUI, fncDir, mockRnd): """Test adding and removing items from the project tree. """ # Block message box monkeypatch.setattr(QMessageBox, "warning", lambda *a: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes) monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True)) nwTree = nwGUI.projView # Try to move item with no project assert nwTree.projTree.moveTreeItem(1) is False # Create a project prjDir = os.path.join(fncDir, "project") buildTestProject(nwGUI, prjDir) # Move Documents # ============== # Add some files nwTree.setSelectedHandle("000000000000d") assert nwTree.projTree.newTreeItem(nwItemType.FILE) is True assert nwTree.projTree.newTreeItem(nwItemType.FILE) is True assert nwTree.projTree.newTreeItem(nwItemType.FILE) is True assert nwTree.getTreeFromHandle("000000000000d") == [ "000000000000d", "000000000000e", "000000000000f", "0000000000010", "0000000000011", "0000000000012", ] # Move with no selections nwTree.projTree.clearSelection() assert nwTree.projTree.moveTreeItem(1) is False # Move second item up twice (should give same result) nwTree.setSelectedHandle("000000000000f") assert nwTree.projTree.moveTreeItem(-1) is True assert nwTree.getTreeFromHandle("000000000000d") == [ "000000000000d", "000000000000f", "000000000000e", "0000000000010", "0000000000011", "0000000000012", ] assert nwTree.projTree.moveTreeItem(-1) is False assert nwTree.getTreeFromHandle("000000000000d") == [ "000000000000d", "000000000000f", "000000000000e", "0000000000010", "0000000000011", "0000000000012", ] # Restore assert nwTree.projTree.moveTreeItem(1) is True assert nwTree.getTreeFromHandle("000000000000d") == [ "000000000000d", "000000000000e", "000000000000f", "0000000000010", "0000000000011", "0000000000012", ] # Move fifth item down twice (should give same result) nwTree.setSelectedHandle("0000000000011") assert nwTree.projTree.moveTreeItem(1) is True assert nwTree.getTreeFromHandle("000000000000d") == [ "000000000000d", "000000000000e", "000000000000f", "0000000000010", "0000000000012", "0000000000011", ] assert nwTree.projTree.moveTreeItem(1) is False assert nwTree.getTreeFromHandle("000000000000d") == [ "000000000000d", "000000000000e", "000000000000f", "0000000000010", "0000000000012", "0000000000011", ] # Restore assert nwTree.projTree.moveTreeItem(-1) is True assert nwTree.getTreeFromHandle("000000000000d") == [ "000000000000d", "000000000000e", "000000000000f", "0000000000010", "0000000000011", "0000000000012", ] # Move down again, and restore via undo nwTree.setSelectedHandle("0000000000011") assert nwTree.projTree.moveTreeItem(1) is True assert nwTree.getTreeFromHandle("000000000000d") == [ "000000000000d", "000000000000e", "000000000000f", "0000000000010", "0000000000012", "0000000000011", ] assert nwTree.projTree.undoLastMove() is True assert nwTree.getTreeFromHandle("000000000000d") == [ "000000000000d", "000000000000e", "000000000000f", "0000000000010", "0000000000011", "0000000000012", ] # Root Folder # =========== nwTree.setSelectedHandle("0000000000008") assert nwGUI.theProject.tree._treeOrder.index("0000000000008") == 0 # Move novel folder up assert nwTree.projTree.moveTreeItem(-1) is False assert nwGUI.theProject.tree._treeOrder.index("0000000000008") == 0 # Move novel folder down assert nwTree.projTree.moveTreeItem(1) is True assert nwGUI.theProject.tree._treeOrder.index("0000000000008") == 1 # Move novel folder up again assert nwTree.projTree.moveTreeItem(-1) is True assert nwGUI.theProject.tree._treeOrder.index("0000000000008") == 0 # Clean up # qtbot.stopForInteraction() nwGUI.closeProject()
def testGuiNovelTree_TreeItems(qtbot, monkeypatch, nwGUI, fncProj, mockRnd): """Test navigating the novel tree. """ # Block message box monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes) monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True)) buildTestProject(nwGUI, fncProj) nwGUI.switchFocus(nwWidget.TREE) nwGUI.projView.projTree.clearSelection() nwGUI.projView.projTree._getTreeItem("000000000000a").setSelected(True) nwGUI.projView.projTree.newTreeItem(nwItemType.FILE) writeFile( os.path.join(nwGUI.theProject.projContent, "0000000000010.nwd"), "# Jane Doe\n\n@tag: Jane\n\n" ) writeFile( os.path.join(nwGUI.theProject.projContent, "000000000000f.nwd"), ( "### Scene One\n\n" "@pov: Jane\n" "@focus: Jane\n\n" "% Synopsis: This is a scene." ) ) novelView = nwGUI.novelView novelTree = novelView.novelTree novelBar = novelView.novelBar # Show/Hide Scrollbars # ==================== nwGUI.mainConf.hideVScroll = True nwGUI.mainConf.hideHScroll = True novelView.initSettings() assert not novelTree.verticalScrollBar().isVisible() assert not novelTree.horizontalScrollBar().isVisible() nwGUI.mainConf.hideVScroll = False nwGUI.mainConf.hideHScroll = False novelView.initSettings() assert novelTree.verticalScrollBar().isEnabled() assert novelTree.horizontalScrollBar().isEnabled() # Populate Tree # ============= nwGUI.projStack.setCurrentIndex(nwGUI.idxNovelView) nwGUI.rebuildIndex() novelTree._populateTree(rootHandle=None) assert novelTree.topLevelItemCount() == 3 # Rebuild should preserve selection topItem = novelTree.topLevelItem(0) assert not topItem.isSelected() topItem.setSelected(True) assert novelTree.selectedItems()[0] == topItem assert novelView.getSelectedHandle() == ("000000000000c", 0) # Refresh using the slot for the butoom novelBar._refreshNovelTree() assert novelTree.topLevelItem(0).isSelected() # Open Items # ========== # Clear selection novelTree.clearSelection() scItem = novelTree.topLevelItem(2) scItem.setSelected(True) assert scItem.isSelected() # Clear selection with mouse vPort = novelTree.viewport() qtbot.mouseClick(vPort, Qt.LeftButton, pos=vPort.rect().center(), delay=10) assert not scItem.isSelected() # Double-click item scItem.setSelected(True) assert scItem.isSelected() assert nwGUI.docEditor.docHandle() is None novelTree._treeDoubleClick(scItem, 0) assert nwGUI.docEditor.docHandle() == "000000000000f" # Open item with middle mouse button scItem.setSelected(True) assert scItem.isSelected() assert nwGUI.docViewer.docHandle() is None qtbot.mouseClick(vPort, Qt.MiddleButton, pos=vPort.rect().center(), delay=10) assert nwGUI.docViewer.docHandle() is None scRect = novelTree.visualItemRect(scItem) oldData = scItem.data(novelTree.C_TITLE, novelTree.D_HANDLE) scItem.setData(novelTree.C_TITLE, novelTree.D_HANDLE, None) qtbot.mouseClick(vPort, Qt.MiddleButton, pos=scRect.center(), delay=10) assert nwGUI.docViewer.docHandle() is None scItem.setData(novelTree.C_TITLE, novelTree.D_HANDLE, oldData) qtbot.mouseClick(vPort, Qt.MiddleButton, pos=scRect.center(), delay=10) assert nwGUI.docViewer.docHandle() == "000000000000f" # Last Column # =========== novelBar.setLastColType(NovelTreeColumn.HIDDEN) assert novelTree.isColumnHidden(novelTree.C_EXTRA) is True assert novelTree.lastColType == NovelTreeColumn.HIDDEN assert novelTree._getLastColumnText("000000000000f", "T000001") == ("", "") novelBar.setLastColType(NovelTreeColumn.POV) assert novelTree.isColumnHidden(novelTree.C_EXTRA) is False assert novelTree.lastColType == NovelTreeColumn.POV assert novelTree._getLastColumnText("000000000000f", "T000001") == ( "Jane", "Point of View: Jane" ) novelBar.setLastColType(NovelTreeColumn.FOCUS) assert novelTree.isColumnHidden(novelTree.C_EXTRA) is False assert novelTree.lastColType == NovelTreeColumn.FOCUS assert novelTree._getLastColumnText("000000000000f", "T000001") == ( "Jane", "Focus: Jane" ) novelBar.setLastColType(NovelTreeColumn.PLOT) assert novelTree.isColumnHidden(novelTree.C_EXTRA) is False assert novelTree.lastColType == NovelTreeColumn.PLOT assert novelTree._getLastColumnText("000000000000f", "T000001") == ( "", "Plot: " ) novelTree._lastCol = None assert novelTree._getLastColumnText("0000000000000", "T000000") == ("", "") # Item Meta # ========= ttText = "" def showText(pos, text): nonlocal ttText ttText = text mIndex = novelTree.model().index(2, novelTree.C_MORE) with monkeypatch.context() as mp: mp.setattr(QToolTip, "showText", showText) novelTree._treeItemClicked(mIndex) assert ttText == ( "<p><b>Point of View</b>: Jane<br><b>Focus</b>: Jane</p>" "<p><b>Synopsis</b>: This is a scene.</p>" ) # Other Checks # ============ scItem = novelTree.topLevelItem(2) scItem.setSelected(True) assert scItem.isSelected() novelTree.focusOutEvent(QFocusEvent(QEvent.None_, Qt.MouseFocusReason)) assert not scItem.isSelected() # Close # ===== # qtbot.stop() nwGUI.closeProject()
def testGuiProjTree_DeleteItems(qtbot, caplog, monkeypatch, nwGUI, fncDir, mockRnd): """Test adding and removing items from the project tree. """ # Block message box monkeypatch.setattr(QMessageBox, "warning", lambda *a: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes) monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True)) nwTree = nwGUI.projView # Try to run with no project assert nwTree.emptyTrash() is False assert nwTree.deleteItem() is False # Create a project prjDir = os.path.join(fncDir, "project") buildTestProject(nwGUI, prjDir) # Try emptying the trash already now, when there is no trash folder assert nwTree.emptyTrash() is False # Add some files nwTree.setSelectedHandle("000000000000d") assert nwTree.projTree.newTreeItem(nwItemType.FILE) is True assert nwTree.projTree.newTreeItem(nwItemType.FILE) is True assert nwTree.projTree.newTreeItem(nwItemType.FILE) is True assert nwTree.getTreeFromHandle("000000000000d") == [ "000000000000d", "000000000000e", "000000000000f", "0000000000010", "0000000000011", "0000000000012", ] # Delete item without focus -> blocked monkeypatch.setattr(GuiProjectTree, "hasFocus", lambda *a: False) nwTree.setSelectedHandle("0000000000012") assert nwTree.deleteItem() is False monkeypatch.setattr(GuiProjectTree, "hasFocus", lambda *a: True) # No selection made nwTree.projTree.clearSelection() caplog.clear() assert nwTree.deleteItem() is False assert "no item to delete" in caplog.text # Not a valid handle nwTree.projTree.clearSelection() caplog.clear() assert nwTree.deleteItem("0000000000000") is False assert "Could not find tree item" in caplog.text # Delete Folder/Root # ================== # Deleting non-empty folders is blocked assert nwTree.deleteItem("0000000000008") is False # Novel Root assert nwTree.deleteItem("000000000000a") is True # Character Root # Delete File # =========== # Block adding trash folder funcPointer = nwTree.projTree._addTrashRoot nwTree.projTree._addTrashRoot = lambda *a: None assert nwTree.deleteItem("0000000000012") is False nwTree.projTree._addTrashRoot = funcPointer # Delete last two documents, which also adds the trash folder assert nwTree.deleteItem("0000000000012") is True assert nwTree.deleteItem("0000000000011") is True assert nwTree.getTreeFromHandle("000000000000d") == [ "000000000000d", "000000000000e", "000000000000f", "0000000000010" ] trashHandle = nwGUI.theProject.tree.trashRoot() assert nwTree.getTreeFromHandle(trashHandle) == [ trashHandle, "0000000000012", "0000000000011" ] # Delete the first file again (permanent), and ask for permission # Also open the document in the editor, which should trigger a close assert os.path.isfile(os.path.join(prjDir, "content", "0000000000012.nwd")) assert "0000000000012" in nwGUI.theProject.tree assert nwGUI.docEditor.docHandle() is None assert nwGUI.openDocument("0000000000012") is True assert nwGUI.docEditor.docHandle() == "0000000000012" assert nwTree.deleteItem("0000000000012") is True assert nwGUI.docEditor.docHandle() is None assert not os.path.isfile(os.path.join(prjDir, "content", "0000000000012.nwd")) assert "0000000000012" not in nwGUI.theProject.tree assert nwTree.getTreeFromHandle(trashHandle) == [ trashHandle, "0000000000011" ] # Delete the second file, and skip asking for permission assert os.path.isfile(os.path.join(prjDir, "content", "0000000000011.nwd")) assert "0000000000011" in nwGUI.theProject.tree assert nwTree.deleteItem("0000000000011", alreadyAsked=True) is True assert not os.path.isfile(os.path.join(prjDir, "content", "0000000000011.nwd")) assert "0000000000011" not in nwGUI.theProject.tree assert nwTree.getTreeFromHandle(trashHandle) == [trashHandle] # Delete Folder # ============= trashHandle = nwGUI.theProject.tree.trashRoot() # Add a folder with two files nwTree.setSelectedHandle("0000000000009") assert nwTree.projTree.newTreeItem(nwItemType.FOLDER) is True nwTree.setSelectedHandle("0000000000014") assert nwTree.projTree.newTreeItem(nwItemType.FILE) is True assert nwTree.projTree.newTreeItem(nwItemType.FILE) is True assert os.path.isfile(os.path.join(fncDir, "project", "content", "0000000000015.nwd")) assert os.path.isfile(os.path.join(fncDir, "project", "content", "0000000000016.nwd")) # Delete the folder, which moves everything to Trash assert nwTree.getTreeFromHandle("0000000000014") == [ "0000000000014", "0000000000015", "0000000000016" ] assert nwTree.deleteItem("0000000000014") is True assert nwTree.getTreeFromHandle(trashHandle) == [ trashHandle, "0000000000014", "0000000000015", "0000000000016" ] assert os.path.isfile(os.path.join(fncDir, "project", "content", "0000000000015.nwd")) assert os.path.isfile(os.path.join(fncDir, "project", "content", "0000000000016.nwd")) # Delete again, which should delete folder and all files assert nwTree.deleteItem("0000000000014") is True assert nwTree.getTreeFromHandle(trashHandle) == [trashHandle] assert not os.path.isfile(os.path.join(fncDir, "project", "content", "0000000000015.nwd")) assert not os.path.isfile(os.path.join(fncDir, "project", "content", "0000000000016.nwd")) # Add an empty folder, which can be deleted with no further restrictions nwTree.setSelectedHandle("0000000000009") assert nwTree.projTree.newTreeItem(nwItemType.FOLDER) is True assert nwTree.getTreeFromHandle("0000000000009") == ["0000000000009", "0000000000017"] nwTree.setSelectedHandle("0000000000017") assert nwTree.deleteItem("0000000000017") is True assert nwTree.getTreeFromHandle("0000000000009") == ["0000000000009"] # Empty Trash # =========== # Try to empty trash that is already empty assert nwTree.getTreeFromHandle(trashHandle) == [trashHandle] assert nwTree.emptyTrash() is False # Move the two remaining scene documents to trash assert nwTree.deleteItem("000000000000f") is True assert nwTree.deleteItem("0000000000010") is True assert nwTree.getTreeFromHandle("000000000000d") == [ "000000000000d", "000000000000e" ] assert nwTree.getTreeFromHandle(trashHandle) == [ trashHandle, "000000000000f", "0000000000010" ] # Empty trash, but select no on question with monkeypatch.context() as mp: mp.setattr(QMessageBox, "question", lambda *a: QMessageBox.No) assert nwTree.emptyTrash() is False # Empty the trash proper assert nwTree.emptyTrash() is True assert nwTree.getTreeFromHandle(trashHandle) == [trashHandle] # Try to delete a file, but block the underlying deletion of the file on disk assert os.path.isfile(os.path.join(fncDir, "project", "content", "000000000000e.nwd")) with monkeypatch.context() as mp: mp.setattr("novelwriter.core.document.NWDoc.deleteDocument", lambda *a: False) assert nwTree.deleteItem("000000000000e") is True assert nwTree.deleteItem("000000000000e") is True assert os.path.isfile(os.path.join(fncDir, "project", "content", "000000000000e.nwd")) # Delete proper assert nwTree.projTree._deleteTreeItem("000000000000e") is True assert not os.path.isfile(os.path.join(fncDir, "project", "content", "000000000000e.nwd")) # Clean up # qtbot.stopForInteraction() nwGUI.closeProject()
def testToolWritingStats_Main(qtbot, monkeypatch, nwGUI, fncDir, fncProj): """Test the full writing stats tool. """ # Block message box monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "warning", lambda *a: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes) # Create a project to work on buildTestProject(nwGUI, fncProj) qtbot.wait(100) assert nwGUI.saveProject() sessFile = os.path.join(fncProj, "meta", nwFiles.SESS_STATS) # Open the Writing Stats dialog nwGUI.mainConf.lastPath = "" nwGUI.mainMenu.aWritingStats.activate(QAction.Trigger) qtbot.waitUntil(lambda: getGuiItem("GuiWritingStats") is not None, timeout=1000) sessLog = getGuiItem("GuiWritingStats") assert isinstance(sessLog, GuiWritingStats) qtbot.wait(stepDelay) # Test Loading # ============ # No initial logfile assert not os.path.isfile(sessFile) assert not sessLog._loadLogFile() # Make a test log file writeFile(sessFile, ( "# Offset 123\n" "# Start Time End Time Novel Notes Idle\n" "2020-01-01 21:00:00 2020-01-01 21:00:05 6 0\n" "2020-01-03 21:00:00 2020-01-03 21:00:15 125 0\n" "2020-01-03 21:30:00 2020-01-03 21:30:15 125 5\n" "2020-01-06 21:00:00 2020-01-06 21:00:10 125 5\n")) assert os.path.isfile(sessFile) assert sessLog._loadLogFile() assert sessLog.wordOffset == 123 assert len(sessLog.logData) == 4 # Make sure a faulty file can still be read writeFile(sessFile, ( "# Offset abc123\n" "# Start Time End Time Novel Notes Idle\n" "2020-01-01 21:00:00 2020-01-01 21:00:05 6 0 50\n" "2020-01-03 21:00:00 2020-01-03 21:00:15 125 0\n" "2020-01-03 21:30:00 2020-01-03 21:30:15 125 5\n" "2020-01-06 21:00:00 2020-01-06 21:00:10 125\n")) assert sessLog._loadLogFile() assert sessLog.wordOffset == 0 assert len(sessLog.logData) == 3 # Test Exporting # ============== writeFile(sessFile, ( "# Offset 1075\n" "# Start Time End Time Novel Notes Idle\n" "2021-01-31 19:00:00 2021-01-31 19:30:00 700 375 0\n" "2021-02-01 19:00:00 2021-02-01 19:30:00 700 375 10\n" "2021-02-01 20:00:00 2021-02-01 20:30:00 600 275 20\n" "2021-02-02 19:00:00 2021-02-02 19:30:00 750 425 30\n" "2021-02-02 20:00:00 2021-02-02 20:30:00 690 365 40\n" "2021-02-03 19:00:00 2021-02-03 19:30:00 680 355 50\n" "2021-02-04 19:00:00 2021-02-04 19:30:00 700 375 60\n" "2021-02-05 19:00:00 2021-02-05 19:30:00 500 175 70\n" "2021-02-06 19:00:00 2021-02-06 19:30:00 600 275 80\n" "2021-02-07 19:00:00 2021-02-07 19:30:00 600 275 90\n" )) sessLog.populateGUI() # Make the saving fail monkeypatch.setattr(QFileDialog, "getSaveFileName", lambda *a, **k: ("", "")) assert not sessLog._saveData(sessLog.FMT_CSV) assert not sessLog._saveData(sessLog.FMT_JSON) assert not sessLog._saveData(None) # Make the save succeed monkeypatch.setattr("os.path.expanduser", lambda *a: fncDir) monkeypatch.setattr(QFileDialog, "getSaveFileName", lambda ss, tt, pp, options: (pp, "")) sessLog.listBox.sortByColumn(sessLog.C_TIME, 0) assert sessLog.novelWords.text() == "{:n}".format(600) assert sessLog.notesWords.text() == "{:n}".format(275) assert sessLog.totalWords.text() == "{:n}".format(875) assert sessLog.listBox.topLevelItem(0).text( sessLog.C_COUNT) == "{:n}".format(1) assert sessLog.listBox.topLevelItem(1).text( sessLog.C_COUNT) == "{:n}".format(-200) assert sessLog.listBox.topLevelItem(2).text( sessLog.C_COUNT) == "{:n}".format(300) assert sessLog.listBox.topLevelItem(3).text( sessLog.C_COUNT) == "{:n}".format(-120) assert sessLog.listBox.topLevelItem(4).text( sessLog.C_COUNT) == "{:n}".format(-20) assert sessLog.listBox.topLevelItem(5).text( sessLog.C_COUNT) == "{:n}".format(40) assert sessLog.listBox.topLevelItem(6).text( sessLog.C_COUNT) == "{:n}".format(-400) assert sessLog.listBox.topLevelItem(7).text( sessLog.C_COUNT) == "{:n}".format(200) assert sessLog._saveData(sessLog.FMT_CSV) qtbot.wait(100) assert sessLog._saveData(sessLog.FMT_JSON) qtbot.wait(100) assert nwGUI.mainConf.lastPath == fncDir # Check the exported files jsonStats = os.path.join(fncDir, "sessionStats.json") with open(jsonStats, mode="r", encoding="utf-8") as inFile: jsonData = json.load(inFile) assert jsonData == [{ "date": "2021-01-31 19:00:00", "length": 1800.0, "newWords": 1, "novelWords": 700, "noteWords": 375, "idleTime": 0 }, { "date": "2021-02-01 20:00:00", "length": 1800.0, "newWords": -200, "novelWords": 600, "noteWords": 275, "idleTime": 20 }, { "date": "2021-02-02 19:00:00", "length": 1800.0, "newWords": 300, "novelWords": 750, "noteWords": 425, "idleTime": 30 }, { "date": "2021-02-02 20:00:00", "length": 1800.0, "newWords": -120, "novelWords": 690, "noteWords": 365, "idleTime": 40 }, { "date": "2021-02-03 19:00:00", "length": 1800.0, "newWords": -20, "novelWords": 680, "noteWords": 355, "idleTime": 50 }, { "date": "2021-02-04 19:00:00", "length": 1800.0, "newWords": 40, "novelWords": 700, "noteWords": 375, "idleTime": 60 }, { "date": "2021-02-05 19:00:00", "length": 1800.0, "newWords": -400, "novelWords": 500, "noteWords": 175, "idleTime": 70 }, { "date": "2021-02-06 19:00:00", "length": 1800.0, "newWords": 200, "novelWords": 600, "noteWords": 275, "idleTime": 80 }] # Test Filters # ============ # No Novel Files qtbot.mouseClick(sessLog.incNovel, Qt.LeftButton) qtbot.wait(stepDelay) assert sessLog._saveData(sessLog.FMT_JSON) qtbot.wait(stepDelay) jsonStats = os.path.join(fncDir, "sessionStats.json") with open(jsonStats, mode="r", encoding="utf-8") as inFile: jsonData = json.loads(inFile.read()) assert sessLog.listBox.topLevelItem(0).text( sessLog.C_COUNT) == "{:n}".format(1) assert sessLog.listBox.topLevelItem(1).text( sessLog.C_COUNT) == "{:n}".format(-100) assert sessLog.listBox.topLevelItem(2).text( sessLog.C_COUNT) == "{:n}".format(150) assert sessLog.listBox.topLevelItem(3).text( sessLog.C_COUNT) == "{:n}".format(-60) assert sessLog.listBox.topLevelItem(4).text( sessLog.C_COUNT) == "{:n}".format(-10) assert sessLog.listBox.topLevelItem(5).text( sessLog.C_COUNT) == "{:n}".format(20) assert sessLog.listBox.topLevelItem(6).text( sessLog.C_COUNT) == "{:n}".format(-200) assert sessLog.listBox.topLevelItem(7).text( sessLog.C_COUNT) == "{:n}".format(100) assert jsonData == [{ "date": "2021-01-31 19:00:00", "length": 1800.0, "newWords": 1, "novelWords": 700, "noteWords": 375, "idleTime": 0 }, { "date": "2021-02-01 20:00:00", "length": 1800.0, "newWords": -100, "novelWords": 600, "noteWords": 275, "idleTime": 20 }, { "date": "2021-02-02 19:00:00", "length": 1800.0, "newWords": 150, "novelWords": 750, "noteWords": 425, "idleTime": 30 }, { "date": "2021-02-02 20:00:00", "length": 1800.0, "newWords": -60, "novelWords": 690, "noteWords": 365, "idleTime": 40 }, { "date": "2021-02-03 19:00:00", "length": 1800.0, "newWords": -10, "novelWords": 680, "noteWords": 355, "idleTime": 50 }, { "date": "2021-02-04 19:00:00", "length": 1800.0, "newWords": 20, "novelWords": 700, "noteWords": 375, "idleTime": 60 }, { "date": "2021-02-05 19:00:00", "length": 1800.0, "newWords": -200, "novelWords": 500, "noteWords": 175, "idleTime": 70 }, { "date": "2021-02-06 19:00:00", "length": 1800.0, "newWords": 100, "novelWords": 600, "noteWords": 275, "idleTime": 80 }] # No Note Files qtbot.mouseClick(sessLog.incNovel, Qt.LeftButton) qtbot.mouseClick(sessLog.incNotes, Qt.LeftButton) qtbot.wait(stepDelay) assert sessLog._saveData(sessLog.FMT_JSON) qtbot.wait(stepDelay) jsonStats = os.path.join(fncDir, "sessionStats.json") with open(jsonStats, mode="r", encoding="utf-8") as inFile: jsonData = json.load(inFile) assert sessLog.listBox.topLevelItem(0).text( sessLog.C_COUNT) == "{:n}".format(1) assert sessLog.listBox.topLevelItem(1).text( sessLog.C_COUNT) == "{:n}".format(-100) assert sessLog.listBox.topLevelItem(2).text( sessLog.C_COUNT) == "{:n}".format(150) assert sessLog.listBox.topLevelItem(3).text( sessLog.C_COUNT) == "{:n}".format(-60) assert sessLog.listBox.topLevelItem(4).text( sessLog.C_COUNT) == "{:n}".format(-10) assert sessLog.listBox.topLevelItem(5).text( sessLog.C_COUNT) == "{:n}".format(20) assert sessLog.listBox.topLevelItem(6).text( sessLog.C_COUNT) == "{:n}".format(-200) assert sessLog.listBox.topLevelItem(7).text( sessLog.C_COUNT) == "{:n}".format(100) assert jsonData == [{ "date": "2021-01-31 19:00:00", "length": 1800.0, "newWords": 1, "novelWords": 700, "noteWords": 375, "idleTime": 0 }, { "date": "2021-02-01 20:00:00", "length": 1800.0, "newWords": -100, "novelWords": 600, "noteWords": 275, "idleTime": 20 }, { "date": "2021-02-02 19:00:00", "length": 1800.0, "newWords": 150, "novelWords": 750, "noteWords": 425, "idleTime": 30 }, { "date": "2021-02-02 20:00:00", "length": 1800.0, "newWords": -60, "novelWords": 690, "noteWords": 365, "idleTime": 40 }, { "date": "2021-02-03 19:00:00", "length": 1800.0, "newWords": -10, "novelWords": 680, "noteWords": 355, "idleTime": 50 }, { "date": "2021-02-04 19:00:00", "length": 1800.0, "newWords": 20, "novelWords": 700, "noteWords": 375, "idleTime": 60 }, { "date": "2021-02-05 19:00:00", "length": 1800.0, "newWords": -200, "novelWords": 500, "noteWords": 175, "idleTime": 70 }, { "date": "2021-02-06 19:00:00", "length": 1800.0, "newWords": 100, "novelWords": 600, "noteWords": 275, "idleTime": 80 }] # No Negative Entries qtbot.mouseClick(sessLog.incNotes, Qt.LeftButton) qtbot.mouseClick(sessLog.hideNegative, Qt.LeftButton) qtbot.wait(stepDelay) assert sessLog._saveData(sessLog.FMT_JSON) qtbot.wait(stepDelay) # qtbot.stopForInteraction() jsonStats = os.path.join(fncDir, "sessionStats.json") with open(jsonStats, mode="r", encoding="utf-8") as inFile: jsonData = json.load(inFile) assert sessLog.listBox.topLevelItem(0).text( sessLog.C_COUNT) == "{:n}".format(1) assert sessLog.listBox.topLevelItem(1).text( sessLog.C_COUNT) == "{:n}".format(300) assert sessLog.listBox.topLevelItem(2).text( sessLog.C_COUNT) == "{:n}".format(40) assert sessLog.listBox.topLevelItem(3).text( sessLog.C_COUNT) == "{:n}".format(200) assert jsonData == [{ "date": "2021-01-31 19:00:00", "length": 1800.0, "newWords": 1, "novelWords": 700, "noteWords": 375, "idleTime": 0 }, { "date": "2021-02-02 19:00:00", "length": 1800.0, "newWords": 300, "novelWords": 750, "noteWords": 425, "idleTime": 30 }, { "date": "2021-02-04 19:00:00", "length": 1800.0, "newWords": 40, "novelWords": 700, "noteWords": 375, "idleTime": 60 }, { "date": "2021-02-06 19:00:00", "length": 1800.0, "newWords": 200, "novelWords": 600, "noteWords": 275, "idleTime": 80 }] # Un-hide Zero Entries qtbot.mouseClick(sessLog.hideNegative, Qt.LeftButton) qtbot.mouseClick(sessLog.hideZeros, Qt.LeftButton) qtbot.wait(stepDelay) assert sessLog._saveData(sessLog.FMT_JSON) qtbot.wait(stepDelay) jsonStats = os.path.join(fncDir, "sessionStats.json") with open(jsonStats, mode="r", encoding="utf-8") as inFile: jsonData = json.load(inFile) assert sessLog.listBox.topLevelItem(0).text( sessLog.C_COUNT) == "{:n}".format(1) assert sessLog.listBox.topLevelItem(1).text( sessLog.C_COUNT) == "{:n}".format(0) assert sessLog.listBox.topLevelItem(2).text( sessLog.C_COUNT) == "{:n}".format(-200) assert sessLog.listBox.topLevelItem(3).text( sessLog.C_COUNT) == "{:n}".format(300) assert sessLog.listBox.topLevelItem(4).text( sessLog.C_COUNT) == "{:n}".format(-120) assert sessLog.listBox.topLevelItem(5).text( sessLog.C_COUNT) == "{:n}".format(-20) assert sessLog.listBox.topLevelItem(6).text( sessLog.C_COUNT) == "{:n}".format(40) assert sessLog.listBox.topLevelItem(7).text( sessLog.C_COUNT) == "{:n}".format(-400) assert sessLog.listBox.topLevelItem(8).text( sessLog.C_COUNT) == "{:n}".format(200) assert sessLog.listBox.topLevelItem(9).text( sessLog.C_COUNT) == "{:n}".format(0) assert jsonData == [{ "date": "2021-01-31 19:00:00", "length": 1800.0, "newWords": 1, "novelWords": 700, "noteWords": 375, "idleTime": 0 }, { "date": "2021-02-01 19:00:00", "length": 1800.0, "newWords": 0, "novelWords": 700, "noteWords": 375, "idleTime": 10 }, { "date": "2021-02-01 20:00:00", "length": 1800.0, "newWords": -200, "novelWords": 600, "noteWords": 275, "idleTime": 20 }, { "date": "2021-02-02 19:00:00", "length": 1800.0, "newWords": 300, "novelWords": 750, "noteWords": 425, "idleTime": 30 }, { "date": "2021-02-02 20:00:00", "length": 1800.0, "newWords": -120, "novelWords": 690, "noteWords": 365, "idleTime": 40 }, { "date": "2021-02-03 19:00:00", "length": 1800.0, "newWords": -20, "novelWords": 680, "noteWords": 355, "idleTime": 50 }, { "date": "2021-02-04 19:00:00", "length": 1800.0, "newWords": 40, "novelWords": 700, "noteWords": 375, "idleTime": 60 }, { "date": "2021-02-05 19:00:00", "length": 1800.0, "newWords": -400, "novelWords": 500, "noteWords": 175, "idleTime": 70 }, { "date": "2021-02-06 19:00:00", "length": 1800.0, "newWords": 200, "novelWords": 600, "noteWords": 275, "idleTime": 80 }, { "date": "2021-02-07 19:00:00", "length": 1800.0, "newWords": 0, "novelWords": 600, "noteWords": 275, "idleTime": 90 }] # Group by Day qtbot.mouseClick(sessLog.groupByDay, Qt.LeftButton) qtbot.wait(stepDelay) assert sessLog._saveData(sessLog.FMT_JSON) qtbot.wait(stepDelay) jsonStats = os.path.join(fncDir, "sessionStats.json") with open(jsonStats, mode="r", encoding="utf-8") as inFile: jsonData = json.load(inFile) assert sessLog.listBox.topLevelItem(0).text( sessLog.C_COUNT) == "{:n}".format(1) assert sessLog.listBox.topLevelItem(1).text( sessLog.C_COUNT) == "{:n}".format(-200) assert sessLog.listBox.topLevelItem(2).text( sessLog.C_COUNT) == "{:n}".format(180) assert sessLog.listBox.topLevelItem(3).text( sessLog.C_COUNT) == "{:n}".format(-20) assert sessLog.listBox.topLevelItem(4).text( sessLog.C_COUNT) == "{:n}".format(40) assert sessLog.listBox.topLevelItem(5).text( sessLog.C_COUNT) == "{:n}".format(-400) assert sessLog.listBox.topLevelItem(6).text( sessLog.C_COUNT) == "{:n}".format(200) assert sessLog.listBox.topLevelItem(7).text( sessLog.C_COUNT) == "{:n}".format(0) assert jsonData == [{ "date": "2021-01-31", "length": 1800.0, "newWords": 1, "novelWords": 700, "noteWords": 375, "idleTime": 0 }, { "date": "2021-02-01", "length": 3600.0, "newWords": -200, "novelWords": 600, "noteWords": 275, "idleTime": 30 }, { "date": "2021-02-02", "length": 3600.0, "newWords": 180, "novelWords": 690, "noteWords": 365, "idleTime": 70 }, { "date": "2021-02-03", "length": 1800.0, "newWords": -20, "novelWords": 680, "noteWords": 355, "idleTime": 50 }, { "date": "2021-02-04", "length": 1800.0, "newWords": 40, "novelWords": 700, "noteWords": 375, "idleTime": 60 }, { "date": "2021-02-05", "length": 1800.0, "newWords": -400, "novelWords": 500, "noteWords": 175, "idleTime": 70 }, { "date": "2021-02-06", "length": 1800.0, "newWords": 200, "novelWords": 600, "noteWords": 275, "idleTime": 80 }, { "date": "2021-02-07", "length": 1800.0, "newWords": 0, "novelWords": 600, "noteWords": 275, "idleTime": 90 }] # IOError # ======= monkeypatch.setattr("builtins.open", causeOSError) assert not sessLog._loadLogFile() assert not sessLog._saveData(sessLog.FMT_CSV) # qtbot.stopForInteraction() sessLog._doClose() assert nwGUI.closeProject() qtbot.wait(stepDelay)
def testGuiProjTree_ContextMenu(qtbot, caplog, monkeypatch, nwGUI, fncDir, mockRnd): """Test the building of the project tree context menu. All this does is test that the menu builds. It doesn't open the actual menu, """ # Block message box monkeypatch.setattr(QMessageBox, "warning", lambda *a: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes) monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True)) monkeypatch.setattr(QMenu, "exec_", lambda *a: None) # Create a project prjDir = os.path.join(fncDir, "project") buildTestProject(nwGUI, prjDir) # Handles for new objects hNovelRoot = "0000000000008" hTitlePage = "000000000000c" hChapterDir = "000000000000d" hChapterFile = "000000000000e" hCharRoot = "000000000000a" hCharNote = "0000000000011" hNovelNote = "0000000000012" projTree = nwGUI.projView.projTree projTree._getTreeItem(hNovelRoot).setExpanded(True) projTree._getTreeItem(hChapterDir).setExpanded(True) projTree._addTrashRoot() hTrashRoot = projTree.theProject.tree.trashRoot() projTree.setSelectedHandle(hCharRoot) projTree.newTreeItem(nwItemType.FILE) projTree.setSelectedHandle(hNovelRoot) projTree.newTreeItem(nwItemType.FILE, isNote=True) def itemPos(tHandle): return projTree.visualItemRect(projTree._getTreeItem(tHandle)).center() # No item under menu assert projTree._openContextMenu(projTree.viewport().rect().bottomRight()) is False # Generate the possible menu combinarions assert projTree._openContextMenu(itemPos(hTrashRoot)) is True assert projTree._openContextMenu(itemPos(hNovelRoot)) is True assert projTree._openContextMenu(itemPos(hNovelNote)) is True assert projTree._openContextMenu(itemPos(hTitlePage)) is True assert projTree._openContextMenu(itemPos(hChapterDir)) is True assert projTree._openContextMenu(itemPos(hChapterFile)) is True assert projTree._openContextMenu(itemPos(hCharRoot)) is True assert projTree._openContextMenu(itemPos(hCharNote)) is True # Check the keyboard shortcut handler as well projTree.setSelectedHandle(hNovelRoot) assert projTree.openContextOnSelected() is True projTree.clearSelection() assert projTree.openContextOnSelected() is False # Direct Edit Functions # ===================== # Trigger the dedicated functions the menu entries connect to nwItem = projTree.theProject.tree[hNovelNote] # Toggle exported flag assert nwItem.isExported is True projTree._toggleItemExported(hNovelNote) assert nwItem.isExported is False # Change item status assert nwItem.itemStatus == "s000000" projTree._changeItemStatus(hNovelNote, "s000001") assert nwItem.itemStatus == "s000001" # Change item importance assert nwItem.itemImport == "i000004" projTree._changeItemImport(hNovelNote, "i000005") assert nwItem.itemImport == "i000005" # Change item layout assert nwItem.itemLayout == nwItemLayout.NOTE projTree._changeItemLayout(hNovelNote, nwItemLayout.DOCUMENT) assert nwItem.itemLayout == nwItemLayout.DOCUMENT projTree._changeItemLayout(hNovelNote, nwItemLayout.NOTE) assert nwItem.itemLayout == nwItemLayout.NOTE
def testGuiMenu_Insert(qtbot, monkeypatch, nwGUI, fncDir, fncProj, mockRnd): """Test the Insert menu. """ # Block message box monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes) buildTestProject(nwGUI, fncProj) assert nwGUI.projView.projTree._getTreeItem("000000000000f") is not None assert nwGUI.openDocument("000000000000f") is True nwGUI.docEditor.clear() # Test Faulty Inserts assert nwGUI.docEditor.insertText("hello world") assert nwGUI.docEditor.getText() == "hello world" nwGUI.docEditor.clear() assert nwGUI.docEditor.insertText(nwDocInsert.NO_INSERT) is False assert nwGUI.docEditor.isEmpty() assert nwGUI.docEditor.insertText(None) is False assert nwGUI.docEditor.isEmpty() # qtbot.stopForInteraction() # Check Menu Entries nwGUI.mainMenu.aInsENDash.activate(QAction.Trigger) assert nwGUI.docEditor.getText() == nwUnicode.U_ENDASH nwGUI.docEditor.clear() nwGUI.mainMenu.aInsEMDash.activate(QAction.Trigger) assert nwGUI.docEditor.getText() == nwUnicode.U_EMDASH nwGUI.docEditor.clear() nwGUI.mainMenu.aInsHorBar.activate(QAction.Trigger) assert nwGUI.docEditor.getText() == nwUnicode.U_HBAR nwGUI.docEditor.clear() nwGUI.mainMenu.aInsFigDash.activate(QAction.Trigger) assert nwGUI.docEditor.getText() == nwUnicode.U_FGDASH nwGUI.docEditor.clear() nwGUI.mainMenu.aInsQuoteLS.activate(QAction.Trigger) assert nwGUI.docEditor.getText() == nwGUI.mainConf.fmtSingleQuotes[0] nwGUI.docEditor.clear() nwGUI.mainMenu.aInsQuoteRS.activate(QAction.Trigger) assert nwGUI.docEditor.getText() == nwGUI.mainConf.fmtSingleQuotes[1] nwGUI.docEditor.clear() nwGUI.mainMenu.aInsQuoteLD.activate(QAction.Trigger) assert nwGUI.docEditor.getText() == nwGUI.mainConf.fmtDoubleQuotes[0] nwGUI.docEditor.clear() nwGUI.mainMenu.aInsQuoteRD.activate(QAction.Trigger) assert nwGUI.docEditor.getText() == nwGUI.mainConf.fmtDoubleQuotes[1] nwGUI.docEditor.clear() nwGUI.mainMenu.aInsMSApos.activate(QAction.Trigger) assert nwGUI.docEditor.getText() == nwUnicode.U_MAPOSS nwGUI.docEditor.clear() nwGUI.mainMenu.aInsEllipsis.activate(QAction.Trigger) assert nwGUI.docEditor.getText() == nwUnicode.U_HELLIP nwGUI.docEditor.clear() nwGUI.mainMenu.aInsPrime.activate(QAction.Trigger) assert nwGUI.docEditor.getText() == nwUnicode.U_PRIME nwGUI.docEditor.clear() nwGUI.mainMenu.aInsDPrime.activate(QAction.Trigger) assert nwGUI.docEditor.getText() == nwUnicode.U_DPRIME nwGUI.docEditor.clear() nwGUI.mainMenu.aInsBullet.activate(QAction.Trigger) assert nwGUI.docEditor.getText() == nwUnicode.U_BULL nwGUI.docEditor.clear() nwGUI.mainMenu.aInsHyBull.activate(QAction.Trigger) assert nwGUI.docEditor.getText() == nwUnicode.U_HYBULL nwGUI.docEditor.clear() nwGUI.mainMenu.aInsFlower.activate(QAction.Trigger) assert nwGUI.docEditor.getText() == nwUnicode.U_FLOWER nwGUI.docEditor.clear() nwGUI.mainMenu.aInsPerMille.activate(QAction.Trigger) assert nwGUI.docEditor.getText() == nwUnicode.U_PERMIL nwGUI.docEditor.clear() nwGUI.mainMenu.aInsDegree.activate(QAction.Trigger) assert nwGUI.docEditor.getText() == nwUnicode.U_DEGREE nwGUI.docEditor.clear() nwGUI.mainMenu.aInsMinus.activate(QAction.Trigger) assert nwGUI.docEditor.getText() == nwUnicode.U_MINUS nwGUI.docEditor.clear() nwGUI.mainMenu.aInsTimes.activate(QAction.Trigger) assert nwGUI.docEditor.getText() == nwUnicode.U_TIMES nwGUI.docEditor.clear() nwGUI.mainMenu.aInsDivide.activate(QAction.Trigger) assert nwGUI.docEditor.getText() == nwUnicode.U_DIVIDE nwGUI.docEditor.clear() nwGUI.mainMenu.aInsNBSpace.activate(QAction.Trigger) if nwGUI.mainConf.verQtValue >= 50900: assert nwGUI.docEditor.getText() == nwUnicode.U_NBSP else: assert nwGUI.docEditor.getText() == " " nwGUI.docEditor.clear() nwGUI.mainMenu.aInsThinSpace.activate(QAction.Trigger) assert nwGUI.docEditor.getText() == nwUnicode.U_THSP nwGUI.docEditor.clear() nwGUI.mainMenu.aInsThinNBSpace.activate(QAction.Trigger) if nwGUI.mainConf.verQtValue >= 50900: assert nwGUI.docEditor.getText() == nwUnicode.U_THNBSP else: assert nwGUI.docEditor.getText() == " " nwGUI.docEditor.clear() ## # Insert Keywords ## nwGUI.docEditor.setText("Stuff") nwGUI.mainMenu.mInsKWItems[nwKeyWords.TAG_KEY][0].activate(QAction.Trigger) assert nwGUI.docEditor.getText() == "Stuff\n%s: " % nwKeyWords.TAG_KEY nwGUI.docEditor.setText("Stuff") nwGUI.mainMenu.mInsKWItems[nwKeyWords.POV_KEY][0].activate(QAction.Trigger) assert nwGUI.docEditor.getText() == "Stuff\n%s: " % nwKeyWords.POV_KEY nwGUI.docEditor.setText("Stuff") nwGUI.mainMenu.mInsKWItems[nwKeyWords.FOCUS_KEY][0].activate( QAction.Trigger) assert nwGUI.docEditor.getText() == "Stuff\n%s: " % nwKeyWords.FOCUS_KEY nwGUI.docEditor.setText("Stuff") nwGUI.mainMenu.mInsKWItems[nwKeyWords.CHAR_KEY][0].activate( QAction.Trigger) assert nwGUI.docEditor.getText() == "Stuff\n%s: " % nwKeyWords.CHAR_KEY nwGUI.docEditor.setText("Stuff") nwGUI.mainMenu.mInsKWItems[nwKeyWords.PLOT_KEY][0].activate( QAction.Trigger) assert nwGUI.docEditor.getText() == "Stuff\n%s: " % nwKeyWords.PLOT_KEY nwGUI.docEditor.setText("Stuff") nwGUI.mainMenu.mInsKWItems[nwKeyWords.TIME_KEY][0].activate( QAction.Trigger) assert nwGUI.docEditor.getText() == "Stuff\n%s: " % nwKeyWords.TIME_KEY nwGUI.docEditor.setText("Stuff") nwGUI.mainMenu.mInsKWItems[nwKeyWords.WORLD_KEY][0].activate( QAction.Trigger) assert nwGUI.docEditor.getText() == "Stuff\n%s: " % nwKeyWords.WORLD_KEY nwGUI.docEditor.setText("Stuff") nwGUI.mainMenu.mInsKWItems[nwKeyWords.OBJECT_KEY][0].activate( QAction.Trigger) assert nwGUI.docEditor.getText() == "Stuff\n%s: " % nwKeyWords.OBJECT_KEY nwGUI.docEditor.setText("Stuff") nwGUI.mainMenu.mInsKWItems[nwKeyWords.ENTITY_KEY][0].activate( QAction.Trigger) assert nwGUI.docEditor.getText() == "Stuff\n%s: " % nwKeyWords.ENTITY_KEY nwGUI.docEditor.setText("Stuff") nwGUI.mainMenu.mInsKWItems[nwKeyWords.CUSTOM_KEY][0].activate( QAction.Trigger) assert nwGUI.docEditor.getText() == "Stuff\n%s: " % nwKeyWords.CUSTOM_KEY # Faulty Keyword Inserts assert not nwGUI.docEditor.insertKeyWord("blabla") with monkeypatch.context() as mp: mp.setattr(QTextBlock, "isValid", lambda *a, **k: False) assert not nwGUI.docEditor.insertKeyWord(nwKeyWords.TAG_KEY) nwGUI.docEditor.clear() ## # Insert Break or Space ## nwGUI.docEditor.setText("### Stuff\n") nwGUI.mainMenu.aInsNewPage.activate(QAction.Trigger) assert nwGUI.docEditor.getText() == "[NEW PAGE]\n### Stuff\n" nwGUI.docEditor.setText("### Stuff\n") nwGUI.mainMenu.aInsVSpaceS.activate(QAction.Trigger) assert nwGUI.docEditor.getText() == "[VSPACE]\n### Stuff\n" nwGUI.docEditor.setText("### Stuff\n") nwGUI.mainMenu.aInsVSpaceM.activate(QAction.Trigger) assert nwGUI.docEditor.getText() == "[VSPACE:2]\n### Stuff\n" nwGUI.docEditor.clear() ## # Insert text from file ## nwGUI.closeDocument() # First, with no path monkeypatch.setattr(QFileDialog, "getOpenFileName", lambda *a, **k: ("", "")) assert not nwGUI.importDocument() # Then with a path, but an invalid one monkeypatch.setattr(QFileDialog, "getOpenFileName", lambda *a, **k: (" ", "")) assert not nwGUI.importDocument() # Then a valid path, but bot a file that exists theFile = os.path.join(fncDir, "import.txt") monkeypatch.setattr(QFileDialog, "getOpenFileName", lambda *a, **k: (theFile, "")) assert not nwGUI.importDocument() # Create the file and try again, but with no target document open writeFile(theFile, "Foo") assert not nwGUI.importDocument() # Open the document from before, and add some text to it nwGUI.openDocument("000000000000f") nwGUI.docEditor.setText("Bar") assert nwGUI.docEditor.getText() == "Bar" # The document isn't empty, so the message box should pop monkeypatch.setattr(QMessageBox, "question", lambda *a, **k: QMessageBox.No) assert not nwGUI.importDocument() assert nwGUI.docEditor.getText() == "Bar" # Finally, accept the replaced text, this time we use the menu entry to trigger it monkeypatch.setattr(QMessageBox, "question", lambda *a, **k: QMessageBox.Yes) nwGUI.mainMenu.aImportFile.activate(QAction.Trigger) assert nwGUI.docEditor.getText() == "Foo" ## # Reveal file location ## theMessage = "" def recordMsg(*args): nonlocal theMessage theMessage = args[3] return None assert not theMessage monkeypatch.setattr(QMessageBox, "information", recordMsg) nwGUI.mainMenu.aFileDetails.activate(QAction.Trigger) theBits = theMessage.split("<br>") assert len(theBits) == 2 assert theBits[0] == "The currently open file is saved in:" assert theBits[1] == os.path.join(fncProj, "content", "000000000000f.nwd")