1 | //===- unittest/Format/SortImportsTestJS.cpp - JS import sort unit tests --===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include "FormatTestUtils.h" |
10 | #include "clang/Format/Format.h" |
11 | #include "llvm/Support/Debug.h" |
12 | #include "gtest/gtest.h" |
13 | |
14 | #define DEBUG_TYPE "format-test" |
15 | |
16 | namespace clang { |
17 | namespace format { |
18 | namespace { |
19 | |
20 | class SortImportsTestJS : public ::testing::Test { |
21 | protected: |
22 | std::string sort(StringRef Code, unsigned Offset = 0, unsigned Length = 0) { |
23 | StringRef FileName = "input.js"; |
24 | if (Length == 0U) |
25 | Length = Code.size() - Offset; |
26 | std::vector<tooling::Range> Ranges(1, tooling::Range(Offset, Length)); |
27 | auto Sorted = |
28 | applyAllReplacements(Code, sortIncludes(Style, Code, Ranges, FileName)); |
29 | EXPECT_TRUE(static_cast<bool>(Sorted)); |
30 | auto Formatted = applyAllReplacements( |
31 | *Sorted, reformat(Style, *Sorted, Ranges, FileName)); |
32 | EXPECT_TRUE(static_cast<bool>(Formatted)); |
33 | return *Formatted; |
34 | } |
35 | |
36 | void verifySort(llvm::StringRef Expected, llvm::StringRef Code, |
37 | unsigned Offset = 0, unsigned Length = 0) { |
38 | std::string Result = sort(Code, Offset, Length); |
39 | EXPECT_EQ(Expected.str(), Result) << "Expected:\n" |
40 | << Expected << "\nActual:\n" |
41 | << Result; |
42 | } |
43 | |
44 | FormatStyle Style = getGoogleStyle(FormatStyle::LK_JavaScript); |
45 | }; |
46 | |
47 | TEST_F(SortImportsTestJS, AlreadySorted) { |
48 | verifySort("import {sym} from 'a';\n" |
49 | "import {sym} from 'b';\n" |
50 | "import {sym} from 'c';\n" |
51 | "\n" |
52 | "let x = 1;", |
53 | "import {sym} from 'a';\n" |
54 | "import {sym} from 'b';\n" |
55 | "import {sym} from 'c';\n" |
56 | "\n" |
57 | "let x = 1;"); |
58 | } |
59 | |
60 | TEST_F(SortImportsTestJS, BasicSorting) { |
61 | verifySort("import {sym} from 'a';\n" |
62 | "import {sym} from 'b';\n" |
63 | "import {sym} from 'c';\n" |
64 | "\n" |
65 | "let x = 1;", |
66 | "import {sym} from 'a';\n" |
67 | "import {sym} from 'c';\n" |
68 | "import {sym} from 'b';\n" |
69 | "let x = 1;"); |
70 | } |
71 | |
72 | TEST_F(SortImportsTestJS, DefaultBinding) { |
73 | verifySort("import A from 'a';\n" |
74 | "import B from 'b';\n" |
75 | "\n" |
76 | "let x = 1;", |
77 | "import B from 'b';\n" |
78 | "import A from 'a';\n" |
79 | "let x = 1;"); |
80 | } |
81 | |
82 | TEST_F(SortImportsTestJS, DefaultAndNamedBinding) { |
83 | verifySort("import A, {a} from 'a';\n" |
84 | "import B, {b} from 'b';\n" |
85 | "\n" |
86 | "let x = 1;", |
87 | "import B, {b} from 'b';\n" |
88 | "import A, {a} from 'a';\n" |
89 | "let x = 1;"); |
90 | } |
91 | |
92 | TEST_F(SortImportsTestJS, WrappedImportStatements) { |
93 | verifySort("import {sym1, sym2} from 'a';\n" |
94 | "import {sym} from 'b';\n" |
95 | "\n" |
96 | "1;", |
97 | "import\n" |
98 | " {sym}\n" |
99 | " from 'b';\n" |
100 | "import {\n" |
101 | " sym1,\n" |
102 | " sym2\n" |
103 | "} from 'a';\n" |
104 | "1;"); |
105 | } |
106 | |
107 | TEST_F(SortImportsTestJS, SeparateMainCodeBody) { |
108 | verifySort("import {sym} from 'a';" |
109 | "\n" |
110 | "let x = 1;\n", |
111 | "import {sym} from 'a'; let x = 1;\n"); |
112 | } |
113 | |
114 | TEST_F(SortImportsTestJS, Comments) { |
115 | verifySort("/** @fileoverview This is a great file. */\n" |
116 | "// A very important import follows.\n" |
117 | "import {sym} from 'a'; /* more comments */\n" |
118 | "import {sym} from 'b'; // from //foo:bar\n", |
119 | "/** @fileoverview This is a great file. */\n" |
120 | "import {sym} from 'b'; // from //foo:bar\n" |
121 | "// A very important import follows.\n" |
122 | "import {sym} from 'a'; /* more comments */\n"); |
123 | verifySort("import {sym} from 'a';\n" |
124 | "import {sym} from 'b';\n" |
125 | "\n" |
126 | "/** Comment on variable. */\n" |
127 | "const x = 1;\n", |
128 | "import {sym} from 'b';\n" |
129 | "import {sym} from 'a';\n" |
130 | "\n" |
131 | "/** Comment on variable. */\n" |
132 | "const x = 1;\n"); |
133 | } |
134 | |
135 | TEST_F(SortImportsTestJS, SortStar) { |
136 | verifySort("import * as foo from 'a';\n" |
137 | "import {sym} from 'a';\n" |
138 | "import * as bar from 'b';\n", |
139 | "import {sym} from 'a';\n" |
140 | "import * as foo from 'a';\n" |
141 | "import * as bar from 'b';\n"); |
142 | } |
143 | |
144 | TEST_F(SortImportsTestJS, AliasesSymbols) { |
145 | verifySort("import {sym1 as alias1} from 'b';\n" |
146 | "import {sym2 as alias2, sym3 as alias3} from 'c';\n", |
147 | "import {sym2 as alias2, sym3 as alias3} from 'c';\n" |
148 | "import {sym1 as alias1} from 'b';\n"); |
149 | } |
150 | |
151 | TEST_F(SortImportsTestJS, SortSymbols) { |
152 | verifySort("import {sym1, sym2 as a, sym3} from 'b';\n", |
153 | "import {sym2 as a, sym1, sym3} from 'b';\n"); |
154 | verifySort("import {sym1 /* important! */, /*!*/ sym2 as a} from 'b';\n", |
155 | "import {/*!*/ sym2 as a, sym1 /* important! */} from 'b';\n"); |
156 | verifySort("import {sym1, sym2} from 'b';\n", "import {\n" |
157 | " sym2 \n" |
158 | ",\n" |
159 | " sym1 \n" |
160 | "} from 'b';\n"); |
161 | } |
162 | |
163 | TEST_F(SortImportsTestJS, GroupImports) { |
164 | verifySort("import {a} from 'absolute';\n" |
165 | "\n" |
166 | "import {b} from '../parent';\n" |
167 | "import {b} from '../parent/nested';\n" |
168 | "\n" |
169 | "import {b} from './relative/path';\n" |
170 | "import {b} from './relative/path/nested';\n" |
171 | "\n" |
172 | "let x = 1;\n", |
173 | "import {b} from './relative/path/nested';\n" |
174 | "import {b} from './relative/path';\n" |
175 | "import {b} from '../parent/nested';\n" |
176 | "import {b} from '../parent';\n" |
177 | "import {a} from 'absolute';\n" |
178 | "let x = 1;\n"); |
179 | } |
180 | |
181 | TEST_F(SortImportsTestJS, Exports) { |
182 | verifySort("import {S} from 'bpath';\n" |
183 | "\n" |
184 | "import {T} from './cpath';\n" |
185 | "\n" |
186 | "export {A, B} from 'apath';\n" |
187 | "export {P} from '../parent';\n" |
188 | "export {R} from './relative';\n" |
189 | "export {S};\n" |
190 | "\n" |
191 | "let x = 1;\n" |
192 | "export y = 1;\n", |
193 | "export {R} from './relative';\n" |
194 | "import {T} from './cpath';\n" |
195 | "export {S};\n" |
196 | "export {A, B} from 'apath';\n" |
197 | "import {S} from 'bpath';\n" |
198 | "export {P} from '../parent';\n" |
199 | "let x = 1;\n" |
200 | "export y = 1;\n"); |
201 | verifySort("import {S} from 'bpath';\n" |
202 | "\n" |
203 | "export {T} from 'epath';\n", |
204 | "export {T} from 'epath';\n" |
205 | "import {S} from 'bpath';\n"); |
206 | } |
207 | |
208 | TEST_F(SortImportsTestJS, SideEffectImports) { |
209 | verifySort("import 'ZZside-effect';\n" |
210 | "import 'AAside-effect';\n" |
211 | "\n" |
212 | "import {A} from 'absolute';\n" |
213 | "\n" |
214 | "import {R} from './relative';\n", |
215 | "import {R} from './relative';\n" |
216 | "import 'ZZside-effect';\n" |
217 | "import {A} from 'absolute';\n" |
218 | "import 'AAside-effect';\n"); |
219 | } |
220 | |
221 | TEST_F(SortImportsTestJS, AffectedRange) { |
222 | // Affected range inside of import statements. |
223 | verifySort("import {sym} from 'a';\n" |
224 | "import {sym} from 'b';\n" |
225 | "import {sym} from 'c';\n" |
226 | "\n" |
227 | "let x = 1;", |
228 | "import {sym} from 'c';\n" |
229 | "import {sym} from 'b';\n" |
230 | "import {sym} from 'a';\n" |
231 | "let x = 1;", |
232 | 0, 30); |
233 | // Affected range outside of import statements. |
234 | verifySort("import {sym} from 'c';\n" |
235 | "import {sym} from 'b';\n" |
236 | "import {sym} from 'a';\n" |
237 | "\n" |
238 | "let x = 1;", |
239 | "import {sym} from 'c';\n" |
240 | "import {sym} from 'b';\n" |
241 | "import {sym} from 'a';\n" |
242 | "\n" |
243 | "let x = 1;", |
244 | 70, 1); |
245 | } |
246 | |
247 | TEST_F(SortImportsTestJS, SortingCanShrink) { |
248 | // Sort excluding a suffix. |
249 | verifySort("import {B} from 'a';\n" |
250 | "import {A} from 'b';\n" |
251 | "\n" |
252 | "1;", |
253 | "import {A} from 'b';\n" |
254 | "\n" |
255 | "import {B} from 'a';\n" |
256 | "\n" |
257 | "1;"); |
258 | } |
259 | |
260 | TEST_F(SortImportsTestJS, TrailingComma) { |
261 | verifySort("import {A, B,} from 'aa';\n", "import {B, A,} from 'aa';\n"); |
262 | } |
263 | |
264 | TEST_F(SortImportsTestJS, SortCaseInsensitive) { |
265 | verifySort("import {A} from 'aa';\n" |
266 | "import {A} from 'Ab';\n" |
267 | "import {A} from 'b';\n" |
268 | "import {A} from 'Bc';\n" |
269 | "\n" |
270 | "1;", |
271 | "import {A} from 'b';\n" |
272 | "import {A} from 'Bc';\n" |
273 | "import {A} from 'Ab';\n" |
274 | "import {A} from 'aa';\n" |
275 | "\n" |
276 | "1;"); |
277 | verifySort("import {aa, Ab, b, Bc} from 'x';\n" |
278 | "\n" |
279 | "1;", |
280 | "import {b, Bc, Ab, aa} from 'x';\n" |
281 | "\n" |
282 | "1;"); |
283 | } |
284 | |
285 | TEST_F(SortImportsTestJS, SortMultiLine) { |
286 | // Reproduces issue where multi-line import was not parsed correctly. |
287 | verifySort("import {A} from 'a';\n" |
288 | "import {A} from 'b';\n" |
289 | "\n" |
290 | "1;", |
291 | "import\n" |
292 | "{\n" |
293 | "A\n" |
294 | "}\n" |
295 | "from\n" |
296 | "'b';\n" |
297 | "import {A} from 'a';\n" |
298 | "\n" |
299 | "1;"); |
300 | } |
301 | |
302 | TEST_F(SortImportsTestJS, SortDefaultImports) { |
303 | // Reproduces issue where multi-line import was not parsed correctly. |
304 | verifySort("import {A} from 'a';\n" |
305 | "import {default as B} from 'b';\n", |
306 | "import {default as B} from 'b';\n" |
307 | "import {A} from 'a';\n"); |
308 | } |
309 | |
310 | } // end namespace |
311 | } // end namespace format |
312 | } // end namespace clang |
313 | |