api.js 28 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141
  1. 'use strict';
  2. require('../lib/chalk').set();
  3. const assert = require('assert');
  4. const path = require('path');
  5. const fs = require('fs');
  6. const del = require('del');
  7. const test = require('tap').test;
  8. const Api = require('../api');
  9. const babelPipeline = require('../lib/babel-pipeline');
  10. const testCapitalizerPlugin = require.resolve('./fixture/babel-plugin-test-capitalizer');
  11. const ROOT_DIR = path.join(__dirname, '..');
  12. function withNodeEnv(value, run) {
  13. assert(!('NODE_ENV' in process.env));
  14. process.env.NODE_ENV = value;
  15. const reset = () => {
  16. delete process.env.NODE_ENV;
  17. };
  18. const promise = new Promise(resolve => {
  19. resolve(run());
  20. });
  21. promise.then(reset, reset);
  22. return promise;
  23. }
  24. function apiCreator(options) {
  25. options = options || {};
  26. options.babelConfig = babelPipeline.validate(options.babelConfig);
  27. options.concurrency = 2;
  28. options.extensions = options.extensions || {all: ['js'], enhancementsOnly: [], full: ['js']};
  29. options.projectDir = options.projectDir || ROOT_DIR;
  30. options.resolveTestsFrom = options.resolveTestsFrom || options.projectDir;
  31. const instance = new Api(options);
  32. if (!options.precompileHelpers) {
  33. instance._precompileHelpers = () => Promise.resolve();
  34. }
  35. return instance;
  36. }
  37. test('ES2015 support', t => {
  38. const api = apiCreator();
  39. return api.run([path.join(__dirname, 'fixture/es2015.js')])
  40. .then(runStatus => {
  41. t.is(runStatus.stats.passedTests, 1);
  42. });
  43. });
  44. test('precompile helpers', t => {
  45. const api = apiCreator({
  46. precompileHelpers: true,
  47. resolveTestsFrom: path.join(__dirname, 'fixture/precompile-helpers')
  48. });
  49. return api.run()
  50. .then(runStatus => {
  51. t.is(runStatus.stats.passedTests, 1);
  52. });
  53. });
  54. test('generators support', t => {
  55. const api = apiCreator();
  56. return api.run([path.join(__dirname, 'fixture/generators.js')])
  57. .then(runStatus => {
  58. t.is(runStatus.stats.passedTests, 1);
  59. });
  60. });
  61. test('async/await support', t => {
  62. const api = apiCreator();
  63. return api.run([path.join(__dirname, 'fixture/async-await.js')])
  64. .then(runStatus => {
  65. t.is(runStatus.stats.passedTests, 2);
  66. });
  67. });
  68. test('fail-fast mode - single file & serial', t => {
  69. const api = apiCreator({
  70. failFast: true
  71. });
  72. const tests = [];
  73. api.on('run', plan => {
  74. plan.status.on('stateChange', evt => {
  75. if (evt.type === 'test-failed') {
  76. tests.push({
  77. ok: false,
  78. title: evt.title
  79. });
  80. } else if (evt.type === 'test-passed') {
  81. tests.push({
  82. ok: true,
  83. title: evt.title
  84. });
  85. }
  86. });
  87. });
  88. return api.run([path.join(__dirname, 'fixture/fail-fast/single-file/test.js')])
  89. .then(runStatus => {
  90. t.ok(api.options.failFast);
  91. t.strictDeepEqual(tests, [{
  92. ok: true,
  93. title: 'first pass'
  94. }, {
  95. ok: false,
  96. title: 'second fail'
  97. }, {
  98. ok: true,
  99. title: 'third pass'
  100. }]);
  101. t.is(runStatus.stats.passedTests, 2);
  102. t.is(runStatus.stats.failedTests, 1);
  103. });
  104. });
  105. test('fail-fast mode - multiple files & serial', t => {
  106. const api = apiCreator({
  107. failFast: true,
  108. serial: true
  109. });
  110. const tests = [];
  111. api.on('run', plan => {
  112. plan.status.on('stateChange', evt => {
  113. if (evt.type === 'test-failed') {
  114. tests.push({
  115. ok: false,
  116. testFile: evt.testFile,
  117. title: evt.title
  118. });
  119. } else if (evt.type === 'test-passed') {
  120. tests.push({
  121. ok: true,
  122. testFile: evt.testFile,
  123. title: evt.title
  124. });
  125. }
  126. });
  127. });
  128. return api.run([
  129. path.join(__dirname, 'fixture/fail-fast/multiple-files/fails.js'),
  130. path.join(__dirname, 'fixture/fail-fast/multiple-files/passes.js')
  131. ])
  132. .then(runStatus => {
  133. t.ok(api.options.failFast);
  134. t.strictDeepEqual(tests, [{
  135. ok: true,
  136. testFile: path.join(__dirname, 'fixture/fail-fast/multiple-files/fails.js'),
  137. title: 'first pass'
  138. }, {
  139. ok: false,
  140. testFile: path.join(__dirname, 'fixture/fail-fast/multiple-files/fails.js'),
  141. title: 'second fail'
  142. }]);
  143. t.is(runStatus.stats.passedTests, 1);
  144. t.is(runStatus.stats.failedTests, 1);
  145. });
  146. });
  147. test('fail-fast mode - multiple files & interrupt', t => {
  148. const api = apiCreator({
  149. failFast: true,
  150. concurrency: 2
  151. });
  152. const tests = [];
  153. api.on('run', plan => {
  154. plan.status.on('stateChange', evt => {
  155. if (evt.type === 'test-failed') {
  156. tests.push({
  157. ok: false,
  158. testFile: evt.testFile,
  159. title: evt.title
  160. });
  161. } else if (evt.type === 'test-passed') {
  162. tests.push({
  163. ok: true,
  164. testFile: evt.testFile,
  165. title: evt.title
  166. });
  167. }
  168. });
  169. });
  170. return api.run([
  171. path.join(__dirname, 'fixture/fail-fast/multiple-files/fails.js'),
  172. path.join(__dirname, 'fixture/fail-fast/multiple-files/passes-slow.js')
  173. ])
  174. .then(runStatus => {
  175. t.ok(api.options.failFast);
  176. t.strictDeepEqual(tests, [{
  177. ok: true,
  178. testFile: path.join(__dirname, 'fixture/fail-fast/multiple-files/fails.js'),
  179. title: 'first pass'
  180. }, {
  181. ok: false,
  182. testFile: path.join(__dirname, 'fixture/fail-fast/multiple-files/fails.js'),
  183. title: 'second fail'
  184. }, {
  185. ok: true,
  186. testFile: path.join(__dirname, 'fixture/fail-fast/multiple-files/fails.js'),
  187. title: 'third pass'
  188. }, {
  189. ok: true,
  190. testFile: path.join(__dirname, 'fixture/fail-fast/multiple-files/passes-slow.js'),
  191. title: 'first pass'
  192. }]);
  193. t.is(runStatus.stats.passedTests, 3);
  194. t.is(runStatus.stats.failedTests, 1);
  195. });
  196. });
  197. test('fail-fast mode - crash & serial', t => {
  198. const api = apiCreator({
  199. failFast: true,
  200. serial: true
  201. });
  202. const tests = [];
  203. const workerFailures = [];
  204. api.on('run', plan => {
  205. plan.status.on('stateChange', evt => {
  206. if (evt.type === 'test-failed') {
  207. tests.push({
  208. ok: false,
  209. title: evt.title
  210. });
  211. } else if (evt.type === 'test-passed') {
  212. tests.push({
  213. ok: true,
  214. title: evt.title
  215. });
  216. } else if (evt.type === 'worker-failed') {
  217. workerFailures.push(evt);
  218. }
  219. });
  220. });
  221. return api.run([
  222. path.join(__dirname, 'fixture/fail-fast/crash/crashes.js'),
  223. path.join(__dirname, 'fixture/fail-fast/crash/passes.js')
  224. ])
  225. .then(runStatus => {
  226. t.ok(api.options.failFast);
  227. t.strictDeepEqual(tests, []);
  228. t.is(workerFailures.length, 1);
  229. t.is(workerFailures[0].testFile, path.join(__dirname, 'fixture', 'fail-fast', 'crash', 'crashes.js'));
  230. t.is(runStatus.stats.passedTests, 0);
  231. t.is(runStatus.stats.failedTests, 0);
  232. });
  233. });
  234. test('fail-fast mode - timeout & serial', t => {
  235. const api = apiCreator({
  236. failFast: true,
  237. serial: true,
  238. timeout: '100ms'
  239. });
  240. const tests = [];
  241. const timeouts = [];
  242. api.on('run', plan => {
  243. plan.status.on('stateChange', evt => {
  244. if (evt.type === 'test-failed') {
  245. tests.push({
  246. ok: false,
  247. title: evt.title
  248. });
  249. } else if (evt.type === 'test-passed') {
  250. tests.push({
  251. ok: true,
  252. title: evt.title
  253. });
  254. } else if (evt.type === 'timeout') {
  255. timeouts.push(evt);
  256. }
  257. });
  258. });
  259. return api.run([
  260. path.join(__dirname, 'fixture/fail-fast/timeout/fails.js'),
  261. path.join(__dirname, 'fixture/fail-fast/timeout/passes.js')
  262. ])
  263. .then(runStatus => {
  264. t.ok(api.options.failFast);
  265. t.strictDeepEqual(tests, []);
  266. t.is(timeouts.length, 1);
  267. t.is(timeouts[0].period, 100);
  268. t.is(runStatus.stats.passedTests, 0);
  269. t.is(runStatus.stats.failedTests, 0);
  270. });
  271. });
  272. test('fail-fast mode - no errors', t => {
  273. const api = apiCreator({
  274. failFast: true
  275. });
  276. return api.run([
  277. path.join(__dirname, 'fixture/fail-fast/without-error/a.js'),
  278. path.join(__dirname, 'fixture/fail-fast/without-error/b.js')
  279. ])
  280. .then(runStatus => {
  281. t.ok(api.options.failFast);
  282. t.is(runStatus.stats.passedTests, 2);
  283. t.is(runStatus.stats.failedTests, 0);
  284. });
  285. });
  286. test('serial execution mode', t => {
  287. const api = apiCreator({
  288. serial: true
  289. });
  290. return api.run([path.join(__dirname, 'fixture/serial.js')])
  291. .then(runStatus => {
  292. t.ok(api.options.serial);
  293. t.is(runStatus.stats.passedTests, 3);
  294. t.is(runStatus.stats.failedTests, 0);
  295. });
  296. });
  297. test('run from package.json folder by default', t => {
  298. const api = apiCreator();
  299. return api.run([path.join(__dirname, 'fixture/process-cwd-default.js')])
  300. .then(runStatus => {
  301. t.is(runStatus.stats.passedTests, 1);
  302. });
  303. });
  304. test('control worker\'s process.cwd() with projectDir option', t => {
  305. const fullPath = path.join(__dirname, 'fixture/process-cwd-pkgdir.js');
  306. const api = apiCreator({projectDir: path.dirname(fullPath)});
  307. return api.run([fullPath])
  308. .then(runStatus => {
  309. t.is(runStatus.stats.passedTests, 1);
  310. });
  311. });
  312. test('stack traces for exceptions are corrected using a source map file', t => {
  313. t.plan(4);
  314. const api = apiCreator({
  315. cacheEnabled: true
  316. });
  317. api.on('run', plan => {
  318. plan.status.on('stateChange', evt => {
  319. if (evt.type === 'uncaught-exception') {
  320. t.match(evt.err.message, /Thrown by source-map-fixtures/);
  321. t.match(evt.err.stack, /^.*?Object\.t.*?as run\b.*source-map-fixtures.src.throws.js:1.*$/m);
  322. t.match(evt.err.stack, /^.*?Immediate\b.*source-map-file.js:4.*$/m);
  323. }
  324. });
  325. });
  326. return api.run([path.join(__dirname, 'fixture/source-map-file.js')])
  327. .then(runStatus => {
  328. t.is(runStatus.stats.passedTests, 1);
  329. });
  330. });
  331. test('babel.testOptions can disable sourceMaps', t => {
  332. t.plan(3);
  333. const api = apiCreator({
  334. babelConfig: {
  335. testOptions: {
  336. sourceMaps: false
  337. }
  338. },
  339. cacheEnabled: true
  340. });
  341. api.on('run', plan => {
  342. plan.status.on('stateChange', evt => {
  343. if (evt.type === 'uncaught-exception') {
  344. t.match(evt.err.message, /Thrown by source-map-fixtures/);
  345. t.match(evt.err.stack, /^.*?Immediate\b.*source-map-file.js:7.*$/m);
  346. }
  347. });
  348. });
  349. return api.run([path.join(__dirname, 'fixture/source-map-file.js')])
  350. .then(runStatus => {
  351. t.is(runStatus.stats.passedTests, 1);
  352. });
  353. });
  354. test('stack traces for exceptions are corrected using a source map file in what looks like a browser env', t => {
  355. t.plan(4);
  356. const api = apiCreator({
  357. cacheEnabled: true
  358. });
  359. api.on('run', plan => {
  360. plan.status.on('stateChange', evt => {
  361. if (evt.type === 'uncaught-exception') {
  362. t.match(evt.err.message, /Thrown by source-map-fixtures/);
  363. t.match(evt.err.stack, /^.*?Object\.t.*?as run\b.*source-map-fixtures.src.throws.js:1.*$/m);
  364. t.match(evt.err.stack, /^.*?Immediate\b.*source-map-file-browser-env.js:7.*$/m);
  365. }
  366. });
  367. });
  368. return api.run([path.join(__dirname, 'fixture/source-map-file-browser-env.js')])
  369. .then(runStatus => {
  370. t.is(runStatus.stats.passedTests, 1);
  371. });
  372. });
  373. test('enhanced assertion formatting necessary whitespace and empty strings', t => {
  374. const expected = [
  375. [
  376. /foo === "" && "" === foo/,
  377. /foo === ""/,
  378. /foo/
  379. ],
  380. [
  381. /new Object\(foo\) instanceof Object/,
  382. /Object/,
  383. /new Object\(foo\)/,
  384. /foo/
  385. ],
  386. [
  387. /\[foo].filter\(item => {\n\s+return item === "bar";\n}\).length > 0/,
  388. /\[foo].filter\(item => {\n\s+return item === "bar";\n}\).length/,
  389. /\[foo].filter\(item => {\n\s+return item === "bar";\n}\)/,
  390. /\[foo]/,
  391. /foo/
  392. ]
  393. ];
  394. t.plan(14);
  395. const api = apiCreator();
  396. const errors = [];
  397. api.on('run', plan => {
  398. plan.status.on('stateChange', evt => {
  399. if (evt.type === 'test-failed') {
  400. errors.push(evt.err);
  401. }
  402. });
  403. });
  404. return api.run([path.join(__dirname, 'fixture/enhanced-assertion-formatting.js')])
  405. .then(runStatus => {
  406. t.is(errors.length, 3);
  407. t.is(runStatus.stats.passedTests, 0);
  408. errors.forEach((error, errorIndex) => {
  409. error.statements.forEach((statement, statementIndex) => {
  410. t.match(statement[0], expected[errorIndex][statementIndex]);
  411. });
  412. });
  413. });
  414. });
  415. test('stack traces for exceptions are corrected using a source map file (cache off)', t => {
  416. t.plan(4);
  417. const api = apiCreator({
  418. cacheEnabled: false
  419. });
  420. api.on('run', plan => {
  421. plan.status.on('stateChange', evt => {
  422. if (evt.type === 'uncaught-exception') {
  423. t.match(evt.err.message, /Thrown by source-map-fixtures/);
  424. t.match(evt.err.stack, /^.*?Object\.t.*?as run\b.*source-map-fixtures.src.throws.js:1.*$/m);
  425. t.match(evt.err.stack, /^.*?Immediate\b.*source-map-file.js:4.*$/m);
  426. }
  427. });
  428. });
  429. return api.run([path.join(__dirname, 'fixture/source-map-file.js')])
  430. .then(runStatus => {
  431. t.is(runStatus.stats.passedTests, 1);
  432. });
  433. });
  434. test('stack traces for exceptions are corrected using a source map, taking an initial source map for the test file into account (cache on)', t => {
  435. t.plan(4);
  436. const api = apiCreator({
  437. cacheEnabled: true
  438. });
  439. api.on('run', plan => {
  440. plan.status.on('stateChange', evt => {
  441. if (evt.type === 'uncaught-exception') {
  442. t.match(evt.err.message, /Thrown by source-map-fixtures/);
  443. t.match(evt.err.stack, /^.*?Object\.t.*?as run\b.*source-map-fixtures.src.throws.js:1.*$/m);
  444. t.match(evt.err.stack, /^.*?Immediate\b.*source-map-initial-input.js:14.*$/m);
  445. }
  446. });
  447. });
  448. return api.run([path.join(__dirname, 'fixture/source-map-initial.js')])
  449. .then(runStatus => {
  450. t.is(runStatus.stats.passedTests, 1);
  451. });
  452. });
  453. test('stack traces for exceptions are corrected using a source map, taking an initial source map for the test file into account (cache off)', t => {
  454. t.plan(4);
  455. const api = apiCreator({
  456. cacheEnabled: false
  457. });
  458. api.on('run', plan => {
  459. plan.status.on('stateChange', evt => {
  460. if (evt.type === 'uncaught-exception') {
  461. t.match(evt.err.message, /Thrown by source-map-fixtures/);
  462. t.match(evt.err.stack, /^.*?Object\.t.*?as run\b.*source-map-fixtures.src.throws.js:1.*$/m);
  463. t.match(evt.err.stack, /^.*?Immediate\b.*source-map-initial-input.js:14.*$/m);
  464. }
  465. });
  466. });
  467. return api.run([path.join(__dirname, 'fixture/source-map-initial.js')])
  468. .then(runStatus => {
  469. t.is(runStatus.stats.passedTests, 1);
  470. });
  471. });
  472. test('absolute paths', t => {
  473. const api = apiCreator();
  474. return api.run([path.resolve('test/fixture/es2015.js')])
  475. .then(runStatus => {
  476. t.is(runStatus.stats.passedTests, 1);
  477. });
  478. });
  479. test('symlink to directory containing test files', t => {
  480. const api = apiCreator();
  481. return api.run([path.join(__dirname, 'fixture/symlink')])
  482. .then(runStatus => {
  483. t.is(runStatus.stats.passedTests, 1);
  484. });
  485. });
  486. test('symlink to test file directly', t => {
  487. const api = apiCreator();
  488. return api.run([path.join(__dirname, 'fixture/symlinkfile.js')])
  489. .then(runStatus => {
  490. t.is(runStatus.stats.passedTests, 1);
  491. });
  492. });
  493. test('search directories recursively for files', t => {
  494. const api = apiCreator();
  495. return api.run([path.join(__dirname, 'fixture/subdir')])
  496. .then(runStatus => {
  497. t.is(runStatus.stats.passedTests, 2);
  498. t.is(runStatus.stats.failedTests, 1);
  499. });
  500. });
  501. test('test file in node_modules is ignored', t => {
  502. t.plan(1);
  503. const api = apiCreator();
  504. return api.run([path.join(__dirname, 'fixture/ignored-dirs/node_modules/test.js')])
  505. .then(runStatus => {
  506. t.is(runStatus.stats.declaredTests, 0);
  507. });
  508. });
  509. test('test file in fixtures is ignored', t => {
  510. t.plan(1);
  511. const api = apiCreator();
  512. return api.run([path.join(__dirname, 'fixture/ignored-dirs/fixtures/test.js')])
  513. .then(runStatus => {
  514. t.is(runStatus.stats.declaredTests, 0);
  515. });
  516. });
  517. test('test file in helpers is ignored', t => {
  518. t.plan(1);
  519. const api = apiCreator();
  520. return api.run([path.join(__dirname, 'fixture/ignored-dirs/helpers/test.js')])
  521. .then(runStatus => {
  522. t.is(runStatus.stats.declaredTests, 0);
  523. });
  524. });
  525. test('Node.js-style --require CLI argument', t => {
  526. const requirePath = './' + path.relative('.', path.join(__dirname, 'fixture/install-global.js')).replace(/\\/g, '/');
  527. const api = apiCreator({
  528. require: [requirePath]
  529. });
  530. return api.run([path.join(__dirname, 'fixture/validate-installed-global.js')])
  531. .then(runStatus => {
  532. t.is(runStatus.stats.passedTests, 1);
  533. });
  534. });
  535. test('Node.js-style --require CLI argument module not found', t => {
  536. t.throws(() => {
  537. /* eslint no-new: 0 */
  538. apiCreator({require: ['foo-bar']});
  539. }, /^Could not resolve required module 'foo-bar'$/);
  540. t.end();
  541. });
  542. test('caching is enabled by default', t => {
  543. del.sync(path.join(__dirname, 'fixture/caching/node_modules'));
  544. const api = apiCreator({
  545. projectDir: path.join(__dirname, 'fixture/caching')
  546. });
  547. return api.run([path.join(__dirname, 'fixture/caching/test.js')])
  548. .then(() => {
  549. const files = fs.readdirSync(path.join(__dirname, 'fixture/caching/node_modules/.cache/ava'));
  550. t.is(files.filter(x => endsWithJs(x)).length, 1);
  551. t.is(files.filter(x => endsWithMap(x)).length, 1);
  552. t.is(files.length, 2);
  553. });
  554. function endsWithJs(filename) {
  555. return /\.js$/.test(filename);
  556. }
  557. function endsWithMap(filename) {
  558. return /\.map$/.test(filename);
  559. }
  560. });
  561. test('caching can be disabled', t => {
  562. del.sync(path.join(__dirname, 'fixture/caching/node_modules'));
  563. const api = apiCreator({
  564. resolveTestsFrom: path.join(__dirname, 'fixture/caching'),
  565. cacheEnabled: false
  566. });
  567. return api.run([path.join(__dirname, 'fixture/caching/test.js')])
  568. .then(() => {
  569. t.false(fs.existsSync(path.join(__dirname, 'fixture/caching/node_modules/.cache/ava')));
  570. });
  571. });
  572. test('test file with only skipped tests does not create a failure', t => {
  573. const api = apiCreator();
  574. return api.run([path.join(__dirname, 'fixture/skip-only.js')])
  575. .then(runStatus => {
  576. t.is(runStatus.stats.selectedTests, 1);
  577. t.is(runStatus.stats.skippedTests, 1);
  578. t.is(runStatus.stats.failedTests, 0);
  579. });
  580. });
  581. test('test file with only skipped tests does not run hooks', t => {
  582. const api = apiCreator();
  583. return api.run([path.join(__dirname, 'fixture/hooks-skipped.js')])
  584. .then(runStatus => {
  585. t.is(runStatus.stats.selectedTests, 1);
  586. t.is(runStatus.stats.skippedTests, 1);
  587. t.is(runStatus.stats.passedTests, 0);
  588. t.is(runStatus.stats.failedTests, 0);
  589. t.is(runStatus.stats.failedHooks, 0);
  590. });
  591. });
  592. test('emits dependencies for test files', t => {
  593. t.plan(8);
  594. const api = apiCreator({
  595. require: [path.resolve('test/fixture/with-dependencies/require-custom.js')]
  596. });
  597. const testFiles = [
  598. path.resolve('test/fixture/with-dependencies/no-tests.js'),
  599. path.resolve('test/fixture/with-dependencies/test.js'),
  600. path.resolve('test/fixture/with-dependencies/test-failure.js'),
  601. path.resolve('test/fixture/with-dependencies/test-uncaught-exception.js')
  602. ];
  603. const sourceFiles = [
  604. path.resolve('test/fixture/with-dependencies/dep-1.js'),
  605. path.resolve('test/fixture/with-dependencies/dep-2.js'),
  606. path.resolve('test/fixture/with-dependencies/dep-3.custom')
  607. ];
  608. api.on('run', plan => {
  609. plan.status.on('stateChange', evt => {
  610. if (evt.type === 'dependencies') {
  611. t.notEqual(testFiles.indexOf(evt.testFile), -1);
  612. t.strictDeepEqual(evt.dependencies.slice(-3), sourceFiles);
  613. }
  614. });
  615. });
  616. return api.run(['test/fixture/with-dependencies/*test*.js']);
  617. });
  618. test('verify test count', t => {
  619. t.plan(4);
  620. const api = apiCreator();
  621. return api.run([
  622. path.join(__dirname, 'fixture/test-count.js'),
  623. path.join(__dirname, 'fixture/test-count-2.js'),
  624. path.join(__dirname, 'fixture/test-count-3.js')
  625. ]).then(runStatus => {
  626. t.is(runStatus.stats.passedTests, 4, 'pass count');
  627. t.is(runStatus.stats.failedTests, 3, 'fail count');
  628. t.is(runStatus.stats.skippedTests, 3, 'skip count');
  629. t.is(runStatus.stats.todoTests, 3, 'todo count');
  630. });
  631. });
  632. test('babel.testOptions with a custom plugin', t => {
  633. t.plan(2);
  634. const api = apiCreator({
  635. babelConfig: {
  636. testOptions: {
  637. plugins: [testCapitalizerPlugin]
  638. }
  639. },
  640. cacheEnabled: false,
  641. projectDir: __dirname
  642. });
  643. api.on('run', plan => {
  644. plan.status.on('stateChange', evt => {
  645. if (evt.type === 'test-passed') {
  646. t.is(evt.title, 'FOO');
  647. }
  648. });
  649. });
  650. return api.run([path.join(__dirname, 'fixture/babelrc/test.js')])
  651. .then(runStatus => {
  652. t.is(runStatus.stats.passedTests, 1);
  653. }, t.threw);
  654. });
  655. test('babel.testOptions.babelrc effectively defaults to true', t => {
  656. t.plan(3);
  657. const api = apiCreator({
  658. projectDir: path.join(__dirname, 'fixture/babelrc')
  659. });
  660. api.on('run', plan => {
  661. plan.status.on('stateChange', evt => {
  662. if (evt.type === 'test-passed') {
  663. t.ok((evt.title === 'foo') || (evt.title === 'repeated test: foo'));
  664. }
  665. });
  666. });
  667. return api.run()
  668. .then(runStatus => {
  669. t.is(runStatus.stats.passedTests, 2);
  670. });
  671. });
  672. test('babel.testOptions.babelrc can explicitly be true', t => {
  673. t.plan(3);
  674. const api = apiCreator({
  675. babelConfig: {
  676. testOptions: {babelrc: true}
  677. },
  678. cacheEnabled: false,
  679. projectDir: path.join(__dirname, 'fixture/babelrc')
  680. });
  681. api.on('run', plan => {
  682. plan.status.on('stateChange', evt => {
  683. if (evt.type === 'test-passed') {
  684. t.ok(evt.title === 'foo' || evt.title === 'repeated test: foo');
  685. }
  686. });
  687. });
  688. return api.run()
  689. .then(runStatus => {
  690. t.is(runStatus.stats.passedTests, 2);
  691. });
  692. });
  693. test('babel.testOptions.babelrc can explicitly be false', t => {
  694. t.plan(2);
  695. const api = apiCreator({
  696. babelConfig: {
  697. testOptions: {babelrc: false}
  698. },
  699. cacheEnabled: false,
  700. projectDir: path.join(__dirname, 'fixture/babelrc')
  701. });
  702. api.on('run', plan => {
  703. plan.status.on('stateChange', evt => {
  704. if (evt.type === 'test-passed') {
  705. t.is(evt.title, 'foo');
  706. }
  707. });
  708. });
  709. return api.run()
  710. .then(runStatus => {
  711. t.is(runStatus.stats.passedTests, 1);
  712. });
  713. });
  714. test('babel.testOptions merges plugins with .babelrc', t => {
  715. t.plan(3);
  716. const api = apiCreator({
  717. babelConfig: {
  718. testOptions: {
  719. babelrc: true,
  720. plugins: [testCapitalizerPlugin]
  721. }
  722. },
  723. cacheEnabled: false,
  724. projectDir: path.join(__dirname, 'fixture/babelrc')
  725. });
  726. api.on('run', plan => {
  727. plan.status.on('stateChange', evt => {
  728. if (evt.type === 'test-passed') {
  729. t.ok(evt.title === 'FOO' || evt.title === 'repeated test: foo');
  730. }
  731. });
  732. });
  733. return api.run()
  734. .then(runStatus => {
  735. t.is(runStatus.stats.passedTests, 2);
  736. });
  737. });
  738. test('babel.testOptions.babelrc (when true) picks up .babelrc.js files', t => {
  739. t.plan(3);
  740. const api = apiCreator({
  741. babelConfig: {
  742. testOptions: {
  743. babelrc: true
  744. }
  745. },
  746. projectDir: path.join(__dirname, 'fixture/babelrc-js')
  747. });
  748. api.on('run', plan => {
  749. plan.status.on('stateChange', evt => {
  750. if (evt.type === 'test-passed') {
  751. t.ok((evt.title === 'foo') || (evt.title === 'repeated test: foo'));
  752. }
  753. });
  754. });
  755. return api.run()
  756. .then(runStatus => {
  757. t.is(runStatus.stats.passedTests, 2);
  758. });
  759. });
  760. test('babel.testOptions can disable ava/stage-4', t => {
  761. t.plan(1);
  762. const api = apiCreator({
  763. babelConfig: {
  764. testOptions: {
  765. babelrc: false,
  766. presets: [[require.resolve('../stage-4'), false]]
  767. }
  768. },
  769. cacheEnabled: false,
  770. projectDir: path.join(__dirname, 'fixture/babelrc')
  771. });
  772. api.on('run', plan => {
  773. plan.status.on('stateChange', evt => {
  774. if (evt.type === 'uncaught-exception') {
  775. t.is(evt.err.name, 'SyntaxError');
  776. }
  777. });
  778. });
  779. return api.run();
  780. });
  781. test('babel.testOptions with extends still merges plugins with .babelrc', t => {
  782. t.plan(3);
  783. const api = apiCreator({
  784. babelConfig: {
  785. testOptions: {
  786. plugins: [testCapitalizerPlugin],
  787. extends: path.join(__dirname, 'fixture/babelrc/.alt-babelrc')
  788. }
  789. },
  790. cacheEnabled: false,
  791. projectDir: path.join(__dirname, 'fixture/babelrc')
  792. });
  793. api.on('run', plan => {
  794. plan.status.on('stateChange', evt => {
  795. if (evt.type === 'test-passed') {
  796. t.ok(evt.title === 'BAR' || evt.title === 'repeated test: bar');
  797. }
  798. });
  799. });
  800. return api.run()
  801. .then(runStatus => {
  802. t.is(runStatus.stats.passedTests, 2);
  803. });
  804. });
  805. test('extended config can disable ava/stage-4', t => {
  806. t.plan(1);
  807. const api = apiCreator({
  808. babelConfig: {
  809. testOptions: {
  810. babelrc: false,
  811. extends: path.join(__dirname, 'fixture/babelrc/disable-stage-4.babelrc')
  812. }
  813. },
  814. cacheEnabled: false,
  815. projectDir: path.join(__dirname, 'fixture/babelrc')
  816. });
  817. api.on('run', plan => {
  818. plan.status.on('stateChange', evt => {
  819. if (evt.type === 'uncaught-exception') {
  820. t.is(evt.err.name, 'SyntaxError');
  821. }
  822. });
  823. });
  824. return api.run();
  825. }, {skip: true}); // FIXME https://github.com/babel/babel/issues/7920
  826. test('babel can be disabled for particular files', t => {
  827. t.plan(1);
  828. const api = apiCreator({
  829. babelConfig: {
  830. testOptions: {
  831. babelrc: false,
  832. ignore: [path.join(__dirname, 'fixture/babelrc/test.js')]
  833. }
  834. },
  835. cacheEnabled: false,
  836. projectDir: path.join(__dirname, 'fixture/babelrc')
  837. });
  838. api.on('run', plan => {
  839. plan.status.on('stateChange', evt => {
  840. if (evt.type === 'uncaught-exception') {
  841. t.is(evt.err.name, 'SyntaxError');
  842. }
  843. });
  844. });
  845. return api.run();
  846. });
  847. test('uses "development" Babel environment if NODE_ENV is the empty string', t => {
  848. t.plan(2);
  849. const api = apiCreator({
  850. cacheEnabled: false,
  851. projectDir: path.join(__dirname, 'fixture/babelrc')
  852. });
  853. api.on('run', plan => {
  854. plan.status.on('stateChange', evt => {
  855. if (evt.type === 'test-passed') {
  856. t.is(evt.title, 'FOO');
  857. }
  858. });
  859. });
  860. return withNodeEnv('', () => api.run())
  861. .then(runStatus => {
  862. t.is(runStatus.stats.passedTests, 1);
  863. });
  864. });
  865. test('full extensions take precedence over enhancements-only', t => {
  866. t.plan(2);
  867. const api = apiCreator({
  868. babelConfig: {
  869. testOptions: {
  870. plugins: [testCapitalizerPlugin]
  871. }
  872. },
  873. extensions: {
  874. all: ['foo.bar', 'bar'],
  875. enhancementsOnly: ['bar'],
  876. full: ['foo.bar']
  877. },
  878. cacheEnabled: false,
  879. projectDir: path.join(__dirname, 'fixture/extensions')
  880. });
  881. api.on('run', plan => {
  882. plan.status.on('stateChange', evt => {
  883. if (evt.type === 'test-passed') {
  884. t.ok(evt.title === 'FOO');
  885. }
  886. });
  887. });
  888. return api.run()
  889. .then(runStatus => {
  890. t.is(runStatus.stats.passedTests, 1);
  891. });
  892. });
  893. test('using --match with matching tests will only report those passing tests', t => {
  894. t.plan(3);
  895. const api = apiCreator({
  896. match: ['this test will match']
  897. });
  898. api.on('run', plan => {
  899. plan.status.on('stateChange', evt => {
  900. if (evt.type === 'selected-test') {
  901. t.match(evt.testFile, /match-no-match-2/);
  902. t.is(evt.title, 'this test will match');
  903. }
  904. });
  905. });
  906. return api.run([
  907. path.join(__dirname, 'fixture/match-no-match.js'),
  908. path.join(__dirname, 'fixture/match-no-match-2.js'),
  909. path.join(__dirname, 'fixture/test-count.js')
  910. ]).then(runStatus => {
  911. t.is(runStatus.stats.passedTests, 1);
  912. });
  913. });
  914. function generatePassDebugTests(execArgv, expectedInspectIndex) {
  915. test(`pass ${execArgv.join(' ')} to fork`, t => {
  916. const api = apiCreator({testOnlyExecArgv: execArgv});
  917. return api._computeForkExecArgv()
  918. .then(result => {
  919. t.true(result.length === execArgv.length);
  920. if (expectedInspectIndex === -1) {
  921. t.true(/--debug=\d+/.test(result[0]));
  922. } else {
  923. t.true(/--inspect=\d+/.test(result[expectedInspectIndex]));
  924. }
  925. });
  926. });
  927. }
  928. function generatePassDebugIntegrationTests(execArgv) {
  929. test(`pass ${execArgv.join(' ')} to fork`, t => {
  930. const api = apiCreator({testOnlyExecArgv: execArgv});
  931. return api.run([path.join(__dirname, 'fixture/debug-arg.js')])
  932. .then(runStatus => {
  933. t.is(runStatus.stats.passedTests, 1);
  934. });
  935. });
  936. }
  937. function generatePassInspectIntegrationTests(execArgv) {
  938. test(`pass ${execArgv.join(' ')} to fork`, t => {
  939. const api = apiCreator({testOnlyExecArgv: execArgv});
  940. return api.run([path.join(__dirname, 'fixture/inspect-arg.js')])
  941. .then(runStatus => {
  942. t.is(runStatus.stats.passedTests, 1);
  943. });
  944. });
  945. }
  946. generatePassDebugTests(['--debug=0'], -1);
  947. generatePassDebugTests(['--debug'], -1);
  948. generatePassDebugTests(['--inspect=0'], 0);
  949. generatePassDebugTests(['--inspect'], 0);
  950. // --inspect takes precedence
  951. generatePassDebugTests(['--inspect=0', '--debug-brk'], 0);
  952. generatePassDebugTests(['--inspect', '--debug-brk'], 0);
  953. // --inspect takes precedence, though --debug-brk is still passed to the worker
  954. generatePassDebugTests(['--debug-brk', '--inspect=0'], 1);
  955. generatePassDebugTests(['--debug-brk', '--inspect'], 1);
  956. if (Number(process.version.split('.')[0].slice(1)) < 8) {
  957. generatePassDebugIntegrationTests(['--debug=0']);
  958. generatePassDebugIntegrationTests(['--debug']);
  959. } else {
  960. generatePassInspectIntegrationTests(['--inspect=9229']);
  961. generatePassInspectIntegrationTests(['--inspect']);
  962. }
  963. test('`esm` package support', t => {
  964. const api = apiCreator({
  965. require: [require.resolve('esm')]
  966. });
  967. return api.run([path.join(__dirname, 'fixture/esm-pkg/test.js')])
  968. .then(runStatus => {
  969. t.is(runStatus.stats.passedTests, 1);
  970. });
  971. });