Exemple #1
0
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
Exemple #2
0
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
Exemple #3
0
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()
Exemple #4
0
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)
Exemple #5
0
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()
Exemple #6
0
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()
Exemple #7
0
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())
Exemple #8
0
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"
                }
            },
        }
    })
Exemple #9
0
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()
Exemple #10
0
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
Exemple #11
0
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!")
    ]
Exemple #12
0
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
Exemple #13
0
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)"
Exemple #14
0
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])
Exemple #15
0
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()
Exemple #16
0
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()
Exemple #17
0
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()
Exemple #18
0
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)
Exemple #19
0
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
Exemple #20
0
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")