• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1'use strict';
2const common = require('../common');
3
4if (common.isIBMi)
5  common.skip('IBMi does not support `fs.watch()`');
6
7// Tests if `filename` is provided to watcher on supported platforms
8
9const fs = require('fs');
10const assert = require('assert');
11const { join } = require('path');
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 cases = [
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
41const tmpdir = require('../common/tmpdir');
42tmpdir.refresh();
43
44for (const testCase of cases) {
45  if (testCase.shouldSkip) continue;
46  fs.mkdirSync(testCase.dirPath);
47  // Long content so it's actually flushed.
48  const content1 = Date.now() + testCase.fileName.toLowerCase().repeat(1e4);
49  fs.writeFileSync(testCase.filePath, content1);
50
51  let interval;
52  const pathToWatch = testCase[testCase.field];
53  const watcher = fs.watch(pathToWatch);
54  watcher.on('error', (err) => {
55    if (interval) {
56      clearInterval(interval);
57      interval = null;
58    }
59    assert.fail(err);
60  });
61  watcher.on('close', common.mustCall(() => {
62    watcher.close(); // Closing a closed watcher should be a noop
63  }));
64  watcher.on('change', common.mustCall(function(eventType, argFilename) {
65    if (interval) {
66      clearInterval(interval);
67      interval = null;
68    }
69    if (common.isOSX)
70      assert.strictEqual(['rename', 'change'].includes(eventType), true);
71    else
72      assert.strictEqual(eventType, 'change');
73    assert.strictEqual(argFilename, testCase.fileName);
74
75    watcher.close();
76
77    // We document that watchers cannot be used anymore when it's closed,
78    // here we turn the methods into noops instead of throwing
79    watcher.close(); // Closing a closed watcher should be a noop
80  }));
81
82  // Long content so it's actually flushed. toUpperCase so there's real change.
83  const content2 = Date.now() + testCase.fileName.toUpperCase().repeat(1e4);
84  interval = setInterval(() => {
85    fs.writeFileSync(testCase.filePath, '');
86    fs.writeFileSync(testCase.filePath, content2);
87  }, 100);
88}
89
90[false, 1, {}, [], null, undefined].forEach((input) => {
91  assert.throws(
92    () => fs.watch(input, common.mustNotCall()),
93    {
94      code: 'ERR_INVALID_ARG_TYPE',
95      name: 'TypeError'
96    }
97  );
98});
99