Coverage for tests / unit / tools / vue_tsc / test_execution.py: 100%
85 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"""Unit tests for vue-tsc plugin check method execution."""
3from __future__ import annotations
5from pathlib import Path
6from typing import Any
7from unittest.mock import patch
9import pytest
10from assertpy import assert_that
12from lintro.parsers.vue_tsc.vue_tsc_issue import VueTscIssue
13from lintro.tools.definitions.vue_tsc import VueTscPlugin
16def _mock_subprocess_success(**kwargs: Any) -> tuple[bool, str]:
17 """Mock subprocess that returns success with no output.
19 Args:
20 **kwargs: Ignored keyword arguments.
22 Returns:
23 Tuple of (success=True, empty string).
24 """
25 return (True, "")
28def _mock_subprocess_with_issues(**kwargs: Any) -> tuple[bool, str]:
29 """Mock subprocess that returns output with type errors.
31 Args:
32 **kwargs: Ignored keyword arguments.
34 Returns:
35 Tuple of (success=False, error output).
36 """
37 output = (
38 "src/components/Button.vue(10,5): error TS2322: "
39 "Type 'string' is not assignable to type 'number'.\n"
40 "src/components/Card.vue(15,10): error TS2339: "
41 "Property 'foo' does not exist on type 'Props'."
42 )
43 return (False, output)
46def test_check_no_vue_files(
47 vue_tsc_plugin: VueTscPlugin,
48 tmp_path: Path,
49) -> None:
50 """Check returns early when no Vue files found.
52 Args:
53 vue_tsc_plugin: The VueTscPlugin instance to test.
54 tmp_path: Temporary directory path for test files.
55 """
56 # Create a non-Vue file
57 test_file = tmp_path / "test.ts"
58 test_file.write_text("const x = 1;")
60 result = vue_tsc_plugin.check([str(test_file)], {})
62 assert_that(result.success).is_true()
63 assert_that(result.issues_count).is_equal_to(0)
66def test_check_with_mocked_subprocess_success(
67 vue_tsc_plugin: VueTscPlugin,
68 tmp_path: Path,
69) -> None:
70 """Check returns success when vue-tsc finds no issues.
72 Args:
73 vue_tsc_plugin: The VueTscPlugin instance to test.
74 tmp_path: Temporary directory path for test files.
75 """
76 # Create Vue file and tsconfig
77 vue_file = tmp_path / "test.vue"
78 vue_file.write_text("<template><div>Hello</div></template>")
79 tsconfig = tmp_path / "tsconfig.json"
80 tsconfig.write_text('{"compilerOptions": {}}')
82 with patch.object(
83 vue_tsc_plugin,
84 "_run_subprocess",
85 side_effect=_mock_subprocess_success,
86 ):
87 result = vue_tsc_plugin.check([str(tmp_path)], {})
89 assert_that(result.success).is_true()
90 assert_that(result.issues_count).is_equal_to(0)
93def test_check_with_mocked_subprocess_issues_found(
94 vue_tsc_plugin: VueTscPlugin,
95 tmp_path: Path,
96) -> None:
97 """Check returns issues when vue-tsc finds type errors.
99 Args:
100 vue_tsc_plugin: The VueTscPlugin instance to test.
101 tmp_path: Temporary directory path for test files.
102 """
103 # Create Vue file and tsconfig
104 vue_file = tmp_path / "Button.vue"
105 vue_file.write_text("<template><div>Hello</div></template>")
106 tsconfig = tmp_path / "tsconfig.json"
107 tsconfig.write_text('{"compilerOptions": {}}')
109 with patch.object(
110 vue_tsc_plugin,
111 "_run_subprocess",
112 side_effect=_mock_subprocess_with_issues,
113 ):
114 result = vue_tsc_plugin.check([str(tmp_path)], {})
116 assert_that(result.success).is_false()
117 assert_that(result.issues_count).is_equal_to(2)
118 issues = result.issues
119 assert_that(issues).is_not_none()
120 assert issues is not None
121 assert_that(issues).is_length(2)
123 # Verify first issue
124 first_issue = issues[0]
125 assert isinstance(first_issue, VueTscIssue)
126 assert_that(first_issue.file).is_equal_to("src/components/Button.vue")
127 assert_that(first_issue.line).is_equal_to(10)
128 assert_that(first_issue.code).is_equal_to("TS2322")
131def test_fix_raises_not_implemented(
132 vue_tsc_plugin: VueTscPlugin,
133 tmp_path: Path,
134) -> None:
135 """Fix method raises NotImplementedError.
137 Args:
138 vue_tsc_plugin: The VueTscPlugin instance to test.
139 tmp_path: Temporary directory path for test files.
140 """
141 with pytest.raises(NotImplementedError, match="cannot automatically fix"):
142 vue_tsc_plugin.fix([str(tmp_path)], {})
145def test_check_with_project_option(
146 vue_tsc_plugin: VueTscPlugin,
147 tmp_path: Path,
148) -> None:
149 """Check uses project option when provided.
151 Args:
152 vue_tsc_plugin: The VueTscPlugin instance to test.
153 tmp_path: Temporary directory path for test files.
154 """
155 # Create Vue file and tsconfig
156 vue_file = tmp_path / "test.vue"
157 vue_file.write_text("<template><div>Hello</div></template>")
158 tsconfig = tmp_path / "tsconfig.app.json"
159 tsconfig.write_text('{"compilerOptions": {}}')
161 captured_cmd: list[str] = []
163 def capture_cmd(cmd: list[str], **kwargs: Any) -> tuple[bool, str]:
164 captured_cmd.extend(cmd)
165 return (True, "")
167 with patch.object(
168 vue_tsc_plugin,
169 "_run_subprocess",
170 side_effect=capture_cmd,
171 ):
172 vue_tsc_plugin.check(
173 [str(tmp_path)],
174 {"project": str(tsconfig)},
175 )
177 # Verify --project was passed
178 assert_that(captured_cmd).contains("--project")
179 project_idx = captured_cmd.index("--project")
180 assert_that(captured_cmd[project_idx + 1]).is_equal_to(str(tsconfig))
183def test_check_no_tsconfig_passes_files_directly(
184 vue_tsc_plugin: VueTscPlugin,
185 tmp_path: Path,
186) -> None:
187 """Check passes files directly when no tsconfig.json is found.
189 Args:
190 vue_tsc_plugin: The VueTscPlugin instance to test.
191 tmp_path: Temporary directory path for test files.
192 """
193 # Create Vue file but NO tsconfig.json
194 vue_file = tmp_path / "App.vue"
195 vue_file.write_text("<template><div>Hello</div></template>")
197 captured_cmd: list[str] = []
199 def capture_cmd(cmd: list[str], **kwargs: Any) -> tuple[bool, str]:
200 captured_cmd.extend(cmd)
201 return (True, "")
203 with patch.object(
204 vue_tsc_plugin,
205 "_run_subprocess",
206 side_effect=capture_cmd,
207 ):
208 result = vue_tsc_plugin.check([str(tmp_path)], {})
210 assert_that(result.success).is_true()
211 # No --project flag should be present
212 assert_that(captured_cmd).does_not_contain("--project")
213 # File should be passed directly in the command
214 assert_that(" ".join(captured_cmd)).contains("App.vue")
217def test_check_timeout_handling(
218 vue_tsc_plugin: VueTscPlugin,
219 tmp_path: Path,
220) -> None:
221 """Check handles subprocess timeout gracefully.
223 Args:
224 vue_tsc_plugin: The VueTscPlugin instance to test.
225 tmp_path: Temporary directory path for test files.
226 """
227 import subprocess
229 # Create Vue file and tsconfig
230 vue_file = tmp_path / "test.vue"
231 vue_file.write_text("<template><div>Hello</div></template>")
232 tsconfig = tmp_path / "tsconfig.json"
233 tsconfig.write_text('{"compilerOptions": {}}')
235 with patch.object(
236 vue_tsc_plugin,
237 "_run_subprocess",
238 side_effect=subprocess.TimeoutExpired("vue-tsc", 120),
239 ):
240 result = vue_tsc_plugin.check([str(tmp_path)], {})
242 assert_that(result.success).is_false()
243 assert_that(result.output).contains("timeout")