Test terminology update
This commit is contained in:
+1
-1
@@ -269,7 +269,7 @@ class NWIndex():
|
|||||||
logger.debug("Indexing item with handle %s" % tHandle)
|
logger.debug("Indexing item with handle %s" % tHandle)
|
||||||
|
|
||||||
# Check file type, and reset its old index
|
# Check file type, and reset its old index
|
||||||
# Also add a dummy entry T000000 in case the file has no title
|
# Also add a default entry T000000 in case the file has no title
|
||||||
self._refIndex[tHandle] = {}
|
self._refIndex[tHandle] = {}
|
||||||
self._refIndex[tHandle]["T000000"] = {
|
self._refIndex[tHandle]["T000000"] = {
|
||||||
"tags" : [],
|
"tags" : [],
|
||||||
|
|||||||
@@ -47,17 +47,17 @@ class NWSpellCheck():
|
|||||||
return
|
return
|
||||||
|
|
||||||
def setLanguage(self, theLang, projectDict=None):
|
def setLanguage(self, theLang, projectDict=None):
|
||||||
"""Dummy function.
|
"""Default function.
|
||||||
"""
|
"""
|
||||||
return
|
return
|
||||||
|
|
||||||
def checkWord(self, theWord):
|
def checkWord(self, theWord):
|
||||||
"""Dummy function.
|
"""Default function.
|
||||||
"""
|
"""
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def suggestWords(self, theWord):
|
def suggestWords(self, theWord):
|
||||||
"""Dummy function.
|
"""Default function.
|
||||||
"""
|
"""
|
||||||
return []
|
return []
|
||||||
|
|
||||||
@@ -78,12 +78,12 @@ class NWSpellCheck():
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def listDictionaries(self):
|
def listDictionaries(self):
|
||||||
"""Dummy function.
|
"""Default function.
|
||||||
"""
|
"""
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def describeDict(self):
|
def describeDict(self):
|
||||||
"""Dummy function.
|
"""Default function.
|
||||||
"""
|
"""
|
||||||
return "", ""
|
return "", ""
|
||||||
|
|
||||||
@@ -136,7 +136,7 @@ class NWSpellEnchant(NWSpellCheck):
|
|||||||
|
|
||||||
def setLanguage(self, theLang, projectDict=None):
|
def setLanguage(self, theLang, projectDict=None):
|
||||||
"""Load a dictionary for the language specified in the config.
|
"""Load a dictionary for the language specified in the config.
|
||||||
If that fails, we load a dummy dictionary so that lookups don't
|
If that fails, we load a mock dictionary so that lookups don't
|
||||||
crash.
|
crash.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
|
|||||||
+1
-1
@@ -563,7 +563,7 @@ class ToOdt(Tokenizer):
|
|||||||
##
|
##
|
||||||
|
|
||||||
if len(theText) != len(theFmt):
|
if len(theText) != len(theFmt):
|
||||||
# Generate dummy format if there isn't any or it doesn't match
|
# Generate an empty format if there isn't any or it doesn't match
|
||||||
theFmt = " "*len(theText)
|
theFmt = " "*len(theText)
|
||||||
|
|
||||||
# XML functions
|
# XML functions
|
||||||
|
|||||||
+2
-2
@@ -173,7 +173,7 @@ class GuiBuildNovel(QDialog):
|
|||||||
if langIdx != -1:
|
if langIdx != -1:
|
||||||
self.buildLang.setCurrentIndex(langIdx)
|
self.buildLang.setCurrentIndex(langIdx)
|
||||||
|
|
||||||
# Dummy boxes due to QGridView and QLineEdit expand bug
|
# Wrapper boxes due to QGridView and QLineEdit expand bug
|
||||||
self.boxTitle = QHBoxLayout()
|
self.boxTitle = QHBoxLayout()
|
||||||
self.boxTitle.addWidget(self.fmtTitle)
|
self.boxTitle.addWidget(self.fmtTitle)
|
||||||
self.boxChapter = QHBoxLayout()
|
self.boxChapter = QHBoxLayout()
|
||||||
@@ -245,7 +245,7 @@ class GuiBuildNovel(QDialog):
|
|||||||
self.optState.getFloat("GuiBuildNovel", "lineHeight", 1.15)
|
self.optState.getFloat("GuiBuildNovel", "lineHeight", 1.15)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Dummy box due to QGridView and QLineEdit expand bug
|
# Wrapper box due to QGridView and QLineEdit expand bug
|
||||||
self.boxFont = QHBoxLayout()
|
self.boxFont = QHBoxLayout()
|
||||||
self.boxFont.addWidget(self.textFont)
|
self.boxFont.addWidget(self.textFont)
|
||||||
|
|
||||||
|
|||||||
+8
-8
@@ -25,7 +25,7 @@ import pytest
|
|||||||
import shutil
|
import shutil
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from dummy import DummyMain
|
from mock import MockGuiMain
|
||||||
from tools import cleanProject
|
from tools import cleanProject
|
||||||
|
|
||||||
sys.path.insert(1, os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)))
|
sys.path.insert(1, os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)))
|
||||||
@@ -88,8 +88,8 @@ def fncDir(tmpDir):
|
|||||||
if not os.path.isdir(fncDir):
|
if not os.path.isdir(fncDir):
|
||||||
os.mkdir(fncDir)
|
os.mkdir(fncDir)
|
||||||
yield fncDir
|
yield fncDir
|
||||||
if os.path.isdir(fncDir):
|
# if os.path.isdir(fncDir):
|
||||||
shutil.rmtree(fncDir)
|
# shutil.rmtree(fncDir)
|
||||||
return
|
return
|
||||||
|
|
||||||
@pytest.fixture(scope="function")
|
@pytest.fixture(scope="function")
|
||||||
@@ -136,12 +136,12 @@ def fncConf(fncDir):
|
|||||||
|
|
||||||
@pytest.fixture(scope="function")
|
@pytest.fixture(scope="function")
|
||||||
def dummyGUI(monkeypatch, tmpConf):
|
def dummyGUI(monkeypatch, tmpConf):
|
||||||
"""Create a dummy instance of novelWriter's main GUI class.
|
"""Create a mock instance of novelWriter's main GUI class.
|
||||||
"""
|
"""
|
||||||
monkeypatch.setattr("nw.CONFIG", tmpConf)
|
monkeypatch.setattr("nw.CONFIG", tmpConf)
|
||||||
theDummy = DummyMain()
|
theGui = MockGuiMain()
|
||||||
theDummy.mainConf = tmpConf
|
theGui.mainConf = tmpConf
|
||||||
return theDummy
|
return theGui
|
||||||
|
|
||||||
@pytest.fixture(scope="function")
|
@pytest.fixture(scope="function")
|
||||||
def nwGUI(qtbot, monkeypatch, fncDir, fncConf):
|
def nwGUI(qtbot, monkeypatch, fncDir, fncConf):
|
||||||
@@ -191,7 +191,7 @@ def nwMinimal(tmpDir):
|
|||||||
@pytest.fixture(scope="function")
|
@pytest.fixture(scope="function")
|
||||||
def nwLipsum(tmpDir):
|
def nwLipsum(tmpDir):
|
||||||
"""A medium sized novelWriter example project with a lot of Lorem
|
"""A medium sized novelWriter example project with a lot of Lorem
|
||||||
Ipsum dummy text.
|
Ipsum text.
|
||||||
"""
|
"""
|
||||||
tstDir = os.path.dirname(__file__)
|
tstDir = os.path.dirname(__file__)
|
||||||
srcDir = os.path.join(tstDir, "lipsum")
|
srcDir = os.path.join(tstDir, "lipsum")
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""
|
"""
|
||||||
novelWriter – Test Suite Dummy Classes
|
novelWriter – Test Suite Mocked Classes
|
||||||
======================================
|
=======================================
|
||||||
|
|
||||||
This file is a part of novelWriter
|
This file is a part of novelWriter
|
||||||
Copyright 2018–2021, Veronica Berglyd Olsen
|
Copyright 2018–2021, Veronica Berglyd Olsen
|
||||||
@@ -24,14 +24,14 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||||||
# Mock GUI
|
# Mock GUI
|
||||||
# =========================================================================== #
|
# =========================================================================== #
|
||||||
|
|
||||||
class DummyMain():
|
class MockGuiMain():
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.mainConf = None
|
self.mainConf = None
|
||||||
self.hasProject = True
|
self.hasProject = True
|
||||||
self.theIndex = None
|
self.theIndex = None
|
||||||
self.theProject = None
|
self.theProject = None
|
||||||
self.statusBar = DummyStatusBar()
|
self.statusBar = MockStatusBar()
|
||||||
|
|
||||||
# Test Variables
|
# Test Variables
|
||||||
self.askResponse = True
|
self.askResponse = True
|
||||||
@@ -79,9 +79,9 @@ class DummyMain():
|
|||||||
self.lastAlert = ""
|
self.lastAlert = ""
|
||||||
return
|
return
|
||||||
|
|
||||||
# END Class DummyMain
|
# END Class MockGuiMain
|
||||||
|
|
||||||
class DummyStatusBar():
|
class MockStatusBar():
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
return
|
return
|
||||||
@@ -89,9 +89,9 @@ class DummyStatusBar():
|
|||||||
def setStatus(self, theText):
|
def setStatus(self, theText):
|
||||||
return
|
return
|
||||||
|
|
||||||
# END Class DummyStatusBar
|
# END Class MockStatusBar
|
||||||
|
|
||||||
class DummyApp:
|
class MockApp:
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
return
|
return
|
||||||
@@ -99,11 +99,11 @@ class DummyApp:
|
|||||||
def installTranslator(self, theLang):
|
def installTranslator(self, theLang):
|
||||||
return
|
return
|
||||||
|
|
||||||
# END Class DummyApp
|
# END Class MockApp
|
||||||
|
|
||||||
# =========================================================================== #
|
# =========================================================================== #
|
||||||
# Error Functions
|
# Error Functions
|
||||||
# Dummy functions that will raise errors instead.
|
# Mock functions that will raise errors instead.
|
||||||
# =========================================================================== #
|
# =========================================================================== #
|
||||||
|
|
||||||
def causeOSError(*args, **kwargs):
|
def causeOSError(*args, **kwargs):
|
||||||
@@ -19,9 +19,9 @@
|
|||||||
|
|
||||||
@char: Jane
|
@char: Jane
|
||||||
|
|
||||||
This is a paragraph of dummy text.
|
This is a paragraph of nonsense text.
|
||||||
|
|
||||||
This is another paragraph of much longer dummy text. It is in fact 1 very very DUMB dummy text! We can also try replacing “quotes”, even single ‘quotes’ are replaced. Isn’t that nice? We can hyphen-ate, make dashes – and even longer dashes — if we want. Ellipsis? Not a problem either … How about three hyphens — for long dash? It works too.
|
This is another paragraph of much longer nonsense text. It is in fact 1 very very NONSENSICAL nonsense text! We can also try replacing “quotes”, even single ‘quotes’ are replaced. Isn’t that nice? We can hyphen-ate, make dashes – and even longer dashes — if we want. Ellipsis? Not a problem either … How about three hyphens — for long dash? It works too.
|
||||||
|
|
||||||
“Full line double quoted text.”
|
“Full line double quoted text.”
|
||||||
|
|
||||||
|
|||||||
@@ -85,10 +85,10 @@
|
|||||||
<status>New</status>
|
<status>New</status>
|
||||||
<exported>True</exported>
|
<exported>True</exported>
|
||||||
<layout>BOOK</layout>
|
<layout>BOOK</layout>
|
||||||
<charCount>466</charCount>
|
<charCount>482</charCount>
|
||||||
<wordCount>83</wordCount>
|
<wordCount>83</wordCount>
|
||||||
<paraCount>4</paraCount>
|
<paraCount>4</paraCount>
|
||||||
<cursorPos>604</cursorPos>
|
<cursorPos>620</cursorPos>
|
||||||
</item>
|
</item>
|
||||||
<item handle="44cb730c42048" order="1" parent="None">
|
<item handle="44cb730c42048" order="1" parent="None">
|
||||||
<name>Plot</name>
|
<name>Plot</name>
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ import configparser
|
|||||||
|
|
||||||
from shutil import copyfile
|
from shutil import copyfile
|
||||||
|
|
||||||
from dummy import causeOSError, DummyApp
|
from mock import causeOSError, MockApp
|
||||||
from tools import cmpFiles, writeFile
|
from tools import cmpFiles, writeFile
|
||||||
|
|
||||||
from nw.config import Config
|
from nw.config import Config
|
||||||
@@ -195,7 +195,7 @@ def testBaseConfig_Init(monkeypatch, tmpDir, fncDir, outDir, refDir, filesDir):
|
|||||||
writeFile(os.path.join(i18nDir, "nw_en_GB.ts"), "")
|
writeFile(os.path.join(i18nDir, "nw_en_GB.ts"), "")
|
||||||
writeFile(os.path.join(i18nDir, "nw_abcd.qm"), "")
|
writeFile(os.path.join(i18nDir, "nw_abcd.qm"), "")
|
||||||
|
|
||||||
tstApp = DummyApp()
|
tstApp = MockApp()
|
||||||
tstConf.initLocalisation(tstApp)
|
tstConf.initLocalisation(tstApp)
|
||||||
theList = tstConf.listLanguages(tstConf.LANG_NW)
|
theList = tstConf.listLanguages(tstConf.LANG_NW)
|
||||||
assert theList == [("en_GB", "British English")]
|
assert theList == [("en_GB", "British English")]
|
||||||
@@ -252,7 +252,7 @@ def testBaseConfig_RecentCache(monkeypatch, tmpConf, tmpDir, fncDir):
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Remove Non-Existent Entry
|
# Remove Non-Existent Entry
|
||||||
assert not tmpConf.removeFromRecentCache("dummy")
|
assert not tmpConf.removeFromRecentCache("stuff")
|
||||||
assert tmpConf.recentProj == {
|
assert tmpConf.recentProj == {
|
||||||
pathOne: {"time": 1600002000, "title": "Proj One", "words": 100},
|
pathOne: {"time": 1600002000, "title": "Proj One", "words": 100},
|
||||||
pathTwo: {"time": 1600005600, "title": "Proj Two", "words": 200},
|
pathTwo: {"time": 1600005600, "title": "Proj Two", "words": 200},
|
||||||
@@ -490,7 +490,7 @@ def testBaseConfig_Internal(monkeypatch, tmpConf):
|
|||||||
cnfParse = configparser.ConfigParser()
|
cnfParse = configparser.ConfigParser()
|
||||||
cnfParse.read_string(
|
cnfParse.read_string(
|
||||||
"[Main]\n"
|
"[Main]\n"
|
||||||
"val_string = dummy\n"
|
"val_string = stuff\n"
|
||||||
"val_int = 123\n"
|
"val_int = 123\n"
|
||||||
"val_bool = True\n"
|
"val_bool = True\n"
|
||||||
"val_list_string = A, B, C\n"
|
"val_list_string = A, B, C\n"
|
||||||
@@ -499,7 +499,7 @@ def testBaseConfig_Internal(monkeypatch, tmpConf):
|
|||||||
|
|
||||||
assert tmpConf._parseLine(
|
assert tmpConf._parseLine(
|
||||||
cnfParse, "Main", "val_string", tmpConf.CNF_STR, "default"
|
cnfParse, "Main", "val_string", tmpConf.CNF_STR, "default"
|
||||||
) == "dummy"
|
) == "stuff"
|
||||||
assert tmpConf._parseLine(
|
assert tmpConf._parseLine(
|
||||||
cnfParse, "Main", "nope", tmpConf.CNF_STR, "default"
|
cnfParse, "Main", "nope", tmpConf.CNF_STR, "default"
|
||||||
) == "default"
|
) == "default"
|
||||||
@@ -554,7 +554,7 @@ def testBaseConfig_Internal(monkeypatch, tmpConf):
|
|||||||
assert tmpConf.hasEnchant is False
|
assert tmpConf.hasEnchant is False
|
||||||
|
|
||||||
with monkeypatch.context() as mp:
|
with monkeypatch.context() as mp:
|
||||||
mp.setattr("shutil.which", lambda *args: "dummy")
|
mp.setattr("shutil.which", lambda *args: "stuff")
|
||||||
tmpConf._checkOptionalPackages()
|
tmpConf._checkOptionalPackages()
|
||||||
assert tmpConf.hasAssistant is True
|
assert tmpConf.hasAssistant is True
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import pytest
|
|||||||
|
|
||||||
from PyQt5.QtWidgets import qApp
|
from PyQt5.QtWidgets import qApp
|
||||||
|
|
||||||
from dummy import causeException
|
from mock import causeException
|
||||||
|
|
||||||
from nw.error import NWErrorMessage, exceptionHandler
|
from nw.error import NWErrorMessage, exceptionHandler
|
||||||
|
|
||||||
|
|||||||
@@ -25,19 +25,19 @@ import pytest
|
|||||||
import logging
|
import logging
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from dummy import DummyMain
|
from mock import MockGuiMain
|
||||||
|
|
||||||
@pytest.mark.base
|
@pytest.mark.base
|
||||||
def testBaseInit_Launch(caplog, monkeypatch, tmpDir):
|
def testBaseInit_Launch(caplog, monkeypatch, tmpDir):
|
||||||
"""Check launching the main GUI.
|
"""Check launching the main GUI.
|
||||||
"""
|
"""
|
||||||
monkeypatch.setattr("nw.guimain.GuiMain", DummyMain)
|
monkeypatch.setattr("nw.guimain.GuiMain", MockGuiMain)
|
||||||
|
|
||||||
# Testmode launch
|
# Testmode launch
|
||||||
nwGUI = nw.main(
|
nwGUI = nw.main(
|
||||||
["--testmode", "--config=%s" % tmpDir, "--data=%s" % tmpDir]
|
["--testmode", "--config=%s" % tmpDir, "--data=%s" % tmpDir]
|
||||||
)
|
)
|
||||||
assert isinstance(nwGUI, DummyMain)
|
assert isinstance(nwGUI, MockGuiMain)
|
||||||
|
|
||||||
# Darwin launch
|
# Darwin launch
|
||||||
monkeypatch.setitem(sys.modules, "Foundation", None)
|
monkeypatch.setitem(sys.modules, "Foundation", None)
|
||||||
@@ -46,7 +46,7 @@ def testBaseInit_Launch(caplog, monkeypatch, tmpDir):
|
|||||||
nwGUI = nw.main(
|
nwGUI = nw.main(
|
||||||
["--testmode", "--config=%s" % tmpDir, "--data=%s" % tmpDir]
|
["--testmode", "--config=%s" % tmpDir, "--data=%s" % tmpDir]
|
||||||
)
|
)
|
||||||
assert isinstance(nwGUI, DummyMain)
|
assert isinstance(nwGUI, MockGuiMain)
|
||||||
assert "Foundation" in caplog.messages[1]
|
assert "Foundation" in caplog.messages[1]
|
||||||
nw.CONFIG.osDarwin = osDarwin
|
nw.CONFIG.osDarwin = osDarwin
|
||||||
|
|
||||||
@@ -68,7 +68,7 @@ def testBaseInit_Launch(caplog, monkeypatch, tmpDir):
|
|||||||
def testBaseInit_Options(monkeypatch, tmpDir):
|
def testBaseInit_Options(monkeypatch, tmpDir):
|
||||||
"""Test command line options for logging level.
|
"""Test command line options for logging level.
|
||||||
"""
|
"""
|
||||||
monkeypatch.setattr("nw.guimain.GuiMain", DummyMain)
|
monkeypatch.setattr("nw.guimain.GuiMain", MockGuiMain)
|
||||||
monkeypatch.setattr(sys, "argv", [
|
monkeypatch.setattr(sys, "argv", [
|
||||||
"novelWriter.py", "--testmode", "--config=%s" % tmpDir, "--data=%s" % tmpDir
|
"novelWriter.py", "--testmode", "--config=%s" % tmpDir, "--data=%s" % tmpDir
|
||||||
])
|
])
|
||||||
@@ -140,7 +140,7 @@ def testBaseInit_Options(monkeypatch, tmpDir):
|
|||||||
def testBaseInit_Imports(caplog, monkeypatch, tmpDir):
|
def testBaseInit_Imports(caplog, monkeypatch, tmpDir):
|
||||||
"""Check import error handling.
|
"""Check import error handling.
|
||||||
"""
|
"""
|
||||||
monkeypatch.setattr("nw.guimain.GuiMain", DummyMain)
|
monkeypatch.setattr("nw.guimain.GuiMain", MockGuiMain)
|
||||||
monkeypatch.setattr("PyQt5.QtWidgets.QApplication.__init__", lambda *args: None)
|
monkeypatch.setattr("PyQt5.QtWidgets.QApplication.__init__", lambda *args: None)
|
||||||
monkeypatch.setattr("PyQt5.QtWidgets.QApplication.exec_", lambda *args: 0)
|
monkeypatch.setattr("PyQt5.QtWidgets.QApplication.exec_", lambda *args: 0)
|
||||||
monkeypatch.setattr("PyQt5.QtWidgets.QErrorMessage.__init__", lambda *args: None)
|
monkeypatch.setattr("PyQt5.QtWidgets.QErrorMessage.__init__", lambda *args: None)
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||||||
import os
|
import os
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from dummy import causeOSError
|
from mock import causeOSError
|
||||||
|
|
||||||
from nw.core import NWProject, NWDoc
|
from nw.core import NWProject, NWDoc
|
||||||
from nw.enum import nwItemClass, nwItemLayout
|
from nw.enum import nwItemClass, nwItemLayout
|
||||||
@@ -39,7 +39,7 @@ def testCoreDocument_LoadSave(monkeypatch, dummyGUI, nwMinimal):
|
|||||||
sHandle = "8c659a11cd429"
|
sHandle = "8c659a11cd429"
|
||||||
|
|
||||||
# Not a valid handle
|
# Not a valid handle
|
||||||
theDoc = NWDoc(theProject, "dummy")
|
theDoc = NWDoc(theProject, "stuff")
|
||||||
assert theDoc.readDocument() is None
|
assert theDoc.readDocument() is None
|
||||||
|
|
||||||
# Non-existent handle
|
# Non-existent handle
|
||||||
@@ -102,7 +102,7 @@ def testCoreDocument_LoadSave(monkeypatch, dummyGUI, nwMinimal):
|
|||||||
assert not theDoc.writeDocument(theText)
|
assert not theDoc.writeDocument(theText)
|
||||||
|
|
||||||
# Delete the last document
|
# Delete the last document
|
||||||
theDoc = NWDoc(theProject, "dummy")
|
theDoc = NWDoc(theProject, "stuff")
|
||||||
assert not theDoc.deleteDocument()
|
assert not theDoc.deleteDocument()
|
||||||
assert os.path.isfile(docPath)
|
assert os.path.isfile(docPath)
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ import json
|
|||||||
|
|
||||||
from shutil import copyfile
|
from shutil import copyfile
|
||||||
|
|
||||||
from dummy import causeException
|
from mock import causeException
|
||||||
from tools import cmpFiles
|
from tools import cmpFiles
|
||||||
|
|
||||||
from nw.core.project import NWProject
|
from nw.core.project import NWProject
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ import os
|
|||||||
import json
|
import json
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from dummy import causeOSError
|
from mock import causeOSError
|
||||||
|
|
||||||
from nw.core import NWProject
|
from nw.core import NWProject
|
||||||
from nw.core.options import OptionState
|
from nw.core.options import OptionState
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ from zipfile import ZipFile
|
|||||||
from lxml import etree
|
from lxml import etree
|
||||||
|
|
||||||
from tools import cmpFiles, writeFile, readFile
|
from tools import cmpFiles, writeFile, readFile
|
||||||
from dummy import causeOSError
|
from mock import causeOSError
|
||||||
|
|
||||||
from nw.core.project import NWProject
|
from nw.core.project import NWProject
|
||||||
from nw.enum import nwItemClass, nwItemType, nwItemLayout
|
from nw.enum import nwItemClass, nwItemType, nwItemLayout
|
||||||
@@ -354,11 +354,11 @@ def testCoreProject_Open(monkeypatch, nwMinimal, dummyGUI):
|
|||||||
oName = os.path.join(nwMinimal, nwFiles.PROJ_FILE[:-3]+"orig")
|
oName = os.path.join(nwMinimal, nwFiles.PROJ_FILE[:-3]+"orig")
|
||||||
bName = os.path.join(nwMinimal, nwFiles.PROJ_FILE[:-3]+"bak")
|
bName = os.path.join(nwMinimal, nwFiles.PROJ_FILE[:-3]+"bak")
|
||||||
os.rename(rName, oName)
|
os.rename(rName, oName)
|
||||||
writeFile(rName, "dummy")
|
writeFile(rName, "stuff")
|
||||||
assert theProject.openProject(nwMinimal) is False
|
assert theProject.openProject(nwMinimal) is False
|
||||||
|
|
||||||
# Also write a jun XML backup file
|
# Also write a jun XML backup file
|
||||||
writeFile(bName, "dummy")
|
writeFile(bName, "stuff")
|
||||||
assert theProject.openProject(nwMinimal) is False
|
assert theProject.openProject(nwMinimal) is False
|
||||||
|
|
||||||
# Wrong root item
|
# Wrong root item
|
||||||
@@ -426,9 +426,9 @@ def testCoreProject_Open(monkeypatch, nwMinimal, dummyGUI):
|
|||||||
os.rename(oName, rName)
|
os.rename(oName, rName)
|
||||||
|
|
||||||
# Add some legacy stuff that cannot be removed
|
# Add some legacy stuff that cannot be removed
|
||||||
writeFile(os.path.join(nwMinimal, "junk"), "dummy")
|
writeFile(os.path.join(nwMinimal, "junk"), "stuff")
|
||||||
os.mkdir(os.path.join(nwMinimal, "data_0"))
|
os.mkdir(os.path.join(nwMinimal, "data_0"))
|
||||||
writeFile(os.path.join(nwMinimal, "data_0", "junk"), "dummy")
|
writeFile(os.path.join(nwMinimal, "data_0", "junk"), "stuff")
|
||||||
dummyGUI.clear()
|
dummyGUI.clear()
|
||||||
assert theProject.openProject(nwMinimal) is True
|
assert theProject.openProject(nwMinimal) is True
|
||||||
assert "data_0" in dummyGUI.lastAlert
|
assert "data_0" in dummyGUI.lastAlert
|
||||||
@@ -559,19 +559,19 @@ def testCoreProject_Helpers(monkeypatch, fncDir, dummyGUI):
|
|||||||
|
|
||||||
# Create a file to block meta folder
|
# Create a file to block meta folder
|
||||||
metaDir = os.path.join(fncDir, "meta")
|
metaDir = os.path.join(fncDir, "meta")
|
||||||
writeFile(metaDir, "dummy")
|
writeFile(metaDir, "stuff")
|
||||||
assert theProject.ensureFolderStructure() is False
|
assert theProject.ensureFolderStructure() is False
|
||||||
os.unlink(metaDir)
|
os.unlink(metaDir)
|
||||||
|
|
||||||
# Create a file to block cache folder
|
# Create a file to block cache folder
|
||||||
cacheDir = os.path.join(fncDir, "cache")
|
cacheDir = os.path.join(fncDir, "cache")
|
||||||
writeFile(cacheDir, "dummy")
|
writeFile(cacheDir, "stuff")
|
||||||
assert theProject.ensureFolderStructure() is False
|
assert theProject.ensureFolderStructure() is False
|
||||||
os.unlink(cacheDir)
|
os.unlink(cacheDir)
|
||||||
|
|
||||||
# Create a file to block content folder
|
# Create a file to block content folder
|
||||||
contentDir = os.path.join(fncDir, "content")
|
contentDir = os.path.join(fncDir, "content")
|
||||||
writeFile(contentDir, "dummy")
|
writeFile(contentDir, "stuff")
|
||||||
assert theProject.ensureFolderStructure() is False
|
assert theProject.ensureFolderStructure() is False
|
||||||
os.unlink(contentDir)
|
os.unlink(contentDir)
|
||||||
|
|
||||||
@@ -977,7 +977,7 @@ def testCoreProject_OldFormat(dummyGUI, nwOldProj):
|
|||||||
"""
|
"""
|
||||||
theProject = NWProject(dummyGUI)
|
theProject = NWProject(dummyGUI)
|
||||||
|
|
||||||
# Create dummy files for known legacy files
|
# Create mock files for known legacy files
|
||||||
deleteFiles = [
|
deleteFiles = [
|
||||||
os.path.join(nwOldProj, "cache", "nwProject.nwx.0"),
|
os.path.join(nwOldProj, "cache", "nwProject.nwx.0"),
|
||||||
os.path.join(nwOldProj, "cache", "nwProject.nwx.1"),
|
os.path.join(nwOldProj, "cache", "nwProject.nwx.1"),
|
||||||
@@ -1005,7 +1005,7 @@ def testCoreProject_OldFormat(dummyGUI, nwOldProj):
|
|||||||
os.mkdir(os.path.join(nwOldProj, "stuff"))
|
os.mkdir(os.path.join(nwOldProj, "stuff"))
|
||||||
os.mkdir(os.path.join(nwOldProj, "data_1", "stuff"))
|
os.mkdir(os.path.join(nwOldProj, "data_1", "stuff"))
|
||||||
|
|
||||||
# Create dummy files
|
# Create mock files
|
||||||
os.mkdir(os.path.join(nwOldProj, "cache"))
|
os.mkdir(os.path.join(nwOldProj, "cache"))
|
||||||
for aFile in deleteFiles:
|
for aFile in deleteFiles:
|
||||||
writeFile(aFile, "Hi")
|
writeFile(aFile, "Hi")
|
||||||
@@ -1071,7 +1071,7 @@ def testCoreProject_LegacyData(monkeypatch, dummyGUI, fncDir):
|
|||||||
|
|
||||||
# Check behaviour of deprecated files function on OSError
|
# Check behaviour of deprecated files function on OSError
|
||||||
tstFile = os.path.join(fncDir, "ToC.json")
|
tstFile = os.path.join(fncDir, "ToC.json")
|
||||||
writeFile(tstFile, "dummy")
|
writeFile(tstFile, "stuff")
|
||||||
assert os.path.isfile(tstFile)
|
assert os.path.isfile(tstFile)
|
||||||
|
|
||||||
with monkeypatch.context() as mp:
|
with monkeypatch.context() as mp:
|
||||||
@@ -1083,7 +1083,7 @@ def testCoreProject_LegacyData(monkeypatch, dummyGUI, fncDir):
|
|||||||
|
|
||||||
# Check processing non-folders
|
# Check processing non-folders
|
||||||
tstFile = os.path.join(fncDir, "data_0")
|
tstFile = os.path.join(fncDir, "data_0")
|
||||||
writeFile(tstFile, "dummy")
|
writeFile(tstFile, "stuff")
|
||||||
assert os.path.isfile(tstFile)
|
assert os.path.isfile(tstFile)
|
||||||
|
|
||||||
errList = []
|
errList = []
|
||||||
@@ -1128,12 +1128,12 @@ def testCoreProject_LegacyData(monkeypatch, dummyGUI, fncDir):
|
|||||||
tstDoc3b = os.path.join(tstData, "tooshort003_main.bak")
|
tstDoc3b = os.path.join(tstData, "tooshort003_main.bak")
|
||||||
|
|
||||||
os.mkdir(tstData)
|
os.mkdir(tstData)
|
||||||
writeFile(tstDoc1m, "dummy")
|
writeFile(tstDoc1m, "stuff")
|
||||||
writeFile(tstDoc1b, "dummy")
|
writeFile(tstDoc1b, "stuff")
|
||||||
writeFile(tstDoc2m, "dummy")
|
writeFile(tstDoc2m, "stuff")
|
||||||
writeFile(tstDoc2b, "dummy")
|
writeFile(tstDoc2b, "stuff")
|
||||||
writeFile(tstDoc3m, "dummy")
|
writeFile(tstDoc3m, "stuff")
|
||||||
writeFile(tstDoc3b, "dummy")
|
writeFile(tstDoc3b, "stuff")
|
||||||
|
|
||||||
# Make the above fail
|
# Make the above fail
|
||||||
with monkeypatch.context() as mp:
|
with monkeypatch.context() as mp:
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ import os
|
|||||||
import sys
|
import sys
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from dummy import causeOSError
|
from mock import causeOSError
|
||||||
from tools import readFile, writeFile
|
from tools import readFile, writeFile
|
||||||
|
|
||||||
from nw.core.spellcheck import NWSpellCheck, NWSpellEnchant, NWSpellSimple
|
from nw.core.spellcheck import NWSpellCheck, NWSpellEnchant, NWSpellSimple
|
||||||
@@ -38,7 +38,7 @@ def testCoreSpell_Super(monkeypatch, tmpDir):
|
|||||||
|
|
||||||
spChk = NWSpellCheck()
|
spChk = NWSpellCheck()
|
||||||
|
|
||||||
# Check that dummy functions return results that reflects that spell
|
# Check that default functions return results that reflects that spell
|
||||||
# checking is effectively disabled
|
# checking is effectively disabled
|
||||||
assert spChk.setLanguage("", "") is None
|
assert spChk.setLanguage("", "") is None
|
||||||
assert spChk.checkWord("")
|
assert spChk.checkWord("")
|
||||||
@@ -47,7 +47,7 @@ def testCoreSpell_Super(monkeypatch, tmpDir):
|
|||||||
assert spChk.describeDict() == ("", "")
|
assert spChk.describeDict() == ("", "")
|
||||||
|
|
||||||
# Add a word to the user's dictionary
|
# Add a word to the user's dictionary
|
||||||
assert spChk._readProjectDictionary("dummy") is False
|
assert spChk._readProjectDictionary("stuff") is False
|
||||||
with monkeypatch.context() as mp:
|
with monkeypatch.context() as mp:
|
||||||
mp.setattr("builtins.open", causeOSError)
|
mp.setattr("builtins.open", causeOSError)
|
||||||
assert spChk._readProjectDictionary(wList) is False
|
assert spChk._readProjectDictionary(wList) is False
|
||||||
@@ -77,7 +77,7 @@ def testCoreSpell_Enchant(monkeypatch, tmpDir):
|
|||||||
wList = os.path.join(tmpDir, "wordlist.txt")
|
wList = os.path.join(tmpDir, "wordlist.txt")
|
||||||
writeFile(wList, "a_word\nb_word\nc_word\n")
|
writeFile(wList, "a_word\nb_word\nc_word\n")
|
||||||
|
|
||||||
# Block the enchant package (and trigger the dummy class)
|
# Block the enchant package (and trigger the default class)
|
||||||
with monkeypatch.context() as mp:
|
with monkeypatch.context() as mp:
|
||||||
mp.setitem(sys.modules, "enchant", None)
|
mp.setitem(sys.modules, "enchant", None)
|
||||||
spChk = NWSpellEnchant()
|
spChk = NWSpellEnchant()
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ def testCoreStatus_Entries():
|
|||||||
|
|
||||||
# Lookups
|
# Lookups
|
||||||
assert theStatus.lookupEntry(None) is None
|
assert theStatus.lookupEntry(None) is None
|
||||||
assert theStatus.lookupEntry("dummy") is None
|
assert theStatus.lookupEntry("stuff") is None
|
||||||
assert theStatus.lookupEntry("Main") == 3
|
assert theStatus.lookupEntry("Main") == 3
|
||||||
|
|
||||||
# Checks
|
# Checks
|
||||||
|
|||||||
@@ -145,13 +145,13 @@ def testCoreToken_TextOps(monkeypatch, nwMinimal, dummyGUI):
|
|||||||
assert theProject.saveProject()
|
assert theProject.saveProject()
|
||||||
|
|
||||||
# Root heading
|
# Root heading
|
||||||
assert theToken.addRootHeading("dummy") is False
|
assert theToken.addRootHeading("stuff") is False
|
||||||
assert theToken.addRootHeading(sHandle) is False
|
assert theToken.addRootHeading(sHandle) is False
|
||||||
assert theToken.addRootHeading("7695ce551d265") is True
|
assert theToken.addRootHeading("7695ce551d265") is True
|
||||||
assert theToken.theMarkdown[-1] == "# Notes: Plot\n\n"
|
assert theToken.theMarkdown[-1] == "# Notes: Plot\n\n"
|
||||||
|
|
||||||
# Set text
|
# Set text
|
||||||
assert theToken.setText("dummy") is False
|
assert theToken.setText("stuff") is False
|
||||||
assert theToken.setText(sHandle) is True
|
assert theToken.setText(sHandle) is True
|
||||||
assert theToken.theText == docText
|
assert theToken.theText == docText
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ from nw.constants import nwFiles
|
|||||||
|
|
||||||
@pytest.fixture(scope="function")
|
@pytest.fixture(scope="function")
|
||||||
def dummyItems(dummyGUI):
|
def dummyItems(dummyGUI):
|
||||||
"""Create a list of dummy items.
|
"""Create a list of mock items.
|
||||||
"""
|
"""
|
||||||
theProject = NWProject(dummyGUI)
|
theProject = NWProject(dummyGUI)
|
||||||
|
|
||||||
@@ -176,7 +176,7 @@ def testCoreTree_BuildTree(dummyGUI, dummyItems):
|
|||||||
assert len(theTree) == len(dummyItems) + 1
|
assert len(theTree) == len(dummyItems) + 1
|
||||||
|
|
||||||
# Delete a non-existing item
|
# Delete a non-existing item
|
||||||
del theTree["dummy"]
|
del theTree["stuff"]
|
||||||
assert len(theTree) == len(dummyItems) + 1
|
assert len(theTree) == len(dummyItems) + 1
|
||||||
|
|
||||||
# Delete the last item
|
# Delete the last item
|
||||||
@@ -214,7 +214,7 @@ def testCoreTree_Methods(dummyGUI, dummyItems):
|
|||||||
assert len(theTree) == len(dummyItems)
|
assert len(theTree) == len(dummyItems)
|
||||||
|
|
||||||
# Root item lookup
|
# Root item lookup
|
||||||
theTree._treeRoots.append("dummy")
|
theTree._treeRoots.append("stuff")
|
||||||
assert theTree.findRoot(nwItemClass.WORLD) is None
|
assert theTree.findRoot(nwItemClass.WORLD) is None
|
||||||
assert theTree.findRoot(nwItemClass.NOVEL) == "a000000000001"
|
assert theTree.findRoot(nwItemClass.NOVEL) == "a000000000001"
|
||||||
assert theTree.findRoot(nwItemClass.CHARACTER) == "a000000000004"
|
assert theTree.findRoot(nwItemClass.CHARACTER) == "a000000000004"
|
||||||
@@ -229,16 +229,16 @@ def testCoreTree_Methods(dummyGUI, dummyItems):
|
|||||||
assert theTree.getRootItem("b000000000001").itemHandle == "a000000000001"
|
assert theTree.getRootItem("b000000000001").itemHandle == "a000000000001"
|
||||||
assert theTree.getRootItem("c000000000001").itemHandle == "a000000000001"
|
assert theTree.getRootItem("c000000000001").itemHandle == "a000000000001"
|
||||||
assert theTree.getRootItem("c000000000002").itemHandle == "a000000000001"
|
assert theTree.getRootItem("c000000000002").itemHandle == "a000000000001"
|
||||||
assert theTree.getRootItem("dummy") is None
|
assert theTree.getRootItem("stuff") is None
|
||||||
|
|
||||||
# Get item path
|
# Get item path
|
||||||
assert theTree.getItemPath("dummy") == []
|
assert theTree.getItemPath("stuff") == []
|
||||||
assert theTree.getItemPath("c000000000001") == [
|
assert theTree.getItemPath("c000000000001") == [
|
||||||
"c000000000001", "b000000000001", "a000000000001"
|
"c000000000001", "b000000000001", "a000000000001"
|
||||||
]
|
]
|
||||||
|
|
||||||
# Break the folder parent handle
|
# Break the folder parent handle
|
||||||
theTree["b000000000001"].itemParent = "dummy"
|
theTree["b000000000001"].itemParent = "stuff"
|
||||||
assert theTree.getItemPath("c000000000001") == [
|
assert theTree.getItemPath("c000000000001") == [
|
||||||
"c000000000001", "b000000000001"
|
"c000000000001", "b000000000001"
|
||||||
]
|
]
|
||||||
@@ -249,7 +249,7 @@ def testCoreTree_Methods(dummyGUI, dummyItems):
|
|||||||
]
|
]
|
||||||
|
|
||||||
# Change file layout
|
# Change file layout
|
||||||
assert not theTree.setFileItemLayout("dummy", nwItemLayout.UNNUMBERED)
|
assert not theTree.setFileItemLayout("stuff", nwItemLayout.UNNUMBERED)
|
||||||
assert not theTree.setFileItemLayout("b000000000001", nwItemLayout.UNNUMBERED)
|
assert not theTree.setFileItemLayout("b000000000001", nwItemLayout.UNNUMBERED)
|
||||||
assert not theTree.setFileItemLayout("c000000000001", "stuff")
|
assert not theTree.setFileItemLayout("c000000000001", "stuff")
|
||||||
assert theTree.setFileItemLayout("c000000000001", nwItemLayout.UNNUMBERED)
|
assert theTree.setFileItemLayout("c000000000001", nwItemLayout.UNNUMBERED)
|
||||||
@@ -424,7 +424,7 @@ def testCoreTree_Stats(dummyGUI, dummyItems):
|
|||||||
theTree.append(tHandle, pHandle, nwItem)
|
theTree.append(tHandle, pHandle, nwItem)
|
||||||
|
|
||||||
assert len(theTree) == len(dummyItems)
|
assert len(theTree) == len(dummyItems)
|
||||||
theTree._treeOrder.append("dummy")
|
theTree._treeOrder.append("stuff")
|
||||||
|
|
||||||
# Count Words
|
# Count Words
|
||||||
novelWords, noteWords = theTree.sumWords()
|
novelWords, noteWords = theTree.sumWords()
|
||||||
@@ -461,10 +461,10 @@ def testCoreTree_Reorder(dummyGUI, dummyItems):
|
|||||||
theTree.setOrder(bHandle)
|
theTree.setOrder(bHandle)
|
||||||
assert theTree.handles() == bHandle
|
assert theTree.handles() == bHandle
|
||||||
|
|
||||||
theTree.setOrder(bHandle + ["dummy"])
|
theTree.setOrder(bHandle + ["stuff"])
|
||||||
assert theTree.handles() == bHandle
|
assert theTree.handles() == bHandle
|
||||||
|
|
||||||
theTree._treeOrder.append("dummy")
|
theTree._treeOrder.append("stuff")
|
||||||
theTree.setOrder(bHandle)
|
theTree.setOrder(bHandle)
|
||||||
assert theTree.handles() == bHandle
|
assert theTree.handles() == bHandle
|
||||||
|
|
||||||
@@ -536,7 +536,7 @@ def testCoreTree_ToCFile(monkeypatch, dummyGUI, dummyItems, tmpDir):
|
|||||||
theTree.append(tHandle, pHandle, nwItem)
|
theTree.append(tHandle, pHandle, nwItem)
|
||||||
|
|
||||||
assert len(theTree) == len(dummyItems)
|
assert len(theTree) == len(dummyItems)
|
||||||
theTree._treeOrder.append("dummy")
|
theTree._treeOrder.append("stuff")
|
||||||
|
|
||||||
def dummyIsFile(fileName):
|
def dummyIsFile(fileName):
|
||||||
"""Return True for items that are files in novelWriter and
|
"""Return True for items that are files in novelWriter and
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ from PyQt5.QtCore import Qt
|
|||||||
from PyQt5.QtWidgets import QDialog, QMessageBox, QAction
|
from PyQt5.QtWidgets import QDialog, QMessageBox, QAction
|
||||||
|
|
||||||
from tools import writeFile, readFile, getGuiItem
|
from tools import writeFile, readFile, getGuiItem
|
||||||
from dummy import causeOSError
|
from mock import causeOSError
|
||||||
|
|
||||||
from nw.dialogs import GuiWordList
|
from nw.dialogs import GuiWordList
|
||||||
from nw.constants import nwFiles
|
from nw.constants import nwFiles
|
||||||
|
|||||||
@@ -249,14 +249,14 @@ def testGuiEditor_Main(qtbot, monkeypatch, nwGUI, fncDir, fncProj, refDir, outDi
|
|||||||
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
|
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
|
||||||
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
|
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
|
||||||
|
|
||||||
for c in "This is a paragraph of dummy text.":
|
for c in "This is a paragraph of nonsense text.":
|
||||||
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
|
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
|
||||||
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
|
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
|
||||||
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
|
qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay)
|
||||||
|
|
||||||
for c in (
|
for c in (
|
||||||
"This is another paragraph of much longer dummy text. "
|
"This is another paragraph of much longer nonsense text. "
|
||||||
"It is in fact 1 very very DUMB dummy text! "
|
"It is in fact 1 very very NONSENSICAL nonsense text! "
|
||||||
):
|
):
|
||||||
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
|
qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay)
|
||||||
for c in "We can also try replacing \"quotes\", even single 'quotes' are replaced. ":
|
for c in "We can also try replacing \"quotes\", even single 'quotes' are replaced. ":
|
||||||
|
|||||||
@@ -165,7 +165,7 @@ def testGuiTheme_Main(qtbot, monkeypatch, nwMinimal, tmpDir):
|
|||||||
assert isinstance(anIcon, QIcon)
|
assert isinstance(anIcon, QIcon)
|
||||||
assert not anIcon.isNull()
|
assert not anIcon.isNull()
|
||||||
|
|
||||||
# Add dummy icons and test alternative load paths
|
# Add test icons and test alternative load paths
|
||||||
theIcons.ICON_MAP["testicon1"] = (QStyle.SP_DriveHDIcon, None)
|
theIcons.ICON_MAP["testicon1"] = (QStyle.SP_DriveHDIcon, None)
|
||||||
anIcon = theIcons.getIcon("testicon1")
|
anIcon = theIcons.getIcon("testicon1")
|
||||||
assert isinstance(anIcon, QIcon)
|
assert isinstance(anIcon, QIcon)
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import json
|
|||||||
import os
|
import os
|
||||||
|
|
||||||
from tools import getGuiItem, writeFile
|
from tools import getGuiItem, writeFile
|
||||||
from dummy import causeOSError
|
from mock import causeOSError
|
||||||
|
|
||||||
from PyQt5.QtCore import Qt
|
from PyQt5.QtCore import Qt
|
||||||
from PyQt5.QtWidgets import QAction, QFileDialog, QMessageBox
|
from PyQt5.QtWidgets import QAction, QFileDialog, QMessageBox
|
||||||
|
|||||||
Reference in New Issue
Block a user