]>
Commit | Line | Data |
---|---|---|
eb39fafa DC |
1 | /** |
2 | * @fileoverview Tests for the prefer-exponentiation-operator rule | |
3 | * @author Milos Djermanovic | |
4 | */ | |
5 | ||
6 | "use strict"; | |
7 | ||
8 | //------------------------------------------------------------------------------ | |
9 | // Requirements | |
10 | //------------------------------------------------------------------------------ | |
11 | ||
12 | const rule = require("../../../lib/rules/prefer-exponentiation-operator"); | |
13 | const { RuleTester } = require("../../../lib/rule-tester"); | |
14 | ||
15 | //------------------------------------------------------------------------------ | |
16 | // Helpers | |
17 | //------------------------------------------------------------------------------ | |
18 | ||
19 | /** | |
20 | * Create an object for the invalid array | |
21 | * @param {string} code source code | |
22 | * @param {string} output fixed source code | |
23 | * @returns {Object} result object | |
24 | * @private | |
25 | */ | |
26 | function invalid(code, output) { | |
27 | return { | |
28 | code, | |
29 | output, | |
30 | errors: [ | |
31 | { | |
32 | messageId: "useExponentiation", | |
33 | type: "CallExpression" | |
34 | } | |
35 | ] | |
36 | }; | |
37 | } | |
38 | ||
39 | //------------------------------------------------------------------------------ | |
40 | // Tests | |
41 | //------------------------------------------------------------------------------ | |
42 | ||
6f036462 | 43 | const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 2020 } }); |
eb39fafa DC |
44 | |
45 | ruleTester.run("prefer-exponentiation-operator", rule, { | |
46 | valid: [ | |
47 | ||
48 | // not Math.pow() | |
49 | "Object.pow(a, b)", | |
50 | "Math.max(a, b)", | |
51 | "Math", | |
52 | "Math(a, b)", | |
53 | "pow", | |
54 | "pow(a, b)", | |
55 | "Math.pow", | |
56 | "Math.Pow(a, b)", | |
57 | "math.pow(a, b)", | |
58 | "foo.Math.pow(a, b)", | |
59 | "new Math.pow(a, b)", | |
60 | "Math[pow](a, b)", | |
61 | { code: "globalThis.Object.pow(a, b)", env: { es2020: true } }, | |
62 | { code: "globalThis.Math.max(a, b)", env: { es2020: true } }, | |
63 | ||
64 | // not the global Math | |
65 | "/* globals Math:off*/ Math.pow(a, b)", | |
66 | "let Math; Math.pow(a, b);", | |
67 | "if (foo) { const Math = 1; Math.pow(a, b); }", | |
68 | "var x = function Math() { Math.pow(a, b); }", | |
69 | "function foo(Math) { Math.pow(a, b); }", | |
70 | "function foo() { Math.pow(a, b); var Math; }", | |
71 | ||
72 | "globalThis.Math.pow(a, b)", | |
73 | { code: "globalThis.Math.pow(a, b)", env: { es6: true } }, | |
74 | { code: "globalThis.Math.pow(a, b)", env: { es2017: true } }, | |
75 | { | |
76 | code: ` | |
77 | var globalThis = bar; | |
78 | globalThis.Math.pow(a, b) | |
79 | `, | |
80 | env: { es2020: true } | |
81 | } | |
82 | ], | |
83 | ||
84 | invalid: [ | |
85 | ||
86 | invalid("Math.pow(a, b)", "a**b"), | |
87 | invalid("(Math).pow(a, b)", "a**b"), | |
88 | invalid("Math['pow'](a, b)", "a**b"), | |
89 | invalid("(Math)['pow'](a, b)", "a**b"), | |
90 | invalid("var x=Math\n. pow( a, \n b )", "var x=a**b"), | |
91 | { | |
92 | code: "globalThis.Math.pow(a, b)", | |
93 | output: "a**b", | |
94 | env: { es2020: true }, | |
95 | errors: [ | |
96 | { | |
97 | messageId: "useExponentiation", | |
98 | type: "CallExpression", | |
99 | line: 1, | |
100 | column: 1, | |
101 | endLine: 1, | |
102 | endColumn: 26 | |
103 | } | |
104 | ] | |
105 | }, | |
106 | { | |
107 | code: "globalThis.Math['pow'](a, b)", | |
108 | output: "a**b", | |
109 | env: { es2020: true }, | |
110 | errors: [ | |
111 | { | |
112 | messageId: "useExponentiation", | |
113 | type: "CallExpression", | |
114 | line: 1, | |
115 | column: 1, | |
116 | endLine: 1, | |
117 | endColumn: 29 | |
118 | } | |
119 | ] | |
120 | }, | |
121 | ||
122 | // able to catch some workarounds | |
123 | invalid("Math[`pow`](a, b)", "a**b"), | |
124 | invalid("Math[`${'pow'}`](a, b)", "a**b"), | |
125 | invalid("Math['p' + 'o' + 'w'](a, b)", "a**b"), | |
126 | ||
127 | // non-expression parents that don't require parens | |
128 | invalid("var x = Math.pow(a, b);", "var x = a**b;"), | |
129 | invalid("if(Math.pow(a, b)){}", "if(a**b){}"), | |
130 | invalid("for(;Math.pow(a, b);){}", "for(;a**b;){}"), | |
131 | invalid("switch(foo){ case Math.pow(a, b): break; }", "switch(foo){ case a**b: break; }"), | |
132 | invalid("{ foo: Math.pow(a, b) }", "{ foo: a**b }"), | |
133 | invalid("function foo(bar, baz = Math.pow(a, b), quux){}", "function foo(bar, baz = a**b, quux){}"), | |
134 | invalid("`${Math.pow(a, b)}`", "`${a**b}`"), | |
135 | ||
136 | // non-expression parents that do require parens | |
137 | invalid("class C extends Math.pow(a, b) {}", "class C extends (a**b) {}"), | |
138 | ||
139 | // parents with a higher precedence | |
140 | invalid("+ Math.pow(a, b)", "+ (a**b)"), | |
141 | invalid("- Math.pow(a, b)", "- (a**b)"), | |
142 | invalid("! Math.pow(a, b)", "! (a**b)"), | |
143 | invalid("typeof Math.pow(a, b)", "typeof (a**b)"), | |
144 | invalid("void Math.pow(a, b)", "void (a**b)"), | |
145 | invalid("Math.pow(a, b) .toString()", "(a**b) .toString()"), | |
146 | invalid("Math.pow(a, b) ()", "(a**b) ()"), | |
147 | invalid("Math.pow(a, b) ``", "(a**b) ``"), | |
148 | invalid("(class extends Math.pow(a, b) {})", "(class extends (a**b) {})"), | |
149 | ||
150 | // already parenthesised, shouldn't insert extra parens | |
151 | invalid("+(Math.pow(a, b))", "+(a**b)"), | |
152 | invalid("(Math.pow(a, b)).toString()", "(a**b).toString()"), | |
153 | invalid("(class extends (Math.pow(a, b)) {})", "(class extends (a**b) {})"), | |
154 | invalid("class C extends (Math.pow(a, b)) {}", "class C extends (a**b) {}"), | |
155 | ||
156 | // parents with a higher precedence, but the expression's role doesn't require parens | |
157 | invalid("f(Math.pow(a, b))", "f(a**b)"), | |
158 | invalid("f(foo, Math.pow(a, b))", "f(foo, a**b)"), | |
159 | invalid("f(Math.pow(a, b), foo)", "f(a**b, foo)"), | |
160 | invalid("f(foo, Math.pow(a, b), bar)", "f(foo, a**b, bar)"), | |
161 | invalid("new F(Math.pow(a, b))", "new F(a**b)"), | |
162 | invalid("new F(foo, Math.pow(a, b))", "new F(foo, a**b)"), | |
163 | invalid("new F(Math.pow(a, b), foo)", "new F(a**b, foo)"), | |
164 | invalid("new F(foo, Math.pow(a, b), bar)", "new F(foo, a**b, bar)"), | |
165 | invalid("obj[Math.pow(a, b)]", "obj[a**b]"), | |
166 | invalid("[foo, Math.pow(a, b), bar]", "[foo, a**b, bar]"), | |
167 | ||
168 | // parents with a lower precedence | |
169 | invalid("a * Math.pow(b, c)", "a * b**c"), | |
170 | invalid("Math.pow(a, b) * c", "a**b * c"), | |
171 | invalid("a + Math.pow(b, c)", "a + b**c"), | |
172 | invalid("Math.pow(a, b)/c", "a**b/c"), | |
173 | invalid("a < Math.pow(b, c)", "a < b**c"), | |
174 | invalid("Math.pow(a, b) > c", "a**b > c"), | |
175 | invalid("a === Math.pow(b, c)", "a === b**c"), | |
176 | invalid("a ? Math.pow(b, c) : d", "a ? b**c : d"), | |
177 | invalid("a = Math.pow(b, c)", "a = b**c"), | |
178 | invalid("a += Math.pow(b, c)", "a += b**c"), | |
179 | invalid("function *f() { yield Math.pow(a, b) }", "function *f() { yield a**b }"), | |
180 | invalid("a, Math.pow(b, c), d", "a, b**c, d"), | |
181 | ||
182 | // '**' is right-associative, that applies to both parent and child nodes | |
183 | invalid("a ** Math.pow(b, c)", "a ** b**c"), | |
184 | invalid("Math.pow(a, b) ** c", "(a**b) ** c"), | |
185 | invalid("Math.pow(a, b ** c)", "a**b ** c"), | |
186 | invalid("Math.pow(a ** b, c)", "(a ** b)**c"), | |
187 | invalid("a ** Math.pow(b ** c, d ** e) ** f", "a ** ((b ** c)**d ** e) ** f"), | |
188 | ||
189 | // doesn't remove already existing unnecessary parens around the whole expression | |
190 | invalid("(Math.pow(a, b))", "(a**b)"), | |
191 | invalid("foo + (Math.pow(a, b))", "foo + (a**b)"), | |
192 | invalid("(Math.pow(a, b)) + foo", "(a**b) + foo"), | |
193 | invalid("`${(Math.pow(a, b))}`", "`${(a**b)}`"), | |
194 | ||
195 | // base and exponent with a higher precedence | |
196 | invalid("Math.pow(2, 3)", "2**3"), | |
197 | invalid("Math.pow(a.foo, b)", "a.foo**b"), | |
198 | invalid("Math.pow(a, b.foo)", "a**b.foo"), | |
199 | invalid("Math.pow(a(), b)", "a()**b"), | |
200 | invalid("Math.pow(a, b())", "a**b()"), | |
201 | invalid("Math.pow(++a, ++b)", "++a**++b"), | |
202 | invalid("Math.pow(a++, ++b)", "a++**++b"), | |
203 | invalid("Math.pow(a--, b--)", "a--**b--"), | |
204 | invalid("Math.pow(--a, b--)", "--a**b--"), | |
205 | ||
206 | // doesn't preserve unnecessary parens around base and exponent | |
207 | invalid("Math.pow((a), (b))", "a**b"), | |
208 | invalid("Math.pow(((a)), ((b)))", "a**b"), | |
209 | invalid("Math.pow((a.foo), b)", "a.foo**b"), | |
210 | invalid("Math.pow(a, (b.foo))", "a**b.foo"), | |
211 | invalid("Math.pow((a()), b)", "a()**b"), | |
212 | invalid("Math.pow(a, (b()))", "a**b()"), | |
213 | ||
214 | // unary expressions are exception by the language - parens are required for the base to disambiguate operator precedence | |
215 | invalid("Math.pow(+a, b)", "(+a)**b"), | |
216 | invalid("Math.pow(a, +b)", "a**+b"), | |
217 | invalid("Math.pow(-a, b)", "(-a)**b"), | |
218 | invalid("Math.pow(a, -b)", "a**-b"), | |
219 | invalid("Math.pow(-2, 3)", "(-2)**3"), | |
220 | invalid("Math.pow(2, -3)", "2**-3"), | |
456be15e TL |
221 | invalid("async () => Math.pow(await a, b)", "async () => (await a)**b"), |
222 | invalid("async () => Math.pow(a, await b)", "async () => a**await b"), | |
eb39fafa DC |
223 | |
224 | // base and exponent with a lower precedence | |
225 | invalid("Math.pow(a * b, c)", "(a * b)**c"), | |
226 | invalid("Math.pow(a, b * c)", "a**(b * c)"), | |
227 | invalid("Math.pow(a / b, c)", "(a / b)**c"), | |
228 | invalid("Math.pow(a, b / c)", "a**(b / c)"), | |
229 | invalid("Math.pow(a + b, 3)", "(a + b)**3"), | |
230 | invalid("Math.pow(2, a - b)", "2**(a - b)"), | |
231 | invalid("Math.pow(a + b, c + d)", "(a + b)**(c + d)"), | |
232 | invalid("Math.pow(a = b, c = d)", "(a = b)**(c = d)"), | |
233 | invalid("Math.pow(a += b, c -= d)", "(a += b)**(c -= d)"), | |
234 | invalid("Math.pow((a, b), (c, d))", "(a, b)**(c, d)"), | |
235 | invalid("function *f() { Math.pow(yield, yield) }", "function *f() { (yield)**(yield) }"), | |
236 | ||
237 | // doesn't put extra parens | |
238 | invalid("Math.pow((a + b), (c + d))", "(a + b)**(c + d)"), | |
239 | ||
240 | // tokens that can be adjacent | |
241 | invalid("a+Math.pow(b, c)+d", "a+b**c+d"), | |
242 | ||
243 | // tokens that cannot be adjacent | |
244 | invalid("a+Math.pow(++b, c)", "a+ ++b**c"), | |
245 | invalid("(a)+(Math).pow((++b), c)", "(a)+ ++b**c"), | |
246 | invalid("Math.pow(a, b)in c", "a**b in c"), | |
247 | invalid("Math.pow(a, (b))in (c)", "a**b in (c)"), | |
248 | invalid("a+Math.pow(++b, c)in d", "a+ ++b**c in d"), | |
249 | invalid("a+Math.pow( ++b, c )in d", "a+ ++b**c in d"), | |
250 | ||
251 | // tokens that cannot be adjacent, but there is already space or something else between | |
252 | invalid("a+ Math.pow(++b, c) in d", "a+ ++b**c in d"), | |
253 | invalid("a+/**/Math.pow(++b, c)/**/in d", "a+/**/++b**c/**/in d"), | |
254 | invalid("a+(Math.pow(++b, c))in d", "a+(++b**c)in d"), | |
255 | ||
256 | // tokens that cannot be adjacent, but the autofix inserts parens required for precedence, so there is no need for an extra space | |
257 | invalid("+Math.pow(++a, b)", "+(++a**b)"), | |
258 | invalid("Math.pow(a, b + c)in d", "a**(b + c)in d"), | |
259 | ||
260 | { | |
261 | code: "Math.pow(a, b) + Math.pow(c,\n d)", | |
262 | output: "a**b + c**d", | |
263 | errors: [ | |
264 | { | |
265 | messageId: "useExponentiation", | |
266 | type: "CallExpression", | |
267 | line: 1, | |
268 | column: 1, | |
269 | endLine: 1, | |
270 | endColumn: 15 | |
271 | }, | |
272 | { | |
273 | messageId: "useExponentiation", | |
274 | type: "CallExpression", | |
275 | line: 1, | |
276 | column: 18, | |
277 | endLine: 2, | |
278 | endColumn: 4 | |
279 | } | |
280 | ] | |
281 | }, | |
282 | { | |
283 | code: "Math.pow(Math.pow(a, b), Math.pow(c, d))", | |
284 | output: "Math.pow(a, b)**Math.pow(c, d)", // tests perform only one autofix iteration, below is the following one | |
285 | errors: [ | |
286 | { | |
287 | messageId: "useExponentiation", | |
288 | type: "CallExpression", | |
289 | column: 1, | |
290 | endColumn: 41 | |
291 | }, | |
292 | { | |
293 | messageId: "useExponentiation", | |
294 | type: "CallExpression", | |
295 | column: 10, | |
296 | endColumn: 24 | |
297 | }, | |
298 | { | |
299 | messageId: "useExponentiation", | |
300 | type: "CallExpression", | |
301 | column: 26, | |
302 | endColumn: 40 | |
303 | } | |
304 | ] | |
305 | }, | |
306 | { | |
307 | code: "Math.pow(a, b)**Math.pow(c, d)", | |
308 | output: "(a**b)**c**d", | |
309 | errors: [ | |
310 | { | |
311 | messageId: "useExponentiation", | |
312 | type: "CallExpression", | |
313 | column: 1, | |
314 | endColumn: 15 | |
315 | }, | |
316 | { | |
317 | messageId: "useExponentiation", | |
318 | type: "CallExpression", | |
319 | column: 17, | |
320 | endColumn: 31 | |
321 | } | |
322 | ] | |
323 | }, | |
324 | ||
325 | // shouldn't autofix if the call doesn't have exactly two arguments | |
326 | invalid("Math.pow()", null), | |
327 | invalid("Math.pow(a)", null), | |
328 | invalid("Math.pow(a, b, c)", null), | |
329 | invalid("Math.pow(a, b, c, d)", null), | |
330 | ||
331 | // shouldn't autofix if any of the arguments is spread | |
332 | invalid("Math.pow(...a)", null), | |
333 | invalid("Math.pow(...a, b)", null), | |
334 | invalid("Math.pow(a, ...b)", null), | |
335 | invalid("Math.pow(a, b, ...c)", null), | |
336 | ||
337 | // shouldn't autofix if that would remove comments | |
338 | invalid("/* comment */Math.pow(a, b)", "/* comment */a**b"), | |
339 | invalid("Math/**/.pow(a, b)", null), | |
340 | invalid("Math//\n.pow(a, b)", null), | |
341 | invalid("Math[//\n'pow'](a, b)", null), | |
342 | invalid("Math['pow'/**/](a, b)", null), | |
343 | invalid("Math./**/pow(a, b)", null), | |
344 | invalid("Math.pow/**/(a, b)", null), | |
345 | invalid("Math.pow//\n(a, b)", null), | |
346 | invalid("Math.pow(/**/a, b)", null), | |
347 | invalid("Math.pow(a,//\n b)", null), | |
348 | invalid("Math.pow(a, b/**/)", null), | |
349 | invalid("Math.pow(a, b//\n)", null), | |
350 | invalid("Math.pow(a, b)/* comment */;", "a**b/* comment */;"), | |
6f036462 TL |
351 | invalid("Math.pow(a, b)// comment\n;", "a**b// comment\n;"), |
352 | ||
353 | // Optional chaining | |
354 | invalid("Math.pow?.(a, b)", "a**b"), | |
355 | invalid("Math?.pow(a, b)", "a**b"), | |
356 | invalid("Math?.pow?.(a, b)", "a**b"), | |
357 | invalid("(Math?.pow)(a, b)", "a**b"), | |
358 | invalid("(Math?.pow)?.(a, b)", "a**b") | |
eb39fafa DC |
359 | ] |
360 | }); |