Integer egestas maximus leo eu facilisis. Nunc rhoncus dignissim lectus eu lacinia. Praesent lacinia urna porttitor aliquam condimentum. Nulla eu eros dictum, dictum nunc vitae, sagittis nibh. Integer ante neque, consequat nec sollicitudin id, consectetur vitae dolor. Nullam volutpat sem orci, quis viverra magna auctor a. Suspendisse potenti. Maecenas commodo sed neque pellentesque vehicula. Sed luctus nisl risus, elementum semper purus interdum vel. Ut pulvinar, massa sit amet venenatis placerat, nunc lacus hendrerit odio, non aliquet nunc risus eu lectus. Maecenas feugiat semper ligula, id lobortis sem porta eu. Integer posuere elit magna, at mollis eros bibendum et. Ut imperdiet purus vel nulla aliquam maximus. Morbi sodales purus tellus, a rhoncus sem rutrum sit amet. Quisque risus sem, laoreet nec convallis nec, rutrum vitae justo.
Pellentesque nec erat ut nulla posuere commodo. Curabitur nisi augue, imperdiet et porta imperdiet, efficitur id leo. Cras finibus arcu at nibh commodo congue. Proin suscipit placerat condimentum. Aenean ante enim, cursus id lorem a, blandit venenatis nibh. Maecenas suscipit porta elit, sit amet porta felis porttitor eu. Sed a dui nibh. Phasellus sed faucibus dui. Pellentesque felis nulla, ultrices non efficitur quis, rutrum id mi. Mauris tempus auctor nisl, in bibendum enim pellentesque sit amet. Proin nunc lacus, imperdiet nec posuere ac, interdum non lectus.
Suspendisse faucibus est auctor orci mollis luctus. Praesent quis sodales neque. Interdum et malesuada fames ac ante ipsum primis in faucibus. Donec sodales rutrum mattis. In in sem ornare, consequat nulla ac, convallis arcu. Duis ac metus id felis commodo commodo sit amet eget diam. Curabitur rhoncus lacinia leo at sodales. Etiam finibus porta diam a viverra. Praesent nisi urna, volutpat sit amet odio at, vehicula vehicula leo. In non enim eget nisl luctus commodo. Pellentesque pellentesque at lectus at luctus. Quisque nec felis bibendum, lacinia libero ut, lacinia eros. Integer finibus ultricies nibh sit amet placerat.
Nullam scelerisque velit et tortor congue vestibulum a at nisi. Vivamus sodales ut turpis a convallis. In dignissim nibh at luctus sodales. Etiam sit amet rhoncus massa. Phasellus ligula magna, sollicitudin non imperdiet sit amet, volutpat vel magna. Nunc vestibulum tempor lectus, sit amet porta nunc hendrerit in. Curabitur non odio sit amet massa tincidunt facilisis. Integer et luctus nunc, eget euismod leo. Praesent faucibus metus sed purus convallis scelerisque. Fusce viverra lorem et placerat malesuada. In at elit malesuada, ullamcorper risus vitae, sodales dolor. Donec quis elementum lectus. Quisque eu eros at dui imperdiet euismod ut id neque.
diff --git a/tests/reference/guiBuild_Tool_Step2_Lorem_Ipsum.md b/tests/reference/guiBuild_Tool_Step2_Lorem_Ipsum.md index 69f43dcf..a4aab271 100644 --- a/tests/reference/guiBuild_Tool_Step2_Lorem_Ipsum.md +++ b/tests/reference/guiBuild_Tool_Step2_Lorem_Ipsum.md @@ -150,7 +150,8 @@ Integer egestas maximus leo eu facilisis. Nunc rhoncus dignissim lectus eu lacin # Nobody Owens -**Tag:** Bod +**Tag:** Bod +**Plot:** Main Pellentesque nec erat ut nulla posuere commodo. Curabitur nisi augue, imperdiet et porta imperdiet, efficitur id leo. Cras finibus arcu at nibh commodo congue. Proin suscipit placerat condimentum. Aenean ante enim, cursus id lorem a, blandit venenatis nibh. Maecenas suscipit porta elit, sit amet porta felis porttitor eu. Sed a dui nibh. Phasellus sed faucibus dui. Pellentesque felis nulla, ultrices non efficitur quis, rutrum id mi. Mauris tempus auctor nisl, in bibendum enim pellentesque sit amet. Proin nunc lacus, imperdiet nec posuere ac, interdum non lectus. diff --git a/tests/reference/guiBuild_Tool_Step2_Lorem_Ipsum.nwd b/tests/reference/guiBuild_Tool_Step2_Lorem_Ipsum.nwd index 101403fb..9ed47b7e 100644 --- a/tests/reference/guiBuild_Tool_Step2_Lorem_Ipsum.nwd +++ b/tests/reference/guiBuild_Tool_Step2_Lorem_Ipsum.nwd @@ -151,6 +151,7 @@ Integer egestas maximus leo eu facilisis. Nunc rhoncus dignissim lectus eu lacin # Nobody Owens @tag: Bod +@plot: Main Pellentesque nec erat ut nulla posuere commodo. Curabitur nisi augue, imperdiet et porta imperdiet, efficitur id leo. Cras finibus arcu at nibh commodo congue. Proin suscipit placerat condimentum. Aenean ante enim, cursus id lorem a, blandit venenatis nibh. Maecenas suscipit porta elit, sit amet porta felis porttitor eu. Sed a dui nibh. Phasellus sed faucibus dui. Pellentesque felis nulla, ultrices non efficitur quis, rutrum id mi. Mauris tempus auctor nisl, in bibendum enim pellentesque sit amet. Proin nunc lacus, imperdiet nec posuere ac, interdum non lectus. diff --git a/tests/reference/guiBuild_Tool_Step3_Lorem_Ipsum.fodt b/tests/reference/guiBuild_Tool_Step3_Lorem_Ipsum.fodt index 5b2251d7..d519190d 100644 --- a/tests/reference/guiBuild_Tool_Step3_Lorem_Ipsum.fodt +++ b/tests/reference/guiBuild_Tool_Step3_Lorem_Ipsum.fodt @@ -1,8 +1,8 @@Integer egestas maximus leo eu facilisis. Nunc rhoncus dignissim lectus eu lacinia. Praesent lacinia urna porttitor aliquam condimentum. Nulla eu eros dictum, dictum nunc vitae, sagittis nibh. Integer ante neque, consequat nec sollicitudin id, consectetur vitae dolor. Nullam volutpat sem orci, quis viverra magna auctor a. Suspendisse potenti. Maecenas commodo sed neque pellentesque vehicula. Sed luctus nisl risus, elementum semper purus interdum vel. Ut pulvinar, massa sit amet venenatis placerat, nunc lacus hendrerit odio, non aliquet nunc risus eu lectus. Maecenas feugiat semper ligula, id lobortis sem porta eu. Integer posuere elit magna, at mollis eros bibendum et. Ut imperdiet purus vel nulla aliquam maximus. Morbi sodales purus tellus, a rhoncus sem rutrum sit amet. Quisque risus sem, laoreet nec convallis nec, rutrum vitae justo.
Pellentesque nec erat ut nulla posuere commodo. Curabitur nisi augue, imperdiet et porta imperdiet, efficitur id leo. Cras finibus arcu at nibh commodo congue. Proin suscipit placerat condimentum. Aenean ante enim, cursus id lorem a, blandit venenatis nibh. Maecenas suscipit porta elit, sit amet porta felis porttitor eu. Sed a dui nibh. Phasellus sed faucibus dui. Pellentesque felis nulla, ultrices non efficitur quis, rutrum id mi. Mauris tempus auctor nisl, in bibendum enim pellentesque sit amet. Proin nunc lacus, imperdiet nec posuere ac, interdum non lectus.
Suspendisse faucibus est auctor orci mollis luctus. Praesent quis sodales neque. Interdum et malesuada fames ac ante ipsum primis in faucibus. Donec sodales rutrum mattis. In in sem ornare, consequat nulla ac, convallis arcu. Duis ac metus id felis commodo commodo sit amet eget diam. Curabitur rhoncus lacinia leo at sodales. Etiam finibus porta diam a viverra. Praesent nisi urna, volutpat sit amet odio at, vehicula vehicula leo. In non enim eget nisl luctus commodo. Pellentesque pellentesque at lectus at luctus. Quisque nec felis bibendum, lacinia libero ut, lacinia eros. Integer finibus ultricies nibh sit amet placerat.
Nullam scelerisque velit et tortor congue vestibulum a at nisi. Vivamus sodales ut turpis a convallis. In dignissim nibh at luctus sodales. Etiam sit amet rhoncus massa. Phasellus ligula magna, sollicitudin non imperdiet sit amet, volutpat vel magna. Nunc vestibulum tempor lectus, sit amet porta nunc hendrerit in. Curabitur non odio sit amet massa tincidunt facilisis. Integer et luctus nunc, eget euismod leo. Praesent faucibus metus sed purus convallis scelerisque. Fusce viverra lorem et placerat malesuada. In at elit malesuada, ullamcorper risus vitae, sodales dolor. Donec quis elementum lectus. Quisque eu eros at dui imperdiet euismod ut id neque.
diff --git a/tests/reference/guiBuild_Tool_Step3_Lorem_Ipsum.md b/tests/reference/guiBuild_Tool_Step3_Lorem_Ipsum.md index aea72b0a..1fba3815 100644 --- a/tests/reference/guiBuild_Tool_Step3_Lorem_Ipsum.md +++ b/tests/reference/guiBuild_Tool_Step3_Lorem_Ipsum.md @@ -150,7 +150,8 @@ Integer egestas maximus leo eu facilisis. Nunc rhoncus dignissim lectus eu lacin # Nobody Owens -**Tag:** Bod +**Tag:** Bod +**Plot:** Main Pellentesque nec erat ut nulla posuere commodo. Curabitur nisi augue, imperdiet et porta imperdiet, efficitur id leo. Cras finibus arcu at nibh commodo congue. Proin suscipit placerat condimentum. Aenean ante enim, cursus id lorem a, blandit venenatis nibh. Maecenas suscipit porta elit, sit amet porta felis porttitor eu. Sed a dui nibh. Phasellus sed faucibus dui. Pellentesque felis nulla, ultrices non efficitur quis, rutrum id mi. Mauris tempus auctor nisl, in bibendum enim pellentesque sit amet. Proin nunc lacus, imperdiet nec posuere ac, interdum non lectus. diff --git a/tests/reference/guiBuild_Tool_Step3_Lorem_Ipsum.nwd b/tests/reference/guiBuild_Tool_Step3_Lorem_Ipsum.nwd index befeafde..9ed47b7e 100644 --- a/tests/reference/guiBuild_Tool_Step3_Lorem_Ipsum.nwd +++ b/tests/reference/guiBuild_Tool_Step3_Lorem_Ipsum.nwd @@ -76,11 +76,11 @@ Ut et consequat enim, quis ornare nibh. In lectus neque, mollis et suscipit et, % Exctracted from the lipsum.com website. - It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. + It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. - The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English. + The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English. - Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes by accident, sometimes on purpose (injected humour and the like). + Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes by accident, sometimes on purpose (injected humour and the like). ## Chapter Two @@ -151,6 +151,7 @@ Integer egestas maximus leo eu facilisis. Nunc rhoncus dignissim lectus eu lacin # Nobody Owens @tag: Bod +@plot: Main Pellentesque nec erat ut nulla posuere commodo. Curabitur nisi augue, imperdiet et porta imperdiet, efficitur id leo. Cras finibus arcu at nibh commodo congue. Proin suscipit placerat condimentum. Aenean ante enim, cursus id lorem a, blandit venenatis nibh. Maecenas suscipit porta elit, sit amet porta felis porttitor eu. Sed a dui nibh. Phasellus sed faucibus dui. Pellentesque felis nulla, ultrices non efficitur quis, rutrum id mi. Mauris tempus auctor nisl, in bibendum enim pellentesque sit amet. Proin nunc lacus, imperdiet nec posuere ac, interdum non lectus. diff --git a/tests/test_base/test_base_common.py b/tests/test_base/test_base_common.py index f85aaf06..ffc8a4ab 100644 --- a/tests/test_base/test_base_common.py +++ b/tests/test_base/test_base_common.py @@ -28,10 +28,11 @@ from datetime import datetime from tools import writeFile from nw.common import ( - checkString, checkBool, checkInt, formatInt, transferCase, fuzzyTime, - checkHandle, formatTimeStamp, parseTimeStamp, formatTime, hexToInt, - makeFileNameSafe, isHandle, isTitleTag, isItemClass, isItemType, - isItemLayout, numberToRoman, NWConfigParser + checkString, checkInt, checkBool, checkHandle, isHandle, isTitleTag, + isItemClass, isItemType, isItemLayout, hexToInt, formatInt, + formatTimeStamp, formatTime, parseTimeStamp, splitVersionNumber, + transferCase, fuzzyTime, numberToRoman, jsonEncode, makeFileNameSafe, + NWConfigParser ) @@ -253,6 +254,25 @@ def testBaseCommon_ParseTimeStamp(): # END Test testBaseCommon_ParseTimeStamp +@pytest.mark.base +def testBaseCommon_SplitVersionNumber(): + """Test the splitVersionNumber function. + """ + # OK Values + assert splitVersionNumber("1") == [1, 0, 0, 10000] + assert splitVersionNumber("1.2") == [1, 2, 0, 10200] + assert splitVersionNumber("1.2.3") == [1, 2, 3, 10203] + assert splitVersionNumber("1.2.3.4") == [1, 2, 3, 10203] + assert splitVersionNumber("99.99.99") == [99, 99, 99, 999999] + + # Failed Values + assert splitVersionNumber(None) == [0, 0, 0, 0] + assert splitVersionNumber(1234) == [0, 0, 0, 0] + assert splitVersionNumber("1.2abc") == [1, 0, 0, 10000] + +# END Test testBaseCommon_SplitVersionNumber + + @pytest.mark.base def testBaseCommon_FormatInt(): """Test the formatInt function. @@ -368,6 +388,90 @@ def testBaseCommon_RomanNumbers(): # END Test testBaseCommon_RomanNumbers +@pytest.mark.base +def testBaseCommon_JsonEncode(): + """Test the jsonEncode function. + """ + # Wrong type + assert jsonEncode(None) == "[]" + + # Correct types + assert jsonEncode([1, 2]) == "[\n 1,\n 2\n]" + assert jsonEncode((1, 2)) == "[\n 1,\n 2\n]" + assert jsonEncode({1: 2}) == "{\n \"1\": 2\n}" + + tstDict = { + "null": None, + "one": 1, + "two": "2", + "three": 3.0, + "four": False, + "five": (1, 2), + "six": {"a": 1, "b": 2}, + "seven": [], + "eight": {}, + } + + # Complex Structure + assert jsonEncode(tstDict) == ( + '{\n' + ' "null": null,\n' + ' "one": 1,\n' + ' "two": "2",\n' + ' "three": 3.0,\n' + ' "four": false,\n' + ' "five": [\n' + ' 1,\n' + ' 2\n' + ' ],\n' + ' "six": {\n' + ' "a": 1,\n' + ' "b": 2\n' + ' },\n' + ' "seven": [],\n' + ' "eight": {}\n' + '}' + ) + + # Additional Indent + assert jsonEncode(tstDict, n=2) == ( + '{\n' + ' "null": null,\n' + ' "one": 1,\n' + ' "two": "2",\n' + ' "three": 3.0,\n' + ' "four": false,\n' + ' "five": [\n' + ' 1,\n' + ' 2\n' + ' ],\n' + ' "six": {\n' + ' "a": 1,\n' + ' "b": 2\n' + ' },\n' + ' "seven": [],\n' + ' "eight": {}\n' + ' }' + ) + + # Max Indent + assert jsonEncode(tstDict, n=0, nmax=1) == ( + '{\n' + ' "null": null,\n' + ' "one": 1,\n' + ' "two": "2",\n' + ' "three": 3.0,\n' + ' "four": false,\n' + ' "five": [1, 2],\n' + ' "six": {"a": 1, "b": 2},\n' + ' "seven": [],\n' + ' "eight": {}\n' + '}' + ) + +# END Test testBaseCommon_JsonEncode + + @pytest.mark.base def testBaseCommon_NWConfigParser(fncDir): """Test the NWConfigParser subclass. diff --git a/tests/test_core/test_core_index.py b/tests/test_core/test_core_index.py index e82ed90e..c9064563 100644 --- a/tests/test_core/test_core_index.py +++ b/tests/test_core/test_core_index.py @@ -46,8 +46,6 @@ def testCoreIndex_LoadSave(monkeypatch, nwLipsum, mockGUI, outDir, refDir): theProject.projTree.setSeed(42) assert theProject.openProject(nwLipsum) - monkeypatch.setattr("nw.core.index.time", lambda: 123.4) - theIndex = NWIndex(theProject) notIndexable = { "b3643d0f92e32": False, # Novel ROOT @@ -60,64 +58,61 @@ def testCoreIndex_LoadSave(monkeypatch, nwLipsum, mockGUI, outDir, refDir): for tItem in theProject.projTree: assert theIndex.reIndexHandle(tItem.itemHandle) is notIndexable.get(tItem.itemHandle, True) - assert not theIndex.reIndexHandle(None) + assert theIndex.reIndexHandle(None) is False # Make the save fail with monkeypatch.context() as mp: - mp.setattr(json, "dump", causeException) - assert not theIndex.saveIndex() + mp.setattr("builtins.open", causeException) + assert theIndex.saveIndex() is False # Make the save pass - assert theIndex.saveIndex() + assert theIndex.saveIndex() is True # Take a copy of the index tagIndex = str(theIndex._tagIndex) refIndex = str(theIndex._refIndex) - novelIndex = str(theIndex._novelIndex) - noteIndex = str(theIndex._noteIndex) + fileIndex = str(theIndex._fileIndex) textCounts = str(theIndex._textCounts) # Delete a handle assert theIndex._tagIndex.get("Bod", None) is not None assert theIndex._refIndex.get("4c4f28287af27", None) is not None - assert theIndex._noteIndex.get("4c4f28287af27", None) is not None + assert theIndex._fileIndex.get("4c4f28287af27", None) is not None assert theIndex._textCounts.get("4c4f28287af27", None) is not None theIndex.deleteHandle("4c4f28287af27") assert theIndex._tagIndex.get("Bod", None) is None assert theIndex._refIndex.get("4c4f28287af27", None) is None - assert theIndex._noteIndex.get("4c4f28287af27", None) is None + assert theIndex._fileIndex.get("4c4f28287af27", None) is None assert theIndex._textCounts.get("4c4f28287af27", None) is None # Clear the index theIndex.clearIndex() - assert not theIndex._tagIndex - assert not theIndex._refIndex - assert not theIndex._novelIndex - assert not theIndex._noteIndex - assert not theIndex._textCounts + assert theIndex._tagIndex == {} + assert theIndex._refIndex == {} + assert theIndex._fileIndex == {} + assert theIndex._textCounts == {} # Make the load fail with monkeypatch.context() as mp: mp.setattr(json, "load", causeException) - assert not theIndex.loadIndex() + assert theIndex.loadIndex() is False # Make the load pass - assert theIndex.loadIndex() + assert theIndex.loadIndex() is True assert str(theIndex._tagIndex) == tagIndex assert str(theIndex._refIndex) == refIndex - assert str(theIndex._novelIndex) == novelIndex - assert str(theIndex._noteIndex) == noteIndex + assert str(theIndex._fileIndex) == fileIndex assert str(theIndex._textCounts) == textCounts # Break the index and check that we notice - assert not theIndex.indexBroken + assert theIndex.indexBroken is False theIndex._tagIndex["Bod"].append("Stuff") theIndex.checkIndex() - assert theIndex.indexBroken + assert theIndex.indexBroken is True # Finalise - assert theProject.closeProject() + assert theProject.closeProject() is True copyfile(projFile, testFile) assert cmpFiles(testFile, compFile) @@ -131,48 +126,48 @@ def testCoreIndex_ScanThis(nwMinimal, mockGUI): """ theProject = NWProject(mockGUI) theProject.projTree.setSeed(42) - assert theProject.openProject(nwMinimal) + assert theProject.openProject(nwMinimal) is True theIndex = NWIndex(theProject) isValid, theBits, thePos = theIndex.scanThis("tag: this, and this") - assert not isValid + assert isValid is False isValid, theBits, thePos = theIndex.scanThis("@") - assert not isValid + assert isValid is False isValid, theBits, thePos = theIndex.scanThis("@:") - assert not isValid + assert isValid is False isValid, theBits, thePos = theIndex.scanThis(" @a: b") - assert not isValid + assert isValid is False isValid, theBits, thePos = theIndex.scanThis("@a:") - assert isValid + assert isValid is True assert theBits == ["@a"] assert thePos == [0] isValid, theBits, thePos = theIndex.scanThis("@a:b") - assert isValid + assert isValid is True assert theBits == ["@a", "b"] assert thePos == [0, 3] isValid, theBits, thePos = theIndex.scanThis("@a:b,c,d") - assert isValid + assert isValid is True assert theBits == ["@a", "b", "c", "d"] assert thePos == [0, 3, 5, 7] isValid, theBits, thePos = theIndex.scanThis("@a : b , c , d") - assert isValid + assert isValid is True assert theBits == ["@a", "b", "c", "d"] assert thePos == [0, 5, 9, 13] isValid, theBits, thePos = theIndex.scanThis("@tag: this, and this") - assert isValid + assert isValid is True assert theBits == ["@tag", "this", "and this"] assert thePos == [0, 6, 12] - assert theProject.closeProject() + assert theProject.closeProject() is True # END Test testCoreIndex_ScanThis @@ -183,7 +178,7 @@ def testCoreIndex_CheckThese(nwMinimal, mockGUI): """ theProject = NWProject(mockGUI) theProject.projTree.setSeed(42) - assert theProject.openProject(nwMinimal) + assert theProject.openProject(nwMinimal) is True theIndex = NWIndex(theProject) nHandle = theProject.newFile("Hello", nwItemClass.NOVEL, "a508bb932959c") @@ -191,9 +186,9 @@ def testCoreIndex_CheckThese(nwMinimal, mockGUI): nItem = theProject.projTree[nHandle] cItem = theProject.projTree[cHandle] - assert not theIndex.novelChangedSince(0) - assert not theIndex.notesChangedSince(0) - assert not theIndex.indexChangedSince(0) + assert theIndex.novelChangedSince(0) is False + assert theIndex.notesChangedSince(0) is False + assert theIndex.indexChangedSince(0) is False assert theIndex.scanText(cHandle, ( "# Jane Smith\n" @@ -220,21 +215,33 @@ def testCoreIndex_CheckThese(nwMinimal, mockGUI): "@time": [] } - assert theIndex.novelChangedSince(0) - assert theIndex.notesChangedSince(0) - assert theIndex.indexChangedSince(0) + assert theIndex.novelChangedSince(0) is True + assert theIndex.notesChangedSince(0) is True + assert theIndex.indexChangedSince(0) is True + # Zero Items assert theIndex.checkThese([], cItem) == [] - 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] + + # 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] - assert theProject.closeProject() + # 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 # END Test testCoreIndex_CheckThese @@ -245,7 +252,7 @@ def testCoreIndex_ScanText(nwMinimal, mockGUI): """ theProject = NWProject(mockGUI) theProject.projTree.setSeed(42) - assert theProject.openProject(nwMinimal) + assert theProject.openProject(nwMinimal) is True theIndex = NWIndex(theProject) @@ -256,25 +263,25 @@ def testCoreIndex_ScanText(nwMinimal, mockGUI): xItem.setLayout(nwItemLayout.NO_LAYOUT) # Check invalid data - assert not theIndex.scanText(None, "Hello World!") - assert not theIndex.scanText(dHandle, "Hello World!") - assert not theIndex.scanText(xHandle, "Hello World!") + 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.SCENE) xItem.setParent(None) - assert not theIndex.scanText(xHandle, "Hello World!") + assert theIndex.scanText(xHandle, "Hello World!") is False # Create the trash folder tHandle = theProject.trashFolder() assert theProject.projTree[tHandle] is not None xItem.setParent(tHandle) - assert not theIndex.scanText(xHandle, "Hello World!") + assert theIndex.scanText(xHandle, "Hello World!") is False # Create the archive root aHandle = theProject.newRoot("Outtakes", nwItemClass.ARCHIVE) assert theProject.projTree[aHandle] is not None xItem.setParent(aHandle) - assert not theIndex.scanText(xHandle, "Hello World!") + assert theIndex.scanText(xHandle, "Hello World!") is False # Make some usable items pHandle = theProject.newFile("Page", nwItemClass.NOVEL, "a508bb932959c") @@ -315,68 +322,42 @@ def testCoreIndex_ScanText(nwMinimal, mockGUI): "##### Title Five\n\n" # Not interpreted as a title, the hashes are counted as a word "Paragraph Five.\n\n" )) - assert theIndex._refIndex[nHandle].get("T000000", None) is not None # Always there - assert theIndex._refIndex[nHandle].get("T000001", None) is not None # Heading 1 - assert theIndex._refIndex[nHandle].get("T000002", None) is None - assert theIndex._refIndex[nHandle].get("T000003", None) is None - assert theIndex._refIndex[nHandle].get("T000004", None) is None - assert theIndex._refIndex[nHandle].get("T000005", None) is None - assert theIndex._refIndex[nHandle].get("T000006", None) is None - assert theIndex._refIndex[nHandle].get("T000007", None) is not None # Heading 2 - assert theIndex._refIndex[nHandle].get("T000008", None) is None - assert theIndex._refIndex[nHandle].get("T000009", None) is None - assert theIndex._refIndex[nHandle].get("T000010", None) is None - assert theIndex._refIndex[nHandle].get("T000011", None) is None - assert theIndex._refIndex[nHandle].get("T000012", None) is None - assert theIndex._refIndex[nHandle].get("T000013", None) is not None # Heading 3 - assert theIndex._refIndex[nHandle].get("T000014", None) is None - assert theIndex._refIndex[nHandle].get("T000015", None) is None - assert theIndex._refIndex[nHandle].get("T000016", None) is None - assert theIndex._refIndex[nHandle].get("T000017", None) is None - assert theIndex._refIndex[nHandle].get("T000018", None) is None - assert theIndex._refIndex[nHandle].get("T000019", None) is not None # Heading 4 - assert theIndex._refIndex[nHandle].get("T000020", None) is None - assert theIndex._refIndex[nHandle].get("T000021", None) is None - assert theIndex._refIndex[nHandle].get("T000022", None) is None - assert theIndex._refIndex[nHandle].get("T000023", None) is None - assert theIndex._refIndex[nHandle].get("T000024", None) is None - assert theIndex._refIndex[nHandle].get("T000025", None) is None - assert theIndex._refIndex[nHandle].get("T000026", None) is None + assert cHandle not in theIndex._refIndex - assert theIndex._novelIndex[nHandle]["T000001"]["level"] == "H1" - assert theIndex._novelIndex[nHandle]["T000007"]["level"] == "H2" - assert theIndex._novelIndex[nHandle]["T000013"]["level"] == "H3" - assert theIndex._novelIndex[nHandle]["T000019"]["level"] == "H4" + assert theIndex._fileIndex[nHandle]["T000001"]["level"] == "H1" + assert theIndex._fileIndex[nHandle]["T000007"]["level"] == "H2" + assert theIndex._fileIndex[nHandle]["T000013"]["level"] == "H3" + assert theIndex._fileIndex[nHandle]["T000019"]["level"] == "H4" - assert theIndex._novelIndex[nHandle]["T000001"]["title"] == "Title One" - assert theIndex._novelIndex[nHandle]["T000007"]["title"] == "Title Two" - assert theIndex._novelIndex[nHandle]["T000013"]["title"] == "Title Three" - assert theIndex._novelIndex[nHandle]["T000019"]["title"] == "Title Four" + assert theIndex._fileIndex[nHandle]["T000001"]["title"] == "Title One" + assert theIndex._fileIndex[nHandle]["T000007"]["title"] == "Title Two" + assert theIndex._fileIndex[nHandle]["T000013"]["title"] == "Title Three" + assert theIndex._fileIndex[nHandle]["T000019"]["title"] == "Title Four" - assert theIndex._novelIndex[nHandle]["T000001"]["layout"] == "SCENE" - assert theIndex._novelIndex[nHandle]["T000007"]["layout"] == "SCENE" - assert theIndex._novelIndex[nHandle]["T000013"]["layout"] == "SCENE" - assert theIndex._novelIndex[nHandle]["T000019"]["layout"] == "SCENE" + assert theIndex._fileIndex[nHandle]["T000001"]["layout"] == "SCENE" + assert theIndex._fileIndex[nHandle]["T000007"]["layout"] == "SCENE" + assert theIndex._fileIndex[nHandle]["T000013"]["layout"] == "SCENE" + assert theIndex._fileIndex[nHandle]["T000019"]["layout"] == "SCENE" - assert theIndex._novelIndex[nHandle]["T000001"]["synopsis"] == "Synopsis One." - assert theIndex._novelIndex[nHandle]["T000007"]["synopsis"] == "Synopsis Two." - assert theIndex._novelIndex[nHandle]["T000013"]["synopsis"] == "Synopsis Three." - assert theIndex._novelIndex[nHandle]["T000019"]["synopsis"] == "Synopsis Four." + assert theIndex._fileIndex[nHandle]["T000001"]["cCount"] == 23 + assert theIndex._fileIndex[nHandle]["T000007"]["cCount"] == 23 + assert theIndex._fileIndex[nHandle]["T000013"]["cCount"] == 27 + assert theIndex._fileIndex[nHandle]["T000019"]["cCount"] == 56 - assert theIndex._novelIndex[nHandle]["T000001"]["cCount"] == 23 - assert theIndex._novelIndex[nHandle]["T000007"]["cCount"] == 23 - assert theIndex._novelIndex[nHandle]["T000013"]["cCount"] == 27 - assert theIndex._novelIndex[nHandle]["T000019"]["cCount"] == 56 + assert theIndex._fileIndex[nHandle]["T000001"]["wCount"] == 4 + assert theIndex._fileIndex[nHandle]["T000007"]["wCount"] == 4 + assert theIndex._fileIndex[nHandle]["T000013"]["wCount"] == 4 + assert theIndex._fileIndex[nHandle]["T000019"]["wCount"] == 9 - assert theIndex._novelIndex[nHandle]["T000001"]["wCount"] == 4 - assert theIndex._novelIndex[nHandle]["T000007"]["wCount"] == 4 - assert theIndex._novelIndex[nHandle]["T000013"]["wCount"] == 4 - assert theIndex._novelIndex[nHandle]["T000019"]["wCount"] == 9 + assert theIndex._fileIndex[nHandle]["T000001"]["pCount"] == 1 + assert theIndex._fileIndex[nHandle]["T000007"]["pCount"] == 1 + assert theIndex._fileIndex[nHandle]["T000013"]["pCount"] == 1 + assert theIndex._fileIndex[nHandle]["T000019"]["pCount"] == 3 - assert theIndex._novelIndex[nHandle]["T000001"]["pCount"] == 1 - assert theIndex._novelIndex[nHandle]["T000007"]["pCount"] == 1 - assert theIndex._novelIndex[nHandle]["T000013"]["pCount"] == 1 - assert theIndex._novelIndex[nHandle]["T000019"]["pCount"] == 3 + assert theIndex._fileIndex[nHandle]["T000001"]["synopsis"] == "Synopsis One." + assert theIndex._fileIndex[nHandle]["T000007"]["synopsis"] == "Synopsis Two." + assert theIndex._fileIndex[nHandle]["T000013"]["synopsis"] == "Synopsis Three." + assert theIndex._fileIndex[nHandle]["T000019"]["synopsis"] == "Synopsis Four." assert theIndex.scanText(cHandle, ( "# Title One\n\n" @@ -384,22 +365,15 @@ def testCoreIndex_ScanText(nwMinimal, mockGUI): "% synopsis: Synopsis One.\n\n" "Paragraph One.\n\n" )) - assert theIndex._refIndex[cHandle].get("T000000", None) is not None - assert theIndex._refIndex[cHandle].get("T000001", None) is not None - assert theIndex._refIndex[cHandle].get("T000002", None) is None - assert theIndex._refIndex[cHandle].get("T000003", None) is None - assert theIndex._refIndex[cHandle].get("T000004", None) is None - assert theIndex._refIndex[cHandle].get("T000005", None) is None - assert theIndex._refIndex[cHandle].get("T000006", None) is None - assert theIndex._refIndex[cHandle].get("T000007", None) is None + assert cHandle not in theIndex._refIndex - assert theIndex._noteIndex[cHandle]["T000001"]["level"] == "H1" - assert theIndex._noteIndex[cHandle]["T000001"]["title"] == "Title One" - assert theIndex._noteIndex[cHandle]["T000001"]["layout"] == "NOTE" - assert theIndex._noteIndex[cHandle]["T000001"]["synopsis"] == "Synopsis One." - assert theIndex._noteIndex[cHandle]["T000001"]["cCount"] == 23 - assert theIndex._noteIndex[cHandle]["T000001"]["wCount"] == 4 - assert theIndex._noteIndex[cHandle]["T000001"]["pCount"] == 1 + assert theIndex._fileIndex[cHandle]["T000001"]["level"] == "H1" + assert theIndex._fileIndex[cHandle]["T000001"]["title"] == "Title One" + assert theIndex._fileIndex[cHandle]["T000001"]["layout"] == "NOTE" + assert theIndex._fileIndex[cHandle]["T000001"]["cCount"] == 23 + assert theIndex._fileIndex[cHandle]["T000001"]["wCount"] == 4 + assert theIndex._fileIndex[cHandle]["T000001"]["pCount"] == 1 + assert theIndex._fileIndex[cHandle]["T000001"]["synopsis"] == "Synopsis One." assert theIndex.scanText(sHandle, ( "# Title One\n\n" @@ -409,7 +383,7 @@ def testCoreIndex_ScanText(nwMinimal, mockGUI): "% synopsis: Synopsis One.\n\n" "Paragraph One.\n\n" )) - assert theIndex._refIndex[sHandle]["T000001"]["tags"] == ( + assert theIndex._refIndex[sHandle]["T000001"] == ( [[3, "@pov", "One"], [5, "@char", "Two"]] ) @@ -418,29 +392,29 @@ def testCoreIndex_ScanText(nwMinimal, mockGUI): assert theIndex.scanText(pHandle, ( "This is a page with some text on it.\n\n" )) - assert theIndex._novelIndex[pHandle]["T000000"]["level"] == "H0" - assert theIndex._novelIndex[pHandle]["T000000"]["title"] == "Untitled Page" - assert theIndex._novelIndex[pHandle]["T000000"]["layout"] == "PAGE" - assert theIndex._novelIndex[pHandle]["T000000"]["synopsis"] == "" - assert theIndex._novelIndex[pHandle]["T000000"]["cCount"] == 36 - assert theIndex._novelIndex[pHandle]["T000000"]["wCount"] == 9 - assert theIndex._novelIndex[pHandle]["T000000"]["pCount"] == 1 - assert pHandle not in theIndex._noteIndex + assert pHandle in theIndex._fileIndex + assert theIndex._fileIndex[pHandle]["T000000"]["level"] == "H0" + assert theIndex._fileIndex[pHandle]["T000000"]["title"] == "" + assert theIndex._fileIndex[pHandle]["T000000"]["layout"] == "PAGE" + assert theIndex._fileIndex[pHandle]["T000000"]["cCount"] == 36 + assert theIndex._fileIndex[pHandle]["T000000"]["wCount"] == 9 + assert theIndex._fileIndex[pHandle]["T000000"]["pCount"] == 1 + assert theIndex._fileIndex[pHandle]["T000000"]["synopsis"] == "" theProject.projTree[pHandle].itemLayout = nwItemLayout.NOTE assert theIndex.scanText(pHandle, ( "This is a page with some text on it.\n\n" )) - assert theIndex._noteIndex[pHandle]["T000000"]["level"] == "H0" - assert theIndex._noteIndex[pHandle]["T000000"]["title"] == "Untitled Page" - assert theIndex._noteIndex[pHandle]["T000000"]["layout"] == "NOTE" - assert theIndex._noteIndex[pHandle]["T000000"]["synopsis"] == "" - assert theIndex._noteIndex[pHandle]["T000000"]["cCount"] == 36 - assert theIndex._noteIndex[pHandle]["T000000"]["wCount"] == 9 - assert theIndex._noteIndex[pHandle]["T000000"]["pCount"] == 1 - assert pHandle not in theIndex._novelIndex + assert pHandle in theIndex._fileIndex + assert theIndex._fileIndex[pHandle]["T000000"]["level"] == "H0" + assert theIndex._fileIndex[pHandle]["T000000"]["title"] == "" + assert theIndex._fileIndex[pHandle]["T000000"]["layout"] == "NOTE" + assert theIndex._fileIndex[pHandle]["T000000"]["cCount"] == 36 + assert theIndex._fileIndex[pHandle]["T000000"]["wCount"] == 9 + assert theIndex._fileIndex[pHandle]["T000000"]["pCount"] == 1 + assert theIndex._fileIndex[pHandle]["T000000"]["synopsis"] == "" - assert theProject.closeProject() + assert theProject.closeProject() is True # END Test testCoreIndex_ScanText @@ -451,7 +425,7 @@ def testCoreIndex_ExtractData(nwMinimal, mockGUI): """ theProject = NWProject(mockGUI) theProject.projTree.setSeed(42) - assert theProject.openProject(nwMinimal) + assert theProject.openProject(nwMinimal) is True theIndex = NWIndex(theProject) nHandle = theProject.newFile("Hello", nwItemClass.NOVEL, "a508bb932959c") @@ -753,10 +727,8 @@ def testCoreIndex_CheckRefIndex(mockGUI): # Valid Index theIndex._refIndex = { "6a2d6d5f4f401": { - "T000000": {"tags": [], "updated": 1611922868}, - "T000001": {"tags": [ - [3, "@pov", "Jane"], [4, "@location", "Earth"] - ], "updated": 1611922868} + "T000000": [], + "T000001": [[3, "@pov", "Jane"], [4, "@location", "Earth"]], } } assert theIndex._checkRefIndex() is None @@ -764,10 +736,8 @@ def testCoreIndex_CheckRefIndex(mockGUI): # Invalid Handle theIndex._refIndex = { "Ha2d6d5f4f401": { - "T000000": {"tags": [], "updated": 1611922868}, - "T000001": {"tags": [ - [3, "@pov", "Jane"], [4, "@location", "Earth"] - ], "updated": 1611922868} + "T000000": [], + "T000001": [[3, "@pov", "Jane"], [4, "@location", "Earth"]], } } with pytest.raises(KeyError): @@ -776,88 +746,48 @@ def testCoreIndex_CheckRefIndex(mockGUI): # Invalid Title theIndex._refIndex = { "6a2d6d5f4f401": { - "T000000": {"tags": [], "updated": 1611922868}, - "INVALID": {"tags": [ - [3, "@pov", "Jane"], [4, "@location", "Earth"] - ], "updated": 1611922868} + "T000000": [], + "INVALID": [[3, "@pov", "Jane"], [4, "@location", "Earth"]], } } with pytest.raises(KeyError): theIndex._checkRefIndex() - # Missing 'tags' + # Wrong Length theIndex._refIndex = { "6a2d6d5f4f401": { - "T000000": {"tags": [], "updated": 1611922868}, - "T000001": {"updated": 1611922868} - } - } - with pytest.raises(KeyError): - theIndex._checkRefIndex() - - # Wrong Length of 'tags' - theIndex._refIndex = { - "6a2d6d5f4f401": { - "T000000": {"tags": [], "updated": 1611922868}, - "T000001": {"tags": [ - [3, "@pov", "Jane"], [4, "@location", "Earth", "Stuff"] - ], "updated": 1611922868} + "T000000": [], + "T000001": [[3, "@pov", "Jane"], [4, "@location", "Earth", "Stuff"]], } } with pytest.raises(IndexError): theIndex._checkRefIndex() - # Wrong Type of 'tags' Entry 0 + # Wrong Type of Entry 0 theIndex._refIndex = { "6a2d6d5f4f401": { - "T000000": {"tags": [], "updated": 1611922868}, - "T000001": {"tags": [ - [3, "@pov", "Jane"], ["4", "@location", "Earth"] - ], "updated": 1611922868} + "T000000": [], + "T000001": [[3, "@pov", "Jane"], ["4", "@location", "Earth"]], } } with pytest.raises(ValueError): theIndex._checkRefIndex() - # Wrong Type of 'tags' Entry 1 + # Wrong Type of Entry 1 theIndex._refIndex = { "6a2d6d5f4f401": { - "T000000": {"tags": [], "updated": 1611922868}, - "T000001": {"tags": [ - [3, "@pov", "Jane"], [4, "@stuff", "Earth"] - ], "updated": 1611922868} + "T000000": [], + "T000001": [[3, "@pov", "Jane"], [4, "@stuff", "Earth"]], } } with pytest.raises(ValueError): theIndex._checkRefIndex() - # Wrong Type of 'tags' Entry 1 + # Wrong Type of Entry 2 theIndex._refIndex = { "6a2d6d5f4f401": { - "T000000": {"tags": [], "updated": 1611922868}, - "T000001": {"tags": [ - [3, "@pov", "Jane"], [4, "@location", 123456] - ], "updated": 1611922868} - } - } - with pytest.raises(ValueError): - theIndex._checkRefIndex() - - # Missing 'updated' - theIndex._refIndex = { - "6a2d6d5f4f401": { - "T000000": {"tags": [], "updated": 1611922868}, - "T000001": {"tags": []} - } - } - with pytest.raises(KeyError): - theIndex._checkRefIndex() - - # Wrong Type of 'updated' Entry 1 - theIndex._refIndex = { - "6a2d6d5f4f401": { - "T000000": {"tags": [], "updated": 1611922868}, - "T000001": {"tags": [], "updated": "1611922868"} + "T000000": [], + "T000001": [[3, "@pov", "Jane"], [4, "@location", 123456]], } } with pytest.raises(ValueError): @@ -874,253 +804,317 @@ def testCoreIndex_CheckNovelNoteIndex(mockGUI): theIndex = NWIndex(theProject) # Valid Index - theIndex._novelIndex = { + theIndex._fileIndex = { "53b69b83cdafc": { "T000001": { - "level": "H1", "title": "My Novel", "layout": "TITLE", "synopsis": "text", - "cCount": 72, "wCount": 15, "pCount": 2, "updated": 1611922868 + "level": "H1", + "title": "My Novel", + "layout": "TITLE", + "cCount": 72, + "wCount": 15, + "pCount": 2, + "synopsis": "text", } } } - theIndex._noteIndex = theIndex._novelIndex.copy() - assert theIndex._checkNovelNoteIndex("novelIndex") is None - assert theIndex._checkNovelNoteIndex("noteIndex") is None - with pytest.raises(IndexError): - theIndex._checkNovelNoteIndex("notAnIndex") + theIndex._fileIndex = theIndex._fileIndex.copy() + assert theIndex._checkFileIndex() is None # Invalid Handle - theIndex._novelIndex = { + theIndex._fileIndex = { "H3b69b83cdafc": { "T000001": { - "level": "H1", "title": "My Novel", "layout": "TITLE", "synopsis": "text", - "cCount": 72, "wCount": 15, "pCount": 2, "updated": 1611922868 + "level": "H1", + "title": "My Novel", + "layout": "TITLE", + "cCount": 72, + "wCount": 15, + "pCount": 2, + "synopsis": "text", } } } with pytest.raises(KeyError): - theIndex._checkNovelNoteIndex("novelIndex") + theIndex._checkFileIndex() # Invalid Title - theIndex._novelIndex = { + theIndex._fileIndex = { "53b69b83cdafc": { "INVALID": { - "level": "H1", "title": "My Novel", "layout": "TITLE", "synopsis": "text", - "cCount": 72, "wCount": 15, "pCount": 2, "updated": 1611922868 + "level": "H1", + "title": "My Novel", + "layout": "TITLE", + "cCount": 72, + "wCount": 15, + "pCount": 2, + "synopsis": "text", } } } with pytest.raises(KeyError): - theIndex._checkNovelNoteIndex("novelIndex") + theIndex._checkFileIndex() # Wrong Length - theIndex._novelIndex = { + theIndex._fileIndex = { "53b69b83cdafc": { "T000001": { - "level": "H1", "title": "My Novel", "layout": "TITLE", "synopsis": "text", - "cCount": 72, "wCount": 15, "pCount": 2, "updated": 1611922868, "stuff": None + "level": "H1", + "title": "My Novel", + "layout": "TITLE", + "cCount": 72, + "wCount": 15, + "pCount": 2, + "synopsis": "text", + "stuff": None } } } with pytest.raises(IndexError): - theIndex._checkNovelNoteIndex("novelIndex") + theIndex._checkFileIndex() # Missing Keys # ============ # Missing 'level' - theIndex._novelIndex = { + theIndex._fileIndex = { "53b69b83cdafc": { "T000001": { - "stuff": "H1", "title": "My Novel", "layout": "TITLE", "synopsis": "text", - "cCount": 72, "wCount": 15, "pCount": 2, "updated": 1611922868 + "stuff": "H1", + "title": "My Novel", + "layout": "TITLE", + "cCount": 72, + "wCount": 15, + "pCount": 2, + "synopsis": "text", } } } with pytest.raises(KeyError): - theIndex._checkNovelNoteIndex("novelIndex") + theIndex._checkFileIndex() # Missing 'title' - theIndex._novelIndex = { + theIndex._fileIndex = { "53b69b83cdafc": { "T000001": { - "level": "H1", "stuff": "My Novel", "layout": "TITLE", "synopsis": "text", - "cCount": 72, "wCount": 15, "pCount": 2, "updated": 1611922868 + "level": "H1", + "stuff": "My Novel", + "layout": "TITLE", + "cCount": 72, + "wCount": 15, + "pCount": 2, + "synopsis": "text", } } } with pytest.raises(KeyError): - theIndex._checkNovelNoteIndex("novelIndex") + theIndex._checkFileIndex() # Missing 'layout' - theIndex._novelIndex = { + theIndex._fileIndex = { "53b69b83cdafc": { "T000001": { - "level": "H1", "title": "My Novel", "stuff": "TITLE", "synopsis": "text", - "cCount": 72, "wCount": 15, "pCount": 2, "updated": 1611922868 + "level": "H1", + "title": "My Novel", + "stuff": "TITLE", + "cCount": 72, + "wCount": 15, + "pCount": 2, + "synopsis": "text", } } } with pytest.raises(KeyError): - theIndex._checkNovelNoteIndex("novelIndex") - - # Missing 'synopsis' - theIndex._novelIndex = { - "53b69b83cdafc": { - "T000001": { - "level": "H1", "title": "My Novel", "layout": "TITLE", "stuff": "text", - "cCount": 72, "wCount": 15, "pCount": 2, "updated": 1611922868 - } - } - } - with pytest.raises(KeyError): - theIndex._checkNovelNoteIndex("novelIndex") + theIndex._checkFileIndex() # Missing 'cCount' - theIndex._novelIndex = { + theIndex._fileIndex = { "53b69b83cdafc": { "T000001": { - "level": "H1", "title": "My Novel", "layout": "TITLE", "synopsis": "text", - "stuff": 72, "wCount": 15, "pCount": 2, "updated": 1611922868 + "level": "H1", + "title": "My Novel", + "layout": "TITLE", + "stuff": 72, + "wCount": 15, + "pCount": 2, + "synopsis": "text", } } } with pytest.raises(KeyError): - theIndex._checkNovelNoteIndex("novelIndex") + theIndex._checkFileIndex() # Missing 'wCount' - theIndex._novelIndex = { + theIndex._fileIndex = { "53b69b83cdafc": { "T000001": { - "level": "H1", "title": "My Novel", "layout": "TITLE", "synopsis": "text", - "cCount": 72, "stuff": 15, "pCount": 2, "updated": 1611922868 + "level": "H1", + "title": "My Novel", + "layout": "TITLE", + "cCount": 72, + "stuff": 15, + "pCount": 2, + "synopsis": "text", } } } with pytest.raises(KeyError): - theIndex._checkNovelNoteIndex("novelIndex") + theIndex._checkFileIndex() # Missing 'pCount' - theIndex._novelIndex = { + theIndex._fileIndex = { "53b69b83cdafc": { "T000001": { - "level": "H1", "title": "My Novel", "layout": "TITLE", "synopsis": "text", - "cCount": 72, "wCount": 15, "stuff": 2, "updated": 1611922868 + "level": "H1", + "title": "My Novel", + "layout": "TITLE", + "cCount": 72, + "wCount": 15, + "stuff": 2, + "synopsis": "text", } } } with pytest.raises(KeyError): - theIndex._checkNovelNoteIndex("novelIndex") + theIndex._checkFileIndex() - # Missing 'updated' - theIndex._novelIndex = { + # Missing 'synopsis' + theIndex._fileIndex = { "53b69b83cdafc": { "T000001": { - "level": "H1", "title": "My Novel", "layout": "TITLE", "synopsis": "text", - "cCount": 72, "wCount": 15, "pCount": 2, "stuff": 1611922868 + "level": "H1", + "title": "My Novel", + "layout": "TITLE", + "cCount": 72, + "wCount": 15, + "pCount": 2, + "stuff": "text", } } } with pytest.raises(KeyError): - theIndex._checkNovelNoteIndex("novelIndex") + theIndex._checkFileIndex() # Wrong Types # =========== # Wrong Type for 'level' - theIndex._novelIndex = { + theIndex._fileIndex = { "53b69b83cdafc": { "T000001": { - "level": "XX", "title": "My Novel", "layout": "TITLE", "synopsis": "text", - "cCount": 72, "wCount": 15, "pCount": 2, "updated": 1611922868 + "level": "XX", + "title": "My Novel", + "layout": "TITLE", + "cCount": 72, + "wCount": 15, + "pCount": 2, + "synopsis": "text", } } } with pytest.raises(ValueError): - theIndex._checkNovelNoteIndex("novelIndex") + theIndex._checkFileIndex() # Wrong Type for 'title' - theIndex._novelIndex = { + theIndex._fileIndex = { "53b69b83cdafc": { "T000001": { - "level": "H1", "title": 12345678, "layout": "TITLE", "synopsis": "text", - "cCount": 72, "wCount": 15, "pCount": 2, "updated": 1611922868 + "level": "H1", + "title": 12345678, + "layout": "TITLE", + "cCount": 72, + "wCount": 15, + "pCount": 2, + "synopsis": "text", } } } with pytest.raises(ValueError): - theIndex._checkNovelNoteIndex("novelIndex") + theIndex._checkFileIndex() # Wrong Type for 'layout' - theIndex._novelIndex = { + theIndex._fileIndex = { "53b69b83cdafc": { "T000001": { - "level": "H1", "title": "My Novel", "layout": "INVALID", "synopsis": "text", - "cCount": 72, "wCount": 15, "pCount": 2, "updated": 1611922868 + "level": "H1", + "title": "My Novel", + "layout": "INVALID", + "cCount": 72, + "wCount": 15, + "pCount": 2, + "synopsis": "text", } } } with pytest.raises(ValueError): - theIndex._checkNovelNoteIndex("novelIndex") - - # Wrong Type for 'synopsis' - theIndex._novelIndex = { - "53b69b83cdafc": { - "T000001": { - "level": "H1", "title": "My Novel", "layout": "TITLE", "synopsis": 123456, - "cCount": 72, "wCount": 15, "pCount": 2, "updated": 1611922868 - } - } - } - with pytest.raises(ValueError): - theIndex._checkNovelNoteIndex("novelIndex") + theIndex._checkFileIndex() # Wrong Type for 'cCount' - theIndex._novelIndex = { + theIndex._fileIndex = { "53b69b83cdafc": { "T000001": { - "level": "H1", "title": "My Novel", "layout": "TITLE", "synopsis": "text", - "cCount": "72", "wCount": 15, "pCount": 2, "updated": 1611922868 + "level": "H1", + "title": "My Novel", + "layout": "TITLE", + "cCount": "72", + "wCount": 15, + "pCount": 2, + "synopsis": "text", } } } with pytest.raises(ValueError): - theIndex._checkNovelNoteIndex("novelIndex") + theIndex._checkFileIndex() # Wrong Type for 'wCount' - theIndex._novelIndex = { + theIndex._fileIndex = { "53b69b83cdafc": { "T000001": { - "level": "H1", "title": "My Novel", "layout": "TITLE", "synopsis": "text", - "cCount": 72, "wCount": "15", "pCount": 2, "updated": 1611922868 + "level": "H1", + "title": "My Novel", + "layout": "TITLE", + "cCount": 72, + "wCount": "15", + "pCount": 2, + "synopsis": "text", } } } with pytest.raises(ValueError): - theIndex._checkNovelNoteIndex("novelIndex") + theIndex._checkFileIndex() # Wrong Type for 'pCount' - theIndex._novelIndex = { + theIndex._fileIndex = { "53b69b83cdafc": { "T000001": { - "level": "H1", "title": "My Novel", "layout": "TITLE", "synopsis": "text", - "cCount": 72, "wCount": 15, "pCount": "2", "updated": 1611922868 + "level": "H1", + "title": "My Novel", + "layout": "TITLE", + "cCount": 72, + "wCount": 15, + "pCount": "2", + "synopsis": "text", } } } with pytest.raises(ValueError): - theIndex._checkNovelNoteIndex("novelIndex") + theIndex._checkFileIndex() - # Wrong Type for 'updated' - theIndex._novelIndex = { + # Wrong Type for 'synopsis' + theIndex._fileIndex = { "53b69b83cdafc": { "T000001": { - "level": "H1", "title": "My Novel", "layout": "TITLE", "synopsis": "text", - "cCount": 72, "wCount": 15, "pCount": 2, "updated": "1611922868" + "level": "H1", + "title": "My Novel", + "layout": "TITLE", + "cCount": 72, + "wCount": 15, + "pCount": 2, + "synopsis": 123456, } } } with pytest.raises(ValueError): - theIndex._checkNovelNoteIndex("novelIndex") + theIndex._checkFileIndex() # END Test testCoreIndex_CheckNovelNoteIndex diff --git a/tests/test_gui/test_gui_doceditor.py b/tests/test_gui/test_gui_doceditor.py index 75b734eb..37f9be08 100644 --- a/tests/test_gui/test_gui_doceditor.py +++ b/tests/test_gui/test_gui_doceditor.py @@ -1175,7 +1175,7 @@ def testGuiEditor_Search(qtbot, monkeypatch, nwGUI, nwLipsum): qtbot.wait(stepDelay) # Select the Word "est" - assert nwGUI.docEditor.setCursorPosition(618) + assert nwGUI.docEditor.setCursorPosition(630) nwGUI.docEditor._makeSelection(QTextCursor.WordUnderCursor) theCursor = nwGUI.docEditor.textCursor() assert theCursor.selectedText() == "est" @@ -1188,11 +1188,11 @@ def testGuiEditor_Search(qtbot, monkeypatch, nwGUI, nwLipsum): # Find Next by Enter monkeypatch.setattr(nwGUI.docEditor.docSearch.searchBox, "hasFocus", lambda: True) qtbot.keyClick(nwGUI.docEditor.docSearch.searchBox, Qt.Key_Return, delay=keyDelay) - assert abs(nwGUI.docEditor.getCursorPosition() - 1272) < 3 + assert abs(nwGUI.docEditor.getCursorPosition() - 1284) < 3 # Find Next by Button qtbot.mouseClick(nwGUI.docEditor.docSearch.searchButton, Qt.LeftButton, delay=keyDelay) - assert abs(nwGUI.docEditor.getCursorPosition() - 1486) < 3 + assert abs(nwGUI.docEditor.getCursorPosition() - 1498) < 3 # Activate Loop Search nwGUI.docEditor.docSearch.toggleLoop.activate(QAction.Trigger) @@ -1201,17 +1201,17 @@ def testGuiEditor_Search(qtbot, monkeypatch, nwGUI, nwLipsum): # Find Next by Menu Search > Find Next nwGUI.mainMenu.aFindNext.activate(QAction.Trigger) - assert abs(nwGUI.docEditor.getCursorPosition() - 620) < 3 + assert abs(nwGUI.docEditor.getCursorPosition() - 632) < 3 # Close Search nwGUI.docEditor.docSearch.cancelSearch.activate(QAction.Trigger) - assert not nwGUI.docEditor.docSearch.isVisible() + assert nwGUI.docEditor.docSearch.isVisible() is False assert nwGUI.docEditor.setCursorPosition(15) # Toggle Search Again with Header Button qtbot.mouseClick(nwGUI.docEditor.docHeader.searchButton, Qt.LeftButton, delay=keyDelay) assert nwGUI.docEditor.docSearch.setSearchText("") - assert nwGUI.docEditor.docSearch.isVisible() + assert nwGUI.docEditor.docSearch.isVisible() is True # Enable RegEx Search nwGUI.docEditor.docSearch.toggleRegEx.activate(QAction.Trigger) @@ -1226,13 +1226,13 @@ def testGuiEditor_Search(qtbot, monkeypatch, nwGUI, nwLipsum): # Set Valid RegEx assert nwGUI.docEditor.docSearch.setSearchText(r"\bSus") qtbot.mouseClick(nwGUI.docEditor.docSearch.searchButton, Qt.LeftButton, delay=keyDelay) - assert abs(nwGUI.docEditor.getCursorPosition() - 196) < 3 + assert abs(nwGUI.docEditor.getCursorPosition() - 208) < 3 # Find Next and then Prev nwGUI.mainMenu.aFindNext.activate(QAction.Trigger) - assert abs(nwGUI.docEditor.getCursorPosition() - 297) < 3 + assert abs(nwGUI.docEditor.getCursorPosition() - 309) < 3 nwGUI.mainMenu.aFindPrev.activate(QAction.Trigger) - assert abs(nwGUI.docEditor.getCursorPosition() - 196) < 3 + assert abs(nwGUI.docEditor.getCursorPosition() - 208) < 3 # Make RegEx Case Sensitive nwGUI.docEditor.docSearch.toggleCase.activate(QAction.Trigger) @@ -1241,9 +1241,9 @@ def testGuiEditor_Search(qtbot, monkeypatch, nwGUI, nwLipsum): # Find Next (One Result) nwGUI.mainMenu.aFindNext.activate(QAction.Trigger) - assert abs(nwGUI.docEditor.getCursorPosition() - 599) < 3 + assert abs(nwGUI.docEditor.getCursorPosition() - 611) < 3 nwGUI.mainMenu.aFindNext.activate(QAction.Trigger) - assert abs(nwGUI.docEditor.getCursorPosition() - 599) < 3 + assert abs(nwGUI.docEditor.getCursorPosition() - 611) < 3 # Trigger Replace nwGUI.mainMenu.aReplace.activate(QAction.Trigger) @@ -1261,14 +1261,14 @@ def testGuiEditor_Search(qtbot, monkeypatch, nwGUI, nwLipsum): # Replace "Sus" with "Foo" via Menu nwGUI.mainMenu.aReplaceNext.activate(QAction.Trigger) - assert nwGUI.docEditor.getText()[596:607] == "Foopendisse" + assert nwGUI.docEditor.getText()[608:619] == "Foopendisse" # Find Next to Loop File nwGUI.mainMenu.aFindNext.activate(QAction.Trigger) # Replace "sus" with "foo" via Replace Button qtbot.mouseClick(nwGUI.docEditor.docSearch.replaceButton, Qt.LeftButton, delay=keyDelay) - assert nwGUI.docEditor.getText()[193:201] == "foocipit" + assert nwGUI.docEditor.getText()[205:213] == "foocipit" # Revert Last Two Replaces assert nwGUI.docEditor.docAction(nwDocAction.UNDO) @@ -1282,7 +1282,7 @@ def testGuiEditor_Search(qtbot, monkeypatch, nwGUI, nwLipsum): # Close Search and Select "est" Again nwGUI.docEditor.docSearch.cancelSearch.activate(QAction.Trigger) - assert nwGUI.docEditor.setCursorPosition(618) + assert nwGUI.docEditor.setCursorPosition(630) nwGUI.docEditor._makeSelection(QTextCursor.WordUnderCursor) theCursor = nwGUI.docEditor.textCursor() assert theCursor.selectedText() == "est" @@ -1299,9 +1299,9 @@ def testGuiEditor_Search(qtbot, monkeypatch, nwGUI, nwLipsum): # Only One Match nwGUI.mainMenu.aFindNext.activate(QAction.Trigger) - assert abs(nwGUI.docEditor.getCursorPosition() - 620) < 3 + assert abs(nwGUI.docEditor.getCursorPosition() - 632) < 3 nwGUI.mainMenu.aFindNext.activate(QAction.Trigger) - assert abs(nwGUI.docEditor.getCursorPosition() - 620) < 3 + assert abs(nwGUI.docEditor.getCursorPosition() - 632) < 3 # Enable Next Doc Search nwGUI.docEditor.docSearch.toggleProject.activate(QAction.Trigger) diff --git a/tests/test_gui/test_gui_mainmenu.py b/tests/test_gui/test_gui_mainmenu.py index 1abd1dab..7c10f34d 100644 --- a/tests/test_gui/test_gui_mainmenu.py +++ b/tests/test_gui/test_gui_mainmenu.py @@ -54,35 +54,35 @@ def testGuiMenu_EditFormat(qtbot, monkeypatch, nwGUI, nwLipsum): # Split By Chapter assert nwGUI.openDocument("4c4f28287af27") is True - assert nwGUI.docEditor.setCursorPosition(30) is True + assert nwGUI.docEditor.setCursorPosition(42) is True - cleanText = nwGUI.docEditor.getText()[27:74] + cleanText = nwGUI.docEditor.getText()[39:86] # Bold nwGUI.mainMenu.aFmtStrong.activate(QAction.Trigger) fmtStr = "**Pellentesque** nec erat ut nulla posuere commodo." - assert nwGUI.docEditor.getText()[27:78] == fmtStr + assert nwGUI.docEditor.getText()[39:90] == fmtStr qtbot.wait(stepDelay) nwGUI.mainMenu.aFmtStrong.activate(QAction.Trigger) - assert nwGUI.docEditor.getText()[27:74] == cleanText + assert nwGUI.docEditor.getText()[39:86] == cleanText qtbot.wait(stepDelay) # Italic nwGUI.mainMenu.aFmtEmph.activate(QAction.Trigger) fmtStr = "_Pellentesque_ nec erat ut nulla posuere commodo." - assert nwGUI.docEditor.getText()[27:76] == fmtStr + assert nwGUI.docEditor.getText()[39:88] == fmtStr qtbot.wait(stepDelay) nwGUI.mainMenu.aFmtEmph.activate(QAction.Trigger) - assert nwGUI.docEditor.getText()[27:74] == cleanText + assert nwGUI.docEditor.getText()[39:86] == cleanText qtbot.wait(stepDelay) # Strikethrough nwGUI.mainMenu.aFmtStrike.activate(QAction.Trigger) fmtStr = "~~Pellentesque~~ nec erat ut nulla posuere commodo." - assert nwGUI.docEditor.getText()[27:78] == fmtStr + assert nwGUI.docEditor.getText()[39:90] == fmtStr qtbot.wait(stepDelay) nwGUI.mainMenu.aFmtStrike.activate(QAction.Trigger) - assert nwGUI.docEditor.getText()[27:74] == cleanText + assert nwGUI.docEditor.getText()[39:86] == cleanText qtbot.wait(stepDelay) # Should get us back to plain @@ -93,122 +93,122 @@ def testGuiMenu_EditFormat(qtbot, monkeypatch, nwGUI, nwLipsum): nwGUI.mainMenu.aFmtEmph.activate(QAction.Trigger) qtbot.wait(stepDelay) nwGUI.mainMenu.aFmtStrong.activate(QAction.Trigger) - assert nwGUI.docEditor.getText()[27:74] == cleanText + assert nwGUI.docEditor.getText()[39:86] == cleanText qtbot.wait(stepDelay) # Double Quotes nwGUI.mainMenu.aFmtDQuote.activate(QAction.Trigger) fmtStr = "“Pellentesque” nec erat ut nulla posuere commodo." - assert nwGUI.docEditor.getText()[27:76] == fmtStr + assert nwGUI.docEditor.getText()[39:88] == fmtStr qtbot.wait(stepDelay) nwGUI.mainMenu.aEditUndo.activate(QAction.Trigger) - assert nwGUI.docEditor.getText()[27:74] == cleanText + assert nwGUI.docEditor.getText()[39:86] == cleanText qtbot.wait(stepDelay) # Single Quotes nwGUI.mainMenu.aFmtSQuote.activate(QAction.Trigger) fmtStr = "‘Pellentesque’ nec erat ut nulla posuere commodo." - assert nwGUI.docEditor.getText()[27:76] == fmtStr + assert nwGUI.docEditor.getText()[39:88] == fmtStr qtbot.wait(stepDelay) nwGUI.mainMenu.aEditUndo.activate(QAction.Trigger) - assert nwGUI.docEditor.getText()[27:74] == cleanText + assert nwGUI.docEditor.getText()[39:86] == cleanText qtbot.wait(stepDelay) # Block Formats # ============= - assert nwGUI.docEditor.setCursorPosition(30) + assert nwGUI.docEditor.setCursorPosition(42) # Header 1 nwGUI.mainMenu.aFmtHead1.activate(QAction.Trigger) fmtStr = "# Pellentesque nec erat ut nulla posuere commodo." - assert nwGUI.docEditor.getText()[27:76] == fmtStr + assert nwGUI.docEditor.getText()[39:88] == fmtStr qtbot.wait(stepDelay) # Header 2 nwGUI.mainMenu.aFmtHead2.activate(QAction.Trigger) fmtStr = "## Pellentesque nec erat ut nulla posuere commodo." - assert nwGUI.docEditor.getText()[27:77] == fmtStr + assert nwGUI.docEditor.getText()[39:89] == fmtStr qtbot.wait(stepDelay) # Header 3 nwGUI.mainMenu.aFmtHead3.activate(QAction.Trigger) fmtStr = "### Pellentesque nec erat ut nulla posuere commodo." - assert nwGUI.docEditor.getText()[27:78] == fmtStr + assert nwGUI.docEditor.getText()[39:90] == fmtStr qtbot.wait(stepDelay) # Header 4 nwGUI.mainMenu.aFmtHead4.activate(QAction.Trigger) fmtStr = "#### Pellentesque nec erat ut nulla posuere commodo." - assert nwGUI.docEditor.getText()[27:79] == fmtStr + assert nwGUI.docEditor.getText()[39:91] == fmtStr qtbot.wait(stepDelay) # Clear Format nwGUI.mainMenu.aFmtNoFormat.activate(QAction.Trigger) - assert nwGUI.docEditor.getText()[27:74] == cleanText + assert nwGUI.docEditor.getText()[39:86] == cleanText qtbot.wait(stepDelay) # Comment On nwGUI.mainMenu.aFmtComment.activate(QAction.Trigger) fmtStr = "% Pellentesque nec erat ut nulla posuere commodo." - assert nwGUI.docEditor.getText()[27:76] == fmtStr + assert nwGUI.docEditor.getText()[39:88] == fmtStr qtbot.wait(stepDelay) # Comment Off nwGUI.mainMenu.aFmtComment.activate(QAction.Trigger) - assert nwGUI.docEditor.getText()[27:74] == cleanText + assert nwGUI.docEditor.getText()[39:86] == cleanText qtbot.wait(stepDelay) # Check comment with no space before text - assert nwGUI.docEditor.setCursorPosition(27) + assert nwGUI.docEditor.setCursorPosition(39) assert nwGUI.docEditor.insertText("%") fmtStr = "%Pellentesque nec erat ut nulla posuere commodo." - assert nwGUI.docEditor.getText()[27:75] == fmtStr + assert nwGUI.docEditor.getText()[39:87] == fmtStr qtbot.wait(stepDelay) nwGUI.mainMenu.aFmtNoFormat.activate(QAction.Trigger) - assert nwGUI.docEditor.getText()[27:74] == cleanText + assert nwGUI.docEditor.getText()[39:86] == cleanText qtbot.wait(stepDelay) # Undo/Redo nwGUI.mainMenu.aEditUndo.activate(QAction.Trigger) fmtStr = "%Pellentesque nec erat ut nulla posuere commodo." - assert nwGUI.docEditor.getText()[27:75] == fmtStr + assert nwGUI.docEditor.getText()[39:87] == fmtStr qtbot.wait(stepDelay) nwGUI.mainMenu.aEditRedo.activate(QAction.Trigger) - assert nwGUI.docEditor.getText()[27:74] == cleanText + assert nwGUI.docEditor.getText()[39:86] == cleanText qtbot.wait(stepDelay) # Cut, Copy and Paste - assert nwGUI.docEditor.setCursorPosition(27) + assert nwGUI.docEditor.setCursorPosition(39) nwGUI.docEditor._makeSelection(QTextCursor.WordUnderCursor) nwGUI.mainMenu.aEditCut.activate(QAction.Trigger) - assert nwGUI.docEditor.getText()[27:77] == ( + assert nwGUI.docEditor.getText()[39:89] == ( " nec erat ut nulla posuere commodo. Curabitur nisi" ) nwGUI.mainMenu.aEditPaste.activate(QAction.Trigger) - assert nwGUI.docEditor.getText()[27:77] == ( + assert nwGUI.docEditor.getText()[39:89] == ( "Pellentesque nec erat ut nulla posuere commodo. Cu" ) - assert nwGUI.docEditor.setCursorPosition(27) + assert nwGUI.docEditor.setCursorPosition(39) nwGUI.docEditor._makeSelection(QTextCursor.WordUnderCursor) nwGUI.mainMenu.aEditCopy.activate(QAction.Trigger) - assert nwGUI.docEditor.getText()[27:77] == ( + assert nwGUI.docEditor.getText()[39:89] == ( "Pellentesque nec erat ut nulla posuere commodo. Cu" ) - assert nwGUI.docEditor.setCursorPosition(27) + assert nwGUI.docEditor.setCursorPosition(39) nwGUI.mainMenu.aEditPaste.activate(QAction.Trigger) - assert nwGUI.docEditor.getText()[27:77] == ( + assert nwGUI.docEditor.getText()[39:89] == ( "PellentesquePellentesque nec erat ut nulla posuere" ) nwGUI.mainMenu.aEditUndo.activate(QAction.Trigger) # Select Paragraph/All - assert nwGUI.docEditor.setCursorPosition(30) + assert nwGUI.docEditor.setCursorPosition(42) nwGUI.mainMenu.aSelectPar.activate(QAction.Trigger) theCursor = nwGUI.docEditor.textCursor() assert theCursor.selectedText() == ( @@ -221,10 +221,10 @@ def testGuiMenu_EditFormat(qtbot, monkeypatch, nwGUI, nwLipsum): "nunc lacus, imperdiet nec posuere ac, interdum non lectus." ) - assert nwGUI.docEditor.setCursorPosition(30) + assert nwGUI.docEditor.setCursorPosition(42) nwGUI.mainMenu.aSelectAll.activate(QAction.Trigger) theCursor = nwGUI.docEditor.textCursor() - assert len(theCursor.selectedText()) == 1883 + assert len(theCursor.selectedText()) == 1895 # Clear the Text nwGUI.docEditor.clear() @@ -388,7 +388,7 @@ def testGuiMenu_ContextMenus(qtbot, monkeypatch, nwGUI, nwLipsum): # Editor Context Menu theCursor = nwGUI.docEditor.textCursor() - theCursor.setPosition(100) + theCursor.setPosition(112) nwGUI.docEditor.setTextCursor(theCursor) theRect = nwGUI.docEditor.cursorRect() @@ -415,7 +415,7 @@ def testGuiMenu_ContextMenus(qtbot, monkeypatch, nwGUI, nwLipsum): assert nwGUI.viewDocument("4c4f28287af27") theCursor = nwGUI.docViewer.textCursor() - theCursor.setPosition(100) + theCursor.setPosition(112) nwGUI.docViewer.setTextCursor(theCursor) theRect = nwGUI.docViewer.cursorRect()