Пример #1
0
def testCoreProject_NewRoot(fncDir, outDir, refDir, mockGUI):
    """Check that new root folders can be added to the project.
    """
    projFile = os.path.join(fncDir, "nwProject.nwx")
    testFile = os.path.join(outDir, "coreProject_NewRoot_nwProject.nwx")
    compFile = os.path.join(refDir, "coreProject_NewRoot_nwProject.nwx")

    theProject = NWProject(mockGUI)
    theProject.projTree.setSeed(42)

    assert theProject.newProject({"projPath": fncDir}) is True
    assert theProject.setProjectPath(fncDir) is True
    assert theProject.saveProject() is True
    assert theProject.closeProject() is True
    assert theProject.openProject(projFile) is True

    assert isinstance(theProject.newRoot("Novel", nwItemClass.NOVEL),
                      type(None))
    assert isinstance(theProject.newRoot("Plot", nwItemClass.PLOT), type(None))
    assert isinstance(theProject.newRoot("Character", nwItemClass.CHARACTER),
                      type(None))
    assert isinstance(theProject.newRoot("World", nwItemClass.WORLD),
                      type(None))
    assert isinstance(theProject.newRoot("Timeline", nwItemClass.TIMELINE),
                      str)
    assert isinstance(theProject.newRoot("Object", nwItemClass.OBJECT), str)
    assert isinstance(theProject.newRoot("Custom1", nwItemClass.CUSTOM), str)
    assert isinstance(theProject.newRoot("Custom2", nwItemClass.CUSTOM), str)

    assert theProject.projChanged is True
    assert theProject.saveProject() is True
    assert theProject.closeProject() is True

    copyfile(projFile, testFile)
    assert cmpFiles(testFile, compFile, [2, 6, 7, 8])
    assert theProject.projChanged is False
Пример #2
0
def testCoreIndex_ScanText(nwMinimal, mockGUI):
    """Check the index text scanner.
    """
    theProject = NWProject(mockGUI)
    theProject.projTree.setSeed(42)
    assert theProject.openProject(nwMinimal) is True

    theIndex = NWIndex(theProject)

    # Some items for fail to scan tests
    dHandle = theProject.newFolder("Folder", nwItemClass.NOVEL,
                                   "a508bb932959c")
    xHandle = theProject.newFile("No Layout", nwItemClass.NOVEL,
                                 "a508bb932959c")
    xItem = theProject.projTree[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.projTree[tHandle] is not None
    xItem.setParent(tHandle)
    assert theIndex.scanText(xHandle, "Hello World!") is False

    # Create the archive root
    aHandle = theProject.newRoot("Archive", nwItemClass.ARCHIVE)
    assert theProject.projTree[aHandle] is not None
    xItem.setParent(aHandle)
    assert theIndex.scanText(xHandle, "Hello World!") is False

    # Make some usable items
    tHandle = theProject.newFile("Title", nwItemClass.NOVEL, "a508bb932959c")
    pHandle = theProject.newFile("Page", nwItemClass.NOVEL, "a508bb932959c")
    nHandle = theProject.newFile("Hello", nwItemClass.NOVEL, "a508bb932959c")
    cHandle = theProject.newFile("Jane", nwItemClass.CHARACTER,
                                 "afb3043c7b2b3")
    sHandle = theProject.newFile("Scene", nwItemClass.NOVEL, "a508bb932959c")

    # 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._tagIndex == {"Jane": [2, cHandle, "CHARACTER", "T000001"]}
    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 nHandle not in theIndex._refIndex

    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._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._fileIndex[nHandle]["T000001"]["layout"] == "DOCUMENT"
    assert theIndex._fileIndex[nHandle]["T000007"]["layout"] == "DOCUMENT"
    assert theIndex._fileIndex[nHandle]["T000013"]["layout"] == "DOCUMENT"
    assert theIndex._fileIndex[nHandle]["T000019"]["layout"] == "DOCUMENT"

    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._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._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._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."

    # Note File
    assert theIndex.scanText(cHandle, ("# Title One\n\n"
                                       "@tag: One\n\n"
                                       "% synopsis: Synopsis One.\n\n"
                                       "Paragraph One.\n\n"))
    assert cHandle not in theIndex._refIndex

    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."

    # 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._refIndex[sHandle]["T000001"] == ([[3, "@pov", "One"],
                                                       [5, "@char", "Two"]])

    # Special Titles
    # ==============

    assert theIndex.scanText(tHandle, ("#! My Project\n\n"
                                       ">> By Jane Doe <<\n\n"))
    assert tHandle not in theIndex._refIndex

    assert theIndex._fileIndex[tHandle]["T000001"]["level"] == "H1"
    assert theIndex._fileIndex[tHandle]["T000001"]["title"] == "My Project"
    assert theIndex._fileIndex[tHandle]["T000001"]["layout"] == "DOCUMENT"
    assert theIndex._fileIndex[tHandle]["T000001"]["cCount"] == 21
    assert theIndex._fileIndex[tHandle]["T000001"]["wCount"] == 5
    assert theIndex._fileIndex[tHandle]["T000001"]["pCount"] == 1
    assert theIndex._fileIndex[tHandle]["T000001"]["synopsis"] == ""

    assert theIndex.scanText(tHandle,
                             ("##! Prologue\n\n"
                              "In the beginning there was time ...\n\n"))
    assert tHandle not in theIndex._refIndex

    assert theIndex._fileIndex[tHandle]["T000001"]["level"] == "H2"
    assert theIndex._fileIndex[tHandle]["T000001"]["title"] == "Prologue"
    assert theIndex._fileIndex[tHandle]["T000001"]["layout"] == "DOCUMENT"
    assert theIndex._fileIndex[tHandle]["T000001"]["cCount"] == 43
    assert theIndex._fileIndex[tHandle]["T000001"]["wCount"] == 8
    assert theIndex._fileIndex[tHandle]["T000001"]["pCount"] == 1
    assert theIndex._fileIndex[tHandle]["T000001"]["synopsis"] == ""

    # Page wo/Title
    # =============

    theProject.projTree[pHandle]._layout = nwItemLayout.DOCUMENT
    assert theIndex.scanText(pHandle,
                             ("This is a page with some text on it.\n\n"))
    assert pHandle in theIndex._fileIndex
    assert theIndex._fileIndex[pHandle]["T000000"]["level"] == "H0"
    assert theIndex._fileIndex[pHandle]["T000000"]["title"] == ""
    assert theIndex._fileIndex[pHandle]["T000000"]["layout"] == "DOCUMENT"
    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]._layout = nwItemLayout.NOTE
    assert theIndex.scanText(pHandle,
                             ("This is a page with some text on it.\n\n"))
    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() is True
Пример #3
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"
                }
            },
        }
    })
Пример #4
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