]>
Commit | Line | Data |
---|---|---|
eb39fafa DC |
1 | /** |
2 | * @fileoverview Tests for createReportTranslator | |
3 | * @author Teddy Katz | |
4 | */ | |
5 | "use strict"; | |
6 | ||
7 | //------------------------------------------------------------------------------ | |
8 | // Requirements | |
9 | //------------------------------------------------------------------------------ | |
10 | ||
11 | const assert = require("chai").assert; | |
12 | const { SourceCode } = require("../../../lib/source-code"); | |
13 | const espree = require("espree"); | |
14 | const createReportTranslator = require("../../../lib/linter/report-translator"); | |
15 | ||
16 | //------------------------------------------------------------------------------ | |
17 | // Tests | |
18 | //------------------------------------------------------------------------------ | |
19 | ||
20 | describe("createReportTranslator", () => { | |
21 | ||
22 | /** | |
23 | * Creates a SourceCode instance out of JavaScript text | |
24 | * @param {string} text Source text | |
25 | * @returns {SourceCode} A SourceCode instance for that text | |
26 | */ | |
27 | function createSourceCode(text) { | |
28 | return new SourceCode( | |
29 | text, | |
30 | espree.parse( | |
31 | text.replace(/^\uFEFF/u, ""), | |
32 | { | |
33 | loc: true, | |
34 | range: true, | |
35 | raw: true, | |
36 | tokens: true, | |
37 | comment: true | |
38 | } | |
39 | ) | |
40 | ); | |
41 | } | |
42 | ||
43 | let node, location, message, translateReport, suggestion1, suggestion2; | |
44 | ||
45 | beforeEach(() => { | |
46 | const sourceCode = createSourceCode("foo\nbar"); | |
47 | ||
48 | node = sourceCode.ast.body[0]; | |
49 | location = sourceCode.ast.body[1].loc.start; | |
50 | message = "foo"; | |
51 | suggestion1 = "First suggestion"; | |
52 | suggestion2 = "Second suggestion {{interpolated}}"; | |
53 | translateReport = createReportTranslator({ | |
54 | ruleId: "foo-rule", | |
55 | severity: 2, | |
56 | sourceCode, | |
57 | messageIds: { | |
58 | testMessage: message, | |
59 | suggestion1, | |
60 | suggestion2 | |
61 | } | |
62 | }); | |
63 | }); | |
64 | ||
65 | describe("old-style call with location", () => { | |
66 | it("should extract the location correctly", () => { | |
67 | assert.deepStrictEqual( | |
68 | translateReport(node, location, message, {}), | |
69 | { | |
70 | ruleId: "foo-rule", | |
71 | severity: 2, | |
72 | message: "foo", | |
73 | line: 2, | |
74 | column: 1, | |
75 | nodeType: "ExpressionStatement" | |
76 | } | |
77 | ); | |
78 | }); | |
79 | }); | |
80 | ||
81 | describe("old-style call without location", () => { | |
82 | it("should use the start location and end location of the node", () => { | |
83 | assert.deepStrictEqual( | |
84 | translateReport(node, message, {}), | |
85 | { | |
86 | ruleId: "foo-rule", | |
87 | severity: 2, | |
88 | message: "foo", | |
89 | line: 1, | |
90 | column: 1, | |
91 | endLine: 1, | |
92 | endColumn: 4, | |
93 | nodeType: "ExpressionStatement" | |
94 | } | |
95 | ); | |
96 | }); | |
97 | }); | |
98 | ||
99 | describe("new-style call with all options", () => { | |
100 | it("should include the new-style options in the report", () => { | |
101 | const reportDescriptor = { | |
102 | node, | |
103 | loc: location, | |
104 | message, | |
105 | fix: () => ({ range: [1, 2], text: "foo" }), | |
106 | suggest: [{ | |
107 | desc: "suggestion 1", | |
108 | fix: () => ({ range: [2, 3], text: "s1" }) | |
109 | }, { | |
110 | desc: "suggestion 2", | |
111 | fix: () => ({ range: [3, 4], text: "s2" }) | |
112 | }] | |
113 | }; | |
114 | ||
115 | assert.deepStrictEqual( | |
116 | translateReport(reportDescriptor), | |
117 | { | |
118 | ruleId: "foo-rule", | |
119 | severity: 2, | |
120 | message: "foo", | |
121 | line: 2, | |
122 | column: 1, | |
123 | nodeType: "ExpressionStatement", | |
124 | fix: { | |
125 | range: [1, 2], | |
126 | text: "foo" | |
127 | }, | |
128 | suggestions: [{ | |
129 | desc: "suggestion 1", | |
130 | fix: { range: [2, 3], text: "s1" } | |
131 | }, { | |
132 | desc: "suggestion 2", | |
133 | fix: { range: [3, 4], text: "s2" } | |
134 | }] | |
135 | } | |
136 | ); | |
137 | }); | |
138 | ||
139 | it("should translate the messageId into a message", () => { | |
140 | const reportDescriptor = { | |
141 | node, | |
142 | loc: location, | |
143 | messageId: "testMessage", | |
144 | fix: () => ({ range: [1, 2], text: "foo" }) | |
145 | }; | |
146 | ||
147 | assert.deepStrictEqual( | |
148 | translateReport(reportDescriptor), | |
149 | { | |
150 | ruleId: "foo-rule", | |
151 | severity: 2, | |
152 | message: "foo", | |
153 | messageId: "testMessage", | |
154 | line: 2, | |
155 | column: 1, | |
156 | nodeType: "ExpressionStatement", | |
157 | fix: { | |
158 | range: [1, 2], | |
159 | text: "foo" | |
160 | } | |
161 | } | |
162 | ); | |
163 | }); | |
164 | ||
165 | it("should throw when both messageId and message are provided", () => { | |
166 | const reportDescriptor = { | |
167 | node, | |
168 | loc: location, | |
169 | messageId: "testMessage", | |
170 | message: "bar", | |
171 | fix: () => ({ range: [1, 2], text: "foo" }) | |
172 | }; | |
173 | ||
174 | assert.throws( | |
175 | () => translateReport(reportDescriptor), | |
176 | TypeError, | |
177 | "context.report() called with a message and a messageId. Please only pass one." | |
178 | ); | |
179 | }); | |
180 | ||
181 | it("should throw when an invalid messageId is provided", () => { | |
182 | const reportDescriptor = { | |
183 | node, | |
184 | loc: location, | |
185 | messageId: "thisIsNotASpecifiedMessageId", | |
186 | fix: () => ({ range: [1, 2], text: "foo" }) | |
187 | }; | |
188 | ||
189 | assert.throws( | |
190 | () => translateReport(reportDescriptor), | |
191 | TypeError, | |
192 | /^context\.report\(\) called with a messageId of '[^']+' which is not present in the 'messages' config:/u | |
193 | ); | |
194 | }); | |
195 | ||
196 | it("should throw when no message is provided", () => { | |
197 | const reportDescriptor = { node }; | |
198 | ||
199 | assert.throws( | |
200 | () => translateReport(reportDescriptor), | |
201 | TypeError, | |
202 | "Missing `message` property in report() call; add a message that describes the linting problem." | |
203 | ); | |
204 | }); | |
205 | ||
206 | it("should support messageIds for suggestions and output resulting descriptions", () => { | |
207 | const reportDescriptor = { | |
208 | node, | |
209 | loc: location, | |
210 | message, | |
211 | suggest: [{ | |
212 | messageId: "suggestion1", | |
213 | fix: () => ({ range: [2, 3], text: "s1" }) | |
214 | }, { | |
215 | messageId: "suggestion2", | |
216 | data: { interpolated: "'interpolated value'" }, | |
217 | fix: () => ({ range: [3, 4], text: "s2" }) | |
218 | }] | |
219 | }; | |
220 | ||
221 | assert.deepStrictEqual( | |
222 | translateReport(reportDescriptor), | |
223 | { | |
224 | ruleId: "foo-rule", | |
225 | severity: 2, | |
226 | message: "foo", | |
227 | line: 2, | |
228 | column: 1, | |
229 | nodeType: "ExpressionStatement", | |
230 | suggestions: [{ | |
231 | messageId: "suggestion1", | |
232 | desc: "First suggestion", | |
233 | fix: { range: [2, 3], text: "s1" } | |
234 | }, { | |
235 | messageId: "suggestion2", | |
236 | data: { interpolated: "'interpolated value'" }, | |
237 | desc: "Second suggestion 'interpolated value'", | |
238 | fix: { range: [3, 4], text: "s2" } | |
239 | }] | |
240 | } | |
241 | ); | |
242 | }); | |
243 | ||
244 | it("should throw when a suggestion defines both a desc and messageId", () => { | |
245 | const reportDescriptor = { | |
246 | node, | |
247 | loc: location, | |
248 | message, | |
249 | suggest: [{ | |
250 | desc: "The description", | |
251 | messageId: "suggestion1", | |
252 | fix: () => ({ range: [2, 3], text: "s1" }) | |
253 | }] | |
254 | }; | |
255 | ||
256 | assert.throws( | |
257 | () => translateReport(reportDescriptor), | |
258 | TypeError, | |
259 | "context.report() called with a suggest option that defines both a 'messageId' and an 'desc'. Please only pass one." | |
260 | ); | |
261 | }); | |
262 | ||
263 | it("should throw when a suggestion uses an invalid messageId", () => { | |
264 | const reportDescriptor = { | |
265 | node, | |
266 | loc: location, | |
267 | message, | |
268 | suggest: [{ | |
269 | messageId: "noMatchingMessage", | |
270 | fix: () => ({ range: [2, 3], text: "s1" }) | |
271 | }] | |
272 | }; | |
273 | ||
274 | assert.throws( | |
275 | () => translateReport(reportDescriptor), | |
276 | TypeError, | |
277 | /^context\.report\(\) called with a suggest option with a messageId '[^']+' which is not present in the 'messages' config:/u | |
278 | ); | |
279 | }); | |
280 | ||
281 | it("should throw when a suggestion does not provide either a desc or messageId", () => { | |
282 | const reportDescriptor = { | |
283 | node, | |
284 | loc: location, | |
285 | message, | |
286 | suggest: [{ | |
287 | fix: () => ({ range: [2, 3], text: "s1" }) | |
288 | }] | |
289 | }; | |
290 | ||
291 | assert.throws( | |
292 | () => translateReport(reportDescriptor), | |
293 | TypeError, | |
294 | "context.report() called with a suggest option that doesn't have either a `desc` or `messageId`" | |
295 | ); | |
296 | }); | |
297 | ||
298 | it("should throw when a suggestion does not provide a fix function", () => { | |
299 | const reportDescriptor = { | |
300 | node, | |
301 | loc: location, | |
302 | message, | |
303 | suggest: [{ | |
304 | desc: "The description", | |
305 | fix: false | |
306 | }] | |
307 | }; | |
308 | ||
309 | assert.throws( | |
310 | () => translateReport(reportDescriptor), | |
311 | TypeError, | |
312 | /^context\.report\(\) called with a suggest option without a fix function. See:/u | |
313 | ); | |
314 | }); | |
315 | }); | |
316 | ||
317 | describe("combining autofixes", () => { | |
318 | it("should merge fixes to one if 'fix' function returns an array of fixes.", () => { | |
319 | const reportDescriptor = { | |
320 | node, | |
321 | loc: location, | |
322 | message, | |
323 | fix: () => [{ range: [1, 2], text: "foo" }, { range: [4, 5], text: "bar" }] | |
324 | }; | |
325 | ||
326 | assert.deepStrictEqual( | |
327 | translateReport(reportDescriptor), | |
328 | { | |
329 | ruleId: "foo-rule", | |
330 | severity: 2, | |
331 | message: "foo", | |
332 | line: 2, | |
333 | column: 1, | |
334 | nodeType: "ExpressionStatement", | |
335 | fix: { | |
336 | range: [1, 5], | |
337 | text: "fooo\nbar" | |
338 | } | |
339 | } | |
340 | ); | |
341 | }); | |
342 | ||
343 | it("should merge fixes to one if 'fix' function returns an iterator of fixes.", () => { | |
344 | const reportDescriptor = { | |
345 | node, | |
346 | loc: location, | |
347 | message, | |
348 | *fix() { | |
349 | yield { range: [1, 2], text: "foo" }; | |
350 | yield { range: [4, 5], text: "bar" }; | |
351 | } | |
352 | }; | |
353 | ||
354 | assert.deepStrictEqual( | |
355 | translateReport(reportDescriptor), | |
356 | { | |
357 | ruleId: "foo-rule", | |
358 | severity: 2, | |
359 | message: "foo", | |
360 | line: 2, | |
361 | column: 1, | |
362 | nodeType: "ExpressionStatement", | |
363 | fix: { | |
364 | range: [1, 5], | |
365 | text: "fooo\nbar" | |
366 | } | |
367 | } | |
368 | ); | |
369 | }); | |
370 | ||
371 | it("should pass through fixes if only one is present", () => { | |
372 | const reportDescriptor = { | |
373 | node, | |
374 | loc: location, | |
375 | message, | |
376 | fix: () => [{ range: [1, 2], text: "foo" }] | |
377 | }; | |
378 | ||
379 | assert.deepStrictEqual( | |
380 | translateReport(reportDescriptor), | |
381 | { | |
382 | ruleId: "foo-rule", | |
383 | severity: 2, | |
384 | message: "foo", | |
385 | line: 2, | |
386 | column: 1, | |
387 | nodeType: "ExpressionStatement", | |
388 | fix: { | |
389 | range: [1, 2], | |
390 | text: "foo" | |
391 | } | |
392 | } | |
393 | ); | |
394 | }); | |
395 | ||
396 | it("should handle inserting BOM correctly.", () => { | |
397 | const reportDescriptor = { | |
398 | node, | |
399 | loc: location, | |
400 | message, | |
401 | fix: () => [{ range: [0, 3], text: "\uFEFFfoo" }, { range: [4, 5], text: "x" }] | |
402 | }; | |
403 | ||
404 | assert.deepStrictEqual( | |
405 | translateReport(reportDescriptor), | |
406 | { | |
407 | ruleId: "foo-rule", | |
408 | severity: 2, | |
409 | message: "foo", | |
410 | line: 2, | |
411 | column: 1, | |
412 | nodeType: "ExpressionStatement", | |
413 | fix: { | |
414 | range: [0, 5], | |
415 | text: "\uFEFFfoo\nx" | |
416 | } | |
417 | } | |
418 | ); | |
419 | }); | |
420 | ||
421 | ||
422 | it("should handle removing BOM correctly.", () => { | |
423 | const sourceCode = createSourceCode("\uFEFFfoo\nbar"); | |
424 | ||
425 | node = sourceCode.ast.body[0]; | |
426 | ||
427 | const reportDescriptor = { | |
428 | node, | |
429 | message, | |
430 | fix: () => [{ range: [-1, 3], text: "foo" }, { range: [4, 5], text: "x" }] | |
431 | }; | |
432 | ||
433 | assert.deepStrictEqual( | |
434 | createReportTranslator({ ruleId: "foo-rule", severity: 1, sourceCode })(reportDescriptor), | |
435 | { | |
436 | ruleId: "foo-rule", | |
437 | severity: 1, | |
438 | message: "foo", | |
439 | line: 1, | |
440 | column: 1, | |
441 | endLine: 1, | |
442 | endColumn: 4, | |
443 | nodeType: "ExpressionStatement", | |
444 | fix: { | |
445 | range: [-1, 5], | |
446 | text: "foo\nx" | |
447 | } | |
448 | } | |
449 | ); | |
450 | }); | |
451 | ||
452 | it("should throw an assertion error if ranges are overlapped.", () => { | |
453 | const reportDescriptor = { | |
454 | node, | |
455 | loc: location, | |
456 | message, | |
457 | fix: () => [{ range: [0, 3], text: "\uFEFFfoo" }, { range: [2, 5], text: "x" }] | |
458 | }; | |
459 | ||
460 | assert.throws( | |
461 | translateReport.bind(null, reportDescriptor), | |
462 | "Fix objects must not be overlapped in a report." | |
463 | ); | |
464 | }); | |
465 | ||
466 | it("should include a fix passed as the last argument when location is passed", () => { | |
467 | assert.deepStrictEqual( | |
468 | translateReport( | |
469 | node, | |
470 | { line: 42, column: 23 }, | |
471 | "my message {{1}}{{0}}", | |
472 | ["!", "testing"], | |
473 | () => ({ range: [1, 1], text: "" }) | |
474 | ), | |
475 | { | |
476 | ruleId: "foo-rule", | |
477 | severity: 2, | |
478 | message: "my message testing!", | |
479 | line: 42, | |
480 | column: 24, | |
481 | nodeType: "ExpressionStatement", | |
482 | fix: { | |
483 | range: [1, 1], | |
484 | text: "" | |
485 | } | |
486 | } | |
487 | ); | |
488 | }); | |
489 | }); | |
490 | ||
491 | describe("suggestions", () => { | |
492 | it("should support multiple suggestions.", () => { | |
493 | const reportDescriptor = { | |
494 | node, | |
495 | loc: location, | |
496 | message, | |
497 | suggest: [{ | |
498 | desc: "A first suggestion for the issue", | |
499 | fix: () => [{ range: [1, 2], text: "foo" }] | |
500 | }, { | |
501 | desc: "A different suggestion for the issue", | |
502 | fix: () => [{ range: [1, 3], text: "foobar" }] | |
503 | }] | |
504 | }; | |
505 | ||
506 | assert.deepStrictEqual( | |
507 | translateReport(reportDescriptor), | |
508 | { | |
509 | ruleId: "foo-rule", | |
510 | severity: 2, | |
511 | message: "foo", | |
512 | line: 2, | |
513 | column: 1, | |
514 | nodeType: "ExpressionStatement", | |
515 | suggestions: [{ | |
516 | desc: "A first suggestion for the issue", | |
517 | fix: { range: [1, 2], text: "foo" } | |
518 | }, { | |
519 | desc: "A different suggestion for the issue", | |
520 | fix: { range: [1, 3], text: "foobar" } | |
521 | }] | |
522 | } | |
523 | ); | |
524 | }); | |
525 | ||
526 | it("should merge suggestion fixes to one if 'fix' function returns an array of fixes.", () => { | |
527 | const reportDescriptor = { | |
528 | node, | |
529 | loc: location, | |
530 | message, | |
531 | suggest: [{ | |
532 | desc: "A suggestion for the issue", | |
533 | fix: () => [{ range: [1, 2], text: "foo" }, { range: [4, 5], text: "bar" }] | |
534 | }] | |
535 | }; | |
536 | ||
537 | assert.deepStrictEqual( | |
538 | translateReport(reportDescriptor), | |
539 | { | |
540 | ruleId: "foo-rule", | |
541 | severity: 2, | |
542 | message: "foo", | |
543 | line: 2, | |
544 | column: 1, | |
545 | nodeType: "ExpressionStatement", | |
546 | suggestions: [{ | |
547 | desc: "A suggestion for the issue", | |
548 | fix: { | |
549 | range: [1, 5], | |
550 | text: "fooo\nbar" | |
551 | } | |
552 | }] | |
553 | } | |
554 | ); | |
555 | }); | |
556 | }); | |
557 | ||
558 | describe("message interpolation", () => { | |
559 | it("should correctly parse a message when being passed all options in an old-style report", () => { | |
560 | assert.deepStrictEqual( | |
561 | translateReport(node, node.loc.end, "hello {{dynamic}}", { dynamic: node.type }), | |
562 | { | |
563 | severity: 2, | |
564 | ruleId: "foo-rule", | |
565 | message: "hello ExpressionStatement", | |
566 | nodeType: "ExpressionStatement", | |
567 | line: 1, | |
568 | column: 4 | |
569 | } | |
570 | ); | |
571 | }); | |
572 | ||
573 | it("should correctly parse a message when being passed all options in a new-style report", () => { | |
574 | assert.deepStrictEqual( | |
575 | translateReport({ node, loc: node.loc.end, message: "hello {{dynamic}}", data: { dynamic: node.type } }), | |
576 | { | |
577 | severity: 2, | |
578 | ruleId: "foo-rule", | |
579 | message: "hello ExpressionStatement", | |
580 | nodeType: "ExpressionStatement", | |
581 | line: 1, | |
582 | column: 4 | |
583 | } | |
584 | ); | |
585 | }); | |
586 | ||
587 | it("should correctly parse a message with object keys as numbers", () => { | |
588 | assert.strictEqual( | |
589 | translateReport(node, "my message {{name}}{{0}}", { 0: "!", name: "testing" }).message, | |
590 | "my message testing!" | |
591 | ); | |
592 | }); | |
593 | ||
594 | it("should correctly parse a message with array", () => { | |
595 | assert.strictEqual( | |
596 | translateReport(node, "my message {{1}}{{0}}", ["!", "testing"]).message, | |
597 | "my message testing!" | |
598 | ); | |
599 | }); | |
600 | ||
601 | it("should allow template parameter with inner whitespace", () => { | |
602 | assert.strictEqual( | |
603 | translateReport(node, "message {{parameter name}}", { "parameter name": "yay!" }).message, | |
604 | "message yay!" | |
605 | ); | |
606 | }); | |
607 | ||
608 | it("should allow template parameter with non-identifier characters", () => { | |
609 | assert.strictEqual( | |
610 | translateReport(node, "message {{parameter-name}}", { "parameter-name": "yay!" }).message, | |
611 | "message yay!" | |
612 | ); | |
613 | }); | |
614 | ||
615 | it("should allow template parameter wrapped in braces", () => { | |
616 | assert.strictEqual( | |
617 | translateReport(node, "message {{{param}}}", { param: "yay!" }).message, | |
618 | "message {yay!}" | |
619 | ); | |
620 | }); | |
621 | ||
622 | it("should ignore template parameter with no specified value", () => { | |
623 | assert.strictEqual( | |
624 | translateReport(node, "message {{parameter}}", {}).message, | |
625 | "message {{parameter}}" | |
626 | ); | |
627 | }); | |
628 | ||
629 | it("should handle leading whitespace in template parameter", () => { | |
630 | assert.strictEqual( | |
631 | translateReport({ node, message: "message {{ parameter}}", data: { parameter: "yay!" } }).message, | |
632 | "message yay!" | |
633 | ); | |
634 | }); | |
635 | ||
636 | it("should handle trailing whitespace in template parameter", () => { | |
637 | assert.strictEqual( | |
638 | translateReport({ node, message: "message {{parameter }}", data: { parameter: "yay!" } }).message, | |
639 | "message yay!" | |
640 | ); | |
641 | }); | |
642 | ||
643 | it("should still allow inner whitespace as well as leading/trailing", () => { | |
644 | assert.strictEqual( | |
645 | translateReport(node, "message {{ parameter name }}", { "parameter name": "yay!" }).message, | |
646 | "message yay!" | |
647 | ); | |
648 | }); | |
649 | ||
650 | it("should still allow non-identifier characters as well as leading/trailing whitespace", () => { | |
651 | assert.strictEqual( | |
652 | translateReport(node, "message {{ parameter-name }}", { "parameter-name": "yay!" }).message, | |
653 | "message yay!" | |
654 | ); | |
655 | }); | |
656 | }); | |
657 | ||
658 | describe("location inference", () => { | |
659 | it("should use the provided location when given in an old-style call", () => { | |
660 | assert.deepStrictEqual( | |
661 | translateReport(node, { line: 42, column: 13 }, "hello world"), | |
662 | { | |
663 | severity: 2, | |
664 | ruleId: "foo-rule", | |
665 | message: "hello world", | |
666 | nodeType: "ExpressionStatement", | |
667 | line: 42, | |
668 | column: 14 | |
669 | } | |
670 | ); | |
671 | }); | |
672 | ||
673 | it("should use the provided location when given in an new-style call", () => { | |
674 | assert.deepStrictEqual( | |
675 | translateReport({ node, loc: { line: 42, column: 13 }, message: "hello world" }), | |
676 | { | |
677 | severity: 2, | |
678 | ruleId: "foo-rule", | |
679 | message: "hello world", | |
680 | nodeType: "ExpressionStatement", | |
681 | line: 42, | |
682 | column: 14 | |
683 | } | |
684 | ); | |
685 | }); | |
686 | ||
687 | it("should extract the start and end locations from a node if no location is provided", () => { | |
688 | assert.deepStrictEqual( | |
689 | translateReport(node, "hello world"), | |
690 | { | |
691 | severity: 2, | |
692 | ruleId: "foo-rule", | |
693 | message: "hello world", | |
694 | nodeType: "ExpressionStatement", | |
695 | line: 1, | |
696 | column: 1, | |
697 | endLine: 1, | |
698 | endColumn: 4 | |
699 | } | |
700 | ); | |
701 | }); | |
702 | ||
703 | it("should have 'endLine' and 'endColumn' when 'loc' property has 'end' property.", () => { | |
704 | assert.deepStrictEqual( | |
705 | translateReport({ loc: node.loc, message: "hello world" }), | |
706 | { | |
707 | severity: 2, | |
708 | ruleId: "foo-rule", | |
709 | message: "hello world", | |
710 | nodeType: null, | |
711 | line: 1, | |
712 | column: 1, | |
713 | endLine: 1, | |
714 | endColumn: 4 | |
715 | } | |
716 | ); | |
717 | }); | |
718 | ||
719 | it("should not have 'endLine' and 'endColumn' when 'loc' property does not have 'end' property.", () => { | |
720 | assert.deepStrictEqual( | |
721 | translateReport({ loc: node.loc.start, message: "hello world" }), | |
722 | { | |
723 | severity: 2, | |
724 | ruleId: "foo-rule", | |
725 | message: "hello world", | |
726 | nodeType: null, | |
727 | line: 1, | |
728 | column: 1 | |
729 | } | |
730 | ); | |
731 | }); | |
732 | ||
733 | it("should infer an 'endLine' and 'endColumn' property when using the object-based context.report API", () => { | |
734 | assert.deepStrictEqual( | |
735 | translateReport({ node, message: "hello world" }), | |
736 | { | |
737 | severity: 2, | |
738 | ruleId: "foo-rule", | |
739 | message: "hello world", | |
740 | nodeType: "ExpressionStatement", | |
741 | line: 1, | |
742 | column: 1, | |
743 | endLine: 1, | |
744 | endColumn: 4 | |
745 | } | |
746 | ); | |
747 | }); | |
748 | }); | |
749 | ||
750 | describe("converting old-style calls", () => { | |
751 | it("should include a fix passed as the last argument when location is not passed", () => { | |
752 | assert.deepStrictEqual( | |
753 | translateReport(node, "my message {{1}}{{0}}", ["!", "testing"], () => ({ range: [1, 1], text: "" })), | |
754 | { | |
755 | severity: 2, | |
756 | ruleId: "foo-rule", | |
757 | message: "my message testing!", | |
758 | nodeType: "ExpressionStatement", | |
759 | line: 1, | |
760 | column: 1, | |
761 | endLine: 1, | |
762 | endColumn: 4, | |
763 | fix: { range: [1, 1], text: "" } | |
764 | } | |
765 | ); | |
766 | }); | |
767 | }); | |
768 | ||
769 | describe("validation", () => { | |
770 | ||
771 | it("should throw an error if node is not an object", () => { | |
772 | assert.throws( | |
773 | () => translateReport("not a node", "hello world"), | |
774 | "Node must be an object" | |
775 | ); | |
776 | }); | |
777 | ||
778 | ||
779 | it("should not throw an error if location is provided and node is not in an old-style call", () => { | |
780 | assert.deepStrictEqual( | |
781 | translateReport(null, { line: 1, column: 1 }, "hello world"), | |
782 | { | |
783 | severity: 2, | |
784 | ruleId: "foo-rule", | |
785 | message: "hello world", | |
786 | nodeType: null, | |
787 | line: 1, | |
788 | column: 2 | |
789 | } | |
790 | ); | |
791 | }); | |
792 | ||
793 | it("should not throw an error if location is provided and node is not in a new-style call", () => { | |
794 | assert.deepStrictEqual( | |
795 | translateReport({ loc: { line: 1, column: 1 }, message: "hello world" }), | |
796 | { | |
797 | severity: 2, | |
798 | ruleId: "foo-rule", | |
799 | message: "hello world", | |
800 | nodeType: null, | |
801 | line: 1, | |
802 | column: 2 | |
803 | } | |
804 | ); | |
805 | }); | |
806 | ||
807 | it("should throw an error if neither node nor location is provided", () => { | |
808 | assert.throws( | |
809 | () => translateReport(null, "hello world"), | |
810 | "Node must be provided when reporting error if location is not provided" | |
811 | ); | |
812 | }); | |
813 | }); | |
814 | }); |