def testCoreToken_TextOps(monkeypatch, nwMinimal, dummyGUI): """Test handling files and text in the Tokenizer class. """ theProject = NWProject(dummyGUI) theProject.projTree.setSeed(42) theProject.projLang = "en" theProject._loadProjectLocalisation() theToken = Tokenizer(theProject, dummyGUI) theToken.setKeepMarkdown(True) assert theProject.openProject(nwMinimal) sHandle = "8c659a11cd429" # Set some content to work with docText = ( "### Scene Six\n\n" "This is text with _italic text_, some **bold text**, some ~~deleted text~~, " "and some **_mixed text_** and **some _nested_ text**.\n\n" "#### Replace\n\n" "Also, replace <A> and <B>.\n\n" ) docTextR = docText.replace("<A>", "this").replace("<B>", "that") nDoc = NWDoc(theProject, dummyGUI) nDoc.openDocument(sHandle) nDoc.saveDocument(docText) nDoc.clearDocument() theProject.setAutoReplace({"A": "this", "B": "that"}) assert theProject.saveProject() # Root heading assert theToken.addRootHeading("dummy") is False assert theToken.addRootHeading(sHandle) is False assert theToken.addRootHeading("7695ce551d265") is True assert theToken.theMarkdown[-1] == "# Notes: Plot\n\n" # Set text assert theToken.setText("dummy") is False assert theToken.setText(sHandle) is True assert theToken.theText == docText with monkeypatch.context() as mp: mp.setattr("nw.constants.nwConst.MAX_DOCSIZE", 100) assert theToken.setText(sHandle, docText) is True assert theToken.theText == ( "# ERROR\n\n" "Document 'New Scene' is too big (0.00 MB). Skipping.\n\n" ) assert theToken.setText(sHandle, docText) is True assert theToken.theText == docText assert theToken.isNone is False assert theToken.isTitle is False assert theToken.isBook is False assert theToken.isPage is False assert theToken.isPart is False assert theToken.isUnNum is False assert theToken.isChap is False assert theToken.isScene is True assert theToken.isNote is False assert theToken.isNovel is True # Pre Processing theToken.doPreProcessing() assert theToken.theText == docTextR # Post Processing theToken.theResult = r"This is text with escapes: \** \~~ \__" theToken.doPostProcessing() assert theToken.theResult == "This is text with escapes: ** ~~ __" # Save File savePath = os.path.join(nwMinimal, "dump.nwd") theToken.saveRawMarkdown(savePath) assert readFile(savePath) == "# Notes: Plot\n\n"
def _doSplit(self): """Perform the split of the file, create a new folder in the same parent folder, and multiple files depending on split level settings. The old file is not removed in the split process, and must be deleted manually. """ logger.verbose("GuiDocSplit split button clicked") if self.sourceItem is None: self.theParent.makeAlert( ("No source document selected. Nothing to do."), nwAlert.ERROR) return srcItem = self.theProject.projTree[self.sourceItem] if srcItem is None: self.theParent.makeAlert(("Could not parse source document."), nwAlert.ERROR) return theDoc = NWDoc(self.theProject, self.theParent) theText = theDoc.openDocument(self.sourceItem, False) theLines = theText.splitlines() nLines = len(theLines) theLines.insert(0, "%Split Doc") logger.debug("Splitting document %s with %d lines" % (self.sourceItem, nLines)) finalOrder = [] for i in range(self.listBox.count()): listItem = self.listBox.item(i) wTitle = listItem.text() lineNo = listItem.data(Qt.UserRole) finalOrder.append([wTitle, lineNo, nLines]) if i > 0: finalOrder[i - 1][2] = lineNo nFiles = len(finalOrder) if nFiles == 0: self.theParent.makeAlert(("No headers found. Nothing to do."), nwAlert.ERROR) return # Check that another folder can be created parTree = self.theProject.projTree.getItemPath(srcItem.itemParent) if len(parTree) >= nwConst.MAX_DEPTH - 1: self.theParent.makeAlert( ("Cannot add new folder for the document split. " "Maximum folder depth has been reached. " "Please move the file to another level in the project tree."), nwAlert.ERROR) return msgYes = self.theParent.askQuestion( "Split Document", ("The document will be split into %d file(s) in a new folder. " "The original document will remain intact.<br><br>" "Continue with the splitting process?") % nFiles) if not msgYes: return # Create the folder fHandle = self.theProject.newFolder(srcItem.itemName, srcItem.itemClass, srcItem.itemParent) self.theParent.treeView.revealNewTreeItem(fHandle) logger.verbose("Creating folder %s" % fHandle) # Loop through, and create the files for wTitle, iStart, iEnd in finalOrder: itemLayout = nwItemLayout.NOTE if srcItem.itemClass == nwItemClass.NOVEL: if wTitle.startswith("# "): itemLayout = nwItemLayout.PARTITION elif wTitle.startswith("## "): itemLayout = nwItemLayout.CHAPTER elif wTitle.startswith("### "): itemLayout = nwItemLayout.SCENE elif wTitle.startswith("#### "): itemLayout = nwItemLayout.SCENE wTitle = wTitle.lstrip("#") wTitle = wTitle.strip() nHandle = self.theProject.newFile(wTitle, srcItem.itemClass, fHandle) newItem = self.theProject.projTree[nHandle] newItem.setLayout(itemLayout) newItem.setStatus(srcItem.itemStatus) logger.verbose( "Creating new document %s with text from line %d to %d" % (nHandle, iStart, iEnd - 1)) theText = "\n".join(theLines[iStart:iEnd]) theText = theText.rstrip("\n") + "\n\n" theDoc.openDocument(nHandle, False) theDoc.saveDocument(theText) theDoc.clearDocument() self.theParent.treeView.revealNewTreeItem(nHandle) self._doClose() return
def testCoreDocument_LoadSave(monkeypatch, dummyGUI, nwMinimal): """Test loading and saving a document with the NWDoc class. """ theProject = NWProject(dummyGUI) assert theProject.openProject(nwMinimal) assert theProject.projPath == nwMinimal theDoc = NWDoc(theProject, dummyGUI) sHandle = "8c659a11cd429" # Not a valid handle assert theDoc.openDocument("dummy") is None # Non-existent handle assert theDoc.openDocument("0000000000000") is None # Cause open() to fail while loading def dummyOpen(*args, **kwargs): raise OSError with monkeypatch.context() as mp: mp.setattr("builtins.open", dummyOpen) assert theDoc.openDocument(sHandle) is None # Load the text assert theDoc.openDocument(sHandle) == "### New Scene\n\n" # Try to open a new (non-existent) file nHandle = theProject.projTree.findRoot(nwItemClass.NOVEL) assert nHandle is not None xHandle = theProject.newFile("New File", nwItemClass.NOVEL, nHandle) assert theDoc.openDocument(xHandle) == "" # Check cached item assert isinstance(theDoc._theItem, NWItem) assert theDoc.openDocument(xHandle, isOrphan=True) == "" assert theDoc._theItem is None # Set handle and save again theText = "### Test File\n\nText ...\n\n" assert theDoc.openDocument(xHandle) == "" assert theDoc.saveDocument(theText) # Save again to ensure temp file and previous file is handled assert theDoc.saveDocument(theText) # Check file content docPath = os.path.join(nwMinimal, "content", xHandle + ".nwd") with open(docPath, mode="r", encoding="utf8") as inFile: assert inFile.read() == ("%%~name: New File\n" f"%%~path: a508bb932959c/{xHandle}\n" "%%~kind: NOVEL/SCENE\n" "### Test File\n\n" "Text ...\n\n") # Force no meta data theDoc._theItem = None assert theDoc.saveDocument(theText) with open(docPath, mode="r", encoding="utf8") as inFile: assert inFile.read() == theText # Cause open() to fail while saving with monkeypatch.context() as mp: mp.setattr("builtins.open", causeOSError) assert not theDoc.saveDocument(theText) # Saving with no handle theDoc.clearDocument() assert not theDoc.saveDocument(theText) # Delete the last document assert not theDoc.deleteDocument("dummy") assert os.path.isfile(docPath) # Cause the delete to fail with monkeypatch.context() as mp: mp.setattr("os.unlink", causeOSError) assert not theDoc.deleteDocument(xHandle) # Make the delete pass assert theDoc.deleteDocument(xHandle) assert not os.path.isfile(docPath)