Coverage for tests / unit / tools / vue_tsc / test_vue_tsc_plugin.py: 100%
70 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 VueTscPlugin temp tsconfig functionality."""
3from __future__ import annotations
5import json
6from pathlib import Path
8from assertpy import assert_that
10from lintro.tools.definitions.vue_tsc import VueTscPlugin
12# =============================================================================
13# Tests for JSONC tsconfig parsing (issue #570)
14# =============================================================================
17def test_create_temp_tsconfig_preserves_type_roots_from_jsonc_base(
18 vue_tsc_plugin: VueTscPlugin,
19 tmp_path: Path,
20) -> None:
21 """Verify typeRoots are preserved when base tsconfig uses JSONC features.
23 This is the primary scenario from issue #570: a tsconfig.json with
24 comments and trailing commas should still have its typeRoots read
25 and propagated into the temporary tsconfig.
27 Args:
28 vue_tsc_plugin: Plugin instance fixture.
29 tmp_path: Pytest temporary directory.
30 """
31 base_tsconfig = tmp_path / "tsconfig.json"
32 base_tsconfig.write_text(
33 """{
34 // TypeScript config with JSONC features
35 "compilerOptions": {
36 "strict": true,
37 /* Custom type roots for this project */
38 "typeRoots": [
39 "./custom-types",
40 "./node_modules/@types",
41 ],
42 },
43}""",
44 )
46 temp_path = vue_tsc_plugin._create_temp_tsconfig(
47 base_tsconfig=base_tsconfig,
48 files=["src/App.vue"],
49 cwd=tmp_path,
50 )
52 try:
53 content = json.loads(temp_path.read_text())
54 # typeRoots should be resolved to absolute paths
55 type_roots = content["compilerOptions"]["typeRoots"]
56 assert_that(type_roots).is_length(2)
57 assert_that(type_roots[0]).ends_with("custom-types")
58 assert_that(type_roots[1]).ends_with("node_modules/@types")
59 finally:
60 temp_path.unlink(missing_ok=True)
63def test_create_temp_tsconfig_no_type_roots_when_base_has_none(
64 vue_tsc_plugin: VueTscPlugin,
65 tmp_path: Path,
66) -> None:
67 """Verify no typeRoots are added when the base config has none.
69 Args:
70 vue_tsc_plugin: Plugin instance fixture.
71 tmp_path: Pytest temporary directory.
72 """
73 base_tsconfig = tmp_path / "tsconfig.json"
74 base_tsconfig.write_text('{"compilerOptions": {"strict": true}}')
76 temp_path = vue_tsc_plugin._create_temp_tsconfig(
77 base_tsconfig=base_tsconfig,
78 files=["src/App.vue"],
79 cwd=tmp_path,
80 )
82 try:
83 content = json.loads(temp_path.read_text())
84 assert_that(content["compilerOptions"]).does_not_contain_key("typeRoots")
85 finally:
86 temp_path.unlink(missing_ok=True)
89def test_create_temp_tsconfig_ignores_non_list_type_roots(
90 vue_tsc_plugin: VueTscPlugin,
91 tmp_path: Path,
92) -> None:
93 """Verify malformed typeRoots (non-list) are safely ignored.
95 Args:
96 vue_tsc_plugin: Plugin instance fixture.
97 tmp_path: Pytest temporary directory.
98 """
99 base_tsconfig = tmp_path / "tsconfig.json"
100 base_tsconfig.write_text(
101 '{"compilerOptions": {"typeRoots": "not-a-list"}}',
102 )
104 temp_path = vue_tsc_plugin._create_temp_tsconfig(
105 base_tsconfig=base_tsconfig,
106 files=["src/App.vue"],
107 cwd=tmp_path,
108 )
110 try:
111 content = json.loads(temp_path.read_text())
112 assert_that(content["compilerOptions"]).does_not_contain_key("typeRoots")
113 finally:
114 temp_path.unlink(missing_ok=True)
117def test_create_temp_tsconfig_filters_non_string_type_roots(
118 vue_tsc_plugin: VueTscPlugin,
119 tmp_path: Path,
120) -> None:
121 """Verify non-string entries in typeRoots are filtered out.
123 Args:
124 vue_tsc_plugin: Plugin instance fixture.
125 tmp_path: Pytest temporary directory.
126 """
127 base_tsconfig = tmp_path / "tsconfig.json"
128 base_tsconfig.write_text(
129 '{"compilerOptions": {"typeRoots": ["./valid-types", 123, null, true]}}',
130 )
132 temp_path = vue_tsc_plugin._create_temp_tsconfig(
133 base_tsconfig=base_tsconfig,
134 files=["src/App.vue"],
135 cwd=tmp_path,
136 )
138 try:
139 content = json.loads(temp_path.read_text())
140 type_roots = content["compilerOptions"]["typeRoots"]
141 assert_that(type_roots).is_length(1)
142 assert_that(type_roots[0]).ends_with("valid-types")
143 finally:
144 temp_path.unlink(missing_ok=True)
147def test_create_temp_tsconfig_fallback_honours_empty_typeroots(
148 vue_tsc_plugin: VueTscPlugin,
149 tmp_path: Path,
150) -> None:
151 """Verify fallback preserves explicit empty typeRoots.
153 When the base tsconfig explicitly sets ``typeRoots: []`` to disable
154 automatic global type discovery, the fallback path must honour that
155 intent and NOT inject the default ``node_modules/@types`` root.
157 Args:
158 vue_tsc_plugin: Plugin instance fixture.
159 tmp_path: Pytest temporary directory.
160 """
161 import tempfile
162 from typing import Any
163 from unittest.mock import patch
165 base_tsconfig = tmp_path / "tsconfig.json"
166 base_tsconfig.write_text(
167 json.dumps(
168 {
169 "compilerOptions": {
170 "typeRoots": [],
171 },
172 },
173 ),
174 )
176 original_mkstemp = tempfile.mkstemp
177 call_count = 0
179 def mock_mkstemp(**kwargs: Any) -> tuple[int, str]:
180 nonlocal call_count
181 call_count += 1
182 if call_count == 1:
183 raise OSError("Read-only filesystem")
184 return original_mkstemp(**kwargs)
186 with patch("tempfile.mkstemp", side_effect=mock_mkstemp):
187 temp_path = vue_tsc_plugin._create_temp_tsconfig(
188 base_tsconfig=base_tsconfig,
189 files=["src/App.vue"],
190 cwd=tmp_path,
191 )
193 try:
194 content = json.loads(temp_path.read_text())
195 type_roots = content["compilerOptions"]["typeRoots"]
196 assert_that(type_roots).is_empty()
197 finally:
198 temp_path.unlink(missing_ok=True)
201def test_create_temp_tsconfig_ignores_non_dict_compiler_options(
202 vue_tsc_plugin: VueTscPlugin,
203 tmp_path: Path,
204) -> None:
205 """Verify malformed compilerOptions (non-dict) are safely ignored.
207 Args:
208 vue_tsc_plugin: Plugin instance fixture.
209 tmp_path: Pytest temporary directory.
210 """
211 base_tsconfig = tmp_path / "tsconfig.json"
212 base_tsconfig.write_text('{"compilerOptions": "not-a-dict"}')
214 temp_path = vue_tsc_plugin._create_temp_tsconfig(
215 base_tsconfig=base_tsconfig,
216 files=["src/App.vue"],
217 cwd=tmp_path,
218 )
220 try:
221 content = json.loads(temp_path.read_text())
222 assert_that(content["compilerOptions"]).does_not_contain_key("typeRoots")
223 finally:
224 temp_path.unlink(missing_ok=True)