diff --git a/.github/workflows/pylint.yml b/.github/workflows/pylint.yml index bffc0b4..6399503 100644 --- a/.github/workflows/pylint.yml +++ b/.github/workflows/pylint.yml @@ -18,6 +18,7 @@ jobs: run: | python -m pip install --upgrade pip pip install pylint + pip install pytest - name: Analysing the code with pylint run: | pylint $(git ls-files '*.py') diff --git a/src/markdown_helper/__init__.py b/src/markdown_helper/__init__.py index b60ff6b..35ba930 100644 --- a/src/markdown_helper/__init__.py +++ b/src/markdown_helper/__init__.py @@ -109,6 +109,8 @@ def sort_table(self, disable_convert: bool = False): # If multiple sort keys are provided, prioritize the first one, then the second, etc. sort_keys = self.sort_key.split(",") for sort_key in sort_keys: + if sort_key not in self.headers: + raise ValueError(f"sort_key {sort_key} not in headers") if disable_convert: self.rows = sorted( self.rows, @@ -243,8 +245,10 @@ def __str__(self): return self.html() return self.markdown() def __repr__(self): - return f"""Image(url={self.url}, title={self.title}, alt={self.alt}, - width={self.width}, height={self.height}, align={self.align}, caption={self.caption})""" + return_string = f"Image(url={self.url}, title={self.title}, alt={self.alt}" + return_string += f", width={self.width}, height={self.height}, align={self.align}," + return_string +=f"caption={self.caption})" + return return_string class Link: diff --git a/tests/test_markdown.py b/tests/test_markdown.py index 298f826..e2afb3a 100644 --- a/tests/test_markdown.py +++ b/tests/test_markdown.py @@ -1,13 +1,18 @@ """ This file contains the pytest tests for the markdown_helper.py file. """ +import pytest try: from src import markdown_helper as markdown except ModuleNotFoundError: try: import markdown_helper as markdown except ModuleNotFoundError: - from ..src import markdown_helper as markdown # pylint: disable=import-error, relative-beyond-top-level + from ..src import ( # pylint: disable=relative-beyond-top-level + markdown_helper as markdown, # pylint: disable=import-error + ) # pylint: disable=relative-beyond-top-level + + def test_ordered_list(): """ This function tests the markdown.List class. @@ -18,6 +23,16 @@ def test_ordered_list(): ), "String representation of ordered list is incorrect." assert list_1.items == ["item 1", "item 2", "item 3"], "List items are incorrect." +def test_empty_list(): + """ + This function tests the markdown.List class. + """ + list_1: markdown.List = markdown.List([], ordered=True) + assert ( + str(list_1) == "" + ), "String representation of ordered list is incorrect." + assert list_1.items == [], "List items are incorrect." + assert repr(list_1) == "List(title=False, items=[], ordered=True)", "List items are incorrect." def test_modify_list(): """ @@ -69,6 +84,9 @@ def test_link(): ), "String representation of link is incorrect." assert link_1.url == "http://www.google.com", "Link URL is incorrect." assert link_1.text == "Google", "Link text is incorrect." + assert ( + repr(link_1) == "Link(url=http://www.google.com, text=Google, title=False, new_tab=False)" + ) def test_link_no_trailing(): @@ -119,6 +137,22 @@ def test_image(): assert image_1.alt == "Google", "Image text is incorrect." +def test_image_texts(): + """ + This function tests the markdown.Image class. + """ + image_1 = markdown.Image( + "http://www.google.com", alt="Google", title="Title", caption="Caption" + ) + assert ( + image_1.markdown() == "### Title\n![Google](http://www.google.com)\n_Caption_\n" + ), "Markdown string representation of image is incorrect." + assert ( + repr(image_1) + == "Image(url=http://www.google.com, title=Title, alt=Google, width=False, height=False, align=False,caption=Caption)" # pylint: disable=line-too-long + ), "String representation of image is incorrect." + + def test_image_size(): """ This function tests the markdown.Image class. @@ -182,6 +216,7 @@ def test_table_add_row(): {"col 1": "item 1", "col 2": "item 2", "col 3": "item 3"} ], "Table rows are incorrect." + def test_table_add_row_no_headers(): """ This function tests the table.add_row function with a list input. @@ -202,6 +237,23 @@ def test_table_add_row_no_headers(): ], "Table rows are incorrect." +def test_table_title(): + """ + This function tests the markdown.Table class. + """ + table_1 = markdown.Table(["col 1", "col 2", "col 3"], title="My Table") + table_1.add_row({"col 1": "value 1", "col 2": "value 2", "col 3": "value 3"}) + assert ( + str(table_1) + == "### My Table\n| col 1 | col 2 | col 3 |\n| --- | --- | --- |\n| value 1 | value 2 | value 3 |\n" # pylint: disable=line-too-long + ), "String representation of table is incorrect." + assert table_1.headers == [ + "col 1", + "col 2", + "col 3", + ], "Table columns are incorrect." + assert table_1.title == "My Table", "Table title is incorrect." + def test_table_sort(): """ @@ -214,21 +266,63 @@ def test_table_sort(): table_1.add_row({"Name": "Third", "Value": 3}) assert ( str(table_1) - == "| Name | Value |\n| --- | --- |\n| First | 1 |\n| Second | 2 |\n| Third | 3 |\n| Fourth | 4 |\n" # pylint: disable=line-too-long + == "| Name | Value |\n| --- | --- |\n| First | 1 |\n| Second | 2 |\n| Third | 3 |\n| Fourth | 4 |\n" # pylint: disable=line-too-long ), "Sorted Table is incorrect." assert table_1.headers == ["Name", "Value"], "Table columns are incorrect." table_1.sort_reverse = True assert ( str(table_1) - == "| Name | Value |\n| --- | --- |\n| Fourth | 4 |\n| Third | 3 |\n| Second | 2 |\n| First | 1 |\n" # pylint: disable=line-too-long + == "| Name | Value |\n| --- | --- |\n| Fourth | 4 |\n| Third | 3 |\n| Second | 2 |\n| First | 1 |\n" # pylint: disable=line-too-long ), "Reverse sorted Table is incorrect." table_1.sort_reverse = False table_1.sort_key = "Name" assert ( str(table_1) - == "| Name | Value |\n| --- | --- |\n| First | 1 |\n| Fourth | 4 |\n| Second | 2 |\n| Third | 3 |\n" # pylint: disable=line-too-long + == "| Name | Value |\n| --- | --- |\n| First | 1 |\n| Fourth | 4 |\n| Second | 2 |\n| Third | 3 |\n" # pylint: disable=line-too-long ), "Second sorted Table is incorrect." +def test_table_sort_error(): + """ + This function tests the markdown.Table class. + """ + table_1 = markdown.Table(["Name", "Value"], sort_key="Value") + table_1.add_row({"Name": "First", "Value": 1}) + table_1.add_row({"Name": "Second", "Value": False}) + table_1.add_row({"Name": "Fourth", "Value": 4}) + table_1.add_row({"Name": "Third", "Value": "String"}) + with pytest.raises(ValueError): + table_1.sort_key = "invalid" + table_1.sort_table() + +def test_table_disable_convert(): + """ + This function tests the markdown.Table class. + """ + table_1 = markdown.Table(["Name", "Value"], sort_key="Value") + table_1.add_row({"Name": "First", "Value": 1}) + table_1.add_row({"Name": "Second", "Value": 0}) + table_1.add_row({"Name": "Fourth", "Value": 1}) + table_1.add_row({"Name": "Third", "Value": 0}) + table_1.sort_table(True) + assert ( + table_1.rows == [{'Name': 'Second', 'Value': 0}, {'Name': 'Third', 'Value': 0}, + {'Name': 'First', 'Value': 1}, {'Name': 'Fourth', 'Value': 1}] + ), "Sorted Table is incorrect." + +def test_table_sort_convert(): + """ + This function tests the markdown.Table class. + """ + table_1 = markdown.Table(["Name", "Value"], sort_key="Value") + table_1.add_row({"Name": "First", "Value": 1}) + table_1.add_row({"Name": "Second", "Value": 0}) + table_1.add_row({"Name": "Fourth", "Value": 1}) + table_1.add_row({"Name": "Third", "Value": 0}) + table_1.sort_table() + assert ( + table_1.rows == [{'Name': 'Second', 'Value': False}, {'Name': 'Third', 'Value': False}, + {'Name': 'First', 'Value': True}, {'Name': 'Fourth', 'Value': True}] + ), "Sorted Table is incorrect." def test_table_flexible(): """ @@ -241,11 +335,55 @@ def test_table_flexible(): table_1.add_row({"Name": "Fourth", "Value": 4}) assert ( str(table_1) - == "| Name | Value | Extra |\n| --- | --- | --- |\n| First | 1 | |\n| Second | 2 | |\n| Third | 3 | Extra Value |\n| Fourth | 4 | |\n" # pylint: disable=line-too-long + == "| Name | Value | Extra |\n| --- | --- | --- |\n| First | 1 | |\n| Second | 2 | |\n| Third | 3 | Extra Value |\n| Fourth | 4 | |\n" # pylint: disable=line-too-long ), "Flexible Table is incorrect." assert table_1.headers == ["Name", "Value", "Extra"], "Table columns are incorrect." +def test_table_add_rows(): + """ + This function tests the markdown.Table class. + """ + table_1 = markdown.Table(["Name", "Value"]) + table_1.add_rows([{"Name": "First", "Value": 1}, {"Name": "Second", "Value": 2}]) + assert ( + str(table_1) + == "| Name | Value |\n| --- | --- |\n| First | 1 |\n| Second | 2 |\n" + ), "Table is incorrect." + assert table_1.headers == ["Name", "Value"], "Table columns are incorrect." +def test_table_remap(): + """ + This function tests the markdown.Table class. + """ + table_1 = markdown.Table(["Name", "Value"], custom_map={"Name":{"First":"1st","Second":"2nd"}}) + table_1.add_row({"Name": "First", "Value": 1}) + table_1.add_row({"Name": "Second", "Value": 2}) + assert ( + str(table_1.get_table()) + == "| Name | Value |\n| --- | --- |\n| 1st | 1 |\n| 2nd | 2 |\n" # pylint: disable=line-too-long + ), "Remapped Table is incorrect." + assert table_1.headers == ["Name", "Value"], "Table columns are incorrect." + table_2 = markdown.Table(["Name", "Value"], custom_map={"Name":{"First":"1st","Second":"2nd"}}) + table_2.add_row({"Name": "First", "Value": 1}) + table_2.remap() + assert ( + str(table_2) + == "| Name | Value |\n| --- | --- |\n| 1st | 1 |\n" # pylint: disable=line-too-long + ), "Remapped Table is incorrect." + + +def test_header(): + """ + This function tests the markdown.Header class. + """ + header_1 = markdown.Header("Header 1", 2) + assert ( + str(header_1) == "## Header 1" + ), "String representation of header is incorrect." + assert header_1.text == "Header 1", "Header text is incorrect." + assert ( + repr(header_1) == "## Header 1" + ) def test_section(): """ This function tests the markdown.Section class. @@ -266,6 +404,23 @@ def test_section_add(): assert ( str(section_1) == "# Section 1\nThis is a paragraph. \n" ), "String representation of section is incorrect." + assert section_1.title.text == "Section 1", "Section title is incorrect." + assert ( + repr(section_1) == "Section(title=# Section 1, content=This is a paragraph. )" + ), "Section representation is incorrect." + + +def test_section_add_nonempty(): + """ + This function tests the markdown.Section class. + """ + section_1 = markdown.Section("Section 1") + section_1.add("This is a paragraph.") + section_1.add("This is another paragraph.") + assert ( + str(section_1) == "# Section 1\nThis is a paragraph. \nThis is another paragraph. \n" + ), "String representation of section is incorrect." + assert section_1.title.text == "Section 1", "Section title is incorrect." def test_document(): @@ -283,6 +438,26 @@ def test_document(): str(document_1) == "# Document 1\n## Section 1\nThis is a paragraph. \n" ), "String representation of document is incorrect." +def test_add_section(): + """ + This function tests the markdown.Document class. + """ + document_1 = markdown.Document("Document 1", filename="document_1.md") + document_1.add_section("RAW SECTION") + assert ( + str(document_1) == "# Document 1\n# RAW SECTION\n\n" + ), "String representation of document is incorrect." + +def test_document_toc(): + """ + This function tests the markdown.Document class. + """ + document_1 = markdown.Document("Document 1", filename="document_1.md", table_of_contents=True) # pylint: disable=line-too-long + document_1.add_section(markdown.Section(markdown.Header("Section 1", 2))) + assert ( + document_1.get_document() == "# Document 1\n## Table of Contents\n* [Section 1](#section-1)\n## Section 1\n\n" # pylint: disable=line-too-long + ), "String representation of document is incorrect." + def test_document_save(tmp_path): """