1. 1/*jslint beta, node*/
2. 1import jslint from "./jslint.mjs";
3. 1import jslintCjs from "./jslint_wrapper_cjs.cjs";
4. 1import moduleChildProcess from "child_process";
5. 1import moduleFs from "fs";
6. 1import modulePath from "path";
7. 1import moduleVm from "vm";
8. 1
9. 1let {
10. 1 assertErrorThrownAsync,
11. 1 assertJsonEqual,
12. 1 assertOrThrow,
13. 1 debugInline,
14. 1 fsWriteFileWithParents,
15. 1 globExclude,
16. 1 jstestDescribe,
17. 1 jstestIt,
18. 1 moduleFsInit,
19. 1 noop,
20. 1 v8CoverageListMerge,
21. 1 v8CoverageReportCreate
22. 1} = jslint;
23. 1let sourceJslintMjs;
24. 1let testCoverageMergeData;
25. 1
26. 15function assertMatch(text, rgx) {
27. 15
28. 15// Assert <text> matches regexp <rgx>, showing <text> on failure.
29. 15
30. 15 assertOrThrow(rgx.test(text), text);
31. 15}
32. 1
33. 23function childRun(env, source, stdin) {
34. 23
35. 23// Run <source> in a child node with jslint imported, fed <stdin>, and resolve
36. 23// its exit-code, stderr and stdout. Clear the parent's test filter, then apply
37. 23// <env>.
38. 23
39. 23 return new Promise(function (resolve) {
40. 23 moduleChildProcess.execFile(process.execPath, [
41. 23 "--input-type=module",
42. 23 "--eval",
43. 23 "import jslint from \"./jslint.mjs\";\n" + source
44. 23 ], {
45. 23 env: {
46. 23 ...process.env,
47. 23 npm_config_mode_test_filter: "",
48. 23 ...env
49. 23 },
50. 23
51. 23// Kill a hung child, so a broken runner fails this suite instead of hanging it.
52. 23// A killed child resolves exit-code null, never a passing 0.
53. 23
54. 23 timeout: 10000
55. 23 }, function (err, stdout, stderr) {
56. 23 resolve([(
57. 23 err
58. 20 ? err.code
59. 3 : 0
60. 23 ), stderr, stdout]);
61. 23 }).stdin.end(stdin);
62. 23 });
63. 23}
64. 1
65. 1await (async function init() {
66. 1
67. 1// Coverage-hack - Ugly-hack to get test-coverage for all initialization-states.
68. 1
69. 1 moduleFsInit();
70. 1 moduleFsInit();
71. 1
72. 1// A runner that ended every async test before it ran would also pass the tests
73. 1// that check it, so check one async failure here, outside any jstestIt.
74. 1
75. 1 await childRun({}, String(`
76. 1jslint.jstestDescribe("aa", function () {
77. 1 jslint.jstestIt("bb", async function () {
78. 1 await null;
79. 1 throw new Error("bb late");
80. 1 });
81. 1});
82. 1 `)).then(function ([exitCode, stderr]) {
83. 1
84. 1// Reported as a failed test, not merely a crash, which also exits 1.
85. 1
86. 1 assertOrThrow(
87. 1 exitCode === 1 && (
88. 1 / 1\. test it - bb\nError: bb late\n/
89. 1 ).test(stderr),
90. 1 "jstest passed an async test that failed\n" + stderr
91. 1 );
92. 1 });
93. 1
94. 1// Cleanup directory .tmp
95. 1
96. 1 await moduleFs.promises.rm(".tmp", {
97. 1 recursive: true
98. 1 }).catch(noop);
99. 1
100. 1// init sourceJslintMjs
101. 1
102. 1 sourceJslintMjs = await moduleFs.promises.readFile("jslint.mjs", "utf8");
103. 1
104. 1// init testCoverageMergeData
105. 1
106. 1 testCoverageMergeData = JSON.parse(
107. 1 await moduleFs.promises.readFile(
108. 1 "test_coverage_merge_data.json",
109. 1 "utf8"
110. 1 )
111. 1 );
112. 1}());
113. 1
114. 16function processExit0(exitCode) {
115. 16 assertOrThrow(exitCode === 0, exitCode);
116. 16}
117. 1
118. 14function processExit1(exitCode) {
119. 14 assertOrThrow(exitCode === 1, exitCode);
120. 14}
121. 1
122. 1jstestDescribe((
123. 1 "test assertXxx handling-behavior"
124. 1), function () {
125. 1 jstestIt((
126. 1 "test assertXxx-error handling-behavior"
127. 1 ), async function () {
128. 1 // Keep <debugInline> imported, for debugging tests.
129. 1 noop(debugInline);
130. 1 // test assertErrorThrownAsync error handling-behavior
131. 1 await assertErrorThrownAsync(function () {
132. 1 return assertErrorThrownAsync(noop);
133. 1 });
134. 1 // test assertJsonEqual error handling-behavior
135. 1 await assertErrorThrownAsync(function () {
136. 1 assertJsonEqual(1, 2);
137. 1 });
138. 1 await assertErrorThrownAsync(function () {
139. 1 assertJsonEqual(1, 2, "undefined");
140. 1 });
141. 1 await assertErrorThrownAsync(function () {
142. 1 assertJsonEqual(1, 2, {});
143. 1 });
144. 1 // test assertOrThrow error handling-behavior
145. 1 await assertErrorThrownAsync(function () {
146. 1 assertOrThrow(undefined, "undefined");
147. 1 });
148. 1 await assertErrorThrownAsync(function () {
149. 1 assertOrThrow(undefined, new Error());
150. 1 });
151. 1 });
152. 1});
153. 1
154. 1jstestDescribe((
155. 1 "test fsXxx handling-behavior"
156. 1), function () {
157. 1 jstestIt((
158. 1 "test fsWriteFileWithParents handling-behavior"
159. 1 ), async function () {
160. 1 await Promise.all([
161. 1 1, 2, 3, 4
162. 4 ].map(async function () {
163. 4 await fsWriteFileWithParents(
164. 4 ".tmp/fsWriteFileWithParents/aa/bb/cc",
165. 4 "aa"
166. 4 );
167. 4 }));
168. 1 assertJsonEqual(
169. 1 await moduleFs.promises.readFile(
170. 1 ".tmp/fsWriteFileWithParents/aa/bb/cc",
171. 1 "utf8"
172. 1 ),
173. 1 "aa"
174. 1 );
175. 1 });
176. 1});
177. 1
178. 1jstestDescribe((
179. 1 "test globXxx handling-behavior"
180. 1), function () {
181. 1 jstestIt((
182. 1 "test globAssertNotWeird-error handling-behavior"
183. 1 ), async function () {
184. 1 await Promise.all([
185. 1 "\n",
186. 1 "\r",
187. 1 "\u0000"
188. 3 ].map(async function (char) {
189. 3 await assertErrorThrownAsync(function () {
190. 3 return globExclude({
191. 3 pathnameList: [
192. 3 "aa",
193. 3 `cc/${char}/dd`,
194. 3 "bb"
195. 3 ]
196. 3 });
197. 3 }, (
198. 3 "Weird character " +
199. 3 JSON.stringify(char).replace((/\\/g), "\\\\") +
200. 3 " found in "
201. 3 ));
202. 3 }));
203. 1 });
204. 1 jstestIt((
205. 1 "test globExclude handling-behavior"
206. 1 ), function () {
207. 1 let pathnameList = [
208. 1 ".dockerignore",
209. 1 ".eslintrc.js",
210. 1 ".gitignore",
211. 1 ".npmignore",
212. 1 ".travis.yml",
213. 1 "/node_modules/aa/bb/cc.js",
214. 1 "/node_modules/aa/bb/dd.js",
215. 1 "CHANGELOG.md",
216. 1 "CONTRIBUTING.md",
217. 1 "Dockerfile",
218. 1 "LICENSE",
219. 1 "Makefile",
220. 1 "README.md",
221. 1 "appveyor.yml",
222. 1 "benchmark/insert-transaction.sql",
223. 1 "benchmark/insert.js",
224. 1 "binding.gyp",
225. 1 "cloudformation/ci.template.js",
226. 1 "deps/common-sqlite.gypi",
227. 1 "deps/extract.py",
228. 1 "deps/sqlite-autoconf-3340000.tar.gz",
229. 1 "deps/sqlite3.gyp",
230. 1 "examples/simple-chaining.js",
231. 1 "lib/index.js",
232. 1 "lib/sqlite3-binding.js",
233. 1 "lib/sqlite3.js",
234. 1 "lib/trace.js",
235. 1 "node_modules/aa/bb/cc.js",
236. 1 "node_modules/aa/bb/dd.js",
237. 1 "package.json",
238. 1 "scripts/build-appveyor.bat",
239. 1 "scripts/build-local.bat",
240. 1 "scripts/build_against_electron.sh",
241. 1 "scripts/build_against_node.sh",
242. 1 "scripts/build_against_node_webkit.sh",
243. 1 "scripts/build_for_node_webkit.cmd",
244. 1 "scripts/install_node.sh",
245. 1 "scripts/validate_tag.sh",
246. 1 "sqlite3.js",
247. 1 "src/async.h",
248. 1 "src/backup.cc",
249. 1 "src/backup.h",
250. 1 "src/database.cc",
251. 1 "src/database.h",
252. 1 "src/gcc-preinclude.h",
253. 1 "src/macros.h",
254. 1 "src/node_sqlite3.cc",
255. 1 "src/statement.cc",
256. 1 "src/statement.h",
257. 1 "src/threading.h",
258. 1 "test/affected.test.js",
259. 1 "test/backup.test.js",
260. 1 "test/blob.test.js",
261. 1 "test/cache.test.js",
262. 1 "test/constants.test.js",
263. 1 "test/database_fail.test.js",
264. 1 "test/each.test.js",
265. 1 "test/exec.test.js",
266. 1 "test/extension.test.js",
267. 1 "test/fts-content.test.js",
268. 1 "test/interrupt.test.js",
269. 1 "test/issue-108.test.js",
270. 1 "test/json.test.js",
271. 1 "test/map.test.js",
272. 1 "test/named_columns.test.js",
273. 1 "test/named_params.test.js",
274. 1 "test/null_error.test.js",
275. 1 "test/nw/.gitignore",
276. 1 "test/nw/Makefile",
277. 1 "test/nw/index.html",
278. 1 "test/nw/package.json",
279. 1 "test/open_close.test.js",
280. 1 "test/other_objects.test.js",
281. 1 "test/parallel_insert.test.js",
282. 1 "test/prepare.test.js",
283. 1 "test/profile.test.js",
284. 1 "test/rerun.test.js",
285. 1 "test/scheduling.test.js",
286. 1 "test/serialization.test.js",
287. 1 "test/support/createdb-electron.js",
288. 1 "test/support/createdb.js",
289. 1 "test/support/elmo.png",
290. 1 "test/support/helper.js",
291. 1 "test/support/prepare.db",
292. 1 "test/support/script.sql",
293. 1 "test/trace.test.js",
294. 1 "test/unicode.test.js",
295. 1 "test/upsert.test.js",
296. 1 "test/verbose.test.js",
297. 1 "tools/docker/architecture/linux-arm/Dockerfile",
298. 1 "tools/docker/architecture/linux-arm/run.sh",
299. 1 "tools/docker/architecture/linux-arm64/Dockerfile",
300. 1 "tools/docker/architecture/linux-arm64/run.sh"
301. 1 ];
302. 1 [
303. 1 "tes?/",
304. 1 "tes[-t-]/",
305. 1 "tes[-t]/",
306. 1 "tes[0-9A-Z_a-z-]/",
307. 1 "tes[t-]/",
308. 1 "test/**/*.js"
309. 6 ].forEach(function (aa) {
310. 6 [
311. 6 "li*/*.js",
312. 6 "li?/*.js",
313. 6 "lib/",
314. 6 "lib/*",
315. 6 "lib/**/*.js",
316. 6 "lib/*.js"
317. 36 ].forEach(function (bb) {
318. 36 [
319. 36 "",
320. 36 "**/node_modules/",
321. 36 "node_modules/"
322. 108 ].forEach(function (cc) {
323. 108 assertJsonEqual(
324. 108 globExclude({
325. 108 excludeList: [
326. 108 "tes[!0-9A-Z_a-z-]/",
327. 108 "tes[^0-9A-Z_a-z-]/",
328. 108 "test/suppor*/*elper.js",
329. 108 "test/suppor?/?elper.js",
330. 108 "test/support/helper.js"
331. 108 ].concat(aa, cc),
332. 108 includeList: [
333. 108 "**/*.cjs",
334. 108 "**/*.js",
335. 108 "**/*.mjs",
336. 108 "lib/sqlite3.js"
337. 108 ].concat(bb),
338. 108 pathnameList
339. 108 }).pathnameList,
340. 108 [
341. 108 ".eslintrc.js",
342. 108 "benchmark/insert.js",
343. 108 "cloudformation/ci.template.js",
344. 108 "examples/simple-chaining.js",
345. 108 "lib/index.js",
346. 108 "lib/sqlite3-binding.js",
347. 108 "lib/sqlite3.js",
348. 108 "lib/trace.js",
349. 108 "sqlite3.js"
350. 108 ].concat(
351. 108 cc === "**/node_modules/"
352. 36 ? [
353. 36 "node_modules/aa/bb/cc.js",
354. 36 "node_modules/aa/bb/dd.js"
355. 36 ]
356. 72 : cc === "node_modules/"
357. 72 ? [
358. 72 "/node_modules/aa/bb/cc.js",
359. 72 "/node_modules/aa/bb/dd.js"
360. 72 ]
361. 72 : [
362. 72 "/node_modules/aa/bb/cc.js",
363. 72 "/node_modules/aa/bb/dd.js",
364. 72 "node_modules/aa/bb/cc.js",
365. 72 "node_modules/aa/bb/dd.js"
366. 72 ]
367. 108 ).sort()
368. 108 );
369. 108 });
370. 36 });
371. 6 });
372. 1 });
373. 1 jstestIt((
374. 1 "test globToRegexp handling-behavior"
375. 1 ), function () {
376. 1 Object.entries({
377. 1 "*": (
378. 1 /^[^\/]*?$/gm
379. 1 ),
380. 1 "**": (
381. 1 /^.*?$/gm
382. 1 ),
383. 1 "***": (
384. 1 /^.*?$/gm
385. 1 ),
386. 1 "****": (
387. 1 /^.*?$/gm
388. 1 ),
389. 1 "****////****": (
390. 1 /^.*?$/gm
391. 1 ),
392. 1 "***///***": (
393. 1 /^.*?$/gm
394. 1 ),
395. 1 "**/*": (
396. 1 /^.*?$/gm
397. 1 ),
398. 1 "**/node_modules/": (
399. 1 /^.*?\/node_modules\/.*?$/gm
400. 1 ),
401. 1 "**/node_modules/**/*": (
402. 1 /^.*?\/node_modules\/.*?$/gm
403. 1 ),
404. 1 "?": (
405. 1 /^[^\/]$/gm
406. 1 ),
407. 1 "[!0-9A-Za-z-]": (
408. 1 /^[^0-9A-Za-z\-]$/gm
409. 1 ),
410. 1 "[0-9A-Za-z-]": (
411. 1 /^[0-9A-Za-z\-]$/gm
412. 1 ),
413. 1 "[[]] ]][[": (
414. 1 /^[\[]\] \]\][\[]$/gm
415. 1 ),
416. 1 "[]": (
417. 1 /^$/gm
418. 1 ),
419. 1 "[^0-9A-Za-z-]": (
420. 1 /^[^0-9A-Za-z\-]$/gm
421. 1 ),
422. 1 "aa/bb/cc": (
423. 1 /^aa\/bb\/cc$/gm
424. 1 ),
425. 1 "aa/bb/cc/": (
426. 1 /^aa\/bb\/cc\/.*?$/gm
427. 1 ),
428. 1 "li*/*": (
429. 1 /^li[^\/]*?\/[^\/]*?$/gm
430. 1 ),
431. 1 "li?/*": (
432. 1 /^li[^\/]\/[^\/]*?$/gm
433. 1 ),
434. 1 "lib/": (
435. 1 /^lib\/.*?$/gm
436. 1 ),
437. 1 "lib/*": (
438. 1 /^lib\/[^\/]*?$/gm
439. 1 ),
440. 1 "lib/**/*.js": (
441. 1 /^lib\/.*?\.js$/gm
442. 1 ),
443. 1 "lib/*.js": (
444. 1 /^lib\/[^\/]*?\.js$/gm
445. 1 ),
446. 1 "node_modules/": (
447. 1 /^node_modules\/.*?$/gm
448. 1 ),
449. 1 "node_modules/**/*": (
450. 1 /^node_modules\/.*?$/gm
451. 1 ),
452. 1 "tes[!0-9A-Z_a-z-]/**/*": (
453. 1 /^tes[^0-9A-Z_a-z\-]\/.*?$/gm
454. 1 ),
455. 1 "tes[0-9A-Z_a-z-]/**/*": (
456. 1 /^tes[0-9A-Z_a-z\-]\/.*?$/gm
457. 1 ),
458. 1 "tes[^0-9A-Z_a-z-]/**/*": (
459. 1 /^tes[^0-9A-Z_a-z\-]\/.*?$/gm
460. 1 ),
461. 1 "test/**/*": (
462. 1 /^test\/.*?$/gm
463. 1 ),
464. 1 "test/**/*.js": (
465. 1 /^test\/.*?\.js$/gm
466. 1 ),
467. 1 "test/suppor*/*elper.js": (
468. 1 /^test\/suppor[^\/]*?\/[^\/]*?elper\.js$/gm
469. 1 ),
470. 1 "test/suppor?/?elper.js": (
471. 1 /^test\/suppor[^\/]\/[^\/]elper\.js$/gm
472. 1 ),
473. 1 "test/support/helper.js": (
474. 1 /^test\/support\/helper\.js$/gm
475. 1 )
476. 33 }).forEach(function ([
477. 33 pattern, rgx
478. 33 ]) {
479. 33 assertJsonEqual(
480. 33 globExclude({
481. 33 excludeList: [
482. 33 pattern
483. 33 ]
484. 33 }).excludeList[0].source,
485. 33 rgx.source
486. 33 );
487. 33 assertJsonEqual(
488. 33 globExclude({
489. 33 includeList: [
490. 33 pattern
491. 33 ]
492. 33 }).includeList[0].source,
493. 33 rgx.source
494. 33 );
495. 33 });
496. 1 });
497. 1});
498. 1
499. 1jstestDescribe((
500. 1 "test jslint's autofix handling-behavior"
501. 1), function () {
502. 1 jstestIt((
503. 1 "test autofix-api handling-behavior"
504. 1 ), function () {
505. 1 let result;
506. 1 let source;
507. 1
508. 1// This function will autofix <source_api> through the API - no fs, no cli -
509. 1// and assert <autofixed> became <expect_api>. Option node is harmless to
510. 1// every fixture here, so one option-object serves them all.
511. 1
512. 17 function assertAutofix(expect_api, source_api) {
513. 17 let result_api = jslint.jslint(source_api, {
514. 17 autofix: true,
515. 17 node: true
516. 17 });
517. 17 assertOrThrow(
518. 17 result_api.autofixed === expect_api,
519. 17 JSON.stringify([
520. 17 source_api, result_api.autofixed
521. 17 ])
522. 17 );
523. 17 return result_api;
524. 17 }
525. 1
526. 1// Option autofix makes jslint() a PURE fixer. <warnings> and <ok> then
527. 1// describe <autofixed>, so a repairable source comes back ok.
528. 1
529. 1 source = (
530. 1 "function aa(bb) {\n return String( bb)+bb;\n}\n" +
531. 1 "export default Object.freeze(aa);\n"
532. 1 );
533. 1 result = assertAutofix((
534. 1 "function aa(bb) {\n return String(bb) + bb;\n}\n" +
535. 1 "export default Object.freeze(aa);\n"
536. 1 ), source);
537. 1 assertOrThrow(result.ok, JSON.stringify(result.warnings));
538. 1
539. 1// Without the option, <autofixed> is undefined and the warnings are the
540. 1// source's own.
541. 1
542. 1 result = jslint.jslint(source, {});
543. 1 assertOrThrow(result.autofixed === undefined, result.autofixed);
544. 1 assertOrThrow(!result.ok, "expected warnings");
545. 1
546. 1// A warning autofix cannot fix BLOCKS the first pass, so there is nothing to
547. 1// write and <autofixed> stays undefined.
548. 1
549. 1 result = assertAutofix(undefined, (
550. 1 "function aa(bb) {\n let cc = 0;\n return String( bb);\n}\n"
551. 1 ));
552. 1 assertOrThrow(!result.ok, "expected warnings");
553. 1
554. 1// Under beta, expected_a_at_end moves a line-leading operator to just after
555. 1// its left operand - past a template's `// x` line, before a trailing
556. 1// comment - and an operator alone on its line takes the line with it.
557. 1
558. 1 result = jslint.jslint((
559. 1 "const aa = [\n `\n// x`\n + 1 // c\n +\n 2\n];\n" +
560. 1 "export default Object.freeze(aa);\n"
561. 1 ), {
562. 1 autofix: true,
563. 1 beta: true
564. 1 });
565. 1 assertOrThrow(result.autofixed === (
566. 1 "const aa = [\n `\n// x` +\n 1 + // c\n 2\n];\n" +
567. 1 "export default Object.freeze(aa);\n"
568. 1 ), result.autofixed);
569. 1
570. 1// A tagged template's backtick is NOT an operator to move - moving it would
571. 1// put the line break inside the template and change its value.
572. 1
573. 1 source = (
574. 1 "function aa() {\n return String.raw\n `x${0}`;\n}\n" +
575. 1 "export default Object.freeze(aa);\n"
576. 1 );
577. 1 result = jslint.jslint(source, {
578. 1 autofix: true,
579. 1 beta: true
580. 1 });
581. 1 assertOrThrow(
582. 1 result.autofixed === undefined && result.ok,
583. 1 JSON.stringify([result.autofixed, result.warnings])
584. 1 );
585. 1
586. 1// THE FIXER'S LINE MODEL MUST BE THE LINTER'S (jslint_rgx_crlf). A CRLF file
587. 1// must come back CRLF - line_list carries NO terminators and the rejoin uses
588. 1// the file's OWN first one - and a lone \r, which the linter counts as a line
589. 1// break, must not shift every later fix onto the wrong line. Both were real:
590. 1// the second deleted spaces from a comment while the warned line stayed
591. 1// untouched. A MIXED file is NORMALIZED to that first terminator, which is
592. 1// what the second case pins: the fix lands on the right line, the comment is
593. 1// untouched, and every \n comes back \r.
594. 1
595. 1 assertAutofix((
596. 1 "function aa(bb) {\r\n if (bb) {\r\n return bb;\r\n" +
597. 1 " }\r\n return 0;\r\n}\r\naa();\r\n"
598. 1 ), (
599. 1 "function aa(bb) {\r\n if (bb) { return bb; }\r\n" +
600. 1 " return 0;\r\n}\r\naa();\r\n"
601. 1 ));
602. 1 assertAutofix((
603. 1 "function bb(cc) {\r cc();\r return String(cc);\r" +
604. 1 " // xx yy\r}\rbb();\r"
605. 1 ), (
606. 1 "function bb(cc) {\r cc();\n return String( cc);\n" +
607. 1 " // xx yy\n}\nbb();\n"
608. 1 ));
609. 1
610. 1// A WARNING-FREE source is returned UNTOUCHED, never rejoined - otherwise a
611. 1// clean mixed-terminator file would come back normalized and get written.
612. 1
613. 1 result = assertAutofix(undefined, (
614. 1 "function cc(dd) {\r\n dd();\r return 0;\n}\ncc();\n"
615. 1 ));
616. 1 assertOrThrow(result.ok, JSON.stringify(result.warnings));
617. 1
618. 1// AN EMPTY source is the one input whose rejoin is the EMPTY STRING, which
619. 1// the call site's <|| state.source> would read as "nothing happened". It is
620. 1// harmless ONLY because state.source is empty too, so the two agree - but a
621. 1// non-empty source can never rejoin to "", because a warning implies a token
622. 1// implies a non-empty line. Whitespace-only sources are blocked instead:
623. 1// unexpected_trailing_space and use_spaces are not in the fixable set.
624. 1
625. 1 [
626. 1 "", "\n", "\n\n", " ", " ", " \n ", "\t"
627. 7 ].forEach(function (source_degenerate) {
628. 7 assertAutofix(undefined, source_degenerate);
629. 7 });
630. 1
631. 1// A STOP in a later pass can only be the fixer's own doing - pass 0 parsed to
632. 1// the end - so the fix is DISCARDED, never written. test_internal_error throws
633. 1// AFTER phase 6, so pass 0 fixes, pass 1 stops on it, and <autofixed> must come
634. 1// back undefined instead of carrying pass 0's text.
635. 1
636. 1 result = jslint.jslint("String( 0);\n", {
637. 1 autofix: true,
638. 1 test_internal_error: true
639. 1 });
640. 1 assertOrThrow(
641. 1 result.stop && result.autofixed === undefined,
642. 1 JSON.stringify([result.stop, result.autofixed])
643. 1 );
644. 1
645. 1// And the discard is NAMED in this pass's own warnings, so a fixer defect
646. 1// cannot pass for an ordinary blocked file. This is also what separates
647. 1// "guard fired" from "no fix was ever attempted" - both leave <autofixed>
648. 1// undefined.
649. 1
650. 1 assertOrThrow(
651. 1 result.warnings.some(function ({
652. 1 message
653. 1 }) {
654. 1 return message.startsWith("[autofix discarded");
655. 1 }),
656. 3 JSON.stringify(result.warnings.map(function ({
657. 3 message
658. 3 }) {
659. 3 return message;
660. 3 }))
661. 1 );
662. 1
663. 1// A ONE-LINE source carries NO terminator at all, so jslint_rgx_crlf.exec()
664. 1// returns null and the rejoin falls back to '\n' - which is also appended,
665. 1// since the fixed code is missing a trailing one.
666. 1
667. 1 result = assertAutofix((
668. 1 "function aa(bb) {\n return bb;\n}\naa();\n"
669. 1 ), "function aa(bb) { return bb; } aa();");
670. 1 assertOrThrow(result.ok, JSON.stringify(result.warnings));
671. 1
672. 1// The appended terminator is the file's own, here '\r\n'.
673. 1
674. 1 assertAutofix((
675. 1 "function aa(bb) {\r\n return bb;\r\n}\r\naa();\r\n"
676. 1 ), "function aa(bb) { return bb; }\r\naa();");
677. 1
678. 1// A used label warns only its placement, so a mid-line label reaches phase 6,
679. 1// which splits it onto its own line at column 1.
680. 1
681. 1 assertAutofix(
682. 1 String(`
683. 1function aa(bb) {
684. 1 bb();
685. 1cc:
686. 1 while (bb) {
687. 1 if (bb()) {
688. 1 break cc;
689. 1 }
690. 1 bb();
691. 1 }
692. 1}
693. 1aa();
694. 1 `).trim() + "\n",
695. 1 String(`
696. 1function aa(bb) {
697. 1 bb(); cc: while (bb) {
698. 1 if (bb()) {
699. 1 break cc;
700. 1 }
701. 1 bb();
702. 1 }
703. 1}
704. 1aa();
705. 1 `).trim() + "\n"
706. 1 );
707. 1
708. 1// A whitespace-run reaching column 0 is INDENTATION or a line-join, not a gap
709. 1// between two tokens on one line, so the fix is DECLINED and the warning is
710. 1// reported against a byte-identical file. Here the run is the whole indent of
711. 1// a continuation line, which is why the warned column is 9 and not 1.
712. 1
713. 1 result = assertAutofix(undefined, (
714. 1 "function aa(bb) {\n return aa\n (bb);\n}\naa();\n"
715. 1 ));
716. 1 assertOrThrow(
717. 1 result.warnings.length === 1 &&
718. 1 result.warnings[0].code === "unexpected_space_a_b" &&
719. 1 result.warnings[0].line === 3 &&
720. 1 result.warnings[0].column === 9,
721. 1 JSON.stringify(result.warnings)
722. 1 );
723. 1
724. 1// A too_long ALREADY in the source does not block autofix - the fix is made,
725. 1// and too_long is still reported.
726. 1
727. 1 result = assertAutofix(
728. 1 String(`
729. 1function aa(bb) {
730. 1 return bb;
731. 1}
732. 1aa("${"a".repeat(80)}");
733. 1 `).trim() + "\n",
734. 1 String(`
735. 1function aa(bb) { return bb; }
736. 1aa("${"a".repeat(80)}");
737. 1 `).trim() + "\n"
738. 1 );
739. 1 assertOrThrow(
740. 1 result.warnings.length === 1 &&
741. 1 result.warnings[0].code === "too_long",
742. 1 JSON.stringify(result.warnings)
743. 1 );
744. 1 });
745. 1 jstestIt((
746. 1 "test autofix-cli handling-behavior"
747. 1 ), async function () {
748. 1 let source;
749. 1
750. 1// This function will autofix <name> in .tmp and assert it became <expect>.
751. 1// <exit> is processExit1 exactly when a residual warning remains - autofix
752. 1// exits nonzero like a plain lint, so `jslint_autofix=x.js && deploy` cannot
753. 1// succeed on a file it failed to repair.
754. 1
755. 13 async function autofixFile({
756. 13 exit = processExit0,
757. 13 expect,
758. 13 name,
759. 13 process_env,
760. 13 source
761. 13 }) {
762. 13 let file = ".tmp/" + name;
763. 13 await fsWriteFileWithParents(file, source);
764. 13 await jslint.jslint_cli({
765. 13 // suppress error
766. 13 console_error: noop,
767. 13 mode_cli: true,
768. 13 process_argv: [
769. 13 "node",
770. 13 "jslint.mjs",
771. 13 "jslint_autofix=" + file
772. 13 ],
773. 13 process_env,
774. 13 process_exit: exit
775. 13 });
776. 13 assertOrThrow(
777. 13 expect === await moduleFs.promises.readFile(file, "utf8"),
778. 13 file
779. 13 );
780. 13 }
781. 1
782. 1// Whitespace-only warnings - autofix repairs them and the file lints clean.
783. 1
784. 1 await autofixFile({
785. 1 expect: (
786. 1 "function aa(bb) {\n return String(bb) + bb;\n}\n" +
787. 1 "export default Object.freeze(aa);\n"
788. 1 ),
789. 1 name: "autofix.mjs",
790. 1 source: (
791. 1 "function aa(bb) {\n return String( bb)+bb;\n}\n" +
792. 1 "export default Object.freeze(aa);\n"
793. 1 )
794. 1 });
795. 1
796. 1// A non-whitespace warning blocks phase-5, so the file is REPORTED and left
797. 1// BYTE-IDENTICAL.
798. 1
799. 1 source = (
800. 1 "function aa(bb) {\n let cc = 0;\n" +
801. 1 " return String( bb);\n}\n"
802. 1 );
803. 1 await autofixFile({
804. 1 exit: processExit1,
805. 1 expect: source,
806. 1 name: "autofix_blocked.mjs",
807. 1 source
808. 1 });
809. 1
810. 1// Indentation is re-indented to the expected column, cascading across passes.
811. 1
812. 1 await autofixFile({
813. 1 expect: (
814. 1 "function aa(bb) {\n if (bb) {\n return bb;\n" +
815. 1 " }\n return 0;\n}\nexport default Object.freeze(aa);\n"
816. 1 ),
817. 1 name: "autofix_indent.mjs",
818. 1 source: (
819. 1 "function aa(bb) {\n if (bb) {\n return bb;\n" +
820. 1 " }\n return 0;\n}\n" +
821. 1 "export default Object.freeze(aa);\n"
822. 1 )
823. 1 });
824. 1
825. 1// A SINGLE-LINE TERNARY WARNS ONLY expected_a_at_b_c, yet what it actually
826. 1// wants is a line break before ? and before :. Autofix still gets there,
827. 1// because a column-warning on a MID-LINE token means that token belongs on
828. 1// its own line - so the break falls out of the column-fix. This is the case
829. 1// that justifies the mid-line branch; without it the fixer would skip these
830. 1// and never converge.
831. 1
832. 1 await autofixFile({
833. 1 expect: (
834. 1 "function aa(bb) {\n return (\n bb\n ? 0\n" +
835. 1 " : 1\n );\n}\nexport default Object.freeze(aa);\n"
836. 1 ),
837. 1 name: "autofix_ternary.mjs",
838. 1 source: (
839. 1 "function aa(bb) {\n return (\n bb ? 0 : 1\n" +
840. 1 " );\n}\nexport default Object.freeze(aa);\n"
841. 1 )
842. 1 });
843. 1
844. 1// An UNPARENTHESISED ternary warns something else entirely, which is not in
845. 1// fix_list, so the file must come back byte-identical.
846. 1
847. 1 source = (
848. 1 "function aa(bb) {\n return (bb ? 0 : 1);\n}\n" +
849. 1 "export default Object.freeze(aa);\n"
850. 1 );
851. 1 await autofixFile({
852. 1 exit: processExit1,
853. 1 expect: source,
854. 1 name: "autofix_ternary2.mjs",
855. 1 source
856. 1 });
857. 1
858. 1// A CLOSED-FORM statement block - opener and body on ONE line - needs BOTH
859. 1// kinds of fix, in separate passes, because a statement block is always open
860. 1// form (jslint_phase5_whitage). First a line-break, then column-fixes for the
861. 1// body and the closer the split leaves misplaced. Assert both kinds appear,
862. 1// so a regression that drops either one cannot hide behind the end-to-end
863. 1// test below.
864. 1
865. 1 assertOrThrow(
866. 1 jslint.jslint(
867. 1 "function aa(bb) {\n if (bb) { return bb; }\n" +
868. 1 " return 0;\n}\nexport default Object.freeze(aa);\n"
869. 1 ).warnings.some(function ({
870. 1 code
871. 1 }) {
872. 1 return code === "expected_line_break_a_b";
873. 1 }),
874. 1 "closed-form block must warn expected_line_break_a_b"
875. 1 );
876. 1 assertOrThrow(
877. 1 jslint.jslint(
878. 1 "function aa(bb) {\n if (bb) {\nreturn bb;}\n" +
879. 1 " return 0;\n}\nexport default Object.freeze(aa);\n"
880. 2 ).warnings.filter(function ({
881. 2 code
882. 2 }) {
883. 2 return code === "expected_a_at_b_c";
884. 2 }).length === 2,
885. 1 "the split leaves body AND closer needing a column-fix"
886. 1 );
887. 1
888. 1// A one-liner block is split, re-indented and its closer moved, across
889. 1// passes. The trailing comment must survive, attached to the closer.
890. 1
891. 1 await autofixFile({
892. 1 expect: (
893. 1 "function aa(bb) {\n if (bb) {\n return bb;\n" +
894. 1 " } // keep me\n return 0;\n}\n" +
895. 1 "export default Object.freeze(aa);\n"
896. 1 ),
897. 1 name: "autofix_break.mjs",
898. 1 source: (
899. 1 "function aa(bb) {\n if (bb) { return bb; } // keep me\n" +
900. 1 " return 0;\n}\nexport default Object.freeze(aa);\n"
901. 1 )
902. 1 });
903. 1
904. 1// A *.sh file is not javascript: only its `node --eval` blocks are fixed,
905. 1// and the surrounding shell must come back byte-identical.
906. 1
907. 1 await autofixFile({
908. 1 expect: (
909. 1 "shAa() {\n node --eval '\nconsole.log(\n 0 +\n 0\n" +
910. 1 ");\n'\n}\n"
911. 1 ),
912. 1 name: "autofix_embedded.sh",
913. 1 process_env: {
914. 1 JSLINT_BETA: "1"
915. 1 },
916. 1 source: (
917. 1 "shAa() {\n node --eval '\nconsole.log(\n 0\n + 0\n" +
918. 1 ");\n'\n}\n"
919. 1 )
920. 1 });
921. 1
922. 1// An embedded block with a too_long ALREADY in it is still fixed, and the
923. 1// residual too_long still exits nonzero.
924. 1
925. 1 source = String(`
926. 1shAa() {
927. 1 node --eval '
928. 1console.log("${"a".repeat(80)}");
929. 1console.log( 0);
930. 1'
931. 1}
932. 1 `).trim() + "\n";
933. 1 await autofixFile({
934. 1 exit: processExit1,
935. 1 expect: source.replace("( 0)", "(0)"),
936. 1 name: "autofix_embedded_long.sh",
937. 1 source
938. 1 });
939. 1
940. 1// A too_long that a fix SURFACES keeps the fix and blocks none after it. The
941. 1// join makes line 3 82 columns, and the closed-form block below still needs
942. 1// two more passes - its split, then its re-indent.
943. 1
944. 1 source = String(`
945. 1/*jslint beta*/
946. 1function aa(bb, cc) {
947. 1 return bb.${"a".repeat(66)}
948. 1 + cc;
949. 1}
950. 1function dd(ee) { return ee; }
951. 1aa(dd(0), 0);
952. 1 `).trim() + "\n";
953. 1 await autofixFile({
954. 1 exit: processExit1,
955. 1 expect: source.replace(
956. 1 "\n + cc;",
957. 1 " +\n cc;"
958. 1 ).replace(
959. 1 "{ return ee; }",
960. 1 "{\n return ee;\n}"
961. 1 ),
962. 1 name: "autofix_long_join.mjs",
963. 1 source
964. 1 });
965. 1
966. 1// An *.html file is fixed the same way, but through its <script> blocks and
967. 1// with browser:true - mirroring how jslint_from_file lints them.
968. 1
969. 1 await autofixFile({
970. 1 expect: (
971. 1 "<body>\n<script>\n/*jslint browser*/\nwindow.console.log(\n" +
972. 1 " 0\n + 0\n);\n</script>\n</body>\n"
973. 1 ),
974. 1 name: "autofix_embedded.html",
975. 1 source: (
976. 1 "<body>\n<script>\n/*jslint browser*/\nwindow.console.log(\n" +
977. 1 " 0\n + 0\n);\n</script>\n</body>\n"
978. 1 )
979. 1 });
980. 1
981. 1// A *.md file is linted with mode_conditional, i.e. only blocks carrying a
982. 1// /*jslint directive. Autofix inherits that from jslint_from_file, so the
983. 1// FIRST block below is repaired and the SECOND is left byte-identical.
984. 1
985. 1 await autofixFile({
986. 1 expect: (
987. 1 "# aa\n\nnode --eval '\n/*jslint node*/\nconsole.log(\n" +
988. 1 " 0 +\n 0\n);\n'\n\nnode --eval '\nconsole.log(\n" +
989. 1 " 0\n + 0\n);\n'\n"
990. 1 ),
991. 1 name: "autofix_embedded.md",
992. 1 process_env: {
993. 1 JSLINT_BETA: "1"
994. 1 },
995. 1 source: (
996. 1 "# aa\n\nnode --eval '\n/*jslint node*/\nconsole.log(\n" +
997. 1 " 0\n + 0\n);\n'\n\nnode --eval '\nconsole.log(\n" +
998. 1 " 0\n + 0\n);\n'\n"
999. 1 )
1000. 1 });
1001. 1
1002. 1// A CRLF or CR container is fixed the same way, and its line terminators
1003. 1// survive, inside the embedded block too.
1004. 1
1005. 1 await autofixFile({
1006. 1 expect: String(
1007. 1 "shAa() {\n node --eval '\nconsole.log(\n 0 +\n 0\n" +
1008. 1 ");\n'\n}\n"
1009. 1 ).replace((/\n/g), "\r\n"),
1010. 1 name: "autofix_embedded_crlf.sh",
1011. 1 process_env: {
1012. 1 JSLINT_BETA: "1"
1013. 1 },
1014. 1 source: String(
1015. 1 "shAa() {\n node --eval '\nconsole.log(\n 0\n + 0\n" +
1016. 1 ");\n'\n}\n"
1017. 1 ).replace((/\n/g), "\r\n")
1018. 1 });
1019. 1 await autofixFile({
1020. 1 expect: String(
1021. 1 "<body>\n<script>\n/*jslint browser*/\nwindow.console.log(\n" +
1022. 1 " 0\n + 0\n);\n</script>\n</body>\n"
1023. 1 ).replace((/\n/g), "\r"),
1024. 1 name: "autofix_embedded_cr.html",
1025. 1 source: String(
1026. 1 "<body>\n<script>\n/*jslint browser*/\nwindow.console.log(\n" +
1027. 1 " 0\n + 0\n);\n</script>\n</body>\n"
1028. 1 ).replace((/\n/g), "\r")
1029. 1 });
1030. 1
1031. 1// A missing file exits 1 with the error printed, like a plain lint - not an
1032. 1// unhandled rejection that never reaches process_exit.
1033. 1
1034. 1 await jslint.jslint_cli({
1035. 1 // suppress error
1036. 1 console_error: noop,
1037. 1 mode_cli: true,
1038. 1 process_argv: [
1039. 1 "node",
1040. 1 "jslint.mjs",
1041. 1 "jslint_autofix=.tmp/autofix_missing.mjs"
1042. 1 ],
1043. 1 process_exit: processExit1
1044. 1 });
1045. 1
1046. 1// A DIRECTORY rides the same walk as a plain lint of one, and every file whose
1047. 1// lint returns <autofixed> is written back; a clean file is left alone.
1048. 1
1049. 1 await fsWriteFileWithParents(
1050. 1 ".tmp/autofix_dir/aa.mjs",
1051. 1 "String( 0);\n"
1052. 1 );
1053. 1 await fsWriteFileWithParents(
1054. 1 ".tmp/autofix_dir/bb.mjs",
1055. 1 "String(0);\n"
1056. 1 );
1057. 1 await jslint.jslint_cli({
1058. 1 // suppress error
1059. 1 console_error: noop,
1060. 1 mode_cli: true,
1061. 1 process_argv: [
1062. 1 "node",
1063. 1 "jslint.mjs",
1064. 1 "jslint_autofix=.tmp/autofix_dir"
1065. 1 ],
1066. 1 process_exit: processExit0
1067. 1 });
1068. 1 assertOrThrow(
1069. 1 (
1070. 1 await moduleFs.promises.readFile(
1071. 1 ".tmp/autofix_dir/aa.mjs",
1072. 1 "utf8"
1073. 1 )
1074. 1 ) === "String(0);\n",
1075. 1 ".tmp/autofix_dir/aa.mjs"
1076. 1 );
1077. 1 assertOrThrow(
1078. 1 (
1079. 1 await moduleFs.promises.readFile(
1080. 1 ".tmp/autofix_dir/bb.mjs",
1081. 1 "utf8"
1082. 1 )
1083. 1 ) === "String(0);\n",
1084. 1 ".tmp/autofix_dir/bb.mjs"
1085. 1 );
1086. 1 });
1087. 1});
1088. 1
1089. 1jstestDescribe((
1090. 1 "test jslint's cli handling-behavior"
1091. 1), function () {
1092. 1 jstestIt((
1093. 1 "test cli-apidoc handling-behavior"
1094. 1 ), function () {
1095. 1 jslint.jslint_cli({
1096. 1 mode_cli: true,
1097. 1 process_argv: [
1098. 1 "node",
1099. 1 "jslint.mjs",
1100. 1 "jslint_apidoc=.artifact/apidoc.html",
1101. 1 JSON.stringify({
1102. 1 example_list: [
1103. 1 "README.md",
1104. 1 "test.mjs",
1105. 1 "jslint.mjs"
1106. 1 ],
1107. 1 github_repo: "https://github.com/jslint-org/jslint",
1108. 1 module_list: [
1109. 1 {
1110. 1 pathname: "./jslint.mjs"
1111. 1 }
1112. 1 ],
1113. 1 package_name: "JSLint",
1114. 1 version: jslint.jslint_edition
1115. 1 })
1116. 1 ],
1117. 1 process_exit: processExit0
1118. 1 });
1119. 1 });
1120. 1 jstestIt((
1121. 1 "test cli-cjs-lint-file handling-behavior"
1122. 1 ), async function () {
1123. 1
1124. 1// Through the cjs wrapper, <jslint_cli> must lint, not resolve 0 unseen: in
1125. 1// its old new-context sandbox there was no process, so the cli returned early.
1126. 1
1127. 1 await fsWriteFileWithParents(
1128. 1 ".tmp/cli_cjs_lint_file/aa.js",
1129. 1 "let aa = 1;\n"
1130. 1 );
1131. 1 assertJsonEqual(
1132. 1 await jslintCjs.jslint_cli({
1133. 1 console_error: noop,
1134. 1 mode_cli: true,
1135. 1 process_argv: [
1136. 1 "node",
1137. 1 "jslint.mjs",
1138. 1 ".tmp/cli_cjs_lint_file/aa.js"
1139. 1 ],
1140. 1 process_exit: processExit1
1141. 1 }),
1142. 1 1
1143. 1 );
1144. 1 });
1145. 1 jstestIt((
1146. 1 "test cli-embedded-line-offset handling-behavior"
1147. 1 ), async function () {
1148. 1
1149. 1// An embedded block whose opening tag is LINE 1 of its container has no line
1150. 1// terminator before it, so the offset arithmetic must still yield 1 - a
1151. 1// precedence slip once made it 0, and every warning in the block reported one
1152. 1// line too high. <foo> sits on file-line 3 and must be reported there.
1153. 1
1154. 1 const stderr_list = [];
1155. 1 await fsWriteFileWithParents(
1156. 1 ".tmp/embedded_line1.html",
1157. 1 "<script>\n/*jslint browser*/\nfoo;\n</script>\n"
1158. 1 );
1159. 1 await jslint.jslint_cli({
1160. 1 console_error: function (msg) {
1161. 1 stderr_list.push(String(msg));
1162. 1 },
1163. 1 mode_cli: true,
1164. 1 process_argv: [
1165. 1 "node",
1166. 1 "jslint.mjs",
1167. 1 ".tmp/embedded_line1.html"
1168. 1 ],
1169. 1 process_exit: processExit1
1170. 1 });
1171. 1 assertOrThrow(
1172. 1 stderr_list.some(function (msg) {
1173. 1 return msg.includes("line 3, column 1");
1174. 1 }),
1175. 1 JSON.stringify(stderr_list)
1176. 1 );
1177. 1 });
1178. 1 jstestIt((
1179. 1 "test cli-file-error handling-behavior"
1180. 1 ), function () {
1181. 1 jslint.jslint_cli({
1182. 1 // suppress error
1183. 1 console_error: noop,
1184. 1 file: "undefined",
1185. 1 mode_cli: true,
1186. 1 process_exit: processExit1
1187. 1 });
1188. 1 });
1189. 1 jstestIt((
1190. 1 "test cli-file-kinds handling-behavior"
1191. 1 ), async function () {
1192. 1 await moduleFs.promises.mkdir(
1193. 1 ".tmp/invalid_file/invalid_file.js",
1194. 1 {recursive: true}
1195. 1 );
1196. 1 [
1197. 1 ".", // test dir handling-behavior
1198. 1 ".tmp/invalid_file",// test invalid-file handling-behavior
1199. 1 "jslint.mjs", // test file handling-behavior
1200. 1 undefined // test file-undefined handling-behavior
1201. 4 ].forEach(function (file) {
1202. 4 jslint.jslint_cli({
1203. 4 file,
1204. 4 mode_cli: true,
1205. 4 process_env: {
1206. 4 JSLINT_BETA: "1"
1207. 4 },
1208. 4 process_exit: processExit0
1209. 4 });
1210. 4 });
1211. 1 });
1212. 1 jstestIt((
1213. 1 "test cli-jslint-wrapper-vim handling-behavior"
1214. 1 ), function () {
1215. 1 jslint.jslint_cli({
1216. 1 // suppress error
1217. 1 console_error: noop,
1218. 1 mode_cli: true,
1219. 1 process_argv: [
1220. 1 "node",
1221. 1 "jslint.mjs",
1222. 1 "jslint_wrapper_vim",
1223. 1 "syntax-error.js"
1224. 1 ],
1225. 1 process_exit: processExit1,
1226. 1 source: "syntax error"
1227. 1 });
1228. 1 });
1229. 1 jstestIt((
1230. 1 "test cli-null-case handling-behavior"
1231. 1 ), function () {
1232. 1 jslint.jslint_cli({
1233. 1 mode_noop: true,
1234. 1 process_exit: processExit0
1235. 1 });
1236. 1 });
1237. 1 jstestIt((
1238. 1 "test cli-report handling-behavior"
1239. 1 ), function () {
1240. 1 jslint.jslint_cli({
1241. 1 // suppress error
1242. 1 console_error: noop,
1243. 1 mode_cli: true,
1244. 1 process_argv: [
1245. 1 "node",
1246. 1 "jslint.mjs",
1247. 1 "jslint_report=.tmp/jslint_report.html",
1248. 1 "jslint.mjs"
1249. 1 ],
1250. 1 process_exit: processExit0
1251. 1 });
1252. 1 });
1253. 1 jstestIt((
1254. 1 "test cli-report-embedded handling-behavior"
1255. 1 ), async function () {
1256. 1
1257. 1// A container-file returns no single lint-result, so jslint_report refuses it
1258. 1// with exit 1 instead of crashing on the missing <warnings>.
1259. 1
1260. 1 await fsWriteFileWithParents(".tmp/jslint_report.md", "# aa\n");
1261. 1 await jslint.jslint_cli({
1262. 1 // suppress error
1263. 1 console_error: noop,
1264. 1 mode_cli: true,
1265. 1 process_argv: [
1266. 1 "node",
1267. 1 "jslint.mjs",
1268. 1 "jslint_report=.tmp/jslint_report_embedded.html",
1269. 1 ".tmp/jslint_report.md"
1270. 1 ],
1271. 1 process_exit: processExit1
1272. 1 });
1273. 1 });
1274. 1 jstestIt((
1275. 1 "test cli-report-error handling-behavior"
1276. 1 ), function () {
1277. 1 jslint.jslint_cli({
1278. 1 // suppress error
1279. 1 console_error: noop,
1280. 1 mode_cli: true,
1281. 1 process_argv: [
1282. 1 "node",
1283. 1 "jslint.mjs",
1284. 1 "jslint_report=.tmp/jslint_report.html",
1285. 1 "syntax-error.js"
1286. 1 ],
1287. 1 process_exit: processExit1,
1288. 1 source: "syntax error"
1289. 1 });
1290. 1 });
1291. 1 jstestIt((
1292. 1 "test cli-report-json handling-behavior"
1293. 1 ), function () {
1294. 1 jslint.jslint_cli({
1295. 1 // suppress error
1296. 1 console_error: noop,
1297. 1 mode_cli: true,
1298. 1 process_argv: [
1299. 1 "node",
1300. 1 "jslint.mjs",
1301. 1 "jslint_report=.tmp/jslint_report.html",
1302. 1 "aa.json"
1303. 1 ],
1304. 1 process_exit: processExit0,
1305. 1 source: "[]"
1306. 1 });
1307. 1 });
1308. 1 jstestIt((
1309. 1 "test cli-report-json-error handling-behavior"
1310. 1 ), function () {
1311. 1 jslint.jslint_cli({
1312. 1 // suppress error
1313. 1 console_error: noop,
1314. 1 mode_cli: true,
1315. 1 process_argv: [
1316. 1 "node",
1317. 1 "jslint.mjs",
1318. 1 "jslint_report=.tmp/jslint_report.html",
1319. 1 "aa.json"
1320. 1 ],
1321. 1 process_exit: processExit1,
1322. 1 source: "["
1323. 1 });
1324. 1 });
1325. 1 jstestIt((
1326. 1 "test cli-report-misc handling-behavior"
1327. 1 ), function () {
1328. 1 jslint.jslint_cli({
1329. 1 // suppress error
1330. 1 console_error: noop,
1331. 1 mode_cli: true,
1332. 1 process_argv: [
1333. 1 "node",
1334. 1 "jslint.mjs",
1335. 1 "jslint_report=.tmp/jslint_report.html",
1336. 1 "aa.js"
1337. 1 ],
1338. 1 process_exit: processExit0,
1339. 1 source: "String();"
1340. 1 });
1341. 1 jslint.jslint_cli({
1342. 1 // suppress error
1343. 1 console_error: noop,
1344. 1 mode_cli: true,
1345. 1 process_argv: [
1346. 1 "node",
1347. 1 "jslint.mjs",
1348. 1 "jslint_report=.tmp/jslint_report.html",
1349. 1 "aa.js"
1350. 1 ],
1351. 1 process_exit: processExit1,
1352. 1 source: "(aa)=>aa; function aa([aa]){}"
1353. 1 });
1354. 1 });
1355. 1 jstestIt((
1356. 1 "test cli-syntax-error handling-behavior"
1357. 1 ), function () {
1358. 1 jslint.jslint_cli({
1359. 1 // suppress error
1360. 1 console_error: noop,
1361. 1 file: "syntax-error.js",
1362. 1 mode_cli: true,
1363. 1 option: {
1364. 1 trace: true
1365. 1 },
1366. 1 process_exit: processExit1,
1367. 1 source: "syntax error"
1368. 1 });
1369. 1 });
1370. 1 jstestIt((
1371. 1 "test cli-window-jslint handling-behavior"
1372. 1 ), function () {
1373. 1 [
1374. 1 "&window_jslint=",
1375. 1 "&window_jslint=12",
1376. 1 "&window_jslint=1?",
1377. 1 "&window_jslint=?",
1378. 1 "?window_jslint=",
1379. 1 "?window_jslint=12",
1380. 1 "?window_jslint=1?",
1381. 1 "?window_jslint=?",
1382. 1 "window_jslint=1",
1383. 1 "window_jslint=1&",
1384. 1 "window_jslint=12",
1385. 1 "window_jslint=1?"
1386. 12 ].forEach(function (import_meta_url) {
1387. 12 jslint.jslint_cli({
1388. 12 import_meta_url
1389. 12 });
1390. 12 assertOrThrow(globalThis.jslint === undefined);
1391. 12 });
1392. 1 [
1393. 1 "&window_jslint=1",
1394. 1 "&window_jslint=1&",
1395. 1 "?window_jslint=1",
1396. 1 "?window_jslint=1&"
1397. 4 ].forEach(function (import_meta_url) {
1398. 4 jslint.jslint_cli({
1399. 4 import_meta_url
1400. 4 });
1401. 4 assertOrThrow(globalThis.jslint === jslint);
1402. 4 delete globalThis.jslint;
1403. 4 });
1404. 1 });
1405. 1});
1406. 1
1407. 1jstestDescribe((
1408. 1 "test jslint's no-warnings handling-behavior"
1409. 1), function () {
1410. 1 jstestIt((
1411. 1 "test jslint's no-warnings handling-behavior"
1412. 1 ), function () {
1413. 1 Object.values({
1414. 1 array: [
1415. 1 "new Array(0);"
1416. 1 ],
1417. 1 async_await: [
1418. 1 (`
1419. 1/*jslint fart*/
1420. 1let aa = 0;
1421. 1aa = async () => {
1422. 1 try {
1423. 1 return await aa();
1424. 1 } catch (err) {
1425. 1 await err();
1426. 1 }
1427. 1};
1428. 1await aa();
1429. 1 `),
1430. 1 "async function aa() {\n await aa();\n}\nawait aa();",
1431. 1
1432. 1// PR-405 - Bugfix - fix expression after "await" mis-identified as statement.
1433. 1
1434. 1 "async function aa() {\n await aa;\n}\nawait aa();",
1435. 1 (`
1436. 1async function aa() {
1437. 1 try {
1438. 1 return await aa();
1439. 1 } catch (err) {
1440. 1 await err();
1441. 1 }
1442. 1}
1443. 1await aa();
1444. 1 `)
1445. 1 ],
1446. 1
1447. 1// PR-351 - Add BigInt support.
1448. 1
1449. 1 bigint: [
1450. 1 (`
1451. 1String(0b0n);
1452. 1String(0o0n);
1453. 1String(0x0n);
1454. 1String(BigInt(0n));
1455. 1String(typeof String === "bigint");
1456. 1 `)
1457. 1 ],
1458. 1 date: [
1459. 1 (`
1460. 1Date.getTime();
1461. 1String().getTime();
1462. 1String.aa().getTime();
1463. 1 `)
1464. 1 ],
1465. 1 destructure: [
1466. 1
1467. 1// PR-500 - Unify ES2015-destructure-logic.
1468. 1
1469. 1 [
1470. 1 (`
1471. 1(function ({expr}) {
1472. 1 aa(bb, cc, dd, ee, ff, gg);
1473. 1}());
1474. 1 `),
1475. 1 (`
1476. 1(function for_loop() {
1477. 1 for (const {expr} of for_loop) {
1478. 1 aa(bb, cc, dd, ee, ff, gg);
1479. 1 }
1480. 1}());
1481. 1 `),
1482. 1 (`
1483. 1/*jslint fart*/
1484. 1(({expr}) => {
1485. 1 aa(bb, cc, dd, ee, ff, gg);
1486. 1}());
1487. 1 `),
1488. 1 "const {expr}",
1489. 1 "let {expr}",
1490. 1 "let [aa, bb, cc, dd, ee, ff, gg] = 0;\n{expr}"
1491. 6 ].map(function (source) {
1492. 6 let expr = String(`
1493. 6[
1494. 6 {
1495. 6 cc,
1496. 6 dd = 0,
1497. 6 dd: ee,
1498. 6 ee: [{bb}],
1499. 6 ...aa
1500. 6 },
1501. 6 [
1502. 6 gg,
1503. 6 ...ff
1504. 6 ]
1505. 6]
1506. 6 `).trim();
1507. 1 if ((/function for_loop/).test(source)) {
1508. 1 expr = expr.replace((/\n/g), "\n ");
1509. 1 }
1510. 6 source = source.trim().replace("{expr}", expr);
1511. 3 if (!(/\=>|function/).test(source)) {
1512. 3 source = (`
1513. 3${source} = (function () {
1514. 3 return;
1515. 3}());
1516. 3aa(bb, cc, dd, ee, ff, gg);
1517. 3 `);
1518. 3 }
1519. 6 return source;
1520. 6 }),
1521. 1
1522. 1// PR-459 - Allow destructuring-assignment after function-definition.
1523. 1
1524. 1 (`
1525. 1let aa;
1526. 1let bb;
1527. 1function cc() {
1528. 1 return;
1529. 1}
1530. 1[aa, bb] = cc();
1531. 1aa(bb, cc);
1532. 1 `),
1533. 1
1534. 1// PR-514 - Bugfix - Walk a default in destructuring-assignment, so 'cc' is
1535. 1// used.
1536. 1
1537. 1 (`
1538. 1let aa;
1539. 1let cc = 0;
1540. 1[
1541. 1 [
1542. 1 aa = cc
1543. 1 ]
1544. 1] = [];
1545. 1aa();
1546. 1 `)
1547. 1 ],
1548. 1 directive: [
1549. 1 (`
1550. 1#!/usr/bin/env node
1551. 1/*global aa*/
1552. 1/*jslint browser:false, node*/
1553. 1/*property bb*/
1554. 1"use strict";
1555. 1aa.bb();
1556. 1 `)
1557. 1 ],
1558. 1 ellipsis: [
1559. 1
1560. 1// PR-483 - Allow parenthesis after ellipsis inside a function call.
1561. 1
1562. 1 (`
1563. 1String(0, ...(
1564. 1 String()
1565. 1 ? 0
1566. 1 : 1
1567. 1));
1568. 1 `),
1569. 1
1570. 1// Issue #401 - Add ES2018-syntax for object-literal-spread-operator.
1571. 1
1572. 1 (`
1573. 1let aa;
1574. 1aa = [
1575. 1 0,
1576. 1 ...aa(),
1577. 1 0,
1578. 1 ...aa,
1579. 1 0,
1580. 1 ...aa.aa,
1581. 1 0
1582. 1];
1583. 1aa = {
1584. 1 aa: 0,
1585. 1 ...aa(),
1586. 1 bb: 0,
1587. 1 ...aa,
1588. 1 cc: 0,
1589. 1 ...aa.aa,
1590. 1 dd: 0
1591. 1};
1592. 1aa();
1593. 1 `)
1594. 1 ],
1595. 1 fart: [
1596. 1 (`
1597. 1/*jslint fart*/
1598. 1let aa = async (bb, [cc, dd], {ee, ff}, ...gg) => {
1599. 1 await bb(cc, dd, ee, ff, gg);
1600. 1};
1601. 1aa();
1602. 1 `),
1603. 1 "let aa = () => 0;\naa();"
1604. 1 ],
1605. 1 for: [
1606. 1 (`
1607. 1async function aa(bb, cc) {
1608. 1 for (; bb < 0; bb += 1) { //jslint-ignore-line
1609. 1 bb(cc);
1610. 1 }
1611. 1 for (bb = 0; bb < 0; bb += 1) { //jslint-ignore-line
1612. 1 bb(cc);
1613. 1 }
1614. 1 for (cc in bb) { //jslint-ignore-line
1615. 1 bb(cc);
1616. 1 }
1617. 1 for (cc of bb) { //jslint-ignore-line
1618. 1 cc();
1619. 1 }
1620. 1 for (const ii in bb) { //jslint-ignore-line
1621. 1 bb(cc, ii);
1622. 1 }
1623. 1 for (const ii of bb) {
1624. 1 bb(cc, ii);
1625. 1 }
1626. 1 for (let ii = 0; ii < 0; ii += 1) {
1627. 1 bb(cc, ii);
1628. 1 }
1629. 1 for (let ii of bb) {
1630. 1 bb(cc, ii);
1631. 1 }
1632. 1 for await (const ii of bb) {
1633. 1 bb(cc, ii);
1634. 1 }
1635. 1 for await (let ii of bb) {
1636. 1 bb(cc, ii);
1637. 1 }
1638. 1 for (const ii of await (bb())) {
1639. 1 bb(cc, ii);
1640. 1 }
1641. 1}
1642. 1aa();
1643. 1 `),
1644. 1
1645. 1// PR-514 - Bugfix - Walk the iterable of destructured for..of.
1646. 1
1647. 1 (`
1648. 1function aa(bb) {
1649. 1 for (const [cc, dd] of bb) {
1650. 1 cc(dd);
1651. 1 }
1652. 1 for (const {ee} of bb) {
1653. 1 ee();
1654. 1 }
1655. 1}
1656. 1aa();
1657. 1 `),
1658. 1
1659. 1// PR-514 - Bugfix - A ';' in a method-body inside a for-loop-head is not a
1660. 1// for-loop-semicolon.
1661. 1
1662. 1 (`
1663. 1function aa(bb) {
1664. 1 for (const cc in { //jslint-ignore-line
1665. 1 dd() {
1666. 1 return;
1667. 1 }
1668. 1 }) {
1669. 1 bb(cc);
1670. 1 }
1671. 1}
1672. 1aa();
1673. 1 `),
1674. 1
1675. 1// PR-514 - Bugfix - The '}' of a '${' does not pop the '{' of a function-body
1676. 1// in a for-loop-head, since '${' is pushed too.
1677. 1
1678. 1 (`
1679. 1function aa(bb) {
1680. 1 for (const cc of function () {
1681. 1 return \`\${bb}\`;
1682. 1 }()) { //jslint-ignore-line
1683. 1 bb(cc);
1684. 1 }
1685. 1}
1686. 1aa();
1687. 1 `)
1688. 1 ],
1689. 1 import: [
1690. 1 `let aa = 0;\nimport(aa).then(aa).catch(aa).finally(aa);`,
1691. 1 `let aa = await import("aa");\naa();`,
1692. 1 `let aa = await import("aa", {with: {type: "json"}});\naa();`,
1693. 1 `let {aa, bb} = await import("aa");\naa(bb);`
1694. 1 ],
1695. 1 indent_method: [
1696. 1 (`
1697. 1String
1698. 1 .aa
1699. 1 .aa()
1700. 1 .aa(0)
1701. 1 .aa(
1702. 1 0,
1703. 1 [0],
1704. 1 \`${0}\`
1705. 1 )
1706. 1 .aa(
1707. 1 String
1708. 1 .aa()
1709. 1 .aa(0)
1710. 1 .aa(
1711. 1 0,
1712. 1 [0],
1713. 1 \`${0}\`
1714. 1 )
1715. 1 )
1716. 1 .aa(function ({
1717. 1 aa
1718. 1 }) {
1719. 1 return aa;
1720. 1 });
1721. 1 `)
1722. 1 ],
1723. 1 jslint_disable: [
1724. 1 "/*jslint-disable*/\n0\n/*jslint-enable*/"
1725. 1 ],
1726. 1 jslint_ignore_line: [
1727. 1 "0 //jslint-ignore-line",
1728. 1 "new aa //jslint-ignore-line"
1729. 1 ],
1730. 1 json: [
1731. 1 "{\"aa\":[[],-0,null]}"
1732. 1 ],
1733. 1 label: [
1734. 1 (`
1735. 1function aa() {
1736. 1bb: do {
1737. 1 if (true) {
1738. 1 break bb;
1739. 1 }
1740. 1 } while (true);
1741. 1}
1742. 1aa();
1743. 1 `),
1744. 1 (`
1745. 1function aa() {
1746. 1bb: for (const ii of aa) {
1747. 1 if (ii) {
1748. 1 break bb;
1749. 1 }
1750. 1 }
1751. 1}
1752. 1aa();
1753. 1 `),
1754. 1 (`
1755. 1function aa() {
1756. 1bb: for (let ii = 0; ii < 0; ii += 1) {
1757. 1 if (ii) {
1758. 1 break bb;
1759. 1 }
1760. 1 }
1761. 1}
1762. 1aa();
1763. 1 `),
1764. 1 (`
1765. 1function aa() {
1766. 1bb: switch (aa) {
1767. 1 case 0:
1768. 1 break bb;
1769. 1 }
1770. 1}
1771. 1aa();
1772. 1 `),
1773. 1 (`
1774. 1function aa() {
1775. 1bb: while (true) {
1776. 1 if (true) {
1777. 1 break bb;
1778. 1 }
1779. 1 }
1780. 1}
1781. 1aa();
1782. 1 `)
1783. 1 ],
1784. 1 literal: [
1785. 1 "String(\"\".at());",
1786. 1 "String(\"\\u{000041}\");",
1787. 1 "String(\"\\u{10FFFF}\");",
1788. 1 "String(String.raw`\\u{110000}${0}\\u0`);",
1789. 1 "String([].at());",
1790. 1 "String(`\\u{000041}`);",
1791. 1 "String(`\\u{10FFFF}`);"
1792. 1 ],
1793. 1 logical_assignment: [
1794. 1
1795. 1// PR-514 - Bugfix - '??=' and '||=' assign an unassigned variable.
1796. 1
1797. 1 (`
1798. 1function aa(bb) {
1799. 1 let cc;
1800. 1 cc ??= bb;
1801. 1 return cc;
1802. 1}
1803. 1aa();
1804. 1 `),
1805. 1 (`
1806. 1function aa(bb) {
1807. 1 let cc;
1808. 1 cc ||= bb;
1809. 1 return cc;
1810. 1}
1811. 1aa();
1812. 1 `),
1813. 1 "let aa = 0;\naa &&= 0;",
1814. 1 "let aa = 0;\naa ??= 0;",
1815. 1 "let aa = 0;\naa ||= 0;"
1816. 1 ],
1817. 1 loop: [
1818. 1 (`
1819. 1function aa() {
1820. 1 do {
1821. 1 aa();
1822. 1 } while (aa());
1823. 1}
1824. 1aa();
1825. 1 `),
1826. 1
1827. 1// PR-378 - Relax warning "function_in_loop".
1828. 1
1829. 1 (`
1830. 1function aa() {
1831. 1 while (true) {
1832. 1 (function () {
1833. 1 return;
1834. 1 }());
1835. 1 }
1836. 1}
1837. 1aa();
1838. 1 `)
1839. 1 ],
1840. 1 module: [
1841. 1 "export default Object.freeze();",
1842. 1
1843. 1// PR-439 - Add grammar for "export async function ...".
1844. 1
1845. 1 (`
1846. 1export default Object.freeze(async function () {
1847. 1 return await 0;
1848. 1});
1849. 1 `),
1850. 1 // `import "aa";`,
1851. 1 `import * as aa from "aa";\naa();`,
1852. 1 `import aa from "aa" with {type: "json"};\naa();`,
1853. 1 `import aa from "aa";\naa();`,
1854. 1 `import aa, {aa as bb, cc} from "aa";\naa(bb, cc);`,
1855. 1 `import {} from "aa";`
1856. 1 ],
1857. 1 new: [
1858. 1 "new String`aa`();"
1859. 1 ],
1860. 1 number: [
1861. 1 "String(0.0e0);",
1862. 1 "String(0b0);",
1863. 1 "String(0o0);",
1864. 1 "String(0x0);"
1865. 1 ],
1866. 1
1867. 1// PR-390 - Add numeric-separator support.
1868. 1
1869. 1 numeric_separator: [
1870. 1 "String(0.0_0_0);",
1871. 1 "String(0b0_1111_1111n);",
1872. 1 "String(0o0_1237_1237n);",
1873. 1 "String(0x0_123f_123fn);",
1874. 1 "String(1_234_234.1_234_234E1_234_234);"
1875. 1 ],
1876. 1 optional_chaining: [
1877. 1 "String().aa?.bb?.cc();",
1878. 1 "delete String?.[0];",
1879. 1 "delete String?.aa;"
1880. 1 ],
1881. 1 param: [
1882. 1 "function aa({aa, bb}) {\n return {aa, bb};\n}\naa();",
1883. 1 (`
1884. 1function aa({constructor}) {
1885. 1 return {constructor};
1886. 1}
1887. 1aa();
1888. 1 `)
1889. 1 ],
1890. 1 property: [
1891. 1 "String[`!`]();"
1892. 1 ],
1893. 1 regexp: [
1894. 1 `RegExp.escape("");`,
1895. 1 `String(/(?!.)(?:.)(?=.)/);`,
1896. 1 `String(/(?im-s:.)/);`,
1897. 1 `String(/./dgimsuy);`,
1898. 1 `String(/./dgimsvy);`,
1899. 1 `String(/[\\--\\-]/);`,
1900. 1 `function aa() {\n return /./;\n}\naa();`
1901. 1 ],
1902. 1 scope: [
1903. 1 "(function aa(bb = aa) {\n aa(bb);\n}());",
1904. 1
1905. 1// PR-514 - Bugfix - A 'var' named after its named function expression is a new
1906. 1// writable binding, so assigning it does not warn bad_assignment_a.
1907. 1
1908. 1 (`
1909. 1String(function aa() {
1910. 1 var aa = 0; //jslint-ignore-line
1911. 1 aa = 1;
1912. 1 return aa;
1913. 1});
1914. 1 `),
1915. 1
1916. 1// PR-514 - A parameter named after its named function expression shadows the
1917. 1// name, like any parameter shadowing an outer name, so it does not warn.
1918. 1
1919. 1 "String(function aa(aa) {\n return aa;\n});",
1920. 1
1921. 1// PR-514 - Bugfix - A 'var' redeclared in a nested block keeps the first one,
1922. 1// so a use between the two does not warn temporal_dead_zone_a.
1923. 1
1924. 1 (`
1925. 1function aa() {
1926. 1 var bb = 0;
1927. 1 bb();
1928. 1 if (aa) {
1929. 1 var bb = 1; //jslint-ignore-line
1930. 1 bb();
1931. 1 }
1932. 1}
1933. 1aa();
1934. 1 `),
1935. 1 "function aa(bb = aa) {\n aa(bb);\n}\naa();",
1936. 1 (`
1937. 1function bb(cc) {
1938. 1 return cc;
1939. 1}
1940. 1bb(function aa() {
1941. 1 return;
1942. 1});
1943. 1 `),
1944. 1 (`
1945. 1if (String) {
1946. 1 let aa = 0;
1947. 1 aa();
1948. 1} else {
1949. 1 let aa = 0;
1950. 1 aa();
1951. 1}
1952. 1 `),
1953. 1 (`
1954. 1if (String) {
1955. 1 var aa = 0; //jslint-ignore-line
1956. 1}
1957. 1aa();
1958. 1 `)
1959. 1 ],
1960. 1 ternary: [
1961. 1 (`
1962. 1String(
1963. 1 String()
1964. 1 ? 0
1965. 1 : 1
1966. 1);
1967. 1String(
1968. 1 String()
1969. 1 ? \`$\{0}\`
1970. 1 : \`$\{1}\`
1971. 1);
1972. 1
1973. 1// PR-394 - Bugfix
1974. 1// Fix jslint falsely believing megastring literals \`0\` and \`1\` are similar.
1975. 1
1976. 1String(
1977. 1 String()
1978. 1 ? \`0\`
1979. 1 : \`1\`
1980. 1);
1981. 1
1982. 1// PR-510 - Bugfix - Fix jslint treating tagged templates as equal.
1983. 1
1984. 1String(
1985. 1 String()
1986. 1 ? String\`$\{0}$\{0}\`
1987. 1 : String\`$\{0}$\{1}\`
1988. 1);
1989. 1 `)
1990. 1 ],
1991. 1 try_catch_finally: [
1992. 1 (`
1993. 1try {
1994. 1 String();
1995. 1} catch (err) {
1996. 1 err();
1997. 1} finally {
1998. 1 String();
1999. 1}
2000. 1 `),
2001. 1 (`
2002. 1try {
2003. 1 String();
2004. 1} catch (err) {
2005. 1 var aa = err; //jslint-ignore-line
2006. 1} finally {
2007. 1 String(aa);
2008. 1}
2009. 1 `)
2010. 1 ],
2011. 1 use_strict: [
2012. 1 (`
2013. 1"use strict";
2014. 1function aa() {
2015. 1 "use strict";
2016. 1 return;
2017. 1}
2018. 1aa();
2019. 1 `)
2020. 1 ],
2021. 1 var: [
2022. 1 "const aa = 0;\naa();\n",
2023. 1 "let aa = 0;\naa();\n",
2024. 1 "var aa = 0;\naa();\n"
2025. 1 ]
2026. 31 }).forEach(function (codeList) {
2027. 31 let elemPrv = "";
2028. 97 codeList.flat().flat().forEach(function (source) {
2029. 97 let warnings;
2030. 97 source = source.trim();
2031. 97 // Assert codeList is sorted.
2032. 97 assertOrThrow(elemPrv < source, JSON.stringify([
2033. 97 elemPrv, source
2034. 97 ], undefined, 4));
2035. 97 elemPrv = source;
2036. 97 [
2037. 97 jslint.jslint,
2038. 97 jslintCjs.jslint
2039. 194 ].forEach(function (jslint) {
2040. 194 warnings = jslint(source, {
2041. 194 beta: true
2042. 194 }).warnings;
2043. 194 assertOrThrow(
2044. 194 warnings.length === 0,
2045. 194 `\n\n${source}\n\n${JSON.stringify(
2046. 194 warnings,
2047. 194 undefined,
2048. 194 4
2049. 194 )}`
2050. 194 );
2051. 194 });
2052. 97 });
2053. 31 });
2054. 1 });
2055. 1});
2056. 1
2057. 1jstestDescribe((
2058. 1 "test jslint's option handling-behavior"
2059. 1), function () {
2060. 1
2061. 1// Samples group by option, so each option is one test with a unique name.
2062. 1
2063. 1 const sampleDict = Object.groupBy([
2064. 1 [{bitwise: true}, "String(String | 0);"],
2065. 1 [{browser: true}, ";\nString(new XMLHttpRequest());"],
2066. 1 [{convert: true}, "String(\"aa\" + 0);"],
2067. 1 [{couch: true}, "registerType();"],
2068. 1 [{devel: true}, "debugger;"],
2069. 1
2070. 1// PR-404 - Alias "evil" to jslint-directive "eval" for backwards-compat.
2071. 1
2072. 1 [{eval: true, evil: true}, "new Function();\neval();"],
2073. 1
2074. 1// PR-514 - Bugfix - A string-key named get aa does not duplicate an accessor.
2075. 1
2076. 1 [{getset: true}, String(`
2077. 1String({
2078. 1 get aa() {
2079. 1 return;
2080. 1 },
2081. 1 "get aa": 0
2082. 1});
2083. 1 `).trim()],
2084. 1 [{getset: true}, "String({get aa() {\n return;\n}});"],
2085. 1 [{getset: true}, "String({set aa(aa) {\n return aa;\n}});"],
2086. 1 [{indent2: true}, sourceJslintMjs.replace((/ /g), " ")],
2087. 1 [{indent2: true}, "function aa() {\n return;\n}\naa();"],
2088. 1 [{long: true}, "/".repeat(100)],
2089. 1
2090. 1// PR-404 - Alias "nomen" to jslint-directive "name" for backwards-compat.
2091. 1
2092. 1 [{name: true, nomen: true}, "let aa = 0;\naa._();"],
2093. 1 [{node: true}, "require();"],
2094. 1 [{single: true}, "String('aa');"],
2095. 1
2096. 1// PR-404 - Add new directive "subscript" to play nice with Google Closure.
2097. 1
2098. 1 [{subscript: true}, "String[\"aa\"]();"],
2099. 1 [{test_internal_error: true}, ""],
2100. 1 [{test_unknown_warning_code: true}, ""],
2101. 1 [{this: true}, "String(this);"],
2102. 1 [{trace: true}, ""],
2103. 1 [{unordered: true}, (`
2104. 1function aa({bb, aa}) {
2105. 1 switch (aa) {
2106. 1 case 1:
2107. 1 break;
2108. 1 case 0:
2109. 1 break;
2110. 1 default:
2111. 1 return {bb, aa};
2112. 1 }
2113. 1}
2114. 1aa();
2115. 1 `)],
2116. 1 [{unordered: true}, "let {bb, aa} = 0;\naa(bb);"],
2117. 1 [
2118. 1 {variable: true},
2119. 1 (`
2120. 1function aa() {
2121. 1 if (aa) {
2122. 1 let bb = 0;
2123. 1 return bb;
2124. 1 }
2125. 1}
2126. 1 `)
2127. 1 ],
2128. 1 [{variable: true}, "let bb = 0;\nlet aa = 0;\naa(bb);"],
2129. 1 [{white: true}, "\t"]
2130. 25 ], function ([
2131. 25 option_dict
2132. 25 ]) {
2133. 25 return JSON.stringify(option_dict);
2134. 25 });
2135. 1 let elemPrv = "";
2136. 20 for (const [key, sampleList] of Object.entries(sampleDict)) {
2137. 20 jstestIt((
2138. 20 `test option=${key} handling-behavior`
2139. 20 ), function () {
2140. 25 for (const [option_dict, sample] of sampleList) {
2141. 25 const elemNow = JSON.stringify([option_dict, sample.trim()]);
2142. 25 const warningsLength = (
2143. 25 (
2144. 25 option_dict.test_internal_error ||
2145. 25 option_dict.test_unknown_warning_code
2146. 25 )
2147. 25 ? 1
2148. 25 : 0
2149. 25 );
2150. 25 let source = sample.trim();
2151. 25 // Assert list is sorted.
2152. 25 assertOrThrow(
2153. 25 elemPrv < elemNow,
2154. 25 JSON.stringify(
2155. 25 [elemPrv, elemNow],
2156. 25 undefined,
2157. 25 4
2158. 25 )
2159. 25 );
2160. 25 elemPrv = elemNow;
2161. 25 option_dict.beta = true;
2162. 50 for (const lint of [jslint.jslint, jslintCjs.jslint]) {
2163. 50 // test jslint's option handling-behavior
2164. 50 let warnings;
2165. 50 warnings = lint(
2166. 50 source,
2167. 50 option_dict
2168. 50 ).warnings;
2169. 50 assertOrThrow(
2170. 50 warnings.length === warningsLength,
2171. 50 JSON.stringify([source, warnings], undefined, 4)
2172. 50 );
2173. 50 // test jslint's directive handling-behavior
2174. 50 source = (
2175. 50 "/*jslint " +
2176. 50 JSON
2177. 50 .stringify(option_dict)
2178. 50 .slice(1, -1)
2179. 50 .replace((/"/g), "") +
2180. 50 "*/\n" +
2181. 50 source.replace((/^#!/), "//")
2182. 50 );
2183. 50 warnings = lint(source).warnings;
2184. 50 assertOrThrow(
2185. 50 warnings.length === warningsLength,
2186. 50 JSON.stringify([source, warnings], undefined, 4)
2187. 50 );
2188. 50 }
2189. 25 }
2190. 20 });
2191. 20 }
2192. 1});
2193. 1
2194. 1jstestDescribe((
2195. 1 "test jslint's report handling-behavior"
2196. 1), function () {
2197. 1 jstestIt((
2198. 1 "test report-autofix handling-behavior"
2199. 1 ), function () {
2200. 1 let result;
2201. 1
2202. 1// The report's Autofix section SPEAKS ONLY AFTER THE BUTTON - index.html sets
2203. 1// <autofix> on the button's own lint-result, so a plain JSLint renders an
2204. 1// empty, default-coloured body. Blocked - a residual warning outside
2205. 1// jslint_autofix_warning_list - is the ONLY state that goes red, and a fixable
2206. 1// residual still reads as success.
2207. 1
2208. 5 function reportAutofix(source, autofix) {
2209. 5 let html = jslint.jslint_report({
2210. 5 ...jslint.jslint(source, {}),
2211. 5 autofix
2212. 5 });
2213. 5 return html.slice(
2214. 5 html.indexOf("<fieldset\n class="),
2215. 5 html.indexOf("<fieldset id=\"JSLINT_REPORT_WARNINGS\"")
2216. 5 );
2217. 5 }
2218. 1
2219. 5 function reportAutofixExpect(klass, body) {
2220. 5 return (
2221. 5 "<fieldset\n class=\"\n " + klass + "\n \"\n" +
2222. 5 " id=\"JSLINT_REPORT_AUTOFIX\"\n>\n" +
2223. 5 "<legend>Report: Autofix</legend>\n" +
2224. 5 "<div class=\"center\">\n " + body + "\n</div>\n" +
2225. 5 "</fieldset>\n"
2226. 5 );
2227. 5 }
2228. 1
2229. 1// A clean source autofixes to itself, and the click is STILL a success - an
2230. 1// empty body would read to the clicker as a missing success message.
2231. 1
2232. 1 result = reportAutofix((
2233. 1 "function aa(bb) {\n return bb;\n}\naa();\n"
2234. 1 ), true);
2235. 1 assertOrThrow(
2236. 1 result === reportAutofixExpect("", "Autofix successful."),
2237. 1 result
2238. 1 );
2239. 1
2240. 1// A residual warning INSIDE the fixable set is not a blocker, so every
2241. 1// jslint_autofix_warning_list member must survive the <some> callback.
2242. 1
2243. 1 result = reportAutofix((
2244. 1 "function aa(bb) {\n return String( bb);\n}\naa();\n"
2245. 1 ), true);
2246. 1 assertOrThrow(
2247. 1 result === reportAutofixExpect("", "Autofix successful."),
2248. 1 result
2249. 1 );
2250. 1
2251. 1// A residual too_long is not a blocker either - autofix fixes around it.
2252. 1
2253. 1 result = reportAutofix(String(`
2254. 1function aa(bb) {
2255. 1 return bb;
2256. 1}
2257. 1aa("${"a".repeat(80)}");
2258. 1 `).trim() + "\n", true);
2259. 1 assertOrThrow(
2260. 1 result === reportAutofixExpect("", "Autofix successful."),
2261. 1 result
2262. 1 );
2263. 1
2264. 1// A warning outside the set blocks, and the class is what paints it red.
2265. 1
2266. 1 result = reportAutofix("console.log(1);\n", true);
2267. 1 assertOrThrow(
2268. 1 result === reportAutofixExpect("blocked", (
2269. 1 "Autofix blocked. Fix non-whitespace warnings below."
2270. 1 )),
2271. 1 result
2272. 1 );
2273. 1
2274. 1// A plain JSLint is ALWAYS empty and default-coloured, even on a source that
2275. 1// WOULD block - <autofix> is undefined, so <some> never runs.
2276. 1
2277. 1 result = reportAutofix("console.log(1);\n", undefined);
2278. 1 assertOrThrow(result === reportAutofixExpect("", ""), result);
2279. 1 });
2280. 1 jstestIt((
2281. 1 "test report-function-detail handling-behavior"
2282. 1 ), function () {
2283. 1 let result;
2284. 1
2285. 1// This function will return the function details in the report of <source>,
2286. 1// each <dl> on its own line.
2287. 1
2288. 2 function reportFunction(source) {
2289. 2 const html = jslint.jslint_report(jslint.jslint(source, {}));
2290. 2 return html.slice(
2291. 2 html.indexOf("<div class=\"level level1\">"),
2292. 2 html.lastIndexOf("</div>\n</fieldset>")
2293. 2 ).replace((/<dl>/g), "\n<dl>");
2294. 2 }
2295. 1
2296. 1// PR-516 - Bugfix - The report's function details list block-scoped names and
2297. 1// catch-variables, and mark closure only across a function boundary.
2298. 1
2299. 1 result = reportFunction(String(`
2300. 1let ee = 0;
2301. 1function aa(gg) {
2302. 1 let bb = 0;
2303. 1 let ff = 0;
2304. 1 if (aa) {
2305. 1 let cc = 1;
2306. 1 aa(bb, cc, ee, gg);
2307. 1 }
2308. 1 try {
2309. 1 aa(String);
2310. 1 } catch (err) {
2311. 1 aa(function () {
2312. 1 return err;
2313. 1 });
2314. 1 }
2315. 1 return function () {
2316. 1 return ff;
2317. 1 };
2318. 1}
2319. 1aa();
2320. 1 `).trim() + "\n");
2321. 1 assertOrThrow(result === String(`
2322. 1<div class="level level1"><address>2: 1</address><dfn>aa(gg)</dfn>
2323. 1<dl><dt>parameter</dt><dd>gg</dd></dl>
2324. 1<dl><dt>variable</dt><dd>bb, cc, ff</dd></dl>
2325. 1<dl><dt>exception</dt><dd>err</dd></dl>
2326. 1<dl><dt>closure</dt><dd>err, ff</dd></dl>
2327. 1<dl><dt>global</dt><dd>String, aa, ee</dd></dl></div>
2328. 1<div class="level level2"><address>12: 12</address><dfn>«aa»()</dfn>
2329. 1<dl><dt>outer</dt><dd>err</dd></dl></div>
2330. 1<div class="level level2"><address>16: 12</address><dfn>«anonymous»()</dfn>
2331. 1<dl><dt>outer</dt><dd>ff</dd></dl></div>
2332. 1 `).trim() + "\n", result);
2333. 1
2334. 1// PR-516 - Bugfix - A named function expression's name is outer only in a
2335. 1// nested function, and a function lists an outer 'bb' even beside its own
2336. 1// block-scoped 'bb'.
2337. 1
2338. 1 result = reportFunction(String(`
2339. 1function aa() {
2340. 1 let bb = 0;
2341. 1 function cc() {
2342. 1 if (cc) {
2343. 1 cc(bb);
2344. 1 }
2345. 1 if (cc) {
2346. 1 let bb = 1;
2347. 1 cc(bb);
2348. 1 }
2349. 1 }
2350. 1 return function dd() {
2351. 1 return dd(cc, function () {
2352. 1 return dd;
2353. 1 });
2354. 1 };
2355. 1}
2356. 1aa();
2357. 1 `).trim() + "\n");
2358. 1 assertOrThrow(result === String(`
2359. 1<div class="level level1"><address>1: 1</address><dfn>aa()</dfn>
2360. 1<dl><dt>variable</dt><dd>bb, cc</dd></dl>
2361. 1<dl><dt>closure</dt><dd>bb, cc</dd></dl></div>
2362. 1<div class="level level2"><address>3: 5</address><dfn>cc()</dfn>
2363. 1<dl><dt>variable</dt><dd>bb</dd></dl>
2364. 1<dl><dt>outer</dt><dd>bb, cc</dd></dl></div>
2365. 1<div class="level level2"><address>12: 12</address><dfn>dd()</dfn>
2366. 1<dl><dt>outer</dt><dd>cc</dd></dl></div>
2367. 1<div class="level level3"><address>13: 23</address><dfn>«cc»()</dfn>
2368. 1<dl><dt>outer</dt><dd>dd</dd></dl></div>
2369. 1 `).trim() + "\n", result);
2370. 1 });
2371. 1 jstestIt((
2372. 1 "test report-function-name handling-behavior"
2373. 1 ), function () {
2374. 1
2375. 1// PR-516 - Bugfix - A method or accessor is named after its property, not
2376. 1// after the identifier before it.
2377. 1
2378. 1 assertJsonEqual(jslint.jslint(String(`
2379. 1/*jslint getset*/
2380. 1String({
2381. 1 aa() {
2382. 1 return;
2383. 1 },
2384. 1 get bb() {
2385. 1 return;
2386. 1 },
2387. 1 cc: 0,
2388. 1 dd() {
2389. 1 return;
2390. 1 }
2391. 1});
2392. 3 `).trim() + "\n").functions.map(function ({name}) {
2393. 3 return name;
2394. 3 }), ["aa", "get bb", "dd"]);
2395. 1
2396. 1// PR-516 - Bugfix - A function after a keyword is anonymous, except after
2397. 1// 'default', the name the spec gives it.
2398. 1
2399. 1 assertJsonEqual(jslint.jslint(String(`
2400. 1String(function () {
2401. 1 return function () {
2402. 1 return;
2403. 1 };
2404. 1}, typeof function () {
2405. 1 return;
2406. 1});
2407. 1export default function () {
2408. 1 return;
2409. 1}
2410. 4 `).trim() + "\n").functions.map(function ({name}) {
2411. 4 return name;
2412. 4 }), ["String", "anonymous", "anonymous", "default"]);
2413. 1
2414. 1// PR-516 - Bugfix - A keyword used as a property name still names a function.
2415. 1
2416. 1 assertJsonEqual(jslint.jslint(String(`
2417. 1const aa = {
2418. 1 delete: function () {
2419. 1 return;
2420. 1 }
2421. 1};
2422. 1aa.catch = function () {
2423. 1 return;
2424. 1};
2425. 1String(aa?.finally || function () {
2426. 1 return;
2427. 1});
2428. 1String(aa
2429. 1? null
2430. 1: function () {
2431. 1 return;
2432. 1});
2433. 4 `).trim() + "\n").functions.map(function ({name}) {
2434. 4 return name;
2435. 4 }), ["delete", "catch", "finally", "anonymous"]);
2436. 1 });
2437. 1 jstestIt((
2438. 1 "test report-function-signature handling-behavior"
2439. 1 ), function () {
2440. 1 const a38 = "a".repeat(38);
2441. 1
2442. 1// PR-516 - Bugfix - A signature keeps default values and drops comments, and a
2443. 1// multi-line destructure joins with no space inside its brackets. A default
2444. 1// value longer than 40 characters, or holding a megastring, shows as an
2445. 1// ellipsis, but a backtick inside a string or regexp does not count.
2446. 1
2447. 1 assertJsonEqual(jslint.jslint(String(`
2448. 1function aa(bb, cc = 1, ...dd) {
2449. 1 return [bb, cc, dd];
2450. 1}
2451. 1function ee({
2452. 1 ff,
2453. 1 gg = "" // Comment.
2454. 1}, hh) {
2455. 1 return [ff, gg, hh];
2456. 1}
2457. 1function ii(jj = function () {
2458. 1 return "a default value longer than forty characters";
2459. 1}, kk = [0, 1], ll = \`\${kk}\`) {
2460. 1 return [jj, kk, ll];
2461. 1}
2462. 1function mm(
2463. 1 nn /* Comment. */,
2464. 1 oo = [
2465. 1 1
2466. 1 ],
2467. 1 pp = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
2468. 1 qq = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
2469. 1 rr = [\`a\`, 1]
2470. 1) {
2471. 1 return [nn, oo, pp, qq, rr];
2472. 1}
2473. 1function ss(tt = "\`", uu = /\`/) {
2474. 1 return [tt, uu];
2475. 1}
2476. 1function vv(ww = (xx = 1) => "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") {
2477. 1 return ww;
2478. 1}
2479. 8 `).trim() + "\n").functions.map(function ({signature}) {
2480. 8 return signature;
2481. 8 }), [
2482. 1 "(bb, cc = 1, ...dd)",
2483. 1 "({ff, gg = \"\"}, hh)",
2484. 1 "(jj = \u2026, kk = [0, 1], ll = \u2026)",
2485. 1 "()",
2486. 1 `(nn, oo = [1], pp = "${a38}", qq = \u2026, rr = \u2026)`,
2487. 1 "(tt = \"`\", uu = /`/)",
2488. 1 "(ww = \u2026)",
2489. 1 "(xx = 1)"
2490. 1 ]);
2491. 1
2492. 1// A parse that stops inside a parameter list leaves the signature '('.
2493. 1
2494. 1 assertJsonEqual(jslint.jslint(
2495. 1 "function aa(bb, 0) {}\n"
2496. 1 ).functions.map(function ({signature}) {
2497. 1 return signature;
2498. 1 }), ["("]);
2499. 1 });
2500. 1});
2501. 1
2502. 1jstestDescribe((
2503. 1 "test jslint's warnings handling-behavior"
2504. 1), function () {
2505. 1 jstestIt((
2506. 1 "test binding-power handling-behavior"
2507. 1 ), function () {
2508. 1
2509. 1// PR-514 - Bugfix - Binding-powers follow the spec's grammar, as tabled in
2510. 1// MDN Operator precedence. Each source returns the expression beside the
2511. 1// parameters, so none is unused, and lists the warning codes it must raise.
2512. 1
2513. 1 for (const [expression, expect] of [
2514. 1 ["(-aa) ** 2", []],
2515. 1 ["-aa ** 2", ["wrap_subexpression_a_b"]],
2516. 1 ["-aa++", ["unexpected_a"]],
2517. 1 ["[aa] ** 2", []],
2518. 1 ["aa ** -2", []],
2519. 1 ["aa ?? (bb || cc)", []],
2520. 1 ["aa ?? bb && cc", ["wrap_subexpression_a_b"]],
2521. 1 ["aa ?? bb ?? cc", []],
2522. 1 ["aa || bb ?? cc", ["wrap_subexpression_a_b"]],
2523. 1 ["typeof aa ** 2", ["wrap_subexpression_a_b"]]
2524. 10 ]) {
2525. 10 const result = jslint.jslint(String(`
2526. 10function ff(aa, bb, cc) {
2527. 10 return [aa, bb, cc, ${expression}];
2528. 10}
2529. 10ff();
2530. 10 `).trim() + "\n");
2531. 10 assertJsonEqual(result.warnings.map(function ({code}) {
2532. 10 return code;
2533. 10 }), expect, expression);
2534. 10 }
2535. 1
2536. 1// PR-514 - Bugfix - The operand of 'void' is parsed at rbp 150, like every
2537. 1// unary operator, so 'void 0 + 0' is '(void 0) + 0'.
2538. 1
2539. 1 assertOrThrow(
2540. 5 jslint.jslint("String(void 0 + 0);\n").tokens.find(function ({
2541. 5 id
2542. 5 }) {
2543. 5 return id === "+";
2544. 5 }).expression[0].id === "void",
2545. 1 "void 0 + 0"
2546. 1 );
2547. 1
2548. 1// PR-514 - Bugfix - A relational right side of a for-loop-head 'of' or 'in'
2549. 1// does not warn on the head's own 'of' or 'in'.
2550. 1
2551. 1 for (const [operator, expect] of [
2552. 1 ["in", ["expected_a_b", "expected_a"]],
2553. 1 ["of", ["expected_a"]]
2554. 2 ]) {
2555. 2 assertJsonEqual(jslint.jslint(String(`
2556. 2function ff(aa, bb) {
2557. 2 for (aa ${operator} bb < aa) {
2558. 2 bb();
2559. 2 }
2560. 2}
2561. 2ff();
2562. 3 `).trim() + "\n").warnings.map(function ({code}) {
2563. 3 return code;
2564. 3 }), expect, operator);
2565. 2 }
2566. 1
2567. 1// PR-514 - A 'for in' suggests 'for...of Object.keys', not 'Object.keys'.
2568. 1
2569. 1 assertJsonEqual(jslint.jslint(String(`
2570. 1function ff(aa) {
2571. 1 for (const bb in aa) {
2572. 1 aa(bb);
2573. 1 }
2574. 1}
2575. 1ff();
2576. 1 `).trim() + "\n").warnings.map(function ({message}) {
2577. 1 return message;
2578. 1 }), ["Expected 'for...of Object.keys' and instead saw 'for in'."]);
2579. 1
2580. 1// PR-514 - Bugfix - '**=' is one assignment token, '**' takes a space on each
2581. 1// side like '*', and a line break before a postfix '++' ends the expression.
2582. 1
2583. 1 for (const [source, expect] of [
2584. 1 ["let aa = 2;\naa **= 2;\n", []],
2585. 1 [
2586. 1 "let aa = 2;\naa = aa**2;\n",
2587. 1 ["expected_space_a_b", "expected_space_a_b"]
2588. 1 ],
2589. 1 [
2590. 1 "let aa = 0;\nlet bb = 0;\naa\n++bb;\n",
2591. 1 [
2592. 1 "unexpected_expression_a",
2593. 1 "expected_a_after_b",
2594. 1 "unexpected_expression_a"
2595. 1 ]
2596. 1 ]
2597. 3 ]) {
2598. 5 assertJsonEqual(jslint.jslint(source).warnings.map(function ({
2599. 5 code
2600. 5 }) {
2601. 5 return code;
2602. 5 }), expect, source);
2603. 3 }
2604. 1 assertJsonEqual(jslint.jslint("let aa = 2;\naa = aa**2;\n", {
2605. 1 autofix: true
2606. 1 }).autofixed, "let aa = 2;\naa = aa ** 2;\n");
2607. 1 });
2608. 1 jstestIt((
2609. 1 "test warning-test-cause handling-behavior"
2610. 1 ), async function () {
2611. 1
2612. 1// This function will validate each fixture in each '// test_cause:' block of
2613. 1// jslint.mjs. A "(live)" source must run the code below its block, by V8 block
2614. 1// coverage. A warning must be shown, with the code below its block on the
2615. 1// stack that option trace records. Then validate the roster of fixtures filed
2616. 1// up the stack, and that each raise site in jslint.mjs has a fixture.
2617. 1
2618. 1 const controlList = [];
2619. 1 const fixtureLineList = [];
2620. 1 const fixtureList = [];
2621. 1 const raiserList = ["stop", "stop_at", "warn", "warn_at"];
2622. 1 const rgxFixtureLine = (
2623. 1 /^\/\/ (?!\/\/).*, \d+\](?: \/\/jslint-ignore-line)?$/gm
2624. 1 );
2625. 1 const rgxFrame = (
2626. 1 /^ {4}at (?:(?:Object\.)?([\w.]+).*? \()?.*?jslint\.mjs:(\d+):/gm
2627. 1 );
2628. 1 const stackTraceLimit = Error.stackTraceLimit;
2629. 765 function fixturePush(list, cause, anchor) {
2630. 765
2631. 765// This function will push to <list> the fixture <cause>, whose block sits
2632. 765// directly above the code at offset <anchor> in jslint.mjs.
2633. 765
2634. 765 const line = (
2635. 765 sourceJslintMjs.slice(0, anchor).match(/\n/g).length + 1
2636. 765 );
2637. 765 const [source, fn, code, aa, column] = cause;
2638. 765 const warnings = jslintTrace(source);
2639. 765
2640. 765// List each warning with its frames in jslint.mjs, by the stack that option
2641. 765// trace records, as [name, line] innermost first, and the frames above the
2642. 765// raisers, whose first is the line that raised it. An anonymous function's
2643. 765// frame has no name.
2644. 765
2645. 1918 const warningsRaised = warnings.map(function (warning) {
2646. 1918 const frameList = Array.from(
2647. 1918 String(warning.stack_trace).matchAll(rgxFrame),
2648. 20226 function ([ignore, name, line]) {
2649. 20226 return [name, Number(line)];
2650. 20226 }
2651. 1918 );
2652. 1918 return {
2653. 20226 aboveList: frameList.filter(function ([name]) {
2654. 20226 return !raiserList.includes(name);
2655. 20226 }),
2656. 1918 frameList,
2657. 1918 warning
2658. 1918 };
2659. 1918 });
2660. 765
2661. 765// A second warning on one token is dropped, so check the warnings the user
2662. 765// sees, never the ones raised.
2663. 765
2664. 1918 const warningsShown = warningsRaised.filter(function ({warning}) {
2665. 1918 return (
2666. 614 warning.code === code &&
2667. 614 String(warning.b || warning.a || "") === aa &&
2668. 592 warning.column === column
2669. 1918 );
2670. 1918 });
2671. 765
2672. 765// The shown warning raised through the code under the block.
2673. 765
2674. 584 const warningsVia = warningsShown.filter(function ({frameList}) {
2675. 1872 return frameList.some(function ([ignore, at]) {
2676. 1872 return at === line;
2677. 1872 });
2678. 584 });
2679. 765 list.push({
2680. 765 anchor,
2681. 765
2682. 765// Name the line of the code under the block, so a failure points at it.
2683. 765
2684. 765 at: "jslint.mjs:" + line,
2685. 765 cause: JSON.stringify(cause),
2686. 765 code,
2687. 765
2688. 765// Validate no internal-error. A crash mid-walk still runs every branch before
2689. 765// it, so a coverage-check alone passes straight through one, and "0``" threw
2690. 765// in post_b_binary for years with its cause declared and green.
2691. 765
2692. 1916 codeless: warnings.some(function (warning) {
2693. 1916 return warning.code === undefined;
2694. 1916 }),
2695. 765 fn,
2696. 765
2697. 765// A fixture has exactly 5 fields. Field 3 "(live)" marks a branch reached with
2698. 765// no warning of its own, documenting how, so it is not taken for deadcode. Any
2699. 765// other field 3 must be a warning shown below, so a typo fails there.
2700. 765
2701. 764 known: cause.length === 5 && (
2702. 764 code !== "(live)" || (aa === "" && column === 0)
2703. 764 ),
2704. 765
2705. 765// Field 2 of a warning names the function that raised it, the first named one
2706. 765// above the raisers in the stack that option trace records, so a fixture may
2707. 765// be filed up the stack, above the call that leads down to the raise site.
2708. 765
2709. 576 raise: warningsVia[0]?.aboveList[0],
2710. 616 raiser: warningsVia[0]?.aboveList.find(function ([name]) {
2711. 616 return name;
2712. 616 })?.[0],
2713. 585 shown: code === "(live)" || warningsShown.length > 0,
2714. 765
2715. 765// A "(live)" block must not sit on a raise whose warning shows, since a
2716. 765// warning fixture checks more.
2717. 765
2718. 393 silent: code !== "(live)" || warningsRaised.every(function ({
2719. 393 aboveList
2720. 393 }) {
2721. 392 return aboveList[0]?.[1] !== line;
2722. 393 }),
2723. 765 source,
2724. 585 via: code === "(live)" || warningsVia.length > 0
2725. 765 });
2726. 765 }
2727. 765 function jslintTrace(source) {
2728. 765
2729. 765// This function will lint <source> with option trace, keeping every frame,
2730. 765// since a fixture filed up the stack can sit past V8's default of 10. Restore
2731. 765// the limit even if jslint throws, so no other test inherits it.
2732. 765
2733. 765 Error.stackTraceLimit = Infinity;
2734. 765 try {
2735. 765 return jslint.jslint(source, {
2736. 765 beta: true,
2737. 765 trace: true
2738. 765 }).warnings;
2739. 765 } finally {
2740. 765 Error.stackTraceLimit = stackTraceLimit;
2741. 765 }
2742. 765 }
2743. 1
2744. 1 sourceJslintMjs.replace((
2745. 1 /(\n\s*?\/\/\s*?test_cause:\s*?)(\S[\S\s]*?\S)(\n\n\s*?) *?\S/g
2746. 413 ), function (match0, header, causeList, footer, offset) {
2747. 413 const rgxAnchor = (/(?:\/\/.*\n\s*)*(?:[?:]\s*)?/y);
2748. 413 let tmp;
2749. 413 // console.error(match0);
2750. 413 // Validate header.
2751. 413 assertOrThrow(header === "\n\n// test_cause:\n", match0);
2752. 413 // Validate footer.
2753. 413 assertOrThrow(footer === "\n\n", match0);
2754. 413 fixtureLineList.push(...match0.match(rgxFixtureLine));
2755. 413 // Validate causeList.
2756. 413 causeList = causeList.replace((
2757. 413 /^\/\/ /gm
2758. 413 ), "").replace((
2759. 413 /^\["\n([\S\s]*?)\n"(,.*?)$/gm
2760. 74 ), function (ignore, source, param) {
2761. 74 source = "[" + JSON.stringify(source) + param;
2762. 74 return source;
2763. 74 }).replace((
2764. 413 / \/\/jslint-ignore-line$/gm
2765. 413 ), "");
2766. 750 tmp = causeList.split("\n").map(function (cause) {
2767. 750 return (
2768. 750 "[" +
2769. 3750 JSON.parse(cause).map(function (elem) {
2770. 3750 return JSON.stringify(elem);
2771. 3750 }).join(", ") +
2772. 750 "]"
2773. 750 );
2774. 750 }).sort().filter(function (cause, ii, list) {
2775. 750 return cause !== list[ii - 1];
2776. 750 }).join("\n");
2777. 413 assertOrThrow(
2778. 413 causeList === tmp,
2779. 413 "\n" + causeList + "\n\n" + tmp
2780. 413 );
2781. 413
2782. 413// Anchor the block to the first code below it, past any comment paragraph and
2783. 413// a leading '?' or ':' of a ternary branch.
2784. 413
2785. 413 rgxAnchor.lastIndex = offset + match0.length - 1;
2786. 413 rgxAnchor.exec(sourceJslintMjs);
2787. 750 for (const cause of causeList.split("\n")) {
2788. 750 fixturePush(
2789. 750 fixtureList,
2790. 750 JSON.parse(cause),
2791. 750 rgxAnchor.lastIndex
2792. 750 );
2793. 750 }
2794. 413 return "";
2795. 413 });
2796. 1
2797. 1// Validate each fixture's last line was parsed in a block, so a misspelled
2798. 1// header fails, naming the lines it hid, instead of hiding its block. The
2799. 1// count catches a line that two blocks share. Commented-out code keeps its
2800. 1// blocks as '// // test_cause:', unchecked.
2801. 1
2802. 1 assertJsonEqual(sourceJslintMjs.match(rgxFixtureLine).filter(
2803. 750 function (line) {
2804. 750 return !fixtureLineList.includes(line);
2805. 750 }
2806. 1 ), []);
2807. 1 assertJsonEqual(
2808. 1 sourceJslintMjs.match(rgxFixtureLine).length,
2809. 1 fixtureLineList.length
2810. 1 );
2811. 1
2812. 1// Negative controls: each bad fixture, filed in the block of a good one, must
2813. 1// fail exactly the named check, so a weakened checker turns this test red.
2814. 1
2815. 1 for (const [good, bad, expect] of [
2816. 1 [
2817. 1 [
2818. 1 "/*jslint test_internal_error*/\nif(0%0){}",
2819. 1 "condition",
2820. 1 "(live)",
2821. 1 "",
2822. 1 0
2823. 1 ],
2824. 1 ["codeless", "silent"]
2825. 1 ], [
2826. 1 ["if(0%0){}", "condition", "unexpectd_a", "%", 5],
2827. 1 ["inFn", "shown", "via"]
2828. 1 ], [
2829. 1 ["if(0%0){}", "condition", "(live)", "", 0],
2830. 1 ["silent"]
2831. 1 ], [
2832. 1 ["if(0%0){}", "condition", "(live)", "aa", 0],
2833. 1 ["known", "silent"]
2834. 1 ], [
2835. 1 ["if(0%0){}", "condition", "(live)", "", 5],
2836. 1 ["known", "silent"]
2837. 1 ], [
2838. 1 ["if(0%0){}", "condition", "unexpected_a", "%", 5, "extra"],
2839. 1 ["known"]
2840. 1 ], [
2841. 1 ["if(0%0){}", "condition", "unexpected_a", "%", 6],
2842. 1 ["inFn", "shown", "via"]
2843. 1 ], [
2844. 1 ["if(0%0){}", "condition", "unexpected_a", "&", 5],
2845. 1 ["inFn", "shown", "via"]
2846. 1 ], [
2847. 1 ["if(0%0){}", "condition", "empty_block", "{", 8],
2848. 1 ["inFn", "via"]
2849. 1 ], [
2850. 1 ["if(0%0){}", "warn", "unexpected_a", "%", 5],
2851. 1 ["inFn"]
2852. 1 ], [
2853. 1 ["if(0&0){}", "condition", "unexpected_a", "&", 5],
2854. 1 ["inFn", "live", "via"]
2855. 1 ]
2856. 11 ].map(function ([bad, expect]) {
2857. 11 return [
2858. 11 "[\"if(0%0){}\",\"condition\",\"unexpected_a\",\"%\",5]",
2859. 11 bad,
2860. 11 expect
2861. 11 ];
2862. 11 }).concat([
2863. 1 [
2864. 1 "[\"let aa=!!0;\",\"post_u_unary\",\"expected_a_b\",\"!!\",8]",
2865. 1 ["!!0", "post_u_unary", "expected_a_b", "!!", 1],
2866. 1 ["inFn", "shown", "via"]
2867. 1 ], [
2868. 1 "[\"aa=/./vu\",\"lex_regexp\",\"unexpected_a\",\"u\",8]",
2869. 1 ["aa=/./u;if(0){}", "lex_regexp", "unexpected_a", "0", 12],
2870. 1 ["inFn", "live", "via"]
2871. 1 ], [
2872. 1
2873. 1// Filed up the stack above the wrong caller: <check_left> raises it through
2874. 1// <infix_lbracket>, not through the call under this block in <infix_dot>.
2875. 1
2876. 1 "[\"aa=[]?.aa\",\"check_left\",\"unexpected_a\",\"?.\",6]",
2877. 1 ["\"\".aa;0[0]", "check_left", "unexpected_a", "[", 8],
2878. 1 ["inFn", "via"]
2879. 1 ], [
2880. 1
2881. 1// Raised here but dropped, since <post_a_pluseq> warned on the token first,
2882. 1// so the warning shown is another site's.
2883. 1
2884. 1 (
2885. 1 "[\"aa-=undefined\",\"post_a_assignment\"," +
2886. 1 "\"unexpected_a\",\"undefined\",5]"
2887. 1 ),
2888. 1 [
2889. 1 "aa+=undefined",
2890. 1 "post_a_assignment",
2891. 1 "unexpected_a",
2892. 1 "undefined",
2893. 1 5
2894. 1 ],
2895. 1 ["inFn", "via"]
2896. 1 ]
2897. 15 ])) {
2898. 4053 const found = fixtureList.find(function ({cause}) {
2899. 4053 return cause === good;
2900. 4053 });
2901. 15 assertOrThrow(found, `control's good fixture is missing: ${good}`);
2902. 15 fixturePush(controlList, bad, found.anchor);
2903. 15 controlList.at(-1).expect = expect;
2904. 15 }
2905. 1
2906. 1// Measure each fixture's coverage in a child, since taking precise coverage
2907. 1// resets the counters that NODE_V8_COVERAGE dumps at exit.
2908. 1
2909. 1 await childRun({
2910. 1 NODE_V8_COVERAGE: ""
2911. 1 }, String(`
2912. 1import moduleFs from "fs";
2913. 1import moduleInspector from "inspector/promises";
2914. 1const fixtureList = JSON.parse(moduleFs.readFileSync(0, "utf8"));
2915. 1const resultList = [];
2916. 1const session = new moduleInspector.Session();
2917. 1session.connect();
2918. 1await session.post("Profiler.enable");
2919. 1await session.post("Profiler.startPreciseCoverage", {
2920. 1 callCount: true,
2921. 1 detailed: true
2922. 1});
2923. 1for (const [offset, source] of fixtureList) {
2924. 1 const result = [0, ""];
2925. 1 const span = [Infinity, Infinity];
2926. 1 jslint(source, {
2927. 1 beta: true
2928. 1 });
2929. 1 for (const {functionName, ranges} of (
2930. 1 await session.post("Profiler.takePreciseCoverage")
2931. 1 ).result.find(function ({url}) {
2932. 1 return url.endsWith("/jslint.mjs");
2933. 1 }).functions) {
2934. 1 for (const range of ranges) {
2935. 1 const width = range.endOffset - range.startOffset;
2936. 1 if (range.startOffset <= offset && offset < range.endOffset) {
2937. 1 if (width <= span[0]) {
2938. 1 result[0] = range.count;
2939. 1 span[0] = width;
2940. 1 }
2941. 1 if (functionName && width < span[1]) {
2942. 1 result[1] = functionName;
2943. 1 span[1] = width;
2944. 1 }
2945. 1 }
2946. 1 }
2947. 1 }
2948. 1 resultList.push(result);
2949. 1}
2950. 1process.stdout.write(JSON.stringify(resultList));
2951. 1 `).trim() + "\n", JSON.stringify(
2952. 765 fixtureList.concat(controlList).map(function ({anchor, source}) {
2953. 765 return [anchor, source];
2954. 765 })
2955. 1 )).then(function ([exitCode, stderr, stdout]) {
2956. 1 const hostDict = {};
2957. 1 const raiseDict = {};
2958. 1 let recordList;
2959. 1 assertOrThrow(exitCode === 0, stderr);
2960. 765 recordList = JSON.parse(stdout).map(function ([count, name], ii) {
2961. 765 const {
2962. 765 at,
2963. 765 cause,
2964. 765 code,
2965. 765 codeless,
2966. 765 expect = [],
2967. 765 fn,
2968. 765 known,
2969. 765 raise,
2970. 765 raiser,
2971. 765 shown,
2972. 765 silent,
2973. 765 via
2974. 765 } = fixtureList.concat(controlList)[ii];
2975. 765 let check;
2976. 750 if (ii < fixtureList.length) {
2977. 750 raiseDict[raise?.[1]] = true;
2978. 750
2979. 750// A fixture filed up the stack pairs the function holding its block with the
2980. 750// one that raised it.
2981. 750
2982. 750 if (name !== fn) {
2983. 750 hostDict[name + " <- " + fn] = true;
2984. 750 }
2985. 750 }
2986. 765 check = {
2987. 765 codeless: !codeless,
2988. 765
2989. 765// Field 2 of a (live) fixture names the function holding the block.
2990. 765
2991. 765 inFn: fn === (
2992. 765 code === "(live)"
2993. 180 ? name
2994. 585 : raiser
2995. 765 ),
2996. 765 known,
2997. 765 live: count > 0,
2998. 765 shown,
2999. 765 silent,
3000. 765 via
3001. 765 };
3002. 765 return {
3003. 765 at,
3004. 765 cause,
3005. 765 expect,
3006. 5355 fail: Object.keys(check).filter(function (key) {
3007. 5355 return !check[key];
3008. 5355 })
3009. 765 };
3010. 765 });
3011. 1
3012. 1// List every fixture whose failed checks differ from those expected: none for
3013. 1// a fixture, exactly the named ones for a negative control.
3014. 1
3015. 765 assertJsonEqual(recordList.filter(function ({expect, fail}) {
3016. 765 return String(fail) !== String(expect);
3017. 765 }), []);
3018. 1
3019. 1// Validate the roster of fixtures filed up the stack, as "host <- raiser"
3020. 1// pairs, so each new pair is a reviewed decision and a filing far up the stack
3021. 1// fails, which <via> alone accepts from any frame. A rename of either function
3022. 1// shows as one pair missing and one extra. No counts: a fixture added or moved
3023. 1// within a known pair is not a review.
3024. 1
3025. 1 assertJsonEqual(Object.keys(hostDict).sort(), [
3026. 1 "infix_dot <- check_left",
3027. 1 "infix_grave <- check_left",
3028. 1 "infix_lbracket <- check_left",
3029. 1 "infix_lparen <- check_left",
3030. 1 "jslint_phase3_parse <- check_ordered",
3031. 1 "post_s_var <- name_lookup",
3032. 1 "prefix_destructure <- check_ordered",
3033. 1 "prefix_destructure <- name_parse",
3034. 1 "prefix_function <- check_ordered",
3035. 1 "prefix_lbrace <- check_ordered",
3036. 1 "stmt_export <- check_ordered",
3037. 1 "stmt_export <- semicolon",
3038. 1 "stmt_switch <- check_ordered_case",
3039. 1 "whitage_default <- one_space_only"
3040. 1 ]);
3041. 1
3042. 1// Validate each raise site in jslint.mjs raises the warning of some fixture,
3043. 1// except the raisers' own calls, and a test hook raising an unknown code.
3044. 1
3045. 1 assertJsonEqual(sourceJslintMjs.replace((
3046. 1 /^ {4}function (?:stop|warn)(?:_at)?\([\S\s]*?\n {4}\}\n/gm
3047. 4 ), function (match0) {
3048. 4 return match0.replace(/[^\n]/g, "");
3049. 13977 }).split("\n").map(function (text, ii) {
3050. 13977 return [ii + 1, text.trim()];
3051. 13977 }).filter(function ([line, text]) {
3052. 13977 return (
3053. 11224 !text.startsWith("//") &&
3054. 11224 (/(?:^|[^\w.])(?:stop|warn)(?:_at)?\(/m).test(text) &&
3055. 292 !text.includes("\"test_unknown_warning_code\"") &&
3056. 291 !raiseDict[line]
3057. 13977 );
3058. 13977 }), []);
3059. 1 });
3060. 1 });
3061. 1});
3062. 1
3063. 1jstestDescribe((
3064. 1 "test jslint_wrapper_vscode handling-behavior"
3065. 1), function () {
3066. 1 jstestIt((
3067. 1 "test jslint_wrapper_vscode commands handling-behavior"
3068. 1 ), async function () {
3069. 1
3070. 1// Load the extension against a stub vscode api, then drive every command.
3071. 1// Like vscode, the stub's <registerTextEditorCommand> runs its callback inside
3072. 1// <edit>, and <edit> applies the builder's edits once the callback returns.
3073. 1
3074. 1 const commandDict = {};
3075. 1 const moduleStub = {
3076. 1 exports: {}
3077. 1 };
3078. 1 const sourceWrapper = await moduleFs.promises.readFile(
3079. 1 "jslint_wrapper_vscode.js",
3080. 1 "utf8"
3081. 1 );
3082. 1 const state = {};
3083. 1 const subscriptions = [];
3084. 1 const vscode = {
3085. 2 Diagnostic: function (ignore, message) {
3086. 2 return {
3087. 2 message
3088. 2 };
3089. 2 },
3090. 1 DiagnosticSeverity: {},
3091. 1 ProgressLocation: {},
3092. 1 Range: noop,
3093. 1 commands: {
3094. 4 registerCommand: function (id, callback) {
3095. 4 commandDict[id] = callback;
3096. 4 return [id, "registerCommand"];
3097. 4 },
3098. 2 registerTextEditorCommand: function (id, callback) {
3099. 4 commandDict[id] = function () {
3100. 4 return state.editor.edit(function (edit) {
3101. 4 callback(state.editor, edit);
3102. 4 });
3103. 4 };
3104. 2 return [id, "registerTextEditorCommand"];
3105. 2 }
3106. 1 },
3107. 1 languages: {
3108. 1 createDiagnosticCollection: function () {
3109. 1 return {
3110. 4 clear: function () {
3111. 4 delete state.diagnosticList;
3112. 4 },
3113. 3 set: function (ignore, list) {
3114. 2 state.diagnosticList = list.map(function ({
3115. 2 message
3116. 2 }) {
3117. 2 return message;
3118. 2 });
3119. 3 }
3120. 1 };
3121. 1 }
3122. 1 },
3123. 1 window: {
3124. 4 withProgress: function (ignore, callback) {
3125. 4 return callback({
3126. 4 report: noop
3127. 4 });
3128. 4 }
3129. 1 }
3130. 1 };
3131. 7 function editorCreate(text, selection) {
3132. 7 state.source = text;
3133. 7 state.editor = {
3134. 7 document: {
3135. 7 getText: function (range) {
3136. 7 return (
3137. 7 range
3138. 3 ? state.source.slice(
3139. 3 offsetAt(range.start),
3140. 3 offsetAt(range.end)
3141. 3 )
3142. 4 : state.source
3143. 7 );
3144. 7 },
3145. 4 lineAt: function ({
3146. 4 line
3147. 4 }) {
3148. 4 const lineList = state.source.split("\n");
3149. 4 return {
3150. 4 range: {
3151. 4 end: {
3152. 4 character: lineList[line].length,
3153. 4 line
3154. 4 },
3155. 4 start: {
3156. 4 character: 0,
3157. 4 line
3158. 4 }
3159. 4 },
3160. 4 rangeIncludingLineBreak: {
3161. 4 end: (
3162. 4 line + 1 < lineList.length
3163. 3 ? {
3164. 3 character: 0,
3165. 3 line: line + 1
3166. 3 }
3167. 1 : {
3168. 1 character: lineList[line].length,
3169. 1 line
3170. 1 }
3171. 4 ),
3172. 4 start: {
3173. 4 character: 0,
3174. 4 line
3175. 4 }
3176. 4 }
3177. 4 };
3178. 4 },
3179. 1 save: function () {
3180. 1
3181. 1// Stand in for a format-on-save that rewrites the text.
3182. 1
3183. 1 state.source = "let cc = 3;\n";
3184. 1 return Promise.resolve(true);
3185. 1 },
3186. 7 validateRange: noop
3187. 7 },
3188. 5 edit: function (callback) {
3189. 5 const editList = [];
3190. 5 callback({
3191. 4 insert: function (position, text) {
3192. 4 editList.push([position, position, text]);
3193. 4 },
3194. 4 replace: function (range, text) {
3195. 4
3196. 4// An undefined range, from the stub <validateRange>, is the whole document.
3197. 4
3198. 4 editList.push([range?.start, range?.end, text]);
3199. 4 }
3200. 5 });
3201. 5
3202. 5// Resolve offsets against the text before any edit, then apply right to left.
3203. 5// On a tie the later edit goes first, so an insert lands before a replace.
3204. 5
3205. 8 editList.map(function ([start, end, text], ii) {
3206. 8 return [
3207. 8 (
3208. 8 start
3209. 7 ? offsetAt(start)
3210. 1 : 0
3211. 8 ),
3212. 8 (
3213. 8 end
3214. 7 ? offsetAt(end)
3215. 1 : state.source.length
3216. 8 ),
3217. 8 text,
3218. 8 ii
3219. 8 ];
3220. 3 }).sort(function (aa, bb) {
3221. 2 return bb[0] - aa[0] || bb[3] - aa[3];
3222. 8 }).forEach(function ([start, end, text]) {
3223. 8 state.source = (
3224. 8 state.source.slice(0, start) +
3225. 8 text +
3226. 8 state.source.slice(end)
3227. 8 );
3228. 8 });
3229. 5 return Promise.resolve(true);
3230. 5 },
3231. 7 selection
3232. 7 };
3233. 7 vscode.window.activeTextEditor = state.editor;
3234. 7 }
3235. 20 function offsetAt({
3236. 20 character,
3237. 20 line
3238. 20 }) {
3239. 14 return state.source.split("\n").slice(0, line).reduce(function (
3240. 14 sum,
3241. 14 text
3242. 14 ) {
3243. 14 return sum + text.length + 1;
3244. 14 }, character);
3245. 20 }
3246. 1 moduleVm.runInThisContext(String(`
3247. 1(function (__dirname, exports, module, require) {
3248. 1${sourceWrapper}
3249. 1})
3250. 1 `).trim() + "\n")(
3251. 1 modulePath.resolve("."),
3252. 1 moduleStub.exports,
3253. 1 moduleStub,
3254. 3 function (id) {
3255. 3 return (
3256. 3 id === "vscode"
3257. 1 ? vscode
3258. 2 : id === "vm"
3259. 2 ? moduleVm
3260. 2 : moduleFs
3261. 3 );
3262. 3 }
3263. 1 );
3264. 1
3265. 1// Activation reuses moduleStub to load jslint, so read activate first.
3266. 1
3267. 1 moduleStub.exports.activate({
3268. 1 subscriptions
3269. 1 });
3270. 1 assertJsonEqual(subscriptions, [
3271. 1 ["jslint.autofix", "registerCommand"],
3272. 1 ["jslint.clear", "registerCommand"],
3273. 1 ["jslint.disableRegion", "registerTextEditorCommand"],
3274. 1 ["jslint.ignoreLine", "registerTextEditorCommand"],
3275. 1 ["jslint.lint", "registerCommand"],
3276. 1 ["jslint.lintAndSave", "registerCommand"]
3277. 1 ]);
3278. 1
3279. 1// With no active editor, clear still clears and the rest return quietly.
3280. 1
3281. 1 state.diagnosticList = ["aa"];
3282. 1 await commandDict["jslint.clear"]();
3283. 1 assertJsonEqual(state.diagnosticList, undefined);
3284. 1 await commandDict["jslint.autofix"]();
3285. 1 await commandDict["jslint.lint"]();
3286. 1 await commandDict["jslint.lintAndSave"]();
3287. 1 assertJsonEqual(state.diagnosticList, undefined);
3288. 1
3289. 1// Lint ignores the context menu's uri argument, and resolves only after the
3290. 1// warnings are set.
3291. 1
3292. 1 editorCreate("let aa = 1;\n");
3293. 1 await commandDict["jslint.lint"]("file:///aa.js");
3294. 1 assertJsonEqual(state.diagnosticList, ["JSLint - Unused 'aa'."]);
3295. 1
3296. 1// Lint-and-save lints the text as saved.
3297. 1
3298. 1 editorCreate("let bb = 2;\n");
3299. 1 await commandDict["jslint.lintAndSave"]();
3300. 1 assertJsonEqual(state.diagnosticList, ["JSLint - Unused 'cc'."]);
3301. 1
3302. 1// Autofix rewrites the text, then re-lints it.
3303. 1
3304. 1 editorCreate("function aa() {\n return 1;\n}\naa();\n");
3305. 1 await commandDict["jslint.autofix"]();
3306. 1 assertJsonEqual(
3307. 1 state.source,
3308. 1 "function aa() {\n return 1;\n}\naa();\n"
3309. 1 );
3310. 1 assertJsonEqual(state.diagnosticList, []);
3311. 1
3312. 1// Disable-region, with a cursor: wrap the cursor's line.
3313. 1
3314. 1 editorCreate("aa();\nbb();\n", {
3315. 1 end: {
3316. 1 character: 2,
3317. 1 line: 0
3318. 1 },
3319. 1 isEmpty: true,
3320. 1 start: {
3321. 1 character: 2,
3322. 1 line: 0
3323. 1 }
3324. 1 });
3325. 1 await commandDict["jslint.disableRegion"]();
3326. 1 assertJsonEqual(
3327. 1 state.source,
3328. 1 "/*jslint-disable*/\naa();\n/*jslint-enable*/\nbb();\n"
3329. 1 );
3330. 1
3331. 1// Disable-region, with a selection ending at the start of a line: the enable
3332. 1// directive goes before that line, not after it.
3333. 1
3334. 1 editorCreate("aa();\nbb();\ncc();\n", {
3335. 1 end: {
3336. 1 character: 0,
3337. 1 line: 2
3338. 1 },
3339. 1 isEmpty: false,
3340. 1 start: {
3341. 1 character: 0,
3342. 1 line: 0
3343. 1 }
3344. 1 });
3345. 1 await commandDict["jslint.disableRegion"]();
3346. 1 assertJsonEqual(
3347. 1 state.source,
3348. 1 "/*jslint-disable*/\naa();\nbb();\n/*jslint-enable*/\ncc();\n"
3349. 1 );
3350. 1
3351. 1// Disable-region, on a last line with no line break: add one first.
3352. 1
3353. 1 editorCreate("aa();", {
3354. 1 end: {
3355. 1 character: 0,
3356. 1 line: 0
3357. 1 },
3358. 1 isEmpty: true,
3359. 1 start: {
3360. 1 character: 0,
3361. 1 line: 0
3362. 1 }
3363. 1 });
3364. 1 await commandDict["jslint.disableRegion"]();
3365. 1 assertJsonEqual(
3366. 1 state.source,
3367. 1 "/*jslint-disable*/\naa();\n/*jslint-enable*/"
3368. 1 );
3369. 1
3370. 1// Ignore-line: append the directive to the end of the selection's last line.
3371. 1
3372. 1 editorCreate("aa();\nbb();\n", {
3373. 1 end: {
3374. 1 character: 2,
3375. 1 line: 1
3376. 1 },
3377. 1 isEmpty: true,
3378. 1 start: {
3379. 1 character: 2,
3380. 1 line: 1
3381. 1 }
3382. 1 });
3383. 1 await commandDict["jslint.ignoreLine"]();
3384. 1 assertJsonEqual(state.source, "aa();\nbb(); //jslint-ignore-line\n");
3385. 1 });
3386. 1});
3387. 1
3388. 1jstestDescribe((
3389. 1 "test jstestXxx handling-behavior"
3390. 1), function () {
3391. 1 jstestIt((
3392. 1 "test jstestDescribe-body-hang handling-behavior"
3393. 1 ), async function () {
3394. 1
3395. 1// An async body that hangs before registering any test fails at exit as a
3396. 1// pending body, instead of passing with 0 tests. With nothing alive, the hang
3397. 1// exits at once.
3398. 1
3399. 1 const [exitCode, stderr] = await childRun({}, String(`
3400. 1jslint.jstestDescribe("aa", async function () {
3401. 1 await new Promise(jslint.noop);
3402. 1 jslint.jstestIt("bb", jslint.noop);
3403. 1});
3404. 1 `));
3405. 1 assertJsonEqual(exitCode, 1);
3406. 1 assertMatch(stderr, /1\. test it pending - aa - jstestDescribe body\n {2}tests total {2}- 1\n {2}tests failed - 1\n/);
3407. 1 });
3408. 1 jstestIt((
3409. 1 "test jstestDescribe-body-throw handling-behavior"
3410. 1 ), async function () {
3411. 1
3412. 1// A describe body that throws crashes node, and the exit report lists the body
3413. 1// and the tests it did register as pending; an invalid filter regexp throws
3414. 1// from that body too. A falsy throw fails the same.
3415. 1
3416. 1 const [
3417. 1 [exitCode, stderr],
3418. 1 [exitCode2, stderr2],
3419. 1 [exitCode3, stderr3]
3420. 1 ] = await Promise.all([
3421. 1 childRun({}, String(`
3422. 1jslint.jstestDescribe("aa", function () {
3423. 1 jslint.jstestIt("bb", jslint.noop);
3424. 1 throw new Error("aa body broke");
3425. 1});
3426. 1 `)),
3427. 1 childRun({
3428. 1 npm_config_mode_test_filter: "("
3429. 1 }, String(`
3430. 1jslint.jstestDescribe("aa", function () {
3431. 1 jslint.jstestIt("bb", jslint.noop);
3432. 1});
3433. 1 `)),
3434. 1 childRun({}, String(`
3435. 1jslint.jstestDescribe("aa", function () {
3436. 1 jslint.jstestIt("bb", jslint.noop);
3437. 1 throw undefined;
3438. 1});
3439. 1 `))
3440. 1 ]);
3441. 1 assertJsonEqual([exitCode, exitCode2, exitCode3], [1, 1, 1]);
3442. 1 assertMatch(stderr, /1\. test it pending - aa - jstestDescribe body\n[\s\S]*2\. test it pending - aa - bb\n[\s\S]*tests failed - 2\n[\s\S]*Error: aa body broke\b/);
3443. 1 assertMatch(stderr2, /1\. test it pending - aa - jstestDescribe body\n[\s\S]*SyntaxError: Invalid regular/);
3444. 1 assertMatch(stderr3, /1\. test it pending - aa - jstestDescribe body\n[\s\S]*tests failed - 2\n[\s\S]*ERR_UNHANDLED_REJECTION/);
3445. 1 });
3446. 1 jstestIt((
3447. 1 "test jstestDescribe-concurrent handling-behavior"
3448. 1 ), async function () {
3449. 1
3450. 1// A describe's tests run concurrently: bb waits on an event cc emits, which
3451. 1// hangs if they run one after another.
3452. 1
3453. 1 const [exitCode, stderr] = await childRun({}, String(`
3454. 1import {EventEmitter} from "node:events";
3455. 1const ee = new EventEmitter();
3456. 1jslint.jstestDescribe("aa", function () {
3457. 1 jslint.jstestIt("bb", function () {
3458. 1 return new Promise(function (resolve) {
3459. 1 ee.once("go", resolve);
3460. 1 });
3461. 1 });
3462. 1 jslint.jstestIt("cc", function () {
3463. 1 ee.emit("go");
3464. 1 });
3465. 1});
3466. 1 `));
3467. 1 assertJsonEqual(exitCode, 0);
3468. 1 assertMatch(stderr, /tests total {2}- 2\n {2}tests failed - 0\n/);
3469. 1 });
3470. 1 jstestIt((
3471. 1 "test jstestDescribe-order handling-behavior"
3472. 1 ), async function () {
3473. 1
3474. 1// The runner fails a describe's tests out of order, and a describe out of order
3475. 1// with the one before it, each as one more failed test.
3476. 1
3477. 1 const [exitCode, stderr] = await childRun({}, String(`
3478. 1jslint.jstestDescribe("cc", function () {
3479. 1 jslint.jstestIt("bb", jslint.noop);
3480. 1 jslint.jstestIt("aa", jslint.noop);
3481. 1});
3482. 1jslint.jstestDescribe("aa", function () {
3483. 1 jslint.jstestIt("dd", jslint.noop);
3484. 1});
3485. 1 `));
3486. 1 assertJsonEqual(exitCode, 1);
3487. 1 assertMatch(stderr, /tests failed - 2\n/);
3488. 1 assertOrThrow(stderr.includes(
3489. 1 "\"aa\" must sort before \"bb\""
3490. 1 ), stderr);
3491. 1 assertOrThrow(stderr.includes(
3492. 1 "\"aa\" must sort before \"cc\""
3493. 1 ), stderr);
3494. 1
3495. 1// A failure its describe printed is not listed again at exit.
3496. 1
3497. 1 assertOrThrow(!stderr.includes("test it failed"), stderr);
3498. 1 });
3499. 1 jstestIt((
3500. 1 "test jstestDescribe-order-edge handling-behavior"
3501. 1 ), async function () {
3502. 1
3503. 1// Only a describe's first bad pair fails. A duplicate fails, and so does a
3504. 1// non-string name, since '>=' would be false for it and let it pass.
3505. 1
3506. 1 const resultList = await Promise.all([
3507. 1 ["aa", "cc", "bb", "aa"],
3508. 1 ["aa", "aa"],
3509. 1 [undefined, "bb"]
3510. 3 ].map(function (descriptionList) {
3511. 3 return childRun({}, String(`
3512. 3jslint.jstestDescribe("aa", function () {
3513. 3 for (const description of ${JSON.stringify(descriptionList)}) {
3514. 3 jslint.jstestIt(description ?? undefined, jslint.noop);
3515. 3 }
3516. 3});
3517. 3 `));
3518. 3 }));
3519. 3 assertJsonEqual(resultList.map(function ([exitCode, stderr]) {
3520. 3 return [exitCode, stderr.match(/\S+ must sort before .*|duplicate description .*/g)];
3521. 3 }), [
3522. 1 [1, ["\"bb\" must sort before \"cc\""]],
3523. 1 [1, ["duplicate description \"aa\""]],
3524. 1 [1, ["\"bb\" must sort before undefined"]]
3525. 1 ]);
3526. 1 });
3527. 1 jstestIt((
3528. 1 "test jstestIt-filter handling-behavior"
3529. 1 ), async function () {
3530. 1
3531. 1// Opt-in npm_config_mode_test_filter runs only "cc - dd", and a describe left
3532. 1// with no test prints nothing.
3533. 1
3534. 1 const [exitCode, stderr] = await childRun({
3535. 1 npm_config_mode_test_filter: "^cc - dd$"
3536. 1 }, String(`
3537. 1jslint.jstestDescribe("aa", function () {
3538. 1 jslint.jstestIt("bb", jslint.noop);
3539. 1});
3540. 1jslint.jstestDescribe("cc", function () {
3541. 1 jslint.jstestIt("dd", jslint.noop);
3542. 1 jslint.jstestIt("ee", function () {
3543. 1 throw new Error();
3544. 1 });
3545. 1});
3546. 1 `));
3547. 1 assertJsonEqual(exitCode, 0);
3548. 1 assertMatch(stderr, /test describe - cc\n.*1\. test it - dd\b[\s\S]*tests total {2}- 1\n/);
3549. 1 assertOrThrow(!stderr.includes("test describe - aa"), stderr);
3550. 1 assertOrThrow(!stderr.includes("test it - ee"), stderr);
3551. 1 });
3552. 1 jstestIt((
3553. 1 "test jstestIt-filter-empty handling-behavior"
3554. 1 ), async function () {
3555. 1
3556. 1// A filter that matches no test fails the run, so a stray or mistyped filter
3557. 1// cannot skip the whole suite and pass.
3558. 1
3559. 1 const [exitCode, stderr] = await childRun({
3560. 1 npm_config_mode_test_filter: "^zz$"
3561. 1 }, String(`
3562. 1jslint.jstestDescribe("aa", function () {
3563. 1 jslint.jstestIt("bb", jslint.noop);
3564. 1});
3565. 1 `));
3566. 1 assertJsonEqual(exitCode, 1);
3567. 1 assertMatch(stderr, /no test matched npm_config_mode_test_filter="\^zz\$"\n {2}tests total {2}- 0\n/);
3568. 1 });
3569. 1 jstestIt((
3570. 1 "test jstestIt-late handling-behavior"
3571. 1 ), async function () {
3572. 1
3573. 1// A jstestIt after an await in an async describe body, from a timer after the
3574. 1// body settled, or before any describe, throws: only the body's synchronous
3575. 1// part registers. The first crashes the body, leaving it and bb pending.
3576. 1
3577. 1 const resultList = await Promise.all([
3578. 1 String(`
3579. 1jslint.jstestDescribe("aa", async function () {
3580. 1 jslint.jstestIt("bb", jslint.noop);
3581. 1 await null;
3582. 1 jslint.jstestIt("cc", jslint.noop);
3583. 1});
3584. 1 `),
3585. 1 String(`
3586. 1jslint.jstestDescribe("aa", function () {
3587. 1 setTimeout(function () {
3588. 1 jslint.jstestIt("cc", jslint.noop);
3589. 1 }, 10);
3590. 1 jslint.jstestIt("bb", function () {
3591. 1 return new Promise(function (resolve) {
3592. 1 setTimeout(resolve, 50);
3593. 1 });
3594. 1 });
3595. 1});
3596. 1 `),
3597. 1 String(`
3598. 1jslint.jstestIt("cc", jslint.noop);
3599. 1 `)
3600. 3 ].map(function (source) {
3601. 3 return childRun({}, source);
3602. 3 }));
3603. 3 assertJsonEqual(resultList.map(function ([exitCode]) {
3604. 3 return exitCode;
3605. 3 }), [1, 1, 1]);
3606. 1 assertMatch(resultList[0][1], /1\. test it pending - aa - jstestDescribe body\n[\s\S]*2\. test it pending - aa - bb\n[\s\S]*tests failed - 2\n/);
3607. 3 resultList.forEach(function ([ignore, stderr]) {
3608. 3 assertOrThrow(stderr.includes(
3609. 3 "jstestIt \"cc\" called outside a jstestDescribe body, or" +
3610. 3 " after an await"
3611. 3 ), stderr);
3612. 3 });
3613. 1 });
3614. 1 jstestIt((
3615. 1 "test jstestIt-late-filtered handling-behavior"
3616. 1 ), async function () {
3617. 1
3618. 1// A late jstestIt throws even when the filter would skip it, so a filter never
3619. 1// hides a test registered after its describe settled.
3620. 1
3621. 1 const [exitCode, stderr] = await childRun({
3622. 1 npm_config_mode_test_filter: "^aa - bb$"
3623. 1 }, String(`
3624. 1jslint.jstestDescribe("aa", function () {
3625. 1 setTimeout(function () {
3626. 1 jslint.jstestIt("cc", jslint.noop);
3627. 1 }, 10);
3628. 1 jslint.jstestIt("bb", function () {
3629. 1 return new Promise(function (resolve) {
3630. 1 setTimeout(resolve, 50);
3631. 1 });
3632. 1 });
3633. 1});
3634. 1 `));
3635. 1 assertJsonEqual(exitCode, 1);
3636. 1 assertOrThrow(stderr.includes(
3637. 1 "jstestIt \"cc\" called outside a jstestDescribe body, or after" +
3638. 1 " an await"
3639. 1 ), stderr);
3640. 1 });
3641. 1 jstestIt((
3642. 1 "test jstestIt-throw-non-error handling-behavior"
3643. 1 ), async function () {
3644. 1
3645. 1// A test that throws undefined or a string, or rejects with no reason, fails
3646. 1// and prints what it threw, without crashing the runner's report. The exit
3647. 1// report prints a string too, for a failure a hung sibling kept unprinted.
3648. 1
3649. 1 const [[exitCode, stderr], [exitCode2, stderr2]] = await Promise.all([
3650. 1 childRun({}, String(`
3651. 1jslint.jstestDescribe("aa", function () {
3652. 1 jslint.jstestIt("bb", function () {
3653. 1 throw undefined;
3654. 1 });
3655. 1 jslint.jstestIt("cc", function () {
3656. 1 return Promise.reject();
3657. 1 });
3658. 1 jslint.jstestIt("dd", function () {
3659. 1 throw "dd string";
3660. 1 });
3661. 1 jslint.jstestIt("ee", function () {
3662. 1 throw Object.create(null);
3663. 1 });
3664. 1});
3665. 1 `)),
3666. 1 childRun({}, String(`
3667. 1jslint.jstestDescribe("aa", function () {
3668. 1 jslint.jstestIt("bb", function () {
3669. 1 throw "bb string";
3670. 1 });
3671. 1 jslint.jstestIt("bc", function () {
3672. 1 throw 0;
3673. 1 });
3674. 1 jslint.jstestIt("cc", function () {
3675. 1 setTimeout(process.exit, 100);
3676. 1 return new Promise(jslint.noop);
3677. 1 });
3678. 1});
3679. 1 `))
3680. 1 ]);
3681. 1 assertJsonEqual([exitCode, exitCode2], [1, 1]);
3682. 1 assertMatch(stderr, /1\. test it - bb\nundefined\b[\s\S]*2\. test it - cc\nundefined\b[\s\S]*3\. test it - dd\ndd string\b[\s\S]*4\. test it - ee\n\[object\][\s\S]*tests failed - 4\n/);
3683. 1 assertOrThrow(!stderr.includes("TypeError"), stderr);
3684. 1 assertMatch(stderr2, /test it failed - aa - bb\nbb string\n[\s\S]*test it failed - aa - bc\n0\n[\s\S]*tests failed - 3\n/);
3685. 1 });
3686. 1 jstestIt((
3687. 1 "test jstestOnExit-exit-code handling-behavior"
3688. 1 ), async function () {
3689. 1
3690. 1// 256 failed tests exit 1, not their count, which an 8-bit exit status would
3691. 1// wrap to a passing 0.
3692. 1
3693. 1 const [exitCode, stderr] = await childRun({}, String(`
3694. 1jslint.jstestDescribe("aa", function () {
3695. 1 let ii = 0;
3696. 1 while (ii < 256) {
3697. 1 jslint.jstestIt(String(1000 + ii), function () {
3698. 1 throw new Error();
3699. 1 });
3700. 1 ii += 1;
3701. 1 }
3702. 1});
3703. 1 `));
3704. 1 assertJsonEqual(exitCode, 1);
3705. 1 assertMatch(stderr, /tests total {2}- 256\n {2}tests failed - 256\n/);
3706. 1 });
3707. 1 jstestIt((
3708. 1 "test jstestOnExit-pending handling-behavior"
3709. 1 ), async function () {
3710. 1
3711. 1// A test still pending at exit counts as failed, in red, though its describe
3712. 1// never prints. With nothing alive, the hang exits at once, since jstest keeps
3713. 1// no timer; else <childRun>'s 10 s watchdog kills it.
3714. 1
3715. 1 const [exitCode, stderr] = await childRun({}, String(`
3716. 1jslint.jstestDescribe("aa", function () {
3717. 1 jslint.jstestIt("bb", function () {
3718. 1 return new Promise(jslint.noop);
3719. 1 });
3720. 1});
3721. 1 `));
3722. 1 assertJsonEqual(exitCode, 1);
3723. 1 assertMatch(stderr, /\[31m {2}\S+ 1\. test it pending - aa - bb\n {2}tests total {2}- 1\n {2}tests failed - 1\n/);
3724. 1 assertOrThrow(!stderr.includes("test describe - aa"), stderr);
3725. 1 });
3726. 1 jstestIt((
3727. 1 "test jstestOnExit-unprinted handling-behavior"
3728. 1 ), async function () {
3729. 1
3730. 1// betadog's flake: bb fails before its emit, cc waits on that emit forever,
3731. 1// then something ends the process. bb's error, stored in a describe that never
3732. 1// prints, still shows and counts.
3733. 1
3734. 1 const [exitCode, stderr] = await childRun({}, String(`
3735. 1import {EventEmitter} from "node:events";
3736. 1const ee = new EventEmitter();
3737. 1jslint.jstestDescribe("aa", function () {
3738. 1 jslint.jstestIt("bb", async function () {
3739. 1 throw new Error("bb real error");
3740. 1 });
3741. 1 jslint.jstestIt("cc", function () {
3742. 1 setTimeout(process.exit, 100);
3743. 1 return new Promise(function (resolve) {
3744. 1 ee.once("done", resolve);
3745. 1 });
3746. 1 });
3747. 1});
3748. 1 `));
3749. 1 assertJsonEqual(exitCode, 1);
3750. 1 assertMatch(stderr, /1\. test it failed - aa - bb\nError: bb real error\n[\s\S]*2\. test it pending - aa - cc\n {2}tests total {2}- 2\n {2}tests failed - 2\n/);
3751. 1 });
3752. 1 jstestIt((
3753. 1 "test jstestReport-numbering handling-behavior"
3754. 1 ), async function () {
3755. 1
3756. 1// The exit report numbers on from the tests already printed, and leaves out a
3757. 1// passed test whose describe never printed, here ee beside its hung sibling.
3758. 1
3759. 1 const [exitCode, stderr] = await childRun({}, String(`
3760. 1jslint.jstestDescribe("aa", function () {
3761. 1 jslint.jstestIt("bb", jslint.noop);
3762. 1});
3763. 1jslint.jstestDescribe("cc", function () {
3764. 1 jslint.jstestIt("dd", function () {
3765. 1 setTimeout(process.exit, 100);
3766. 1 return new Promise(jslint.noop);
3767. 1 });
3768. 1 jslint.jstestIt("ee", jslint.noop);
3769. 1});
3770. 1 `));
3771. 1 assertJsonEqual(exitCode, 1);
3772. 1 assertMatch(stderr, /1\. test it - bb\b[\s\S]*✘ 2\. test it pending - cc - dd\n/);
3773. 1 assertOrThrow(!stderr.includes("cc - ee"), stderr);
3774. 1 });
3775. 1});
3776. 1
3777. 1jstestDescribe((
3778. 1 "test v8CoverageListMerge handling-behavior"
3779. 1), function () {
3780. 1 let functionsInput = JSON.stringify([
3781. 1 {
3782. 1 functionName: "test",
3783. 1 isBlockCoverage: true,
3784. 1 ranges: [
3785. 1 {
3786. 1 count: 2,
3787. 1 endOffset: 4,
3788. 1 startOffset: 0
3789. 1 },
3790. 1 {
3791. 1 count: 1,
3792. 1 endOffset: 2,
3793. 1 startOffset: 1
3794. 1 },
3795. 1 {
3796. 1 count: 1,
3797. 1 endOffset: 3,
3798. 1 startOffset: 2
3799. 1 }
3800. 1 ]
3801. 1 }
3802. 1 ]);
3803. 1 jstestIt((
3804. 1 "test merge-empty-list handling-behavior"
3805. 1 ), function () {
3806. 1 assertJsonEqual(v8CoverageListMerge([]), {
3807. 1 result: []
3808. 1 });
3809. 1 });
3810. 1 jstestIt((
3811. 1 "test merge-fixture handling-behavior"
3812. 1 ), function () {
3813. 1 for (const file of [
3814. 1 "test_coverage_merge_is_block_coverage_test.json",
3815. 1 "test_coverage_merge_issue_2_mixed_is_block_coverage_test.json",
3816. 1 "test_coverage_merge_node_10_internal_errors_one_of_test.json",
3817. 1 "test_coverage_merge_reduced_test.json",
3818. 1 "test_coverage_merge_simple_test.json",
3819. 1 "test_coverage_merge_various_test.json"
3820. 6 ]) {
3821. 84 for (const {expected, inputs} of testCoverageMergeData[file]) {
3822. 84 assertJsonEqual(v8CoverageListMerge(inputs), expected, file);
3823. 84 }
3824. 6 }
3825. 1 });
3826. 1 jstestIt((
3827. 1 "test merge-node-sqlite-files handling-behavior"
3828. 1 ), function () {
3829. 1 let data1 = [
3830. 1 "test_v8_coverage_node_sqlite_9884_1633662346346_0.json",
3831. 1 "test_v8_coverage_node_sqlite_13216_1633662333140_0.json"
3832. 2 ].map(function (file) {
3833. 2 return testCoverageMergeData[file];
3834. 2 });
3835. 1 let data2 = testCoverageMergeData[
3836. 1 "test_v8_coverage_node_sqlite_merged.json"
3837. 1 ];
3838. 1 data1 = v8CoverageListMerge(data1);
3839. 1 data1 = v8CoverageListMerge([data1]);
3840. 1
3841. 1// Debug data1.
3842. 1// await moduleFs.promises.writeFile(
3843. 1// ".test_v8_coverage_node_sqlite_merged.json",
3844. 1// JSON.stringify(objectDeepCopyWithKeysSorted(data1), undefined, 4) + "\n"
3845. 1// );
3846. 1
3847. 1 assertJsonEqual(data1, data2);
3848. 1 });
3849. 1 jstestIt((
3850. 1 "test merge-one-list handling-behavior"
3851. 1 ), function () {
3852. 1 assertJsonEqual(v8CoverageListMerge([
3853. 1 {
3854. 1 result: [
3855. 1 {
3856. 1 functions: JSON.parse(functionsInput),
3857. 1 moduleUrl: "/lib.js",
3858. 1 scriptId: "123"
3859. 1 }
3860. 1 ]
3861. 1 }
3862. 1 ]), {
3863. 1 result: [
3864. 1 {
3865. 1 functions: [
3866. 1 {
3867. 1 functionName: "test",
3868. 1 isBlockCoverage: true,
3869. 1 ranges: [
3870. 1 {
3871. 1 count: 2,
3872. 1 endOffset: 4,
3873. 1 startOffset: 0
3874. 1 },
3875. 1 {
3876. 1 count: 1,
3877. 1 endOffset: 3,
3878. 1 startOffset: 1
3879. 1 }
3880. 1 ]
3881. 1 }
3882. 1 ],
3883. 1 moduleUrl: "/lib.js",
3884. 1 scriptId: "0"
3885. 1 }
3886. 1 ]
3887. 1 });
3888. 1 });
3889. 1 jstestIt((
3890. 1 "test merge-two-identical-lists handling-behavior"
3891. 1 ), function () {
3892. 1 assertJsonEqual(v8CoverageListMerge([
3893. 1 {
3894. 1 result: [
3895. 1 {
3896. 1 functions: JSON.parse(functionsInput),
3897. 1 scriptId: "123",
3898. 1 url: "/lib.js"
3899. 1 }, {
3900. 1 functions: JSON.parse(functionsInput),
3901. 1 scriptId: "123",
3902. 1 url: "/lib.js"
3903. 1 }
3904. 1 ]
3905. 1 }
3906. 1 ]), {
3907. 1 result: [
3908. 1 {
3909. 1 functions: [
3910. 1 {
3911. 1 functionName: "test",
3912. 1 isBlockCoverage: true,
3913. 1 ranges: [
3914. 1 {
3915. 1 count: 4,
3916. 1 endOffset: 4,
3917. 1 startOffset: 0
3918. 1 },
3919. 1 {
3920. 1 count: 2,
3921. 1 endOffset: 3,
3922. 1 startOffset: 1
3923. 1 }
3924. 1 ]
3925. 1 }
3926. 1 ],
3927. 1 scriptId: "0",
3928. 1 url: "/lib.js"
3929. 1 }
3930. 1 ]
3931. 1 });
3932. 1 });
3933. 1 jstestIt((
3934. 1 "test merge-unshared-function handling-behavior"
3935. 1 ), function () {
3936. 1 assertJsonEqual(v8CoverageListMerge([
3937. 1 {
3938. 1 result: [
3939. 1 {
3940. 1 functions: [
3941. 1 {
3942. 1 functionName: "test",
3943. 1 isBlockCoverage: true,
3944. 1 ranges: [
3945. 1 {
3946. 1 count: 2,
3947. 1 endOffset: 4,
3948. 1 startOffset: 0
3949. 1 }
3950. 1 ]
3951. 1 }
3952. 1 ],
3953. 1 moduleUrl: "/lib.js",
3954. 1 scriptId: "1"
3955. 1 }
3956. 1 ]
3957. 1 },
3958. 1 {
3959. 1 result: [
3960. 1 {
3961. 1 functions: [],
3962. 1 moduleUrl: "/lib.js",
3963. 1 scriptId: "2"
3964. 1 }
3965. 1 ]
3966. 1 }
3967. 1 ]), {
3968. 1 result: [
3969. 1 {
3970. 1 functions: [
3971. 1 {
3972. 1 functionName: "test",
3973. 1 isBlockCoverage: true,
3974. 1 ranges: [
3975. 1 {
3976. 1 count: 2,
3977. 1 endOffset: 4,
3978. 1 startOffset: 0
3979. 1 }
3980. 1 ]
3981. 1 }
3982. 1 ],
3983. 1 scriptId: "0"
3984. 1 }
3985. 1 ]
3986. 1 });
3987. 1 });
3988. 1});
3989. 1
3990. 1jstestDescribe((
3991. 1 "test v8CoverageReportCreate handling-behavior"
3992. 1), function () {
3993. 1 jstestIt((
3994. 1 "test coverage-hole-closing-midline handling-behavior"
3995. 1 ), async function () {
3996. 1
3997. 1// Pin two branches of <v8CoverageReportCreate> that line coverage cannot see.
3998. 1// A hole ending before end-of-line must close with a bare span, the
3999. 1// deadcode-dispelled case, and a hole on an ignored line must render "ignore".
4000. 1// Passing 0 makes each "&& ..." a hole, while the ";" after it is covered.
4001. 1
4002. 1 const dir = ".tmp/coverage_hole/";
4003. 1 const file = dir + "coverage_hole.js";
4004. 1 await fsWriteFileWithParents(file, (
4005. 1 "function aa(bb) {\n" +
4006. 1 " return bb && bb.cc;\n" +
4007. 1 "}\n" +
4008. 1 "function dd(ee) {\n" +
4009. 1 " return ee && ee.ff; //coverage-ignore-line\n" +
4010. 1 "}\n" +
4011. 1 "aa(0);\n" +
4012. 1 "dd(0);\n"
4013. 1 ));
4014. 1 await jslint.jslint_cli({
4015. 1 console_error: noop, // comment to debug
4016. 1 mode_cli: true,
4017. 1 process_argv: [
4018. 1 "node", "jslint.mjs",
4019. 1 "v8_coverage_report=" + dir,
4020. 1 "node",
4021. 1 file
4022. 1 ]
4023. 1 });
4024. 1 assertOrThrow((
4025. 1 await moduleFs.promises.readFile(dir + file + ".html", "utf8")
4026. 1 ).includes(
4027. 1 "<span class=\"uncovered\">&& bb.cc</span><span>;</span>"
4028. 1 ), "expected a hole closing mid-line in " + dir + file + ".html");
4029. 1 assertOrThrow((
4030. 1 await moduleFs.promises.readFile(dir + file + ".html", "utf8")
4031. 1 ).includes(
4032. 1 "<span class=\"ignore\">&& ee.ff</span><span>;"
4033. 1 ), "expected an ignored hole in " + dir + file + ".html");
4034. 1 });
4035. 1 jstestIt((
4036. 1 "test coverage-ignore handling-behavior"
4037. 1 ), function () {
4038. -0 switch (noop() && noop()) { //coverage-ignore-line
4039. -0 case 1: //coverage-ignore-line
4040. -0 break; //coverage-ignore-line
4041. 1/*coverage-disable*/
4042. -0 case 2:
4043. -0 break;
4044. 1/*coverage-enable*/
4045. 1 case undefined:
4046. 1 break;
4047. 1 }
4048. 1 });
4049. 1 jstestIt((
4050. 1 "test coverage-report-jslint-mjs handling-behavior"
4051. 1 ), async function () {
4052. 1 // test remove-old-coverage handling-behavior
4053. 1 await fsWriteFileWithParents(
4054. 1 ".tmp/coverage_jslint/coverage-0-0-0.json",
4055. 1 ""
4056. 1 );
4057. 1 await jslint.jslint_cli({
4058. 1 console_error: noop, // comment to debug
4059. 1 mode_cli: true,
4060. 1 process_argv: [
4061. 1 "node", "jslint.mjs",
4062. 1 "v8_coverage_report=.tmp/coverage_jslint",
4063. 1 "--exclude=aa.js",
4064. 1 "--include-node-modules=1",
4065. 1 "--include=jslint.mjs",
4066. 1 "node", "jslint.mjs"
4067. 1 ]
4068. 1 });
4069. 1 });
4070. 1 jstestIt((
4071. 1 "test misc handling-behavior"
4072. 1 ), async function () {
4073. 1 await Promise.all([
4074. 1 [
4075. 1 ".tmp/coverage_misc/aa.js", "\n".repeat(0x100)
4076. 1 ], [
4077. 1 ".tmp/coverage_misc/coverage-0-0-0.json", JSON.stringify({
4078. 1 "result": [
4079. 1 {
4080. 1 "functions": [
4081. 1 {
4082. 1 "functionName": "",
4083. 1 "isBlockCoverage": true,
4084. 1 "ranges": [
4085. 1 {
4086. 1 "count": 1,
4087. 1 "endOffset": 0xf0,
4088. 1 "startOffset": 0x10
4089. 1 },
4090. 1 {
4091. 1 "count": 1,
4092. 1 "endOffset": 0x40,
4093. 1 "startOffset": 0x20
4094. 1 },
4095. 1 {
4096. 1 "count": 1,
4097. 1 "endOffset": 0x80,
4098. 1 "startOffset": 0x60
4099. 1 },
4100. 1 {
4101. 1 "count": 0,
4102. 1 "endOffset": 0x45,
4103. 1 "startOffset": 0x25
4104. 1 },
4105. 1 {
4106. 1 "count": 0,
4107. 1 "endOffset": 0x85,
4108. 1 "startOffset": 0x65
4109. 1 }
4110. 1 ]
4111. 1 }
4112. 1 ],
4113. 1 "scriptId": "0",
4114. 1 "url": "file:///" + modulePath.resolve(
4115. 1 ".tmp/coverage_misc/aa.js"
4116. 1 )
4117. 1 }
4118. 1 ]
4119. 1 }, undefined, 4)
4120. 1 ]
4121. 2 ].map(async function ([
4122. 2 file, data
4123. 2 ]) {
4124. 2 await fsWriteFileWithParents(file, data);
4125. 2 }));
4126. 1 await jslint.jslint_cli({
4127. 1 console_error: noop, // comment to debug
4128. 1 mode_cli: true,
4129. 1 process_argv: [
4130. 1 "node", "jslint.mjs",
4131. 1 "v8_coverage_report=.tmp/coverage_misc"
4132. 1 // "node", ".tmp/coverage_misc/aa.js"
4133. 1 ]
4134. 1 });
4135. 1 });
4136. 1 jstestIt((
4137. 1 "test npm handling-behavior"
4138. 1 ), async function () {
4139. 1 await jslint.jslint_cli({
4140. 1 console_error: noop, // comment to debug
4141. 1 mode_cli: true,
4142. 1 process_argv: [
4143. 1 "node", "jslint.mjs",
4144. 1 "v8_coverage_report=.tmp/coverage_npm",
4145. 1 "npm", "--version"
4146. 1 ]
4147. 1 });
4148. 1 });
4149. 1 jstestIt((
4150. 1 "test null-case handling-behavior"
4151. 1 ), async function () {
4152. 1 await assertErrorThrownAsync(function () {
4153. 1 return v8CoverageReportCreate({});
4154. 1 }, "invalid coverageDir");
4155. 1 });
4156. 1 jstestIt((
4157. 1 "test report-fixture handling-behavior"
4158. 1 ), async function () {
4159. 1 const fixtureList = [
4160. 1 [
4161. 1 "v8CoverageReportCreate_high.js", (
4162. 1 "switch(0){\n" +
4163. 1 "case 0:break;\n" +
4164. 1 "}\n"
4165. 1 )
4166. 1 ], [
4167. 1 "v8CoverageReportCreate_ignore.js", (
4168. 1 "/*coverage-ignore-file*/\n" +
4169. 1 "switch(0){\n" +
4170. 1 "case 0:break;\n" +
4171. 1 "case 1:break;//coverage-ignore-line\n" +
4172. 1 "/*coverage-disable*/\n" +
4173. 1 "case 2:break;\n" +
4174. 1 "/*coverage-enable*/\n" +
4175. 1 "}\n"
4176. 1 )
4177. 1 ], [
4178. 1 "v8CoverageReportCreate_low.js", (
4179. 1 "switch(0){\n" +
4180. 1 "case 1:break;\n" +
4181. 1 "case 2:break;\n" +
4182. 1 "case 3:break;\n" +
4183. 1 "case 4:break;\n" +
4184. 1 "}\n"
4185. 1 )
4186. 1 ], [
4187. 1 "v8CoverageReportCreate_medium.js", (
4188. 1 "switch(0){\n" +
4189. 1 "case 0:break;\n" +
4190. 1 "case 1:break;\n" +
4191. 1 "case 2:break;\n" +
4192. 1 "}\n"
4193. 1 )
4194. 1 ]
4195. 1 ];
4196. 4 await Promise.all(fixtureList.map(async function ([
4197. 4 file, data
4198. 4 ], ii) {
4199. 4 const dir = ".tmp/coverage_" + ii + "/";
4200. 4 file = dir + file;
4201. 4 await fsWriteFileWithParents(file, data);
4202. 4 await jslint.jslint_cli({
4203. 4 console_error: noop, // comment to debug
4204. 4 mode_cli: true,
4205. 4 process_argv: [
4206. 4 "node", "jslint.mjs",
4207. 4 "v8_coverage_report=" + dir,
4208. 4 "node",
4209. 4 file
4210. 4 ]
4211. 4 });
4212. 4 }));
4213. 1 });
4214. 1});
4215. 1