Clean up Markdown build code
This commit is contained in:
@@ -97,7 +97,6 @@ SETTINGS_TEMPLATE = {
|
|||||||
"odt.addColours": (bool, True),
|
"odt.addColours": (bool, True),
|
||||||
"odt.pageHeader": (str, nwHeadFmt.ODT_AUTO),
|
"odt.pageHeader": (str, nwHeadFmt.ODT_AUTO),
|
||||||
"odt.pageCountOffset": (int, 0),
|
"odt.pageCountOffset": (int, 0),
|
||||||
"md.preserveBreaks": (bool, True),
|
|
||||||
"html.addStyles": (bool, True),
|
"html.addStyles": (bool, True),
|
||||||
"html.preserveTabs": (bool, False),
|
"html.preserveTabs": (bool, False),
|
||||||
}
|
}
|
||||||
@@ -155,9 +154,6 @@ SETTINGS_LABELS = {
|
|||||||
"odt.pageHeader": QT_TRANSLATE_NOOP("Builds", "Page Header"),
|
"odt.pageHeader": QT_TRANSLATE_NOOP("Builds", "Page Header"),
|
||||||
"odt.pageCountOffset": QT_TRANSLATE_NOOP("Builds", "Page Counter Offset"),
|
"odt.pageCountOffset": QT_TRANSLATE_NOOP("Builds", "Page Counter Offset"),
|
||||||
|
|
||||||
"md": QT_TRANSLATE_NOOP("Builds", "Markdown (.md)"),
|
|
||||||
"md.preserveBreaks": QT_TRANSLATE_NOOP("Builds", "Preserve Hard Line Breaks"),
|
|
||||||
|
|
||||||
"html": QT_TRANSLATE_NOOP("Builds", "HTML (.html)"),
|
"html": QT_TRANSLATE_NOOP("Builds", "HTML (.html)"),
|
||||||
"html.addStyles": QT_TRANSLATE_NOOP("Builds", "Add CSS Styles"),
|
"html.addStyles": QT_TRANSLATE_NOOP("Builds", "Add CSS Styles"),
|
||||||
"html.preserveTabs": QT_TRANSLATE_NOOP("Builds", "Preserve Tab Characters"),
|
"html.preserveTabs": QT_TRANSLATE_NOOP("Builds", "Preserve Tab Characters"),
|
||||||
|
|||||||
@@ -216,16 +216,10 @@ class NWBuildDocument:
|
|||||||
makeObj = ToMarkdown(self._project)
|
makeObj = ToMarkdown(self._project)
|
||||||
filtered = self._setupBuild(makeObj)
|
filtered = self._setupBuild(makeObj)
|
||||||
|
|
||||||
if extendedMd:
|
makeObj.setExtendedMarkdown(extendedMd)
|
||||||
makeObj.setExtendedMarkdown()
|
|
||||||
else:
|
|
||||||
makeObj.setStandardMarkdown()
|
|
||||||
|
|
||||||
if self._build.getBool("format.replaceTabs"):
|
if self._build.getBool("format.replaceTabs"):
|
||||||
makeObj.replaceTabs(nSpaces=4, spaceChar=" ")
|
makeObj.replaceTabs(nSpaces=4, spaceChar=" ")
|
||||||
|
|
||||||
makeObj.setPreserveBreaks(self._build.getBool("md.preserveBreaks"))
|
|
||||||
|
|
||||||
for i, tHandle in enumerate(self._queue):
|
for i, tHandle in enumerate(self._queue):
|
||||||
self._error = None
|
self._error = None
|
||||||
if filtered.get(tHandle, (False, 0))[0]:
|
if filtered.get(tHandle, (False, 0))[0]:
|
||||||
@@ -333,6 +327,7 @@ class NWBuildDocument:
|
|||||||
bldObj.setFont(fontFamily, textSize, textFixed)
|
bldObj.setFont(fontFamily, textSize, textFixed)
|
||||||
bldObj.setJustify(self._build.getBool("format.justifyText"))
|
bldObj.setJustify(self._build.getBool("format.justifyText"))
|
||||||
bldObj.setLineHeight(self._build.getFloat("format.lineHeight"))
|
bldObj.setLineHeight(self._build.getFloat("format.lineHeight"))
|
||||||
|
bldObj.setKeepLineBreaks(self._build.getBool("format.keepBreaks"))
|
||||||
bldObj.setFirstLineIndent(
|
bldObj.setFirstLineIndent(
|
||||||
self._build.getBool("format.firstLineIndent"),
|
self._build.getBool("format.firstLineIndent"),
|
||||||
self._build.getFloat("format.firstIndentWidth"),
|
self._build.getFloat("format.firstIndentWidth"),
|
||||||
|
|||||||
@@ -81,15 +81,11 @@ class ToMarkdown(Tokenizer):
|
|||||||
supports concatenating novelWriter markup files.
|
supports concatenating novelWriter markup files.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
M_STD = 0 # Standard Markdown
|
|
||||||
M_EXT = 1 # Extended Markdown
|
|
||||||
|
|
||||||
def __init__(self, project: NWProject) -> None:
|
def __init__(self, project: NWProject) -> None:
|
||||||
super().__init__(project)
|
super().__init__(project)
|
||||||
self._genMode = self.M_STD
|
|
||||||
self._fullMD: list[str] = []
|
self._fullMD: list[str] = []
|
||||||
self._preserveBreaks = True
|
|
||||||
self._usedNotes: dict[str, int] = {}
|
self._usedNotes: dict[str, int] = {}
|
||||||
|
self._extended = True
|
||||||
return
|
return
|
||||||
|
|
||||||
##
|
##
|
||||||
@@ -105,19 +101,9 @@ class ToMarkdown(Tokenizer):
|
|||||||
# Setters
|
# Setters
|
||||||
##
|
##
|
||||||
|
|
||||||
def setStandardMarkdown(self) -> None:
|
def setExtendedMarkdown(self, state: bool) -> None:
|
||||||
"""Set the converter to use standard Markdown formatting."""
|
|
||||||
self._genMode = self.M_STD
|
|
||||||
return
|
|
||||||
|
|
||||||
def setExtendedMarkdown(self) -> None:
|
|
||||||
"""Set the converter to use Extended Markdown formatting."""
|
"""Set the converter to use Extended Markdown formatting."""
|
||||||
self._genMode = self.M_EXT
|
self._extended = state
|
||||||
return
|
|
||||||
|
|
||||||
def setPreserveBreaks(self, state: bool) -> None:
|
|
||||||
"""Preserve line breaks in paragraphs."""
|
|
||||||
self._preserveBreaks = state
|
|
||||||
return
|
return
|
||||||
|
|
||||||
##
|
##
|
||||||
@@ -132,12 +118,12 @@ class ToMarkdown(Tokenizer):
|
|||||||
"""Convert the list of text tokens into a Markdown document."""
|
"""Convert the list of text tokens into a Markdown document."""
|
||||||
self._result = ""
|
self._result = ""
|
||||||
|
|
||||||
if self._genMode == self.M_STD:
|
if self._extended:
|
||||||
mTags = STD_MD
|
|
||||||
cSkip = ""
|
|
||||||
else:
|
|
||||||
mTags = EXT_MD
|
mTags = EXT_MD
|
||||||
cSkip = nwUnicode.U_MMSP
|
cSkip = nwUnicode.U_MMSP
|
||||||
|
else:
|
||||||
|
mTags = STD_MD
|
||||||
|
cSkip = ""
|
||||||
|
|
||||||
lines = []
|
lines = []
|
||||||
for tType, _, tText, tFormat, tStyle in self._tokens:
|
for tType, _, tText, tFormat, tStyle in self._tokens:
|
||||||
@@ -195,7 +181,7 @@ class ToMarkdown(Tokenizer):
|
|||||||
def appendFootnotes(self) -> None:
|
def appendFootnotes(self) -> None:
|
||||||
"""Append the footnotes in the buffer."""
|
"""Append the footnotes in the buffer."""
|
||||||
if self._usedNotes:
|
if self._usedNotes:
|
||||||
tags = STD_MD if self._genMode == self.M_STD else EXT_MD
|
tags = EXT_MD if self._extended else STD_MD
|
||||||
footnotes = self._localLookup("Footnotes")
|
footnotes = self._localLookup("Footnotes")
|
||||||
|
|
||||||
lines = []
|
lines = []
|
||||||
|
|||||||
@@ -1383,12 +1383,6 @@ class _OutputTab(NScrollableForm):
|
|||||||
self.htmlPreserveTabs = NSwitch(self, height=iPx)
|
self.htmlPreserveTabs = NSwitch(self, height=iPx)
|
||||||
self.addRow(self._build.getLabel("html.preserveTabs"), self.htmlPreserveTabs)
|
self.addRow(self._build.getLabel("html.preserveTabs"), self.htmlPreserveTabs)
|
||||||
|
|
||||||
# Markdown Document
|
|
||||||
self.addGroupLabel(self._build.getLabel("md"))
|
|
||||||
|
|
||||||
self.mdPreserveBreaks = NSwitch(self, height=iPx)
|
|
||||||
self.addRow(self._build.getLabel("md.preserveBreaks"), self.mdPreserveBreaks)
|
|
||||||
|
|
||||||
# Finalise
|
# Finalise
|
||||||
self.finalise()
|
self.finalise()
|
||||||
|
|
||||||
@@ -1401,7 +1395,6 @@ class _OutputTab(NScrollableForm):
|
|||||||
self.odtPageCountOffset.setValue(self._build.getInt("odt.pageCountOffset"))
|
self.odtPageCountOffset.setValue(self._build.getInt("odt.pageCountOffset"))
|
||||||
self.htmlAddStyles.setChecked(self._build.getBool("html.addStyles"))
|
self.htmlAddStyles.setChecked(self._build.getBool("html.addStyles"))
|
||||||
self.htmlPreserveTabs.setChecked(self._build.getBool("html.preserveTabs"))
|
self.htmlPreserveTabs.setChecked(self._build.getBool("html.preserveTabs"))
|
||||||
self.mdPreserveBreaks.setChecked(self._build.getBool("md.preserveBreaks"))
|
|
||||||
self.odtPageHeader.setCursorPosition(0)
|
self.odtPageHeader.setCursorPosition(0)
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -1412,7 +1405,6 @@ class _OutputTab(NScrollableForm):
|
|||||||
self._build.setValue("odt.pageCountOffset", self.odtPageCountOffset.value())
|
self._build.setValue("odt.pageCountOffset", self.odtPageCountOffset.value())
|
||||||
self._build.setValue("html.addStyles", self.htmlAddStyles.isChecked())
|
self._build.setValue("html.addStyles", self.htmlAddStyles.isChecked())
|
||||||
self._build.setValue("html.preserveTabs", self.htmlPreserveTabs.isChecked())
|
self._build.setValue("html.preserveTabs", self.htmlPreserveTabs.isChecked())
|
||||||
self._build.setValue("md.preserveBreaks", self.mdPreserveBreaks.isChecked())
|
|
||||||
return
|
return
|
||||||
|
|
||||||
##
|
##
|
||||||
|
|||||||
@@ -316,7 +316,6 @@ def testCoreToHtml_ConvertDirect(mockGUI):
|
|||||||
# Title
|
# Title
|
||||||
html._tokens = [
|
html._tokens = [
|
||||||
(html.T_TITLE, 1, "A Title", [], html.A_PBB | html.A_CENTRE),
|
(html.T_TITLE, 1, "A Title", [], html.A_PBB | html.A_CENTRE),
|
||||||
(html.T_EMPTY, 1, "", [], html.A_NONE),
|
|
||||||
]
|
]
|
||||||
html.doConvert()
|
html.doConvert()
|
||||||
assert html.result == (
|
assert html.result == (
|
||||||
@@ -327,7 +326,6 @@ def testCoreToHtml_ConvertDirect(mockGUI):
|
|||||||
# Unnumbered
|
# Unnumbered
|
||||||
html._tokens = [
|
html._tokens = [
|
||||||
(html.T_HEAD2, 1, "Prologue", [], html.A_PBB),
|
(html.T_HEAD2, 1, "Prologue", [], html.A_PBB),
|
||||||
(html.T_EMPTY, 1, "", [], html.A_NONE),
|
|
||||||
]
|
]
|
||||||
html.doConvert()
|
html.doConvert()
|
||||||
assert html.result == (
|
assert html.result == (
|
||||||
@@ -341,7 +339,6 @@ def testCoreToHtml_ConvertDirect(mockGUI):
|
|||||||
# Separator
|
# Separator
|
||||||
html._tokens = [
|
html._tokens = [
|
||||||
(html.T_SEP, 1, "* * *", [], html.A_CENTRE),
|
(html.T_SEP, 1, "* * *", [], html.A_CENTRE),
|
||||||
(html.T_EMPTY, 1, "", [], html.A_NONE),
|
|
||||||
]
|
]
|
||||||
html.doConvert()
|
html.doConvert()
|
||||||
assert html.result == "<p class='sep' style='text-align: center;'>* * *</p>\n"
|
assert html.result == "<p class='sep' style='text-align: center;'>* * *</p>\n"
|
||||||
@@ -349,7 +346,6 @@ def testCoreToHtml_ConvertDirect(mockGUI):
|
|||||||
# Skip
|
# Skip
|
||||||
html._tokens = [
|
html._tokens = [
|
||||||
(html.T_SKIP, 1, "", [], html.A_NONE),
|
(html.T_SKIP, 1, "", [], html.A_NONE),
|
||||||
(html.T_EMPTY, 1, "", [], html.A_NONE),
|
|
||||||
]
|
]
|
||||||
html.doConvert()
|
html.doConvert()
|
||||||
assert html.result == "<p class='skip'> </p>\n"
|
assert html.result == "<p class='skip'> </p>\n"
|
||||||
@@ -425,8 +421,7 @@ def testCoreToHtml_ConvertDirect(mockGUI):
|
|||||||
|
|
||||||
# Indent Left
|
# Indent Left
|
||||||
html._tokens = [
|
html._tokens = [
|
||||||
(html.T_TEXT, 1, "Some text ...", [], html.A_IND_L),
|
(html.T_TEXT, 1, "Some text ...", [], html.A_IND_L),
|
||||||
(html.T_EMPTY, 2, "", [], html.A_NONE),
|
|
||||||
]
|
]
|
||||||
html.doConvert()
|
html.doConvert()
|
||||||
assert html.result == (
|
assert html.result == (
|
||||||
@@ -435,8 +430,7 @@ def testCoreToHtml_ConvertDirect(mockGUI):
|
|||||||
|
|
||||||
# Indent Right
|
# Indent Right
|
||||||
html._tokens = [
|
html._tokens = [
|
||||||
(html.T_TEXT, 1, "Some text ...", [], html.A_IND_R),
|
(html.T_TEXT, 1, "Some text ...", [], html.A_IND_R),
|
||||||
(html.T_EMPTY, 2, "", [], html.A_NONE),
|
|
||||||
]
|
]
|
||||||
html.doConvert()
|
html.doConvert()
|
||||||
assert html.result == (
|
assert html.result == (
|
||||||
|
|||||||
@@ -1835,7 +1835,7 @@ def testCoreToken_SceneSeparators(mockGUI):
|
|||||||
# ===========================
|
# ===========================
|
||||||
# Requires a fresh builder class
|
# Requires a fresh builder class
|
||||||
md = ToMarkdown(project)
|
md = ToMarkdown(project)
|
||||||
md.setExtendedMarkdown()
|
md.setExtendedMarkdown(True)
|
||||||
md._isNovel = True
|
md._isNovel = True
|
||||||
|
|
||||||
md._text = (
|
md._text = (
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ def testCoreToMarkdown_ConvertParagraphs(mockGUI):
|
|||||||
toMD._isFirst = True
|
toMD._isFirst = True
|
||||||
|
|
||||||
# Text for Extended Markdown
|
# Text for Extended Markdown
|
||||||
toMD.setExtendedMarkdown()
|
toMD.setExtendedMarkdown(True)
|
||||||
toMD._text = "Some **nested bold and _italic_ and ~~strikethrough~~ text** here\n"
|
toMD._text = "Some **nested bold and _italic_ and ~~strikethrough~~ text** here\n"
|
||||||
toMD.tokenizeText()
|
toMD.tokenizeText()
|
||||||
toMD.doConvert()
|
toMD.doConvert()
|
||||||
@@ -91,7 +91,7 @@ def testCoreToMarkdown_ConvertParagraphs(mockGUI):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Text for Standard Markdown
|
# Text for Standard Markdown
|
||||||
toMD.setStandardMarkdown()
|
toMD.setExtendedMarkdown(False)
|
||||||
toMD._text = "Some **nested bold and _italic_ and ~~strikethrough~~ text** here\n"
|
toMD._text = "Some **nested bold and _italic_ and ~~strikethrough~~ text** here\n"
|
||||||
toMD.tokenizeText()
|
toMD.tokenizeText()
|
||||||
toMD.doConvert()
|
toMD.doConvert()
|
||||||
@@ -100,7 +100,7 @@ def testCoreToMarkdown_ConvertParagraphs(mockGUI):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Shortcodes for Extended Markdown
|
# Shortcodes for Extended Markdown
|
||||||
toMD.setExtendedMarkdown()
|
toMD.setExtendedMarkdown(True)
|
||||||
toMD._text = (
|
toMD._text = (
|
||||||
"Some [b]bold[/b], [i]italic[/i], [s]strike[/s], [u]underline[/u], [m]mark[/m], "
|
"Some [b]bold[/b], [i]italic[/i], [s]strike[/s], [u]underline[/u], [m]mark[/m], "
|
||||||
"super[sup]script[/sup], sub[sub]script[/sub] here\n"
|
"super[sup]script[/sup], sub[sub]script[/sub] here\n"
|
||||||
@@ -113,7 +113,7 @@ def testCoreToMarkdown_ConvertParagraphs(mockGUI):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Shortcodes for Standard Markdown
|
# Shortcodes for Standard Markdown
|
||||||
toMD.setStandardMarkdown()
|
toMD.setExtendedMarkdown(False)
|
||||||
toMD._text = (
|
toMD._text = (
|
||||||
"Some [b]bold[/b], [i]italic[/i], [s]strike[/s], [u]underline[/u], [m]mark[/m], "
|
"Some [b]bold[/b], [i]italic[/i], [s]strike[/s], [u]underline[/u], [m]mark[/m], "
|
||||||
"super[sup]script[/sup], sub[sub]script[/sub] here\n"
|
"super[sup]script[/sup], sub[sub]script[/sub] here\n"
|
||||||
@@ -215,6 +215,7 @@ def testCoreToMarkdown_ConvertDirect(mockGUI):
|
|||||||
toMD = ToMarkdown(project)
|
toMD = ToMarkdown(project)
|
||||||
|
|
||||||
toMD._isNovel = True
|
toMD._isNovel = True
|
||||||
|
toMD.setExtendedMarkdown(False)
|
||||||
|
|
||||||
# Special Titles
|
# Special Titles
|
||||||
# ==============
|
# ==============
|
||||||
@@ -222,7 +223,6 @@ def testCoreToMarkdown_ConvertDirect(mockGUI):
|
|||||||
# Title
|
# Title
|
||||||
toMD._tokens = [
|
toMD._tokens = [
|
||||||
(toMD.T_TITLE, 1, "A Title", [], toMD.A_PBB | toMD.A_CENTRE),
|
(toMD.T_TITLE, 1, "A Title", [], toMD.A_PBB | toMD.A_CENTRE),
|
||||||
(toMD.T_EMPTY, 1, "", [], toMD.A_NONE),
|
|
||||||
]
|
]
|
||||||
toMD.doConvert()
|
toMD.doConvert()
|
||||||
assert toMD.result == "# A Title\n\n"
|
assert toMD.result == "# A Title\n\n"
|
||||||
@@ -233,7 +233,6 @@ def testCoreToMarkdown_ConvertDirect(mockGUI):
|
|||||||
# Separator
|
# Separator
|
||||||
toMD._tokens = [
|
toMD._tokens = [
|
||||||
(toMD.T_SEP, 1, "* * *", [], toMD.A_CENTRE),
|
(toMD.T_SEP, 1, "* * *", [], toMD.A_CENTRE),
|
||||||
(toMD.T_EMPTY, 1, "", [], toMD.A_NONE),
|
|
||||||
]
|
]
|
||||||
toMD.doConvert()
|
toMD.doConvert()
|
||||||
assert toMD.result == "* * *\n\n"
|
assert toMD.result == "* * *\n\n"
|
||||||
@@ -241,7 +240,6 @@ def testCoreToMarkdown_ConvertDirect(mockGUI):
|
|||||||
# Skip
|
# Skip
|
||||||
toMD._tokens = [
|
toMD._tokens = [
|
||||||
(toMD.T_SKIP, 1, "", [], toMD.A_NONE),
|
(toMD.T_SKIP, 1, "", [], toMD.A_NONE),
|
||||||
(toMD.T_EMPTY, 1, "", [], toMD.A_NONE),
|
|
||||||
]
|
]
|
||||||
toMD.doConvert()
|
toMD.doConvert()
|
||||||
assert toMD.result == "\n\n"
|
assert toMD.result == "\n\n"
|
||||||
|
|||||||
@@ -683,7 +683,6 @@ def testCoreToOdt_ConvertDirect(mockGUI):
|
|||||||
doc = ToOdt(project, isFlat=True)
|
doc = ToOdt(project, isFlat=True)
|
||||||
doc._tokens = [
|
doc._tokens = [
|
||||||
(doc.T_TEXT, 1, "This is a paragraph", [], doc.A_JUSTIFY),
|
(doc.T_TEXT, 1, "This is a paragraph", [], doc.A_JUSTIFY),
|
||||||
(doc.T_EMPTY, 1, "", [], doc.A_NONE),
|
|
||||||
]
|
]
|
||||||
doc.initDocument()
|
doc.initDocument()
|
||||||
doc.doConvert()
|
doc.doConvert()
|
||||||
@@ -704,7 +703,6 @@ def testCoreToOdt_ConvertDirect(mockGUI):
|
|||||||
doc = ToOdt(project, isFlat=True)
|
doc = ToOdt(project, isFlat=True)
|
||||||
doc._tokens = [
|
doc._tokens = [
|
||||||
(doc.T_TEXT, 1, "This is a paragraph", [], doc.A_PBA),
|
(doc.T_TEXT, 1, "This is a paragraph", [], doc.A_PBA),
|
||||||
(doc.T_EMPTY, 1, "", [], doc.A_NONE),
|
|
||||||
]
|
]
|
||||||
doc.initDocument()
|
doc.initDocument()
|
||||||
doc.doConvert()
|
doc.doConvert()
|
||||||
|
|||||||
@@ -677,13 +677,11 @@ def testBuildSettings_Output(qtbot, nwGUI):
|
|||||||
assert outTab.odtPageCountOffset.value() == 0
|
assert outTab.odtPageCountOffset.value() == 0
|
||||||
assert outTab.htmlAddStyles.isChecked() is False
|
assert outTab.htmlAddStyles.isChecked() is False
|
||||||
assert outTab.htmlPreserveTabs.isChecked() is False
|
assert outTab.htmlPreserveTabs.isChecked() is False
|
||||||
assert outTab.mdPreserveBreaks.isChecked() is True
|
|
||||||
|
|
||||||
# Toggle all
|
# Toggle all
|
||||||
outTab.odtAddColours.setChecked(True)
|
outTab.odtAddColours.setChecked(True)
|
||||||
outTab.htmlAddStyles.setChecked(True)
|
outTab.htmlAddStyles.setChecked(True)
|
||||||
outTab.htmlPreserveTabs.setChecked(True)
|
outTab.htmlPreserveTabs.setChecked(True)
|
||||||
outTab.mdPreserveBreaks.setChecked(False)
|
|
||||||
|
|
||||||
# Change Values
|
# Change Values
|
||||||
outTab.odtPageCountOffset.setValue(1)
|
outTab.odtPageCountOffset.setValue(1)
|
||||||
@@ -697,7 +695,6 @@ def testBuildSettings_Output(qtbot, nwGUI):
|
|||||||
assert build.getInt("odt.pageCountOffset") == 1
|
assert build.getInt("odt.pageCountOffset") == 1
|
||||||
assert build.getBool("html.addStyles") is True
|
assert build.getBool("html.addStyles") is True
|
||||||
assert build.getBool("html.preserveTabs") is True
|
assert build.getBool("html.preserveTabs") is True
|
||||||
assert build.getBool("md.preserveBreaks") is False
|
|
||||||
|
|
||||||
# Reset header format
|
# Reset header format
|
||||||
outTab.btnPageHeader.click()
|
outTab.btnPageHeader.click()
|
||||||
|
|||||||
Reference in New Issue
Block a user