Coverage for tests / integration / tools / test_prettier_integration.py: 97%
94 statements
« prev ^ index » next coverage.py v7.13.0, created at 2026-04-03 18:53 +0000
« prev ^ index » next coverage.py v7.13.0, created at 2026-04-03 18:53 +0000
1"""Integration tests for Prettier tool definition.
3These tests require prettier to be installed (npm install -g prettier).
4They verify the PrettierPlugin definition, check command, fix command, and set_options method.
5"""
7from __future__ import annotations
9import json
10import shutil
11from collections.abc import Callable
12from pathlib import Path
13from typing import TYPE_CHECKING
15import pytest
16from assertpy import assert_that
18if TYPE_CHECKING:
19 from lintro.plugins.base import BaseToolPlugin
21# Skip all tests if prettier is not installed
22pytestmark = pytest.mark.skipif(
23 shutil.which("prettier") is None,
24 reason="prettier not installed",
25)
28@pytest.fixture
29def temp_json_file_unformatted(tmp_path: Path) -> str:
30 """Create a temporary JSON file with formatting issues.
32 Creates a file containing a single-line JSON object that Prettier
33 should format with proper indentation and newlines.
35 Args:
36 tmp_path: Pytest fixture providing a temporary directory.
38 Returns:
39 Path to the created file as a string.
40 """
41 file_path = tmp_path / "unformatted.json"
42 file_path.write_text('{"name":"test","version":"1.0.0","dependencies":{}}')
43 return str(file_path)
46# --- Tests for PrettierPlugin definition ---
49@pytest.mark.parametrize(
50 ("attr", "expected"),
51 [
52 ("name", "prettier"),
53 ("can_fix", True),
54 ],
55 ids=["name", "can_fix"],
56)
57def test_definition_attributes(
58 get_plugin: Callable[[str], BaseToolPlugin],
59 attr: str,
60 expected: object,
61) -> None:
62 """Verify PrettierPlugin definition has correct attribute values.
64 Tests that the plugin definition exposes the expected values for
65 name and can_fix attributes.
67 Args:
68 get_plugin: Fixture factory to get plugin instances.
69 attr: The attribute name to check on the definition.
70 expected: The expected value of the attribute.
71 """
72 prettier_plugin = get_plugin("prettier")
73 assert_that(getattr(prettier_plugin.definition, attr)).is_equal_to(expected)
76def test_definition_file_patterns(get_plugin: Callable[[str], BaseToolPlugin]) -> None:
77 """Verify PrettierPlugin definition includes non-JS/TS file patterns.
79 Tests that the plugin is configured to handle CSS, HTML, JSON, YAML, Markdown,
80 GraphQL, and Astro files. JS/TS files are handled by oxfmt for better performance.
82 Args:
83 get_plugin: Fixture factory to get plugin instances.
84 """
85 prettier_plugin = get_plugin("prettier")
86 patterns = prettier_plugin.definition.file_patterns
87 # Prettier handles non-JS/TS files (JS/TS delegated to oxfmt)
88 has_expected_patterns = "*.css" in patterns and "*.json" in patterns
89 assert_that(has_expected_patterns).is_true()
90 # Verify Astro pattern is included
91 assert_that("*.astro" in patterns).is_true()
92 # Verify JS/TS patterns are NOT in prettier (handled by oxfmt)
93 assert_that("*.js" in patterns or "*.ts" in patterns).is_false()
96# --- Integration tests for prettier check command ---
99@pytest.fixture
100def temp_json_file_formatted(tmp_path: Path) -> str:
101 """Create a temporary JSON file that is already formatted.
103 Creates a file containing properly formatted JSON that
104 Prettier should leave unchanged.
106 Args:
107 tmp_path: Pytest fixture providing a temporary directory.
109 Returns:
110 Path to the created file as a string.
111 """
112 file_path = tmp_path / "formatted.json"
113 file_path.write_text(
114 """\
115{
116 "name": "test",
117 "version": "1.0.0",
118 "dependencies": {}
119}
120""",
121 )
122 return str(file_path)
125@pytest.mark.parametrize(
126 ("file_fixture", "expect_issues"),
127 [
128 ("temp_json_file_unformatted", True),
129 ("temp_json_file_formatted", False),
130 ],
131 ids=["unformatted_json", "formatted_json"],
132)
133def test_check_json_file_formatting_state(
134 get_plugin: Callable[[str], BaseToolPlugin],
135 file_fixture: str,
136 expect_issues: bool,
137 request: pytest.FixtureRequest,
138) -> None:
139 """Verify Prettier check correctly detects JSON file formatting state.
141 Runs Prettier in check mode on files with different formatting states
142 and verifies the expected issue count. Note: JS/TS files are handled
143 by oxfmt, so we test with JSON files here.
145 Args:
146 get_plugin: Fixture factory to get plugin instances.
147 file_fixture: Name of the fixture providing the file path.
148 expect_issues: Whether issues are expected (True for unformatted).
149 request: Pytest request fixture for dynamic fixture access.
150 """
151 file_path = request.getfixturevalue(file_fixture)
152 prettier_plugin = get_plugin("prettier")
153 result = prettier_plugin.check([file_path], {})
155 assert_that(result).is_not_none()
156 assert_that(result.name).is_equal_to("prettier")
157 if expect_issues:
158 assert_that(result.issues_count).is_greater_than(0)
159 else:
160 assert_that(result.issues_count).is_equal_to(0)
163# --- Integration tests for prettier fix command ---
166def test_fix_formats_json_file(
167 get_plugin: Callable[[str], BaseToolPlugin],
168 temp_json_file_unformatted: str,
169) -> None:
170 """Verify Prettier fix reformats JSON files.
172 Runs Prettier fix on a JSON file and verifies the file is
173 formatted with proper indentation.
175 Args:
176 get_plugin: Fixture factory to get plugin instances.
177 temp_json_file_unformatted: Path to JSON file with formatting issues.
178 """
179 prettier_plugin = get_plugin("prettier")
180 original = Path(temp_json_file_unformatted).read_text()
182 result = prettier_plugin.fix([temp_json_file_unformatted], {})
184 assert_that(result).is_not_none()
185 assert_that(result.success).is_true()
187 new_content = Path(temp_json_file_unformatted).read_text()
188 assert_that(new_content).is_not_equal_to(original)
191# --- Tests for PrettierPlugin.set_options method ---
194@pytest.mark.parametrize(
195 ("option_name", "option_value", "expected"),
196 [
197 ("verbose_fix_output", True, True),
198 ("line_length", 120, 120),
199 ],
200 ids=["verbose_fix_output", "line_length"],
201)
202def test_set_options(
203 get_plugin: Callable[[str], BaseToolPlugin],
204 option_name: str,
205 option_value: object,
206 expected: object,
207) -> None:
208 """Verify PrettierPlugin.set_options correctly sets various options.
210 Tests that plugin options can be set and retrieved correctly.
211 Note: Formatting options like tab_width, use_tabs, single_quote are
212 configured via .prettierrc config file, not via set_options.
214 Args:
215 get_plugin: Fixture factory to get plugin instances.
216 option_name: Name of the option to set.
217 option_value: Value to set for the option.
218 expected: Expected value when retrieving the option.
219 """
220 prettier_plugin = get_plugin("prettier")
221 prettier_plugin.set_options(**{option_name: option_value})
222 assert_that(prettier_plugin.options.get(option_name)).is_equal_to(expected)
225# --- Integration tests for Astro file formatting ---
227PROJECT_ROOT = Path(__file__).resolve().parents[3]
230@pytest.fixture
231def astro_project_dir(tmp_path: Path) -> Path:
232 """Create a temporary project directory with prettier-plugin-astro configured.
234 Sets up a .prettierrc that loads prettier-plugin-astro so Prettier
235 can parse and format .astro files. Symlinks node_modules from the
236 project root so the plugin can be resolved.
238 Args:
239 tmp_path: Pytest fixture providing a temporary directory.
241 Returns:
242 Path to the temporary project directory.
243 """
244 # Symlink node_modules so prettier can resolve the astro plugin
245 project_node_modules = PROJECT_ROOT / "node_modules"
246 if not project_node_modules.exists():
247 pytest.skip("node_modules not found; run bun install first")
248 try:
249 (tmp_path / "node_modules").symlink_to(project_node_modules)
250 except OSError as exc:
251 pytest.skip(f"Cannot create symlink: {exc}")
253 prettierrc = {"plugins": ["prettier-plugin-astro"]}
254 (tmp_path / ".prettierrc").write_text(json.dumps(prettierrc))
255 return tmp_path
258@pytest.fixture
259def temp_astro_file_unformatted(astro_project_dir: Path) -> str:
260 """Create a temporary Astro file with formatting issues.
262 Creates a file with bad indentation, missing semicolons, and
263 inconsistent quotes that Prettier should reformat.
265 Args:
266 astro_project_dir: Temporary project with .prettierrc configured.
268 Returns:
269 Path to the created file as a string.
270 """
271 file_path = astro_project_dir / "unformatted.astro"
272 file_path.write_text(
273 '---\nconst greeting = "Hello"\n'
274 'const items = ["apple","banana","cherry"]\n'
275 "---\n\n<html>\n<head>\n<title>Test</title>\n"
276 "</head>\n<body>\n <h1>{greeting}</h1>\n"
277 "<ul>\n{items.map((item) => (\n<li>{item}</li>\n"
278 "))}\n</ul>\n</body>\n</html>\n",
279 )
280 return str(file_path)
283@pytest.fixture
284def temp_astro_file_formatted(astro_project_dir: Path) -> str:
285 """Create a temporary Astro file that is already formatted.
287 Creates a file that conforms to Prettier's default formatting
288 with prettier-plugin-astro.
290 Args:
291 astro_project_dir: Temporary project with .prettierrc configured.
293 Returns:
294 Path to the created file as a string.
295 """
296 file_path = astro_project_dir / "formatted.astro"
297 file_path.write_text(
298 '---\nconst greeting = "Hello";\n'
299 'const items = ["apple", "banana", "cherry"];\n'
300 "---\n\n<html>\n <head>\n <title>Test</title>\n"
301 " </head>\n <body>\n <h1>{greeting}</h1>\n"
302 " <ul>\n {items.map((item) => <li>{item}</li>)}\n"
303 " </ul>\n </body>\n</html>\n",
304 )
305 return str(file_path)
308@pytest.mark.parametrize(
309 ("file_fixture", "expect_issues"),
310 [
311 ("temp_astro_file_unformatted", True),
312 ("temp_astro_file_formatted", False),
313 ],
314 ids=["unformatted_astro", "formatted_astro"],
315)
316def test_check_astro_file_formatting_state(
317 get_plugin: Callable[[str], BaseToolPlugin],
318 file_fixture: str,
319 expect_issues: bool,
320 request: pytest.FixtureRequest,
321) -> None:
322 """Verify Prettier check correctly detects Astro file formatting state.
324 Runs Prettier in check mode on .astro files with different formatting
325 states and verifies the expected issue count. Requires prettier-plugin-astro
326 to be installed and configured via .prettierrc.
328 Args:
329 get_plugin: Fixture factory to get plugin instances.
330 file_fixture: Name of the fixture providing the file path.
331 expect_issues: Whether issues are expected (True for unformatted).
332 request: Pytest request fixture for dynamic fixture access.
333 """
334 file_path = request.getfixturevalue(file_fixture)
335 prettier_plugin = get_plugin("prettier")
336 result = prettier_plugin.check([file_path], {})
338 assert_that(result).is_not_none()
339 assert_that(result.name).is_equal_to("prettier")
340 if expect_issues:
341 assert_that(result.issues_count).is_greater_than(0)
342 else:
343 assert_that(result.issues_count).is_equal_to(0)
346def test_fix_formats_astro_file(
347 get_plugin: Callable[[str], BaseToolPlugin],
348 temp_astro_file_unformatted: str,
349) -> None:
350 """Verify Prettier fix reformats Astro files.
352 Runs Prettier fix on an Astro file and verifies the file is
353 reformatted with proper indentation and consistent style.
355 Args:
356 get_plugin: Fixture factory to get plugin instances.
357 temp_astro_file_unformatted: Path to Astro file with formatting issues.
358 """
359 prettier_plugin = get_plugin("prettier")
360 original = Path(temp_astro_file_unformatted).read_text()
362 result = prettier_plugin.fix([temp_astro_file_unformatted], {})
364 assert_that(result).is_not_none()
365 assert_that(result.success).is_true()
367 new_content = Path(temp_astro_file_unformatted).read_text()
368 assert_that(new_content).is_not_equal_to(original)