• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Flags: --expose-internals
2import * as common from '../common/index.mjs';
3import path from 'node:path';
4import { describe, it } from 'node:test';
5import { spawn } from 'node:child_process';
6import { writeFileSync } from 'node:fs';
7import util from 'internal/util';
8import tmpdir from '../common/tmpdir.js';
9
10
11if (common.isIBMi)
12  common.skip('IBMi does not support `fs.watch()`');
13
14tmpdir.refresh();
15
16// This test updates these files repeatedly,
17// Reading them from disk is unreliable due to race conditions.
18const fixtureContent = {
19  'dependency.js': 'module.exports = {};',
20  'dependency.mjs': 'export const a = 1;',
21  'test.js': `
22const test = require('node:test');
23require('./dependency.js');
24import('./dependency.mjs');
25import('data:text/javascript,');
26test('test has ran');`,
27};
28const fixturePaths = Object.keys(fixtureContent)
29  .reduce((acc, file) => ({ ...acc, [file]: path.join(tmpdir.path, file) }), {});
30Object.entries(fixtureContent)
31  .forEach(([file, content]) => writeFileSync(fixturePaths[file], content));
32
33async function testWatch({ fileToUpdate, file }) {
34  const ran1 = util.createDeferredPromise();
35  const ran2 = util.createDeferredPromise();
36  const child = spawn(process.execPath,
37                      ['--watch', '--test', file ? fixturePaths[file] : undefined].filter(Boolean),
38                      { encoding: 'utf8', stdio: 'pipe', cwd: tmpdir.path });
39  let stdout = '';
40
41  child.stdout.on('data', (data) => {
42    stdout += data.toString();
43    const testRuns = stdout.match(/ - test has ran/g);
44    if (testRuns?.length >= 1) ran1.resolve();
45    if (testRuns?.length >= 2) ran2.resolve();
46  });
47
48  await ran1.promise;
49  const content = fixtureContent[fileToUpdate];
50  const path = fixturePaths[fileToUpdate];
51  const interval = setInterval(() => writeFileSync(path, content), common.platformTimeout(1000));
52  await ran2.promise;
53  clearInterval(interval);
54  child.kill();
55}
56
57describe('test runner watch mode', () => {
58  it('should run tests repeatedly', async () => {
59    await testWatch({ file: 'test.js', fileToUpdate: 'test.js' });
60  });
61
62  it('should run tests with dependency repeatedly', async () => {
63    await testWatch({ file: 'test.js', fileToUpdate: 'dependency.js' });
64  });
65
66  it('should run tests with ESM dependency', async () => {
67    await testWatch({ file: 'test.js', fileToUpdate: 'dependency.mjs' });
68  });
69
70  it('should support running tests without a file', async () => {
71    await testWatch({ fileToUpdate: 'test.js' });
72  });
73});
74