1'use strict'; 2const common = require('../common'); 3 4if (common.isIBMi) 5 common.skip('IBMi does not support `fs.watch()`'); 6 7const { watch } = require('fs/promises'); 8const fs = require('fs'); 9const assert = require('assert'); 10const { join } = require('path'); 11const tmpdir = require('../common/tmpdir'); 12 13class WatchTestCase { 14 constructor(shouldInclude, dirName, fileName, field) { 15 this.dirName = dirName; 16 this.fileName = fileName; 17 this.field = field; 18 this.shouldSkip = !shouldInclude; 19 } 20 get dirPath() { return join(tmpdir.path, this.dirName); } 21 get filePath() { return join(this.dirPath, this.fileName); } 22} 23 24const kCases = [ 25 // Watch on a directory should callback with a filename on supported systems 26 new WatchTestCase( 27 common.isLinux || common.isOSX || common.isWindows || common.isAIX, 28 'watch1', 29 'foo', 30 'filePath' 31 ), 32 // Watch on a file should callback with a filename on supported systems 33 new WatchTestCase( 34 common.isLinux || common.isOSX || common.isWindows, 35 'watch2', 36 'bar', 37 'dirPath' 38 ), 39]; 40 41tmpdir.refresh(); 42 43for (const testCase of kCases) { 44 if (testCase.shouldSkip) continue; 45 fs.mkdirSync(testCase.dirPath); 46 // Long content so it's actually flushed. 47 const content1 = Date.now() + testCase.fileName.toLowerCase().repeat(1e4); 48 fs.writeFileSync(testCase.filePath, content1); 49 50 let interval; 51 async function test() { 52 const watcher = watch(testCase[testCase.field]); 53 for await (const { eventType, filename } of watcher) { 54 clearInterval(interval); 55 assert.strictEqual(['rename', 'change'].includes(eventType), true); 56 assert.strictEqual(filename, testCase.fileName); 57 break; 58 } 59 60 // Waiting on it again is a non-op 61 // eslint-disable-next-line no-unused-vars 62 for await (const p of watcher) { 63 assert.fail('should not run'); 64 } 65 } 66 67 // Long content so it's actually flushed. toUpperCase so there's real change. 68 const content2 = Date.now() + testCase.fileName.toUpperCase().repeat(1e4); 69 interval = setInterval(() => { 70 fs.writeFileSync(testCase.filePath, ''); 71 fs.writeFileSync(testCase.filePath, content2); 72 }, 100); 73 74 test().then(common.mustCall()); 75} 76 77assert.rejects( 78 async () => { 79 // eslint-disable-next-line no-unused-vars, no-empty 80 for await (const _ of watch(1)) { } 81 }, 82 { code: 'ERR_INVALID_ARG_TYPE' }); 83 84assert.rejects( 85 async () => { 86 // eslint-disable-next-line no-unused-vars, no-empty 87 for await (const _ of watch(__filename, 1)) { } 88 }, 89 { code: 'ERR_INVALID_ARG_TYPE' }); 90 91assert.rejects( 92 async () => { 93 // eslint-disable-next-line no-unused-vars, no-empty 94 for await (const _ of watch('', { persistent: 1 })) { } 95 }, 96 { code: 'ERR_INVALID_ARG_TYPE' }); 97 98assert.rejects( 99 async () => { 100 // eslint-disable-next-line no-unused-vars, no-empty 101 for await (const _ of watch('', { recursive: 1 })) { } 102 }, 103 { code: 'ERR_INVALID_ARG_TYPE' }); 104 105assert.rejects( 106 async () => { 107 // eslint-disable-next-line no-unused-vars, no-empty 108 for await (const _ of watch('', { encoding: 1 })) { } 109 }, 110 { code: 'ERR_INVALID_ARG_VALUE' }); 111 112assert.rejects( 113 async () => { 114 // eslint-disable-next-line no-unused-vars, no-empty 115 for await (const _ of watch('', { signal: 1 })) { } 116 }, 117 { code: 'ERR_INVALID_ARG_TYPE' }); 118 119(async () => { 120 const ac = new AbortController(); 121 const { signal } = ac; 122 setImmediate(() => ac.abort()); 123 try { 124 // eslint-disable-next-line no-unused-vars, no-empty 125 for await (const _ of watch(__filename, { signal })) { } 126 } catch (err) { 127 assert.strictEqual(err.name, 'AbortError'); 128 } 129})().then(common.mustCall()); 130