Use the random generator for item handles (#1044)

This commit is contained in:
Veronica Berglyd Olsen
2022-04-20 22:00:34 +02:00
committed by GitHub
34 changed files with 774 additions and 839 deletions
+4 -4
View File
@@ -978,12 +978,12 @@ class Config:
"""
try:
import enchant # noqa: F401
self.hasEnchant = True
logger.debug("Checking package 'pyenchant': OK")
except Exception:
except ImportError:
self.hasEnchant = False
logger.debug("Checking package 'pyenchant': Missing")
else:
self.hasEnchant = True
logger.debug("Checking package 'pyenchant': OK")
return
# END Class Config
+8 -33
View File
@@ -24,11 +24,10 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
import os
import random
import logging
from time import time
from lxml import etree
from hashlib import sha256
from novelwriter.enum import nwItemType, nwItemClass, nwItemLayout
from novelwriter.error import logException
@@ -55,9 +54,6 @@ class NWTree():
self._theIndex = 0 # The current iterator index
self._treeChanged = False # True if tree structure has changed
self._handleSeed = None # Used for generating handles for testing
self._handleCount = 0 # A counter that is added to the handle generator
return
##
@@ -340,14 +336,6 @@ class NWTree():
return
def setSeed(self, theSeed):
"""Used for debugging!
Sets a seed for generating handles so that they always come out
in a predictable order.
"""
self._handleSeed = theSeed
return
def setFileItemLayout(self, tHandle, itemLayout):
"""Set the nwItemLayout for a specific file.
"""
@@ -474,29 +462,16 @@ class NWTree():
self.theProject.setProjectChanged(True)
return
def _makeHandle(self, addSeed=""):
def _makeHandle(self):
"""Generate a unique item handle. In the event that the key
already exists, salt the seed and generate a new handle.
A key collision is very unlikely to be caused by the truncation
of the sha256 hash to 13 characters. Assuming it is near-random,
it will on average happen every 4.5^15 times. However, the clock
seed is likely to occasionally generate a collision if the
handle requests come faster than the clock resolution.
already exists, generate a new one.
"""
if self._handleSeed is None:
newSeed = "%s_%d_%s" % (str(time()), self._handleCount, addSeed)
self._handleCount += 1
else:
# This is used for debugging
newSeed = str(self._handleSeed)
self._handleSeed += 1
logger.verbose("Generating handle with seed '%s'", newSeed)
itemHandle = sha256(newSeed.encode()).hexdigest()[0:13]
if itemHandle in self._projTree:
logger.verbose("Generating new handle")
handle = f"{random.getrandbits(52):013x}"
if handle in self._projTree:
logger.warning("Duplicate handle encountered! Retrying ...")
itemHandle = self._makeHandle(addSeed+"!")
handle = self._makeHandle()
return itemHandle
return handle
# END Class NWTree
+2
View File
@@ -87,6 +87,8 @@ class NWErrorMessage(QDialog):
self.mainBox.addWidget(self.btnBox, 2, 0, 1, 2)
self.mainBox.setSpacing(16)
# Pick a random window title from a set of error messages by
# Hex, the computer, from Discworld
self.setWindowTitle([
"+++ Out of Cheese Error +++",
"+++ Divide by Cucumber Error +++",
+20 -21
View File
@@ -24,8 +24,6 @@ import sys
import pytest
import shutil
from dataclasses import dataclass
from mock import MockGuiMain
from tools import cleanProject
@@ -184,6 +182,26 @@ def nwGUI(qtbot, monkeypatch, fncDir, fncConf):
return
##
# Python Objects
##
@pytest.fixture(scope="function")
def mockRnd(monkeypatch):
"""Create a mock random number generator that just counts upwards
from 0. This one will generate status/importance flags and handles
in a predictable sequence.
"""
def rnd(n):
for x in range(n):
yield x
gen = rnd(1000)
monkeypatch.setattr("random.getrandbits", lambda *a: next(gen))
return
##
# Temp Project Folders
##
@@ -252,25 +270,6 @@ def nwOldProj(tmpDir):
return
##
# Data Fixtures
##
@dataclass
class TestConst:
statusKeys = ["sa3b179", "s1c8031", "s06671a", "sbdd640"]
importKeys = ["i466852", "i3eb13b", "i392456", "i23b8c1"]
@pytest.fixture(scope="session")
def constData():
"""A named tuple of known contstant values. For those that depend on
the random number generator, they assume the seed is 42.
"""
return TestConst()
@pytest.fixture(scope="session")
def ipsumText():
"""Return five paragraphs of Lorem Ipsum text.
@@ -1,5 +1,5 @@
<?xml version='1.0' encoding='utf-8'?>
<novelWriterXML appVersion="1.7-alpha0" hexVersion="0x010700a0" fileVersion="1.4" timeStamp="2022-04-16 20:40:13">
<novelWriterXML appVersion="1.7-alpha0" hexVersion="0x010700a0" fileVersion="1.4" timeStamp="2022-04-20 20:01:32">
<project>
<name>Test Custom</name>
<title>Test Novel</title>
@@ -29,110 +29,110 @@
<section></section>
</titleFormat>
<status>
<entry key="sbc8960" count="17" red="100" green="100" blue="100">New</entry>
<entry key="s1a3d1f" count="0" red="200" green="50" blue="0">Note</entry>
<entry key="sad3c2d" count="0" red="200" green="150" blue="0">Draft</entry>
<entry key="sbd9c66" count="0" red="50" green="200" blue="0">Finished</entry>
<entry key="s000008" count="17" red="100" green="100" blue="100">New</entry>
<entry key="s000009" count="0" red="200" green="50" blue="0">Note</entry>
<entry key="s00000a" count="0" red="200" green="150" blue="0">Draft</entry>
<entry key="s00000b" count="0" red="50" green="200" blue="0">Finished</entry>
</status>
<importance>
<entry key="ie465e1" count="6" red="100" green="100" blue="100">New</entry>
<entry key="i8b9d24" count="0" red="200" green="50" blue="0">Minor</entry>
<entry key="i16419f" count="0" red="200" green="150" blue="0">Major</entry>
<entry key="i972a84" count="0" red="50" green="200" blue="0">Main</entry>
<entry key="i00000c" count="6" red="100" green="100" blue="100">New</entry>
<entry key="i00000d" count="0" red="200" green="50" blue="0">Minor</entry>
<entry key="i00000e" count="0" red="200" green="150" blue="0">Major</entry>
<entry key="i00000f" count="0" red="50" green="200" blue="0">Main</entry>
</importance>
</settings>
<content count="23">
<item handle="73475cb40a568" parent="None" root="73475cb40a568" order="0" type="ROOT" class="NOVEL">
<item handle="0000000000010" parent="None" root="0000000000010" order="0" type="ROOT" class="NOVEL">
<meta expanded="False"/>
<name status="sbc8960" import="ie465e1">Novel</name>
<name status="s000008" import="i00000c">Novel</name>
</item>
<item handle="44cb730c42048" parent="None" root="44cb730c42048" order="0" type="ROOT" class="PLOT">
<item handle="0000000000011" parent="None" root="0000000000011" order="0" type="ROOT" class="PLOT">
<meta expanded="False"/>
<name status="sbc8960" import="ie465e1">Plot</name>
<name status="s000008" import="i00000c">Plot</name>
</item>
<item handle="71ee45a3c0db9" parent="None" root="71ee45a3c0db9" order="0" type="ROOT" class="CHARACTER">
<item handle="0000000000012" parent="None" root="0000000000012" order="0" type="ROOT" class="CHARACTER">
<meta expanded="False"/>
<name status="sbc8960" import="ie465e1">Characters</name>
<name status="s000008" import="i00000c">Characters</name>
</item>
<item handle="811786ad1ae74" parent="None" root="811786ad1ae74" order="0" type="ROOT" class="WORLD">
<item handle="0000000000013" parent="None" root="0000000000013" order="0" type="ROOT" class="WORLD">
<meta expanded="False"/>
<name status="sbc8960" import="ie465e1">Locations</name>
<name status="s000008" import="i00000c">Locations</name>
</item>
<item handle="25fc0e7096fc6" parent="None" root="25fc0e7096fc6" order="0" type="ROOT" class="TIMELINE">
<item handle="0000000000014" parent="None" root="0000000000014" order="0" type="ROOT" class="TIMELINE">
<meta expanded="False"/>
<name status="sbc8960" import="ie465e1">Timeline</name>
<name status="s000008" import="i00000c">Timeline</name>
</item>
<item handle="31489056e0916" parent="None" root="31489056e0916" order="0" type="ROOT" class="OBJECT">
<item handle="0000000000015" parent="None" root="0000000000015" order="0" type="ROOT" class="OBJECT">
<meta expanded="False"/>
<name status="sbc8960" import="ie465e1">Objects</name>
<name status="s000008" import="i00000c">Objects</name>
</item>
<item handle="98010bd9270f9" parent="None" root="98010bd9270f9" order="0" type="ROOT" class="ENTITY">
<item handle="0000000000016" parent="None" root="0000000000016" order="0" type="ROOT" class="ENTITY">
<meta expanded="False"/>
<name status="sbc8960" import="ie465e1">Entities</name>
<name status="s000008" import="i00000c">Entities</name>
</item>
<item handle="0e17daca5f3e1" parent="73475cb40a568" root="73475cb40a568" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<item handle="0000000000017" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="sbc8960" import="ie465e1" exported="True">Title Page</name>
<name status="s000008" import="i00000c" exported="True">Title Page</name>
</item>
<item handle="1a6562590ef19" parent="73475cb40a568" root="73475cb40a568" order="0" type="FOLDER" class="NOVEL">
<item handle="0000000000018" parent="0000000000010" root="0000000000010" order="0" type="FOLDER" class="NOVEL">
<meta expanded="False"/>
<name status="sbc8960" import="ie465e1">Chapter 1</name>
<name status="s000008" import="i00000c">Chapter 1</name>
</item>
<item handle="031b4af5197ec" parent="1a6562590ef19" root="73475cb40a568" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<item handle="0000000000019" parent="0000000000018" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="sbc8960" import="ie465e1" exported="True">Chapter 1</name>
<name status="s000008" import="i00000c" exported="True">Chapter 1</name>
</item>
<item handle="41cfc0d1f2d12" parent="1a6562590ef19" root="73475cb40a568" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<item handle="000000000001a" parent="0000000000018" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="sbc8960" import="ie465e1" exported="True">Scene 1.1</name>
<name status="s000008" import="i00000c" exported="True">Scene 1.1</name>
</item>
<item handle="2858dcd1057d3" parent="1a6562590ef19" root="73475cb40a568" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<item handle="000000000001b" parent="0000000000018" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="sbc8960" import="ie465e1" exported="True">Scene 1.2</name>
<name status="s000008" import="i00000c" exported="True">Scene 1.2</name>
</item>
<item handle="2fca346db6561" parent="1a6562590ef19" root="73475cb40a568" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<item handle="000000000001c" parent="0000000000018" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="sbc8960" import="ie465e1" exported="True">Scene 1.3</name>
<name status="s000008" import="i00000c" exported="True">Scene 1.3</name>
</item>
<item handle="02d20bbd7e394" parent="73475cb40a568" root="73475cb40a568" order="0" type="FOLDER" class="NOVEL">
<item handle="000000000001d" parent="0000000000010" root="0000000000010" order="0" type="FOLDER" class="NOVEL">
<meta expanded="False"/>
<name status="sbc8960" import="ie465e1">Chapter 2</name>
<name status="s000008" import="i00000c">Chapter 2</name>
</item>
<item handle="7688b6ef52555" parent="02d20bbd7e394" root="73475cb40a568" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<item handle="000000000001e" parent="000000000001d" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="sbc8960" import="ie465e1" exported="True">Chapter 2</name>
<name status="s000008" import="i00000c" exported="True">Chapter 2</name>
</item>
<item handle="c837649cce43f" parent="02d20bbd7e394" root="73475cb40a568" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<item handle="000000000001f" parent="000000000001d" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="sbc8960" import="ie465e1" exported="True">Scene 2.1</name>
<name status="s000008" import="i00000c" exported="True">Scene 2.1</name>
</item>
<item handle="6208ef0f7750c" parent="02d20bbd7e394" root="73475cb40a568" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<item handle="0000000000020" parent="000000000001d" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="sbc8960" import="ie465e1" exported="True">Scene 2.2</name>
<name status="s000008" import="i00000c" exported="True">Scene 2.2</name>
</item>
<item handle="3e1e967e9b793" parent="02d20bbd7e394" root="73475cb40a568" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<item handle="0000000000021" parent="000000000001d" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="sbc8960" import="ie465e1" exported="True">Scene 2.3</name>
<name status="s000008" import="i00000c" exported="True">Scene 2.3</name>
</item>
<item handle="39fa9ec190eee" parent="73475cb40a568" root="73475cb40a568" order="0" type="FOLDER" class="NOVEL">
<item handle="0000000000022" parent="0000000000010" root="0000000000010" order="0" type="FOLDER" class="NOVEL">
<meta expanded="False"/>
<name status="sbc8960" import="ie465e1">Chapter 3</name>
<name status="s000008" import="i00000c">Chapter 3</name>
</item>
<item handle="d029fa3a95e17" parent="39fa9ec190eee" root="73475cb40a568" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<item handle="0000000000023" parent="0000000000022" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="sbc8960" import="ie465e1" exported="True">Chapter 3</name>
<name status="s000008" import="i00000c" exported="True">Chapter 3</name>
</item>
<item handle="81b8a03f97e87" parent="39fa9ec190eee" root="73475cb40a568" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<item handle="0000000000024" parent="0000000000022" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="sbc8960" import="ie465e1" exported="True">Scene 3.1</name>
<name status="s000008" import="i00000c" exported="True">Scene 3.1</name>
</item>
<item handle="da4ea2a5506f2" parent="39fa9ec190eee" root="73475cb40a568" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<item handle="0000000000025" parent="0000000000022" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="sbc8960" import="ie465e1" exported="True">Scene 3.2</name>
<name status="s000008" import="i00000c" exported="True">Scene 3.2</name>
</item>
<item handle="a68b412c42825" parent="39fa9ec190eee" root="73475cb40a568" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<item handle="0000000000026" parent="0000000000022" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="sbc8960" import="ie465e1" exported="True">Scene 3.3</name>
<name status="s000008" import="i00000c" exported="True">Scene 3.3</name>
</item>
</content>
</novelWriterXML>
@@ -1,5 +1,5 @@
<?xml version='1.0' encoding='utf-8'?>
<novelWriterXML appVersion="1.7-alpha0" hexVersion="0x010700a0" fileVersion="1.4" timeStamp="2022-04-16 20:39:10">
<novelWriterXML appVersion="1.7-alpha0" hexVersion="0x010700a0" fileVersion="1.4" timeStamp="2022-04-20 20:03:03">
<project>
<name>Test Custom</name>
<title>Test Novel</title>
@@ -29,74 +29,74 @@
<section></section>
</titleFormat>
<status>
<entry key="sbc8960" count="8" red="100" green="100" blue="100">New</entry>
<entry key="s1a3d1f" count="0" red="200" green="50" blue="0">Note</entry>
<entry key="sad3c2d" count="0" red="200" green="150" blue="0">Draft</entry>
<entry key="sbd9c66" count="0" red="50" green="200" blue="0">Finished</entry>
<entry key="s000008" count="8" red="100" green="100" blue="100">New</entry>
<entry key="s000009" count="0" red="200" green="50" blue="0">Note</entry>
<entry key="s00000a" count="0" red="200" green="150" blue="0">Draft</entry>
<entry key="s00000b" count="0" red="50" green="200" blue="0">Finished</entry>
</status>
<importance>
<entry key="ie465e1" count="6" red="100" green="100" blue="100">New</entry>
<entry key="i8b9d24" count="0" red="200" green="50" blue="0">Minor</entry>
<entry key="i16419f" count="0" red="200" green="150" blue="0">Major</entry>
<entry key="i972a84" count="0" red="50" green="200" blue="0">Main</entry>
<entry key="i00000c" count="6" red="100" green="100" blue="100">New</entry>
<entry key="i00000d" count="0" red="200" green="50" blue="0">Minor</entry>
<entry key="i00000e" count="0" red="200" green="150" blue="0">Major</entry>
<entry key="i00000f" count="0" red="50" green="200" blue="0">Main</entry>
</importance>
</settings>
<content count="14">
<item handle="73475cb40a568" parent="None" root="73475cb40a568" order="0" type="ROOT" class="NOVEL">
<item handle="0000000000010" parent="None" root="0000000000010" order="0" type="ROOT" class="NOVEL">
<meta expanded="False"/>
<name status="sbc8960" import="ie465e1">Novel</name>
<name status="s000008" import="i00000c">Novel</name>
</item>
<item handle="44cb730c42048" parent="None" root="44cb730c42048" order="0" type="ROOT" class="PLOT">
<item handle="0000000000011" parent="None" root="0000000000011" order="0" type="ROOT" class="PLOT">
<meta expanded="False"/>
<name status="sbc8960" import="ie465e1">Plot</name>
<name status="s000008" import="i00000c">Plot</name>
</item>
<item handle="71ee45a3c0db9" parent="None" root="71ee45a3c0db9" order="0" type="ROOT" class="CHARACTER">
<item handle="0000000000012" parent="None" root="0000000000012" order="0" type="ROOT" class="CHARACTER">
<meta expanded="False"/>
<name status="sbc8960" import="ie465e1">Characters</name>
<name status="s000008" import="i00000c">Characters</name>
</item>
<item handle="811786ad1ae74" parent="None" root="811786ad1ae74" order="0" type="ROOT" class="WORLD">
<item handle="0000000000013" parent="None" root="0000000000013" order="0" type="ROOT" class="WORLD">
<meta expanded="False"/>
<name status="sbc8960" import="ie465e1">Locations</name>
<name status="s000008" import="i00000c">Locations</name>
</item>
<item handle="25fc0e7096fc6" parent="None" root="25fc0e7096fc6" order="0" type="ROOT" class="TIMELINE">
<item handle="0000000000014" parent="None" root="0000000000014" order="0" type="ROOT" class="TIMELINE">
<meta expanded="False"/>
<name status="sbc8960" import="ie465e1">Timeline</name>
<name status="s000008" import="i00000c">Timeline</name>
</item>
<item handle="31489056e0916" parent="None" root="31489056e0916" order="0" type="ROOT" class="OBJECT">
<item handle="0000000000015" parent="None" root="0000000000015" order="0" type="ROOT" class="OBJECT">
<meta expanded="False"/>
<name status="sbc8960" import="ie465e1">Objects</name>
<name status="s000008" import="i00000c">Objects</name>
</item>
<item handle="98010bd9270f9" parent="None" root="98010bd9270f9" order="0" type="ROOT" class="ENTITY">
<item handle="0000000000016" parent="None" root="0000000000016" order="0" type="ROOT" class="ENTITY">
<meta expanded="False"/>
<name status="sbc8960" import="ie465e1">Entities</name>
<name status="s000008" import="i00000c">Entities</name>
</item>
<item handle="0e17daca5f3e1" parent="73475cb40a568" root="73475cb40a568" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<item handle="0000000000017" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="sbc8960" import="ie465e1" exported="True">Title Page</name>
<name status="s000008" import="i00000c" exported="True">Title Page</name>
</item>
<item handle="1a6562590ef19" parent="73475cb40a568" root="73475cb40a568" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<item handle="0000000000018" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="sbc8960" import="ie465e1" exported="True">Scene 1</name>
<name status="s000008" import="i00000c" exported="True">Scene 1</name>
</item>
<item handle="031b4af5197ec" parent="73475cb40a568" root="73475cb40a568" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<item handle="0000000000019" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="sbc8960" import="ie465e1" exported="True">Scene 2</name>
<name status="s000008" import="i00000c" exported="True">Scene 2</name>
</item>
<item handle="41cfc0d1f2d12" parent="73475cb40a568" root="73475cb40a568" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<item handle="000000000001a" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="sbc8960" import="ie465e1" exported="True">Scene 3</name>
<name status="s000008" import="i00000c" exported="True">Scene 3</name>
</item>
<item handle="2858dcd1057d3" parent="73475cb40a568" root="73475cb40a568" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<item handle="000000000001b" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="sbc8960" import="ie465e1" exported="True">Scene 4</name>
<name status="s000008" import="i00000c" exported="True">Scene 4</name>
</item>
<item handle="2fca346db6561" parent="73475cb40a568" root="73475cb40a568" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<item handle="000000000001c" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="sbc8960" import="ie465e1" exported="True">Scene 5</name>
<name status="s000008" import="i00000c" exported="True">Scene 5</name>
</item>
<item handle="02d20bbd7e394" parent="73475cb40a568" root="73475cb40a568" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<item handle="000000000001d" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="sbc8960" import="ie465e1" exported="True">Scene 6</name>
<name status="s000008" import="i00000c" exported="True">Scene 6</name>
</item>
</content>
</novelWriterXML>
@@ -1,5 +1,5 @@
<?xml version='1.0' encoding='utf-8'?>
<novelWriterXML appVersion="1.7-alpha0" hexVersion="0x010700a0" fileVersion="1.4" timeStamp="2022-04-16 20:37:42">
<novelWriterXML appVersion="1.7-alpha0" hexVersion="0x010700a0" fileVersion="1.4" timeStamp="2022-04-20 20:09:19">
<project>
<name>New Project</name>
<title></title>
@@ -27,58 +27,58 @@
<section></section>
</titleFormat>
<status>
<entry key="sbc8960" count="6" red="100" green="100" blue="100">New</entry>
<entry key="s1a3d1f" count="0" red="200" green="50" blue="0">Note</entry>
<entry key="sad3c2d" count="0" red="200" green="150" blue="0">Draft</entry>
<entry key="sbd9c66" count="0" red="50" green="200" blue="0">Finished</entry>
<entry key="s000008" count="5" red="100" green="100" blue="100">New</entry>
<entry key="s000009" count="0" red="200" green="50" blue="0">Note</entry>
<entry key="s00000a" count="0" red="200" green="150" blue="0">Draft</entry>
<entry key="s00000b" count="0" red="50" green="200" blue="0">Finished</entry>
</status>
<importance>
<entry key="ie465e1" count="4" red="100" green="100" blue="100">New</entry>
<entry key="i8b9d24" count="0" red="200" green="50" blue="0">Minor</entry>
<entry key="i16419f" count="0" red="200" green="150" blue="0">Major</entry>
<entry key="i972a84" count="0" red="50" green="200" blue="0">Main</entry>
<entry key="i00000c" count="3" red="100" green="100" blue="100">New</entry>
<entry key="i00000d" count="0" red="200" green="50" blue="0">Minor</entry>
<entry key="i00000e" count="0" red="200" green="150" blue="0">Major</entry>
<entry key="i00000f" count="0" red="50" green="200" blue="0">Main</entry>
</importance>
</settings>
<content count="10">
<item handle="73475cb40a568" parent="None" root="73475cb40a568" order="0" type="ROOT" class="NOVEL">
<item handle="0000000000010" parent="None" root="0000000000010" order="0" type="ROOT" class="NOVEL">
<meta expanded="False"/>
<name status="sbc8960" import="ie465e1">Novel</name>
<name status="s000008" import="i00000c">Novel</name>
</item>
<item handle="44cb730c42048" parent="None" root="44cb730c42048" order="0" type="ROOT" class="PLOT">
<item handle="0000000000011" parent="None" root="0000000000011" order="0" type="ROOT" class="PLOT">
<meta expanded="False"/>
<name status="sbc8960" import="ie465e1">Plot</name>
<name status="s000008" import="i00000c">Plot</name>
</item>
<item handle="71ee45a3c0db9" parent="None" root="71ee45a3c0db9" order="0" type="ROOT" class="CHARACTER">
<item handle="0000000000012" parent="None" root="0000000000012" order="0" type="ROOT" class="CHARACTER">
<meta expanded="False"/>
<name status="sbc8960" import="ie465e1">Characters</name>
<name status="s000008" import="i00000c">Characters</name>
</item>
<item handle="811786ad1ae74" parent="None" root="811786ad1ae74" order="0" type="ROOT" class="WORLD">
<item handle="0000000000013" parent="None" root="0000000000013" order="0" type="ROOT" class="WORLD">
<meta expanded="False"/>
<name status="sbc8960" import="ie465e1">World</name>
<name status="s000008" import="i00000c">World</name>
</item>
<item handle="25fc0e7096fc6" parent="73475cb40a568" root="73475cb40a568" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<item handle="0000000000014" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="sbc8960" import="ie465e1" exported="True">Title Page</name>
<name status="s000008" import="i00000c" exported="True">Title Page</name>
</item>
<item handle="31489056e0916" parent="73475cb40a568" root="73475cb40a568" order="0" type="FOLDER" class="NOVEL">
<item handle="0000000000015" parent="0000000000010" root="0000000000010" order="0" type="FOLDER" class="NOVEL">
<meta expanded="False"/>
<name status="sbc8960" import="ie465e1">New Chapter</name>
<name status="s000008" import="i00000c">New Chapter</name>
</item>
<item handle="98010bd9270f9" parent="31489056e0916" root="73475cb40a568" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<item handle="0000000000016" parent="0000000000015" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="sbc8960" import="ie465e1" exported="True">New Chapter</name>
<name status="s000008" import="i00000c" exported="True">New Chapter</name>
</item>
<item handle="0e17daca5f3e1" parent="31489056e0916" root="73475cb40a568" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<item handle="0000000000017" parent="0000000000015" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="sbc8960" import="ie465e1" exported="True">New Scene</name>
<name status="s000008" import="i00000c" exported="True">New Scene</name>
</item>
<item handle="1a6562590ef19" parent="31489056e0916" root="73475cb40a568" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<item handle="0000000000028" parent="31489056e0916" root="None" order="0" type="FILE" class="NO_CLASS" layout="NO_LAYOUT">
<meta charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="sbc8960" import="ie465e1" exported="True">Hello</name>
<name status="None" import="None" exported="True">Hello</name>
</item>
<item handle="031b4af5197ec" parent="71ee45a3c0db9" root="71ee45a3c0db9" order="0" type="FILE" class="CHARACTER" layout="NOTE">
<item handle="0000000000029" parent="71ee45a3c0db9" root="None" order="0" type="FILE" class="NO_CLASS" layout="NO_LAYOUT">
<meta charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="sbc8960" import="ie465e1" exported="True">Jane</name>
<name status="None" import="None" exported="True">Jane</name>
</item>
</content>
</novelWriterXML>
@@ -1,9 +1,9 @@
<?xml version='1.0' encoding='utf-8'?>
<novelWriterXML appVersion="1.7-alpha0" hexVersion="0x010700a0" fileVersion="1.4" timeStamp="2022-04-16 14:13:27">
<novelWriterXML appVersion="1.7-alpha0" hexVersion="0x010700a0" fileVersion="1.4" timeStamp="2022-04-20 20:00:23">
<project>
<name>New Project</name>
<title></title>
<saveCount>1</saveCount>
<saveCount>2</saveCount>
<autoCount>1</autoCount>
<editTime>0</editTime>
</project>
@@ -27,50 +27,50 @@
<section></section>
</titleFormat>
<status>
<entry key="sbc8960" count="5" red="100" green="100" blue="100">New</entry>
<entry key="s1a3d1f" count="0" red="200" green="50" blue="0">Note</entry>
<entry key="sad3c2d" count="0" red="200" green="150" blue="0">Draft</entry>
<entry key="sbd9c66" count="0" red="50" green="200" blue="0">Finished</entry>
<entry key="s000008" count="5" red="100" green="100" blue="100">New</entry>
<entry key="s000009" count="0" red="200" green="50" blue="0">Note</entry>
<entry key="s00000a" count="0" red="200" green="150" blue="0">Draft</entry>
<entry key="s00000b" count="0" red="50" green="200" blue="0">Finished</entry>
</status>
<importance>
<entry key="ie465e1" count="3" red="100" green="100" blue="100">New</entry>
<entry key="i8b9d24" count="0" red="200" green="50" blue="0">Minor</entry>
<entry key="i16419f" count="0" red="200" green="150" blue="0">Major</entry>
<entry key="i972a84" count="0" red="50" green="200" blue="0">Main</entry>
<entry key="i00000c" count="3" red="100" green="100" blue="100">New</entry>
<entry key="i00000d" count="0" red="200" green="50" blue="0">Minor</entry>
<entry key="i00000e" count="0" red="200" green="150" blue="0">Major</entry>
<entry key="i00000f" count="0" red="50" green="200" blue="0">Main</entry>
</importance>
</settings>
<content count="8">
<item handle="73475cb40a568" parent="None" root="73475cb40a568" order="0" type="ROOT" class="NOVEL">
<item handle="0000000000010" parent="None" root="0000000000010" order="0" type="ROOT" class="NOVEL">
<meta expanded="False"/>
<name status="sbc8960" import="ie465e1">Novel</name>
<name status="s000008" import="i00000c">Novel</name>
</item>
<item handle="44cb730c42048" parent="None" root="44cb730c42048" order="0" type="ROOT" class="PLOT">
<item handle="0000000000011" parent="None" root="0000000000011" order="0" type="ROOT" class="PLOT">
<meta expanded="False"/>
<name status="sbc8960" import="ie465e1">Plot</name>
<name status="s000008" import="i00000c">Plot</name>
</item>
<item handle="71ee45a3c0db9" parent="None" root="71ee45a3c0db9" order="0" type="ROOT" class="CHARACTER">
<item handle="0000000000012" parent="None" root="0000000000012" order="0" type="ROOT" class="CHARACTER">
<meta expanded="False"/>
<name status="sbc8960" import="ie465e1">Characters</name>
<name status="s000008" import="i00000c">Characters</name>
</item>
<item handle="811786ad1ae74" parent="None" root="811786ad1ae74" order="0" type="ROOT" class="WORLD">
<item handle="0000000000013" parent="None" root="0000000000013" order="0" type="ROOT" class="WORLD">
<meta expanded="False"/>
<name status="sbc8960" import="ie465e1">World</name>
<name status="s000008" import="i00000c">World</name>
</item>
<item handle="25fc0e7096fc6" parent="73475cb40a568" root="73475cb40a568" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<item handle="0000000000014" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="sbc8960" import="ie465e1" exported="True">Title Page</name>
<name status="s000008" import="i00000c" exported="True">Title Page</name>
</item>
<item handle="31489056e0916" parent="73475cb40a568" root="73475cb40a568" order="0" type="FOLDER" class="NOVEL">
<item handle="0000000000015" parent="0000000000010" root="0000000000010" order="0" type="FOLDER" class="NOVEL">
<meta expanded="False"/>
<name status="sbc8960" import="ie465e1">New Chapter</name>
<name status="s000008" import="i00000c">New Chapter</name>
</item>
<item handle="98010bd9270f9" parent="31489056e0916" root="73475cb40a568" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<item handle="0000000000016" parent="0000000000015" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="sbc8960" import="ie465e1" exported="True">New Chapter</name>
<name status="s000008" import="i00000c" exported="True">New Chapter</name>
</item>
<item handle="0e17daca5f3e1" parent="31489056e0916" root="73475cb40a568" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<item handle="0000000000017" parent="0000000000015" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="sbc8960" import="ie465e1" exported="True">New Scene</name>
<name status="s000008" import="i00000c" exported="True">New Scene</name>
</item>
</content>
</novelWriterXML>
@@ -1,5 +1,5 @@
<?xml version='1.0' encoding='utf-8'?>
<novelWriterXML appVersion="1.7-alpha0" hexVersion="0x010700a0" fileVersion="1.4" timeStamp="2022-04-16 20:38:35">
<novelWriterXML appVersion="1.7-alpha0" hexVersion="0x010700a0" fileVersion="1.4" timeStamp="2022-04-20 20:04:09">
<project>
<name>New Project</name>
<title></title>
@@ -27,82 +27,82 @@
<section></section>
</titleFormat>
<status>
<entry key="sbc8960" count="6" red="100" green="100" blue="100">New</entry>
<entry key="s1a3d1f" count="0" red="200" green="50" blue="0">Note</entry>
<entry key="sad3c2d" count="0" red="200" green="150" blue="0">Draft</entry>
<entry key="sbd9c66" count="0" red="50" green="200" blue="0">Finished</entry>
<entry key="s000008" count="6" red="100" green="100" blue="100">New</entry>
<entry key="s000009" count="0" red="200" green="50" blue="0">Note</entry>
<entry key="s00000a" count="0" red="200" green="150" blue="0">Draft</entry>
<entry key="s00000b" count="0" red="50" green="200" blue="0">Finished</entry>
</status>
<importance>
<entry key="ie465e1" count="10" red="100" green="100" blue="100">New</entry>
<entry key="i8b9d24" count="0" red="200" green="50" blue="0">Minor</entry>
<entry key="i16419f" count="0" red="200" green="150" blue="0">Major</entry>
<entry key="i972a84" count="0" red="50" green="200" blue="0">Main</entry>
<entry key="i00000c" count="10" red="100" green="100" blue="100">New</entry>
<entry key="i00000d" count="0" red="200" green="50" blue="0">Minor</entry>
<entry key="i00000e" count="0" red="200" green="150" blue="0">Major</entry>
<entry key="i00000f" count="0" red="50" green="200" blue="0">Main</entry>
</importance>
</settings>
<content count="16">
<item handle="73475cb40a568" parent="None" root="73475cb40a568" order="0" type="ROOT" class="NOVEL">
<item handle="0000000000010" parent="None" root="0000000000010" order="0" type="ROOT" class="NOVEL">
<meta expanded="False"/>
<name status="sbc8960" import="ie465e1">Novel</name>
<name status="s000008" import="i00000c">Novel</name>
</item>
<item handle="44cb730c42048" parent="None" root="44cb730c42048" order="0" type="ROOT" class="PLOT">
<item handle="0000000000011" parent="None" root="0000000000011" order="0" type="ROOT" class="PLOT">
<meta expanded="False"/>
<name status="sbc8960" import="ie465e1">Plot</name>
<name status="s000008" import="i00000c">Plot</name>
</item>
<item handle="71ee45a3c0db9" parent="None" root="71ee45a3c0db9" order="0" type="ROOT" class="CHARACTER">
<item handle="0000000000012" parent="None" root="0000000000012" order="0" type="ROOT" class="CHARACTER">
<meta expanded="False"/>
<name status="sbc8960" import="ie465e1">Characters</name>
<name status="s000008" import="i00000c">Characters</name>
</item>
<item handle="811786ad1ae74" parent="None" root="811786ad1ae74" order="0" type="ROOT" class="WORLD">
<item handle="0000000000013" parent="None" root="0000000000013" order="0" type="ROOT" class="WORLD">
<meta expanded="False"/>
<name status="sbc8960" import="ie465e1">World</name>
<name status="s000008" import="i00000c">World</name>
</item>
<item handle="25fc0e7096fc6" parent="73475cb40a568" root="73475cb40a568" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<item handle="0000000000014" parent="0000000000010" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="sbc8960" import="ie465e1" exported="True">Title Page</name>
<name status="s000008" import="i00000c" exported="True">Title Page</name>
</item>
<item handle="31489056e0916" parent="73475cb40a568" root="73475cb40a568" order="0" type="FOLDER" class="NOVEL">
<item handle="0000000000015" parent="0000000000010" root="0000000000010" order="0" type="FOLDER" class="NOVEL">
<meta expanded="False"/>
<name status="sbc8960" import="ie465e1">New Chapter</name>
<name status="s000008" import="i00000c">New Chapter</name>
</item>
<item handle="98010bd9270f9" parent="31489056e0916" root="73475cb40a568" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<item handle="0000000000016" parent="0000000000015" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="sbc8960" import="ie465e1" exported="True">New Chapter</name>
<name status="s000008" import="i00000c" exported="True">New Chapter</name>
</item>
<item handle="0e17daca5f3e1" parent="31489056e0916" root="73475cb40a568" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<item handle="0000000000017" parent="0000000000015" root="0000000000010" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta charCount="0" wordCount="0" paraCount="0" cursorPos="0"/>
<name status="sbc8960" import="ie465e1" exported="True">New Scene</name>
<name status="s000008" import="i00000c" exported="True">New Scene</name>
</item>
<item handle="1a6562590ef19" parent="None" root="1a6562590ef19" order="0" type="ROOT" class="NOVEL">
<item handle="0000000000028" parent="None" root="0000000000028" order="0" type="ROOT" class="NOVEL">
<meta expanded="False"/>
<name status="sbc8960" import="ie465e1">Novel</name>
<name status="s000008" import="i00000c">Novel</name>
</item>
<item handle="031b4af5197ec" parent="None" root="031b4af5197ec" order="0" type="ROOT" class="PLOT">
<item handle="0000000000029" parent="None" root="0000000000029" order="0" type="ROOT" class="PLOT">
<meta expanded="False"/>
<name status="sbc8960" import="ie465e1">Plot</name>
<name status="s000008" import="i00000c">Plot</name>
</item>
<item handle="41cfc0d1f2d12" parent="None" root="41cfc0d1f2d12" order="0" type="ROOT" class="CHARACTER">
<item handle="000000000002a" parent="None" root="000000000002a" order="0" type="ROOT" class="CHARACTER">
<meta expanded="False"/>
<name status="sbc8960" import="ie465e1">Character</name>
<name status="s000008" import="i00000c">Character</name>
</item>
<item handle="2858dcd1057d3" parent="None" root="2858dcd1057d3" order="0" type="ROOT" class="WORLD">
<item handle="000000000002b" parent="None" root="000000000002b" order="0" type="ROOT" class="WORLD">
<meta expanded="False"/>
<name status="sbc8960" import="ie465e1">World</name>
<name status="s000008" import="i00000c">World</name>
</item>
<item handle="2fca346db6561" parent="None" root="2fca346db6561" order="0" type="ROOT" class="TIMELINE">
<item handle="000000000002c" parent="None" root="000000000002c" order="0" type="ROOT" class="TIMELINE">
<meta expanded="False"/>
<name status="sbc8960" import="ie465e1">Timeline</name>
<name status="s000008" import="i00000c">Timeline</name>
</item>
<item handle="02d20bbd7e394" parent="None" root="02d20bbd7e394" order="0" type="ROOT" class="OBJECT">
<item handle="000000000002d" parent="None" root="000000000002d" order="0" type="ROOT" class="OBJECT">
<meta expanded="False"/>
<name status="sbc8960" import="ie465e1">Object</name>
<name status="s000008" import="i00000c">Object</name>
</item>
<item handle="7688b6ef52555" parent="None" root="7688b6ef52555" order="0" type="ROOT" class="CUSTOM">
<item handle="000000000002e" parent="None" root="000000000002e" order="0" type="ROOT" class="CUSTOM">
<meta expanded="False"/>
<name status="sbc8960" import="ie465e1">Custom1</name>
<name status="s000008" import="i00000c">Custom1</name>
</item>
<item handle="c837649cce43f" parent="None" root="c837649cce43f" order="0" type="ROOT" class="CUSTOM">
<item handle="000000000002f" parent="None" root="000000000002f" order="0" type="ROOT" class="CUSTOM">
<meta expanded="False"/>
<name status="sbc8960" import="ie465e1">Custom2</name>
<name status="s000008" import="i00000c">Custom2</name>
</item>
</content>
</novelWriterXML>
@@ -1,5 +1,5 @@
%%~name: New Scene
%%~path: 31489056e0916/0e17daca5f3e1
%%~path: 000000000000d/000000000000f
%%~kind: NOVEL/DOCUMENT
# Novel
@@ -1,5 +1,5 @@
%%~name: New Note
%%~path: 71ee45a3c0db9/1a6562590ef19
%%~path: 000000000000a/0000000000020
%%~kind: CHARACTER/NOTE
# Jane Doe
@@ -1,5 +1,5 @@
%%~name: New Note
%%~path: 44cb730c42048/031b4af5197ec
%%~path: 0000000000009/0000000000021
%%~kind: PLOT/NOTE
# Main Plot
@@ -1,5 +1,5 @@
%%~name: New Note
%%~path: 811786ad1ae74/41cfc0d1f2d12
%%~path: 000000000000b/0000000000022
%%~kind: WORLD/NOTE
# Main Location
@@ -1,11 +1,11 @@
<?xml version='1.0' encoding='utf-8'?>
<novelWriterXML appVersion="1.7-alpha0" hexVersion="0x010700a0" fileVersion="1.4" timeStamp="2022-04-17 14:18:28">
<novelWriterXML appVersion="1.7-alpha0" hexVersion="0x010700a0" fileVersion="1.4" timeStamp="2022-04-20 21:23:06">
<project>
<name>New Project</name>
<title></title>
<saveCount>5</saveCount>
<autoCount>2</autoCount>
<editTime>3</editTime>
<editTime>4</editTime>
</project>
<settings>
<doBackup>True</doBackup>
@@ -13,7 +13,7 @@
<spellCheck>True</spellCheck>
<spellLang>None</spellLang>
<autoOutline>True</autoOutline>
<lastEdited>0e17daca5f3e1</lastEdited>
<lastEdited>000000000000f</lastEdited>
<lastViewed>None</lastViewed>
<lastWordCount>126</lastWordCount>
<novelWordCount>99</novelWordCount>
@@ -27,66 +27,66 @@
<section></section>
</titleFormat>
<status>
<entry key="sa3b179" count="5" red="100" green="100" blue="100">New</entry>
<entry key="s1c8031" count="0" red="200" green="50" blue="0">Note</entry>
<entry key="s06671a" count="0" red="200" green="150" blue="0">Draft</entry>
<entry key="sbdd640" count="0" red="50" green="200" blue="0">Finished</entry>
<entry key="s000000" count="5" red="100" green="100" blue="100">New</entry>
<entry key="s000001" count="0" red="200" green="50" blue="0">Note</entry>
<entry key="s000002" count="0" red="200" green="150" blue="0">Draft</entry>
<entry key="s000003" count="0" red="50" green="200" blue="0">Finished</entry>
</status>
<importance>
<entry key="i466852" count="7" red="100" green="100" blue="100">New</entry>
<entry key="i3eb13b" count="0" red="200" green="50" blue="0">Minor</entry>
<entry key="i392456" count="0" red="200" green="150" blue="0">Major</entry>
<entry key="i23b8c1" count="0" red="50" green="200" blue="0">Main</entry>
<entry key="i000004" count="7" red="100" green="100" blue="100">New</entry>
<entry key="i000005" count="0" red="200" green="50" blue="0">Minor</entry>
<entry key="i000006" count="0" red="200" green="150" blue="0">Major</entry>
<entry key="i000007" count="0" red="50" green="200" blue="0">Main</entry>
</importance>
</settings>
<content count="12">
<item handle="73475cb40a568" parent="None" root="73475cb40a568" order="0" type="ROOT" class="NOVEL">
<item handle="0000000000008" parent="None" root="0000000000008" order="0" type="ROOT" class="NOVEL">
<meta expanded="True"/>
<name status="sa3b179" import="i466852">Novel</name>
<name status="s000000" import="i000004">Novel</name>
</item>
<item handle="25fc0e7096fc6" parent="73475cb40a568" root="73475cb40a568" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<item handle="000000000000c" parent="0000000000008" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta charCount="11" wordCount="2" paraCount="0" cursorPos="0"/>
<name status="sa3b179" import="i466852" exported="True">Title Page</name>
<name status="s000000" import="i000004" exported="True">Title Page</name>
</item>
<item handle="31489056e0916" parent="73475cb40a568" root="73475cb40a568" order="1" type="FOLDER" class="NOVEL">
<item handle="000000000000d" parent="0000000000008" root="0000000000008" order="1" type="FOLDER" class="NOVEL">
<meta expanded="True"/>
<name status="sa3b179" import="i466852">New Chapter</name>
<name status="s000000" import="i000004">New Chapter</name>
</item>
<item handle="98010bd9270f9" parent="31489056e0916" root="73475cb40a568" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<item handle="000000000000e" parent="000000000000d" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta charCount="11" wordCount="2" paraCount="0" cursorPos="0"/>
<name status="sa3b179" import="i466852" exported="True">New Chapter</name>
<name status="s000000" import="i000004" exported="True">New Chapter</name>
</item>
<item handle="0e17daca5f3e1" parent="31489056e0916" root="73475cb40a568" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
<item handle="000000000000f" parent="000000000000d" root="0000000000008" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta charCount="612" wordCount="95" paraCount="10" cursorPos="768"/>
<name status="sa3b179" import="i466852" exported="True">New Scene</name>
<name status="s000000" import="i000004" exported="True">New Scene</name>
</item>
<item handle="44cb730c42048" parent="None" root="44cb730c42048" order="1" type="ROOT" class="PLOT">
<item handle="0000000000009" parent="None" root="0000000000009" order="1" type="ROOT" class="PLOT">
<meta expanded="True"/>
<name status="sa3b179" import="i466852">Plot</name>
<name status="s000000" import="i000004">Plot</name>
</item>
<item handle="031b4af5197ec" parent="44cb730c42048" root="44cb730c42048" order="0" type="FILE" class="PLOT" layout="NOTE">
<item handle="0000000000021" parent="0000000000009" root="0000000000009" order="0" type="FILE" class="PLOT" layout="NOTE">
<meta charCount="48" wordCount="10" paraCount="1" cursorPos="69"/>
<name status="sa3b179" import="i466852" exported="True">New Note</name>
<name status="s000000" import="i000004" exported="True">New Note</name>
</item>
<item handle="71ee45a3c0db9" parent="None" root="71ee45a3c0db9" order="2" type="ROOT" class="CHARACTER">
<item handle="000000000000a" parent="None" root="000000000000a" order="2" type="ROOT" class="CHARACTER">
<meta expanded="True"/>
<name status="sa3b179" import="i466852">Characters</name>
<name status="s000000" import="i000004">Characters</name>
</item>
<item handle="1a6562590ef19" parent="71ee45a3c0db9" root="71ee45a3c0db9" order="0" type="FILE" class="CHARACTER" layout="NOTE">
<item handle="0000000000020" parent="000000000000a" root="000000000000a" order="0" type="FILE" class="CHARACTER" layout="NOTE">
<meta charCount="34" wordCount="8" paraCount="1" cursorPos="51"/>
<name status="sa3b179" import="i466852" exported="True">New Note</name>
<name status="s000000" import="i000004" exported="True">New Note</name>
</item>
<item handle="811786ad1ae74" parent="None" root="811786ad1ae74" order="3" type="ROOT" class="WORLD">
<item handle="000000000000b" parent="None" root="000000000000b" order="3" type="ROOT" class="WORLD">
<meta expanded="True"/>
<name status="sa3b179" import="i466852">World</name>
<name status="s000000" import="i000004">World</name>
</item>
<item handle="41cfc0d1f2d12" parent="811786ad1ae74" root="811786ad1ae74" order="0" type="FILE" class="WORLD" layout="NOTE">
<item handle="0000000000022" parent="000000000000b" root="000000000000b" order="0" type="FILE" class="WORLD" layout="NOTE">
<meta charCount="51" wordCount="9" paraCount="1" cursorPos="68"/>
<name status="sa3b179" import="i466852" exported="True">New Note</name>
<name status="s000000" import="i000004" exported="True">New Note</name>
</item>
<item handle="2fca346db6561" parent="None" root="2fca346db6561" order="4" type="TRASH" class="TRASH">
<item handle="0000000000024" parent="None" root="0000000000024" order="4" type="TRASH" class="TRASH">
<meta expanded="True"/>
<name status="sa3b179" import="i466852">Trash</name>
<name status="s000000" import="i000004">Trash</name>
</item>
</content>
</novelWriterXML>
@@ -1,5 +1,5 @@
<?xml version='1.0' encoding='utf-8'?>
<novelWriterXML appVersion="1.7-alpha0" hexVersion="0x010700a0" fileVersion="1.4" timeStamp="2022-04-16 12:48:08">
<novelWriterXML appVersion="1.7-alpha0" hexVersion="0x010700a0" fileVersion="1.4" timeStamp="2022-04-20 20:57:43">
<project>
<name>New Project</name>
<title></title>
@@ -27,50 +27,50 @@
<section></section>
</titleFormat>
<status>
<entry key="sa3b179" count="5" red="100" green="100" blue="100">New</entry>
<entry key="s1c8031" count="0" red="200" green="50" blue="0">Note</entry>
<entry key="s06671a" count="0" red="200" green="150" blue="0">Draft</entry>
<entry key="sbdd640" count="0" red="50" green="200" blue="0">Finished</entry>
<entry key="s000000" count="5" red="100" green="100" blue="100">New</entry>
<entry key="s000001" count="0" red="200" green="50" blue="0">Note</entry>
<entry key="s000002" count="0" red="200" green="150" blue="0">Draft</entry>
<entry key="s000003" count="0" red="50" green="200" blue="0">Finished</entry>
</status>
<importance>
<entry key="i466852" count="3" red="100" green="100" blue="100">New</entry>
<entry key="i3eb13b" count="0" red="200" green="50" blue="0">Minor</entry>
<entry key="i392456" count="0" red="200" green="150" blue="0">Major</entry>
<entry key="i23b8c1" count="0" red="50" green="200" blue="0">Main</entry>
<entry key="i000004" count="3" red="100" green="100" blue="100">New</entry>
<entry key="i000005" count="0" red="200" green="50" blue="0">Minor</entry>
<entry key="i000006" count="0" red="200" green="150" blue="0">Major</entry>
<entry key="i000007" count="0" red="50" green="200" blue="0">Main</entry>
</importance>
</settings>
<content count="8">
<item handle="73475cb40a568" parent="None" root="73475cb40a568" order="0" type="ROOT" class="NOVEL">
<item handle="0000000000008" parent="None" root="0000000000008" order="0" type="ROOT" class="NOVEL">
<meta expanded="False"/>
<name status="sa3b179" import="i466852">Novel</name>
<name status="s000000" import="i000004">Novel</name>
</item>
<item handle="25fc0e7096fc6" parent="73475cb40a568" root="73475cb40a568" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<item handle="000000000000c" parent="0000000000008" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta charCount="11" wordCount="2" paraCount="0" cursorPos="0"/>
<name status="sa3b179" import="i466852" exported="True">Title Page</name>
<name status="s000000" import="i000004" exported="True">Title Page</name>
</item>
<item handle="31489056e0916" parent="73475cb40a568" root="73475cb40a568" order="1" type="FOLDER" class="NOVEL">
<item handle="000000000000d" parent="0000000000008" root="0000000000008" order="1" type="FOLDER" class="NOVEL">
<meta expanded="False"/>
<name status="sa3b179" import="i466852">New Chapter</name>
<name status="s000000" import="i000004">New Chapter</name>
</item>
<item handle="98010bd9270f9" parent="31489056e0916" root="73475cb40a568" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<item handle="000000000000e" parent="000000000000d" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta charCount="11" wordCount="2" paraCount="0" cursorPos="0"/>
<name status="sa3b179" import="i466852" exported="True">New Chapter</name>
<name status="s000000" import="i000004" exported="True">New Chapter</name>
</item>
<item handle="0e17daca5f3e1" parent="31489056e0916" root="73475cb40a568" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
<item handle="000000000000f" parent="000000000000d" root="0000000000008" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta charCount="9" wordCount="2" paraCount="0" cursorPos="0"/>
<name status="sa3b179" import="i466852" exported="True">New Scene</name>
<name status="s000000" import="i000004" exported="True">New Scene</name>
</item>
<item handle="44cb730c42048" parent="None" root="44cb730c42048" order="1" type="ROOT" class="PLOT">
<item handle="0000000000009" parent="None" root="0000000000009" order="1" type="ROOT" class="PLOT">
<meta expanded="False"/>
<name status="sa3b179" import="i466852">Plot</name>
<name status="s000000" import="i000004">Plot</name>
</item>
<item handle="71ee45a3c0db9" parent="None" root="71ee45a3c0db9" order="2" type="ROOT" class="CHARACTER">
<item handle="000000000000a" parent="None" root="000000000000a" order="2" type="ROOT" class="CHARACTER">
<meta expanded="False"/>
<name status="sa3b179" import="i466852">Characters</name>
<name status="s000000" import="i000004">Characters</name>
</item>
<item handle="811786ad1ae74" parent="None" root="811786ad1ae74" order="3" type="ROOT" class="WORLD">
<item handle="000000000000b" parent="None" root="000000000000b" order="3" type="ROOT" class="WORLD">
<meta expanded="False"/>
<name status="sa3b179" import="i466852">World</name>
<name status="s000000" import="i000004">World</name>
</item>
</content>
</novelWriterXML>
@@ -1,5 +1,5 @@
<?xml version='1.0' encoding='utf-8'?>
<novelWriterXML appVersion="1.7-alpha0" hexVersion="0x010700a0" fileVersion="1.4" timeStamp="2022-04-16 20:52:19">
<novelWriterXML appVersion="1.7-alpha0" hexVersion="0x010700a0" fileVersion="1.4" timeStamp="2022-04-20 20:50:55">
<project>
<name>Project Name</name>
<title>Project Title</title>
@@ -33,50 +33,50 @@
<section></section>
</titleFormat>
<status>
<entry key="sa3b179" count="5" red="100" green="100" blue="100">New</entry>
<entry key="s1c8031" count="0" red="200" green="50" blue="0">Note</entry>
<entry key="sbdd640" count="0" red="50" green="200" blue="0">Finished</entry>
<entry key="sbc8960" count="0" red="20" green="30" blue="40">Final</entry>
<entry key="s000000" count="5" red="100" green="100" blue="100">New</entry>
<entry key="s000001" count="0" red="200" green="50" blue="0">Note</entry>
<entry key="s000003" count="0" red="50" green="200" blue="0">Finished</entry>
<entry key="s000010" count="0" red="20" green="30" blue="40">Final</entry>
</status>
<importance>
<entry key="i466852" count="3" red="100" green="100" blue="100">New</entry>
<entry key="i3eb13b" count="0" red="200" green="50" blue="0">Minor</entry>
<entry key="i392456" count="0" red="200" green="150" blue="0">Major</entry>
<entry key="i1a3d1f" count="0" red="100" green="100" blue="100">Final</entry>
<entry key="i000004" count="3" red="100" green="100" blue="100">New</entry>
<entry key="i000005" count="0" red="200" green="50" blue="0">Minor</entry>
<entry key="i000006" count="0" red="200" green="150" blue="0">Major</entry>
<entry key="i000011" count="0" red="100" green="100" blue="100">Final</entry>
</importance>
</settings>
<content count="8">
<item handle="73475cb40a568" parent="None" root="73475cb40a568" order="0" type="ROOT" class="NOVEL">
<item handle="0000000000008" parent="None" root="0000000000008" order="0" type="ROOT" class="NOVEL">
<meta expanded="False"/>
<name status="sa3b179" import="i466852">Novel</name>
<name status="s000000" import="i000004">Novel</name>
</item>
<item handle="25fc0e7096fc6" parent="73475cb40a568" root="73475cb40a568" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<item handle="000000000000c" parent="0000000000008" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta charCount="11" wordCount="2" paraCount="0" cursorPos="0"/>
<name status="sa3b179" import="i466852" exported="True">Title Page</name>
<name status="s000000" import="i000004" exported="True">Title Page</name>
</item>
<item handle="31489056e0916" parent="73475cb40a568" root="73475cb40a568" order="1" type="FOLDER" class="NOVEL">
<item handle="000000000000d" parent="0000000000008" root="0000000000008" order="1" type="FOLDER" class="NOVEL">
<meta expanded="False"/>
<name status="sa3b179" import="i466852">New Chapter</name>
<name status="s000000" import="i000004">New Chapter</name>
</item>
<item handle="98010bd9270f9" parent="31489056e0916" root="73475cb40a568" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<item handle="000000000000e" parent="000000000000d" root="0000000000008" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta charCount="11" wordCount="2" paraCount="0" cursorPos="0"/>
<name status="sa3b179" import="i466852" exported="True">New Chapter</name>
<name status="s000000" import="i000004" exported="True">New Chapter</name>
</item>
<item handle="0e17daca5f3e1" parent="31489056e0916" root="73475cb40a568" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
<item handle="000000000000f" parent="000000000000d" root="0000000000008" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
<meta charCount="9" wordCount="2" paraCount="0" cursorPos="0"/>
<name status="sa3b179" import="i466852" exported="True">New Scene</name>
<name status="s000000" import="i000004" exported="True">New Scene</name>
</item>
<item handle="44cb730c42048" parent="None" root="44cb730c42048" order="1" type="ROOT" class="PLOT">
<item handle="0000000000009" parent="None" root="0000000000009" order="1" type="ROOT" class="PLOT">
<meta expanded="False"/>
<name status="sa3b179" import="i466852">Plot</name>
<name status="s000000" import="i000004">Plot</name>
</item>
<item handle="71ee45a3c0db9" parent="None" root="71ee45a3c0db9" order="2" type="ROOT" class="CHARACTER">
<item handle="000000000000a" parent="None" root="000000000000a" order="2" type="ROOT" class="CHARACTER">
<meta expanded="False"/>
<name status="sa3b179" import="i466852">Characters</name>
<name status="s000000" import="i000004">Characters</name>
</item>
<item handle="811786ad1ae74" parent="None" root="811786ad1ae74" order="3" type="ROOT" class="WORLD">
<item handle="000000000000b" parent="None" root="000000000000b" order="3" type="ROOT" class="WORLD">
<meta expanded="False"/>
<name status="sa3b179" import="i466852">World</name>
<name status="s000000" import="i000004">World</name>
</item>
</content>
</novelWriterXML>
-5
View File
@@ -43,7 +43,6 @@ def testCoreIndex_LoadSave(monkeypatch, nwLipsum, mockGUI, outDir, refDir):
compFile = os.path.join(refDir, "coreIndex_LoadSave_tagsIndex.json")
theProject = NWProject(mockGUI)
theProject.projTree.setSeed(42)
assert theProject.openProject(nwLipsum)
theIndex = NWIndex(theProject)
@@ -125,7 +124,6 @@ def testCoreIndex_ScanThis(nwMinimal, mockGUI):
"""Test the tag scanner function scanThis.
"""
theProject = NWProject(mockGUI)
theProject.projTree.setSeed(42)
assert theProject.openProject(nwMinimal) is True
theIndex = NWIndex(theProject)
@@ -177,7 +175,6 @@ def testCoreIndex_CheckThese(nwMinimal, mockGUI):
"""Test the tag checker function checkThese.
"""
theProject = NWProject(mockGUI)
theProject.projTree.setSeed(42)
assert theProject.openProject(nwMinimal) is True
theIndex = NWIndex(theProject)
@@ -254,7 +251,6 @@ 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)
@@ -473,7 +469,6 @@ def testCoreIndex_ExtractData(nwMinimal, mockGUI):
"""Check the index data extraction functions.
"""
theProject = NWProject(mockGUI)
theProject.projTree.setSeed(42)
assert theProject.openProject(nwMinimal) is True
theIndex = NWIndex(theProject)
+24 -21
View File
@@ -20,7 +20,6 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
import pytest
import random
from lxml import etree
@@ -32,13 +31,15 @@ from novelwriter.enum import nwItemClass, nwItemType, nwItemLayout
@pytest.mark.core
def testCoreItem_Setters(mockGUI, constData):
def testCoreItem_Setters(mockGUI, mockRnd):
"""Test all the simple setters for the NWItem class.
"""
random.seed(42)
theProject = NWProject(mockGUI)
theItem = NWItem(theProject)
statusKeys = ["s000000", "s000001", "s000002", "s000003"]
importKeys = ["i000004", "i000005", "i000006", "i000007"]
# Name
theItem.setName("A Name")
assert theItem.itemName == "A Name"
@@ -94,30 +95,30 @@ def testCoreItem_Setters(mockGUI, constData):
# Importance
theItem._class = nwItemClass.CHARACTER
theItem.setImport("Word")
assert theItem.itemImport == constData.importKeys[0] # Default
for key in constData.importKeys:
assert theItem.itemImport == importKeys[0] # Default
for key in importKeys:
theItem.setImport(key)
assert theItem.itemImport == key
# Status
theItem._class = nwItemClass.NOVEL
theItem.setStatus("Word")
assert theItem.itemStatus == constData.statusKeys[0] # Default
for key in constData.statusKeys:
assert theItem.itemStatus == statusKeys[0] # Default
for key in statusKeys:
theItem.setStatus(key)
assert theItem.itemStatus == key
# Status/Importance Wrapper
theItem._class = nwItemClass.CHARACTER
for key in constData.importKeys:
for key in importKeys:
theItem.setImport(key)
assert theItem.itemImport == key
assert theItem.itemStatus == constData.statusKeys[3] # Should not change
assert theItem.itemStatus == statusKeys[3] # Should not change
theItem._class = nwItemClass.NOVEL
for key in constData.statusKeys:
for key in statusKeys:
theItem.setStatus(key)
assert theItem.itemImport == constData.importKeys[3] # Should not change
assert theItem.itemImport == importKeys[3] # Should not change
assert theItem.itemStatus == key
# Expanded
@@ -465,13 +466,15 @@ def testCoreItem_ClassDefaults(mockGUI):
@pytest.mark.core
def testCoreItem_XMLPackUnpack(mockGUI, caplog, constData):
def testCoreItem_XMLPackUnpack(mockGUI, caplog, mockRnd):
"""Test packing and unpacking XML objects for the NWItem class.
"""
random.seed(42)
theProject = NWProject(mockGUI)
nwXML = etree.Element("novelWriterXML")
statusKeys = ["s000000", "s000001", "s000002", "s000003"]
importKeys = ["i000004", "i000005", "i000006", "i000007"]
# File
# ====
@@ -483,7 +486,7 @@ def testCoreItem_XMLPackUnpack(mockGUI, caplog, constData):
theItem.setName("A Name")
theItem.setClass("NOVEL")
theItem.setType("FILE")
theItem.setImport(constData.importKeys[3])
theItem.setImport(importKeys[3])
theItem.setLayout("NOTE")
theItem.setExported(False)
theItem.setParaCount(3)
@@ -500,7 +503,7 @@ def testCoreItem_XMLPackUnpack(mockGUI, caplog, constData):
b'type="FILE" class="NOVEL" layout="NOTE"><meta charCount="7" wordCount="5" paraCount="3" '
b'cursorPos="11"/><name status="None" import="%s" exported="False">A Name</name></item>'
b'</content>'
) % bytes(constData.importKeys[3], encoding="utf8")
) % bytes(importKeys[3], encoding="utf8")
# Unpack
theItem = NWItem(theProject)
@@ -517,8 +520,8 @@ def testCoreItem_XMLPackUnpack(mockGUI, caplog, constData):
assert theItem.itemClass == nwItemClass.NOVEL
assert theItem.itemType == nwItemType.FILE
assert theItem.itemLayout == nwItemLayout.NOTE
assert theItem.itemStatus == constData.statusKeys[0] # Was None, should now be default
assert theItem.itemImport == constData.importKeys[3]
assert theItem.itemStatus == statusKeys[0] # Was None, should now be default
assert theItem.itemImport == importKeys[3]
# Folder
# ======
@@ -531,7 +534,7 @@ def testCoreItem_XMLPackUnpack(mockGUI, caplog, constData):
theItem.setName("A Name")
theItem.setClass("NOVEL")
theItem.setType("FOLDER")
theItem.setStatus(constData.statusKeys[1])
theItem.setStatus(statusKeys[1])
theItem.setLayout("NOTE")
theItem.setExpanded(True)
theItem.setExported(False)
@@ -549,7 +552,7 @@ def testCoreItem_XMLPackUnpack(mockGUI, caplog, constData):
b'type="FOLDER" class="NOVEL"><meta expanded="True"/><name status="%s" '
b'import="None">A Name</name></item>'
b'</content>'
) % bytes(constData.statusKeys[1], encoding="utf8")
) % bytes(statusKeys[1], encoding="utf8")
# Unpack
theItem = NWItem(theProject)
@@ -567,8 +570,8 @@ def testCoreItem_XMLPackUnpack(mockGUI, caplog, constData):
assert theItem.itemClass == nwItemClass.NOVEL
assert theItem.itemType == nwItemType.FOLDER
assert theItem.itemLayout == nwItemLayout.NO_LAYOUT
assert theItem.itemStatus == constData.statusKeys[1]
assert theItem.itemImport == constData.importKeys[0] # Was None, should now be default
assert theItem.itemStatus == statusKeys[1]
assert theItem.itemImport == importKeys[0] # Was None, should now be default
# Errors
# ======
+62 -70
View File
@@ -21,7 +21,6 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
import os
import pytest
import random
from shutil import copyfile
from zipfile import ZipFile
@@ -37,7 +36,7 @@ from novelwriter.constants import nwFiles
@pytest.mark.core
def testCoreProject_NewMinimal(fncDir, outDir, refDir, mockGUI):
def testCoreProject_NewMinimal(fncDir, outDir, refDir, mockGUI, mockRnd):
"""Create a new project from a project wizard dictionary. With
default setting, creating a Minimal project.
"""
@@ -45,9 +44,7 @@ def testCoreProject_NewMinimal(fncDir, outDir, refDir, mockGUI):
testFile = os.path.join(outDir, "coreProject_NewMinimal_nwProject.nwx")
compFile = os.path.join(refDir, "coreProject_NewMinimal_nwProject.nwx")
random.seed(42)
theProject = NWProject(mockGUI)
theProject.projTree.setSeed(42)
# Setting no data should fail
assert theProject.newProject({}) is False
@@ -86,7 +83,7 @@ def testCoreProject_NewMinimal(fncDir, outDir, refDir, mockGUI):
@pytest.mark.core
def testCoreProject_NewCustomA(fncDir, outDir, refDir, mockGUI):
def testCoreProject_NewCustomA(fncDir, outDir, refDir, mockGUI, mockRnd):
"""Create a new project from a project wizard dictionary.
Custom type with chapters and scenes.
"""
@@ -114,9 +111,7 @@ def testCoreProject_NewCustomA(fncDir, outDir, refDir, mockGUI):
"numScenes": 3,
"chFolders": True,
}
random.seed(42)
theProject = NWProject(mockGUI)
theProject.projTree.setSeed(42)
assert theProject.newProject(projData) is True
assert theProject.saveProject() is True
@@ -129,7 +124,7 @@ def testCoreProject_NewCustomA(fncDir, outDir, refDir, mockGUI):
@pytest.mark.core
def testCoreProject_NewCustomB(fncDir, outDir, refDir, mockGUI):
def testCoreProject_NewCustomB(fncDir, outDir, refDir, mockGUI, mockRnd):
"""Create a new project from a project wizard dictionary.
Custom type without chapters, but with scenes.
"""
@@ -157,9 +152,7 @@ def testCoreProject_NewCustomB(fncDir, outDir, refDir, mockGUI):
"numScenes": 6,
"chFolders": True,
}
random.seed(42)
theProject = NWProject(mockGUI)
theProject.projTree.setSeed(42)
assert theProject.newProject(projData) is True
assert theProject.saveProject() is True
@@ -186,7 +179,6 @@ def testCoreProject_NewSampleA(fncDir, tmpConf, mockGUI, tmpDir):
"popCustom": False,
}
theProject = NWProject(mockGUI)
theProject.projTree.setSeed(42)
# Sample set, but no path
assert not theProject.newProject({"popSample": True})
@@ -235,7 +227,6 @@ def testCoreProject_NewSampleB(monkeypatch, fncDir, tmpConf, mockGUI, tmpDir):
"popCustom": False,
}
theProject = NWProject(mockGUI)
theProject.projTree.setSeed(42)
# Make sure we do not pick up the novelwriter/assets/sample.zip file
tmpConf.assetPath = tmpDir
@@ -259,16 +250,14 @@ def testCoreProject_NewSampleB(monkeypatch, fncDir, tmpConf, mockGUI, tmpDir):
@pytest.mark.core
def testCoreProject_NewRoot(fncDir, outDir, refDir, mockGUI):
def testCoreProject_NewRoot(fncDir, outDir, refDir, mockGUI, mockRnd):
"""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")
random.seed(42)
theProject = NWProject(mockGUI)
theProject.projTree.setSeed(42)
assert theProject.newProject({"projPath": fncDir}) is True
assert theProject.setProjectPath(fncDir) is True
@@ -297,16 +286,14 @@ def testCoreProject_NewRoot(fncDir, outDir, refDir, mockGUI):
@pytest.mark.core
def testCoreProject_NewFile(fncDir, outDir, refDir, mockGUI):
def testCoreProject_NewFile(fncDir, outDir, refDir, mockGUI, mockRnd):
"""Check that new files can be added to the project.
"""
projFile = os.path.join(fncDir, "nwProject.nwx")
testFile = os.path.join(outDir, "coreProject_NewFile_nwProject.nwx")
compFile = os.path.join(refDir, "coreProject_NewFile_nwProject.nwx")
random.seed(42)
theProject = NWProject(mockGUI)
theProject.projTree.setSeed(42)
assert theProject.newProject({"projPath": fncDir}) is True
assert theProject.setProjectPath(fncDir) is True
@@ -686,50 +673,51 @@ def testCoreProject_AccessItems(nwMinimal, mockGUI):
@pytest.mark.core
def testCoreProject_StatusImport(mockGUI, fncDir, constData):
def testCoreProject_StatusImport(mockGUI, fncDir, mockRnd):
"""Test the status and importance flag handling.
"""
theProject = NWProject(mockGUI)
random.seed(42)
theProject.projTree.setSeed(42)
assert theProject.newProject({"projPath": fncDir}) is True
statusKeys = ["s000008", "s000009", "s00000a", "s00000b"]
importKeys = ["i00000c", "i00000d", "i00000e", "i00000f"]
# Change Status
# =============
theProject.projTree["44cb730c42048"].setStatus("Finished")
theProject.projTree["71ee45a3c0db9"].setStatus("Draft")
theProject.projTree["811786ad1ae74"].setStatus("Note")
theProject.projTree["25fc0e7096fc6"].setStatus("Finished")
theProject.projTree["0000000000014"].setStatus("Finished")
theProject.projTree["0000000000015"].setStatus("Draft")
theProject.projTree["0000000000016"].setStatus("Note")
theProject.projTree["0000000000017"].setStatus("Finished")
assert theProject.projTree["44cb730c42048"].itemStatus == constData.statusKeys[3]
assert theProject.projTree["71ee45a3c0db9"].itemStatus == constData.statusKeys[2]
assert theProject.projTree["811786ad1ae74"].itemStatus == constData.statusKeys[1]
assert theProject.projTree["25fc0e7096fc6"].itemStatus == constData.statusKeys[3]
assert theProject.projTree["0000000000014"].itemStatus == statusKeys[3]
assert theProject.projTree["0000000000015"].itemStatus == statusKeys[2]
assert theProject.projTree["0000000000016"].itemStatus == statusKeys[1]
assert theProject.projTree["0000000000017"].itemStatus == statusKeys[3]
newList = [
{"key": constData.statusKeys[0], "name": "New", "cols": (1, 1, 1)},
{"key": constData.statusKeys[1], "name": "Draft", "cols": (2, 2, 2)}, # These are swapped
{"key": constData.statusKeys[2], "name": "Note", "cols": (3, 3, 3)}, # These are swapped
{"key": constData.statusKeys[3], "name": "Edited", "cols": (4, 4, 4)}, # Renamed
{"key": None, "name": "Finished", "cols": (5, 5, 5)}, # New, reused name
{"key": statusKeys[0], "name": "New", "cols": (1, 1, 1)},
{"key": statusKeys[1], "name": "Draft", "cols": (2, 2, 2)}, # These are swapped
{"key": statusKeys[2], "name": "Note", "cols": (3, 3, 3)}, # These are swapped
{"key": statusKeys[3], "name": "Edited", "cols": (4, 4, 4)}, # Renamed
{"key": None, "name": "Finished", "cols": (5, 5, 5)}, # New, reused name
]
assert theProject.setStatusColours(None, None) is False
assert theProject.setStatusColours([], []) is False
assert theProject.setStatusColours(newList, []) is True
assert theProject.statusItems.name(constData.statusKeys[0]) == "New"
assert theProject.statusItems.name(constData.statusKeys[1]) == "Draft"
assert theProject.statusItems.name(constData.statusKeys[2]) == "Note"
assert theProject.statusItems.name(constData.statusKeys[3]) == "Edited"
assert theProject.statusItems.cols(constData.statusKeys[0]) == (1, 1, 1)
assert theProject.statusItems.cols(constData.statusKeys[1]) == (2, 2, 2)
assert theProject.statusItems.cols(constData.statusKeys[2]) == (3, 3, 3)
assert theProject.statusItems.cols(constData.statusKeys[3]) == (4, 4, 4)
assert theProject.statusItems.name(statusKeys[0]) == "New"
assert theProject.statusItems.name(statusKeys[1]) == "Draft"
assert theProject.statusItems.name(statusKeys[2]) == "Note"
assert theProject.statusItems.name(statusKeys[3]) == "Edited"
assert theProject.statusItems.cols(statusKeys[0]) == (1, 1, 1)
assert theProject.statusItems.cols(statusKeys[1]) == (2, 2, 2)
assert theProject.statusItems.cols(statusKeys[2]) == (3, 3, 3)
assert theProject.statusItems.cols(statusKeys[3]) == (4, 4, 4)
# Check the new entry
lastKey = theProject.statusItems.check("Finished")
assert lastKey == "sbc8960"
assert lastKey == "s000018"
assert theProject.statusItems.name(lastKey) == "Finished"
assert theProject.statusItems.cols(lastKey) == (5, 5, 5)
@@ -740,33 +728,33 @@ def testCoreProject_StatusImport(mockGUI, fncDir, constData):
# Change Importance
# =================
fHandle = theProject.newFile("Jane Doe", "73475cb40a568")
fHandle = theProject.newFile("Jane Doe", "8b9d2e465e150")
theProject.projTree[fHandle].setImport("Main")
assert theProject.projTree[fHandle].itemImport == constData.importKeys[3]
assert theProject.projTree[fHandle].itemImport == importKeys[3]
newList = [
{"key": constData.importKeys[0], "name": "New", "cols": (1, 1, 1)},
{"key": constData.importKeys[1], "name": "Minor", "cols": (2, 2, 2)},
{"key": constData.importKeys[2], "name": "Major", "cols": (3, 3, 3)},
{"key": constData.importKeys[3], "name": "Min", "cols": (4, 4, 4)},
{"key": importKeys[0], "name": "New", "cols": (1, 1, 1)},
{"key": importKeys[1], "name": "Minor", "cols": (2, 2, 2)},
{"key": importKeys[2], "name": "Major", "cols": (3, 3, 3)},
{"key": importKeys[3], "name": "Min", "cols": (4, 4, 4)},
{"key": None, "name": "Max", "cols": (5, 5, 5)},
]
assert theProject.setImportColours(None, None) is False
assert theProject.setImportColours([], []) is False
assert theProject.setImportColours(newList, []) is True
assert theProject.importItems.name(constData.importKeys[0]) == "New"
assert theProject.importItems.name(constData.importKeys[1]) == "Minor"
assert theProject.importItems.name(constData.importKeys[2]) == "Major"
assert theProject.importItems.name(constData.importKeys[3]) == "Min"
assert theProject.importItems.cols(constData.importKeys[0]) == (1, 1, 1)
assert theProject.importItems.cols(constData.importKeys[1]) == (2, 2, 2)
assert theProject.importItems.cols(constData.importKeys[2]) == (3, 3, 3)
assert theProject.importItems.cols(constData.importKeys[3]) == (4, 4, 4)
assert theProject.importItems.name(importKeys[0]) == "New"
assert theProject.importItems.name(importKeys[1]) == "Minor"
assert theProject.importItems.name(importKeys[2]) == "Major"
assert theProject.importItems.name(importKeys[3]) == "Min"
assert theProject.importItems.cols(importKeys[0]) == (1, 1, 1)
assert theProject.importItems.cols(importKeys[1]) == (2, 2, 2)
assert theProject.importItems.cols(importKeys[2]) == (3, 3, 3)
assert theProject.importItems.cols(importKeys[3]) == (4, 4, 4)
# Check the new entry
lastKey = theProject.importItems.check("Max")
assert lastKey == "i1a3d1f"
assert lastKey == "i00001a"
assert theProject.importItems.name(lastKey) == "Max"
assert theProject.importItems.cols(lastKey) == (5, 5, 5)
@@ -791,21 +779,25 @@ def testCoreProject_StatusImport(mockGUI, fncDir, constData):
assert theProject.closeProject() is True
# This should restore the default status/import labels
random.seed(42)
assert theProject.openProject(fncDir) is True
assert theProject.saveProject() is True
assert list(theProject.statusItems.keys()) == constData.statusKeys
assert list(theProject.importItems.keys()) == constData.importKeys
assert theProject.statusItems.name("s000023") == "New"
assert theProject.statusItems.name("s000024") == "Note"
assert theProject.statusItems.name("s000025") == "Draft"
assert theProject.statusItems.name("s000026") == "Finished"
assert theProject.importItems.name("i000027") == "New"
assert theProject.importItems.name("i000028") == "Minor"
assert theProject.importItems.name("i000029") == "Major"
assert theProject.importItems.name("i00002a") == "Main"
# END Test testCoreProject_StatusImport
@pytest.mark.core
def testCoreProject_Methods(monkeypatch, mockGUI, tmpDir, fncDir):
def testCoreProject_Methods(monkeypatch, mockGUI, tmpDir, fncDir, mockRnd):
"""Test other project class methods and functions.
"""
theProject = NWProject(mockGUI)
theProject.projTree.setSeed(42)
assert theProject.newProject({"projPath": fncDir}) is True
# Setting project path
@@ -864,7 +856,7 @@ def testCoreProject_Methods(monkeypatch, mockGUI, tmpDir, fncDir):
# Trash folder
# Should create on first call, and just returned on later calls
hTrash = "1a6562590ef19"
hTrash = "0000000000018"
assert theProject.projTree[hTrash] is None
assert theProject.trashFolder() == hTrash
assert theProject.trashFolder() == hTrash
@@ -934,14 +926,14 @@ def testCoreProject_Methods(monkeypatch, mockGUI, tmpDir, fncDir):
# Change project tree order
oldOrder = [
"73475cb40a568", "44cb730c42048", "71ee45a3c0db9",
"811786ad1ae74", "25fc0e7096fc6", "31489056e0916",
"98010bd9270f9", "0e17daca5f3e1", "1a6562590ef19",
"0000000000010", "0000000000011", "0000000000012",
"0000000000013", "0000000000014", "0000000000015",
"0000000000016", "0000000000017", "0000000000018",
]
newOrder = [
"811786ad1ae74", "25fc0e7096fc6", "31489056e0916",
"73475cb40a568", "44cb730c42048", "71ee45a3c0db9",
"98010bd9270f9", "0e17daca5f3e1",
"0000000000013", "0000000000014", "0000000000015",
"0000000000010", "0000000000011", "0000000000012",
"0000000000016", "0000000000017",
]
assert theProject.projTree.handles() == oldOrder
assert theProject.setTreeOrder(newOrder)
+85 -81
View File
@@ -28,9 +28,12 @@ from PyQt5.QtGui import QIcon
from novelwriter.core.status import NWStatus
statusKeys = ["sa3b179", "s1c8031", "s06671a", "sbdd640"]
importKeys = ["i466852", "i3eb13b", "i392456", "i23b8c1"]
@pytest.mark.core
def testCoreStatus_Internal(constData):
def testCoreStatus_Internal():
"""Test all the internal functions of the NWStatus class.
"""
random.seed(42)
@@ -43,19 +46,19 @@ def testCoreStatus_Internal(constData):
# Generate Key
# ============
assert theStatus._newKey() == constData.statusKeys[0]
assert theStatus._newKey() == constData.statusKeys[1]
assert theStatus._newKey() == statusKeys[0]
assert theStatus._newKey() == statusKeys[1]
# Key collision, should move to key 3
theStatus.write(constData.statusKeys[2], "Crash", (0, 0, 0))
assert theStatus._newKey() == constData.statusKeys[3]
theStatus.write(statusKeys[2], "Crash", (0, 0, 0))
assert theStatus._newKey() == statusKeys[3]
assert theImport._newKey() == constData.importKeys[0]
assert theImport._newKey() == constData.importKeys[1]
assert theImport._newKey() == importKeys[0]
assert theImport._newKey() == importKeys[1]
# Key collision, should move to key 3
theImport.write(constData.importKeys[2], "Crash", (0, 0, 0))
assert theImport._newKey() == constData.importKeys[3]
theImport.write(importKeys[2], "Crash", (0, 0, 0))
assert theImport._newKey() == importKeys[3]
# Check Key
# =========
@@ -84,18 +87,19 @@ def testCoreStatus_Internal(constData):
@pytest.mark.core
def testCoreStatus_Iterator(constData):
def testCoreStatus_Iterator():
"""Test the iterator functions of the NWStatus class.
"""
random.seed(42)
theStatus = NWStatus(NWStatus.STATUS)
theStatus.write(None, "New", (100, 100, 100))
theStatus.write(None, "Note", (200, 50, 0))
theStatus.write(None, "Draft", (200, 150, 0))
theStatus.write(None, "Finished", (50, 200, 0))
# Direct access
entry = theStatus[constData.statusKeys[0]]
entry = theStatus[statusKeys[0]]
assert entry["cols"] == (100, 100, 100)
assert entry["name"] == "New"
assert entry["count"] == 0
@@ -107,11 +111,11 @@ def testCoreStatus_Iterator(constData):
assert len(theStatus) == 4
# Keys
assert list(theStatus.keys()) == constData.statusKeys
assert list(theStatus.keys()) == statusKeys
# Items
for index, (key, entry) in enumerate(theStatus.items()):
assert key == constData.statusKeys[index]
assert key == statusKeys[index]
assert "cols" in entry
assert "name" in entry
assert "count" in entry
@@ -128,7 +132,7 @@ def testCoreStatus_Iterator(constData):
@pytest.mark.core
def testCoreStatus_Entries(constData):
def testCoreStatus_Entries():
"""Test all the simple setters for the NWStatus class.
"""
random.seed(42)
@@ -138,74 +142,74 @@ def testCoreStatus_Entries(constData):
# =====
# Have a key
theStatus.write(constData.statusKeys[0], "Entry 1", (200, 100, 50))
assert theStatus[constData.statusKeys[0]]["name"] == "Entry 1"
assert theStatus[constData.statusKeys[0]]["cols"] == (200, 100, 50)
theStatus.write(statusKeys[0], "Entry 1", (200, 100, 50))
assert theStatus[statusKeys[0]]["name"] == "Entry 1"
assert theStatus[statusKeys[0]]["cols"] == (200, 100, 50)
# Don't have a key
theStatus.write(None, "Entry 2", (210, 110, 60))
assert theStatus[constData.statusKeys[1]]["name"] == "Entry 2"
assert theStatus[constData.statusKeys[1]]["cols"] == (210, 110, 60)
assert theStatus[statusKeys[1]]["name"] == "Entry 2"
assert theStatus[statusKeys[1]]["cols"] == (210, 110, 60)
# Wrong colour spec
theStatus.write(None, "Entry 3", "what?")
assert theStatus[constData.statusKeys[2]]["name"] == "Entry 3"
assert theStatus[constData.statusKeys[2]]["cols"] == (100, 100, 100)
assert theStatus[statusKeys[2]]["name"] == "Entry 3"
assert theStatus[statusKeys[2]]["cols"] == (100, 100, 100)
# Wrong colour count
theStatus.write(None, "Entry 4", (10, 20))
assert theStatus[constData.statusKeys[3]]["name"] == "Entry 4"
assert theStatus[constData.statusKeys[3]]["cols"] == (100, 100, 100)
assert theStatus[statusKeys[3]]["name"] == "Entry 4"
assert theStatus[statusKeys[3]]["cols"] == (100, 100, 100)
# Check reverse map
assert theStatus._reverse == {
"Entry 1": constData.statusKeys[0],
"Entry 2": constData.statusKeys[1],
"Entry 3": constData.statusKeys[2],
"Entry 4": constData.statusKeys[3],
"Entry 1": statusKeys[0],
"Entry 2": statusKeys[1],
"Entry 3": statusKeys[2],
"Entry 4": statusKeys[3],
}
# Check
# =====
# Normal lookup
for key in constData.statusKeys:
for key in statusKeys:
assert theStatus.check(key) == key
# Reverse map lookup
assert theStatus.check("Entry 1") == constData.statusKeys[0]
assert theStatus.check("Entry 2") == constData.statusKeys[1]
assert theStatus.check("Entry 3") == constData.statusKeys[2]
assert theStatus.check("Entry 4") == constData.statusKeys[3]
assert theStatus.check("Entry 1") == statusKeys[0]
assert theStatus.check("Entry 2") == statusKeys[1]
assert theStatus.check("Entry 3") == statusKeys[2]
assert theStatus.check("Entry 4") == statusKeys[3]
# Non-existing name
assert theStatus.check("Entry 5") == constData.statusKeys[0]
assert theStatus.check("Entry 5") == statusKeys[0]
# Name Access
# ===========
assert theStatus.name(constData.statusKeys[0]) == "Entry 1"
assert theStatus.name(constData.statusKeys[1]) == "Entry 2"
assert theStatus.name(constData.statusKeys[2]) == "Entry 3"
assert theStatus.name(constData.statusKeys[3]) == "Entry 4"
assert theStatus.name(statusKeys[0]) == "Entry 1"
assert theStatus.name(statusKeys[1]) == "Entry 2"
assert theStatus.name(statusKeys[2]) == "Entry 3"
assert theStatus.name(statusKeys[3]) == "Entry 4"
assert theStatus.name("blablabla") == "Entry 1"
# Colour Access
# =============
assert theStatus.cols(constData.statusKeys[0]) == (200, 100, 50)
assert theStatus.cols(constData.statusKeys[1]) == (210, 110, 60)
assert theStatus.cols(constData.statusKeys[2]) == (100, 100, 100)
assert theStatus.cols(constData.statusKeys[3]) == (100, 100, 100)
assert theStatus.cols(statusKeys[0]) == (200, 100, 50)
assert theStatus.cols(statusKeys[1]) == (210, 110, 60)
assert theStatus.cols(statusKeys[2]) == (100, 100, 100)
assert theStatus.cols(statusKeys[3]) == (100, 100, 100)
assert theStatus.cols("blablabla") == (200, 100, 50)
# Icon Access
# ===========
assert isinstance(theStatus.icon(constData.statusKeys[0]), QIcon)
assert isinstance(theStatus.icon(constData.statusKeys[1]), QIcon)
assert isinstance(theStatus.icon(constData.statusKeys[2]), QIcon)
assert isinstance(theStatus.icon(constData.statusKeys[3]), QIcon)
assert isinstance(theStatus.icon(statusKeys[0]), QIcon)
assert isinstance(theStatus.icon(statusKeys[1]), QIcon)
assert isinstance(theStatus.icon(statusKeys[2]), QIcon)
assert isinstance(theStatus.icon(statusKeys[3]), QIcon)
assert isinstance(theStatus.icon("blablabla"), QIcon)
# Increment and Count Access
@@ -214,26 +218,26 @@ def testCoreStatus_Entries(constData):
countTo = [3, 5, 7, 9]
for i, n in enumerate(countTo):
for _ in range(n):
theStatus.increment(constData.statusKeys[i])
theStatus.increment(statusKeys[i])
assert theStatus.count(constData.statusKeys[0]) == countTo[0]
assert theStatus.count(constData.statusKeys[1]) == countTo[1]
assert theStatus.count(constData.statusKeys[2]) == countTo[2]
assert theStatus.count(constData.statusKeys[3]) == countTo[3]
assert theStatus.count(statusKeys[0]) == countTo[0]
assert theStatus.count(statusKeys[1]) == countTo[1]
assert theStatus.count(statusKeys[2]) == countTo[2]
assert theStatus.count(statusKeys[3]) == countTo[3]
assert theStatus.count("blablabla") == countTo[0]
theStatus.resetCounts()
assert theStatus.count(constData.statusKeys[0]) == 0
assert theStatus.count(constData.statusKeys[1]) == 0
assert theStatus.count(constData.statusKeys[2]) == 0
assert theStatus.count(constData.statusKeys[3]) == 0
assert theStatus.count(statusKeys[0]) == 0
assert theStatus.count(statusKeys[1]) == 0
assert theStatus.count(statusKeys[2]) == 0
assert theStatus.count(statusKeys[3]) == 0
# Reorder
# =======
cOrder = list(theStatus.keys())
assert cOrder == constData.statusKeys
assert cOrder == statusKeys
# Wrong length
assert theStatus.reorder([]) is False
@@ -243,10 +247,10 @@ def testCoreStatus_Entries(constData):
# Actual reaorder
nOrder = [
constData.statusKeys[0],
constData.statusKeys[2],
constData.statusKeys[1],
constData.statusKeys[3],
statusKeys[0],
statusKeys[2],
statusKeys[1],
statusKeys[3],
]
assert theStatus.reorder(nOrder) is True
assert list(theStatus.keys()) == nOrder
@@ -282,15 +286,15 @@ def testCoreStatus_Entries(constData):
assert theStatus.remove("blablabla") is False
# Non-zero entry
theStatus.increment(constData.statusKeys[3])
assert theStatus.remove(constData.statusKeys[3]) is False
theStatus.increment(statusKeys[3])
assert theStatus.remove(statusKeys[3]) is False
# Delete last entry
theStatus.resetCounts()
lastName = theStatus.name(constData.statusKeys[3])
lastName = theStatus.name(statusKeys[3])
assert lastName == "Entry 4"
assert theStatus.remove(constData.statusKeys[3]) is True
assert theStatus.check(constData.statusKeys[3]) == theStatus._default
assert theStatus.remove(statusKeys[3]) is True
assert theStatus.check(statusKeys[3]) == theStatus._default
assert theStatus.check(lastName) == theStatus._default
# Delete default entry, Entry 2 is new default
@@ -300,8 +304,8 @@ def testCoreStatus_Entries(constData):
assert theStatus.name(firstName) == "Entry 2"
# Remove remaining entries
assert theStatus.remove(constData.statusKeys[1]) is True
assert theStatus.remove(constData.statusKeys[2]) is True
assert theStatus.remove(statusKeys[1]) is True
assert theStatus.remove(statusKeys[2]) is True
assert len(theStatus) == 0
assert theStatus._default is None
@@ -310,7 +314,7 @@ def testCoreStatus_Entries(constData):
@pytest.mark.core
def testCoreStatus_XMLPackUnpack(constData):
def testCoreStatus_XMLPackUnpack():
"""Test all the XML pack/unpack of the NWStatus class.
"""
random.seed(42)
@@ -323,7 +327,7 @@ def testCoreStatus_XMLPackUnpack(constData):
countTo = [3, 5, 7, 9]
for i, n in enumerate(countTo):
for _ in range(n):
theStatus.increment(constData.statusKeys[i])
theStatus.increment(statusKeys[i])
nwXML = etree.Element("novelWriterXML")
@@ -343,18 +347,18 @@ def testCoreStatus_XMLPackUnpack(constData):
theStatus = NWStatus(NWStatus.STATUS)
assert theStatus.unpackXML(xStatus)
assert len(theStatus._store) == 4
assert list(theStatus._store.keys()) == constData.statusKeys
assert theStatus._store[constData.statusKeys[0]]["name"] == "New"
assert theStatus._store[constData.statusKeys[1]]["name"] == "Note"
assert theStatus._store[constData.statusKeys[2]]["name"] == "Draft"
assert theStatus._store[constData.statusKeys[3]]["name"] == "Finished"
assert theStatus._store[constData.statusKeys[0]]["cols"] == (100, 100, 100)
assert theStatus._store[constData.statusKeys[1]]["cols"] == (200, 50, 0)
assert theStatus._store[constData.statusKeys[2]]["cols"] == (200, 150, 0)
assert theStatus._store[constData.statusKeys[3]]["cols"] == (50, 200, 0)
assert theStatus._store[constData.statusKeys[0]]["count"] == countTo[0]
assert theStatus._store[constData.statusKeys[1]]["count"] == countTo[1]
assert theStatus._store[constData.statusKeys[2]]["count"] == countTo[2]
assert theStatus._store[constData.statusKeys[3]]["count"] == countTo[3]
assert list(theStatus._store.keys()) == statusKeys
assert theStatus._store[statusKeys[0]]["name"] == "New"
assert theStatus._store[statusKeys[1]]["name"] == "Note"
assert theStatus._store[statusKeys[2]]["name"] == "Draft"
assert theStatus._store[statusKeys[3]]["name"] == "Finished"
assert theStatus._store[statusKeys[0]]["cols"] == (100, 100, 100)
assert theStatus._store[statusKeys[1]]["cols"] == (200, 50, 0)
assert theStatus._store[statusKeys[2]]["cols"] == (200, 150, 0)
assert theStatus._store[statusKeys[3]]["cols"] == (50, 200, 0)
assert theStatus._store[statusKeys[0]]["count"] == countTo[0]
assert theStatus._store[statusKeys[1]]["count"] == countTo[1]
assert theStatus._store[statusKeys[2]]["count"] == countTo[2]
assert theStatus._store[statusKeys[3]]["count"] == countTo[3]
# END Test testCoreStatus_XMLPackUnpack
-1
View File
@@ -136,7 +136,6 @@ def testCoreToken_TextOps(monkeypatch, nwMinimal, mockGUI):
"""Test handling files and text in the Tokenizer class.
"""
theProject = NWProject(mockGUI)
theProject.projTree.setSeed(42)
theProject.projLang = "en"
theProject._loadProjectLocalisation()
+53 -72
View File
@@ -24,7 +24,6 @@ import pytest
import random
from lxml import etree
from hashlib import sha256
from tools import readFile
@@ -34,10 +33,9 @@ from novelwriter.constants import nwFiles
@pytest.fixture(scope="function")
def mockItems(mockGUI):
def mockItems(mockGUI, mockRnd):
"""Create a list of mock items.
"""
random.seed(42)
theProject = NWProject(mockGUI)
itemA = NWItem(theProject)
@@ -118,9 +116,6 @@ def testCoreTree_BuildTree(mockGUI, mockItems):
theProject = NWProject(mockGUI)
theTree = NWTree(theProject)
theTree.setSeed(42)
assert theTree._handleSeed == 42
# Check that tree is empty (calls NWTree.__bool__)
assert bool(theTree) is False
@@ -196,10 +191,11 @@ def testCoreTree_BuildTree(mockGUI, mockItems):
assert len(theTree) == len(mockItems) + 1
theList = theTree.handles()
assert theList[-1] == "73475cb40a568"
nHandle = "0000000000010"
assert theList[-1] == nHandle
# Try to add existing handle
assert theTree.append("73475cb40a568", None, itemT) is False
assert theTree.append(nHandle, None, itemT) is False
assert len(theTree) == len(mockItems) + 1
# Delete a non-existing item
@@ -207,9 +203,9 @@ def testCoreTree_BuildTree(mockGUI, mockItems):
assert len(theTree) == len(mockItems) + 1
# Delete the last item
del theTree["73475cb40a568"]
del theTree[nHandle]
assert len(theTree) == len(mockItems)
assert "73475cb40a568" not in theTree
assert nHandle not in theTree
# Delete the Novel, Archive and Trash folders
del theTree["a000000000001"]
@@ -310,44 +306,31 @@ def testCoreTree_Methods(mockGUI, mockItems):
@pytest.mark.core
def testCoreTree_MakeHandles(monkeypatch, mockGUI):
def testCoreTree_MakeHandles(mockGUI):
"""Test generating item handles.
"""
random.seed(42)
theProject = NWProject(mockGUI)
theTree = NWTree(theProject)
theTree.setSeed(42)
handles = ["1c803a3b1799d", "bdd6406671ad1", "3eb1346685257", "23b8c392456de"]
random.seed(42)
tHandle = theTree._makeHandle()
assert tHandle == "73475cb40a568"
assert tHandle == handles[0]
theTree._projTree[handles[0]] = None
# Add the next in line to the project to force duplicate
theTree._projTree["44cb730c42048"] = None
theTree._projTree[handles[1]] = None
tHandle = theTree._makeHandle()
assert tHandle == "71ee45a3c0db9"
# Fix the time() function and force a handle collission
theTree.setSeed(None)
theTree._handleCount = 0
monkeypatch.setattr("novelwriter.core.tree.time", lambda: 123.4)
assert tHandle == handles[2]
theTree._projTree[handles[2]] = None
# Reset the seed to force collissions, which should still end up
# returning the next handle in the sequence
random.seed(42)
tHandle = theTree._makeHandle()
theTree._projTree[tHandle] = None
newSeed = "123.4_0_"
assert tHandle == sha256(newSeed.encode()).hexdigest()[0:13]
tHandle = theTree._makeHandle()
theTree._projTree[tHandle] = None
newSeed = "123.4_1_"
assert tHandle == sha256(newSeed.encode()).hexdigest()[0:13]
# Reset the count and the handle for 0 and 1 should be duplicates
# which forces the function to add the '!'
theTree._handleCount = 0
tHandle = theTree._makeHandle()
theTree._projTree[tHandle] = None
newSeed = "123.4_1_!"
assert tHandle == sha256(newSeed.encode()).hexdigest()[0:13]
assert tHandle == handles[3]
# END Test testCoreTree_MakeHandles
@@ -412,7 +395,7 @@ def testCoreTree_Reorder(mockGUI, mockItems):
@pytest.mark.core
def testCoreTree_XMLPackUnpack(mockGUI, mockItems, constData):
def testCoreTree_XMLPackUnpack(mockGUI, mockItems):
"""Test packing and unpacking the tree to and from XML.
"""
theProject = NWProject(mockGUI)
@@ -426,41 +409,39 @@ def testCoreTree_XMLPackUnpack(mockGUI, mockItems, constData):
nwXML = etree.Element("novelWriterXML")
theTree.packXML(nwXML)
assert etree.tostring(nwXML, pretty_print=False, encoding="utf-8") == bytes((
'<novelWriterXML>'
'<content count="8">'
'<item handle="a000000000001" parent="None" root="a000000000001" order="0" type="ROOT" '
'class="NOVEL"><meta expanded="True"/><name status="{s0}" '
'import="{i0}">Novel</name></item>'
'<item handle="b000000000001" parent="a000000000001" root="a000000000001" order="0" '
'type="FOLDER" class="NOVEL"><meta expanded="True"/><name status="{s0}" '
'import="{i0}">Act One</name></item>'
'<item handle="c000000000001" parent="b000000000001" root="a000000000001" order="0" '
'type="FILE" class="NOVEL" layout="DOCUMENT"><meta charCount="300" wordCount="50" '
'paraCount="2" cursorPos="0"/><name status="{s0}" import="{i0}" '
'exported="True">Chapter One</name></item>'
'<item handle="c000000000002" parent="b000000000001" root="a000000000001" order="0" '
'type="FILE" class="NOVEL" layout="DOCUMENT"><meta charCount="3000" wordCount="500" '
'paraCount="20" cursorPos="0"/><name status="{s0}" import="{i0}" '
'exported="True">Scene One</name></item>'
'<item handle="a000000000002" parent="None" root="a000000000002" order="0" type="ROOT" '
'class="ARCHIVE"><meta expanded="False"/><name status="{s0}" '
'import="{i0}">Outtakes</name></item>'
'<item handle="a000000000003" parent="None" root="a000000000003" order="0" type="TRASH" '
'class="TRASH"><meta expanded="False"/><name status="{s0}" '
'import="{i0}">Trash</name></item>'
'<item handle="a000000000004" parent="None" root="a000000000004" order="0" type="ROOT" '
'class="CHARACTER"><meta expanded="True"/><name status="{s0}" '
'import="{i0}">Characters</name></item>'
'<item handle="b000000000002" parent="a000000000004" root="a000000000004" order="0" '
'type="FILE" class="CHARACTER" layout="NOTE"><meta charCount="2000" wordCount="400" '
'paraCount="16" cursorPos="0"/><name status="{s0}" import="{i0}" '
'exported="True">Jane Doe</name></item>'
'</content>'
'</novelWriterXML>'
).format(
s0=constData.statusKeys[0], i0=constData.importKeys[0]
), encoding="utf8")
assert etree.tostring(nwXML, pretty_print=False, encoding="utf-8") == (
b'<novelWriterXML>'
b'<content count="8">'
b'<item handle="a000000000001" parent="None" root="a000000000001" order="0" type="ROOT" '
b'class="NOVEL"><meta expanded="True"/><name status="s000000" '
b'import="i000004">Novel</name></item>'
b'<item handle="b000000000001" parent="a000000000001" root="a000000000001" order="0" '
b'type="FOLDER" class="NOVEL"><meta expanded="True"/><name status="s000000" '
b'import="i000004">Act One</name></item>'
b'<item handle="c000000000001" parent="b000000000001" root="a000000000001" order="0" '
b'type="FILE" class="NOVEL" layout="DOCUMENT"><meta charCount="300" wordCount="50" '
b'paraCount="2" cursorPos="0"/><name status="s000000" import="i000004" '
b'exported="True">Chapter One</name></item>'
b'<item handle="c000000000002" parent="b000000000001" root="a000000000001" order="0" '
b'type="FILE" class="NOVEL" layout="DOCUMENT"><meta charCount="3000" wordCount="500" '
b'paraCount="20" cursorPos="0"/><name status="s000000" import="i000004" '
b'exported="True">Scene One</name></item>'
b'<item handle="a000000000002" parent="None" root="a000000000002" order="0" type="ROOT" '
b'class="ARCHIVE"><meta expanded="False"/><name status="s000000" '
b'import="i000004">Outtakes</name></item>'
b'<item handle="a000000000003" parent="None" root="a000000000003" order="0" type="TRASH" '
b'class="TRASH"><meta expanded="False"/><name status="s000000" '
b'import="i000004">Trash</name></item>'
b'<item handle="a000000000004" parent="None" root="a000000000004" order="0" type="ROOT" '
b'class="CHARACTER"><meta expanded="True"/><name status="s000000" '
b'import="i000004">Characters</name></item>'
b'<item handle="b000000000002" parent="a000000000004" root="a000000000004" order="0" '
b'type="FILE" class="CHARACTER" layout="NOTE"><meta charCount="2000" wordCount="400" '
b'paraCount="16" cursorPos="0"/><name status="s000000" import="i000004" '
b'exported="True">Jane Doe</name></item>'
b'</content>'
b'</novelWriterXML>'
)
theTree.clear()
assert len(theTree) == 0
+15 -13
View File
@@ -19,21 +19,21 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
import pytest
import os
import pytest
from tools import getGuiItem, readFile, writeFile
from mock import causeOSError
from tools import getGuiItem, readFile, writeFile
from PyQt5.QtWidgets import QAction, QMessageBox, QDialog
from novelwriter.dialogs import GuiDocMerge, GuiItemEditor
from novelwriter.enum import nwItemType, nwWidget
from novelwriter.dialogs import GuiDocMerge, GuiItemEditor
from novelwriter.core.tree import NWTree
@pytest.mark.gui
def testDlgMerge_Main(qtbot, monkeypatch, nwGUI, fncProj):
def testDlgMerge_Main(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
"""Test the merge documents tool.
"""
# Block message box
@@ -41,17 +41,17 @@ def testDlgMerge_Main(qtbot, monkeypatch, nwGUI, fncProj):
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Ok)
# Create a new project
nwGUI.theProject.projTree.setSeed(42)
assert nwGUI.newProject({"projPath": fncProj})
# Handles for new objects
hChapterDir = "31489056e0916"
hChapterOne = "98010bd9270f9"
hSceneOne = "0e17daca5f3e1"
hSceneTwo = "1a6562590ef19"
hSceneThree = "031b4af5197ec"
hSceneFour = "41cfc0d1f2d12"
hMergedDoc = "2858dcd1057d3"
hNovelRoot = "0000000000008"
hChapterDir = "000000000000d"
hChapterOne = "000000000000e"
hSceneOne = "000000000000f"
hSceneTwo = "0000000000010"
hSceneThree = "0000000000011"
hSceneFour = "0000000000012"
hMergedDoc = "0000000000023"
# Add Project Content
monkeypatch.setattr(GuiItemEditor, "exec_", lambda *a: QDialog.Accepted)
@@ -137,7 +137,7 @@ def testDlgMerge_Main(qtbot, monkeypatch, nwGUI, fncProj):
assert os.path.isfile(mergedFile)
assert readFile(mergedFile) == (
"%%%%~name: New Chapter\n"
"%%%%~path: 73475cb40a568/2858dcd1057d3\n"
"%%%%~path: %s/%s\n"
"%%%%~kind: NOVEL/DOCUMENT\n"
"%s\n\n"
"%s\n\n"
@@ -145,6 +145,8 @@ def testDlgMerge_Main(qtbot, monkeypatch, nwGUI, fncProj):
"%s\n\n"
"%s\n\n"
) % (
hNovelRoot,
hMergedDoc,
tChapterOne.strip(),
tSceneOne.strip(),
tSceneTwo.strip(),
+30 -30
View File
@@ -19,22 +19,22 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
import pytest
import os
import pytest
from tools import getGuiItem, readFile, writeFile
from mock import causeOSError
from tools import getGuiItem, readFile, writeFile
from PyQt5.QtWidgets import QAction, QMessageBox, QDialog
from novelwriter.dialogs import GuiDocSplit, GuiItemEditor
from novelwriter.enum import nwItemType, nwWidget
from novelwriter.core.document import NWDoc
from novelwriter.dialogs import GuiDocSplit, GuiItemEditor
from novelwriter.core.tree import NWTree
from novelwriter.core.document import NWDoc
@pytest.mark.gui
def testDlgSplit_Main(qtbot, monkeypatch, nwGUI, fncProj):
def testDlgSplit_Main(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
"""Test the split document tool.
"""
# Block message box
@@ -42,20 +42,20 @@ def testDlgSplit_Main(qtbot, monkeypatch, nwGUI, fncProj):
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Ok)
# Create a new project
nwGUI.theProject.projTree.setSeed(42)
assert nwGUI.newProject({"projPath": fncProj}) is True
# Handles for new objects
hNovelRoot = "73475cb40a568"
hChapterDir = "31489056e0916"
hToSplit = "1a6562590ef19"
hPartition = "41cfc0d1f2d12"
hChapterOne = "2858dcd1057d3"
hSceneOne = "2fca346db6561"
hSceneTwo = "02d20bbd7e394"
hSceneThree = "7688b6ef52555"
hSceneFour = "c837649cce43f"
hSceneFive = "6208ef0f7750c"
hNovelRoot = "0000000000008"
hChapterDir = "000000000000d"
hToSplit = "0000000000010"
hNewFolder = "0000000000021"
hPartition = "0000000000022"
hChapterOne = "0000000000023"
hSceneOne = "0000000000024"
hSceneTwo = "0000000000025"
hSceneThree = "0000000000026"
hSceneFour = "0000000000027"
hSceneFive = "0000000000028"
# Add Project Content
monkeypatch.setattr(GuiItemEditor, "exec_", lambda *a: QDialog.Accepted)
@@ -173,52 +173,52 @@ def testDlgSplit_Main(qtbot, monkeypatch, nwGUI, fncProj):
assert readFile(os.path.join(contentDir, hPartition+".nwd")) == (
"%%%%~name: Nantucket\n"
"%%%%~path: 031b4af5197ec/%s\n"
"%%%%~path: %s/%s\n"
"%%%%~kind: NOVEL/DOCUMENT\n"
"%s\n\n"
) % (hPartition, tPartition)
) % (hNewFolder, hPartition, tPartition)
assert readFile(os.path.join(contentDir, hChapterOne+".nwd")) == (
"%%%%~name: Chapter One\n"
"%%%%~path: 031b4af5197ec/%s\n"
"%%%%~path: %s/%s\n"
"%%%%~kind: NOVEL/DOCUMENT\n"
"%s\n\n"
) % (hChapterOne, tChapterOne)
) % (hNewFolder, hChapterOne, tChapterOne)
assert readFile(os.path.join(contentDir, hSceneOne+".nwd")) == (
"%%%%~name: Scene One\n"
"%%%%~path: 031b4af5197ec/%s\n"
"%%%%~path: %s/%s\n"
"%%%%~kind: NOVEL/DOCUMENT\n"
"%s\n\n"
) % (hSceneOne, tSceneOne)
) % (hNewFolder, hSceneOne, tSceneOne)
assert readFile(os.path.join(contentDir, hSceneTwo+".nwd")) == (
"%%%%~name: Scene Two\n"
"%%%%~path: 031b4af5197ec/%s\n"
"%%%%~path: %s/%s\n"
"%%%%~kind: NOVEL/DOCUMENT\n"
"%s\n\n"
) % (hSceneTwo, tSceneTwo)
) % (hNewFolder, hSceneTwo, tSceneTwo)
assert readFile(os.path.join(contentDir, hSceneThree+".nwd")) == (
"%%%%~name: Scene Three\n"
"%%%%~path: 031b4af5197ec/%s\n"
"%%%%~path: %s/%s\n"
"%%%%~kind: NOVEL/DOCUMENT\n"
"%s\n\n"
) % (hSceneThree, tSceneThree)
) % (hNewFolder, hSceneThree, tSceneThree)
assert readFile(os.path.join(contentDir, hSceneFour+".nwd")) == (
"%%%%~name: Scene Four\n"
"%%%%~path: 031b4af5197ec/%s\n"
"%%%%~path: %s/%s\n"
"%%%%~kind: NOVEL/DOCUMENT\n"
"%s\n\n"
) % (hSceneFour, tSceneFour)
) % (hNewFolder, hSceneFour, tSceneFour)
assert readFile(os.path.join(contentDir, hSceneFive+".nwd")) == (
"%%%%~name: The End\n"
"%%%%~path: 031b4af5197ec/%s\n"
"%%%%~path: %s/%s\n"
"%%%%~kind: NOVEL/DOCUMENT\n"
"%s\n\n"
) % (hSceneFive, tSceneFive)
) % (hNewFolder, hSceneFive, tSceneFive)
# OS error
with monkeypatch.context() as mp:
+35 -37
View File
@@ -20,7 +20,6 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
import pytest
import random
from tools import getGuiItem
@@ -31,9 +30,12 @@ from novelwriter.enum import nwItemLayout, nwItemType
from novelwriter.dialogs import GuiItemEditor
from novelwriter.core.tree import NWTree
statusKeys = ["s000000", "s000001", "s000002", "s000003"]
importKeys = ["i000004", "i000005", "i000006", "i000007"]
@pytest.mark.gui
def testDlgItemEditor_Dialog(qtbot, monkeypatch, nwGUI, fncProj):
def testDlgItemEditor_Dialog(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
"""Test launching the item editor dialog from GuiMain.
"""
# Block message box
@@ -46,15 +48,15 @@ def testDlgItemEditor_Dialog(qtbot, monkeypatch, nwGUI, fncProj):
assert nwGUI.editItem() is False
# Create and Open Project
nwGUI.theProject.projTree.setSeed(42)
assert nwGUI.newProject({"projPath": fncProj})
tHandle = "000000000000f"
# No Selection
nwGUI.treeView.clearSelection()
assert nwGUI.editItem() is False
# Force opening from editor
assert nwGUI.openDocument("0e17daca5f3e1")
assert nwGUI.openDocument(tHandle)
nwGUI.isFocusMode = True
# Block Tree Lookup
@@ -63,9 +65,9 @@ def testDlgItemEditor_Dialog(qtbot, monkeypatch, nwGUI, fncProj):
assert nwGUI.editItem() is False
# Invalid Type
nwGUI.theProject.projTree["0e17daca5f3e1"]._type = nwItemType.NO_TYPE
nwGUI.theProject.projTree[tHandle]._type = nwItemType.NO_TYPE
assert nwGUI.editItem() is False
nwGUI.theProject.projTree["0e17daca5f3e1"]._type = nwItemType.FILE
nwGUI.theProject.projTree[tHandle]._type = nwItemType.FILE
# Open Properly
assert nwGUI.editItem() is True
@@ -89,7 +91,7 @@ def testDlgItemEditor_Dialog(qtbot, monkeypatch, nwGUI, fncProj):
@pytest.mark.gui
def testDlgItemEditor_Novel(qtbot, monkeypatch, nwGUI, fncProj, constData):
def testDlgItemEditor_Novel(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
"""Test the item editor dialog for a novel document.
"""
# Block message box
@@ -97,13 +99,13 @@ def testDlgItemEditor_Novel(qtbot, monkeypatch, nwGUI, fncProj, constData):
monkeypatch.setattr(GuiProjectTree, "hasFocus", lambda *a: True)
# Create Project and Open Document
random.seed(42)
nwGUI.theProject.projTree.setSeed(42)
assert nwGUI.newProject({"projPath": fncProj})
assert nwGUI.theProject.statusItems.name(constData.statusKeys[0]) == "New"
assert nwGUI.theProject.statusItems.name(constData.statusKeys[1]) == "Note"
tHandle = "000000000000f"
assert nwGUI.openDocument("0e17daca5f3e1") is True
assert nwGUI.theProject.statusItems.name(statusKeys[0]) == "New"
assert nwGUI.theProject.statusItems.name(statusKeys[1]) == "Note"
assert nwGUI.openDocument(tHandle) is True
# Check that an invalid handle is managed
itemEdit = GuiItemEditor(nwGUI, "whatever")
@@ -111,12 +113,12 @@ def testDlgItemEditor_Novel(qtbot, monkeypatch, nwGUI, fncProj, constData):
itemEdit._doClose()
# Edit a Document
itemEdit = GuiItemEditor(nwGUI, "0e17daca5f3e1")
itemEdit = GuiItemEditor(nwGUI, tHandle)
itemEdit.show()
# Check Existing Settings
assert itemEdit.editName.text() == "New Scene"
assert itemEdit.editStatus.currentData() == constData.statusKeys[0]
assert itemEdit.editStatus.currentData() == statusKeys[0]
assert itemEdit.editLayout.currentData() == nwItemLayout.DOCUMENT
assert itemEdit.editExport.isChecked() is True
@@ -130,12 +132,12 @@ def testDlgItemEditor_Novel(qtbot, monkeypatch, nwGUI, fncProj, constData):
# Check New Settings
itemEdit._doSave()
assert itemEdit.theItem.itemName == "Great Scene"
assert itemEdit.theItem.itemStatus == constData.statusKeys[1]
assert itemEdit.theItem.itemStatus == statusKeys[1]
assert itemEdit.theItem.itemLayout == nwItemLayout.NOTE
assert itemEdit.theItem.isExported is False
# Check that the editor header is updated
nwGUI.docEditor.updateDocInfo("0e17daca5f3e1")
nwGUI.docEditor.updateDocInfo(tHandle)
assert nwGUI.docEditor.docHeader.theTitle.text() == "Novel New Chapter Great Scene"
itemEdit.close()
@@ -146,7 +148,7 @@ def testDlgItemEditor_Novel(qtbot, monkeypatch, nwGUI, fncProj, constData):
@pytest.mark.gui
def testDlgItemEditor_Note(qtbot, monkeypatch, nwGUI, fncProj, constData):
def testDlgItemEditor_Note(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
"""Test the item editor dialog for a project note.
"""
# Block message box
@@ -154,29 +156,27 @@ def testDlgItemEditor_Note(qtbot, monkeypatch, nwGUI, fncProj, constData):
monkeypatch.setattr(GuiProjectTree, "hasFocus", lambda *a: True)
# Create Project and Open Document
random.seed(42)
nwGUI.theProject.projTree.setSeed(42)
assert nwGUI.newProject({"projPath": fncProj})
assert nwGUI.theProject.statusItems.name(constData.statusKeys[0]) == "New"
assert nwGUI.theProject.statusItems.name(constData.statusKeys[1]) == "Note"
assert nwGUI.theProject.importItems.name(constData.importKeys[0]) == "New"
assert nwGUI.theProject.importItems.name(constData.importKeys[1]) == "Minor"
assert nwGUI.theProject.statusItems.name(statusKeys[0]) == "New"
assert nwGUI.theProject.statusItems.name(statusKeys[1]) == "Note"
assert nwGUI.theProject.importItems.name(importKeys[0]) == "New"
assert nwGUI.theProject.importItems.name(importKeys[1]) == "Minor"
# Create Note
nwGUI.treeView.clearSelection()
nwGUI.treeView._getTreeItem("71ee45a3c0db9").setSelected(True)
nwGUI.treeView._getTreeItem("000000000000a").setSelected(True)
nwGUI.treeView.newTreeItem(nwItemType.FILE, None)
# Open Note
assert nwGUI.openDocument("1a6562590ef19")
assert nwGUI.openDocument("0000000000010")
# Edit a Document
itemEdit = GuiItemEditor(nwGUI, "1a6562590ef19")
itemEdit = GuiItemEditor(nwGUI, "0000000000010")
itemEdit.show()
# Check Existing Settings
assert itemEdit.editName.text() == "New Note"
assert itemEdit.editStatus.currentData() == constData.importKeys[0]
assert itemEdit.editStatus.currentData() == importKeys[0]
assert itemEdit.editLayout.currentData() == nwItemLayout.NOTE
assert itemEdit.editExport.isChecked() is True
@@ -188,8 +188,8 @@ def testDlgItemEditor_Note(qtbot, monkeypatch, nwGUI, fncProj, constData):
# Check New Settings
assert itemEdit.theItem.itemName == "New Character"
assert itemEdit.theItem.itemStatus == constData.statusKeys[0]
assert itemEdit.theItem.itemImport == constData.importKeys[1]
assert itemEdit.theItem.itemStatus == statusKeys[0]
assert itemEdit.theItem.itemImport == importKeys[1]
assert itemEdit.theItem.itemLayout == nwItemLayout.NOTE
assert itemEdit.theItem.isExported is False
@@ -201,7 +201,7 @@ def testDlgItemEditor_Note(qtbot, monkeypatch, nwGUI, fncProj, constData):
@pytest.mark.gui
def testDlgItemEditor_Folder(qtbot, monkeypatch, nwGUI, fncProj, constData):
def testDlgItemEditor_Folder(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
"""Test the item editor dialog for a folder.
"""
# Block message box
@@ -209,20 +209,18 @@ def testDlgItemEditor_Folder(qtbot, monkeypatch, nwGUI, fncProj, constData):
monkeypatch.setattr(GuiProjectTree, "hasFocus", lambda *a: True)
# Create Project and Open Document
random.seed(42)
nwGUI.theProject.projTree.setSeed(42)
assert nwGUI.newProject({"projPath": fncProj})
# Edit a Folder
itemEdit = GuiItemEditor(nwGUI, "31489056e0916")
itemEdit = GuiItemEditor(nwGUI, "000000000000d")
itemEdit.show()
assert nwGUI.theProject.statusItems.name(constData.statusKeys[0]) == "New"
assert nwGUI.theProject.statusItems.name(constData.statusKeys[1]) == "Note"
assert nwGUI.theProject.statusItems.name(statusKeys[0]) == "New"
assert nwGUI.theProject.statusItems.name(statusKeys[1]) == "Note"
# Check Existing Settings
assert itemEdit.editName.text() == "New Chapter"
assert itemEdit.editStatus.currentData() == constData.statusKeys[0]
assert itemEdit.editStatus.currentData() == statusKeys[0]
assert itemEdit.editLayout.currentData() == nwItemLayout.NO_LAYOUT
assert itemEdit.editExport.isChecked() is False
@@ -236,7 +234,7 @@ def testDlgItemEditor_Folder(qtbot, monkeypatch, nwGUI, fncProj, constData):
# Check New Settings
itemEdit._doSave()
assert itemEdit.theItem.itemName == "Chapter One"
assert itemEdit.theItem.itemStatus == constData.statusKeys[1]
assert itemEdit.theItem.itemStatus == statusKeys[1]
assert itemEdit.theItem.itemLayout == nwItemLayout.NO_LAYOUT
assert itemEdit.theItem.isExported is False
+11 -12
View File
@@ -21,7 +21,6 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
import os
import pytest
import random
from shutil import copyfile
from tools import cmpFiles, getGuiItem
@@ -35,11 +34,13 @@ from novelwriter.dialogs import GuiProjectSettings
keyDelay = 2
typeDelay = 1
stepDelay = 20
statusKeys = ["s000000", "s000001", "s000002", "s000003"]
importKeys = ["i000004", "i000005", "i000006", "i000007"]
@pytest.mark.gui
def testDlgProjSettings_Dialog(
qtbot, monkeypatch, nwGUI, fncDir, fncProj, outDir, refDir, constData
qtbot, monkeypatch, nwGUI, fncDir, fncProj, outDir, refDir, mockRnd
):
"""Test the full project settings dialog.
"""
@@ -56,8 +57,6 @@ def testDlgProjSettings_Dialog(
assert getGuiItem("GuiProjectSettings") is None
# Create new project
random.seed(42)
nwGUI.theProject.projTree.setSeed(42)
assert nwGUI.newProject({"projPath": fncProj})
nwGUI.mainConf.backupPath = fncDir
@@ -145,15 +144,15 @@ def testDlgProjSettings_Dialog(
assert projEdit.tabStatus.getNewList() == (
[
{
"key": constData.statusKeys[0],
"key": statusKeys[0],
"name": "New",
"cols": (100, 100, 100)
}, {
"key": constData.statusKeys[1],
"key": statusKeys[1],
"name": "Note",
"cols": (200, 50, 0)
}, {
"key": constData.statusKeys[3],
"key": statusKeys[3],
"name": "Finished",
"cols": (50, 200, 0)
}, {
@@ -162,7 +161,7 @@ def testDlgProjSettings_Dialog(
"cols": (20, 30, 40)
}
], [
constData.statusKeys[2] # Deleted item
statusKeys[2] # Deleted item
]
)
@@ -170,25 +169,25 @@ def testDlgProjSettings_Dialog(
projEdit.tabStatus.listBox.clearSelection()
projEdit.tabStatus._moveItem(1)
assert [x["key"] for x in projEdit.tabStatus.getNewList()[0]] == [
constData.statusKeys[0], constData.statusKeys[1], constData.statusKeys[3], None
statusKeys[0], statusKeys[1], statusKeys[3], None
]
projEdit.tabStatus.listBox.clearSelection()
projEdit.tabStatus.listBox.topLevelItem(0).setSelected(True)
projEdit.tabStatus._moveItem(-1)
assert [x["key"] for x in projEdit.tabStatus.getNewList()[0]] == [
constData.statusKeys[0], constData.statusKeys[1], constData.statusKeys[3], None
statusKeys[0], statusKeys[1], statusKeys[3], None
]
projEdit.tabStatus.listBox.clearSelection()
projEdit.tabStatus.listBox.topLevelItem(3).setSelected(True)
projEdit.tabStatus._moveItem(-1)
assert [x["key"] for x in projEdit.tabStatus.getNewList()[0]] == [
constData.statusKeys[0], constData.statusKeys[1], None, constData.statusKeys[3]
statusKeys[0], statusKeys[1], None, statusKeys[3]
]
projEdit.tabStatus._moveItem(1)
assert [x["key"] for x in projEdit.tabStatus.getNewList()[0]] == [
constData.statusKeys[0], constData.statusKeys[1], constData.statusKeys[3], None
statusKeys[0], statusKeys[1], statusKeys[3], None
]
# Importance Tab
-1
View File
@@ -1286,7 +1286,6 @@ def testGuiEditor_Search(qtbot, monkeypatch, nwGUI, nwLipsum):
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(GuiDocEditor, "hasFocus", lambda *a: True)
nwGUI.theProject.projTree.setSeed(42)
assert nwGUI.openProject(nwLipsum) is True
assert nwGUI.openDocument("4c4f28287af27") is True
origText = nwGUI.docEditor.getText()
-1
View File
@@ -43,7 +43,6 @@ def testGuiViewer_Main(qtbot, monkeypatch, nwGUI, nwLipsum):
monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
# Open project
nwGUI.theProject.projTree.setSeed(42)
assert nwGUI.openProject(nwLipsum)
# Rebuild the index
+34 -37
View File
@@ -20,7 +20,6 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
import os
import random
import pytest
from shutil import copyfile
@@ -72,16 +71,16 @@ def testGuiMain_ProjectBlocker(monkeypatch, nwGUI):
@pytest.mark.gui
def testGuiMain_ProjectTreeItems(qtbot, monkeypatch, nwGUI, fncProj):
def testGuiMain_ProjectTreeItems(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
"""Test handling of project tree items based on GUI focus states.
"""
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
nwGUI.theProject.projTree.setSeed(42)
assert nwGUI.newProject({"projPath": fncProj}) is True
assert nwGUI.saveProject() is True
# assert False
sHandle = "0e17daca5f3e1"
sHandle = "000000000000f"
assert nwGUI.openSelectedItem() is False
# Project Tree has focus
@@ -128,7 +127,7 @@ def testGuiMain_ProjectTreeItems(qtbot, monkeypatch, nwGUI, fncProj):
@pytest.mark.gui
def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir):
def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir, mockRnd):
"""Test the document editor.
"""
# Block message box
@@ -140,8 +139,6 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir):
monkeypatch.setattr(GuiDocEditor, "hasFocus", lambda *a: True)
# Create new, save, close project
random.seed(42)
nwGUI.theProject.projTree.setSeed(42)
assert nwGUI.newProject({"projPath": fncProj})
assert nwGUI.saveProject()
assert nwGUI.closeProject()
@@ -186,14 +183,14 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir):
assert not nwGUI.theProject.spellCheck
# Check that tree items have been created
assert nwGUI.treeView._getTreeItem("73475cb40a568") is not None
assert nwGUI.treeView._getTreeItem("25fc0e7096fc6") is not None
assert nwGUI.treeView._getTreeItem("31489056e0916") is not None
assert nwGUI.treeView._getTreeItem("98010bd9270f9") is not None
assert nwGUI.treeView._getTreeItem("0e17daca5f3e1") is not None
assert nwGUI.treeView._getTreeItem("44cb730c42048") is not None
assert nwGUI.treeView._getTreeItem("71ee45a3c0db9") is not None
assert nwGUI.treeView._getTreeItem("811786ad1ae74") is not None
assert nwGUI.treeView._getTreeItem("0000000000008") is not None
assert nwGUI.treeView._getTreeItem("0000000000009") is not None
assert nwGUI.treeView._getTreeItem("000000000000a") is not None
assert nwGUI.treeView._getTreeItem("000000000000b") is not None
assert nwGUI.treeView._getTreeItem("000000000000c") is not None
assert nwGUI.treeView._getTreeItem("000000000000d") is not None
assert nwGUI.treeView._getTreeItem("000000000000e") is not None
assert nwGUI.treeView._getTreeItem("000000000000f") is not None
nwGUI.mainMenu.aSpellCheck.setChecked(True)
assert nwGUI.mainMenu._toggleSpellCheck()
@@ -207,7 +204,7 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir):
# Add a Character File
nwGUI.switchFocus(nwWidget.TREE)
nwGUI.treeView.clearSelection()
nwGUI.treeView._getTreeItem("71ee45a3c0db9").setSelected(True)
nwGUI.treeView._getTreeItem("000000000000a").setSelected(True)
nwGUI.treeView.newTreeItem(nwItemType.FILE, None)
assert nwGUI.openSelectedItem()
@@ -229,7 +226,7 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir):
# Add a Plot File
nwGUI.switchFocus(nwWidget.TREE)
nwGUI.treeView.clearSelection()
nwGUI.treeView._getTreeItem("44cb730c42048").setSelected(True)
nwGUI.treeView._getTreeItem("0000000000009").setSelected(True)
nwGUI.treeView.newTreeItem(nwItemType.FILE, None)
assert nwGUI.openSelectedItem()
@@ -251,7 +248,7 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir):
# Add a World File
nwGUI.switchFocus(nwWidget.TREE)
nwGUI.treeView.clearSelection()
nwGUI.treeView._getTreeItem("811786ad1ae74").setSelected(True)
nwGUI.treeView._getTreeItem("000000000000b").setSelected(True)
nwGUI.treeView.newTreeItem(nwItemType.FILE, None)
assert nwGUI.openSelectedItem()
@@ -282,9 +279,9 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir):
# Select the 'New Scene' file
nwGUI.switchFocus(nwWidget.TREE)
nwGUI.treeView.clearSelection()
nwGUI.treeView._getTreeItem("73475cb40a568").setExpanded(True)
nwGUI.treeView._getTreeItem("31489056e0916").setExpanded(True)
nwGUI.treeView._getTreeItem("0e17daca5f3e1").setSelected(True)
nwGUI.treeView._getTreeItem("0000000000008").setExpanded(True)
nwGUI.treeView._getTreeItem("000000000000d").setExpanded(True)
nwGUI.treeView._getTreeItem("000000000000f").setSelected(True)
assert nwGUI.openSelectedItem()
# Type something into the document
@@ -419,8 +416,8 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir):
# Open and view the edited document
nwGUI.switchFocus(nwWidget.VIEWER)
assert nwGUI.openDocument("0e17daca5f3e1")
assert nwGUI.viewDocument("0e17daca5f3e1")
assert nwGUI.openDocument("000000000000f")
assert nwGUI.viewDocument("000000000000f")
qtbot.wait(stepDelay)
assert nwGUI.saveProject()
assert nwGUI.closeDocViewer()
@@ -429,11 +426,11 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir):
# Check a Quick Create and Delete
assert nwGUI.treeView.newTreeItem(nwItemType.FILE, None)
newHandle = nwGUI.treeView.getSelectedHandle()
assert nwGUI.theProject.projTree["2858dcd1057d3"] is not None
assert nwGUI.theProject.projTree["0000000000020"] is not None
assert nwGUI.treeView.deleteItem()
assert nwGUI.treeView.setSelectedHandle(newHandle)
assert nwGUI.treeView.deleteItem()
assert nwGUI.theProject.projTree["2fca346db6561"] is not None # Trash
assert nwGUI.theProject.projTree["0000000000024"] is not None # Trash
assert nwGUI.saveProject()
# Check the files
@@ -443,27 +440,27 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir):
copyfile(projFile, testFile)
assert cmpFiles(testFile, compFile, [2, 6, 7, 8])
projFile = os.path.join(fncProj, "content", "031b4af5197ec.nwd")
testFile = os.path.join(outDir, "guiEditor_Main_Final_031b4af5197ec.nwd")
compFile = os.path.join(refDir, "guiEditor_Main_Final_031b4af5197ec.nwd")
projFile = os.path.join(fncProj, "content", "000000000000f.nwd")
testFile = os.path.join(outDir, "guiEditor_Main_Final_000000000000f.nwd")
compFile = os.path.join(refDir, "guiEditor_Main_Final_000000000000f.nwd")
copyfile(projFile, testFile)
assert cmpFiles(testFile, compFile)
projFile = os.path.join(fncProj, "content", "1a6562590ef19.nwd")
testFile = os.path.join(outDir, "guiEditor_Main_Final_1a6562590ef19.nwd")
compFile = os.path.join(refDir, "guiEditor_Main_Final_1a6562590ef19.nwd")
projFile = os.path.join(fncProj, "content", "0000000000020.nwd")
testFile = os.path.join(outDir, "guiEditor_Main_Final_0000000000020.nwd")
compFile = os.path.join(refDir, "guiEditor_Main_Final_0000000000020.nwd")
copyfile(projFile, testFile)
assert cmpFiles(testFile, compFile)
projFile = os.path.join(fncProj, "content", "0e17daca5f3e1.nwd")
testFile = os.path.join(outDir, "guiEditor_Main_Final_0e17daca5f3e1.nwd")
compFile = os.path.join(refDir, "guiEditor_Main_Final_0e17daca5f3e1.nwd")
projFile = os.path.join(fncProj, "content", "0000000000021.nwd")
testFile = os.path.join(outDir, "guiEditor_Main_Final_0000000000021.nwd")
compFile = os.path.join(refDir, "guiEditor_Main_Final_0000000000021.nwd")
copyfile(projFile, testFile)
assert cmpFiles(testFile, compFile)
projFile = os.path.join(fncProj, "content", "41cfc0d1f2d12.nwd")
testFile = os.path.join(outDir, "guiEditor_Main_Final_41cfc0d1f2d12.nwd")
compFile = os.path.join(refDir, "guiEditor_Main_Final_41cfc0d1f2d12.nwd")
projFile = os.path.join(fncProj, "content", "0000000000022.nwd")
testFile = os.path.join(outDir, "guiEditor_Main_Final_0000000000022.nwd")
compFile = os.path.join(refDir, "guiEditor_Main_Final_0000000000022.nwd")
copyfile(projFile, testFile)
assert cmpFiles(testFile, compFile)
+5 -8
View File
@@ -48,7 +48,6 @@ def testGuiMenu_EditFormat(qtbot, monkeypatch, nwGUI, nwLipsum):
# Test Document Action with No Project
assert nwGUI.docEditor.docAction(nwDocAction.COPY) is False
nwGUI.theProject.projTree.setSeed(42)
assert nwGUI.openProject(nwLipsum) is True
qtbot.wait(stepDelay)
@@ -381,7 +380,6 @@ def testGuiMenu_ContextMenus(qtbot, monkeypatch, nwGUI, nwLipsum):
# Block message box
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
nwGUI.theProject.projTree.setSeed(42)
assert nwGUI.openProject(nwLipsum)
assert nwGUI.openDocument("4c4f28287af27")
qtbot.wait(stepDelay)
@@ -460,18 +458,17 @@ def testGuiMenu_ContextMenus(qtbot, monkeypatch, nwGUI, nwLipsum):
@pytest.mark.gui
def testGuiMenu_Insert(qtbot, monkeypatch, nwGUI, fncDir, fncProj):
def testGuiMenu_Insert(qtbot, monkeypatch, nwGUI, fncDir, fncProj, mockRnd):
"""Test the Insert menu.
"""
# Block message box
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
nwGUI.theProject.projTree.setSeed(42)
assert nwGUI.newProject({"projPath": fncProj})
assert nwGUI.treeView._getTreeItem("0e17daca5f3e1") is not None
assert nwGUI.openDocument("0e17daca5f3e1") is True
assert nwGUI.treeView._getTreeItem("000000000000f") is not None
assert nwGUI.openDocument("000000000000f") is True
nwGUI.docEditor.clear()
# Test Faulty Inserts
@@ -680,7 +677,7 @@ def testGuiMenu_Insert(qtbot, monkeypatch, nwGUI, fncDir, fncProj):
assert not nwGUI.importDocument()
# Open the document from before, and add some text to it
nwGUI.openDocument("0e17daca5f3e1")
nwGUI.openDocument("000000000000f")
nwGUI.docEditor.setText("Bar")
assert nwGUI.docEditor.getText() == "Bar"
@@ -712,7 +709,7 @@ def testGuiMenu_Insert(qtbot, monkeypatch, nwGUI, fncDir, fncProj):
theBits = theMessage.split("<br>")
assert len(theBits) == 2
assert theBits[0] == "The currently open file is saved in:"
assert theBits[1] == os.path.join(fncProj, "content", "0e17daca5f3e1.nwd")
assert theBits[1] == os.path.join(fncProj, "content", "000000000000f.nwd")
# qtbot.stopForInteraction()
-1
View File
@@ -37,7 +37,6 @@ def testGuiNovelTree_TreeItems(qtbot, monkeypatch, nwGUI, nwMinimal):
monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
nwGUI.openProject(nwMinimal)
nwGUI.theProject.projTree.setSeed(42)
nwTree = nwGUI.novelView
##
+105 -108
View File
@@ -30,7 +30,7 @@ from novelwriter.enum import nwItemType, nwItemClass
@pytest.mark.gui
def testGuiProjTree_NewItems(qtbot, caplog, monkeypatch, nwGUI, fncDir):
def testGuiProjTree_NewItems(qtbot, caplog, monkeypatch, nwGUI, fncDir, mockRnd):
"""Test adding and removing items from the project tree.
"""
# Block message box
@@ -46,7 +46,6 @@ def testGuiProjTree_NewItems(qtbot, caplog, monkeypatch, nwGUI, fncDir):
assert nwTree.newTreeItem(nwItemType.FILE) is False
# Create a project
nwGUI.theProject.projTree.setSeed(42)
prjDir = os.path.join(fncDir, "project")
assert nwGUI.newProject({"projPath": prjDir}) is True
@@ -62,7 +61,7 @@ def testGuiProjTree_NewItems(qtbot, caplog, monkeypatch, nwGUI, fncDir):
# Create root item
assert nwTree.newTreeItem(nwItemType.ROOT, nwItemClass.WORLD) is True
assert "1a6562590ef19" in nwGUI.theProject.projTree
assert "0000000000010" in nwGUI.theProject.projTree
# File/Folder Items
# =================
@@ -75,44 +74,44 @@ def testGuiProjTree_NewItems(qtbot, caplog, monkeypatch, nwGUI, fncDir):
assert "Did not find anywhere" in caplog.text
# Create new folder as child of Novel folder
nwTree.setSelectedHandle("73475cb40a568")
nwTree.setSelectedHandle("0000000000008")
assert nwTree.newTreeItem(nwItemType.FOLDER) is True
assert nwGUI.theProject.projTree["031b4af5197ec"].itemParent == "73475cb40a568"
assert nwGUI.theProject.projTree["031b4af5197ec"].itemRoot == "73475cb40a568"
assert nwGUI.theProject.projTree["031b4af5197ec"].itemClass == nwItemClass.NOVEL
assert nwGUI.theProject.projTree["0000000000011"].itemParent == "0000000000008"
assert nwGUI.theProject.projTree["0000000000011"].itemRoot == "0000000000008"
assert nwGUI.theProject.projTree["0000000000011"].itemClass == nwItemClass.NOVEL
# Add a new file in the new folder
nwTree.setSelectedHandle("031b4af5197ec")
nwTree.setSelectedHandle("0000000000011")
assert nwTree.newTreeItem(nwItemType.FILE) is True
assert nwGUI.theProject.projTree["41cfc0d1f2d12"].itemParent == "031b4af5197ec"
assert nwGUI.theProject.projTree["41cfc0d1f2d12"].itemRoot == "73475cb40a568"
assert nwGUI.theProject.projTree["41cfc0d1f2d12"].itemClass == nwItemClass.NOVEL
assert nwGUI.theProject.projTree["0000000000012"].itemParent == "0000000000011"
assert nwGUI.theProject.projTree["0000000000012"].itemRoot == "0000000000008"
assert nwGUI.theProject.projTree["0000000000012"].itemClass == nwItemClass.NOVEL
# Add a new file next to the other new file
nwTree.setSelectedHandle("41cfc0d1f2d12")
nwTree.setSelectedHandle("0000000000012")
assert nwTree.newTreeItem(nwItemType.FILE) is True
assert nwGUI.theProject.projTree["2858dcd1057d3"].itemParent == "031b4af5197ec"
assert nwGUI.theProject.projTree["2858dcd1057d3"].itemRoot == "73475cb40a568"
assert nwGUI.theProject.projTree["2858dcd1057d3"].itemClass == nwItemClass.NOVEL
assert nwGUI.openDocument("2858dcd1057d3")
assert nwGUI.theProject.projTree["0000000000013"].itemParent == "0000000000011"
assert nwGUI.theProject.projTree["0000000000013"].itemRoot == "0000000000008"
assert nwGUI.theProject.projTree["0000000000013"].itemClass == nwItemClass.NOVEL
assert nwGUI.openDocument("0000000000013")
assert nwGUI.docEditor.getText() == "### New Document\n\n"
# Add a new file to the characters folder
nwTree.setSelectedHandle("71ee45a3c0db9")
nwTree.setSelectedHandle("000000000000a")
assert nwTree.newTreeItem(nwItemType.FILE) is True
assert nwGUI.theProject.projTree["2fca346db6561"].itemParent == "71ee45a3c0db9"
assert nwGUI.theProject.projTree["2fca346db6561"].itemRoot == "71ee45a3c0db9"
assert nwGUI.theProject.projTree["2fca346db6561"].itemClass == nwItemClass.CHARACTER
assert nwGUI.openDocument("2fca346db6561")
assert nwGUI.theProject.projTree["0000000000014"].itemParent == "000000000000a"
assert nwGUI.theProject.projTree["0000000000014"].itemRoot == "000000000000a"
assert nwGUI.theProject.projTree["0000000000014"].itemClass == nwItemClass.CHARACTER
assert nwGUI.openDocument("0000000000014")
assert nwGUI.docEditor.getText() == "# New Note\n\n"
# Make sure the sibling folder bug trap works
nwTree.setSelectedHandle("2858dcd1057d3")
nwGUI.theProject.projTree["2858dcd1057d3"].setParent(None) # This should not happen
nwTree.setSelectedHandle("0000000000013")
nwGUI.theProject.projTree["0000000000013"].setParent(None) # This should not happen
caplog.clear()
assert nwTree.newTreeItem(nwItemType.FILE) is False
assert "Internal error" in caplog.text
nwGUI.theProject.projTree["2858dcd1057d3"].setParent("031b4af5197ec")
nwGUI.theProject.projTree["0000000000013"].setParent("0000000000011")
# Get the trash folder
nwTree._addTrashRoot()
@@ -139,7 +138,7 @@ def testGuiProjTree_NewItems(qtbot, caplog, monkeypatch, nwGUI, fncDir):
@pytest.mark.gui
def testGuiProjTree_MoveItems(qtbot, monkeypatch, nwGUI, fncDir):
def testGuiProjTree_MoveItems(qtbot, monkeypatch, nwGUI, fncDir, mockRnd):
"""Test adding and removing items from the project tree.
"""
# Block message box
@@ -155,7 +154,6 @@ def testGuiProjTree_MoveItems(qtbot, monkeypatch, nwGUI, fncDir):
assert nwTree.moveTreeItem(1) is False
# Create a project
nwGUI.theProject.projTree.setSeed(42)
prjDir = os.path.join(fncDir, "project")
assert nwGUI.newProject({"projPath": prjDir}) is True
@@ -163,21 +161,21 @@ def testGuiProjTree_MoveItems(qtbot, monkeypatch, nwGUI, fncDir):
# ==============
# Add some files
nwTree.setSelectedHandle("31489056e0916")
nwTree.setSelectedHandle("000000000000d")
assert nwTree.newTreeItem(nwItemType.FILE) is True
assert nwTree.newTreeItem(nwItemType.FILE) is True
assert nwTree.newTreeItem(nwItemType.FILE) is True
assert nwTree.getTreeFromHandle("31489056e0916") == [
"31489056e0916", "98010bd9270f9", "0e17daca5f3e1",
"1a6562590ef19", "031b4af5197ec", "41cfc0d1f2d12",
assert nwTree.getTreeFromHandle("000000000000d") == [
"000000000000d", "000000000000e", "000000000000f",
"0000000000010", "0000000000011", "0000000000012",
]
# Move item without focus
monkeypatch.setattr(GuiProjectTree, "hasFocus", lambda *a: False)
assert nwTree.moveTreeItem(1) is False
assert nwTree.getTreeFromHandle("31489056e0916") == [
"31489056e0916", "98010bd9270f9", "0e17daca5f3e1",
"1a6562590ef19", "031b4af5197ec", "41cfc0d1f2d12",
assert nwTree.getTreeFromHandle("000000000000d") == [
"000000000000d", "000000000000e", "000000000000f",
"0000000000010", "0000000000011", "0000000000012",
]
monkeypatch.setattr(GuiProjectTree, "hasFocus", lambda *a: True)
@@ -186,78 +184,78 @@ def testGuiProjTree_MoveItems(qtbot, monkeypatch, nwGUI, fncDir):
assert nwTree.moveTreeItem(1) is False
# Move second item up twice (should give same result)
nwTree.setSelectedHandle("0e17daca5f3e1")
nwTree.setSelectedHandle("000000000000f")
assert nwTree.moveTreeItem(-1) is True
assert nwTree.getTreeFromHandle("31489056e0916") == [
"31489056e0916", "0e17daca5f3e1", "98010bd9270f9",
"1a6562590ef19", "031b4af5197ec", "41cfc0d1f2d12",
assert nwTree.getTreeFromHandle("000000000000d") == [
"000000000000d", "000000000000f", "000000000000e",
"0000000000010", "0000000000011", "0000000000012",
]
assert nwTree.moveTreeItem(-1) is False
assert nwTree.getTreeFromHandle("31489056e0916") == [
"31489056e0916", "0e17daca5f3e1", "98010bd9270f9",
"1a6562590ef19", "031b4af5197ec", "41cfc0d1f2d12",
assert nwTree.getTreeFromHandle("000000000000d") == [
"000000000000d", "000000000000f", "000000000000e",
"0000000000010", "0000000000011", "0000000000012",
]
# Restore via menu entry
nwGUI.mainMenu.aMoveDown.activate(QAction.Trigger)
assert nwTree.getTreeFromHandle("31489056e0916") == [
"31489056e0916", "98010bd9270f9", "0e17daca5f3e1",
"1a6562590ef19", "031b4af5197ec", "41cfc0d1f2d12",
assert nwTree.getTreeFromHandle("000000000000d") == [
"000000000000d", "000000000000e", "000000000000f",
"0000000000010", "0000000000011", "0000000000012",
]
# Move fifth item down twice (should give same result)
nwTree.setSelectedHandle("031b4af5197ec")
nwTree.setSelectedHandle("0000000000011")
assert nwTree.moveTreeItem(1) is True
assert nwTree.getTreeFromHandle("31489056e0916") == [
"31489056e0916", "98010bd9270f9", "0e17daca5f3e1",
"1a6562590ef19", "41cfc0d1f2d12", "031b4af5197ec",
assert nwTree.getTreeFromHandle("000000000000d") == [
"000000000000d", "000000000000e", "000000000000f",
"0000000000010", "0000000000012", "0000000000011",
]
assert nwTree.moveTreeItem(1) is False
assert nwTree.getTreeFromHandle("31489056e0916") == [
"31489056e0916", "98010bd9270f9", "0e17daca5f3e1",
"1a6562590ef19", "41cfc0d1f2d12", "031b4af5197ec",
assert nwTree.getTreeFromHandle("000000000000d") == [
"000000000000d", "000000000000e", "000000000000f",
"0000000000010", "0000000000012", "0000000000011",
]
# Restore via menu entry
nwGUI.mainMenu.aMoveUp.activate(QAction.Trigger)
assert nwTree.getTreeFromHandle("31489056e0916") == [
"31489056e0916", "98010bd9270f9", "0e17daca5f3e1",
"1a6562590ef19", "031b4af5197ec", "41cfc0d1f2d12",
assert nwTree.getTreeFromHandle("000000000000d") == [
"000000000000d", "000000000000e", "000000000000f",
"0000000000010", "0000000000011", "0000000000012",
]
# Move down again, and restore via undo
nwTree.setSelectedHandle("031b4af5197ec")
nwTree.setSelectedHandle("0000000000011")
assert nwTree.moveTreeItem(1) is True
assert nwTree.getTreeFromHandle("31489056e0916") == [
"31489056e0916", "98010bd9270f9", "0e17daca5f3e1",
"1a6562590ef19", "41cfc0d1f2d12", "031b4af5197ec",
assert nwTree.getTreeFromHandle("000000000000d") == [
"000000000000d", "000000000000e", "000000000000f",
"0000000000010", "0000000000012", "0000000000011",
]
nwGUI.mainMenu.aMoveUndo.activate(QAction.Trigger)
assert nwTree.getTreeFromHandle("31489056e0916") == [
"31489056e0916", "98010bd9270f9", "0e17daca5f3e1",
"1a6562590ef19", "031b4af5197ec", "41cfc0d1f2d12",
assert nwTree.getTreeFromHandle("000000000000d") == [
"000000000000d", "000000000000e", "000000000000f",
"0000000000010", "0000000000011", "0000000000012",
]
# Root Folder
# ===========
nwTree.setSelectedHandle("73475cb40a568")
assert nwGUI.theProject.projTree._treeOrder.index("73475cb40a568") == 0
nwTree.setSelectedHandle("0000000000008")
assert nwGUI.theProject.projTree._treeOrder.index("0000000000008") == 0
# Move novel folder up
assert nwTree.moveTreeItem(-1) is False
nwTree.flushTreeOrder()
assert nwGUI.theProject.projTree._treeOrder.index("73475cb40a568") == 0
assert nwGUI.theProject.projTree._treeOrder.index("0000000000008") == 0
# Move novel folder down
assert nwTree.moveTreeItem(1) is True
nwTree.flushTreeOrder()
assert nwGUI.theProject.projTree._treeOrder.index("73475cb40a568") == 1
assert nwGUI.theProject.projTree._treeOrder.index("0000000000008") == 1
# Move novel folder up again
assert nwTree.moveTreeItem(-1) is True
nwTree.flushTreeOrder()
assert nwGUI.theProject.projTree._treeOrder.index("73475cb40a568") == 0
assert nwGUI.theProject.projTree._treeOrder.index("0000000000008") == 0
# Clean up
# qtbot.stopForInteraction()
@@ -267,7 +265,7 @@ def testGuiProjTree_MoveItems(qtbot, monkeypatch, nwGUI, fncDir):
@pytest.mark.gui
def testGuiProjTree_DeleteItems(qtbot, caplog, monkeypatch, nwGUI, fncDir):
def testGuiProjTree_DeleteItems(qtbot, caplog, monkeypatch, nwGUI, fncDir, mockRnd):
"""Test adding and removing items from the project tree.
"""
# Block message box
@@ -284,7 +282,6 @@ def testGuiProjTree_DeleteItems(qtbot, caplog, monkeypatch, nwGUI, fncDir):
assert nwTree.deleteItem() is False
# Create a project
nwGUI.theProject.projTree.setSeed(42)
prjDir = os.path.join(fncDir, "project")
assert nwGUI.newProject({"projPath": prjDir}) is True
@@ -292,13 +289,13 @@ def testGuiProjTree_DeleteItems(qtbot, caplog, monkeypatch, nwGUI, fncDir):
assert nwTree.emptyTrash() is False
# Add some files
nwTree.setSelectedHandle("31489056e0916")
nwTree.setSelectedHandle("000000000000d")
assert nwTree.newTreeItem(nwItemType.FILE) is True
assert nwTree.newTreeItem(nwItemType.FILE) is True
assert nwTree.newTreeItem(nwItemType.FILE) is True
assert nwTree.getTreeFromHandle("31489056e0916") == [
"31489056e0916", "98010bd9270f9", "0e17daca5f3e1",
"1a6562590ef19", "031b4af5197ec", "41cfc0d1f2d12",
assert nwTree.getTreeFromHandle("000000000000d") == [
"000000000000d", "000000000000e", "000000000000f",
"0000000000010", "0000000000011", "0000000000012",
]
# Delete File
@@ -306,7 +303,7 @@ def testGuiProjTree_DeleteItems(qtbot, caplog, monkeypatch, nwGUI, fncDir):
# Delete item without focus -> blocked
monkeypatch.setattr(GuiProjectTree, "hasFocus", lambda *a: False)
nwTree.setSelectedHandle("41cfc0d1f2d12")
nwTree.setSelectedHandle("0000000000012")
caplog.clear()
assert nwTree.deleteItem() is False
assert "blocked" in caplog.text
@@ -327,76 +324,76 @@ def testGuiProjTree_DeleteItems(qtbot, caplog, monkeypatch, nwGUI, fncDir):
# Block adding trash folder
funcPointer = nwTree._addTrashRoot
nwTree._addTrashRoot = lambda *a: None
assert nwTree.deleteItem("41cfc0d1f2d12") is False
assert nwTree.deleteItem("0000000000012") is False
nwTree._addTrashRoot = funcPointer
# Delete last two documents, which also adds the trash folder
assert nwTree.deleteItem("41cfc0d1f2d12") is True
assert nwTree.deleteItem("031b4af5197ec") is True
assert nwTree.getTreeFromHandle("31489056e0916") == [
"31489056e0916", "98010bd9270f9", "0e17daca5f3e1",
"1a6562590ef19"
assert nwTree.deleteItem("0000000000012") is True
assert nwTree.deleteItem("0000000000011") is True
assert nwTree.getTreeFromHandle("000000000000d") == [
"000000000000d", "000000000000e", "000000000000f",
"0000000000010"
]
trashHandle = nwGUI.theProject.projTree.trashRoot()
assert nwTree.getTreeFromHandle(trashHandle) == [
trashHandle, "41cfc0d1f2d12", "031b4af5197ec"
trashHandle, "0000000000012", "0000000000011"
]
# Delete the first file again (permanent), and ask for permission
# Also open the document in the editor, which should trigger a close
assert os.path.isfile(os.path.join(prjDir, "content", "41cfc0d1f2d12.nwd"))
assert "41cfc0d1f2d12" in nwGUI.theProject.projTree
assert os.path.isfile(os.path.join(prjDir, "content", "0000000000012.nwd"))
assert "0000000000012" in nwGUI.theProject.projTree
assert nwGUI.docEditor.docHandle() is None
assert nwGUI.openDocument("41cfc0d1f2d12") is True
assert nwGUI.docEditor.docHandle() == "41cfc0d1f2d12"
assert nwTree.deleteItem("41cfc0d1f2d12") is True
assert nwGUI.openDocument("0000000000012") is True
assert nwGUI.docEditor.docHandle() == "0000000000012"
assert nwTree.deleteItem("0000000000012") is True
assert nwGUI.docEditor.docHandle() is None
assert not os.path.isfile(os.path.join(prjDir, "content", "41cfc0d1f2d12.nwd"))
assert "41cfc0d1f2d12" not in nwGUI.theProject.projTree
assert not os.path.isfile(os.path.join(prjDir, "content", "0000000000012.nwd"))
assert "0000000000012" not in nwGUI.theProject.projTree
assert nwTree.getTreeFromHandle(trashHandle) == [
trashHandle, "031b4af5197ec"
trashHandle, "0000000000011"
]
# Try to delete the second document, but block the deletion
with monkeypatch.context() as mp:
mp.setattr("novelwriter.core.document.NWDoc.deleteDocument", lambda *a: False)
assert nwTree.deleteItem("031b4af5197ec") is False
assert nwTree.deleteItem("0000000000011") is False
# Delete proper, and skip asking for permission
assert os.path.isfile(os.path.join(prjDir, "content", "031b4af5197ec.nwd"))
assert "031b4af5197ec" in nwGUI.theProject.projTree
assert nwTree.deleteItem("031b4af5197ec", alreadyAsked=True) is True
assert not os.path.isfile(os.path.join(prjDir, "content", "031b4af5197ec.nwd"))
assert "031b4af5197ec" not in nwGUI.theProject.projTree
assert os.path.isfile(os.path.join(prjDir, "content", "0000000000011.nwd"))
assert "0000000000011" in nwGUI.theProject.projTree
assert nwTree.deleteItem("0000000000011", alreadyAsked=True) is True
assert not os.path.isfile(os.path.join(prjDir, "content", "0000000000011.nwd"))
assert "0000000000011" not in nwGUI.theProject.projTree
assert nwTree.getTreeFromHandle(trashHandle) == [trashHandle]
# Delete Folder/Root
# ==================
# Deleting non-empty folders is blocked
assert nwTree.deleteItem("31489056e0916") is False # Folder
assert nwTree.deleteItem("73475cb40a568") is False # Root
assert nwTree.deleteItem("000000000000d") is False # Folder
assert nwTree.deleteItem("0000000000008") is False # Root
# Add a folder we can delete
nwTree.setSelectedHandle("71ee45a3c0db9") # Character Root
nwTree.setSelectedHandle("000000000000a") # Character Root
assert nwTree.newTreeItem(nwItemType.FOLDER) is True
assert "2fca346db6561" in nwGUI.theProject.projTree
assert "0000000000014" in nwGUI.theProject.projTree
# Try to delete, but block parent item lookup
with monkeypatch.context() as mp:
mp.setattr("PyQt5.QtWidgets.QTreeWidgetItem.parent", lambda *a: None)
caplog.clear()
assert nwTree.deleteItem("2fca346db6561") is False
assert nwTree.deleteItem("0000000000014") is False
assert "Could not delete folder" in caplog.text
assert "2fca346db6561" in nwGUI.theProject.projTree
assert "0000000000014" in nwGUI.theProject.projTree
# Delete folder properly
assert nwTree.deleteItem("2fca346db6561") is True
assert "2fca346db6561" not in nwGUI.theProject.projTree
assert nwTree.deleteItem("0000000000014") is True
assert "0000000000014" not in nwGUI.theProject.projTree
# Delete the Character root
assert nwTree.deleteItem("71ee45a3c0db9") is True
assert "71ee45a3c0db9" not in nwGUI.theProject.projTree
assert nwTree.deleteItem("000000000000a") is True
assert "000000000000a" not in nwGUI.theProject.projTree
# Empty Trash
# ===========
@@ -408,13 +405,13 @@ def testGuiProjTree_DeleteItems(qtbot, caplog, monkeypatch, nwGUI, fncDir):
assert "already empty" in caplog.text
# Move the two remaining scene documents to trash
assert nwTree.deleteItem("0e17daca5f3e1") is True
assert nwTree.deleteItem("1a6562590ef19") is True
assert nwTree.getTreeFromHandle("31489056e0916") == [
"31489056e0916", "98010bd9270f9"
assert nwTree.deleteItem("000000000000f") is True
assert nwTree.deleteItem("0000000000010") is True
assert nwTree.getTreeFromHandle("000000000000d") == [
"000000000000d", "000000000000e"
]
assert nwTree.getTreeFromHandle(trashHandle) == [
trashHandle, "0e17daca5f3e1", "1a6562590ef19"
trashHandle, "000000000000f", "0000000000010"
]
# Empty trash, but select no on question
+2 -3
View File
@@ -29,14 +29,13 @@ from novelwriter.enum import nwState
@pytest.mark.gui
def testGuiStatusBar_Main(qtbot, monkeypatch, nwGUI, fncProj):
def testGuiStatusBar_Main(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
"""Test the the various features of the status bar.
"""
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
nwGUI.theProject.projTree.setSeed(42)
assert nwGUI.newProject({"projPath": fncProj}) is True
cHandle = nwGUI.theProject.newFile("A Note", "71ee45a3c0db9")
cHandle = nwGUI.theProject.newFile("A Note", "000000000000a")
newDoc = NWDoc(nwGUI.theProject, cHandle)
newDoc.writeDocument("# A Note\n\n")
nwGUI.treeView.revealNewTreeItem(cHandle)
+2 -3
View File
@@ -29,7 +29,7 @@ from novelwriter.tools import GuiLipsum
@pytest.mark.gui
def testToolLipsum_Main(qtbot, monkeypatch, nwGUI, fncProj):
def testToolLipsum_Main(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
"""Test the Lorem Ipsum tool.
"""
# Block message box
@@ -40,9 +40,8 @@ def testToolLipsum_Main(qtbot, monkeypatch, nwGUI, fncProj):
assert getGuiItem("GuiLipsum") is None
# Create a new project
nwGUI.theProject.projTree.setSeed(42)
assert nwGUI.newProject({"projPath": fncProj}) is True
assert nwGUI.openDocument("0e17daca5f3e1") is True
assert nwGUI.openDocument("000000000000f") is True
assert len(nwGUI.docEditor.getText()) == 15
# Open the tool