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:
sort(StringRef Code,unsigned Offset=0,unsigned Length=0)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 
verifySort(llvm::StringRef Expected,llvm::StringRef Code,unsigned Offset=0,unsigned Length=0)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 
TEST_F(SortImportsTestJS,AlreadySorted)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 
TEST_F(SortImportsTestJS,BasicSorting)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 
TEST_F(SortImportsTestJS,DefaultBinding)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 
TEST_F(SortImportsTestJS,DefaultAndNamedBinding)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 
TEST_F(SortImportsTestJS,WrappedImportStatements)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 
TEST_F(SortImportsTestJS,SeparateMainCodeBody)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 
TEST_F(SortImportsTestJS,Comments)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 
TEST_F(SortImportsTestJS,SortStar)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 
TEST_F(SortImportsTestJS,AliasesSymbols)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 
TEST_F(SortImportsTestJS,SortSymbols)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 
TEST_F(SortImportsTestJS,GroupImports)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 
TEST_F(SortImportsTestJS,Exports)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 
TEST_F(SortImportsTestJS,SideEffectImports)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 
TEST_F(SortImportsTestJS,AffectedRange)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 
TEST_F(SortImportsTestJS,SortingCanShrink)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 
TEST_F(SortImportsTestJS,TrailingComma)260 TEST_F(SortImportsTestJS, TrailingComma) {
261   verifySort("import {A, B,} from 'aa';\n", "import {B, A,} from 'aa';\n");
262 }
263 
TEST_F(SortImportsTestJS,SortCaseInsensitive)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 
TEST_F(SortImportsTestJS,SortMultiLine)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 
TEST_F(SortImportsTestJS,SortDefaultImports)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