|
| 1 | +# License: MIT |
| 2 | +# Copyright © 2023 Frequenz Energy-as-a-Service GmbH |
| 3 | + |
| 4 | +"""Tests for the do_format() function.""" |
| 5 | + |
| 6 | +import dataclasses |
| 7 | +from typing import Iterator |
| 8 | +from unittest import mock |
| 9 | + |
| 10 | +import pytest |
| 11 | + |
| 12 | +from frequenz.pymdownx.superfences.filter_lines import ( |
| 13 | + LinesRange, |
| 14 | + LinesRanges, |
| 15 | + Options, |
| 16 | + do_format, |
| 17 | +) |
| 18 | + |
| 19 | + |
| 20 | +@pytest.fixture |
| 21 | +def md_mock() -> Iterator[mock.MagicMock]: |
| 22 | + """Mock the `md` object.""" |
| 23 | + md = mock.MagicMock() |
| 24 | + preprocessor = mock.MagicMock() |
| 25 | + preprocessor.highlight.side_effect = True |
| 26 | + md.preprocessors.return_value = {"fenced_code_block": preprocessor} |
| 27 | + yield md |
| 28 | + |
| 29 | + |
| 30 | +_SOURCE = """\ |
| 31 | +1. This is some text |
| 32 | +2. which have multiple lines |
| 33 | +3. and we want to filter some of them |
| 34 | +4. we number them |
| 35 | +5. so we can see which ones are filtered |
| 36 | +6. and which ones are not |
| 37 | +7. and we can also filter multiple ranges |
| 38 | +""" |
| 39 | + |
| 40 | + |
| 41 | +@dataclasses.dataclass(frozen=True, kw_only=True) |
| 42 | +class _TestCase: |
| 43 | + title: str | None = None |
| 44 | + options: Options = dataclasses.field( |
| 45 | + # We can't use just `Options` because `mypy` complains about type |
| 46 | + # incompatibility, which is not true. |
| 47 | + default_factory=lambda: Options() # pylint: disable=unnecessary-lambda |
| 48 | + ) |
| 49 | + expected_src: str |
| 50 | + |
| 51 | + |
| 52 | +_cases = [ |
| 53 | + _TestCase(title="No options", expected_src=_SOURCE), |
| 54 | + _TestCase( |
| 55 | + title="First line", |
| 56 | + options=Options(show_lines=LinesRanges({LinesRange(start=1, end=1)})), |
| 57 | + expected_src="""\ |
| 58 | +1. This is some text |
| 59 | +""", |
| 60 | + ), |
| 61 | + _TestCase( |
| 62 | + title="Middle line", |
| 63 | + options=Options(show_lines=LinesRanges({LinesRange(start=3, end=3)})), |
| 64 | + expected_src="""\ |
| 65 | +3. and we want to filter some of them |
| 66 | +""", |
| 67 | + ), |
| 68 | + _TestCase( |
| 69 | + title="Last line", |
| 70 | + options=Options(show_lines=LinesRanges({LinesRange(start=7, end=7)})), |
| 71 | + expected_src="""\ |
| 72 | +7. and we can also filter multiple ranges |
| 73 | +""", |
| 74 | + ), |
| 75 | + _TestCase( |
| 76 | + title="Open range from the start", |
| 77 | + options=Options(show_lines=LinesRanges({LinesRange(start=1)})), |
| 78 | + expected_src=_SOURCE, |
| 79 | + ), |
| 80 | + _TestCase( |
| 81 | + title="Open range until the end", |
| 82 | + options=Options(show_lines=LinesRanges({LinesRange(end=7)})), |
| 83 | + expected_src=_SOURCE, |
| 84 | + ), |
| 85 | + _TestCase( |
| 86 | + title="Open range with start in the middle", |
| 87 | + options=Options(show_lines=LinesRanges({LinesRange(start=3)})), |
| 88 | + expected_src="""\ |
| 89 | +3. and we want to filter some of them |
| 90 | +4. we number them |
| 91 | +5. so we can see which ones are filtered |
| 92 | +6. and which ones are not |
| 93 | +7. and we can also filter multiple ranges |
| 94 | +""", |
| 95 | + ), |
| 96 | + _TestCase( |
| 97 | + title="Open range with end in the middle", |
| 98 | + options=Options(show_lines=LinesRanges({LinesRange(end=3)})), |
| 99 | + expected_src="""\ |
| 100 | +1. This is some text |
| 101 | +2. which have multiple lines |
| 102 | +3. and we want to filter some of them |
| 103 | +""", |
| 104 | + ), |
| 105 | + # range with start and end in the middle |
| 106 | + _TestCase( |
| 107 | + title="Open range with start and end in the middle", |
| 108 | + options=Options(show_lines=LinesRanges({LinesRange(start=2, end=4)})), |
| 109 | + expected_src="""\ |
| 110 | +2. which have multiple lines |
| 111 | +3. and we want to filter some of them |
| 112 | +4. we number them |
| 113 | +""", |
| 114 | + ), |
| 115 | + _TestCase( |
| 116 | + title="Multiple lines", |
| 117 | + options=Options( |
| 118 | + show_lines=LinesRanges( |
| 119 | + { |
| 120 | + LinesRange(start=1, end=1), |
| 121 | + LinesRange(start=3, end=3), |
| 122 | + LinesRange(start=6, end=6), |
| 123 | + } |
| 124 | + ) |
| 125 | + ), |
| 126 | + expected_src="""\ |
| 127 | +1. This is some text |
| 128 | +3. and we want to filter some of them |
| 129 | +6. and which ones are not |
| 130 | +""", |
| 131 | + ), |
| 132 | + _TestCase( |
| 133 | + title="Multiple ranges", |
| 134 | + options=Options( |
| 135 | + show_lines=LinesRanges( |
| 136 | + { |
| 137 | + LinesRange(end=2), |
| 138 | + LinesRange(start=4, end=5), |
| 139 | + LinesRange(start=6), |
| 140 | + } |
| 141 | + ) |
| 142 | + ), |
| 143 | + expected_src="""\ |
| 144 | +1. This is some text |
| 145 | +2. which have multiple lines |
| 146 | +4. we number them |
| 147 | +5. so we can see which ones are filtered |
| 148 | +6. and which ones are not |
| 149 | +7. and we can also filter multiple ranges |
| 150 | +""", |
| 151 | + ), |
| 152 | + _TestCase( |
| 153 | + title="Multiple ranges with overlap", |
| 154 | + options=Options( |
| 155 | + show_lines=LinesRanges( |
| 156 | + { |
| 157 | + LinesRange(end=2), |
| 158 | + LinesRange(start=2, end=5), |
| 159 | + LinesRange(start=4, end=6), |
| 160 | + LinesRange(start=6), |
| 161 | + } |
| 162 | + ) |
| 163 | + ), |
| 164 | + expected_src=_SOURCE, |
| 165 | + ), |
| 166 | + _TestCase( |
| 167 | + title="Multiple ranges and lines", |
| 168 | + options=Options( |
| 169 | + show_lines=LinesRanges( |
| 170 | + { |
| 171 | + LinesRange(end=1), |
| 172 | + LinesRange(start=4, end=4), |
| 173 | + LinesRange(start=5, end=5), |
| 174 | + LinesRange(start=7), |
| 175 | + } |
| 176 | + ) |
| 177 | + ), |
| 178 | + expected_src="""\ |
| 179 | +1. This is some text |
| 180 | +4. we number them |
| 181 | +5. so we can see which ones are filtered |
| 182 | +7. and we can also filter multiple ranges |
| 183 | +""", |
| 184 | + ), |
| 185 | +] |
| 186 | + |
| 187 | + |
| 188 | +@pytest.mark.parametrize( |
| 189 | + "case", _cases, ids=lambda c: f"{c.title} ({c.options.get('show_lines')})" |
| 190 | +) |
| 191 | +def test_do_format( |
| 192 | + case: _TestCase, |
| 193 | + md_mock: mock.MagicMock, # pylint: disable=redefined-outer-name |
| 194 | +) -> None: |
| 195 | + """Test valid initializations succeed.""" |
| 196 | + language = "xxx" |
| 197 | + class_name = "yyy" |
| 198 | + kwargs = {"kwarg": "test"} |
| 199 | + assert do_format(_SOURCE, language, class_name, case.options, md_mock, **kwargs) |
| 200 | + md_mock.preprocessors["fenced_code_block"].highlight.assert_called_once_with( |
| 201 | + src=case.expected_src, |
| 202 | + class_name=class_name, |
| 203 | + language=language, |
| 204 | + md=md_mock, |
| 205 | + options=case.options, |
| 206 | + **kwargs, |
| 207 | + ) |
0 commit comments