Skip to content

Commit

Permalink
test: --extensions, --no-extensions, --codeformatters and --no-codefo…
Browse files Browse the repository at this point in the history
…rmatters
  • Loading branch information
hukkin authored Nov 18, 2024
1 parent 47e0452 commit aefd630
Show file tree
Hide file tree
Showing 3 changed files with 196 additions and 103 deletions.
85 changes: 83 additions & 2 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,13 @@

import mdformat
from mdformat._cli import get_package_name, get_plugin_info_str, run, wrap_paragraphs
from mdformat.plugins import CODEFORMATTERS
from tests.utils import FORMATTED_MARKDOWN, UNFORMATTED_MARKDOWN
from mdformat.plugins import CODEFORMATTERS, PARSER_EXTENSIONS
from tests.utils import (
FORMATTED_MARKDOWN,
UNFORMATTED_MARKDOWN,
ASTChangingPlugin,
PrefixPostprocessPlugin,
)


def test_no_files_passed():
Expand Down Expand Up @@ -412,6 +417,59 @@ def test_exclude(tmp_path):
assert file_path_1.read_text() == FORMATTED_MARKDOWN


def test_codeformatters(tmp_path, monkeypatch):
monkeypatch.setitem(CODEFORMATTERS, "enabled-lang", lambda code, info: "dumdum")
monkeypatch.setitem(CODEFORMATTERS, "disabled-lang", lambda code, info: "dumdum")
file_path = tmp_path / "test.md"
unformatted = """\
```disabled-lang
hey
```
```enabled-lang
hey
```
"""
formatted = """\
```disabled-lang
hey
```
```enabled-lang
dumdum
```
"""
file_path.write_text(unformatted)
assert run((str(file_path), "--codeformatters", "enabled-lang")) == 0
assert file_path.read_text() == formatted


def test_extensions(tmp_path, monkeypatch):
ast_plugin_name = "ast-plug"
prefix_plugin_name = "prefix-plug"
monkeypatch.setitem(PARSER_EXTENSIONS, ast_plugin_name, ASTChangingPlugin)
monkeypatch.setitem(PARSER_EXTENSIONS, prefix_plugin_name, PrefixPostprocessPlugin)
unformatted = "original text\n"
file_path = tmp_path / "test.md"

file_path.write_text(unformatted)
assert run((str(file_path), "--extensions", "prefix-plug")) == 0
assert file_path.read_text() == "Prefixed!original text\n"

file_path.write_text(unformatted)
assert run((str(file_path), "--extensions", "ast-plug")) == 0
assert file_path.read_text() == ASTChangingPlugin.TEXT_REPLACEMENT + "\n"

file_path.write_text(unformatted)
assert (
run((str(file_path), "--extensions", "ast-plug", "--extensions", "prefix-plug"))
== 0
)
assert (
file_path.read_text() == "Prefixed!" + ASTChangingPlugin.TEXT_REPLACEMENT + "\n"
)


def test_codeformatters__invalid(tmp_path, capsys):
file_path = tmp_path / "test.md"
file_path.write_text("")
Expand All @@ -426,3 +484,26 @@ def test_extensions__invalid(tmp_path, capsys):
assert run((str(file_path), "--extensions", "no-exists")) == 1
captured = capsys.readouterr()
assert "Error: Invalid extension required" in captured.err


def test_no_codeformatters(tmp_path, monkeypatch):
monkeypatch.setitem(CODEFORMATTERS, "lang", lambda code, info: "dumdum")
file_path = tmp_path / "test.md"
original_md = """\
```lang
original code
```
"""
file_path.write_text(original_md)
assert run((str(file_path), "--no-codeformatters")) == 0
assert file_path.read_text() == original_md


def test_no_extensions(tmp_path, monkeypatch):
plugin_name = "plug-name"
monkeypatch.setitem(PARSER_EXTENSIONS, plugin_name, ASTChangingPlugin)
file_path = tmp_path / "test.md"
original_md = "original md\n"
file_path.write_text(original_md)
assert run((str(file_path), "--no-extensions")) == 0
assert file_path.read_text() == original_md
115 changes: 14 additions & 101 deletions tests/test_plugins.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import argparse
import json
from textwrap import dedent
from unittest.mock import patch

Expand All @@ -15,8 +14,16 @@
PARSER_EXTENSIONS,
_load_entrypoints,
)
from mdformat.renderer import MDRenderer, RenderContext, RenderTreeNode
from tests.utils import run_with_clear_cache
from mdformat.renderer import MDRenderer
from tests.utils import (
ASTChangingPlugin,
JSONFormatterPlugin,
PrefixPostprocessPlugin,
SuffixPostprocessPlugin,
TablePlugin,
TextEditorPlugin,
run_with_clear_cache,
)


def test_code_formatter(monkeypatch):
Expand Down Expand Up @@ -119,21 +126,6 @@ def fmt_func(code, info):
)


class TextEditorPlugin:
"""A plugin that makes all text the same."""

@staticmethod
def update_mdit(mdit: MarkdownIt):
pass

def _text_renderer( # type: ignore[misc]
tree: RenderTreeNode, context: RenderContext
) -> str:
return "All text is like this now!"

RENDERERS = {"text": _text_renderer}


def test_single_token_extension(monkeypatch):
"""Test the front matter plugin, as a single token extension example."""
plugin_name = "text_editor"
Expand All @@ -157,24 +149,9 @@ def test_single_token_extension(monkeypatch):
)


class ExampleTablePlugin:
"""A plugin that adds table extension to the parser."""

@staticmethod
def update_mdit(mdit: MarkdownIt):
mdit.enable("table")

def _table_renderer( # type: ignore[misc]
tree: RenderTreeNode, context: RenderContext
) -> str:
return "dummy 21"

RENDERERS = {"table": _table_renderer}


def test_table(monkeypatch):
"""Test the table plugin, as a multi-token extension example."""
monkeypatch.setitem(PARSER_EXTENSIONS, "table", ExampleTablePlugin)
monkeypatch.setitem(PARSER_EXTENSIONS, "table", TablePlugin)
text = mdformat.text(
dedent(
"""\
Expand Down Expand Up @@ -306,27 +283,8 @@ def test_cli_options_group__no_toml(monkeypatch, tmp_path):
assert opts["mdformat"]["plugin"]["table"]["o1"] == "other"


class ExampleASTChangingPlugin:
"""A plugin that makes AST breaking formatting changes."""

CHANGES_AST = True

TEXT_REPLACEMENT = "Content replaced completely. AST is now broken!"

@staticmethod
def update_mdit(mdit: MarkdownIt):
pass

def _text_renderer( # type: ignore[misc]
tree: RenderTreeNode, context: RenderContext
) -> str:
return ExampleASTChangingPlugin.TEXT_REPLACEMENT

RENDERERS = {"text": _text_renderer}


def test_ast_changing_plugin(monkeypatch, tmp_path):
plugin = ExampleASTChangingPlugin()
plugin = ASTChangingPlugin()
monkeypatch.setitem(PARSER_EXTENSIONS, "ast_changer", plugin)
file_path = tmp_path / "test_markdown.md"

Expand All @@ -345,15 +303,6 @@ def test_ast_changing_plugin(monkeypatch, tmp_path):
assert file_path.read_text() == "Some markdown here\n"


class JSONFormatterPlugin:
"""A code formatter plugin that formats JSON."""

@staticmethod
def format_json(unformatted: str, _info_str: str) -> str:
parsed = json.loads(unformatted)
return json.dumps(parsed, indent=2) + "\n"


def test_code_format_warnings__cli(monkeypatch, tmp_path, capsys):
monkeypatch.setitem(CODEFORMATTERS, "json", JSONFormatterPlugin.format_json)
file_path = tmp_path / "test_markdown.md"
Expand Down Expand Up @@ -383,7 +332,7 @@ def test_plugin_conflict(monkeypatch, tmp_path, capsys):
plugin_name_1 = "plug1"
plugin_name_2 = "plug2"
monkeypatch.setitem(PARSER_EXTENSIONS, plugin_name_1, TextEditorPlugin)
monkeypatch.setitem(PARSER_EXTENSIONS, plugin_name_2, ExampleASTChangingPlugin)
monkeypatch.setitem(PARSER_EXTENSIONS, plugin_name_2, ASTChangingPlugin)

file_path = tmp_path / "test_markdown.md"
file_path.write_text("some markdown here")
Expand All @@ -407,42 +356,6 @@ def test_plugin_versions_in_cli_help(monkeypatch, capsys):
assert "table-dist: table-ext" in captured.out


class PrefixPostprocessPlugin:
"""A plugin that postprocesses text, adding a prefix."""

CHANGES_AST = True

@staticmethod
def update_mdit(mdit: MarkdownIt):
pass

def _text_postprocess( # type: ignore[misc]
text: str, tree: RenderTreeNode, context: RenderContext
) -> str:
return "Prefixed!" + text

RENDERERS: dict = {}
POSTPROCESSORS = {"text": _text_postprocess}


class SuffixPostprocessPlugin:
"""A plugin that postprocesses text, adding a suffix."""

CHANGES_AST = True

@staticmethod
def update_mdit(mdit: MarkdownIt):
pass

def _text_postprocess( # type: ignore[misc]
text: str, tree: RenderTreeNode, context: RenderContext
) -> str:
return text + "Suffixed!"

RENDERERS: dict = {}
POSTPROCESSORS = {"text": _text_postprocess}


def test_postprocess_plugins(monkeypatch):
"""Test that postprocessors work collaboratively."""
suffix_plugin_name = "suffixer"
Expand Down Expand Up @@ -529,7 +442,7 @@ def test_no_codeformatters__toml(tmp_path, monkeypatch):


def test_no_extensions__toml(tmp_path, monkeypatch):
plugin = ExampleASTChangingPlugin()
plugin = ASTChangingPlugin()
monkeypatch.setitem(PARSER_EXTENSIONS, "ast_changer", plugin)
unformatted = "text\n"
formatted = plugin.TEXT_REPLACEMENT + "\n"
Expand Down
99 changes: 99 additions & 0 deletions tests/utils.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import json

from markdown_it import MarkdownIt

from mdformat._cli import run
from mdformat._conf import read_toml_opts
from mdformat.renderer import RenderContext, RenderTreeNode

UNFORMATTED_MARKDOWN = "\n\n# A header\n\n"
FORMATTED_MARKDOWN = "# A header\n"
Expand All @@ -8,3 +13,97 @@
def run_with_clear_cache(*args, **kwargs):
read_toml_opts.cache_clear()
return run(*args, **kwargs)


class JSONFormatterPlugin:
"""A code formatter plugin that formats JSON."""

@staticmethod
def format_json(unformatted: str, _info_str: str) -> str:
parsed = json.loads(unformatted)
return json.dumps(parsed, indent=2) + "\n"


class TextEditorPlugin:
"""A plugin that makes all text the same."""

@staticmethod
def update_mdit(mdit: MarkdownIt):
pass

def _text_renderer( # type: ignore[misc]
tree: RenderTreeNode, context: RenderContext
) -> str:
return "All text is like this now!"

RENDERERS = {"text": _text_renderer}


class TablePlugin:
"""A plugin that adds table extension to the parser."""

@staticmethod
def update_mdit(mdit: MarkdownIt):
mdit.enable("table")

def _table_renderer( # type: ignore[misc]
tree: RenderTreeNode, context: RenderContext
) -> str:
return "dummy 21"

RENDERERS = {"table": _table_renderer}


class ASTChangingPlugin:
"""A plugin that makes AST breaking formatting changes."""

CHANGES_AST = True

TEXT_REPLACEMENT = "Content replaced completely. AST is now broken!"

@staticmethod
def update_mdit(mdit: MarkdownIt):
pass

def _text_renderer( # type: ignore[misc]
tree: RenderTreeNode, context: RenderContext
) -> str:
return ASTChangingPlugin.TEXT_REPLACEMENT

RENDERERS = {"text": _text_renderer}


class PrefixPostprocessPlugin:
"""A plugin that postprocesses text, adding a prefix."""

CHANGES_AST = True

@staticmethod
def update_mdit(mdit: MarkdownIt):
pass

def _text_postprocess( # type: ignore[misc]
text: str, tree: RenderTreeNode, context: RenderContext
) -> str:
return "Prefixed!" + text

RENDERERS: dict = {}
POSTPROCESSORS = {"text": _text_postprocess}


class SuffixPostprocessPlugin:
"""A plugin that postprocesses text, adding a suffix."""

CHANGES_AST = True

@staticmethod
def update_mdit(mdit: MarkdownIt):
pass

def _text_postprocess( # type: ignore[misc]
text: str, tree: RenderTreeNode, context: RenderContext
) -> str:
return text + "Suffixed!"

RENDERERS: dict = {}
POSTPROCESSORS = {"text": _text_postprocess}

0 comments on commit aefd630

Please sign in to comment.