Coverage for tests/test_rendering.py: 100.00%
48 statements
« prev ^ index » next coverage.py v7.10.3, created at 2025-08-14 23:22 +0200
« prev ^ index » next coverage.py v7.10.3, created at 2025-08-14 23:22 +0200
1"""Tests for the `rendering` module."""
3from __future__ import annotations
5import re
6from dataclasses import dataclass
7from typing import TYPE_CHECKING, Any, Callable
9import pytest
10from griffe import ModulesCollection, temporary_visited_module
12from mkdocstrings_handlers.python._internal import rendering
14if TYPE_CHECKING:
15 from markupsafe import Markup
18@pytest.mark.parametrize(
19 "code",
20 [
21 "print('Hello')",
22 "aaaaa(bbbbb, ccccc=1) + ddddd.eeeee[ffff] or {ggggg: hhhhh, iiiii: jjjjj}",
23 ],
24)
25@pytest.mark.parametrize(
26 "formatter",
27 [
28 rendering._get_black_formatter(),
29 rendering._get_ruff_formatter(),
30 rendering._get_formatter(),
31 ],
32)
33def test_format_code(code: str, formatter: Callable[[str, int], str]) -> None:
34 """Assert code can be formatted.
36 Parameters:
37 code: Code to format.
38 """
39 for length in (5, 100):
40 assert formatter(code, length)
43@pytest.mark.parametrize(
44 ("name", "signature"),
45 [("Class.method", "(param: str = 'hello') -> 'OtherClass'")],
46)
47def test_format_signature(name: Markup, signature: str) -> None:
48 """Assert signatures can be formatted.
50 Parameters:
51 signature: Signature to format.
52 """
53 for length in (5, 100):
54 assert rendering._format_signature(name, signature, length)
57@dataclass
58class _FakeObject:
59 name: str
60 inherited: bool = False
61 parent: None = None
62 is_alias: bool = False
65@pytest.mark.parametrize(
66 ("names", "filter_params", "expected_names"),
67 [
68 (["aa", "ab", "ac", "da"], {"filters": [(re.compile("^a[^b]"), True)]}, {"ab", "da"}),
69 (["aa", "ab", "ac", "da"], {"members_list": ["aa", "ab"]}, {"aa", "ab"}),
70 ],
71)
72def test_filter_objects(names: list[str], filter_params: dict[str, Any], expected_names: set[str]) -> None:
73 """Assert the objects filter works correctly.
75 Parameters:
76 names: Names of the objects.
77 filter_params: Parameters passed to the filter function.
78 expected_names: Names expected to be kept.
79 """
80 objects = {name: _FakeObject(name) for name in names}
81 filtered = rendering.do_filter_objects(objects, **filter_params) # type: ignore[arg-type]
82 filtered_names = {obj.name for obj in filtered}
83 assert set(filtered_names) == set(expected_names)
86@pytest.mark.parametrize(
87 ("members", "inherited_members", "expected_names"),
88 [
89 (True, True, {"base", "main"}),
90 (True, False, {"main"}),
91 (True, ["base"], {"base", "main"}),
92 (True, [], {"main"}),
93 (False, True, {"base"}),
94 (False, False, set()),
95 (False, ["base"], {"base"}),
96 (False, [], set()),
97 ([], True, {"base"}),
98 ([], False, set()),
99 ([], ["base"], {"base"}),
100 ([], [], set()),
101 (None, True, {"base", "main"}),
102 (None, False, {"main"}),
103 (None, ["base"], {"base", "main"}),
104 (None, [], {"main"}),
105 (["base"], True, {"base"}),
106 (["base"], False, set()),
107 (["base"], ["base"], {"base"}),
108 (["base"], [], set()),
109 (["main"], True, {"main"}),
110 (["main"], False, {"main"}),
111 (["main"], ["base"], {"base", "main"}),
112 (["main"], [], {"main"}),
113 ],
114)
115def test_filter_inherited_members(
116 members: bool | list[str] | None,
117 inherited_members: bool | list[str],
118 expected_names: list[str],
119) -> None:
120 """Test inherited members filtering.
122 Parameters:
123 members: Members option (parametrized).
124 inherited_members: Inherited members option (parametrized).
125 expected_names: The expected result as a list of member names.
126 """
127 collection = ModulesCollection()
128 with temporary_visited_module(
129 """
130 class Base:
131 def base(self): ...
133 class Main(Base):
134 def main(self): ...
135 """,
136 modules_collection=collection,
137 ) as module:
138 collection["module"] = module
139 objects = module["Main"].all_members
140 filtered = rendering.do_filter_objects(objects, members_list=members, inherited_members=inherited_members)
141 names = {obj.name for obj in filtered}
142 assert names == expected_names
145@pytest.mark.parametrize(
146 ("order", "members_list", "expected_names"),
147 [
148 ("alphabetical", None, ["a", "b", "c"]),
149 ("source", None, ["c", "b", "a"]),
150 ("alphabetical", ["c", "b"], ["c", "b"]),
151 ("source", ["a", "c"], ["a", "c"]),
152 ("alphabetical", [], ["a", "b", "c"]),
153 ("source", [], ["c", "b", "a"]),
154 ("alphabetical", True, ["a", "b", "c"]),
155 ("source", False, ["c", "b", "a"]),
156 ],
157)
158def test_ordering_members(order: rendering.Order, members_list: list[str | None], expected_names: list[str]) -> None:
159 """Assert the objects are correctly ordered.
161 Parameters:
162 order: The order to use (alphabetical or source).
163 members_list: The user specified members list.
164 expected_names: The expected ordered list of object names.
165 """
167 class Obj:
168 def __init__(self, name: str, lineno: int | None = None, *, is_alias: bool = False) -> None:
169 self.name = name
170 self.lineno = lineno
171 self.alias_lineno = lineno
172 self.is_alias = is_alias
174 members = [Obj("a", 10, is_alias=True), Obj("b", 9, is_alias=False), Obj("c", 8, is_alias=True)]
175 ordered = rendering.do_order_members(members, order, members_list) # type: ignore[arg-type]
176 assert [obj.name for obj in ordered] == expected_names