Files
textual/tests/test_markdown.py
Will McGugan 879c985296 Rich log (#3046)
* log

* tests

* snapshot tests

* change to richlog

* keep raw lines

* disable highlighting by default

* simplify

* superfluous test

* optimization

* update cell length

* add refresh

* write method

* version bump

* doc fix link

* makes lines private

* docstring

* relax dev dependancy

* remove superfluous code [skip ci]

* added FAQ [skipci]

* fix code in faq [skipci]

* fix typo

* max lines fix
2023-08-03 10:11:17 +01:00

128 lines
4.2 KiB
Python

"""Unit tests for the Markdown widget."""
from __future__ import annotations
from pathlib import Path
from typing import Iterator
import pytest
from markdown_it.token import Token
from rich.style import Style
from rich.text import Span
import textual.widgets._markdown as MD
from textual.app import App, ComposeResult
from textual.widget import Widget
from textual.widgets import Markdown
from textual.widgets.markdown import MarkdownBlock
class UnhandledToken(MarkdownBlock):
def __init__(self, markdown: Markdown, token: Token) -> None:
super().__init__(markdown)
self._token = token
def __repr___(self) -> str:
return self._token.type
class FussyMarkdown(Markdown):
def unhandled_token(self, token: Token) -> MarkdownBlock | None:
return UnhandledToken(self, token)
class MarkdownApp(App[None]):
def __init__(self, markdown: str) -> None:
super().__init__()
self._markdown = markdown
def compose(self) -> ComposeResult:
yield FussyMarkdown(self._markdown)
@pytest.mark.parametrize(
["document", "expected_nodes"],
[
# Basic markup.
("", []),
("# Hello", [MD.MarkdownH1]),
("## Hello", [MD.MarkdownH2]),
("### Hello", [MD.MarkdownH3]),
("#### Hello", [MD.MarkdownH4]),
("##### Hello", [MD.MarkdownH5]),
("###### Hello", [MD.MarkdownH6]),
("---", [MD.MarkdownHorizontalRule]),
("Hello", [MD.MarkdownParagraph]),
("Hello\nWorld", [MD.MarkdownParagraph]),
("> Hello", [MD.MarkdownBlockQuote, MD.MarkdownParagraph]),
("- One\n-Two", [MD.MarkdownBulletList, MD.MarkdownParagraph]),
(
"1. One\n2. Two",
[MD.MarkdownOrderedList, MD.MarkdownParagraph, MD.MarkdownParagraph],
),
(" 1", [MD.MarkdownFence]),
("```\n1\n```", [MD.MarkdownFence]),
("```python\n1\n```", [MD.MarkdownFence]),
("""| One | Two |\n| :- | :- |\n| 1 | 2 |""", [MD.MarkdownTable]),
# Test for https://github.com/Textualize/textual/issues/2676
(
"- One\n```\nTwo\n```\n- Three\n",
[
MD.MarkdownBulletList,
MD.MarkdownParagraph,
MD.MarkdownFence,
MD.MarkdownBulletList,
MD.MarkdownParagraph,
],
),
],
)
async def test_markdown_nodes(
document: str, expected_nodes: list[Widget | list[Widget]]
) -> None:
"""A Markdown document should parse into the expected Textual node list."""
def markdown_nodes(root: Widget) -> Iterator[MarkdownBlock]:
for node in root.children:
if isinstance(node, MarkdownBlock):
yield node
yield from markdown_nodes(node)
async with MarkdownApp(document).run_test() as pilot:
assert [
node.__class__ for node in markdown_nodes(pilot.app.query_one(Markdown))
] == expected_nodes
async def test_softbreak_split_links_rendered_correctly() -> None:
"""Test for https://github.com/Textualize/textual/issues/2805"""
document = """\
My site [has
this
URL](https://example.com)\
"""
async with MarkdownApp(document).run_test() as pilot:
markdown = pilot.app.query_one(Markdown)
paragraph = markdown.children[0]
assert isinstance(paragraph, MD.MarkdownParagraph)
assert paragraph._text.plain == "My site has this URL"
expected_spans = [
Span(8, 11, Style(meta={"@click": "link('https://example.com')"})),
Span(11, 12, Style(meta={"@click": "link('https://example.com')"})),
Span(12, 16, Style(meta={"@click": "link('https://example.com')"})),
Span(16, 17, Style(meta={"@click": "link('https://example.com')"})),
Span(17, 20, Style(meta={"@click": "link('https://example.com')"})),
]
assert paragraph._text.spans == expected_spans
async def test_load_non_existing_file() -> None:
"""Loading a file that doesn't exist should result in the obvious error."""
async with MarkdownApp("").run_test() as pilot:
with pytest.raises(FileNotFoundError):
await pilot.app.query_one(Markdown).load(
Path("---this-does-not-exist---.it.is.not.a.md")
)