Skip to content

Add to_html method for TableData#62

Merged
ZmeiGorynych merged 1 commit into
mainfrom
egor/add_to_html_method_for_tables
Apr 7, 2026
Merged

Add to_html method for TableData#62
ZmeiGorynych merged 1 commit into
mainfrom
egor/add_to_html_method_for_tables

Conversation

@ZmeiGorynych

@ZmeiGorynych ZmeiGorynych commented Apr 2, 2026

Copy link
Copy Markdown
Member

Summary by CodeRabbit

  • New Features
    • Tables can now be exported to HTML format, enabling easy integration with web applications. Optional CSS class support enables custom styling, while automatic HTML escaping protects content and ensures proper rendering of special characters.

@coderabbitai

coderabbitai Bot commented Apr 2, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

A new to_html() method is added to the TableData class to generate HTML table markup with HTML escaping and optional CSS class support. Version bumped from 0.3.5 to 0.3.6, accompanied by comprehensive test coverage for the new method.

Changes

Cohort / File(s) Summary
Feature Implementation
gslides_api/agnostic/element.py
Added to_html() method to TableData class that converts table data to HTML string with configurable CSS class, proper escaping of headers/cells, and conditional output formatting.
Test Coverage
tests/test_table_data_to_html.py
New test suite validating HTML structure, CSS class handling, HTML escaping (special characters, quotes, ampersands), row padding behavior, and empty table edge cases.
Version Bump
pyproject.toml
Updated package version from 0.3.5 to 0.3.6.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Poem

🐰 A table hops into HTML with grace,
Each cell and header finds its proper place,
With escaping tricks to keep the tags so clean,
CSS classes add style to the scene,
Tests verify every hop is right!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 27.27% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and directly summarizes the main change: adding a to_html method to the TableData class, which is the primary focus of the changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch egor/add_to_html_method_for_tables

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@tests/test_table_data_to_html.py`:
- Around line 8-69: The tests only exercise TableData.to_html and repeat
TableData setup; add domain model validation tests and consolidate setup into
pytest fixtures: create a fixture (e.g., tabledata_basic) that returns a
TableData instance used by multiple tests, and add tests asserting model
validation behavior on TableData constructors (e.g., invalid headers types,
mismatched row lengths, non-string cell values) expecting the appropriate
exceptions (ValueError/TypeError) instead of calling to_html; reference the
TableData class and its to_html method when adding these tests and use
pytest.fixture to DRY the repeated setup.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 4381762a-ea46-4b5f-b80b-909a7b01a51d

📥 Commits

Reviewing files that changed from the base of the PR and between c772530 and 65f1922.

📒 Files selected for processing (3)
  • gslides_api/agnostic/element.py
  • pyproject.toml
  • tests/test_table_data_to_html.py

Comment on lines +8 to +69
class TestTableDataToHtml:
def test_basic_table(self):
td = TableData(headers=["Name", "Value"], rows=[["Alice", "100"], ["Bob", "200"]])
result = td.to_html()
assert "<table>" in result
assert "<thead>" in result
assert "<tbody>" in result
assert "<th>Name</th>" in result
assert "<th>Value</th>" in result
assert "<td>Alice</td>" in result
assert "<td>100</td>" in result
assert "<td>Bob</td>" in result
assert "<td>200</td>" in result

def test_with_css_class(self):
td = TableData(headers=["A"], rows=[["1"]])
result = td.to_html(css_class="dtbl")
assert '<table class="dtbl">' in result

def test_without_css_class(self):
td = TableData(headers=["A"], rows=[["1"]])
result = td.to_html()
assert "<table>" in result
assert "class=" not in result

def test_empty_headers(self):
td = TableData(headers=[], rows=[])
assert td.to_html() == ""

def test_html_escaping(self):
td = TableData(
headers=["<script>", "A&B"],
rows=[["x < y", '"quoted"']],
)
result = td.to_html()
assert "&lt;script&gt;" in result
assert "A&amp;B" in result
assert "x &lt; y" in result
assert "&quot;quoted&quot;" in result
assert "<script>" not in result # must be escaped

def test_css_class_escaping(self):
td = TableData(headers=["A"], rows=[["1"]])
result = td.to_html(css_class='x" onclick="alert(1)')
# The double quote must be escaped so it can't break out of the attribute
assert '&quot;' in result
assert 'class="x&quot; onclick=&quot;alert(1)"' in result

def test_short_row_pads_with_empty(self):
td = TableData(headers=["A", "B", "C"], rows=[["only_one"]])
result = td.to_html()
assert "<td>only_one</td>" in result
assert result.count("<td></td>") == 2

def test_structure_order(self):
td = TableData(headers=["H"], rows=[["R"]])
result = td.to_html()
# Verify structural ordering
assert result.index("<thead>") < result.index("</thead>")
assert result.index("</thead>") < result.index("<tbody>")
assert result.index("<tbody>") < result.index("</tbody>")
assert result.index("</tbody>") < result.index("</table>")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add model-validation coverage and move repeated setup into fixtures.

This file verifies HTML serialization well, but it misses domain model validation tests and repeats common TableData setup inline.

Suggested test refactor/extension
 import pytest
+from pydantic import ValidationError
 
 from gslides_api.agnostic.element import TableData
 
+@pytest.fixture
+def single_cell_table() -> TableData:
+    return TableData(headers=["A"], rows=[["1"]])
+
 
 class TestTableDataToHtml:
@@
-    def test_with_css_class(self):
-        td = TableData(headers=["A"], rows=[["1"]])
-        result = td.to_html(css_class="dtbl")
+    def test_with_css_class(self, single_cell_table: TableData):
+        result = single_cell_table.to_html(css_class="dtbl")
         assert '<table class="dtbl">' in result
 
-    def test_without_css_class(self):
-        td = TableData(headers=["A"], rows=[["1"]])
-        result = td.to_html()
+    def test_without_css_class(self, single_cell_table: TableData):
+        result = single_cell_table.to_html()
         assert "<table>" in result
         assert "class=" not in result
+
+    `@pytest.mark.parametrize`(
+        "headers,rows",
+        [
+            (None, [["1"]]),
+            (["A"], None),
+            ("A", [["1"]]),
+        ],
+    )
+    def test_domain_model_validation(self, headers, rows):
+        with pytest.raises(ValidationError):
+            TableData(headers=headers, rows=rows)

As per coding guidelines tests/**/*.py: "Test both API format serialization and domain model validation in test files" and "Use pytest fixtures for common setup patterns in tests".

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
class TestTableDataToHtml:
def test_basic_table(self):
td = TableData(headers=["Name", "Value"], rows=[["Alice", "100"], ["Bob", "200"]])
result = td.to_html()
assert "<table>" in result
assert "<thead>" in result
assert "<tbody>" in result
assert "<th>Name</th>" in result
assert "<th>Value</th>" in result
assert "<td>Alice</td>" in result
assert "<td>100</td>" in result
assert "<td>Bob</td>" in result
assert "<td>200</td>" in result
def test_with_css_class(self):
td = TableData(headers=["A"], rows=[["1"]])
result = td.to_html(css_class="dtbl")
assert '<table class="dtbl">' in result
def test_without_css_class(self):
td = TableData(headers=["A"], rows=[["1"]])
result = td.to_html()
assert "<table>" in result
assert "class=" not in result
def test_empty_headers(self):
td = TableData(headers=[], rows=[])
assert td.to_html() == ""
def test_html_escaping(self):
td = TableData(
headers=["<script>", "A&B"],
rows=[["x < y", '"quoted"']],
)
result = td.to_html()
assert "&lt;script&gt;" in result
assert "A&amp;B" in result
assert "x &lt; y" in result
assert "&quot;quoted&quot;" in result
assert "<script>" not in result # must be escaped
def test_css_class_escaping(self):
td = TableData(headers=["A"], rows=[["1"]])
result = td.to_html(css_class='x" onclick="alert(1)')
# The double quote must be escaped so it can't break out of the attribute
assert '&quot;' in result
assert 'class="x&quot; onclick=&quot;alert(1)"' in result
def test_short_row_pads_with_empty(self):
td = TableData(headers=["A", "B", "C"], rows=[["only_one"]])
result = td.to_html()
assert "<td>only_one</td>" in result
assert result.count("<td></td>") == 2
def test_structure_order(self):
td = TableData(headers=["H"], rows=[["R"]])
result = td.to_html()
# Verify structural ordering
assert result.index("<thead>") < result.index("</thead>")
assert result.index("</thead>") < result.index("<tbody>")
assert result.index("<tbody>") < result.index("</tbody>")
assert result.index("</tbody>") < result.index("</table>")
import pytest
from pydantic import ValidationError
from gslides_api.agnostic.element import TableData
`@pytest.fixture`
def single_cell_table() -> TableData:
return TableData(headers=["A"], rows=[["1"]])
class TestTableDataToHtml:
def test_basic_table(self):
td = TableData(headers=["Name", "Value"], rows=[["Alice", "100"], ["Bob", "200"]])
result = td.to_html()
assert "<table>" in result
assert "<thead>" in result
assert "<tbody>" in result
assert "<th>Name</th>" in result
assert "<th>Value</th>" in result
assert "<td>Alice</td>" in result
assert "<td>100</td>" in result
assert "<td>Bob</td>" in result
assert "<td>200</td>" in result
def test_with_css_class(self, single_cell_table: TableData):
result = single_cell_table.to_html(css_class="dtbl")
assert '<table class="dtbl">' in result
def test_without_css_class(self, single_cell_table: TableData):
result = single_cell_table.to_html()
assert "<table>" in result
assert "class=" not in result
def test_empty_headers(self):
td = TableData(headers=[], rows=[])
assert td.to_html() == ""
def test_html_escaping(self):
td = TableData(
headers=["<script>", "A&B"],
rows=[["x < y", '"quoted"']],
)
result = td.to_html()
assert "&lt;script&gt;" in result
assert "A&amp;B" in result
assert "x &lt; y" in result
assert "&quot;quoted&quot;" in result
assert "<script>" not in result # must be escaped
def test_css_class_escaping(self):
td = TableData(headers=["A"], rows=[["1"]])
result = td.to_html(css_class='x" onclick="alert(1)')
# The double quote must be escaped so it can't break out of the attribute
assert '&quot;' in result
assert 'class="x&quot; onclick=&quot;alert(1)"' in result
def test_short_row_pads_with_empty(self):
td = TableData(headers=["A", "B", "C"], rows=[["only_one"]])
result = td.to_html()
assert "<td>only_one</td>" in result
assert result.count("<td></td>") == 2
def test_structure_order(self):
td = TableData(headers=["H"], rows=[["R"]])
result = td.to_html()
# Verify structural ordering
assert result.index("<thead>") < result.index("</thead>")
assert result.index("</thead>") < result.index("<tbody>")
assert result.index("<tbody>") < result.index("</tbody>")
assert result.index("</tbody>") < result.index("</table>")
`@pytest.mark.parametrize`(
"headers,rows",
[
(None, [["1"]]),
(["A"], None),
("A", [["1"]]),
],
)
def test_domain_model_validation(self, headers, rows):
with pytest.raises(ValidationError):
TableData(headers=headers, rows=rows)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/test_table_data_to_html.py` around lines 8 - 69, The tests only
exercise TableData.to_html and repeat TableData setup; add domain model
validation tests and consolidate setup into pytest fixtures: create a fixture
(e.g., tabledata_basic) that returns a TableData instance used by multiple
tests, and add tests asserting model validation behavior on TableData
constructors (e.g., invalid headers types, mismatched row lengths, non-string
cell values) expecting the appropriate exceptions (ValueError/TypeError) instead
of calling to_html; reference the TableData class and its to_html method when
adding these tests and use pytest.fixture to DRY the repeated setup.

@ZmeiGorynych ZmeiGorynych merged commit 98e6162 into main Apr 7, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant