• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1import * as common from '../common/index.mjs';
2import * as fixtures from '../common/fixtures.mjs';
3import { join } from 'node:path';
4import { describe, it, run } from 'node:test';
5import { dot, spec, tap } from 'node:test/reporters';
6import assert from 'node:assert';
7
8const testFixtures = fixtures.path('test-runner');
9
10describe('require(\'node:test\').run', { concurrency: true }, () => {
11
12  it('should run with no tests', async () => {
13    const stream = run({ files: [] });
14    stream.on('test:fail', common.mustNotCall());
15    stream.on('test:pass', common.mustNotCall());
16    // eslint-disable-next-line no-unused-vars
17    for await (const _ of stream);
18  });
19
20  it('should fail with non existing file', async () => {
21    const stream = run({ files: ['a-random-file-that-does-not-exist.js'] });
22    stream.on('test:fail', common.mustCall(1));
23    stream.on('test:pass', common.mustNotCall());
24    // eslint-disable-next-line no-unused-vars
25    for await (const _ of stream);
26  });
27
28  it('should succeed with a file', async () => {
29    const stream = run({ files: [join(testFixtures, 'test/random.cjs')] });
30    stream.on('test:fail', common.mustNotCall());
31    stream.on('test:pass', common.mustCall(1));
32    // eslint-disable-next-line no-unused-vars
33    for await (const _ of stream);
34  });
35
36  it('should run same file twice', async () => {
37    const stream = run({ files: [join(testFixtures, 'test/random.cjs'), join(testFixtures, 'test/random.cjs')] });
38    stream.on('test:fail', common.mustNotCall());
39    stream.on('test:pass', common.mustCall(2));
40    // eslint-disable-next-line no-unused-vars
41    for await (const _ of stream);
42  });
43
44  it('should run a failed test', async () => {
45    const stream = run({ files: [testFixtures] });
46    stream.on('test:fail', common.mustCall(1));
47    stream.on('test:pass', common.mustNotCall());
48    // eslint-disable-next-line no-unused-vars
49    for await (const _ of stream);
50  });
51
52  it('should support timeout', async () => {
53    const stream = run({ timeout: 50, files: [
54      fixtures.path('test-runner', 'never_ending_sync.js'),
55      fixtures.path('test-runner', 'never_ending_async.js'),
56    ] });
57    stream.on('test:fail', common.mustCall(2));
58    stream.on('test:pass', common.mustNotCall());
59    // eslint-disable-next-line no-unused-vars
60    for await (const _ of stream);
61  });
62
63  it('should validate files', async () => {
64    [Symbol(), {}, () => {}, 0, 1, 0n, 1n, '', '1', Promise.resolve([]), true, false]
65      .forEach((files) => assert.throws(() => run({ files }), {
66        code: 'ERR_INVALID_ARG_TYPE'
67      }));
68  });
69
70  it('should be piped with dot', async () => {
71    const result = await run({ files: [join(testFixtures, 'test/random.cjs')] }).compose(dot).toArray();
72    assert.deepStrictEqual(result, [
73      '.',
74      '\n',
75    ]);
76  });
77
78  it('should be piped with spec', async () => {
79    const specReporter = new spec();
80    const result = await run({ files: [join(testFixtures, 'test/random.cjs')] }).compose(specReporter).toArray();
81    const stringResults = result.map((bfr) => bfr.toString());
82    assert.match(stringResults[0], /this should pass/);
83    assert.match(stringResults[1], /tests 1/);
84    assert.match(stringResults[1], /pass 1/);
85  });
86
87  it('should be piped with tap', async () => {
88    const result = await run({ files: [join(testFixtures, 'test/random.cjs')] }).compose(tap).toArray();
89    assert.strictEqual(result.length, 13);
90    assert.strictEqual(result[0], 'TAP version 13\n');
91    assert.strictEqual(result[1], '# Subtest: this should pass\n');
92    assert.strictEqual(result[2], 'ok 1 - this should pass\n');
93    assert.match(result[3], /duration_ms: \d+\.?\d*/);
94    assert.strictEqual(result[4], '1..1\n');
95    assert.strictEqual(result[5], '# tests 1\n');
96    assert.strictEqual(result[6], '# suites 0\n');
97    assert.strictEqual(result[7], '# pass 1\n');
98    assert.strictEqual(result[8], '# fail 0\n');
99    assert.strictEqual(result[9], '# cancelled 0\n');
100    assert.strictEqual(result[10], '# skipped 0\n');
101    assert.strictEqual(result[11], '# todo 0\n');
102    assert.match(result[12], /# duration_ms \d+\.?\d*/);
103  });
104
105  it('should skip tests not matching testNamePatterns - RegExp', async () => {
106    const result = await run({ files: [join(testFixtures, 'test/skip_by_name.cjs')], testNamePatterns: [/executed/] })
107      .compose(tap)
108      .toArray();
109    assert.strictEqual(result[2], 'ok 1 - this should be skipped # SKIP test name does not match pattern\n');
110    assert.strictEqual(result[5], 'ok 2 - this should be executed\n');
111  });
112
113  it('should skip tests not matching testNamePatterns - string', async () => {
114    const result = await run({ files: [join(testFixtures, 'test/skip_by_name.cjs')], testNamePatterns: ['executed'] })
115      .compose(tap)
116      .toArray();
117    assert.strictEqual(result[2], 'ok 1 - this should be skipped # SKIP test name does not match pattern\n');
118    assert.strictEqual(result[5], 'ok 2 - this should be executed\n');
119  });
120
121  it('should stop watch mode when abortSignal aborts', async () => {
122    const controller = new AbortController();
123    const result = await run({ files: [join(testFixtures, 'test/random.cjs')], watch: true, signal: controller.signal })
124      .compose(async function* (source) {
125        for await (const chunk of source) {
126          if (chunk.type === 'test:pass') {
127            controller.abort();
128            yield chunk.data.name;
129          }
130        }
131      })
132      .toArray();
133    assert.deepStrictEqual(result, ['this should pass']);
134  });
135
136  it('should emit "test:watch:drained" event on watch mode', async () => {
137    const controller = new AbortController();
138    await run({ files: [join(testFixtures, 'test/random.cjs')], watch: true, signal: controller.signal })
139      .on('data', function({ type }) {
140        if (type === 'test:watch:drained') {
141          controller.abort();
142        }
143      });
144  });
145});
146