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