From ca818bc4971fbd41ffe7342b1ded3c0f99262813 Mon Sep 17 00:00:00 2001
From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com>
Date: Sat, 22 Aug 2020 12:36:17 +0200
Subject: [PATCH 01/16] Fix detecting slashes in spell check hihglighting
---
nw/gui/dochighlight.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/nw/gui/dochighlight.py b/nw/gui/dochighlight.py
index d743caa7..23902db1 100644
--- a/nw/gui/dochighlight.py
+++ b/nw/gui/dochighlight.py
@@ -198,7 +198,7 @@ class GuiDocHighlighter(QSyntaxHighlighter):
# Build a QRegExp for spell checker
# Include additional characters that the highlighter should
# consider to be word separators
- wordSep = r"_\+"
+ wordSep = r"_\+/"
wordSep += nwUnicode.U_ENDASH
wordSep += nwUnicode.U_EMDASH
self.spellRx = QRegularExpression(r"\b[^\s"+wordSep+r"]+\b")
From 27c36f38808ddb1565c7e555ea7188fa8d14e615 Mon Sep 17 00:00:00 2001
From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com>
Date: Sat, 22 Aug 2020 13:19:25 +0200
Subject: [PATCH 02/16] Added the modifier apostrophe characters to the Unicode
list and doc actions
---
nw/constants/constants.py | 4 ++++
nw/constants/enum.py | 2 ++
2 files changed, 6 insertions(+)
diff --git a/nw/constants/constants.py b/nw/constants/constants.py
index 7c25fa46..e810dabf 100644
--- a/nw/constants/constants.py
+++ b/nw/constants/constants.py
@@ -233,6 +233,8 @@ class nwUnicode:
U_ENDASH = "\u2013" # Short dash
U_EMDASH = "\u2014" # Long dash
U_HELLIP = "\u2026" # Ellipsis
+ U_MAPOSS = "\u02bc" # Modifier letter single apostrophe
+ U_MAPOSD = "\u02ee" # Modifier letter double apostrophe
## Spaces and Lines
U_NBSP = "\u00a0" # Non-breaking space
@@ -283,6 +285,8 @@ class nwUnicode:
H_ENDASH = "–"
H_EMDASH = "—"
H_HELLIP = "…"
+ H_MAPOSS = "ʼ"
+ H_MAPOSD = "ˮ"
## Spaces
H_NBSP = " "
diff --git a/nw/constants/enum.py b/nw/constants/enum.py
index 49d76f5e..81200a63 100644
--- a/nw/constants/enum.py
+++ b/nw/constants/enum.py
@@ -112,6 +112,8 @@ class nwDocInsert(Enum):
QUOTE_RS = 9
QUOTE_LD = 10
QUOTE_RD = 11
+ MODAPOS_S = 12
+ MODAPOS_D = 13
# END Enum nwDocInsert
From 377c9a3d410f381870987dd9d64f8c71cb61ee63 Mon Sep 17 00:00:00 2001
From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com>
Date: Sat, 22 Aug 2020 13:20:01 +0200
Subject: [PATCH 03/16] Added insert menu entries and insert action code
---
nw/gui/doceditor.py | 4 ++++
nw/gui/mainmenu.py | 18 ++++++++++++++++++
nw/guimain.py | 2 ++
3 files changed, 24 insertions(+)
diff --git a/nw/gui/doceditor.py b/nw/gui/doceditor.py
index d10a4d6e..dc25b909 100644
--- a/nw/gui/doceditor.py
+++ b/nw/gui/doceditor.py
@@ -612,6 +612,10 @@ class GuiDocEditor(QTextEdit):
theText = nwUnicode.U_EMDASH
elif theInsert == nwDocInsert.ELLIPSIS:
theText = nwUnicode.U_HELLIP
+ elif theInsert == nwDocInsert.MODAPOS_S:
+ theText = nwUnicode.U_MAPOSS
+ elif theInsert == nwDocInsert.MODAPOS_D:
+ theText = nwUnicode.U_MAPOSD
elif theInsert == nwDocInsert.QUOTE_LS:
theText = self.typSQOpen
elif theInsert == nwDocInsert.QUOTE_RS:
diff --git a/nw/gui/mainmenu.py b/nw/gui/mainmenu.py
index c462c748..4aa7e4e9 100644
--- a/nw/gui/mainmenu.py
+++ b/nw/gui/mainmenu.py
@@ -554,6 +554,24 @@ class GuiMainMenu(QMenuBar):
# Insert > Separator
self.insertMenu.addSeparator()
+ # Insert > Modifier Single Apostrophe
+ self.aInsMSApos = QAction("Modifier Single Apostrophe", self)
+ self.aInsMSApos.setStatusTip("Insert unicode modifier letter single apostrophe")
+ self.aInsMSApos.setShortcut("Ctrl+K, '")
+ self.aInsMSApos.triggered.connect(lambda: self._docInsert(nwDocInsert.MODAPOS_S))
+ self.insertMenu.addAction(self.aInsMSApos)
+
+ # Insert > Modifier Double Apostrophe
+ self.aInsMDApos = QAction("Modifier Double Apostrophe", self)
+ self.aInsMDApos.setStatusTip("Insert unicode modifier letter double apostrophe")
+ self.aInsMDApos.setShortcut("Ctrl+K, \"")
+ self.aInsMDApos.triggered.connect(lambda: self._docInsert(nwDocInsert.MODAPOS_D))
+ self.insertMenu.addAction(self.aInsMDApos)
+
+ # Insert > Separator
+ self.insertMenu.addSeparator()
+
+ # Insert > Hard Line Break
# Insert > Hard Line Break
self.aInsHardBreak = QAction("Hard Line Break", self)
self.aInsHardBreak.setStatusTip("Insert a hard line break")
diff --git a/nw/guimain.py b/nw/guimain.py
index 9267a49d..48ea5c73 100644
--- a/nw/guimain.py
+++ b/nw/guimain.py
@@ -1039,6 +1039,8 @@ class GuiMain(QMainWindow):
self.addAction(self.mainMenu.aInsQuoteRS)
self.addAction(self.mainMenu.aInsQuoteLD)
self.addAction(self.mainMenu.aInsQuoteRD)
+ self.addAction(self.mainMenu.aInsMSApos)
+ self.addAction(self.mainMenu.aInsMDApos)
self.addAction(self.mainMenu.aInsHardBreak)
self.addAction(self.mainMenu.aInsNBSpace)
self.addAction(self.mainMenu.aInsThinSpace)
From 995685241a1b400ab9370bd81aa736d9ddfefbf8 Mon Sep 17 00:00:00 2001
From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com>
Date: Sat, 22 Aug 2020 13:20:19 +0200
Subject: [PATCH 04/16] The html converter should strip these out again
---
nw/core/tohtml.py | 2 ++
1 file changed, 2 insertions(+)
diff --git a/nw/core/tohtml.py b/nw/core/tohtml.py
index dadf55bd..6053082a 100644
--- a/nw/core/tohtml.py
+++ b/nw/core/tohtml.py
@@ -54,6 +54,8 @@ class ToHtml(Tokenizer):
nwUnicode.U_NBSP : nwUnicode.H_NBSP,
nwUnicode.U_THNSP : nwUnicode.H_THNSP,
nwUnicode.U_THNBSP : nwUnicode.H_THNBSP,
+ nwUnicode.U_MAPOSS : nwUnicode.H_RSQUO,
+ nwUnicode.U_MAPOSD : nwUnicode.H_RDQUO,
}
self.revDict = {}
self.reReplace = []
From 21d68eb1dc015dd000e2c7aaa6ad96420bb494c4 Mon Sep 17 00:00:00 2001
From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com>
Date: Sat, 22 Aug 2020 13:20:39 +0200
Subject: [PATCH 05/16] Added the new insert menu entries to the corresponding
test
---
tests/test_gui.py | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/tests/test_gui.py b/tests/test_gui.py
index f7104308..318c9f1a 100644
--- a/tests/test_gui.py
+++ b/tests/test_gui.py
@@ -1049,6 +1049,14 @@ def testInsertMenu(qtbot, nwTempGUI, nwFuncTemp, nwTemp):
assert nwGUI.docEditor.getText() == nwGUI.mainConf.fmtDoubleQuotes[1]
nwGUI.docEditor.clear()
+ nwGUI.mainMenu.aInsMSApos.activate(QAction.Trigger)
+ assert nwGUI.docEditor.getText() == nwUnicode.U_MAPOSS
+ nwGUI.docEditor.clear()
+
+ nwGUI.mainMenu.aInsMDApos.activate(QAction.Trigger)
+ assert nwGUI.docEditor.getText() == nwUnicode.U_MAPOSD
+ nwGUI.docEditor.clear()
+
nwGUI.mainMenu.aInsHardBreak.activate(QAction.Trigger)
assert nwGUI.docEditor.getText() == " \n"
nwGUI.docEditor.clear()
From bc1c54b643e8311e061952feada9fd067db3b0a4 Mon Sep 17 00:00:00 2001
From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com>
Date: Sat, 22 Aug 2020 14:06:57 +0200
Subject: [PATCH 06/16] Updated documentation
---
docs/source/index.rst | 1 +
docs/source/interface.rst | 27 ++++++-----
docs/source/typography.rst | 93 ++++++++++++++++++++++++++++++++++++++
3 files changed, 109 insertions(+), 12 deletions(-)
create mode 100644 docs/source/typography.rst
diff --git a/docs/source/index.rst b/docs/source/index.rst
index 8ac56990..fe485433 100644
--- a/docs/source/index.rst
+++ b/docs/source/index.rst
@@ -60,6 +60,7 @@ already familiar with how to run Python applications on your platform.
introduction
started
interface
+ typography
.. toctree::
diff --git a/docs/source/interface.rst b/docs/source/interface.rst
index 48b27691..9191a5d8 100644
--- a/docs/source/interface.rst
+++ b/docs/source/interface.rst
@@ -1,8 +1,8 @@
.. _a_ui:
-***************
+**************
User Interface
-***************
+**************
The user interface is kept as simple as possible to avoid distractions when writing. This page lists
all the main GUI elements, and explains what they do.
@@ -341,11 +341,12 @@ Most features are available as keyboard shortcuts. These are as follows:
":kbd:`Ctrl`:kbd:`B`", "Format selected text, or word under cursor, with strong emphasis (bold)."
":kbd:`Ctrl`:kbd:`C`", "Copy selected text to clipboard."
":kbd:`Ctrl`:kbd:`D`", "Wrap selected text, or word under cursor, in double quotes."
- ":kbd:`Ctrl`:kbd:`E`", "If in the project tree, edit a document or folder settings. (Same as :kbd:`F2`)"
+ ":kbd:`Ctrl`:kbd:`E`", "If in the project tree, edit a document or folder settings. (Same as :kbd:`F2`.)"
":kbd:`Ctrl`:kbd:`F`", "Open the search bar and search for the selected word, if any is selected."
- ":kbd:`Ctrl`:kbd:`G`", "Find next occurrence of search word in current document. (Same as :kbd:`F3`)"
- ":kbd:`Ctrl`:kbd:`H`", "Open the search and replace bar and search for the selected word, if any is selected. (On Mac, this is :kbd:`Cmd`:kbd:`=`)"
+ ":kbd:`Ctrl`:kbd:`G`", "Find next occurrence of search word in current document. (Same as :kbd:`F3`.)"
+ ":kbd:`Ctrl`:kbd:`H`", "Open the search and replace bar and search for the selected word, if any is selected. (On Mac, this is :kbd:`Cmd`:kbd:`=`.)"
":kbd:`Ctrl`:kbd:`I`", "Format selected text, or word under cursor, with emphasis (italic)."
+ ":kbd:`Ctrl`:kbd:`K`", "Activate the insert commands. The commands are listed in :ref:`a_ui_shortcuts_ins`."
":kbd:`Ctrl`:kbd:`N`", "Create new document."
":kbd:`Ctrl`:kbd:`O`", "Open selected document."
":kbd:`Ctrl`:kbd:`Q`", "Exit novelWriter."
@@ -365,7 +366,7 @@ Most features are available as keyboard shortcuts. These are as follows:
":kbd:`Ctrl`:kbd:`Shift`:kbd:`1`", "Replace occurrence of search word in current document, and search for next occurrence."
":kbd:`Ctrl`:kbd:`Shift`:kbd:`A`", "Select all text in current paragraph."
":kbd:`Ctrl`:kbd:`Shift`:kbd:`D`", "Wrap selected text, or word under cursor, in single quotes."
- ":kbd:`Ctrl`:kbd:`Shift`:kbd:`G`", "Find previous occurrence of search word in current document. (Same as :kbd:`Shift`:kbd:`F3`)"
+ ":kbd:`Ctrl`:kbd:`Shift`:kbd:`G`", "Find previous occurrence of search word in current document. (Same as :kbd:`Shift`:kbd:`F3`.)"
":kbd:`Ctrl`:kbd:`Shift`:kbd:`I`", "Import text to the current document from a text file."
":kbd:`Ctrl`:kbd:`Shift`:kbd:`N`", "Create new folder."
":kbd:`Ctrl`:kbd:`Shift`:kbd:`O`", "Open a project."
@@ -386,7 +387,7 @@ Most features are available as keyboard shortcuts. These are as follows:
":kbd:`F10`", "Re-build the project outline."
":kbd:`F11`", "Activate full screen mode."
":kbd:`Shift`:kbd:`F1`", "Open the online documentation in the system default browser."
- ":kbd:`Shift`:kbd:`F3`", "Find previous occurrence of search word in current document. (Same as :kbd:`Ctrl`:kbd:`Shift`:kbd:`G`)"
+ ":kbd:`Shift`:kbd:`F3`", "Find previous occurrence of search word in current document. (Same as :kbd:`Ctrl`:kbd:`Shift`:kbd:`G`.)"
":kbd:`Return`", "If in the project tree, open a document for editing."
.. note::
@@ -409,11 +410,13 @@ combination for the inserted character or punctuation.
":kbd:`Ctrl`:kbd:`K`, :kbd:`-`", "Insert a short dash (en dash)."
":kbd:`Ctrl`:kbd:`K`, :kbd:`_`", "Insert a long dash (em dash)."
- ":kbd:`Ctrl`:kbd:`K`, :kbd:`.`", "Insert ellipsis."
- ":kbd:`Ctrl`:kbd:`K`, :kbd:`1`", "Insert left single quote."
- ":kbd:`Ctrl`:kbd:`K`, :kbd:`2`", "Insert right single quote."
- ":kbd:`Ctrl`:kbd:`K`, :kbd:`3`", "Insert left double quote."
- ":kbd:`Ctrl`:kbd:`K`, :kbd:`4`", "Insert right double quote."
+ ":kbd:`Ctrl`:kbd:`K`, :kbd:`.`", "Insert an ellipsis."
+ ":kbd:`Ctrl`:kbd:`K`, :kbd:`1`", "Insert a left single quote."
+ ":kbd:`Ctrl`:kbd:`K`, :kbd:`2`", "Insert a right single quote."
+ ":kbd:`Ctrl`:kbd:`K`, :kbd:`3`", "Insert a left double quote."
+ ":kbd:`Ctrl`:kbd:`K`, :kbd:`4`", "Insert a right double quote."
+ ":kbd:`Ctrl`:kbd:`K`, :kbd:`'`", "Insert a modifier single apostrophe."
+ ":kbd:`Ctrl`:kbd:`K`, :kbd:`""`", "Insert a modifier double apostrophe."
":kbd:`Ctrl`:kbd:`K`, :kbd:`Return`", "Insert a hard line break."
":kbd:`Ctrl`:kbd:`K`, :kbd:`Space`", "Insert a non-breaking space."
":kbd:`Ctrl`:kbd:`K`, :kbd:`Shift`:kbd:`Space`", "Insert a thin space."
diff --git a/docs/source/typography.rst b/docs/source/typography.rst
new file mode 100644
index 00000000..debddb50
--- /dev/null
+++ b/docs/source/typography.rst
@@ -0,0 +1,93 @@
+.. _a_typ:
+
+*******************
+Typographical Notes
+*******************
+
+novelWriter has some support for typographical symbols that are not usually easily available in many
+text editors. This includes for instance the proper unicode quotation marks, dashes, ellipsis, thin
+spaces, etc. All these symbols are available from the :guilabel:`Insert` menu, and via keyboard
+shortcuts. See :ref:`a_ui_shortcuts_ins`.
+
+This chapter provides some additional information on how novelWriter handles these symbols.
+
+
+.. _a_typ_notes:
+
+Special Notes on Symbols
+========================
+
+Some additional notes on these symbols.
+
+
+Dashes and Ellipsis
+-------------------
+
+With the auto-replace feature enabled (see :ref:`a_ui_edit_auto`), multiple hyphens are converted
+automatically to short and long dashes, and three dots to ellipsis. The last auto-replace can always
+be reverted with the undo command :kbd:`Ctrl`:kbd:`Z`, reverting the text to what you typed before
+the automatic replacement occurred.
+
+
+Single and Double Quotes
+------------------------
+
+All the different quotation marks listed on the `Quotation Mark`_ Wikipedia page are available, and
+can be selected as auto-replaced symbols for straight single and double quote key strokes. The
+settings can be found in the :guilabel:`Preferences`.
+
+Ordinarily, text wrapped in quotes are highlighted by the editor. This is meant as a convenience for
+highlighting dialogue between characters. This feature can be disabled in the
+:guilabel:`Preferences` if this feature isn't wanted.
+
+The editor distinguishes between text wrapped in straight quotes and with the user-selected double
+quote symbols. This is to help the writer recognise which parts of the text are not using the chosen
+quote symbols. Two convenience functions in the :guilabel:`Format` menu can be used to re-format a
+selected section of text with the correct quote symbols.
+
+.. _Quotation Mark: https://en.wikipedia.org/wiki/Quotation_mark
+
+
+Modifier Letter Apostrophes
+---------------------------
+
+The auto-replace feature will consider any right-facing single straight quote as a quote symbol,
+even if it's intended as an apostrophe. This also includes the syntax highlighter, which may decide
+to use an apostrophe as the closing symbol of a single quoted string.
+
+Alternative apostrophes, both single and double, are available. They are special Unicode characters
+that are not categorised as punctuation, but modifiers. They are usually renderred the same way as
+right single and double quotation marks, but not always, depending on the font.
+
+There is a Wikipedia article for both the single_ and double_ version of this symbol, explaining
+what they're for.
+
+You can use these symbols if you want, and especially if you have an apostrophe within a single
+quoted piece of text.
+
+.. note::
+ On export with the :guilabel:`Build Novel Project` tool, these apostrophes will be replaced
+ automatically with the corresponding right hand quote symbols. In that respect, it doesn't matter
+ if you mix them.
+
+.. _single: https://en.wikipedia.org/wiki/Modifier_letter_apostrophe
+.. _double: https://en.wikipedia.org/wiki/Modifier_letter_double_apostrophe
+
+
+Special Space Symbols
+---------------------
+
+A few variations of the regular space character is supported. The correct typographical way to
+separate a number from its unit is with a `thin space`_. It is usually 2/3 the width of a regular
+space. For numbers and units, this should in addition be a non-breaking space, that is, the text
+wrapping should not add a line break on this particular space.
+
+A regular space can also be made into a non-breaking space.
+
+All non-breaking spaces are highlighted with a differently coloured packground. The colour will
+depend on the selected colour theme.
+
+The thin and non-breaking spaces are converted to their corresponding HTML codes on export to HTML
+format. For plain text, they are exported as regular spaces.
+
+.. _thin space: https://en.wikipedia.org/wiki/Thin_space
From 6c367a4b58b2a3b329a844852628cb6363fea078 Mon Sep 17 00:00:00 2001
From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com>
Date: Sat, 22 Aug 2020 14:10:22 +0200
Subject: [PATCH 07/16] Added prefixes to documentation files
---
docs/source/index.rst | 18 +++++++++---------
.../{interface.rst => int_interface.rst} | 0
.../{introduction.rst => int_introduction.rst} | 0
docs/source/{started.rst => int_started.rst} | 0
.../{typography.rst => int_typography.rst} | 0
.../{technical.rst => tech_technical.rst} | 0
docs/source/{export.rst => write_export.rst} | 0
docs/source/{notes.rst => write_notes.rst} | 0
.../{projects.rst => write_projects.rst} | 0
.../{structure.rst => write_structure.rst} | 0
10 files changed, 9 insertions(+), 9 deletions(-)
rename docs/source/{interface.rst => int_interface.rst} (100%)
rename docs/source/{introduction.rst => int_introduction.rst} (100%)
rename docs/source/{started.rst => int_started.rst} (100%)
rename docs/source/{typography.rst => int_typography.rst} (100%)
rename docs/source/{technical.rst => tech_technical.rst} (100%)
rename docs/source/{export.rst => write_export.rst} (100%)
rename docs/source/{notes.rst => write_notes.rst} (100%)
rename docs/source/{projects.rst => write_projects.rst} (100%)
rename docs/source/{structure.rst => write_structure.rst} (100%)
diff --git a/docs/source/index.rst b/docs/source/index.rst
index fe485433..03e8cf98 100644
--- a/docs/source/index.rst
+++ b/docs/source/index.rst
@@ -57,27 +57,27 @@ already familiar with how to run Python applications on your platform.
:maxdepth: 2
:caption: First Steps
- introduction
- started
- interface
- typography
+ int_introduction
+ int_started
+ int_interface
+ int_typography
.. toctree::
:maxdepth: 2
:caption: Writing Novels
- projects
- structure
- notes
- export
+ write_projects
+ write_structure
+ write_notes
+ write_export
.. toctree::
:maxdepth: 2
:caption: Under the Hood
- technical
+ tech_technical
Indices and Tables
diff --git a/docs/source/interface.rst b/docs/source/int_interface.rst
similarity index 100%
rename from docs/source/interface.rst
rename to docs/source/int_interface.rst
diff --git a/docs/source/introduction.rst b/docs/source/int_introduction.rst
similarity index 100%
rename from docs/source/introduction.rst
rename to docs/source/int_introduction.rst
diff --git a/docs/source/started.rst b/docs/source/int_started.rst
similarity index 100%
rename from docs/source/started.rst
rename to docs/source/int_started.rst
diff --git a/docs/source/typography.rst b/docs/source/int_typography.rst
similarity index 100%
rename from docs/source/typography.rst
rename to docs/source/int_typography.rst
diff --git a/docs/source/technical.rst b/docs/source/tech_technical.rst
similarity index 100%
rename from docs/source/technical.rst
rename to docs/source/tech_technical.rst
diff --git a/docs/source/export.rst b/docs/source/write_export.rst
similarity index 100%
rename from docs/source/export.rst
rename to docs/source/write_export.rst
diff --git a/docs/source/notes.rst b/docs/source/write_notes.rst
similarity index 100%
rename from docs/source/notes.rst
rename to docs/source/write_notes.rst
diff --git a/docs/source/projects.rst b/docs/source/write_projects.rst
similarity index 100%
rename from docs/source/projects.rst
rename to docs/source/write_projects.rst
diff --git a/docs/source/structure.rst b/docs/source/write_structure.rst
similarity index 100%
rename from docs/source/structure.rst
rename to docs/source/write_structure.rst
From 42fa913be571b053c9690dc06ad3ca2a9131c969 Mon Sep 17 00:00:00 2001
From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com>
Date: Sat, 22 Aug 2020 14:17:18 +0200
Subject: [PATCH 08/16] Shorter menu entries
---
nw/gui/mainmenu.py | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/nw/gui/mainmenu.py b/nw/gui/mainmenu.py
index 4aa7e4e9..949a8c77 100644
--- a/nw/gui/mainmenu.py
+++ b/nw/gui/mainmenu.py
@@ -554,15 +554,15 @@ class GuiMainMenu(QMenuBar):
# Insert > Separator
self.insertMenu.addSeparator()
- # Insert > Modifier Single Apostrophe
- self.aInsMSApos = QAction("Modifier Single Apostrophe", self)
+ # Insert > Alt. Single Apostrophe
+ self.aInsMSApos = QAction("Alt. Single Apostrophe", self)
self.aInsMSApos.setStatusTip("Insert unicode modifier letter single apostrophe")
self.aInsMSApos.setShortcut("Ctrl+K, '")
self.aInsMSApos.triggered.connect(lambda: self._docInsert(nwDocInsert.MODAPOS_S))
self.insertMenu.addAction(self.aInsMSApos)
- # Insert > Modifier Double Apostrophe
- self.aInsMDApos = QAction("Modifier Double Apostrophe", self)
+ # Insert > Alt. Double Apostrophe
+ self.aInsMDApos = QAction("Alt. Double Apostrophe", self)
self.aInsMDApos.setStatusTip("Insert unicode modifier letter double apostrophe")
self.aInsMDApos.setShortcut("Ctrl+K, \"")
self.aInsMDApos.triggered.connect(lambda: self._docInsert(nwDocInsert.MODAPOS_D))
@@ -571,7 +571,6 @@ class GuiMainMenu(QMenuBar):
# Insert > Separator
self.insertMenu.addSeparator()
- # Insert > Hard Line Break
# Insert > Hard Line Break
self.aInsHardBreak = QAction("Hard Line Break", self)
self.aInsHardBreak.setStatusTip("Insert a hard line break")
From 5f033a98bfeb662eb53418c6e557754a6d9e9976 Mon Sep 17 00:00:00 2001
From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com>
Date: Sun, 23 Aug 2020 20:55:30 +0200
Subject: [PATCH 09/16] Remove the alternative double apostrophe again
---
nw/constants/constants.py | 2 --
nw/constants/enum.py | 1 -
nw/core/tohtml.py | 1 -
nw/gui/doceditor.py | 2 --
nw/gui/mainmenu.py | 11 ++---------
nw/guimain.py | 1 -
6 files changed, 2 insertions(+), 16 deletions(-)
diff --git a/nw/constants/constants.py b/nw/constants/constants.py
index e810dabf..3bfc9125 100644
--- a/nw/constants/constants.py
+++ b/nw/constants/constants.py
@@ -234,7 +234,6 @@ class nwUnicode:
U_EMDASH = "\u2014" # Long dash
U_HELLIP = "\u2026" # Ellipsis
U_MAPOSS = "\u02bc" # Modifier letter single apostrophe
- U_MAPOSD = "\u02ee" # Modifier letter double apostrophe
## Spaces and Lines
U_NBSP = "\u00a0" # Non-breaking space
@@ -286,7 +285,6 @@ class nwUnicode:
H_EMDASH = "—"
H_HELLIP = "…"
H_MAPOSS = "ʼ"
- H_MAPOSD = "ˮ"
## Spaces
H_NBSP = " "
diff --git a/nw/constants/enum.py b/nw/constants/enum.py
index 81200a63..d7c3bde2 100644
--- a/nw/constants/enum.py
+++ b/nw/constants/enum.py
@@ -113,7 +113,6 @@ class nwDocInsert(Enum):
QUOTE_LD = 10
QUOTE_RD = 11
MODAPOS_S = 12
- MODAPOS_D = 13
# END Enum nwDocInsert
diff --git a/nw/core/tohtml.py b/nw/core/tohtml.py
index 6053082a..cb8cfa1a 100644
--- a/nw/core/tohtml.py
+++ b/nw/core/tohtml.py
@@ -55,7 +55,6 @@ class ToHtml(Tokenizer):
nwUnicode.U_THNSP : nwUnicode.H_THNSP,
nwUnicode.U_THNBSP : nwUnicode.H_THNBSP,
nwUnicode.U_MAPOSS : nwUnicode.H_RSQUO,
- nwUnicode.U_MAPOSD : nwUnicode.H_RDQUO,
}
self.revDict = {}
self.reReplace = []
diff --git a/nw/gui/doceditor.py b/nw/gui/doceditor.py
index dc25b909..ca860561 100644
--- a/nw/gui/doceditor.py
+++ b/nw/gui/doceditor.py
@@ -614,8 +614,6 @@ class GuiDocEditor(QTextEdit):
theText = nwUnicode.U_HELLIP
elif theInsert == nwDocInsert.MODAPOS_S:
theText = nwUnicode.U_MAPOSS
- elif theInsert == nwDocInsert.MODAPOS_D:
- theText = nwUnicode.U_MAPOSD
elif theInsert == nwDocInsert.QUOTE_LS:
theText = self.typSQOpen
elif theInsert == nwDocInsert.QUOTE_RS:
diff --git a/nw/gui/mainmenu.py b/nw/gui/mainmenu.py
index 949a8c77..b904ffd9 100644
--- a/nw/gui/mainmenu.py
+++ b/nw/gui/mainmenu.py
@@ -554,20 +554,13 @@ class GuiMainMenu(QMenuBar):
# Insert > Separator
self.insertMenu.addSeparator()
- # Insert > Alt. Single Apostrophe
- self.aInsMSApos = QAction("Alt. Single Apostrophe", self)
+ # Insert > Alternative Apostrophe
+ self.aInsMSApos = QAction("Alternative Apostrophe", self)
self.aInsMSApos.setStatusTip("Insert unicode modifier letter single apostrophe")
self.aInsMSApos.setShortcut("Ctrl+K, '")
self.aInsMSApos.triggered.connect(lambda: self._docInsert(nwDocInsert.MODAPOS_S))
self.insertMenu.addAction(self.aInsMSApos)
- # Insert > Alt. Double Apostrophe
- self.aInsMDApos = QAction("Alt. Double Apostrophe", self)
- self.aInsMDApos.setStatusTip("Insert unicode modifier letter double apostrophe")
- self.aInsMDApos.setShortcut("Ctrl+K, \"")
- self.aInsMDApos.triggered.connect(lambda: self._docInsert(nwDocInsert.MODAPOS_D))
- self.insertMenu.addAction(self.aInsMDApos)
-
# Insert > Separator
self.insertMenu.addSeparator()
diff --git a/nw/guimain.py b/nw/guimain.py
index 48ea5c73..66bcc7cb 100644
--- a/nw/guimain.py
+++ b/nw/guimain.py
@@ -1040,7 +1040,6 @@ class GuiMain(QMainWindow):
self.addAction(self.mainMenu.aInsQuoteLD)
self.addAction(self.mainMenu.aInsQuoteRD)
self.addAction(self.mainMenu.aInsMSApos)
- self.addAction(self.mainMenu.aInsMDApos)
self.addAction(self.mainMenu.aInsHardBreak)
self.addAction(self.mainMenu.aInsNBSpace)
self.addAction(self.mainMenu.aInsThinSpace)
From f1f7697df2e15655d4f2526fcc5107fdecd599c0 Mon Sep 17 00:00:00 2001
From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com>
Date: Sun, 23 Aug 2020 20:55:54 +0200
Subject: [PATCH 10/16] Clean up docs and test
---
docs/source/int_interface.rst | 3 +--
docs/source/int_typography.rst | 24 +++++++++---------------
tests/test_gui.py | 4 ----
3 files changed, 10 insertions(+), 21 deletions(-)
diff --git a/docs/source/int_interface.rst b/docs/source/int_interface.rst
index 9191a5d8..3c4b772a 100644
--- a/docs/source/int_interface.rst
+++ b/docs/source/int_interface.rst
@@ -415,8 +415,7 @@ combination for the inserted character or punctuation.
":kbd:`Ctrl`:kbd:`K`, :kbd:`2`", "Insert a right single quote."
":kbd:`Ctrl`:kbd:`K`, :kbd:`3`", "Insert a left double quote."
":kbd:`Ctrl`:kbd:`K`, :kbd:`4`", "Insert a right double quote."
- ":kbd:`Ctrl`:kbd:`K`, :kbd:`'`", "Insert a modifier single apostrophe."
- ":kbd:`Ctrl`:kbd:`K`, :kbd:`""`", "Insert a modifier double apostrophe."
+ ":kbd:`Ctrl`:kbd:`K`, :kbd:`'`", "Insert a modifier apostrophe."
":kbd:`Ctrl`:kbd:`K`, :kbd:`Return`", "Insert a hard line break."
":kbd:`Ctrl`:kbd:`K`, :kbd:`Space`", "Insert a non-breaking space."
":kbd:`Ctrl`:kbd:`K`, :kbd:`Shift`:kbd:`Space`", "Insert a thin space."
diff --git a/docs/source/int_typography.rst b/docs/source/int_typography.rst
index debddb50..1903ba15 100644
--- a/docs/source/int_typography.rst
+++ b/docs/source/int_typography.rst
@@ -52,26 +52,20 @@ Modifier Letter Apostrophes
---------------------------
The auto-replace feature will consider any right-facing single straight quote as a quote symbol,
-even if it's intended as an apostrophe. This also includes the syntax highlighter, which may decide
-to use an apostrophe as the closing symbol of a single quoted string.
+even if it's intended as an apostrophe. This also includes the syntax highlighter, which may assume
+the first following apostrophe is the closing symbol of a single quoted region of text.
-Alternative apostrophes, both single and double, are available. They are special Unicode characters
-that are not categorised as punctuation, but modifiers. They are usually renderred the same way as
-right single and double quotation marks, but not always, depending on the font.
-
-There is a Wikipedia article for both the single_ and double_ version of this symbol, explaining
-what they're for.
-
-You can use these symbols if you want, and especially if you have an apostrophe within a single
-quoted piece of text.
+To get around this, an alternative apostrophe is available. It is a special Unicode character that
+is not categorised as punctuation, but as a modifier. It is usually renderred the same way as right
+single quotation marks, but not always, depending on the font. There is a Wikipedia article for the
+`Modifier letter apostrophe`_ with more details.
.. note::
On export with the :guilabel:`Build Novel Project` tool, these apostrophes will be replaced
- automatically with the corresponding right hand quote symbols. In that respect, it doesn't matter
- if you mix them.
+ automatically with the corresponding right hand quote symbols as is generally recommended.
+ Therefore it doesn't really matter if you only use them to correct highlighting.
-.. _single: https://en.wikipedia.org/wiki/Modifier_letter_apostrophe
-.. _double: https://en.wikipedia.org/wiki/Modifier_letter_double_apostrophe
+.. _Modifier letter apostrophe: https://en.wikipedia.org/wiki/Modifier_letter_apostrophe
Special Space Symbols
diff --git a/tests/test_gui.py b/tests/test_gui.py
index 318c9f1a..22f82c61 100644
--- a/tests/test_gui.py
+++ b/tests/test_gui.py
@@ -1053,10 +1053,6 @@ def testInsertMenu(qtbot, nwTempGUI, nwFuncTemp, nwTemp):
assert nwGUI.docEditor.getText() == nwUnicode.U_MAPOSS
nwGUI.docEditor.clear()
- nwGUI.mainMenu.aInsMDApos.activate(QAction.Trigger)
- assert nwGUI.docEditor.getText() == nwUnicode.U_MAPOSD
- nwGUI.docEditor.clear()
-
nwGUI.mainMenu.aInsHardBreak.activate(QAction.Trigger)
assert nwGUI.docEditor.getText() == " \n"
nwGUI.docEditor.clear()
From 9567e3dc4a68351bc7ecd3f9876520d979093d9c Mon Sep 17 00:00:00 2001
From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com>
Date: Sun, 23 Aug 2020 20:56:15 +0200
Subject: [PATCH 11/16] Improve regex matches for quote strings
---
nw/gui/dochighlight.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/nw/gui/dochighlight.py b/nw/gui/dochighlight.py
index 23902db1..a374c9a6 100644
--- a/nw/gui/dochighlight.py
+++ b/nw/gui/dochighlight.py
@@ -143,17 +143,17 @@ class GuiDocHighlighter(QSyntaxHighlighter):
# Quoted Strings
if self.mainConf.highlightQuotes:
self.hRules.append((
- "{:s}(.+?){:s}".format('"', '"'), {
+ "\\B{:s}(.*?){:s}\\B".format('"', '"'), {
0 : self.hStyles["dialogue1"],
}
))
self.hRules.append((
- "{:s}(.+?){:s}".format(*self.mainConf.fmtDoubleQuotes), {
+ "\\B{:s}(.*?){:s}\\B".format(*self.mainConf.fmtDoubleQuotes), {
0 : self.hStyles["dialogue2"],
}
))
self.hRules.append((
- "{:s}(.+?){:s}".format(*self.mainConf.fmtSingleQuotes), {
+ "\\B{:s}(.*?){:s}\\B".format(*self.mainConf.fmtSingleQuotes), {
0 : self.hStyles["dialogue3"],
}
))
From 8340c0665d3bc4a0ba44f2ac5907ebfb60dd5368 Mon Sep 17 00:00:00 2001
From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com>
Date: Fri, 28 Aug 2020 12:31:38 +0200
Subject: [PATCH 12/16] Made project tests independent
---
tests/conftest.py | 57 +++++---
tests/minimal/ToC.json | 17 +++
tests/minimal/ToC.txt | 10 ++
tests/minimal/content/8c659a11cd429.nwd | 3 +
tests/minimal/content/a35baf2e93843.nwd | 4 +
tests/minimal/content/f5ab3e30151e1.nwd | 3 +
tests/minimal/meta/guiOptions.json | 1 +
tests/minimal/meta/sessionStats.log | 2 +
tests/minimal/meta/tagsIndex.json | 91 +++++++++++++
tests/minimal/nwProject.nwx | 115 ++++++++++++++++
tests/reference/proj/3_nwProject.nwx | 38 +-----
tests/test_project.py | 171 ++++++++++++++----------
12 files changed, 390 insertions(+), 122 deletions(-)
create mode 100644 tests/minimal/ToC.json
create mode 100644 tests/minimal/ToC.txt
create mode 100644 tests/minimal/content/8c659a11cd429.nwd
create mode 100644 tests/minimal/content/a35baf2e93843.nwd
create mode 100644 tests/minimal/content/f5ab3e30151e1.nwd
create mode 100644 tests/minimal/meta/guiOptions.json
create mode 100644 tests/minimal/meta/sessionStats.log
create mode 100644 tests/minimal/meta/tagsIndex.json
create mode 100644 tests/minimal/nwProject.nwx
diff --git a/tests/conftest.py b/tests/conftest.py
index 7423d849..d4b10213 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -5,10 +5,14 @@
import sys
import pytest
import shutil
+
from os import path, mkdir
+from nwdummy import DummyMain
sys.path.insert(1, path.abspath(path.join(path.dirname(__file__), path.pardir)))
+from nw.config import Config # noqa: E402
+
@pytest.fixture(scope="session")
def nwTemp():
testDir = path.dirname(__file__)
@@ -19,6 +23,24 @@ def nwTemp():
mkdir(tempDir)
return tempDir
+@pytest.fixture(scope="session")
+def nwRef():
+ testDir = path.dirname(__file__)
+ refDir = path.join(testDir, "reference")
+ return refDir
+
+@pytest.fixture(scope="session")
+def nwConf(nwRef, nwTemp):
+ theConf = Config()
+ theConf.initConfig(nwRef, nwTemp)
+ return theConf
+
+@pytest.fixture(scope="session")
+def nwDummy(nwRef, nwTemp, nwConf):
+ theDummy = DummyMain()
+ theDummy.mainConf = nwConf
+ return theDummy
+
@pytest.fixture(scope="session")
def nwTempProj(nwTemp):
projDir = path.join(nwTemp, "proj")
@@ -40,20 +62,6 @@ def nwTempBuild(nwTemp):
mkdir(buildDir)
return buildDir
-@pytest.fixture(scope="session")
-def nwTempCustom(nwTemp):
- customDir = path.join(nwTemp, "custom")
- if not path.isdir(customDir):
- mkdir(customDir)
- return customDir
-
-@pytest.fixture(scope="session")
-def nwTempSample(nwTemp):
- sampleDir = path.join(nwTemp, "sample")
- if not path.isdir(sampleDir):
- mkdir(sampleDir)
- return sampleDir
-
@pytest.fixture(scope="function")
def nwFuncTemp(nwTemp):
funcDir = path.join(nwTemp, "ftemp")
@@ -66,11 +74,24 @@ def nwFuncTemp(nwTemp):
shutil.rmtree(funcDir)
return
-@pytest.fixture(scope="session")
-def nwRef():
+@pytest.fixture(scope="function")
+def nwMinimal(nwTemp):
testDir = path.dirname(__file__)
- refDir = path.join(testDir, "reference")
- return refDir
+ minimalStore = path.join(testDir, "minimal")
+ minimalDir = path.join(nwTemp, "minimal")
+ if path.isdir(minimalDir):
+ shutil.rmtree(minimalDir)
+ shutil.copytree(minimalStore, minimalDir)
+ cacheDir = path.join(minimalDir, "cache")
+ if path.isdir(cacheDir):
+ shutil.rmtree(cacheDir)
+ metaDir = path.join(minimalDir, "meta")
+ if path.isdir(metaDir):
+ shutil.rmtree(metaDir)
+ yield minimalDir
+ if path.isdir(minimalDir):
+ shutil.rmtree(minimalDir)
+ return
@pytest.fixture(scope="session")
def nwLipsum():
diff --git a/tests/minimal/ToC.json b/tests/minimal/ToC.json
new file mode 100644
index 00000000..5881c392
--- /dev/null
+++ b/tests/minimal/ToC.json
@@ -0,0 +1,17 @@
+[
+ [
+ "content/8c659a11cd429.nwd",
+ "NOVEL",
+ "New Scene"
+ ],
+ [
+ "content/a35baf2e93843.nwd",
+ "NOVEL",
+ "Title Page"
+ ],
+ [
+ "content/f5ab3e30151e1.nwd",
+ "NOVEL",
+ "New Chapter"
+ ]
+]
\ No newline at end of file
diff --git a/tests/minimal/ToC.txt b/tests/minimal/ToC.txt
new file mode 100644
index 00000000..f5b94b72
--- /dev/null
+++ b/tests/minimal/ToC.txt
@@ -0,0 +1,10 @@
+
+ Table of Contents
+===================
+
+ File Name Class Document Label
+--------------------------------------------------------------------------------
+ content/8c659a11cd429.nwd NOVEL New Scene
+ content/a35baf2e93843.nwd NOVEL Title Page
+ content/f5ab3e30151e1.nwd NOVEL New Chapter
+
diff --git a/tests/minimal/content/8c659a11cd429.nwd b/tests/minimal/content/8c659a11cd429.nwd
new file mode 100644
index 00000000..5ecf5c59
--- /dev/null
+++ b/tests/minimal/content/8c659a11cd429.nwd
@@ -0,0 +1,3 @@
+%%~ 8c659a11cd429:a6d311a93600a:a508bb932959c:NOVEL:SCENE:New Scene
+### New Scene
+
diff --git a/tests/minimal/content/a35baf2e93843.nwd b/tests/minimal/content/a35baf2e93843.nwd
new file mode 100644
index 00000000..a7745e3b
--- /dev/null
+++ b/tests/minimal/content/a35baf2e93843.nwd
@@ -0,0 +1,4 @@
+%%~ a35baf2e93843:a508bb932959c:NOVEL:TITLE:Title Page
+# Minimal
+
+By Jane Doe, John Doh
diff --git a/tests/minimal/content/f5ab3e30151e1.nwd b/tests/minimal/content/f5ab3e30151e1.nwd
new file mode 100644
index 00000000..ba1c9faa
--- /dev/null
+++ b/tests/minimal/content/f5ab3e30151e1.nwd
@@ -0,0 +1,3 @@
+%%~ f5ab3e30151e1:a6d311a93600a:a508bb932959c:NOVEL:CHAPTER:New Chapter
+## New Chapter
+
diff --git a/tests/minimal/meta/guiOptions.json b/tests/minimal/meta/guiOptions.json
new file mode 100644
index 00000000..9e26dfee
--- /dev/null
+++ b/tests/minimal/meta/guiOptions.json
@@ -0,0 +1 @@
+{}
\ No newline at end of file
diff --git a/tests/minimal/meta/sessionStats.log b/tests/minimal/meta/sessionStats.log
new file mode 100644
index 00000000..38cbde3d
--- /dev/null
+++ b/tests/minimal/meta/sessionStats.log
@@ -0,0 +1,2 @@
+# Start Time End Time Novel Notes
+2020-08-28 11:36:36 2020-08-28 11:36:48 10 0
diff --git a/tests/minimal/meta/tagsIndex.json b/tests/minimal/meta/tagsIndex.json
new file mode 100644
index 00000000..ec600a1c
--- /dev/null
+++ b/tests/minimal/meta/tagsIndex.json
@@ -0,0 +1,91 @@
+{
+ "tagIndex": {},
+ "refIndex": {
+ "a35baf2e93843": {
+ "T000000": {
+ "tags": [],
+ "updated": 1598607396
+ },
+ "T000001": {
+ "tags": [],
+ "updated": 1598607396
+ }
+ },
+ "f5ab3e30151e1": {
+ "T000000": {
+ "tags": [],
+ "updated": 1598607396
+ },
+ "T000001": {
+ "tags": [],
+ "updated": 1598607396
+ }
+ },
+ "8c659a11cd429": {
+ "T000000": {
+ "tags": [],
+ "updated": 1598607396
+ },
+ "T000001": {
+ "tags": [],
+ "updated": 1598607396
+ }
+ }
+ },
+ "novelIndex": {
+ "a35baf2e93843": {
+ "T000001": {
+ "level": "H1",
+ "title": "Minimal",
+ "layout": "TITLE",
+ "synopsis": "",
+ "cCount": 7,
+ "wCount": 1,
+ "pCount": 0,
+ "updated": 1598607396
+ }
+ },
+ "f5ab3e30151e1": {
+ "T000001": {
+ "level": "H2",
+ "title": "New Chapter",
+ "layout": "CHAPTER",
+ "synopsis": "",
+ "cCount": 11,
+ "wCount": 2,
+ "pCount": 0,
+ "updated": 1598607396
+ }
+ },
+ "8c659a11cd429": {
+ "T000001": {
+ "level": "H3",
+ "title": "New Scene",
+ "layout": "SCENE",
+ "synopsis": "",
+ "cCount": 9,
+ "wCount": 2,
+ "pCount": 0,
+ "updated": 1598607396
+ }
+ }
+ },
+ "noteIndex": {},
+ "textCounts": {
+ "a35baf2e93843": [
+ 28,
+ 6,
+ 1
+ ],
+ "f5ab3e30151e1": [
+ 11,
+ 2,
+ 0
+ ],
+ "8c659a11cd429": [
+ 9,
+ 2,
+ 0
+ ]
+ }
+}
\ No newline at end of file
diff --git a/tests/minimal/nwProject.nwx b/tests/minimal/nwProject.nwx
new file mode 100644
index 00000000..af99a8ce
--- /dev/null
+++ b/tests/minimal/nwProject.nwx
@@ -0,0 +1,115 @@
+
+
+
+ Test Minimal
+ Minimal
+ Jane Doe
+ John Doh
+ 1
+ 1
+ 8
+
+
+ True
+ False
+ True
+ None
+ None
+ 10
+ 10
+ 0
+
+
+ %title%
+ Chapter %ch%: %title%
+ %title%
+ * * *
+
+
+
+ New
+ Note
+ Draft
+ Finished
+
+
+ New
+ Minor
+ Major
+ Main
+
+
+
+ -
+ Novel
+ ROOT
+ NOVEL
+ New
+ False
+
+ -
+ Title Page
+ FILE
+ NOVEL
+ New
+ True
+ TITLE
+ 28
+ 6
+ 1
+ 0
+
+ -
+ New Chapter
+ FOLDER
+ NOVEL
+ New
+ False
+
+ -
+ New Chapter
+ FILE
+ NOVEL
+ New
+ True
+ CHAPTER
+ 11
+ 2
+ 0
+ 0
+
+ -
+ New Scene
+ FILE
+ NOVEL
+ New
+ True
+ SCENE
+ 9
+ 2
+ 0
+ 0
+
+ -
+ Plot
+ ROOT
+ PLOT
+ New
+ False
+
+ -
+ Characters
+ ROOT
+ CHARACTER
+ New
+ False
+
+ -
+ World
+ ROOT
+ WORLD
+ New
+ False
+
+
+
diff --git a/tests/reference/proj/3_nwProject.nwx b/tests/reference/proj/3_nwProject.nwx
index 0c396343..c2be9007 100644
--- a/tests/reference/proj/3_nwProject.nwx
+++ b/tests/reference/proj/3_nwProject.nwx
@@ -1,9 +1,9 @@
-
+
New Project
- 5
+ 2
1
0
@@ -37,7 +37,7 @@
Main
-
+
-
Novel
ROOT
@@ -109,35 +109,7 @@
0
0
- -
- Timeline
- ROOT
- TIMELINE
- New
- False
-
- -
- Object
- ROOT
- OBJECT
- New
- False
-
- -
- Custom1
- ROOT
- CUSTOM
- New
- False
-
- -
- Custom2
- ROOT
- CUSTOM
- New
- False
-
- -
+
-
Hello
FILE
NOVEL
@@ -149,7 +121,7 @@
0
0
- -
+
-
Jane
FILE
CHARACTER
diff --git a/tests/test_project.py b/tests/test_project.py
index 005bac2b..4e90a8c4 100644
--- a/tests/test_project.py
+++ b/tests/test_project.py
@@ -4,63 +4,66 @@
import pytest
from os import path
+from shutil import copyfile
from nwtools import cmpFiles
-from nwdummy import DummyMain
-from nw.config import Config
from nw.core.project import NWProject
from nw.core.index import NWIndex
from nw.constants import nwItemClass
-theConf = Config()
-theMain = DummyMain()
-theMain.mainConf = theConf
-
-theProject = NWProject(theMain)
-theProject.projTree.setSeed(42)
-
@pytest.mark.project
-def testProjectNewMinimal(nwTempProj, nwRef, nwTemp):
- projFile = path.join(nwTempProj, "nwProject.nwx")
+def testProjectNewOpenSave(nwFuncTemp, nwTempProj, nwRef, nwTemp, nwDummy):
+ projFile = path.join(nwFuncTemp, "nwProject.nwx")
+ testFile = path.join(nwTempProj, "1_nwProject.nwx")
refFile = path.join(nwRef, "proj", "1_nwProject.nwx")
- assert theConf.initConfig(nwRef, nwTemp)
- assert theProject.newProject({"projPath": nwTempProj})
- assert theProject.setProjectPath(nwTempProj)
+
+ theProject = NWProject(nwDummy)
+ theProject.projTree.setSeed(42)
+
+ assert theProject.newProject({"projPath": nwFuncTemp})
+ assert theProject.setProjectPath(nwFuncTemp)
assert theProject.saveProject()
assert theProject.closeProject()
- assert cmpFiles(projFile, refFile, [2, 6, 7, 8])
-@pytest.mark.project
-def testProjectOpen(nwTempProj):
- projFile = path.join(nwTempProj, "nwProject.nwx")
+ # Check the new project
+ copyfile(projFile, testFile)
+ assert cmpFiles(testFile, refFile, [2, 6, 7, 8])
+
+ # Open again
assert theProject.openProject(projFile)
-@pytest.mark.project
-def testProjectSave(nwTempProj, nwRef):
- projFile = path.join(nwTempProj, "nwProject.nwx")
- refFile = path.join(nwRef, "proj", "1_nwProject.nwx")
+ # Save and close
assert theProject.saveProject()
assert theProject.closeProject()
- assert cmpFiles(projFile, refFile, [2, 6, 7, 8])
+ copyfile(projFile, testFile)
+ assert cmpFiles(testFile, refFile, [2, 6, 7, 8])
assert not theProject.projChanged
-@pytest.mark.project
-def testProjectOpenTwice(nwTempProj, nwRef):
- projFile = path.join(nwTempProj, "nwProject.nwx")
- refFile = path.join(nwRef, "proj", "1_nwProject.nwx")
+ # Open a second time
assert theProject.openProject(projFile)
assert not theProject.openProject(projFile)
assert theProject.openProject(projFile, overrideLock=True)
assert theProject.saveProject()
assert theProject.closeProject()
- assert cmpFiles(projFile, refFile, [2, 6, 7, 8])
+ copyfile(projFile, testFile)
+ assert cmpFiles(testFile, refFile, [2, 6, 7, 8])
@pytest.mark.project
-def testProjectNewRoot(nwTempProj, nwRef):
- projFile = path.join(nwTempProj, "nwProject.nwx")
+def testProjectNewRoot(nwFuncTemp, nwTempProj, nwRef, nwDummy):
+ projFile = path.join(nwFuncTemp, "nwProject.nwx")
+ testFile = path.join(nwTempProj, "2_nwProject.nwx")
refFile = path.join(nwRef, "proj", "2_nwProject.nwx")
+
+ theProject = NWProject(nwDummy)
+ theProject.projTree.setSeed(42)
+
+ assert theProject.newProject({"projPath": nwFuncTemp})
+ assert theProject.setProjectPath(nwFuncTemp)
+ assert theProject.saveProject()
+ assert theProject.closeProject()
assert theProject.openProject(projFile)
+
assert isinstance(theProject.newRoot("Novel", nwItemClass.NOVEL), type(None))
assert isinstance(theProject.newRoot("Plot", nwItemClass.PLOT), type(None))
assert isinstance(theProject.newRoot("Character", nwItemClass.CHARACTER), type(None))
@@ -69,31 +72,48 @@ def testProjectNewRoot(nwTempProj, nwRef):
assert isinstance(theProject.newRoot("Object", nwItemClass.OBJECT), str)
assert isinstance(theProject.newRoot("Custom1", nwItemClass.CUSTOM), str)
assert isinstance(theProject.newRoot("Custom2", nwItemClass.CUSTOM), str)
+
assert theProject.projChanged
assert theProject.saveProject()
assert theProject.closeProject()
- assert cmpFiles(projFile, refFile, [2, 6, 7, 8])
+
+ copyfile(projFile, testFile)
+ assert cmpFiles(testFile, refFile, [2, 6, 7, 8])
assert not theProject.projChanged
@pytest.mark.project
-def testProjectNewFile(nwTempProj, nwRef):
- projFile = path.join(nwTempProj, "nwProject.nwx")
+def testProjectNewFile(nwFuncTemp, nwTempProj, nwRef, nwDummy):
+ projFile = path.join(nwFuncTemp, "nwProject.nwx")
+ testFile = path.join(nwTempProj, "3_nwProject.nwx")
refFile = path.join(nwRef, "proj", "3_nwProject.nwx")
+
+ theProject = NWProject(nwDummy)
+ theProject.projTree.setSeed(42)
+
+ assert theProject.newProject({"projPath": nwFuncTemp})
+ assert theProject.setProjectPath(nwFuncTemp)
+ assert theProject.saveProject()
+ assert theProject.closeProject()
assert theProject.openProject(projFile)
- assert isinstance(theProject.newFile("Hello", nwItemClass.NOVEL, "73475cb40a568"), str)
+
+ assert isinstance(theProject.newFile("Hello", nwItemClass.NOVEL, "31489056e0916"), str)
assert isinstance(theProject.newFile("Jane", nwItemClass.CHARACTER, "71ee45a3c0db9"), str)
assert theProject.projChanged
assert theProject.saveProject()
assert theProject.closeProject()
- assert cmpFiles(projFile, refFile, [2, 6, 7, 8])
+
+ copyfile(projFile, testFile)
+ assert cmpFiles(testFile, refFile, [2, 6, 7, 8])
assert not theProject.projChanged
@pytest.mark.project
-def testIndexScanThis(nwTempProj):
- projFile = path.join(nwTempProj, "nwProject.nwx")
- assert theProject.openProject(projFile)
+def testIndexScanThis(nwMinimal, nwDummy):
- theIndex = NWIndex(theProject, theMain)
+ theProject = NWProject(nwDummy)
+ theProject.projTree.setSeed(42)
+ assert theProject.openProject(nwMinimal)
+
+ theIndex = NWIndex(theProject, nwDummy)
isValid, theBits, thePos = theIndex.scanThis("tag: this, and this")
assert not isValid
@@ -135,15 +155,17 @@ def testIndexScanThis(nwTempProj):
assert theProject.closeProject()
@pytest.mark.project
-def testIndexCheckThese(nwTempProj):
- projFile = path.join(nwTempProj, "nwProject.nwx")
- assert theProject.openProject(projFile)
+def testIndexCheckThese(nwMinimal, nwDummy):
- theIndex = NWIndex(theProject, theMain)
- nHandle = "0e17daca5f3e1"
- nItem = theProject.projTree[nHandle]
- cHandle = "02d20bbd7e394"
- cItem = theProject.projTree[cHandle]
+ theProject = NWProject(nwDummy)
+ theProject.projTree.setSeed(42)
+ assert theProject.openProject(nwMinimal)
+
+ theIndex = NWIndex(theProject, nwDummy)
+ nHandle = theProject.newFile("Hello", nwItemClass.NOVEL, "a508bb932959c")
+ cHandle = theProject.newFile("Jane", nwItemClass.CHARACTER, "afb3043c7b2b3")
+ nItem = theProject.projTree[nHandle]
+ cItem = theProject.projTree[cHandle]
assert theIndex.scanText(cHandle, (
"# Jane Smith\n"
@@ -153,7 +175,7 @@ def testIndexCheckThese(nwTempProj):
"# Hello World!\n"
"@pov: Jane"
))
- assert str(theIndex.tagIndex) == "{'Jane': [2, '02d20bbd7e394', 'CHARACTER', 'T000001']}"
+ assert str(theIndex.tagIndex) == "{'Jane': [2, '%s', 'CHARACTER', 'T000001']}" % cHandle
assert theIndex.novelIndex[nHandle]["T000001"]["title"] == "Hello World!"
assert str(theIndex.checkThese(["@tag", "Jane"], cItem)) == "[True, True]"
@@ -168,13 +190,15 @@ def testIndexCheckThese(nwTempProj):
assert theProject.closeProject()
@pytest.mark.project
-def testIndexMeta(nwTempProj):
- projFile = path.join(nwTempProj, "nwProject.nwx")
- assert theProject.openProject(projFile)
+def testIndexMeta(nwMinimal, nwDummy):
- theIndex = NWIndex(theProject, theMain)
- nHandle = "0e17daca5f3e1"
- cHandle = "02d20bbd7e394"
+ theProject = NWProject(nwDummy)
+ theProject.projTree.setSeed(42)
+ assert theProject.openProject(nwMinimal)
+
+ theIndex = NWIndex(theProject, nwDummy)
+ nHandle = theProject.newFile("Hello", nwItemClass.NOVEL, "a508bb932959c")
+ cHandle = theProject.newFile("Jane", nwItemClass.CHARACTER, "afb3043c7b2b3")
assert theIndex.scanText(cHandle, (
"# Jane Smith\n"
@@ -191,11 +215,11 @@ def testIndexMeta(nwTempProj):
"\n"
"Well, not really.\n"
))
- assert str(theIndex.tagIndex) == "{'Jane': [2, '02d20bbd7e394', 'CHARACTER', 'T000001']}"
+ assert str(theIndex.tagIndex) == "{'Jane': [2, '%s', 'CHARACTER', 'T000001']}" % cHandle
assert theIndex.novelIndex[nHandle]["T000001"]["title"] == "Hello World!"
# The novel structure should contain the pointer to the novel file header
- assert str(theIndex.getNovelStructure()) == "['0e17daca5f3e1:T000001']"
+ assert str(theIndex.getNovelStructure()) == "['%s:T000001']" % nHandle
# The novel file should have the correct counts
cC, wC, pC = theIndex.getCounts(nHandle)
@@ -210,21 +234,22 @@ def testIndexMeta(nwTempProj):
# The character file should have a record of the reference from the novel file
theRefs = theIndex.getBackReferenceList(cHandle)
- assert str(theRefs) == "{'0e17daca5f3e1': 'T000001'}"
+ assert str(theRefs) == "{'%s': 'T000001'}" % nHandle
assert theProject.closeProject()
-# The two following tests must be at the end as they mess up the config object
-# and the handle seed. They go into their own folders, but use the same project
-# object as the test above.
-
@pytest.mark.project
-def testProjectNewCustom(nwTempCustom, nwRef, nwTemp):
+def testProjectNewCustom(nwFuncTemp, nwTempProj, nwRef, nwDummy):
+
+ projFile = path.join(nwFuncTemp, "nwProject.nwx")
+ testFile = path.join(nwTempProj, "4_nwProject.nwx")
+ refFile = path.join(nwRef, "proj", "4_nwProject.nwx")
+
projData = {
"projName": "Test Custom",
"projTitle": "Test Novel",
"projAuthors": "Jane Doe\nJohn Doh\n",
- "projPath": nwTempCustom,
+ "projPath": nwFuncTemp,
"popSample": False,
"popMinimal": False,
"popCustom": True,
@@ -240,29 +265,33 @@ def testProjectNewCustom(nwTempCustom, nwRef, nwTemp):
"numScenes": 3,
"chFolders": True,
}
- theProject.mainConf = theConf
+ theProject = NWProject(nwDummy)
theProject.projTree.setSeed(42)
+
assert theProject.newProject(projData)
assert theProject.saveProject()
assert theProject.closeProject()
- projFile = path.join(nwTempCustom, "nwProject.nwx")
- refFile = path.join(nwRef, "proj", "4_nwProject.nwx")
- assert cmpFiles(projFile, refFile, [2, 6, 7, 8])
+
+ copyfile(projFile, testFile)
+ assert cmpFiles(testFile, refFile, [2, 6, 7, 8])
@pytest.mark.project
-def testProjectNewSample(nwTempSample, nwLipsum, nwRef, nwTemp):
+def testProjectNewSample(nwFuncTemp, nwRef, nwConf, nwDummy):
projData = {
"projName": "Test Sample",
"projTitle": "Test Novel",
"projAuthors": "Jane Doe\nJohn Doh\n",
- "projPath": nwTempSample,
+ "projPath": nwFuncTemp,
"popSample": True,
"popMinimal": False,
"popCustom": False,
}
- theProject.mainConf = theConf
+ theProject = NWProject(nwDummy)
+ theProject.projTree.setSeed(42)
+ theProject.mainConf = nwConf
+
assert theProject.newProject(projData)
- assert theProject.openProject(nwTempSample)
+ assert theProject.openProject(nwFuncTemp)
assert theProject.projName == "Sample Project"
assert theProject.saveProject()
assert theProject.closeProject()
From b065e4d52b5beb09ddebb5ec808608a77d4c2736 Mon Sep 17 00:00:00 2001
From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com>
Date: Fri, 28 Aug 2020 12:56:03 +0200
Subject: [PATCH 13/16] Decoupled config tests
---
tests/conftest.py | 7 ++
tests/test_config.py | 176 +++++++++++++++++++++----------------------
2 files changed, 94 insertions(+), 89 deletions(-)
diff --git a/tests/conftest.py b/tests/conftest.py
index d4b10213..c3128fc4 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -35,6 +35,13 @@ def nwConf(nwRef, nwTemp):
theConf.initConfig(nwRef, nwTemp)
return theConf
+@pytest.fixture(scope="session")
+def tmpConf(nwRef, nwTemp):
+ theConf = Config()
+ theConf.initConfig(nwTemp, nwTemp)
+ theConf.setLastPath("")
+ return theConf
+
@pytest.fixture(scope="session")
def nwDummy(nwRef, nwTemp, nwConf):
theDummy = DummyMain()
diff --git a/tests/test_config.py b/tests/test_config.py
index cfff5d5d..b58e9697 100644
--- a/tests/test_config.py
+++ b/tests/test_config.py
@@ -5,112 +5,110 @@
import pytest
from nwtools import cmpFiles
from os import path
-from nw.config import Config
-
-theConf = Config()
@pytest.mark.core
-def testConfigInit(nwTemp, nwRef):
- tmpConf = path.join(nwTemp, "novelwriter.conf")
+def testConfigCore(tmpConf, nwTemp, nwRef):
refConf = path.join(nwRef, "novelwriter.conf")
- assert theConf.initConfig(nwTemp, nwTemp)
- assert theConf.setLastPath("")
- assert theConf.saveConfig()
- assert cmpFiles(tmpConf, refConf, [2])
- assert not theConf.confChanged
+ testConf = path.join(tmpConf.confPath, "novelwriter.conf")
+
+ assert tmpConf.confPath == nwTemp
+ assert tmpConf.saveConfig()
+ assert cmpFiles(testConf, refConf, [2])
+ assert not tmpConf.confChanged
+
+ assert tmpConf.loadConfig()
+ assert not tmpConf.confChanged
@pytest.mark.core
-def testConfigSave(nwTemp, nwRef):
- tmpConf = path.join(nwTemp, "novelwriter.conf")
+def testConfigSetConfPath(tmpConf, nwTemp):
+ assert tmpConf.setConfPath(None)
+ assert not tmpConf.setConfPath(path.join("somewhere", "over", "the", "rainbow"))
+ assert tmpConf.setConfPath(path.join(nwTemp, "novelwriter.conf"))
+ assert tmpConf.confPath == nwTemp
+ assert tmpConf.confFile == "novelwriter.conf"
+ assert not tmpConf.confChanged
+
+@pytest.mark.core
+def testConfigSetDataPath(tmpConf, nwTemp):
+ assert tmpConf.setDataPath(None)
+ assert not tmpConf.setDataPath(path.join("somewhere", "over", "the", "rainbow"))
+ assert tmpConf.setDataPath(nwTemp)
+ assert tmpConf.dataPath == nwTemp
+ assert not tmpConf.confChanged
+
+@pytest.mark.core
+def testConfigSetWinSize(tmpConf, nwTemp, nwRef):
refConf = path.join(nwRef, "novelwriter.conf")
- assert theConf.confPath == nwTemp
- assert theConf.saveConfig()
- assert cmpFiles(tmpConf, refConf, [2])
- assert not theConf.confChanged
+ testConf = path.join(tmpConf.confPath, "novelwriter.conf")
+
+ assert tmpConf.confPath == nwTemp
+ assert tmpConf.setWinSize(1105, 655)
+ assert not tmpConf.confChanged
+ assert tmpConf.setWinSize(70, 70)
+ assert tmpConf.confChanged
+ assert tmpConf.setWinSize(1100, 650)
+ assert tmpConf.saveConfig()
+
+ assert cmpFiles(testConf, refConf, [2])
+ assert not tmpConf.confChanged
@pytest.mark.core
-def testConfigSetConfPath(nwTemp):
- assert theConf.setConfPath(None)
- assert not theConf.setConfPath(path.join("somewhere", "over", "the", "rainbow"))
- assert theConf.setConfPath(path.join(nwTemp, "novelwriter.conf"))
- assert theConf.confPath == nwTemp
- assert theConf.confFile == "novelwriter.conf"
- assert not theConf.confChanged
-
-@pytest.mark.core
-def testConfigSetDataPath(nwTemp):
- assert theConf.setDataPath(None)
- assert not theConf.setDataPath(path.join("somewhere", "over", "the", "rainbow"))
- assert theConf.setDataPath(nwTemp)
- assert theConf.dataPath == nwTemp
- assert not theConf.confChanged
-
-@pytest.mark.core
-def testConfigLoad():
- assert theConf.loadConfig()
- assert not theConf.confChanged
-
-@pytest.mark.core
-def testConfigSetWinSize(nwTemp, nwRef):
- tmpConf = path.join(nwTemp, "novelwriter.conf")
+def testConfigSetTreeColWidths(tmpConf, nwTemp, nwRef):
refConf = path.join(nwRef, "novelwriter.conf")
- assert theConf.setWinSize(1105, 655)
- assert not theConf.confChanged
- assert theConf.setWinSize(70, 70)
- assert theConf.confChanged
- assert theConf.setWinSize(1100, 650)
- assert theConf.saveConfig()
- assert cmpFiles(tmpConf, refConf, [2])
- assert not theConf.confChanged
+ testConf = path.join(tmpConf.confPath, "novelwriter.conf")
+
+ assert tmpConf.confPath == nwTemp
+ assert tmpConf.setTreeColWidths([0, 0, 0])
+ assert tmpConf.confChanged
+ assert tmpConf.setTreeColWidths([120, 30, 50])
+ assert tmpConf.setProjColWidths([140, 55, 140])
+ assert tmpConf.saveConfig()
+
+ assert cmpFiles(testConf, refConf, [2])
+ assert not tmpConf.confChanged
@pytest.mark.core
-def testConfigSetTreeColWidths(nwTemp, nwRef):
- tmpConf = path.join(nwTemp, "novelwriter.conf")
+def testConfigSetPanePos(tmpConf, nwTemp, nwRef):
refConf = path.join(nwRef, "novelwriter.conf")
- assert theConf.setTreeColWidths([0, 0, 0])
- assert theConf.confChanged
- assert theConf.setTreeColWidths([120, 30, 50])
- assert theConf.setProjColWidths([140, 55, 140])
- assert theConf.saveConfig()
- assert cmpFiles(tmpConf, refConf, [2])
- assert not theConf.confChanged
+ testConf = path.join(tmpConf.confPath, "novelwriter.conf")
+
+ assert tmpConf.confPath == nwTemp
+ assert tmpConf.setMainPanePos([0, 0])
+ assert tmpConf.confChanged
+ assert tmpConf.setMainPanePos([300, 800])
+ assert tmpConf.setDocPanePos([400, 400])
+ assert tmpConf.setViewPanePos([500, 150])
+ assert tmpConf.setOutlinePanePos([500, 150])
+ assert tmpConf.saveConfig()
+
+ assert cmpFiles(testConf, refConf, [2])
+ assert not tmpConf.confChanged
@pytest.mark.core
-def testConfigSetPanePos(nwTemp, nwRef):
- tmpConf = path.join(nwTemp, "novelwriter.conf")
+def testConfigFlags(tmpConf, nwTemp, nwRef):
refConf = path.join(nwRef, "novelwriter.conf")
- assert theConf.setMainPanePos([0, 0])
- assert theConf.confChanged
- assert theConf.setMainPanePos([300, 800])
- assert theConf.setDocPanePos([400, 400])
- assert theConf.setViewPanePos([500, 150])
- assert theConf.setOutlinePanePos([500, 150])
- assert theConf.saveConfig()
- assert cmpFiles(tmpConf, refConf, [2])
- assert not theConf.confChanged
+ testConf = path.join(tmpConf.confPath, "novelwriter.conf")
+
+ assert tmpConf.confPath == nwTemp
+ assert not tmpConf.setShowRefPanel(False)
+ assert tmpConf.setShowRefPanel(True)
+ assert tmpConf.confChanged
+ assert tmpConf.saveConfig()
+
+ assert cmpFiles(testConf, refConf, [2])
+ assert not tmpConf.confChanged
@pytest.mark.core
-def testConfigFlags(nwTemp, nwRef):
- tmpConf = path.join(nwTemp, "novelwriter.conf")
- refConf = path.join(nwRef, "novelwriter.conf")
- assert not theConf.setShowRefPanel(False)
- assert theConf.setShowRefPanel(True)
- assert theConf.confChanged
- assert theConf.saveConfig()
- assert cmpFiles(tmpConf, refConf, [2])
- assert not theConf.confChanged
-
-@pytest.mark.core
-def testConfigErrors(nwTemp):
+def testConfigErrors(tmpConf):
nonPath = path.join("somewhere", "over", "the", "rainbow")
- assert theConf.initConfig(nonPath, nonPath)
- assert theConf.hasError
- assert not theConf.loadConfig()
- assert not theConf.saveConfig()
- assert not theConf.loadRecentCache()
- assert len(theConf.getErrData()) > 0
+ assert tmpConf.initConfig(nonPath, nonPath)
+ assert tmpConf.hasError
+ assert not tmpConf.loadConfig()
+ assert not tmpConf.saveConfig()
+ assert not tmpConf.loadRecentCache()
+ assert len(tmpConf.getErrData()) > 0
@pytest.mark.core
-def testConfigInternals():
- assert theConf._checkNone(None) is None
- assert theConf._checkNone("None") is None
+def testConfigInternals(tmpConf):
+ assert tmpConf._checkNone(None) is None
+ assert tmpConf._checkNone("None") is None
From 3869d8670c530e5d9effe92ee2304dea2c8e67c9 Mon Sep 17 00:00:00 2001
From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com>
Date: Fri, 28 Aug 2020 13:16:22 +0200
Subject: [PATCH 14/16] Decouple item tests
---
tests/test_item.py | 76 ++++++++++++++++++++++++++++++++--------------
1 file changed, 54 insertions(+), 22 deletions(-)
diff --git a/tests/test_item.py b/tests/test_item.py
index ca78d28e..54cb02d4 100644
--- a/tests/test_item.py
+++ b/tests/test_item.py
@@ -11,16 +11,19 @@ from nw.config import Config
from nw.core.project import NWProject, NWItem
from nw.constants import nwItemClass, nwItemType, nwItemLayout
-theConf = Config()
-theMain = DummyMain()
-theMain.mainConf = theConf
+# theConf = Config()
+# theMain = DummyMain()
+# theMain.mainConf = theConf
-theProject = NWProject(theMain)
-theItem = NWItem(theProject)
-nwXML = etree.Element("novelWriterXML")
+# theProject = NWProject(theMain)
+# theItem = NWItem(theProject)
+# nwXML = etree.Element("novelWriterXML")
@pytest.mark.project
-def testItemSettersSimple():
+def testItemSettersSimple(nwDummy):
+
+ theProject = NWProject(nwDummy)
+ theItem = NWItem(theProject)
# Name
theItem.setName("A Name")
@@ -113,7 +116,10 @@ def testItemSettersSimple():
assert theItem.cursorPos == 1
@pytest.mark.project
-def testItemClassSetter():
+def testItemClassSetter(nwDummy):
+
+ theProject = NWProject(nwDummy)
+ theItem = NWItem(theProject)
# Class
theItem.setClass(None)
@@ -138,13 +144,18 @@ def testItemClassSetter():
assert theItem.itemClass == nwItemClass.ENTITY
theItem.setClass("CUSTOM")
assert theItem.itemClass == nwItemClass.CUSTOM
+ theItem.setClass("ARCHIVE")
+ assert theItem.itemClass == nwItemClass.ARCHIVE
theItem.setClass("TRASH")
assert theItem.itemClass == nwItemClass.TRASH
@pytest.mark.project
-def testItemTypeSetter():
+def testItemTypeSetter(nwDummy):
- # Class
+ theProject = NWProject(nwDummy)
+ theItem = NWItem(theProject)
+
+ # Type
theItem.setType(None)
assert theItem.itemType == nwItemType.NO_TYPE
theItem.setType("NONSENSE")
@@ -161,9 +172,12 @@ def testItemTypeSetter():
assert theItem.itemType == nwItemType.TRASH
@pytest.mark.project
-def testItemLayoutSetter():
+def testItemLayoutSetter(nwDummy):
- # Class
+ theProject = NWProject(nwDummy)
+ theItem = NWItem(theProject)
+
+ # Layout
theItem.setLayout(None)
assert theItem.itemLayout == nwItemLayout.NO_LAYOUT
theItem.setLayout("NONSENSE")
@@ -188,7 +202,25 @@ def testItemLayoutSetter():
assert theItem.itemLayout == nwItemLayout.NOTE
@pytest.mark.project
-def testItemXMLPackUnpack():
+def testItemXMLPackUnpack(nwDummy):
+
+ theProject = NWProject(nwDummy)
+ theItem = NWItem(theProject)
+ nwXML = etree.Element("novelWriterXML")
+
+ theItem.setHandle("0123456789abc")
+ theItem.setParent("0123456789abc")
+ theItem.setOrder(1)
+ theItem.setName("A Name")
+ theItem.setClass("NOVEL")
+ theItem.setType("FILE")
+ theItem.setStatus("Main")
+ theItem.setLayout("NOTE")
+ theItem.setExpanded(True)
+ theItem.setParaCount(3)
+ theItem.setWordCount(5)
+ theItem.setCharCount(7)
+ theItem.setCursorPos(11)
# Pack
xContent = etree.SubElement(nwXML, "content")
@@ -196,9 +228,9 @@ def testItemXMLPackUnpack():
assert etree.tostring(xContent, pretty_print=False, encoding="utf-8") == (
b""
b"
- "
- b"A NameTRASHTRASH"
- b"MainTrue"
- b"
"
+ b"A NameFILENOVELNew"
+ b"TrueNOTE7"
+ b"5311 "
b" "
)
@@ -208,10 +240,10 @@ def testItemXMLPackUnpack():
assert theItem.parHandle == "0123456789abc"
assert theItem.itemOrder == 1
assert theItem.isExpanded
- assert theItem.charCount == 1
- assert theItem.wordCount == 1
- assert theItem.paraCount == 1
- assert theItem.cursorPos == 1
- assert theItem.itemClass == nwItemClass.TRASH
- assert theItem.itemType == nwItemType.TRASH
+ assert theItem.paraCount == 3
+ assert theItem.wordCount == 5
+ assert theItem.charCount == 7
+ assert theItem.cursorPos == 11
+ assert theItem.itemClass == nwItemClass.NOVEL
+ assert theItem.itemType == nwItemType.FILE
assert theItem.itemLayout == nwItemLayout.NOTE
From 64cd356c341e2b452043653dd114bd002c925882 Mon Sep 17 00:00:00 2001
From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com>
Date: Fri, 28 Aug 2020 16:10:47 +0200
Subject: [PATCH 15/16] Decoupled gui tests
---
tests/conftest.py | 12 +-
tests/lipsum/ToC.json | 2 +-
tests/lipsum/ToC.txt | 2 +-
tests/lipsum/nwProject.nwx | 14 +-
tests/test_gui.py | 364 ++++++++++++++++++++++++-------------
5 files changed, 258 insertions(+), 136 deletions(-)
diff --git a/tests/conftest.py b/tests/conftest.py
index c3128fc4..021c888b 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -100,12 +100,11 @@ def nwMinimal(nwTemp):
shutil.rmtree(minimalDir)
return
-@pytest.fixture(scope="session")
-def nwLipsum():
+@pytest.fixture(scope="function")
+def nwLipsum(nwTemp):
testDir = path.dirname(__file__)
- tempDir = path.join(testDir, "temp")
lipsumStore = path.join(testDir, "lipsum")
- lipsumDir = path.join(tempDir, "lipsum")
+ lipsumDir = path.join(nwTemp, "lipsum")
if path.isdir(lipsumDir):
shutil.rmtree(lipsumDir)
shutil.copytree(lipsumStore, lipsumDir)
@@ -115,4 +114,7 @@ def nwLipsum():
metaDir = path.join(lipsumDir, "meta")
if path.isdir(metaDir):
shutil.rmtree(metaDir)
- return lipsumDir
+ yield lipsumDir
+ if path.isdir(lipsumDir):
+ shutil.rmtree(lipsumDir)
+ return
diff --git a/tests/lipsum/ToC.json b/tests/lipsum/ToC.json
index 7540d6c9..4d22a21c 100644
--- a/tests/lipsum/ToC.json
+++ b/tests/lipsum/ToC.json
@@ -27,7 +27,7 @@
[
"content/7a992350f3eb6.nwd",
"NOVEL",
- "Lorem Ipusm"
+ "Lorem Ipsum"
],
[
"content/846352075de7d.nwd",
diff --git a/tests/lipsum/ToC.txt b/tests/lipsum/ToC.txt
index 86098bcd..a499c6f8 100644
--- a/tests/lipsum/ToC.txt
+++ b/tests/lipsum/ToC.txt
@@ -9,7 +9,7 @@
content/441420a886d82.nwd NOVEL Chapter Two
content/47666c91c7ccf.nwd NOVEL Scene Five
content/4c4f28287af27.nwd CHARACTER Mr. Nobody
- content/7a992350f3eb6.nwd NOVEL Lorem Ipusm
+ content/7a992350f3eb6.nwd NOVEL Lorem Ipsum
content/846352075de7d.nwd NOVEL Interlude
content/88243afbe5ed8.nwd NOVEL Scene One
content/88d59a277361b.nwd NOVEL Prologue
diff --git a/tests/lipsum/nwProject.nwx b/tests/lipsum/nwProject.nwx
index d6a58a7a..be2463a4 100644
--- a/tests/lipsum/nwProject.nwx
+++ b/tests/lipsum/nwProject.nwx
@@ -1,23 +1,25 @@
-
+
Lorem Ipsum
Lorem Ipsum
lipsum.com
- 7
+ 8
21
- 1459
+ 1472
False
False
True
- 04468803b92e1
+ fb609cd8319dc
None
3847
+ 3109
+ 738
- Replace Text 1
- Replace Text 2
+ Replace Text 1
+ Replace Text 2
%title%
diff --git a/tests/test_gui.py b/tests/test_gui.py
index 22f82c61..5e02b21c 100644
--- a/tests/test_gui.py
+++ b/tests/test_gui.py
@@ -5,6 +5,7 @@
import nw
import pytest
import json
+from shutil import copyfile
from nwtools import cmpFiles
from os import path
@@ -24,8 +25,9 @@ keyDelay = 2
stepDelay = 20
@pytest.mark.gui
-def testMainWindows(qtbot, nwTempGUI, nwRef, nwTemp):
- nwGUI = nw.main(["--testmode", "--config=%s" % nwTempGUI, "--data=%s" % nwTemp])
+def testMainWindows(qtbot, nwFuncTemp, nwTempGUI, nwRef, nwTemp):
+
+ nwGUI = nw.main(["--testmode", "--config=%s" % nwFuncTemp, "--data=%s" % nwTemp])
qtbot.addWidget(nwGUI)
nwGUI.show()
qtbot.waitForWindowShown(nwGUI)
@@ -33,7 +35,7 @@ def testMainWindows(qtbot, nwTempGUI, nwRef, nwTemp):
# Create new, save, close project
nwGUI.theProject.projTree.setSeed(42)
- assert nwGUI.newProject({"projPath": nwTempGUI}, True)
+ assert nwGUI.newProject({"projPath": nwFuncTemp}, True)
assert nwGUI.saveProject()
assert nwGUI.closeProject()
@@ -50,14 +52,17 @@ def testMainWindows(qtbot, nwTempGUI, nwRef, nwTemp):
assert not nwGUI.theProject.spellCheck
# Check the files
- projFile = path.join(nwTempGUI, "nwProject.nwx")
- assert cmpFiles(projFile, path.join(nwRef, "gui", "0_nwProject.nwx"), [2, 6, 7, 8])
+ projFile = path.join(nwFuncTemp, "nwProject.nwx")
+ testFile = path.join(nwTempGUI, "0_nwProject.nwx")
+ refFile = path.join(nwRef, "gui", "0_nwProject.nwx")
+ copyfile(projFile, testFile)
+ assert cmpFiles(testFile, refFile, [2, 6, 7, 8])
qtbot.wait(stepDelay)
# qtbot.stopForInteraction()
# Re-open project
- assert nwGUI.openProject(nwTempGUI)
+ assert nwGUI.openProject(nwFuncTemp)
qtbot.wait(stepDelay)
# Check that we loaded the data
@@ -65,8 +70,8 @@ def testMainWindows(qtbot, nwTempGUI, nwRef, nwTemp):
assert len(nwGUI.theProject.projTree._treeOrder) == 8
assert len(nwGUI.theProject.projTree._treeRoots) == 4
assert nwGUI.theProject.projTree.trashRoot() is None
- assert nwGUI.theProject.projPath == nwTempGUI
- assert nwGUI.theProject.projMeta == path.join(nwTempGUI, "meta")
+ assert nwGUI.theProject.projPath == nwFuncTemp
+ assert nwGUI.theProject.projMeta == path.join(nwFuncTemp, "meta")
assert nwGUI.theProject.projFile == "nwProject.nwx"
assert nwGUI.theProject.projName == "New Project"
assert nwGUI.theProject.bookTitle == ""
@@ -266,23 +271,42 @@ def testMainWindows(qtbot, nwTempGUI, nwRef, nwTemp):
assert nwGUI.saveProject()
# Check the files
- refFile = path.join(nwTempGUI, "nwProject.nwx")
- assert cmpFiles(refFile, path.join(nwRef, "gui", "1_nwProject.nwx"), [2, 6, 7, 8])
- refFile = path.join(nwTempGUI, "content", "031b4af5197ec.nwd")
- assert cmpFiles(refFile, path.join(nwRef, "gui", "1_031b4af5197ec.nwd"))
- refFile = path.join(nwTempGUI, "content", "1a6562590ef19.nwd")
- assert cmpFiles(refFile, path.join(nwRef, "gui", "1_1a6562590ef19.nwd"))
- refFile = path.join(nwTempGUI, "content", "0e17daca5f3e1.nwd")
- assert cmpFiles(refFile, path.join(nwRef, "gui", "1_0e17daca5f3e1.nwd"))
- refFile = path.join(nwTempGUI, "content", "41cfc0d1f2d12.nwd")
- assert cmpFiles(refFile, path.join(nwRef, "gui", "1_41cfc0d1f2d12.nwd"))
+ projFile = path.join(nwFuncTemp, "nwProject.nwx")
+ testFile = path.join(nwTempGUI, "1_nwProject.nwx")
+ refFile = path.join(nwRef, "gui", "1_nwProject.nwx")
+ copyfile(projFile, testFile)
+ assert cmpFiles(testFile, refFile, [2, 6, 7, 8])
+
+ projFile = path.join(nwFuncTemp, "content", "031b4af5197ec.nwd")
+ testFile = path.join(nwTempGUI, "1_031b4af5197ec.nwd")
+ refFile = path.join(nwRef, "gui", "1_031b4af5197ec.nwd")
+ copyfile(projFile, testFile)
+ assert cmpFiles(testFile, refFile)
+
+ projFile = path.join(nwFuncTemp, "content", "1a6562590ef19.nwd")
+ testFile = path.join(nwTempGUI, "1_1a6562590ef19.nwd")
+ refFile = path.join(nwRef, "gui", "1_1a6562590ef19.nwd")
+ copyfile(projFile, testFile)
+ assert cmpFiles(testFile, refFile)
+
+ projFile = path.join(nwFuncTemp, "content", "0e17daca5f3e1.nwd")
+ testFile = path.join(nwTempGUI, "1_0e17daca5f3e1.nwd")
+ refFile = path.join(nwRef, "gui", "1_0e17daca5f3e1.nwd")
+ copyfile(projFile, testFile)
+ assert cmpFiles(testFile, refFile)
+
+ projFile = path.join(nwFuncTemp, "content", "41cfc0d1f2d12.nwd")
+ testFile = path.join(nwTempGUI, "1_41cfc0d1f2d12.nwd")
+ refFile = path.join(nwRef, "gui", "1_41cfc0d1f2d12.nwd")
+ copyfile(projFile, testFile)
+ assert cmpFiles(testFile, refFile)
nwGUI.closeMain()
# qtbot.stopForInteraction()
@pytest.mark.gui
-def testProjectEditor(qtbot, nwTempGUI, nwRef, nwTemp):
- nwGUI = nw.main(["--testmode", "--config=%s" % nwTempGUI, "--data=%s" % nwTemp])
+def testProjectEditor(qtbot, nwFuncTemp, nwTempGUI, nwRef, nwTemp):
+ nwGUI = nw.main(["--testmode", "--config=%s" % nwFuncTemp, "--data=%s" % nwTemp])
qtbot.addWidget(nwGUI)
nwGUI.show()
qtbot.waitForWindowShown(nwGUI)
@@ -290,8 +314,8 @@ def testProjectEditor(qtbot, nwTempGUI, nwRef, nwTemp):
# Create new, save, open project
nwGUI.theProject.projTree.setSeed(42)
- assert nwGUI.newProject({"projPath": nwTempGUI}, True)
- nwGUI.mainConf.backupPath = nwTempGUI
+ assert nwGUI.newProject({"projPath": nwFuncTemp}, True)
+ nwGUI.mainConf.backupPath = nwFuncTemp
projEdit = GuiProjectSettings(nwGUI, nwGUI.theProject)
projEdit.show()
@@ -369,15 +393,18 @@ def testProjectEditor(qtbot, nwTempGUI, nwRef, nwTemp):
qtbot.wait(stepDelay)
# Check the files
- projFile = path.join(nwTempGUI, "nwProject.nwx")
- assert cmpFiles(projFile, path.join(nwRef, "gui", "2_nwProject.nwx"), [2, 8, 9, 10])
+ projFile = path.join(nwFuncTemp, "nwProject.nwx")
+ testFile = path.join(nwTempGUI, "2_nwProject.nwx")
+ refFile = path.join(nwRef, "gui", "2_nwProject.nwx")
+ copyfile(projFile, testFile)
+ assert cmpFiles(testFile, refFile, [2, 6, 7, 8])
# qtbot.stopForInteraction()
nwGUI.closeMain()
@pytest.mark.gui
-def testItemEditor(qtbot, nwTempGUI, nwRef, nwTemp):
- nwGUI = nw.main(["--testmode", "--config=%s" % nwTempGUI, "--data=%s" % nwTemp])
+def testItemEditor(qtbot, nwFuncTemp, nwTempGUI, nwRef, nwTemp):
+ nwGUI = nw.main(["--testmode", "--config=%s" % nwFuncTemp, "--data=%s" % nwTemp])
qtbot.addWidget(nwGUI)
nwGUI.show()
qtbot.waitForWindowShown(nwGUI)
@@ -385,7 +412,7 @@ def testItemEditor(qtbot, nwTempGUI, nwRef, nwTemp):
# Create new, save, open project
nwGUI.theProject.projTree.setSeed(42)
- assert nwGUI.newProject({"projPath": nwTempGUI}, True)
+ assert nwGUI.newProject({"projPath": nwFuncTemp}, True)
assert nwGUI.openDocument("0e17daca5f3e1")
itemEdit = GuiItemEditor(nwGUI, nwGUI.theProject, "0e17daca5f3e1")
@@ -425,24 +452,70 @@ def testItemEditor(qtbot, nwTempGUI, nwRef, nwTemp):
qtbot.wait(stepDelay)
# Check the files
- projFile = path.join(nwTempGUI, "nwProject.nwx")
- assert cmpFiles(projFile, path.join(nwRef, "gui", "3_nwProject.nwx"), [2, 6, 7, 8])
+ projFile = path.join(nwFuncTemp, "nwProject.nwx")
+ testFile = path.join(nwTempGUI, "3_nwProject.nwx")
+ refFile = path.join(nwRef, "gui", "3_nwProject.nwx")
+ copyfile(projFile, testFile)
+ assert cmpFiles(testFile, refFile, [2, 6, 7, 8])
nwGUI.closeMain()
# qtbot.stopForInteraction()
@pytest.mark.gui
-def testWritingStatsExport(qtbot, nwTempGUI, nwRef, nwTemp):
- nwGUI = nw.main(["--testmode", "--config=%s" % nwTempGUI, "--data=%s" % nwTemp])
+def testWritingStatsExport(qtbot, nwFuncTemp, nwTemp):
+ nwGUI = nw.main(["--testmode", "--config=%s" % nwFuncTemp, "--data=%s" % nwTemp])
qtbot.addWidget(nwGUI)
nwGUI.show()
qtbot.waitForWindowShown(nwGUI)
qtbot.wait(stepDelay)
- assert nwGUI.openProject(nwTempGUI)
+ # Create new, save, close project
+ nwGUI.theProject.projTree.setSeed(42)
+ assert nwGUI.newProject({"projPath": nwFuncTemp}, True)
+ assert nwGUI.saveProject()
+ assert nwGUI.closeProject()
qtbot.wait(stepDelay)
- nwGUI.mainConf.lastPath = nwTempGUI
+ assert nwGUI.openProject(nwFuncTemp)
+ qtbot.wait(stepDelay)
+
+ # Add some text to the scene file
+ assert nwGUI.openDocument("0e17daca5f3e1")
+ assert nwGUI.docEditor.insertText(
+ "# Scene One\n\n"
+ "It was the best of times, it was the worst of times, it was the age of wisdom, it was "
+ "the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it "
+ "was the season of Light, it was the season of Darkness, it was the spring of hope, it "
+ "was the winter of despair, we had everything before us, we had nothing before us, we "
+ "were all going direct to Heaven, we were all going direct the other way – in short, the "
+ "period was so far like the present period, that some of its noisiest authorities "
+ "insisted on its being received, for good or for evil, in the superlative degree of "
+ "comparison only.\n\n"
+ )
+ assert nwGUI.saveDocument()
+
+ # Add a note file with some text
+ nwGUI.setFocus(1)
+ nwGUI.treeView.clearSelection()
+ nwGUI.treeView._getTreeItem("71ee45a3c0db9").setSelected(True)
+ nwGUI.treeView.newTreeItem(nwItemType.FILE, None)
+ assert nwGUI.openSelectedItem()
+ assert nwGUI.docEditor.insertText(
+ "# Jane Doe\n\n"
+ "All about Jane.\n\n"
+ )
+ assert nwGUI.saveDocument()
+ qtbot.wait(500) # Ensures that the session length is > 0
+
+ assert nwGUI.saveProject()
+ assert nwGUI.closeProject()
+ qtbot.wait(stepDelay)
+
+ # Open again, and check the stats
+ assert nwGUI.openProject(nwFuncTemp)
+ qtbot.wait(stepDelay)
+
+ nwGUI.mainConf.lastPath = nwFuncTemp
sessLog = GuiWritingStats(nwGUI, nwGUI.theProject)
sessLog.show()
qtbot.wait(stepDelay)
@@ -452,15 +525,15 @@ def testWritingStatsExport(qtbot, nwTempGUI, nwRef, nwTemp):
assert sessLog._saveData(sessLog.FMT_JSON)
qtbot.wait(stepDelay)
- jsonStats = path.join(nwTempGUI, "sessionStats.json")
+ jsonStats = path.join(nwFuncTemp, "sessionStats.json")
with open(jsonStats, mode="r", encoding="utf-8") as inFile:
jsonData = json.loads(inFile.read())
- assert len(jsonData) == 3
- assert jsonData[1]["length"] > 0
- assert jsonData[1]["newWords"] == 84
- assert jsonData[1]["novelWords"] == 63
- assert jsonData[1]["noteWords"] == 27
+ assert len(jsonData) == 2
+ assert jsonData[1]["length"] >= 0
+ assert jsonData[1]["newWords"] == 126
+ assert jsonData[1]["novelWords"] == 127
+ assert jsonData[1]["noteWords"] == 5
# No Novel Files
qtbot.mouseClick(sessLog.incNovel, Qt.LeftButton)
@@ -468,15 +541,15 @@ def testWritingStatsExport(qtbot, nwTempGUI, nwRef, nwTemp):
assert sessLog._saveData(sessLog.FMT_JSON)
qtbot.wait(stepDelay)
- jsonStats = path.join(nwTempGUI, "sessionStats.json")
+ jsonStats = path.join(nwFuncTemp, "sessionStats.json")
with open(jsonStats, mode="r", encoding="utf-8") as inFile:
jsonData = json.loads(inFile.read())
- assert len(jsonData) == 2
- assert jsonData[0]["length"] > 0
- assert jsonData[0]["newWords"] == 27
- assert jsonData[0]["novelWords"] == 63
- assert jsonData[0]["noteWords"] == 27
+ assert len(jsonData) == 1
+ assert jsonData[0]["length"] >= 0
+ assert jsonData[0]["newWords"] == 5
+ assert jsonData[0]["novelWords"] == 127
+ assert jsonData[0]["noteWords"] == 5
# No Note Files
qtbot.mouseClick(sessLog.incNovel, Qt.LeftButton)
@@ -485,15 +558,15 @@ def testWritingStatsExport(qtbot, nwTempGUI, nwRef, nwTemp):
assert sessLog._saveData(sessLog.FMT_JSON)
qtbot.wait(stepDelay)
- jsonStats = path.join(nwTempGUI, "sessionStats.json")
+ jsonStats = path.join(nwFuncTemp, "sessionStats.json")
with open(jsonStats, mode="r", encoding="utf-8") as inFile:
jsonData = json.loads(inFile.read())
- assert len(jsonData) == 3
- assert jsonData[1]["length"] > 0
- assert jsonData[1]["newWords"] == 57
- assert jsonData[1]["novelWords"] == 63
- assert jsonData[1]["noteWords"] == 27
+ assert len(jsonData) == 2
+ assert jsonData[1]["length"] >= 0
+ assert jsonData[1]["newWords"] == 121
+ assert jsonData[1]["novelWords"] == 127
+ assert jsonData[1]["noteWords"] == 5
# No Negative Entries
qtbot.mouseClick(sessLog.incNotes, Qt.LeftButton)
@@ -502,7 +575,7 @@ def testWritingStatsExport(qtbot, nwTempGUI, nwRef, nwTemp):
assert sessLog._saveData(sessLog.FMT_JSON)
qtbot.wait(stepDelay)
- jsonStats = path.join(nwTempGUI, "sessionStats.json")
+ jsonStats = path.join(nwFuncTemp, "sessionStats.json")
with open(jsonStats, mode="r", encoding="utf-8") as inFile:
jsonData = json.loads(inFile.read())
@@ -515,11 +588,11 @@ def testWritingStatsExport(qtbot, nwTempGUI, nwRef, nwTemp):
assert sessLog._saveData(sessLog.FMT_JSON)
qtbot.wait(stepDelay)
- jsonStats = path.join(nwTempGUI, "sessionStats.json")
+ jsonStats = path.join(nwFuncTemp, "sessionStats.json")
with open(jsonStats, mode="r", encoding="utf-8") as inFile:
jsonData = json.loads(inFile.read())
- assert len(jsonData) == 4
+ assert len(jsonData) == 2
# Group by Day
qtbot.mouseClick(sessLog.groupByDay, Qt.LeftButton)
@@ -527,7 +600,7 @@ def testWritingStatsExport(qtbot, nwTempGUI, nwRef, nwTemp):
assert sessLog._saveData(sessLog.FMT_JSON)
qtbot.wait(stepDelay)
- jsonStats = path.join(nwTempGUI, "sessionStats.json")
+ jsonStats = path.join(nwFuncTemp, "sessionStats.json")
with open(jsonStats, mode="r", encoding="utf-8") as inFile:
jsonData = json.loads(inFile.read())
@@ -541,8 +614,8 @@ def testWritingStatsExport(qtbot, nwTempGUI, nwRef, nwTemp):
nwGUI.closeMain()
@pytest.mark.gui
-def testAboutBox(qtbot, nwTempGUI, nwRef, nwTemp):
- nwGUI = nw.main(["--testmode", "--config=%s" % nwTempGUI, "--data=%s" % nwTemp])
+def testAboutBox(qtbot, nwFuncTemp, nwTemp):
+ nwGUI = nw.main(["--testmode", "--config=%s" % nwFuncTemp, "--data=%s" % nwTemp])
qtbot.addWidget(nwGUI)
nwGUI.show()
qtbot.waitForWindowShown(nwGUI)
@@ -553,14 +626,13 @@ def testAboutBox(qtbot, nwTempGUI, nwRef, nwTemp):
assert msgAbout.pageLicense.document().characterCount() > 100
# qtbot.stopForInteraction()
-
msgAbout._doClose()
nwGUI.closeMain()
@pytest.mark.gui
def testBuildTool(qtbot, nwTempBuild, nwLipsum, nwRef, nwTemp):
- nwGUI = nw.main(["--testmode", "--config=%s" % nwTempBuild, "--data=%s" % nwTemp])
+ nwGUI = nw.main(["--testmode", "--config=%s" % nwLipsum, "--data=%s" % nwTemp])
qtbot.addWidget(nwGUI)
nwGUI.show()
qtbot.waitForWindowShown(nwGUI)
@@ -568,7 +640,7 @@ def testBuildTool(qtbot, nwTempBuild, nwLipsum, nwRef, nwTemp):
assert nwGUI.openProject(nwLipsum)
- nwGUI.mainConf.lastPath = nwTempBuild
+ nwGUI.mainConf.lastPath = nwLipsum
nwBuild = GuiBuildNovel(nwGUI, nwGUI.theProject)
@@ -578,10 +650,17 @@ def testBuildTool(qtbot, nwTempBuild, nwLipsum, nwRef, nwTemp):
assert nwBuild._saveDocument(nwBuild.FMT_NWD)
assert nwBuild._saveDocument(nwBuild.FMT_HTM)
- refFile = path.join(nwTempBuild, "Lorem Ipsum.nwd")
- assert cmpFiles(refFile, path.join(nwRef, "build", "1_LoremIpsum.nwd"), [])
- refFile = path.join(nwTempBuild, "Lorem Ipsum.htm")
- assert cmpFiles(refFile, path.join(nwRef, "build", "1_LoremIpsum.htm"), [])
+ projFile = path.join(nwLipsum, "Lorem Ipsum.nwd")
+ testFile = path.join(nwTempBuild, "1_LoremIpsum.nwd")
+ refFile = path.join(nwRef, "build", "1_LoremIpsum.nwd")
+ copyfile(projFile, testFile)
+ assert cmpFiles(testFile, refFile)
+
+ projFile = path.join(nwLipsum, "Lorem Ipsum.htm")
+ testFile = path.join(nwTempBuild, "1_LoremIpsum.htm")
+ refFile = path.join(nwRef, "build", "1_LoremIpsum.htm")
+ copyfile(projFile, testFile)
+ assert cmpFiles(testFile, refFile)
# Change Title Formats and Flip Switches
nwBuild.fmtChapter.setText(r"Chapter %chw%: %title%")
@@ -610,10 +689,17 @@ def testBuildTool(qtbot, nwTempBuild, nwLipsum, nwRef, nwTemp):
assert nwBuild._saveDocument(nwBuild.FMT_NWD)
assert nwBuild._saveDocument(nwBuild.FMT_HTM)
- refFile = path.join(nwTempBuild, "Lorem Ipsum.nwd")
- assert cmpFiles(refFile, path.join(nwRef, "build", "2_LoremIpsum.nwd"), [])
- refFile = path.join(nwTempBuild, "Lorem Ipsum.htm")
- assert cmpFiles(refFile, path.join(nwRef, "build", "2_LoremIpsum.htm"), [])
+ projFile = path.join(nwLipsum, "Lorem Ipsum.nwd")
+ testFile = path.join(nwTempBuild, "2_LoremIpsum.nwd")
+ refFile = path.join(nwRef, "build", "2_LoremIpsum.nwd")
+ copyfile(projFile, testFile)
+ assert cmpFiles(testFile, refFile)
+
+ projFile = path.join(nwLipsum, "Lorem Ipsum.htm")
+ testFile = path.join(nwTempBuild, "2_LoremIpsum.htm")
+ refFile = path.join(nwRef, "build", "2_LoremIpsum.htm")
+ copyfile(projFile, testFile)
+ assert cmpFiles(testFile, refFile)
# Putline Mode
nwBuild.fmtChapter.setText(r"Chapter %chw%: %title%")
@@ -637,19 +723,26 @@ def testBuildTool(qtbot, nwTempBuild, nwLipsum, nwRef, nwTemp):
assert nwBuild._saveDocument(nwBuild.FMT_NWD)
assert nwBuild._saveDocument(nwBuild.FMT_HTM)
- refFile = path.join(nwTempBuild, "Lorem Ipsum.nwd")
- assert cmpFiles(refFile, path.join(nwRef, "build", "3_LoremIpsum.nwd"), [])
- refFile = path.join(nwTempBuild, "Lorem Ipsum.htm")
- assert cmpFiles(refFile, path.join(nwRef, "build", "3_LoremIpsum.htm"), [])
+ projFile = path.join(nwLipsum, "Lorem Ipsum.nwd")
+ testFile = path.join(nwTempBuild, "3_LoremIpsum.nwd")
+ refFile = path.join(nwRef, "build", "3_LoremIpsum.nwd")
+ copyfile(projFile, testFile)
+ assert cmpFiles(testFile, refFile)
+
+ projFile = path.join(nwLipsum, "Lorem Ipsum.htm")
+ testFile = path.join(nwTempBuild, "3_LoremIpsum.htm")
+ refFile = path.join(nwRef, "build", "3_LoremIpsum.htm")
+ copyfile(projFile, testFile)
+ assert cmpFiles(testFile, refFile)
# qtbot.stopForInteraction()
nwBuild._doClose()
nwGUI.closeMain()
@pytest.mark.gui
-def testMergeTool(qtbot, nwTempGUI, nwLipsum, nwRef, nwTemp):
+def testMergeSplitTools(qtbot, nwTempGUI, nwLipsum, nwRef, nwTemp):
- nwGUI = nw.main(["--testmode", "--config=%s" % nwTempGUI, "--data=%s" % nwTemp])
+ nwGUI = nw.main(["--testmode", "--config=%s" % nwLipsum, "--data=%s" % nwTemp])
qtbot.addWidget(nwGUI)
nwGUI.show()
qtbot.waitForWindowShown(nwGUI)
@@ -670,24 +763,11 @@ def testMergeTool(qtbot, nwTempGUI, nwLipsum, nwRef, nwTemp):
assert nwGUI.theProject.projTree["73475cb40a568"] is not None
- refFile = path.join(nwLipsum, "content", "73475cb40a568.nwd")
- assert cmpFiles(refFile, path.join(nwRef, "gui", "4_73475cb40a568.nwd"))
-
- # qtbot.stopForInteraction()
- nwGUI.closeMain()
-
-@pytest.mark.gui
-def testSplitTool(qtbot, nwTempGUI, nwLipsum, nwRef, nwTemp):
-
- nwGUI = nw.main(["--testmode", "--config=%s" % nwTempGUI, "--data=%s" % nwTemp])
- qtbot.addWidget(nwGUI)
- nwGUI.show()
- qtbot.waitForWindowShown(nwGUI)
- qtbot.wait(stepDelay)
-
- nwGUI.theProject.projTree.setSeed(42)
- assert nwGUI.openProject(nwLipsum)
- qtbot.wait(stepDelay)
+ projFile = path.join(nwLipsum, "content", "73475cb40a568.nwd")
+ testFile = path.join(nwTempGUI, "4_73475cb40a568.nwd")
+ refFile = path.join(nwRef, "gui", "4_73475cb40a568.nwd")
+ copyfile(projFile, testFile)
+ assert cmpFiles(testFile, refFile)
# Split By Chapter
assert nwGUI.treeView.setSelectedHandle("73475cb40a568")
@@ -701,8 +781,11 @@ def testSplitTool(qtbot, nwTempGUI, nwLipsum, nwRef, nwTemp):
assert nwGUI.theProject.projTree["71ee45a3c0db9"] is not None
# This should give us back the file as it was before
- refFile = path.join(nwLipsum, "content", "71ee45a3c0db9.nwd")
- assert cmpFiles(refFile, path.join(nwRef, "gui", "4_73475cb40a568.nwd"), [1])
+ projFile = path.join(nwLipsum, "content", "71ee45a3c0db9.nwd")
+ testFile = path.join(nwTempGUI, "4_71ee45a3c0db9.nwd")
+ refFile = path.join(nwRef, "gui", "4_73475cb40a568.nwd")
+ copyfile(projFile, testFile)
+ assert cmpFiles(testFile, refFile, [1])
# Split By Scene
assert nwGUI.treeView.setSelectedHandle("73475cb40a568")
@@ -718,12 +801,23 @@ def testSplitTool(qtbot, nwTempGUI, nwLipsum, nwRef, nwTemp):
assert nwGUI.theProject.projTree["31489056e0916"] is not None
assert nwGUI.theProject.projTree["98010bd9270f9"] is not None
- refFile = path.join(nwLipsum, "content", "25fc0e7096fc6.nwd")
- assert cmpFiles(refFile, path.join(nwRef, "gui", "5_25fc0e7096fc6.nwd"))
- refFile = path.join(nwLipsum, "content", "31489056e0916.nwd")
- assert cmpFiles(refFile, path.join(nwRef, "gui", "5_31489056e0916.nwd"))
- refFile = path.join(nwLipsum, "content", "98010bd9270f9.nwd")
- assert cmpFiles(refFile, path.join(nwRef, "gui", "5_98010bd9270f9.nwd"))
+ projFile = path.join(nwLipsum, "content", "25fc0e7096fc6.nwd")
+ testFile = path.join(nwTempGUI, "5_25fc0e7096fc6.nwd")
+ refFile = path.join(nwRef, "gui", "5_25fc0e7096fc6.nwd")
+ copyfile(projFile, testFile)
+ assert cmpFiles(testFile, refFile)
+
+ projFile = path.join(nwLipsum, "content", "31489056e0916.nwd")
+ testFile = path.join(nwTempGUI, "5_31489056e0916.nwd")
+ refFile = path.join(nwRef, "gui", "5_31489056e0916.nwd")
+ copyfile(projFile, testFile)
+ assert cmpFiles(testFile, refFile)
+
+ projFile = path.join(nwLipsum, "content", "98010bd9270f9.nwd")
+ testFile = path.join(nwTempGUI, "5_98010bd9270f9.nwd")
+ refFile = path.join(nwRef, "gui", "5_98010bd9270f9.nwd")
+ copyfile(projFile, testFile)
+ assert cmpFiles(testFile, refFile)
# Split By Section
assert nwGUI.treeView.setSelectedHandle("73475cb40a568")
@@ -741,22 +835,41 @@ def testSplitTool(qtbot, nwTempGUI, nwLipsum, nwRef, nwTemp):
assert nwGUI.theProject.projTree["2858dcd1057d3"] is not None
assert nwGUI.theProject.projTree["2fca346db6561"] is not None
- refFile = path.join(nwLipsum, "content", "1a6562590ef19.nwd")
- assert cmpFiles(refFile, path.join(nwRef, "gui", "5_25fc0e7096fc6.nwd"), [1])
- refFile = path.join(nwLipsum, "content", "031b4af5197ec.nwd")
- assert cmpFiles(refFile, path.join(nwRef, "gui", "5_031b4af5197ec.nwd"))
- refFile = path.join(nwLipsum, "content", "41cfc0d1f2d12.nwd")
- assert cmpFiles(refFile, path.join(nwRef, "gui", "5_41cfc0d1f2d12.nwd"))
- refFile = path.join(nwLipsum, "content", "2858dcd1057d3.nwd")
- assert cmpFiles(refFile, path.join(nwRef, "gui", "5_2858dcd1057d3.nwd"))
- refFile = path.join(nwLipsum, "content", "2fca346db6561.nwd")
- assert cmpFiles(refFile, path.join(nwRef, "gui", "5_2fca346db6561.nwd"))
+ projFile = path.join(nwLipsum, "content", "1a6562590ef19.nwd")
+ testFile = path.join(nwTempGUI, "5_25fc0e7096fc6.nwd")
+ refFile = path.join(nwRef, "gui", "5_25fc0e7096fc6.nwd")
+ copyfile(projFile, testFile)
+ assert cmpFiles(testFile, refFile, [1])
+
+ projFile = path.join(nwLipsum, "content", "031b4af5197ec.nwd")
+ testFile = path.join(nwTempGUI, "5_031b4af5197ec.nwd")
+ refFile = path.join(nwRef, "gui", "5_031b4af5197ec.nwd")
+ copyfile(projFile, testFile)
+ assert cmpFiles(testFile, refFile)
+
+ projFile = path.join(nwLipsum, "content", "41cfc0d1f2d12.nwd")
+ testFile = path.join(nwTempGUI, "5_41cfc0d1f2d12.nwd")
+ refFile = path.join(nwRef, "gui", "5_41cfc0d1f2d12.nwd")
+ copyfile(projFile, testFile)
+ assert cmpFiles(testFile, refFile)
+
+ projFile = path.join(nwLipsum, "content", "2858dcd1057d3.nwd")
+ testFile = path.join(nwTempGUI, "5_2858dcd1057d3.nwd")
+ refFile = path.join(nwRef, "gui", "5_2858dcd1057d3.nwd")
+ copyfile(projFile, testFile)
+ assert cmpFiles(testFile, refFile)
+
+ projFile = path.join(nwLipsum, "content", "2fca346db6561.nwd")
+ testFile = path.join(nwTempGUI, "5_2fca346db6561.nwd")
+ refFile = path.join(nwRef, "gui", "5_2fca346db6561.nwd")
+ copyfile(projFile, testFile)
+ assert cmpFiles(testFile, refFile)
# qtbot.stopForInteraction()
nwGUI.closeMain()
@pytest.mark.gui
-def testNewProjectWizard(qtbot, nwTempGUI, nwLipsum, nwRef, nwTemp):
+def testNewProjectWizard(qtbot, nwLipsum, nwTemp):
from PyQt5.QtWidgets import QWizard
from nw.gui.projwizard import (
@@ -764,7 +877,7 @@ def testNewProjectWizard(qtbot, nwTempGUI, nwLipsum, nwRef, nwTemp):
ProjWizardCustomPage, ProjWizardFinalPage
)
- nwGUI = nw.main(["--testmode", "--config=%s" % nwTempGUI, "--data=%s" % nwTemp])
+ nwGUI = nw.main(["--testmode", "--config=%s" % nwLipsum, "--data=%s" % nwTemp])
qtbot.addWidget(nwGUI)
nwGUI.show()
qtbot.waitForWindowShown(nwGUI)
@@ -885,9 +998,9 @@ def testNewProjectWizard(qtbot, nwTempGUI, nwLipsum, nwRef, nwTemp):
nwGUI.closeMain()
@pytest.mark.gui
-def testDocAction(qtbot, nwTempGUI, nwLipsum, nwRef, nwTemp):
+def testDocAction(qtbot, nwLipsum, nwTemp):
- nwGUI = nw.main(["--testmode", "--config=%s" % nwTempGUI, "--data=%s" % nwTemp])
+ nwGUI = nw.main(["--testmode", "--config=%s" % nwLipsum, "--data=%s" % nwTemp])
qtbot.addWidget(nwGUI)
nwGUI.show()
qtbot.waitForWindowShown(nwGUI)
@@ -1001,8 +1114,8 @@ def testDocAction(qtbot, nwTempGUI, nwLipsum, nwRef, nwTemp):
nwGUI.closeMain()
@pytest.mark.gui
-def testInsertMenu(qtbot, nwTempGUI, nwFuncTemp, nwTemp):
- nwGUI = nw.main(["--testmode", "--config=%s" % nwTempGUI, "--data=%s" % nwTemp])
+def testInsertMenu(qtbot, nwFuncTemp, nwTemp):
+ nwGUI = nw.main(["--testmode", "--config=%s" % nwFuncTemp, "--data=%s" % nwTemp])
qtbot.addWidget(nwGUI)
nwGUI.show()
qtbot.waitForWindowShown(nwGUI)
@@ -1079,24 +1192,29 @@ def testInsertMenu(qtbot, nwTempGUI, nwFuncTemp, nwTemp):
nwGUI.closeMain()
@pytest.mark.gui
-def testLoadProject(qtbot, nwTempGUI, nwTemp):
- nwGUI = nw.main(["--testmode", "--config=%s" % nwTempGUI, "--data=%s" % nwTemp])
+def testLoadProject(qtbot, nwMinimal, nwTemp):
+ nwGUI = nw.main(["--testmode", "--config=%s" % nwMinimal, "--data=%s" % nwTemp])
qtbot.addWidget(nwGUI)
nwGUI.show()
qtbot.waitForWindowShown(nwGUI)
qtbot.wait(stepDelay)
+ assert nwGUI.openProject(nwMinimal)
+ assert nwGUI.closeProject()
+
nwLoad = GuiProjectLoad(nwGUI)
nwLoad.show()
recentCount = nwLoad.listBox.topLevelItemCount()
- assert recentCount > 1
+ assert recentCount > 0
- selItem = nwLoad.listBox.topLevelItem(1)
+ selItem = nwLoad.listBox.topLevelItem(0)
selPath = selItem.data(nwLoad.C_NAME, Qt.UserRole)
+ assert isinstance(selItem, QTreeWidgetItem)
nwLoad.selPath.setText("")
nwLoad.listBox.setCurrentItem(selItem)
+ nwLoad._doSelectRecent()
assert nwLoad.selPath.text() == selPath
qtbot.mouseClick(nwLoad.buttonBox.button(QDialogButtonBox.Open), Qt.LeftButton)
@@ -1125,16 +1243,16 @@ def testLoadProject(qtbot, nwTempGUI, nwTemp):
nwGUI.closeMain()
@pytest.mark.gui
-def testOutline(qtbot, nwTempBuild, nwLipsum, nwTemp):
+def testOutline(qtbot, nwLipsum, nwTemp):
- nwGUI = nw.main(["--testmode", "--config=%s" % nwTempBuild, "--data=%s" % nwTemp])
+ nwGUI = nw.main(["--testmode", "--config=%s" % nwLipsum, "--data=%s" % nwTemp])
qtbot.addWidget(nwGUI)
nwGUI.show()
qtbot.waitForWindowShown(nwGUI)
qtbot.wait(stepDelay)
assert nwGUI.openProject(nwLipsum)
- nwGUI.mainConf.lastPath = nwTempBuild
+ nwGUI.mainConf.lastPath = nwLipsum
nwGUI.rebuildIndex()
nwGUI.tabWidget.setCurrentIndex(nwGUI.idxTabProj)
From 7e830c9aab059574ef743e4418f036325a043c6a Mon Sep 17 00:00:00 2001
From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com>
Date: Fri, 28 Aug 2020 16:20:31 +0200
Subject: [PATCH 16/16] Cleanup unneeded stuff
---
tests/test_item.py | 10 ----------
1 file changed, 10 deletions(-)
diff --git a/tests/test_item.py b/tests/test_item.py
index 54cb02d4..a3280c14 100644
--- a/tests/test_item.py
+++ b/tests/test_item.py
@@ -5,20 +5,10 @@
import pytest
from lxml import etree
-from nwdummy import DummyMain
-from nw.config import Config
from nw.core.project import NWProject, NWItem
from nw.constants import nwItemClass, nwItemType, nwItemLayout
-# theConf = Config()
-# theMain = DummyMain()
-# theMain.mainConf = theConf
-
-# theProject = NWProject(theMain)
-# theItem = NWItem(theProject)
-# nwXML = etree.Element("novelWriterXML")
-
@pytest.mark.project
def testItemSettersSimple(nwDummy):