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