diff --git a/.github/workflows/pytest_cov.yml b/.github/workflows/pytest_cov.yml
new file mode 100644
index 00000000..8e60f72b
--- /dev/null
+++ b/.github/workflows/pytest_cov.yml
@@ -0,0 +1,58 @@
+name: PyTest
+
+on:
+ pull_request:
+ branches: [ main, dev ]
+
+jobs:
+ pyTestCov:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Python Setup
+ uses: actions/setup-python@v1
+ with:
+ python-version: 3.8
+ architecture: x64
+ - name: Install Packages
+ run: sudo apt install xvfb libenchant-dev qt5-default
+ - name: Checkout Source
+ uses: actions/checkout@v2
+ - name: Install Dependencies
+ run: |
+ pip install --upgrade pip
+ pip install -r requirements.txt
+ pip install PyVirtualDisplay
+ pip install pytest-cov
+ pip install pytest-xvfb
+ pip install pytest-qt
+ pip install codecov
+ - name: Run Tests
+ run: xvfb-run pytest -v --cov=nw
+ - name: Upload to Codecov
+ uses: codecov/codecov-action@v1
+
+ pyTest:
+ needs: pyTestCov
+ strategy:
+ matrix:
+ python-version: [3.6, 3.7]
+ runs-on: ubuntu-latest
+ steps:
+ - name: Python Setup
+ uses: actions/setup-python@v1
+ with:
+ python-version: ${{ matrix.python-version }}
+ architecture: x64
+ - name: Install Packages
+ run: sudo apt install xvfb libenchant-dev qt5-default
+ - name: Checkout Source
+ uses: actions/checkout@v2
+ - name: Install Dependencies
+ run: |
+ pip install --upgrade pip
+ pip install -r requirements.txt
+ pip install PyVirtualDisplay
+ pip install pytest-xvfb
+ pip install pytest-qt
+ - name: Run Tests
+ run: xvfb-run pytest -v
diff --git a/.github/workflows/syntax.yml b/.github/workflows/syntax.yml
index c12279af..20736e91 100644
--- a/.github/workflows/syntax.yml
+++ b/.github/workflows/syntax.yml
@@ -2,9 +2,9 @@ name: Flake8 Checks
on:
push:
- branches: [ main ]
+ branches: [ main, dev ]
pull_request:
- branches: [ main ]
+ branches: [ main, dev ]
jobs:
checkSyntax:
@@ -15,13 +15,15 @@ jobs:
with:
python-version: 3.7
architecture: x64
- - name: Checkout novelWriter
+ - name: Checkout Source
uses: actions/checkout@v2
- name: Install flake8
run: pip install flake8
- - name: Check for Syntax Error in novelWriter
- run: flake8 nw --count --select=E9,F63,F7,F82 --show-source --statistics
- - name: Check for Syntax Error in Tests
- run: flake8 tests --count --select=E9,F63,F7,F82 --show-source --statistics
- - name: Check for Code Style Violations
- run: flake8 nw --count --max-line-length=99 --ignore E203,E221,E226,E241,E251,E261,E266,E302,E305 --show-source --statistics
+ - name: Syntax Error Check
+ run: |
+ flake8 nw --count --select=E9,F63,F7,F82 --show-source --statistics
+ flake8 tests --count --select=E9,F63,F7,F82 --show-source --statistics
+ - name: Coding Style Violations
+ run: |
+ flake8 nw --count --max-line-length=99 --ignore E203,E221,E226,E241,E251,E261,E266,E302,E305 --show-source --statistics
+ flake8 tests --count --max-line-length=99 --ignore E203,E221,E226,E241,E251,E261,E266,E302,E305 --show-source --statistics
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index b0b45ee9..00000000
--- a/.travis.yml
+++ /dev/null
@@ -1,57 +0,0 @@
-os: linux
-dist: focal
-services:
- - xvfb
-language: python
-cache:
- - bundler
-
-addons:
- apt:
- packages:
- - libenchant-dev
- - python3-pyqt5
- - python3-pyqt5.qtsvg
- - python3-lxml
-
-install:
-# - pip install --upgrade pip
- - pip install -r requirements.txt
- - pip install PyVirtualDisplay
- - pip install pytest-cov
- - pip install pytest-xvfb
- - pip install pytest-qt
- - pip install codecov
-
-stages:
- - name: Main
- - name: Supported
-# - name: Future
-# if: branch = main
-
-jobs:
- include:
- - stage: Main
- python: 3.8
- script:
- - python -m pytest --cov=nw -v
- after_success:
- - codecov
- after_failure:
- - cat /sys/fs/cgroup/memory/memory.max_usage_in_bytes
-
- - stage: Supported
- python: 3.6
- script:
- - python -m pytest -v
-
- - python: 3.7
- script:
- - python -m pytest -v
-
-# - stage:
-# - Future
-# python:
-# - 3.9-dev
-# script:
-# - python -m pytest -v
diff --git a/README.md b/README.md
index 3c60e951..7adc676f 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# novelWriter
-[](https://travis-ci.com/vkbo/novelWriter)
+[](https://github.com/vkbo/novelWriter/actions)
[](https://codecov.io/gh/vkbo/novelWriter)
[](https://novelwriter.readthedocs.io/en/latest/?badge=latest)
[](https://github.com/vkbo/novelWriter/actions)
diff --git a/nw/gui/outlinedetails.py b/nw/gui/outlinedetails.py
index 9e53653f..6360a321 100644
--- a/nw/gui/outlinedetails.py
+++ b/nw/gui/outlinedetails.py
@@ -249,8 +249,8 @@ class GuiOutlineDetails(QScrollArea):
self.itemValue.setText(nwItem.itemStatus)
self.cCValue.setText("{:n}".format(checkInt(novIdx["cCount"], 0)))
- self.wCValue.setText("{:n}".format(checkInt(novIdx["pCount"], 0)))
- self.pCValue.setText("{:n}".format(checkInt(novIdx["wCount"], 0)))
+ self.wCValue.setText("{:n}".format(checkInt(novIdx["wCount"], 0)))
+ self.pCValue.setText("{:n}".format(checkInt(novIdx["pCount"], 0)))
self.synopValue.setText(novIdx["synopsis"])
diff --git a/tests/lipsum/nwProject.nwx b/tests/lipsum/nwProject.nwx
index ebfbde69..d6a58a7a 100644
--- a/tests/lipsum/nwProject.nwx
+++ b/tests/lipsum/nwProject.nwx
@@ -48,7 +48,7 @@
True
-
- Lorem Ipusm
+ Lorem Ipsum
FILE
NOVEL
Finished
diff --git a/tests/test_gui.py b/tests/test_gui.py
index d6ac126b..f7104308 100644
--- a/tests/test_gui.py
+++ b/tests/test_gui.py
@@ -8,14 +8,17 @@ import json
from nwtools import cmpFiles
from os import path
-from PyQt5.QtCore import Qt
-from PyQt5.QtWidgets import QAction
+from PyQt5.QtCore import Qt, QPoint
+from PyQt5.QtWidgets import QAction, QDialogButtonBox, QTreeWidgetItem
from nw.gui import (
GuiProjectSettings, GuiItemEditor, GuiAbout, GuiBuildNovel,
- GuiDocMerge, GuiDocSplit, GuiWritingStats, GuiProjectWizard
+ GuiDocMerge, GuiDocSplit, GuiWritingStats, GuiProjectWizard,
+ GuiProjectLoad
+)
+from nw.constants import (
+ nwItemType, nwItemLayout, nwItemClass, nwDocAction, nwUnicode, nwOutline
)
-from nw.constants import nwItemType, nwItemLayout, nwItemClass, nwDocAction, nwUnicode
keyDelay = 2
stepDelay = 20
@@ -294,6 +297,7 @@ def testProjectEditor(qtbot, nwTempGUI, nwRef, nwTemp):
projEdit.show()
qtbot.addWidget(projEdit)
+ qtbot.wait(stepDelay)
projEdit.tabMain.editName.setText("")
for c in "Project Name":
qtbot.keyClick(projEdit.tabMain.editName, c, delay=keyDelay)
@@ -306,6 +310,7 @@ def testProjectEditor(qtbot, nwTempGUI, nwRef, nwTemp):
qtbot.keyClick(projEdit.tabMain.editAuthors, c, delay=keyDelay)
# Test Status Tab
+ qtbot.wait(stepDelay)
projEdit._tabBox.setCurrentWidget(projEdit.tabStatus)
projEdit.tabStatus.listBox.item(2).setSelected(True)
qtbot.mouseClick(projEdit.tabStatus.delButton, Qt.LeftButton)
@@ -318,6 +323,7 @@ def testProjectEditor(qtbot, nwTempGUI, nwRef, nwTemp):
qtbot.mouseClick(projEdit.tabStatus.saveButton, Qt.LeftButton)
# Auto-Replace Tab
+ qtbot.wait(stepDelay)
projEdit._tabBox.setCurrentWidget(projEdit.tabReplace)
qtbot.mouseClick(projEdit.tabReplace.addButton, Qt.LeftButton)
@@ -328,19 +334,22 @@ def testProjectEditor(qtbot, nwTempGUI, nwRef, nwTemp):
qtbot.keyClick(projEdit.tabReplace.editValue, c, delay=keyDelay)
qtbot.mouseClick(projEdit.tabReplace.saveButton, Qt.LeftButton)
+ qtbot.wait(stepDelay)
projEdit.tabReplace.listBox.clearSelection()
qtbot.mouseClick(projEdit.tabReplace.addButton, Qt.LeftButton)
- projEdit.tabReplace.listBox.topLevelItem(0).setSelected(True)
- for c in "Delete":
- qtbot.keyClick(projEdit.tabReplace.editKey, c, delay=keyDelay)
- for c in "This Stuff":
- qtbot.keyClick(projEdit.tabReplace.editValue, c, delay=keyDelay)
- qtbot.mouseClick(projEdit.tabReplace.saveButton, Qt.LeftButton)
- projEdit.tabReplace.listBox.clearSelection()
- projEdit.tabReplace.listBox.topLevelItem(0).setSelected(True)
+ newIdx = -1
+ for i in range(projEdit.tabReplace.listBox.topLevelItemCount()):
+ if projEdit.tabReplace.listBox.topLevelItem(i).text(0) == "":
+ newIdx = i
+ break
+
+ assert newIdx >= 0
+ newItem = projEdit.tabReplace.listBox.topLevelItem(newIdx)
+ projEdit.tabReplace.listBox.setCurrentItem(newItem)
qtbot.mouseClick(projEdit.tabReplace.delButton, Qt.LeftButton)
+ qtbot.wait(stepDelay)
projEdit._doSave()
# Open again, and check project settings
@@ -363,8 +372,8 @@ def testProjectEditor(qtbot, nwTempGUI, nwRef, nwTemp):
projFile = path.join(nwTempGUI, "nwProject.nwx")
assert cmpFiles(projFile, path.join(nwRef, "gui", "2_nwProject.nwx"), [2, 8, 9, 10])
- nwGUI.closeMain()
# qtbot.stopForInteraction()
+ nwGUI.closeMain()
@pytest.mark.gui
def testItemEditor(qtbot, nwTempGUI, nwRef, nwTemp):
@@ -1064,3 +1073,109 @@ def testInsertMenu(qtbot, nwTempGUI, nwFuncTemp, nwTemp):
# qtbot.stopForInteraction()
nwGUI.closeMain()
+
+@pytest.mark.gui
+def testLoadProject(qtbot, nwTempGUI, nwTemp):
+ nwGUI = nw.main(["--testmode", "--config=%s" % nwTempGUI, "--data=%s" % nwTemp])
+ qtbot.addWidget(nwGUI)
+ nwGUI.show()
+ qtbot.waitForWindowShown(nwGUI)
+ qtbot.wait(stepDelay)
+
+ nwLoad = GuiProjectLoad(nwGUI)
+ nwLoad.show()
+
+ recentCount = nwLoad.listBox.topLevelItemCount()
+ assert recentCount > 1
+
+ selItem = nwLoad.listBox.topLevelItem(1)
+ selPath = selItem.data(nwLoad.C_NAME, Qt.UserRole)
+
+ nwLoad.selPath.setText("")
+ nwLoad.listBox.setCurrentItem(selItem)
+ assert nwLoad.selPath.text() == selPath
+
+ qtbot.mouseClick(nwLoad.buttonBox.button(QDialogButtonBox.Open), Qt.LeftButton)
+ assert nwLoad.openPath == selPath
+ assert nwLoad.openState == nwLoad.OPEN_STATE
+
+ del nwLoad
+ nwLoad = GuiProjectLoad(nwGUI)
+ nwLoad.show()
+
+ qtbot.mouseClick(nwLoad.buttonBox.button(QDialogButtonBox.Cancel), Qt.LeftButton)
+ assert nwLoad.openPath is None
+ assert nwLoad.openState == nwLoad.NONE_STATE
+
+ nwLoad.show()
+ qtbot.mouseClick(nwLoad.newButton, Qt.LeftButton)
+ assert nwLoad.openPath is None
+ assert nwLoad.openState == nwLoad.NEW_STATE
+
+ nwLoad.show()
+ nwLoad._keyPressDelete()
+ assert nwLoad.listBox.topLevelItemCount() == recentCount - 1
+
+ nwLoad.close()
+ # qtbot.stopForInteraction()
+ nwGUI.closeMain()
+
+@pytest.mark.gui
+def testOutline(qtbot, nwTempBuild, nwLipsum, nwTemp):
+
+ nwGUI = nw.main(["--testmode", "--config=%s" % nwTempBuild, "--data=%s" % nwTemp])
+ qtbot.addWidget(nwGUI)
+ nwGUI.show()
+ qtbot.waitForWindowShown(nwGUI)
+ qtbot.wait(stepDelay)
+
+ assert nwGUI.openProject(nwLipsum)
+ nwGUI.mainConf.lastPath = nwTempBuild
+
+ nwGUI.rebuildIndex()
+ nwGUI.tabWidget.setCurrentIndex(nwGUI.idxTabProj)
+
+ assert nwGUI.projView.topLevelItemCount() > 0
+
+ # Context Menu
+ nwGUI.projView._headerRightClick(QPoint(1, 1))
+ nwGUI.projView.headerMenu.actionMap[nwOutline.CCOUNT].activate(QAction.Trigger)
+ nwGUI.projView.headerMenu.close()
+ qtbot.mouseClick(nwGUI.projView, Qt.LeftButton)
+
+ nwGUI.projView._loadHeaderState()
+ assert not nwGUI.projView.colHidden[nwOutline.CCOUNT]
+
+ # First Item
+ nwGUI.rebuildOutline()
+ selItem = nwGUI.projView.topLevelItem(0)
+ assert isinstance(selItem, QTreeWidgetItem)
+
+ nwGUI.projView.setCurrentItem(selItem)
+ assert nwGUI.projMeta.titleLabel.text() == "Title"
+ assert nwGUI.projMeta.titleValue.text() == "Lorem Ipsum"
+ assert nwGUI.projMeta.fileValue.text() == "Lorem Ipsum"
+ assert nwGUI.projMeta.itemValue.text() == "Finished"
+
+ assert nwGUI.projMeta.cCValue.text() == "122"
+ assert nwGUI.projMeta.wCValue.text() == "18"
+ assert nwGUI.projMeta.pCValue.text() == "2"
+
+ # Scene One
+ actItem = nwGUI.projView.topLevelItem(1)
+ chpItem = actItem.child(0)
+ selItem = chpItem.child(0)
+
+ nwGUI.projView.setCurrentItem(selItem)
+ assert nwGUI.projMeta.titleLabel.text() == "Scene"
+ assert nwGUI.projMeta.titleValue.text() == "Scene One"
+ assert nwGUI.projMeta.fileValue.text() == "Scene One"
+ assert nwGUI.projMeta.itemValue.text() == "Finished"
+
+ # Click POV Link
+ assert nwGUI.projMeta.povKeyValue.text() == "Bod"
+ nwGUI.projMeta._tagClicked("#pov=Bod")
+ assert nwGUI.docViewer.theHandle == "4c4f28287af27"
+
+ # qtbot.stopForInteraction()
+ nwGUI.closeMain()