diff -Nru node-chokidar-3.3.1/debian/changelog node-chokidar-3.4.0/debian/changelog --- node-chokidar-3.3.1/debian/changelog 2020-02-13 10:09:44.000000000 +0000 +++ node-chokidar-3.4.0/debian/changelog 2020-05-25 06:23:54.000000000 +0000 @@ -1,3 +1,10 @@ +node-chokidar (3.4.0-1) unstable; urgency=medium + + * Team upload + * New upstream version 3.4.0 + + -- Xavier Guimard Mon, 25 May 2020 08:23:54 +0200 + node-chokidar (3.3.1-2) unstable; urgency=medium * Team upload diff -Nru node-chokidar-3.3.1/.github/full_changelog.md node-chokidar-3.4.0/.github/full_changelog.md --- node-chokidar-3.3.1/.github/full_changelog.md 2019-12-15 18:31:50.000000000 +0000 +++ node-chokidar-3.4.0/.github/full_changelog.md 2020-04-26 14:20:10.000000000 +0000 @@ -1,3 +1,10 @@ +### Chokidar 3.4.0 (Apr 26, 2020) +* Support for directory-based symlinks. +* Fix a case on macos when replacing a file with a dir of the same name + emitted invalid events. +* Fix error swallowing inside `.on()` event handlers +* Known issue: `followSymlinks: false` on macos still follows symlinked directories + ### Chokidar 3.3.0 (Nov 2, 2019) * `FSWatcher#close()` method became async. This ensures IO operations are finished properly and fixes a few segfaulty crashes. diff -Nru node-chokidar-3.3.1/.github/workflows/nodejs.yml node-chokidar-3.4.0/.github/workflows/nodejs.yml --- node-chokidar-3.3.1/.github/workflows/nodejs.yml 2019-12-15 18:31:50.000000000 +0000 +++ node-chokidar-3.4.0/.github/workflows/nodejs.yml 2020-04-26 14:20:10.000000000 +0000 @@ -10,7 +10,7 @@ strategy: fail-fast: false matrix: - node-version: [8, 10, 12] + node-version: [8, 10, 12, 14] os: [ubuntu-latest, windows-latest, macOS-latest] steps: diff -Nru node-chokidar-3.3.1/index.js node-chokidar-3.4.0/index.js --- node-chokidar-3.3.1/index.js 2019-12-15 18:31:50.000000000 +0000 +++ node-chokidar-3.4.0/index.js 2020-04-26 14:20:10.000000000 +0000 @@ -152,12 +152,13 @@ const {items} = this; if (!items) return; items.delete(item); + if (items.size > 0) return; - if (!items.size) { - const dir = this.path; - try { - await readdir(dir); - } catch (err) { + const dir = this.path; + try { + await readdir(dir); + } catch (err) { + if (this._removeWatcher) { this._removeWatcher(sysPath.dirname(dir), sysPath.basename(dir)); } } @@ -483,7 +484,7 @@ * @returns {Promise}. */ close() { - if (this.closed) return this; + if (this.closed) return this._closePromise; this.closed = true; // Memory management. @@ -501,7 +502,9 @@ ['closers', 'watched', 'streams', 'symlinkPaths', 'throttled'].forEach(key => { this[`_${key}`].clear(); }); - return closers.length ? Promise.all(closers).then(() => undefined) : Promise.resolve(); + + this._closePromise = closers.length ? Promise.all(closers).then(() => undefined) : Promise.resolve(); + return this._closePromise; } /** @@ -602,16 +605,15 @@ (event === EV_ADD || event === EV_ADD_DIR || event === EV_CHANGE) ) { const fullPath = opts.cwd ? sysPath.join(opts.cwd, path) : path; + let stats; try { - const stats = await stat(fullPath); - // Suppress event when fs_stat fails, to avoid sending undefined 'stat' - if (!stats) return; - args.push(stats); - this.emitWithAll(event, args); + stats = await stat(fullPath); } catch (err) {} - } else { - this.emitWithAll(event, args); + // Suppress event when fs_stat fails, to avoid sending undefined 'stat' + if (!stats || this.closed) return; + args.push(stats); } + this.emitWithAll(event, args); return this; } @@ -820,13 +822,15 @@ * @param {String} item base path of item/directory * @returns {void} */ -_remove(directory, item) { +_remove(directory, item, isDirectory) { // if what is being deleted is a directory, get that directory's paths // for recursive deleting and cleaning of watched object // if it is not a directory, nestedDirectoryChildren will be empty array const path = sysPath.join(directory, item); const fullPath = sysPath.resolve(path); - const isDirectory = this._watched.has(path) || this._watched.has(fullPath); + isDirectory = isDirectory != null + ? isDirectory + : this._watched.has(path) || this._watched.has(fullPath); // prevent duplicate handling in case of arriving here nearly simultaneously // via multiple paths (such as _handleFile and _handleDir) diff -Nru node-chokidar-3.3.1/lib/constants.js node-chokidar-3.4.0/lib/constants.js --- node-chokidar-3.3.1/lib/constants.js 2019-12-15 18:31:50.000000000 +0000 +++ node-chokidar-3.4.0/lib/constants.js 2020-04-26 14:20:10.000000000 +0000 @@ -23,6 +23,7 @@ exports.FSEVENT_MOVED = 'moved'; exports.FSEVENT_CLONED = 'cloned'; exports.FSEVENT_UNKNOWN = 'unknown'; +exports.FSEVENT_TYPE_FILE = 'file'; exports.FSEVENT_TYPE_DIRECTORY = 'directory'; exports.FSEVENT_TYPE_SYMLINK = 'symlink'; diff -Nru node-chokidar-3.3.1/lib/fsevents-handler.js node-chokidar-3.4.0/lib/fsevents-handler.js --- node-chokidar-3.3.1/lib/fsevents-handler.js 2019-12-15 18:31:50.000000000 +0000 +++ node-chokidar-3.4.0/lib/fsevents-handler.js 2020-04-26 14:20:10.000000000 +0000 @@ -37,6 +37,7 @@ FSEVENT_MOVED, // FSEVENT_CLONED, FSEVENT_UNKNOWN, + FSEVENT_TYPE_FILE, FSEVENT_TYPE_DIRECTORY, FSEVENT_TYPE_SYMLINK, @@ -47,13 +48,10 @@ EMPTY_FN, IDENTITY_FN } = require('./constants'); -const FS_MODE_READ = 'r'; const Depth = (value) => isNaN(value) ? {} : {depth: value}; const stat = promisify(fs.stat); -const open = promisify(fs.open); -const close = promisify(fs.close); const lstat = promisify(fs.lstat); const realpath = promisify(fs.realpath); @@ -202,6 +200,14 @@ return i; }; +// returns boolean indicating whether the fsevents' event info has the same type +// as the one returned by fs.stat +const sameTypes = (info, stats) => ( + info.type === FSEVENT_TYPE_DIRECTORY && stats.isDirectory() || + info.type === FSEVENT_TYPE_SYMLINK && stats.isSymbolicLink() || + info.type === FSEVENT_TYPE_FILE && stats.isFile() +) + /** * @mixin */ @@ -232,13 +238,16 @@ this.handleEvent(event, path, fullPath, realPath, parent, watchedDir, item, info, opts); } -async checkFd(path, fullPath, realPath, parent, watchedDir, item, info, opts) { +async checkExists(path, fullPath, realPath, parent, watchedDir, item, info, opts) { try { - const fd = await open(path, FS_MODE_READ); + const stats = await stat(path) if (this.fsw.closed) return; - await close(fd); if (this.fsw.closed) return; - this.addOrChange(path, fullPath, realPath, parent, watchedDir, item, info, opts); + if (sameTypes(info, stats)) { + this.addOrChange(path, fullPath, realPath, parent, watchedDir, item, info, opts); + } else { + this.handleEvent(EV_UNLINK, path, fullPath, realPath, parent, watchedDir, item, info, opts); + } } catch (error) { if (error.code === 'EACCES') { this.addOrChange(path, fullPath, realPath, parent, watchedDir, item, info, opts); @@ -252,9 +261,10 @@ if (this.fsw.closed || this.checkIgnored(path)) return; if (event === EV_UNLINK) { + const isDirectory = info.type === FSEVENT_TYPE_DIRECTORY // suppress unlink events on never before seen files - if (info.type === FSEVENT_TYPE_DIRECTORY || watchedDir.has(item)) { - this.fsw._remove(parent, item); + if (isDirectory || watchedDir.has(item)) { + this.fsw._remove(parent, item, isDirectory); } } else { if (event === EV_ADD) { @@ -319,13 +329,13 @@ } catch (error) {} if (this.fsw.closed) return; if (this.checkIgnored(path, stats)) return; - if (stats) { + if (sameTypes(info, stats)) { this.addOrChange(path, fullPath, realPath, parent, watchedDir, item, info, opts); } else { this.handleEvent(EV_UNLINK, path, fullPath, realPath, parent, watchedDir, item, info, opts); } } else { - this.checkFd(path, fullPath, realPath, parent, watchedDir, item, info, opts); + this.checkExists(path, fullPath, realPath, parent, watchedDir, item, info, opts); } } else { switch (info.event) { @@ -334,7 +344,7 @@ return this.addOrChange(path, fullPath, realPath, parent, watchedDir, item, info, opts); case FSEVENT_DELETED: case FSEVENT_MOVED: - return this.checkFd(path, fullPath, realPath, parent, watchedDir, item, info, opts); + return this.checkExists(path, fullPath, realPath, parent, watchedDir, item, info, opts); } } }; diff -Nru node-chokidar-3.3.1/package.json node-chokidar-3.4.0/package.json --- node-chokidar-3.3.1/package.json 2019-12-15 18:31:50.000000000 +0000 +++ node-chokidar-3.4.0/package.json 2020-04-26 14:20:10.000000000 +0000 @@ -1,7 +1,7 @@ { "name": "chokidar", "description": "A neat wrapper around node.js fs.watch / fs.watchFile / fsevents.", - "version": "3.3.1", + "version": "3.4.0", "homepage": "https://github.com/paulmillr/chokidar", "author": "Paul Miller (https://paulmillr.com)", "contributors": [ @@ -19,20 +19,20 @@ "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", - "readdirp": "~3.3.0" + "readdirp": "~3.4.0" }, "optionalDependencies": { "fsevents": "~2.1.2" }, "devDependencies": { - "@types/node": "^12", + "@types/node": "^13", "chai": "^4.2", - "dtslint": "^2.0.0", + "dtslint": "^3.3.0", "eslint": "^6.6.0", - "mocha": "^6.2.2", - "nyc": "^14.1.1", + "mocha": "^7.0.0", + "nyc": "^15.0.0", "rimraf": "^3.0.0", - "sinon": "^7.5.0", + "sinon": "^9.0.1", "sinon-chai": "^3.3.0", "upath": "^1.2.0" }, diff -Nru node-chokidar-3.3.1/README.md node-chokidar-3.4.0/README.md --- node-chokidar-3.3.1/README.md 2019-12-15 18:31:50.000000000 +0000 +++ node-chokidar-3.4.0/README.md 2020-04-26 14:20:10.000000000 +0000 @@ -114,7 +114,7 @@ var watchedPaths = watcher.getWatched(); // Un-watch some files. -watcher.unwatch('new-file*'); +await watcher.unwatch('new-file*'); // Stop watching. // The method is async! @@ -253,8 +253,8 @@ `raw`, `error`. Additionally `all` is available which gets emitted with the underlying event name and path for every event other than `ready`, `raw`, and `error`. `raw` is internal, use it carefully. -* `.unwatch(path / paths)`: Stop watching files, directories, or glob patterns. -Takes an array of strings or just one string. +* `.unwatch(path / paths)`: **async** Stop watching files, directories, or glob patterns. +Takes an array of strings or just one string. Use with `await` to ensure bugs don't happen. * `.close()`: Removes all listeners from watched files. Asynchronous, returns Promise. * `.getWatched()`: Returns an object representing all the paths on the file system being watched by this `FSWatcher` instance. The object's keys are all the @@ -286,8 +286,8 @@ ## Changelog For more detailed changelog, see [`full_changelog.md`](.github/full_changelog.md). - -- **v3.3 (Nov 2, 2019):** `FSWatcher#close()` method became async. +- **v3.4 (Apr 26, 2020):** Support for directory-based symlinks. Macos file replacement fixes. +- **v3.3 (Nov 2, 2019):** `FSWatcher#close()` method became async. That fixes IO race conditions related to close method. - **v3.2 (Oct 1, 2019):** Improve Linux RAM usage by 50%. Race condition fixes. Windows glob fixes. Improve stability by using tight range of dependency versions. - **v3.1 (Sep 16, 2019):** dotfiles are no longer filtered out by default. Use `ignored` option if needed. Improve initial Linux scan time by 50%. - **v3 (Apr 30, 2019):** massive CPU & RAM consumption improvements; reduces deps / package size by a factor of 17x and bumps Node.js requirement to v8.16 and higher. diff -Nru node-chokidar-3.3.1/test.js node-chokidar-3.4.0/test.js --- node-chokidar-3.3.1/test.js 2019-12-15 18:31:50.000000000 +0000 +++ node-chokidar-3.4.0/test.js 2020-04-26 14:20:10.000000000 +0000 @@ -11,7 +11,9 @@ const sinon = require('sinon'); const sinonChai = require('sinon-chai'); const upath = require('upath'); -const {isWindows, isMacos} = require('./lib/constants'); + +const {isWindows, isMacos, EV_ALL, EV_READY, EV_ADD, EV_CHANGE, EV_ADD_DIR, EV_UNLINK, EV_UNLINK_DIR, EV_RAW, EV_ERROR} + = require('./lib/constants'); const chokidar = require('.'); const {expect} = chai; @@ -44,20 +46,20 @@ if (spy == null) spy = sinon.spy(); return new Promise((resolve, reject) => { const handler = noStat ? - (eventName === 'all' ? + (eventName === EV_ALL ? (event, path) => spy(event, path) : (path) => spy(path)) : spy; - watcher.on('error', reject); - watcher.on('ready', () => resolve(spy)); + watcher.on(EV_ERROR, reject); + watcher.on(EV_READY, () => resolve(spy)); watcher.on(eventName, handler); }); }; const waitForWatcher = (watcher) => { return new Promise((resolve, reject) => { - watcher.on('error', reject); - watcher.on('ready', resolve); + watcher.on(EV_ERROR, reject); + watcher.on(EV_READY, resolve); }); }; @@ -108,6 +110,8 @@ }); }; +const dateNow = () => Date.now().toString(); + const runTests = (baseopts) => { let macosFswatch; let win32Polling; @@ -135,15 +139,13 @@ options.alwaysStat = true; readySpy = sinon.spy(function readySpy(){}); rawSpy = sinon.spy(function rawSpy(){}); - watcher = chokidar_watch().on('ready', readySpy).on('raw', rawSpy); + watcher = chokidar_watch().on(EV_READY, readySpy).on(EV_RAW, rawSpy); }); afterEach(async () => { await waitFor([readySpy]); readySpy.should.have.been.calledOnce; - /* eslint-disable require-atomic-updates */ readySpy = undefined; rawSpy = undefined; - /* eslint-enable require-atomic-updates */ }); it('should produce an instance of chokidar.FSWatcher', () => { watcher.should.be.an.instanceof(chokidar.FSWatcher); @@ -157,9 +159,9 @@ }); it('should emit `add` event when file was added', async () => { const testPath = getFixturePath('add.txt'); - const spy = await aspy(watcher, 'add'); + const spy = await aspy(watcher, EV_ADD); await delay(); - await write(testPath, Date.now()); + await write(testPath, dateNow()); await waitFor([spy]); spy.should.have.been.calledOnce; spy.should.have.been.calledWith(testPath); @@ -173,25 +175,25 @@ } const spy = sinon.spy(); - watcher.on('add', (path) => { + watcher.on(EV_ADD, (path) => { spy(path); }); await waitForWatcher(watcher); - write(paths[0], Date.now()); - write(paths[1], Date.now()); - write(paths[2], Date.now()); - write(paths[3], Date.now()); - write(paths[4], Date.now()); + write(paths[0], dateNow()); + write(paths[1], dateNow()); + write(paths[2], dateNow()); + write(paths[3], dateNow()); + write(paths[4], dateNow()); await delay(100); - write(paths[5], Date.now()); - write(paths[6], Date.now()); + write(paths[5], dateNow()); + write(paths[6], dateNow()); await delay(150); - write(paths[7], Date.now()); - write(paths[8], Date.now()); + write(paths[7], dateNow()); + write(paths[8], dateNow()); await waitFor([[spy, 4]]); @@ -201,7 +203,7 @@ spy.should.have.been.calledWith(path); }); }); - it.skip('should emit thirtythree `add` events when thirtythree files were added in nine directories', async () => { + it('should emit thirtythree `add` events when thirtythree files were added in nine directories', async () => { await watcher.close(); const test1Path = getFixturePath('add1.txt'); @@ -246,54 +248,54 @@ fs.mkdirSync(getFixturePath('h'), PERM_ARR); fs.mkdirSync(getFixturePath('i'), PERM_ARR); - watcher2 = chokidar_watch().on('ready', readySpy).on('raw', rawSpy); - const spy = await aspy(watcher2, 'add', null, true); + watcher2 = chokidar_watch().on(EV_READY, readySpy).on(EV_RAW, rawSpy); + const spy = await aspy(watcher2, EV_ADD, null, true); - await write(test1Path, Date.now()); - await write(test2Path, Date.now()); - await write(test3Path, Date.now()); - await write(test4Path, Date.now()); - await write(test5Path, Date.now()); + await write(test1Path, dateNow()); + await write(test2Path, dateNow()); + await write(test3Path, dateNow()); + await write(test4Path, dateNow()); + await write(test5Path, dateNow()); await delay(200); - await write(test6Path, Date.now()); - await write(test7Path, Date.now()); - await write(test8Path, Date.now()); - await write(test9Path, Date.now()); + await write(test6Path, dateNow()); + await write(test7Path, dateNow()); + await write(test8Path, dateNow()); + await write(test9Path, dateNow()); await delay(200); - await write(testb1Path, Date.now()); - await write(testb2Path, Date.now()); - await write(testb3Path, Date.now()); - await write(testb4Path, Date.now()); - await write(testb5Path, Date.now()); + await write(testb1Path, dateNow()); + await write(testb2Path, dateNow()); + await write(testb3Path, dateNow()); + await write(testb4Path, dateNow()); + await write(testb5Path, dateNow()); await delay(200); - await write(testb6Path, Date.now()); - await write(testb7Path, Date.now()); - await write(testb8Path, Date.now()); - await write(testb9Path, Date.now()); + await write(testb6Path, dateNow()); + await write(testb7Path, dateNow()); + await write(testb8Path, dateNow()); + await write(testb9Path, dateNow()); await delay(200); - await write(testc1Path, Date.now()); - await write(testc2Path, Date.now()); - await write(testc3Path, Date.now()); - await write(testc4Path, Date.now()); - await write(testc5Path, Date.now()); + await write(testc1Path, dateNow()); + await write(testc2Path, dateNow()); + await write(testc3Path, dateNow()); + await write(testc4Path, dateNow()); + await write(testc5Path, dateNow()); await delay(150); - await write(testc6Path, Date.now()); - await write(testc7Path, Date.now()); - await write(testc8Path, Date.now()); - await write(testc9Path, Date.now()); - await write(testd1Path, Date.now()); - await write(teste1Path, Date.now()); - await write(testf1Path, Date.now()); + await write(testc6Path, dateNow()); + await write(testc7Path, dateNow()); + await write(testc8Path, dateNow()); + await write(testc9Path, dateNow()); + await write(testd1Path, dateNow()); + await write(teste1Path, dateNow()); + await write(testf1Path, dateNow()); await delay(100); - await write(testg1Path, Date.now()); - await write(testh1Path, Date.now()); - await write(testi1Path, Date.now()); + await write(testg1Path, dateNow()); + await write(testh1Path, dateNow()); + await write(testi1Path, dateNow()); await delay(300); await waitFor([[spy, 11]]); @@ -357,9 +359,9 @@ }); it('should emit `change` event when file was changed', async () => { const testPath = getFixturePath('change.txt'); - const spy = await aspy(watcher, 'change'); + const spy = await aspy(watcher, EV_CHANGE); spy.should.not.have.been.called; - await write(testPath, Date.now()); + await write(testPath, dateNow()); await waitFor([spy]); spy.should.have.been.calledWith(testPath); expect(spy.args[0][1]).to.be.ok; // stats @@ -368,7 +370,7 @@ }); it('should emit `unlink` event when file was removed', async () => { const testPath = getFixturePath('unlink.txt'); - const spy = await aspy(watcher, 'unlink'); + const spy = await aspy(watcher, EV_UNLINK); spy.should.not.have.been.called; await fs_unlink(testPath); await waitFor([spy]); @@ -380,7 +382,7 @@ it('should emit `unlinkDir` event when a directory was removed', async () => { const testDir = getFixturePath('subdir'); fs.mkdirSync(testDir, PERM_ARR); - const spy = await aspy(watcher, 'unlinkDir'); + const spy = await aspy(watcher, EV_UNLINK_DIR); await delay(); await fs_rmdir(testDir); @@ -397,7 +399,7 @@ fs.mkdirSync(testDir, PERM_ARR); fs.mkdirSync(testDir2, PERM_ARR); fs.mkdirSync(testDir3, PERM_ARR); - const spy = await aspy(watcher, 'unlinkDir'); + const spy = await aspy(watcher, EV_UNLINK_DIR); await waitFor([spy]); await pRimraf(testDir2); // test removing in one await waitFor([spy]); @@ -412,7 +414,7 @@ const addSpy = sinon.spy(function add(){}); const testPath = getFixturePath('change.txt'); const newPath = getFixturePath('moved.txt'); - watcher.on('unlink', unlinkSpy).on('add', addSpy); + watcher.on(EV_UNLINK, unlinkSpy).on(EV_ADD, addSpy); await waitForWatcher(watcher); unlinkSpy.should.not.have.been.called; addSpy.should.not.have.been.called; @@ -435,9 +437,9 @@ const testPath = getFixturePath('add.txt'); fs.writeFileSync(testPath, 'hello'); watcher - .on('unlink', unlinkSpy) - .on('add', addSpy) - .on('change', changeSpy); + .on(EV_UNLINK, unlinkSpy) + .on(EV_ADD, addSpy) + .on(EV_CHANGE, changeSpy); await waitForWatcher(watcher); unlinkSpy.should.not.have.been.called; addSpy.should.not.have.been.called; @@ -447,7 +449,7 @@ unlinkSpy.should.have.been.calledWith(testPath); await delay(); - await write(testPath, Date.now()); + await write(testPath, dateNow()); await waitFor([addSpy.withArgs(testPath)]); addSpy.should.have.been.calledWith(testPath); changeSpy.should.not.have.been.called; @@ -457,7 +459,7 @@ const testPath = getFixturePath('change.txt'); const newPath1 = getFixturePath('moved.txt'); const newPath2 = getFixturePath('moved-again.txt'); - await aspy(watcher, 'unlink', unlinkSpy); + await aspy(watcher, EV_UNLINK, unlinkSpy); await fs_rename(testPath, newPath1); await delay(300); @@ -475,10 +477,10 @@ it('should notice when a file appears in a new directory', async () => { const testDir = getFixturePath('subdir'); const testPath = getFixturePath('subdir/add.txt'); - const spy = await aspy(watcher, 'add'); + const spy = await aspy(watcher, EV_ADD); spy.should.not.have.been.called; await fs_mkdir(testDir, PERM_ARR); - await write(testPath, Date.now()); + await write(testPath, dateNow()); await waitFor([spy]); spy.should.have.been.calledOnce; spy.should.have.been.calledWith(testPath); @@ -490,7 +492,7 @@ const addSpy = sinon.spy(function addSpy(){}); const parentPath = getFixturePath('subdir2'); const subPath = getFixturePath('subdir2/subsub'); - watcher.on('unlinkDir', unlinkSpy).on('addDir', addSpy); + watcher.on(EV_UNLINK_DIR, unlinkSpy).on(EV_ADD_DIR, addSpy); await waitForWatcher(watcher); await fs_mkdir(parentPath, PERM_ARR); @@ -506,20 +508,58 @@ addSpy.should.have.been.calledWith(parentPath); addSpy.should.have.been.calledWith(subPath); }); + it('should emit `unlinkDir` and `add` when dir is replaced by file', async () => { + options.ignoreInitial = true; + const unlinkSpy = sinon.spy(function unlinkSpy(){}); + const addSpy = sinon.spy(function addSpy(){}); + const testPath = getFixturePath('dirFile'); + await fs_mkdir(testPath, PERM_ARR); + watcher.on(EV_UNLINK_DIR, unlinkSpy).on(EV_ADD, addSpy); + await waitForWatcher(watcher); + + await delay(); + await fs_rmdir(testPath); + await delay(); + await write(testPath, 'file content'); + + await waitFor([unlinkSpy]); + unlinkSpy.should.have.been.calledWith(testPath); + await waitFor([addSpy]); + addSpy.should.have.been.calledWith(testPath); + }); + it('should emit `unlink` and `addDir` when file is replaced by dir', async () => { + options.ignoreInitial = true; + const unlinkSpy = sinon.spy(function unlinkSpy(){}); + const addSpy = sinon.spy(function addSpy(){}); + const testPath = getFixturePath('fileDir'); + await write(testPath, 'file content'); + watcher.on(EV_UNLINK, unlinkSpy).on(EV_ADD_DIR, addSpy); + await waitForWatcher(watcher); + + await delay(); + await fs_unlink(testPath); + await delay(); + await fs_mkdir(testPath, PERM_ARR); + + await waitFor([unlinkSpy]); + unlinkSpy.should.have.been.calledWith(testPath); + await waitFor([addSpy]); + addSpy.should.have.been.calledWith(testPath); + }); }); describe('watch individual files', () => { it('should detect changes', async () => { const testPath = getFixturePath('change.txt'); const watcher = chokidar_watch(testPath, options); - const spy = await aspy(watcher, 'change'); - await write(testPath, Date.now()); + const spy = await aspy(watcher, EV_CHANGE); + await write(testPath, dateNow()); await waitFor([spy]); spy.should.have.always.been.calledWith(testPath); }); it('should detect unlinks', async () => { const testPath = getFixturePath('unlink.txt'); const watcher = chokidar_watch(testPath, options); - const spy = await aspy(watcher, 'unlink'); + const spy = await aspy(watcher, EV_UNLINK); await delay(); await fs_unlink(testPath); @@ -532,8 +572,8 @@ const addSpy = sinon.spy(function addSpy(){}); const testPath = getFixturePath('unlink.txt'); const watcher = chokidar_watch([testPath], options) - .on('unlink', unlinkSpy) - .on('add', addSpy); + .on(EV_UNLINK, unlinkSpy) + .on(EV_ADD, addSpy); await waitForWatcher(watcher); await delay(); @@ -551,13 +591,13 @@ const testPath = getFixturePath('add.txt'); const siblingPath = getFixturePath('change.txt'); const watcher = chokidar_watch(testPath, options); - const spy = await aspy(watcher, 'all'); + const spy = await aspy(watcher, EV_ALL); await delay(); - await write(siblingPath, Date.now()); - await write(testPath, Date.now()); + await write(siblingPath, dateNow()); + await write(testPath, dateNow()); await waitFor([spy]); - spy.should.have.always.been.calledWith('add', testPath); + spy.should.have.always.been.calledWith(EV_ADD, testPath); }); // PR 682 is failing. @@ -569,7 +609,7 @@ fs.mkdirSync(otherDirPath, PERM_ARR); const watcher = chokidar_watch([testPath, otherPath], options); // intentionally for this test don't write fs.writeFileSync(otherPath, 'other'); - const spy = await aspy(watcher, 'unlink'); + const spy = await aspy(watcher, EV_UNLINK); await delay(); await fs_unlink(testPath); @@ -584,8 +624,8 @@ const otherPath = getFixturePath('other.txt'); fs.writeFileSync(otherPath, 'other'); const watcher = chokidar_watch([testPath, otherPath], options) - .on('unlink', unlinkSpy) - .on('add', addSpy); + .on(EV_UNLINK, unlinkSpy) + .on(EV_ADD, addSpy); await waitForWatcher(watcher); await delay(); @@ -610,8 +650,8 @@ fs.mkdirSync(otherDirPath, PERM_ARR); // intentionally for this test don't write fs.writeFileSync(otherPath, 'other'); const watcher = chokidar_watch([testPath, otherPath], options) - .on('unlink', unlinkSpy) - .on('add', addSpy); + .on(EV_UNLINK, unlinkSpy) + .on(EV_ADD, addSpy); await waitForWatcher(watcher); await delay(); @@ -634,8 +674,8 @@ const otherPath = getFixturePath('other.txt'); // intentionally for this test don't write fs.writeFileSync(otherPath, 'other'); const watcher = chokidar_watch([testPath, otherPath], options) - .on('unlink', unlinkSpy) - .on('add', addSpy); + .on(EV_UNLINK, unlinkSpy) + .on(EV_ADD, addSpy); await waitForWatcher(watcher); await delay(); @@ -658,8 +698,8 @@ const otherPath = getFixturePath('other.txt'); fs.writeFileSync(otherPath, 'other'); const watcher = chokidar_watch([testPath, otherPath], options) - .on('unlink', unlinkSpy) - .on('add', addSpy); + .on(EV_UNLINK, unlinkSpy) + .on(EV_ADD, addSpy); await waitForWatcher(watcher); await delay(); @@ -688,8 +728,8 @@ fs.writeFileSync(otherPath, 'other'); // intentionally for this test don't write fs.writeFileSync(other2Path, 'other2'); const watcher = chokidar_watch([testPath, otherPath, other2Path], options) - .on('unlink', unlinkSpy) - .on('add', addSpy); + .on(EV_UNLINK, unlinkSpy) + .on(EV_ADD, addSpy); await waitForWatcher(watcher); await delay(); await fs_unlink(testPath); @@ -713,9 +753,9 @@ const renamedDir = getFixturePath('subdir-renamed'); const expectedPath = sysPath.join(renamedDir, 'add.txt'); await fs_mkdir(testDir, PERM_ARR); - await write(testPath, Date.now()); + await write(testPath, dateNow()); const watcher = chokidar_watch(currentDir, options); - const spy = await aspy(watcher, 'add'); + const spy = await aspy(watcher, EV_ADD); await delay(1000); await fs_rename(testDir, renamedDir); @@ -727,10 +767,10 @@ it('should watch non-existent file and detect add', async () => { const testPath = getFixturePath('add.txt'); const watcher = chokidar_watch(testPath, options); - const spy = await aspy(watcher, 'add'); + const spy = await aspy(watcher, EV_ADD); await delay(); - await write(testPath, Date.now()); + await write(testPath, dateNow()); await waitFor([spy]); spy.should.have.been.calledWith(testPath); }); @@ -738,7 +778,7 @@ const testDir = getFixturePath('subdir'); const testPath = getFixturePath('subdir/add.txt'); const watcher = chokidar_watch(testDir, options); - const spy = await aspy(watcher, 'all'); + const spy = await aspy(watcher, EV_ALL); spy.should.not.have.been.called; await delay(); @@ -746,9 +786,9 @@ await delay(); await write(testPath, 'hello'); - await waitFor([spy.withArgs('add')]); - spy.should.have.been.calledWith('addDir', testDir); - spy.should.have.been.calledWith('add', testPath); + await waitFor([spy.withArgs(EV_ADD)]); + spy.should.have.been.calledWith(EV_ADD_DIR, testDir); + spy.should.have.been.calledWith(EV_ADD, testPath); }); }); describe('watch glob patterns', () => { @@ -757,33 +797,33 @@ const addPath = getFixturePath('add.txt'); const changePath = getFixturePath('change.txt'); const watcher = chokidar_watch(watchPath, options); - const spy = await aspy(watcher, 'all'); - spy.should.have.been.calledWith('add', changePath); + const spy = await aspy(watcher, EV_ALL); + spy.should.have.been.calledWith(EV_ADD, changePath); - await write(addPath, Date.now()); - await write(changePath, Date.now()); + await write(addPath, dateNow()); + await write(changePath, dateNow()); await delay(); - await waitFor([[spy, 3], spy.withArgs('add', addPath)]); - spy.should.have.been.calledWith('add', addPath); - spy.should.have.been.calledWith('change', changePath); - spy.should.not.have.been.calledWith('add', getFixturePath('unlink.txt')); - spy.should.not.have.been.calledWith('addDir'); + await waitFor([[spy, 3], spy.withArgs(EV_ADD, addPath)]); + spy.should.have.been.calledWith(EV_ADD, addPath); + spy.should.have.been.calledWith(EV_CHANGE, changePath); + spy.should.not.have.been.calledWith(EV_ADD, getFixturePath('unlink.txt')); + spy.should.not.have.been.calledWith(EV_ADD_DIR); }); it('should respect negated glob patterns', async () => { const watchPath = getGlobPath('*'); const negatedWatchPath = `!${getGlobPath('*a*.txt')}`; const unlinkPath = getFixturePath('unlink.txt'); const watcher = chokidar_watch([watchPath, negatedWatchPath], options); - const spy = await aspy(watcher, 'all'); + const spy = await aspy(watcher, EV_ALL); spy.should.have.been.calledOnce; - spy.should.have.been.calledWith('add', unlinkPath); + spy.should.have.been.calledWith(EV_ADD, unlinkPath); await delay(); await fs_unlink(unlinkPath); - await waitFor([[spy, 2], spy.withArgs('unlink')]); + await waitFor([[spy, 2], spy.withArgs(EV_UNLINK)]); spy.should.have.been.calledTwice; - spy.should.have.been.calledWith('unlink', unlinkPath); + spy.should.have.been.calledWith(EV_UNLINK, unlinkPath); }); it('should traverse subdirs to match globstar patterns', async () => { const watchPath = getGlobPath(`../../test-*/${subdirId}/**/a*.txt`); @@ -801,24 +841,24 @@ await delay(); const watcher = chokidar_watch(watchPath, options); - const spy = await aspy(watcher, 'all'); + const spy = await aspy(watcher, EV_ALL); await Promise.all([ - write(addFile, Date.now()), - write(subFile, Date.now()), + write(addFile, dateNow()), + write(subFile, dateNow()), fs_unlink(aFile), fs_unlink(bFile) ]); - await waitFor([spy.withArgs('change')]); - spy.withArgs('change').should.have.been.calledOnce; - spy.should.have.been.calledWith('change', subFile); - - await waitFor([spy.withArgs('unlink')]); - spy.withArgs('unlink').should.have.been.calledOnce; - spy.should.have.been.calledWith('unlink', aFile); + await waitFor([spy.withArgs(EV_CHANGE)]); + spy.withArgs(EV_CHANGE).should.have.been.calledOnce; + spy.should.have.been.calledWith(EV_CHANGE, subFile); + + await waitFor([spy.withArgs(EV_UNLINK)]); + spy.withArgs(EV_UNLINK).should.have.been.calledOnce; + spy.should.have.been.calledWith(EV_UNLINK, aFile); - await waitFor([[spy.withArgs('add'), 3]]); - spy.withArgs('add').should.have.been.calledThrice; + await waitFor([[spy.withArgs(EV_ADD), 3]]); + spy.withArgs(EV_ADD).should.have.been.calledThrice; }); it('should resolve relative paths with glob patterns', async () => { const id = subdirId.toString(); @@ -828,18 +868,18 @@ const changePath = sysPath.join(FIXTURES_PATH_REL, id, 'change.txt'); const unlinkPath = getFixturePath('unlink.txt'); const watcher = chokidar_watch(watchPath, options); - const spy = await aspy(watcher, 'all'); + const spy = await aspy(watcher, EV_ALL); - spy.should.have.been.calledWith('add'); + spy.should.have.been.calledWith(EV_ADD); await Promise.all([ - write(addPath, Date.now()), - write(changePath, Date.now()) + write(addPath, dateNow()), + write(changePath, dateNow()) ]); - await waitFor([[spy, 3], spy.withArgs('add', addPath)]); - spy.should.have.been.calledWith('add', addPath); - spy.should.have.been.calledWith('change', changePath); - spy.should.not.have.been.calledWith('add', unlinkPath); - spy.should.not.have.been.calledWith('addDir'); + await waitFor([[spy, 3], spy.withArgs(EV_ADD, addPath)]); + spy.should.have.been.calledWith(EV_ADD, addPath); + spy.should.have.been.calledWith(EV_CHANGE, changePath); + spy.should.not.have.been.calledWith(EV_ADD, unlinkPath); + spy.should.not.have.been.calledWith(EV_ADD_DIR); if (!macosFswatch) spy.should.have.been.calledThrice; }); it('should correctly handle conflicting glob patterns', async () => { @@ -848,47 +888,47 @@ const addPath = getFixturePath('add.txt'); const watchPaths = [getGlobPath('change*'), getGlobPath('unlink*')]; const watcher = chokidar_watch(watchPaths, options); - const spy = await aspy(watcher, 'all'); - spy.should.have.been.calledWith('add', changePath); - spy.should.have.been.calledWith('add', unlinkPath); + const spy = await aspy(watcher, EV_ALL); + spy.should.have.been.calledWith(EV_ADD, changePath); + spy.should.have.been.calledWith(EV_ADD, unlinkPath); spy.should.have.been.calledTwice; await delay(); await fs_unlink(unlinkPath); - await write(addPath, Date.now()); - await write(changePath, Date.now()); + await write(addPath, dateNow()); + await write(changePath, dateNow()); - await waitFor([[spy, 4], spy.withArgs('unlink', unlinkPath)]); - spy.should.have.been.calledWith('change', changePath); - spy.should.have.been.calledWith('unlink', unlinkPath); - spy.should.not.have.been.calledWith('add', addPath); + await waitFor([[spy, 4], spy.withArgs(EV_UNLINK, unlinkPath)]); + spy.should.have.been.calledWith(EV_CHANGE, changePath); + spy.should.have.been.calledWith(EV_UNLINK, unlinkPath); + spy.should.not.have.been.calledWith(EV_ADD, addPath); spy.callCount.should.equal(4); }); it('should correctly handle intersecting glob patterns', async () => { const changePath = getFixturePath('change.txt'); const watchPaths = [getGlobPath('cha*'), getGlobPath('*nge.*')]; const watcher = chokidar_watch(watchPaths, options); - const spy = await aspy(watcher, 'all'); - spy.should.have.been.calledWith('add', changePath); + const spy = await aspy(watcher, EV_ALL); + spy.should.have.been.calledWith(EV_ADD, changePath); spy.should.have.been.calledOnce; - await write(changePath, Date.now()); + await write(changePath, dateNow()); await delay(); await waitFor([[spy, 2]]); - spy.should.have.been.calledWith('change', changePath); + spy.should.have.been.calledWith(EV_CHANGE, changePath); spy.should.have.been.calledTwice; }); it('should not confuse glob-like filenames with globs', async () => { const filePath = getFixturePath('nota[glob].txt'); await write(filePath, 'b'); await delay(); - const spy = await aspy(chokidar_watch(), 'all'); - spy.should.have.been.calledWith('add', filePath); + const spy = await aspy(chokidar_watch(), EV_ALL); + spy.should.have.been.calledWith(EV_ADD, filePath); await delay(); - await write(filePath, Date.now()); - await waitFor([spy.withArgs('change', filePath)]); - spy.should.have.been.calledWith('change', filePath); + await write(filePath, dateNow()); + await waitFor([spy.withArgs(EV_CHANGE, filePath)]); + spy.should.have.been.calledWith(EV_CHANGE, filePath); }); it('should treat glob-like directory names as literal directory names when globbing is disabled', async () => { options.disableGlobbing = true; @@ -904,17 +944,17 @@ fs.writeFileSync(matchingFile, 'c'); fs.writeFileSync(matchingFile2, 'd'); const watcher = chokidar_watch(watchPath, options); - const spy = await aspy(watcher, 'all'); + const spy = await aspy(watcher, EV_ALL); - spy.should.have.been.calledWith('add', filePath); - spy.should.not.have.been.calledWith('addDir', matchingDir); - spy.should.not.have.been.calledWith('add', matchingFile); - spy.should.not.have.been.calledWith('add', matchingFile2); + spy.should.have.been.calledWith(EV_ADD, filePath); + spy.should.not.have.been.calledWith(EV_ADD_DIR, matchingDir); + spy.should.not.have.been.calledWith(EV_ADD, matchingFile); + spy.should.not.have.been.calledWith(EV_ADD, matchingFile2); await delay(); - await write(filePath, Date.now()); + await write(filePath, dateNow()); - await waitFor([spy.withArgs('change', filePath)]); - spy.should.have.been.calledWith('change', filePath); + await waitFor([spy.withArgs(EV_CHANGE, filePath)]); + spy.should.have.been.calledWith(EV_CHANGE, filePath); }); it('should treat glob-like filenames as literal filenames when globbing is disabled', async () => { options.disableGlobbing = true; @@ -929,17 +969,17 @@ fs.writeFileSync(matchingFile, 'c'); fs.writeFileSync(matchingFile2, 'd'); const watcher = chokidar_watch(watchPath, options); - const spy = await aspy(watcher, 'all'); + const spy = await aspy(watcher, EV_ALL); - spy.should.have.been.calledWith('add', filePath); - spy.should.not.have.been.calledWith('addDir', matchingDir); - spy.should.not.have.been.calledWith('add', matchingFile); - spy.should.not.have.been.calledWith('add', matchingFile2); + spy.should.have.been.calledWith(EV_ADD, filePath); + spy.should.not.have.been.calledWith(EV_ADD_DIR, matchingDir); + spy.should.not.have.been.calledWith(EV_ADD, matchingFile); + spy.should.not.have.been.calledWith(EV_ADD, matchingFile2); await delay(); - await write(filePath, Date.now()); + await write(filePath, dateNow()); - await waitFor([spy.withArgs('change', filePath)]); - spy.should.have.been.calledWith('change', filePath); + await waitFor([spy.withArgs(EV_CHANGE, filePath)]); + spy.should.have.been.calledWith(EV_CHANGE, filePath); }); it('should not prematurely filter dirs against complex globstar patterns', async () => { const deepFile = getFixturePath('subdir/subsub/subsubsub/a.txt'); @@ -949,13 +989,13 @@ fs.mkdirSync(getFixturePath('subdir/subsub/subsubsub'), PERM_ARR); fs.writeFileSync(deepFile, 'b'); const watcher = chokidar_watch(watchPath, options); - const spy = await aspy(watcher, 'all'); + const spy = await aspy(watcher, EV_ALL); await delay(); - await write(deepFile, Date.now()); + await write(deepFile, dateNow()); await waitFor([[spy, 2]]); - spy.should.have.been.calledWith('add', deepFile); - spy.should.have.been.calledWith('change', deepFile); + spy.should.have.been.calledWith(EV_ADD, deepFile); + spy.should.have.been.calledWith(EV_CHANGE, deepFile); }); it('should emit matching dir events', async () => { // test with and without globstar matches @@ -965,22 +1005,22 @@ fs.mkdirSync(getFixturePath('subdir'), PERM_ARR); fs.mkdirSync(getFixturePath('subdir/subsub'), PERM_ARR); const watcher = chokidar_watch(watchPaths, options); - const spy = await aspy(watcher, 'all'); + const spy = await aspy(watcher, EV_ALL); - spy.should.have.been.calledWith('addDir', getFixturePath('subdir')); - spy.withArgs('addDir').should.have.been.calledOnce; + spy.should.have.been.calledWith(EV_ADD_DIR, getFixturePath('subdir')); + spy.withArgs(EV_ADD_DIR).should.have.been.calledOnce; fs.mkdirSync(deepDir, PERM_ARR); - fs.writeFileSync(deepFile, Date.now()); + fs.writeFileSync(deepFile, dateNow()); - await waitFor([[spy.withArgs('addDir'), 2], spy.withArgs('add', deepFile)]); + await waitFor([[spy.withArgs(EV_ADD_DIR), 2], spy.withArgs(EV_ADD, deepFile)]); if (win32Polling) return true; - spy.should.have.been.calledWith('addDir', deepDir); + spy.should.have.been.calledWith(EV_ADD_DIR, deepDir); fs.unlinkSync(deepFile); fs.rmdirSync(deepDir); - await waitFor([spy.withArgs('unlinkDir')]); - spy.should.have.been.calledWith('unlinkDir', deepDir); + await waitFor([spy.withArgs(EV_UNLINK_DIR)]); + spy.should.have.been.calledWith(EV_UNLINK_DIR, deepDir); }); it('should correctly handle glob with braces', async () => { const watchPath = upath.normalizeSafe(getGlobPath('{subdir/*,subdir1/subsub1}/subsubsub/*.txt')); @@ -992,19 +1032,19 @@ fs.mkdirSync(getFixturePath('subdir1'), PERM_ARR); fs.mkdirSync(getFixturePath('subdir1/subsub1'), PERM_ARR); fs.mkdirSync(getFixturePath('subdir1/subsub1/subsubsub'), PERM_ARR); - fs.writeFileSync(deepFileA, Date.now()); - fs.writeFileSync(deepFileB, Date.now()); + fs.writeFileSync(deepFileA, dateNow()); + fs.writeFileSync(deepFileB, dateNow()); const watcher = chokidar_watch(watchPath, options); - const spy = await aspy(watcher, 'all'); + const spy = await aspy(watcher, EV_ALL); - spy.should.have.been.calledWith('add', deepFileA); - spy.should.have.been.calledWith('add', deepFileB); - fs.appendFileSync(deepFileA, Date.now()); - fs.appendFileSync(deepFileB, Date.now()); + spy.should.have.been.calledWith(EV_ADD, deepFileA); + spy.should.have.been.calledWith(EV_ADD, deepFileB); + fs.appendFileSync(deepFileA, dateNow()); + fs.appendFileSync(deepFileB, dateNow()); await waitFor([[spy, 4]]); - spy.should.have.been.calledWith('change', deepFileA); - spy.should.have.been.calledWith('change', deepFileB); + spy.should.have.been.calledWith(EV_CHANGE, deepFileA); + spy.should.have.been.calledWith(EV_CHANGE, deepFileB); }); }); describe('watch symlinks', () => { @@ -1026,8 +1066,8 @@ const dirSpy = sinon.spy(function dirSpy(){}); const addSpy = sinon.spy(function addSpy(){}); const watcher = chokidar_watch(linkedDir, options) - .on('addDir', dirSpy) - .on('add', addSpy); + .on(EV_ADD_DIR, dirSpy) + .on(EV_ADD, addSpy); await waitForWatcher(watcher); dirSpy.should.have.been.calledWith(linkedDir); @@ -1039,36 +1079,36 @@ const linkPath = getFixturePath('link.txt'); fs.symlinkSync(changePath, linkPath); const watcher = chokidar_watch(linkPath, options); - const spy = await aspy(watcher, 'all'); + const spy = await aspy(watcher, EV_ALL); - await write(changePath, Date.now()); - await waitFor([spy.withArgs('change')]); - spy.should.have.been.calledWith('add', linkPath); - spy.should.have.been.calledWith('change', linkPath); + await write(changePath, dateNow()); + await waitFor([spy.withArgs(EV_CHANGE)]); + spy.should.have.been.calledWith(EV_ADD, linkPath); + spy.should.have.been.calledWith(EV_CHANGE, linkPath); }); it('should follow symlinked files within a normal dir', async () => { const changePath = getFixturePath('change.txt'); const linkPath = getFixturePath('subdir/link.txt'); fs.symlinkSync(changePath, linkPath); const watcher = chokidar_watch(getFixturePath('subdir'), options); - const spy = await aspy(watcher, 'all'); + const spy = await aspy(watcher, EV_ALL); - await write(changePath, Date.now()); - await waitFor([spy.withArgs('change', linkPath)]); - spy.should.have.been.calledWith('add', linkPath); - spy.should.have.been.calledWith('change', linkPath); + await write(changePath, dateNow()); + await waitFor([spy.withArgs(EV_CHANGE, linkPath)]); + spy.should.have.been.calledWith(EV_ADD, linkPath); + spy.should.have.been.calledWith(EV_CHANGE, linkPath); }); it('should watch paths with a symlinked parent', async () => { const testDir = sysPath.join(linkedDir, 'subdir'); const testFile = sysPath.join(testDir, 'add.txt'); const watcher = chokidar_watch(testDir, options); - const spy = await aspy(watcher, 'all'); + const spy = await aspy(watcher, EV_ALL); - spy.should.have.been.calledWith('addDir', testDir); - spy.should.have.been.calledWith('add', testFile); - await write(getFixturePath('subdir/add.txt'), Date.now()); - await waitFor([spy.withArgs('change')]); - spy.should.have.been.calledWith('change', testFile); + spy.should.have.been.calledWith(EV_ADD_DIR, testDir); + spy.should.have.been.calledWith(EV_ADD, testFile); + await write(getFixturePath('subdir/add.txt'), dateNow()); + await waitFor([spy.withArgs(EV_CHANGE)]); + spy.should.have.been.calledWith(EV_CHANGE, testFile); }); it('should not recurse indefinitely on circular symlinks', async () => { await fs_symlink(currentDir, getFixturePath('subdir/circular')); @@ -1079,51 +1119,51 @@ it('should recognize changes following symlinked dirs', async () => { const linkedFilePath = sysPath.join(linkedDir, 'change.txt'); const watcher = chokidar_watch(linkedDir, options); - const spy = await aspy(watcher, 'change'); + const spy = await aspy(watcher, EV_CHANGE); const wa = spy.withArgs(linkedFilePath); - await write(getFixturePath('change.txt'), Date.now()); + await write(getFixturePath('change.txt'), dateNow()); await waitFor([wa]); spy.should.have.been.calledWith(linkedFilePath); }); it('should follow newly created symlinks', async () => { options.ignoreInitial = true; const watcher = chokidar_watch(); - const spy = await aspy(watcher, 'all'); + const spy = await aspy(watcher, EV_ALL); await delay(); await fs_symlink(getFixturePath('subdir'), getFixturePath('link')); await waitFor([ - spy.withArgs('add', getFixturePath('link/add.txt')), - spy.withArgs('addDir', getFixturePath('link')) + spy.withArgs(EV_ADD, getFixturePath('link/add.txt')), + spy.withArgs(EV_ADD_DIR, getFixturePath('link')) ]); - spy.should.have.been.calledWith('addDir', getFixturePath('link')); - spy.should.have.been.calledWith('add', getFixturePath('link/add.txt')); + spy.should.have.been.calledWith(EV_ADD_DIR, getFixturePath('link')); + spy.should.have.been.calledWith(EV_ADD, getFixturePath('link/add.txt')); }); it('should watch symlinks as files when followSymlinks:false', async () => { options.followSymlinks = false; const watcher = chokidar_watch(linkedDir, options); - const spy = await aspy(watcher, 'all'); - spy.should.not.have.been.calledWith('addDir'); - spy.should.have.been.calledWith('add', linkedDir); + const spy = await aspy(watcher, EV_ALL); + spy.should.not.have.been.calledWith(EV_ADD_DIR); + spy.should.have.been.calledWith(EV_ADD, linkedDir); spy.should.have.been.calledOnce; }); it('should watch symlinks within a watched dir as files when followSymlinks:false', async () => { options.followSymlinks = false; + // Create symlink in linkPath const linkPath = getFixturePath('link'); fs.symlinkSync(getFixturePath('subdir'), linkPath); - const spy = await aspy(chokidar_watch(), 'all'); - - // await delay(); + const spy = await aspy(chokidar_watch(), EV_ALL); + await delay(); setTimeout(() => { - fs.writeFileSync(getFixturePath('subdir/add.txt'), Date.now()); + fs.writeFileSync(getFixturePath('subdir/add.txt'), dateNow()); fs.unlinkSync(linkPath); fs.symlinkSync(getFixturePath('subdir/add.txt'), linkPath); }, options.usePolling ? 1200 : 300); - await waitFor([spy.withArgs('change', linkPath)]); - spy.should.not.have.been.calledWith('addDir', linkPath); - spy.should.not.have.been.calledWith('add', getFixturePath('link/add.txt')); - spy.should.have.been.calledWith('add', linkPath); - spy.should.have.been.calledWith('change', linkPath); + await waitFor([spy.withArgs(EV_CHANGE, linkPath)]); + spy.should.not.have.been.calledWith(EV_ADD_DIR, linkPath); + spy.should.not.have.been.calledWith(EV_ADD, getFixturePath('link/add.txt')); + spy.should.have.been.calledWith(EV_ADD, linkPath); + spy.should.have.been.calledWith(EV_CHANGE, linkPath); }); it('should not reuse watcher when following a symlink to elsewhere', async () => { const linkedPath = getFixturePath('outside'); @@ -1138,12 +1178,12 @@ await delay(options.usePolling ? 900 : undefined); const watchedPath = getFixturePath('subdir/subsub/text.txt'); const watcher = chokidar_watch(watchedPath, options); - const spy = await aspy(watcher, 'all'); + const spy = await aspy(watcher, EV_ALL); await delay(); - await write(linkedFilePath, Date.now()); - await waitFor([spy.withArgs('change')]); - spy.should.have.been.calledWith('change', watchedPath); + await write(linkedFilePath, dateNow()); + await waitFor([spy.withArgs(EV_CHANGE)]); + spy.should.have.been.calledWith(EV_CHANGE, watchedPath); }); it('should properly match glob patterns that include a symlinked dir', async () => { const dirSpy = sinon.spy(function dirSpy(){}); @@ -1151,8 +1191,8 @@ // test with relative path to ensure proper resolution const watchDir = upath.relative(process.cwd(), linkedDir); const watcher = chokidar_watch(upath.join(watchDir, '**/*'), options) - .on('addDir', dirSpy) - .on('add', addSpy); + .on(EV_ADD_DIR, dirSpy) + .on(EV_ADD, addSpy); await waitForWatcher(watcher); // only the children are matched by the glob pattern, not the link itself addSpy.should.have.been.calledThrice; // also unlink.txt & subdir/add.txt @@ -1169,7 +1209,7 @@ await fs_rmdir(targetDir); const readySpy = sinon.spy(function readySpy(){}); const watcher = chokidar_watch(getFixturePath('subdir'), options) - .on('ready', readySpy); + .on(EV_READY, readySpy); await waitForWatcher(watcher); readySpy.should.have.been.calledOnce; }); @@ -1180,26 +1220,26 @@ const testDir = getFixturePath('subdir'); fs.mkdirSync(testDir); const watcher = chokidar_watch([testDir, testPath], options); - const spy = await aspy(watcher, 'all'); - spy.should.have.been.calledWith('add', testPath); - spy.should.have.been.calledWith('addDir', testDir); - spy.should.not.have.been.calledWith('add', getFixturePath('unlink.txt')); - await write(testPath, Date.now()); - await waitFor([spy.withArgs('change')]); - spy.should.have.been.calledWith('change', testPath); + const spy = await aspy(watcher, EV_ALL); + spy.should.have.been.calledWith(EV_ADD, testPath); + spy.should.have.been.calledWith(EV_ADD_DIR, testDir); + spy.should.not.have.been.calledWith(EV_ADD, getFixturePath('unlink.txt')); + await write(testPath, dateNow()); + await waitFor([spy.withArgs(EV_CHANGE)]); + spy.should.have.been.calledWith(EV_CHANGE, testPath); }); it('should accommodate nested arrays in input', async () => { const testPath = getFixturePath('change.txt'); const testDir = getFixturePath('subdir'); await fs_mkdir(testDir); const watcher = chokidar_watch([[testDir], [testPath]], options); - const spy = await aspy(watcher, 'all'); - spy.should.have.been.calledWith('add', testPath); - spy.should.have.been.calledWith('addDir', testDir); - spy.should.not.have.been.calledWith('add', getFixturePath('unlink.txt')); - await write(testPath, Date.now()); - await waitFor([spy.withArgs('change')]); - spy.should.have.been.calledWith('change', testPath); + const spy = await aspy(watcher, EV_ALL); + spy.should.have.been.calledWith(EV_ADD, testPath); + spy.should.have.been.calledWith(EV_ADD_DIR, testDir); + spy.should.not.have.been.calledWith(EV_ADD, getFixturePath('unlink.txt')); + await write(testPath, dateNow()); + await waitFor([spy.withArgs(EV_CHANGE)]); + spy.should.have.been.calledWith(EV_CHANGE, testPath); }); it('should throw if provided any non-string paths', () => { expect(chokidar_watch.bind(null, [[currentDir], /notastring/])) @@ -1212,12 +1252,12 @@ beforeEach(() => { options.ignoreInitial = false; }); it('should emit `add` events for preexisting files', async () => { const watcher = chokidar_watch(currentDir, options); - const spy = await aspy(watcher, 'add'); + const spy = await aspy(watcher, EV_ADD); spy.should.have.been.calledTwice; }); it('should emit `addDir` event for watched dir', async () => { const watcher = chokidar_watch(currentDir, options); - const spy = await aspy(watcher, 'addDir'); + const spy = await aspy(watcher, EV_ADD_DIR); spy.should.have.been.calledOnce; spy.should.have.been.calledWith(currentDir); }); @@ -1225,7 +1265,7 @@ await fs_mkdir(getFixturePath('subdir'), PERM_ARR); await fs_mkdir(getFixturePath('subdir/subsub'), PERM_ARR); const watcher = chokidar_watch(currentDir, options); - const spy = await aspy(watcher, 'addDir'); + const spy = await aspy(watcher, EV_ADD_DIR); spy.should.have.been.calledWith(currentDir); spy.should.have.been.calledWith(getFixturePath('subdir')); spy.should.have.been.calledWith(getFixturePath('subdir/subsub')); @@ -1236,13 +1276,13 @@ beforeEach(() => { options.ignoreInitial = true; }); it('should ignore initial add events', async () => { const watcher = chokidar_watch(); - const spy = await aspy(watcher, 'add'); + const spy = await aspy(watcher, EV_ADD); await delay(); spy.should.not.have.been.called; }); it('should ignore add events on a subsequent .add()', async () => { const watcher = chokidar_watch(getFixturePath('subdir'), options); - const spy = await aspy(watcher, 'add'); + const spy = await aspy(watcher, EV_ADD); watcher.add(currentDir); await delay(1000); spy.should.not.have.been.called; @@ -1250,22 +1290,22 @@ it('should notice when a file appears in an empty directory', async () => { const testDir = getFixturePath('subdir'); const testPath = getFixturePath('subdir/add.txt'); - const spy = await aspy(chokidar_watch(), 'add'); + const spy = await aspy(chokidar_watch(), EV_ADD); spy.should.not.have.been.called; await fs_mkdir(testDir, PERM_ARR); - await write(testPath, Date.now()); + await write(testPath, dateNow()); await waitFor([spy]); spy.should.have.been.calledOnce; spy.should.have.been.calledWith(testPath); }); it('should emit a change on a preexisting file as a change', async () => { const testPath = getFixturePath('change.txt'); - const spy = await aspy(chokidar_watch(), 'all'); + const spy = await aspy(chokidar_watch(), EV_ALL); spy.should.not.have.been.called; - await write(testPath, Date.now()); - await waitFor([spy.withArgs('change', testPath)]); - spy.should.have.been.calledWith('change', testPath); - spy.should.not.have.been.calledWith('add'); + await write(testPath, dateNow()); + await waitFor([spy.withArgs(EV_CHANGE, testPath)]); + spy.should.have.been.calledWith(EV_CHANGE, testPath); + spy.should.not.have.been.calledWith(EV_ADD); }); it('should not emit for preexisting dirs when depth is 0', async () => { options.depth = 0; @@ -1273,13 +1313,13 @@ await fs_mkdir(getFixturePath('subdir'), PERM_ARR); await delay(200); - const spy = await aspy(chokidar_watch(), 'all'); - await write(testPath, Date.now()); + const spy = await aspy(chokidar_watch(), EV_ALL); + await write(testPath, dateNow()); await waitFor([spy]); await delay(200); - spy.should.have.been.calledWith('add', testPath); - spy.should.not.have.been.calledWith('addDir'); + spy.should.have.been.calledWith(EV_ADD, testPath); + spy.should.not.have.been.calledWith(EV_ADD_DIR); }); }); }); @@ -1295,7 +1335,7 @@ fs.mkdirSync(sysPath.join(testDir, 'subsub'), PERM_ARR); fs.writeFileSync(sysPath.join(testDir, 'subsub', 'ab.txt'), ''); const watcher = chokidar_watch(testDir, options); - const spy = await aspy(watcher, 'add'); + const spy = await aspy(watcher, EV_ADD); spy.should.have.been.calledOnce; spy.should.have.been.calledWith(sysPath.join(testDir, 'add.txt')); }); @@ -1310,15 +1350,15 @@ fs.mkdirSync(testDir, PERM_ARR); fs.writeFileSync(testFile, 'b'); const watcher = chokidar_watch(currentDir, options); - const spy = await aspy(watcher, 'all'); + const spy = await aspy(watcher, EV_ALL); await delay(); - await write(testFile, Date.now()); + await write(testFile, dateNow()); await delay(300); - spy.should.not.have.been.calledWith('addDir', testDir); - spy.should.not.have.been.calledWith('add', testFile); - spy.should.not.have.been.calledWith('change', testFile); + spy.should.not.have.been.calledWith(EV_ADD_DIR, testDir); + spy.should.not.have.been.calledWith(EV_ADD, testFile); + spy.should.not.have.been.calledWith(EV_CHANGE, testFile); }); it('should allow regex/fn ignores', async () => { options.cwd = currentDir; @@ -1326,17 +1366,17 @@ fs.writeFileSync(getFixturePath('add.txt'), 'b'); const watcher = chokidar_watch(currentDir, options); - const spy = await aspy(watcher, 'all'); + const spy = await aspy(watcher, EV_ALL); await delay(); - await write(getFixturePath('add.txt'), Date.now()); - await write(getFixturePath('change.txt'), Date.now()); + await write(getFixturePath('add.txt'), dateNow()); + await write(getFixturePath('change.txt'), dateNow()); - await waitFor([spy.withArgs('change', 'change.txt')]); - spy.should.not.have.been.calledWith('add', 'add.txt'); - spy.should.not.have.been.calledWith('change', 'add.txt'); - spy.should.have.been.calledWith('add', 'change.txt'); - spy.should.have.been.calledWith('change', 'change.txt'); + await waitFor([spy.withArgs(EV_CHANGE, 'change.txt')]); + spy.should.not.have.been.calledWith(EV_ADD, 'add.txt'); + spy.should.not.have.been.calledWith(EV_CHANGE, 'add.txt'); + spy.should.have.been.calledWith(EV_ADD, 'change.txt'); + spy.should.have.been.calledWith(EV_CHANGE, 'change.txt'); }); }); describe('depth', () => { @@ -1351,14 +1391,14 @@ it('should not recurse if depth is 0', async () => { options.depth = 0; const watcher = chokidar_watch(); - const spy = await aspy(watcher, 'all'); - await write(getFixturePath('subdir/add.txt'), Date.now()); + const spy = await aspy(watcher, EV_ALL); + await write(getFixturePath('subdir/add.txt'), dateNow()); await waitFor([[spy, 4]]); - spy.should.have.been.calledWith('addDir', currentDir); - spy.should.have.been.calledWith('addDir', getFixturePath('subdir')); - spy.should.have.been.calledWith('add', getFixturePath('change.txt')); - spy.should.have.been.calledWith('add', getFixturePath('unlink.txt')); - spy.should.not.have.been.calledWith('change'); + spy.should.have.been.calledWith(EV_ADD_DIR, currentDir); + spy.should.have.been.calledWith(EV_ADD_DIR, getFixturePath('subdir')); + spy.should.have.been.calledWith(EV_ADD, getFixturePath('change.txt')); + spy.should.have.been.calledWith(EV_ADD, getFixturePath('unlink.txt')); + spy.should.not.have.been.calledWith(EV_CHANGE); if (!macosFswatch) spy.callCount.should.equal(4); }); it('should recurse to specified depth', async () => { @@ -1366,17 +1406,17 @@ const addPath = getFixturePath('subdir/add.txt'); const changePath = getFixturePath('change.txt'); const ignoredPath = getFixturePath('subdir/subsub/ab.txt'); - const spy = await aspy(chokidar_watch(), 'all'); + const spy = await aspy(chokidar_watch(), EV_ALL); await delay(); - await write(getFixturePath('change.txt'), Date.now()); - await write(addPath, Date.now()); - await write(ignoredPath, Date.now()); - await waitFor([spy.withArgs('change', addPath), spy.withArgs('change', changePath)]); - spy.should.have.been.calledWith('addDir', getFixturePath('subdir/subsub')); - spy.should.have.been.calledWith('change', changePath); - spy.should.have.been.calledWith('change', addPath); - spy.should.not.have.been.calledWith('add', ignoredPath); - spy.should.not.have.been.calledWith('change', ignoredPath); + await write(getFixturePath('change.txt'), dateNow()); + await write(addPath, dateNow()); + await write(ignoredPath, dateNow()); + await waitFor([spy.withArgs(EV_CHANGE, addPath), spy.withArgs(EV_CHANGE, changePath)]); + spy.should.have.been.calledWith(EV_ADD_DIR, getFixturePath('subdir/subsub')); + spy.should.have.been.calledWith(EV_CHANGE, changePath); + spy.should.have.been.calledWith(EV_CHANGE, addPath); + spy.should.not.have.been.calledWith(EV_ADD, ignoredPath); + spy.should.not.have.been.calledWith(EV_CHANGE, ignoredPath); if (!macosFswatch) spy.callCount.should.equal(8); }); it('should respect depth setting when following symlinks', async () => { @@ -1384,11 +1424,11 @@ options.depth = 1; await fs_symlink(getFixturePath('subdir'), getFixturePath('link')); await delay(); - const spy = await aspy(chokidar_watch(), 'all'); - spy.should.have.been.calledWith('addDir', getFixturePath('link')); - spy.should.have.been.calledWith('addDir', getFixturePath('link/subsub')); - spy.should.have.been.calledWith('add', getFixturePath('link/add.txt')); - spy.should.not.have.been.calledWith('add', getFixturePath('link/subsub/ab.txt')); + const spy = await aspy(chokidar_watch(), EV_ALL); + spy.should.have.been.calledWith(EV_ADD_DIR, getFixturePath('link')); + spy.should.have.been.calledWith(EV_ADD_DIR, getFixturePath('link/subsub')); + spy.should.have.been.calledWith(EV_ADD, getFixturePath('link/add.txt')); + spy.should.not.have.been.calledWith(EV_ADD, getFixturePath('link/subsub/ab.txt')); }); it('should respect depth setting when following a new symlink', async () => { if (isWindows) return true; // skip on windows @@ -1396,22 +1436,22 @@ options.ignoreInitial = true; const linkPath = getFixturePath('link'); const dirPath = getFixturePath('link/subsub'); - const spy = await aspy(chokidar_watch(), 'all'); + const spy = await aspy(chokidar_watch(), EV_ALL); await fs_symlink(getFixturePath('subdir'), linkPath); - await waitFor([[spy, 3], spy.withArgs('addDir', dirPath)]); - spy.should.have.been.calledWith('addDir', linkPath); - spy.should.have.been.calledWith('addDir', dirPath); - spy.should.have.been.calledWith('add', getFixturePath('link/add.txt')); + await waitFor([[spy, 3], spy.withArgs(EV_ADD_DIR, dirPath)]); + spy.should.have.been.calledWith(EV_ADD_DIR, linkPath); + spy.should.have.been.calledWith(EV_ADD_DIR, dirPath); + spy.should.have.been.calledWith(EV_ADD, getFixturePath('link/add.txt')); spy.should.have.been.calledThrice; }); it('should correctly handle dir events when depth is 0', async () => { options.depth = 0; const subdir2 = getFixturePath('subdir2'); - const spy = await aspy(chokidar_watch(), 'all'); - const addSpy = spy.withArgs('addDir'); - const unlinkSpy = spy.withArgs('unlinkDir'); - spy.should.have.been.calledWith('addDir', currentDir); - spy.should.have.been.calledWith('addDir', getFixturePath('subdir')); + const spy = await aspy(chokidar_watch(), EV_ALL); + const addSpy = spy.withArgs(EV_ADD_DIR); + const unlinkSpy = spy.withArgs(EV_UNLINK_DIR); + spy.should.have.been.calledWith(EV_ADD_DIR, currentDir); + spy.should.have.been.calledWith(EV_ADD_DIR, getFixturePath('subdir')); await fs_mkdir(subdir2, PERM_ARR); await waitFor([[addSpy, 3]]); addSpy.should.have.been.calledThrice; @@ -1419,7 +1459,7 @@ await fs_rmdir(subdir2); await waitFor([unlinkSpy]); await delay(); - unlinkSpy.should.have.been.calledWith('unlinkDir', subdir2); + unlinkSpy.should.have.been.calledWith(EV_UNLINK_DIR, subdir2); unlinkSpy.should.have.been.calledOnce; }); }); @@ -1429,7 +1469,7 @@ options.ignoreInitial = true; }); it('should ignore vim/emacs/Sublime swapfiles', async () => { - const spy = await aspy(chokidar_watch(), 'all'); + const spy = await aspy(chokidar_watch(), EV_ALL); await write(getFixturePath('.change.txt.swp'), 'a'); // vim await write(getFixturePath('add.txt~'), 'a'); // vim/emacs await write(getFixturePath('.subl5f4.tmp'), 'a'); // sublime @@ -1448,7 +1488,7 @@ options.ignoreInitial = false; await write(getFixturePath('old.txt~'), 'a'); await delay(); - const spy = await aspy(chokidar_watch(), 'all'); + const spy = await aspy(chokidar_watch(), EV_ALL); spy.should.not.have.been.calledWith(getFixturePath('old.txt')); spy.should.not.have.been.calledWith(getFixturePath('old.txt~')); }); @@ -1457,14 +1497,14 @@ it('should emit relative paths based on cwd', async () => { options.cwd = currentDir; const watcher = chokidar_watch('**', options); - const spy = await aspy(watcher, 'all'); + const spy = await aspy(watcher, EV_ALL); await fs_unlink(getFixturePath('unlink.txt')); - await write(getFixturePath('change.txt'), Date.now()); - await waitFor([spy.withArgs('unlink')]); - spy.should.have.been.calledWith('add', 'change.txt'); - spy.should.have.been.calledWith('add', 'unlink.txt'); - spy.should.have.been.calledWith('change', 'change.txt'); - spy.should.have.been.calledWith('unlink', 'unlink.txt'); + await write(getFixturePath('change.txt'), dateNow()); + await waitFor([spy.withArgs(EV_UNLINK)]); + spy.should.have.been.calledWith(EV_ADD, 'change.txt'); + spy.should.have.been.calledWith(EV_ADD, 'unlink.txt'); + spy.should.have.been.calledWith(EV_CHANGE, 'change.txt'); + spy.should.have.been.calledWith(EV_UNLINK, 'unlink.txt'); }); it('should emit `addDir` with alwaysStat for renamed directory', async () => { options.cwd = currentDir; @@ -1478,7 +1518,7 @@ const watcher = chokidar_watch('.', options); setTimeout(() => { - watcher.on('addDir', spy); + watcher.on(EV_ADD_DIR, spy); fs_rename(testDir, renamedDir); }, 1000); @@ -1495,21 +1535,21 @@ }); options2.cwd = getFixturePath('subdir'); const watcher = chokidar_watch(getGlobPath('**'), options); - const spy1 = await aspy(watcher, 'all'); + const spy1 = await aspy(watcher, EV_ALL); await delay(); const watcher2 = chokidar_watch(currentDir, options2); - const spy2 = await aspy(watcher2, 'all'); + const spy2 = await aspy(watcher2, EV_ALL); await fs_unlink(getFixturePath('unlink.txt')); - await write(getFixturePath('change.txt'), Date.now()); - await waitFor([spy1.withArgs('unlink'), spy2.withArgs('unlink')]); - spy1.should.have.been.calledWith('change', 'change.txt'); - spy1.should.have.been.calledWith('unlink', 'unlink.txt'); - spy2.should.have.been.calledWith('add', sysPath.join('..', 'change.txt')); - spy2.should.have.been.calledWith('add', sysPath.join('..', 'unlink.txt')); - spy2.should.have.been.calledWith('change', sysPath.join('..', 'change.txt')); - spy2.should.have.been.calledWith('unlink', sysPath.join('..', 'unlink.txt')); + await write(getFixturePath('change.txt'), dateNow()); + await waitFor([spy1.withArgs(EV_UNLINK), spy2.withArgs(EV_UNLINK)]); + spy1.should.have.been.calledWith(EV_CHANGE, 'change.txt'); + spy1.should.have.been.calledWith(EV_UNLINK, 'unlink.txt'); + spy2.should.have.been.calledWith(EV_ADD, sysPath.join('..', 'change.txt')); + spy2.should.have.been.calledWith(EV_ADD, sysPath.join('..', 'unlink.txt')); + spy2.should.have.been.calledWith(EV_CHANGE, sysPath.join('..', 'change.txt')); + spy2.should.have.been.calledWith(EV_UNLINK, sysPath.join('..', 'unlink.txt')); }); it('should ignore files even with cwd', async () => { options.cwd = currentDir; @@ -1523,22 +1563,22 @@ fs.writeFileSync(getFixturePath('ignored-option.txt'), 'ignored option'); const watcher = chokidar_watch(files, options); - const spy = await aspy(watcher, 'all'); - fs.writeFileSync(getFixturePath('ignored.txt'), Date.now()); - fs.writeFileSync(getFixturePath('ignored-option.txt'), Date.now()); + const spy = await aspy(watcher, EV_ALL); + fs.writeFileSync(getFixturePath('ignored.txt'), dateNow()); + fs.writeFileSync(getFixturePath('ignored-option.txt'), dateNow()); await fs_unlink(getFixturePath('ignored.txt')); await fs_unlink(getFixturePath('ignored-option.txt')); await delay(); - await write(getFixturePath('change.txt'), 'change'); - await waitFor([spy.withArgs('change', 'change.txt')]); - spy.should.have.been.calledWith('add', 'change.txt'); - spy.should.not.have.been.calledWith('add', 'ignored.txt'); - spy.should.not.have.been.calledWith('add', 'ignored-option.txt'); - spy.should.not.have.been.calledWith('change', 'ignored.txt'); - spy.should.not.have.been.calledWith('change', 'ignored-option.txt'); - spy.should.not.have.been.calledWith('unlink', 'ignored.txt'); - spy.should.not.have.been.calledWith('unlink', 'ignored-option.txt'); - spy.should.have.been.calledWith('change', 'change.txt'); + await write(getFixturePath('change.txt'), EV_CHANGE); + await waitFor([spy.withArgs(EV_CHANGE, 'change.txt')]); + spy.should.have.been.calledWith(EV_ADD, 'change.txt'); + spy.should.not.have.been.calledWith(EV_ADD, 'ignored.txt'); + spy.should.not.have.been.calledWith(EV_ADD, 'ignored-option.txt'); + spy.should.not.have.been.calledWith(EV_CHANGE, 'ignored.txt'); + spy.should.not.have.been.calledWith(EV_CHANGE, 'ignored-option.txt'); + spy.should.not.have.been.calledWith(EV_UNLINK, 'ignored.txt'); + spy.should.not.have.been.calledWith(EV_UNLINK, 'ignored-option.txt'); + spy.should.have.been.calledWith(EV_CHANGE, 'change.txt'); }); }); describe('ignorePermissionErrors', () => { @@ -1555,23 +1595,23 @@ }); it('should not watch files without read permissions', async () => { if (isWindows) return true; - const spy = await aspy(chokidar_watch(), 'all'); - spy.should.not.have.been.calledWith('add', filePath); - await write(filePath, Date.now()); + const spy = await aspy(chokidar_watch(), EV_ALL); + spy.should.not.have.been.calledWith(EV_ADD, filePath); + await write(filePath, dateNow()); await delay(200); - spy.should.not.have.been.calledWith('change', filePath); + spy.should.not.have.been.calledWith(EV_CHANGE, filePath); }); }); describe('true', () => { beforeEach(() => { options.ignorePermissionErrors = true; }); it('should watch unreadable files if possible', async () => { - const spy = await aspy(chokidar_watch(), 'all'); - spy.should.have.been.calledWith('add', filePath); + const spy = await aspy(chokidar_watch(), EV_ALL); + spy.should.have.been.calledWith(EV_ADD, filePath); if (!options.useFsEvents) return true; - await write(filePath, Date.now()); - await waitFor([spy.withArgs('change')]); - spy.should.have.been.calledWith('change', filePath); + await write(filePath, dateNow()); + await waitFor([spy.withArgs(EV_CHANGE)]); + spy.should.have.been.calledWith(EV_CHANGE, filePath); }); it('should not choke on non-existent files', async () => { const watcher = chokidar_watch(getFixturePath('nope.txt'), options); @@ -1592,74 +1632,74 @@ }); it('should not emit add event before a file is fully written', async () => { const testPath = getFixturePath('add.txt'); - const spy = await aspy(chokidar_watch(), 'all'); + const spy = await aspy(chokidar_watch(), EV_ALL); await write(testPath, 'hello'); await delay(200); - spy.should.not.have.been.calledWith('add'); + spy.should.not.have.been.calledWith(EV_ADD); }); it('should wait for the file to be fully written before emitting the add event', async () => { const testPath = getFixturePath('add.txt'); - const spy = await aspy(chokidar_watch(), 'all'); + const spy = await aspy(chokidar_watch(), EV_ALL); await write(testPath, 'hello'); await delay(300); spy.should.not.have.been.called; await waitFor([spy]); - spy.should.have.been.calledWith('add', testPath); + spy.should.have.been.calledWith(EV_ADD, testPath); }); it('should emit with the final stats', async () => { const testPath = getFixturePath('add.txt'); - const spy = await aspy(chokidar_watch(), 'all'); + const spy = await aspy(chokidar_watch(), EV_ALL); await write(testPath, 'hello '); await delay(300); fs.appendFileSync(testPath, 'world!'); await waitFor([spy]); - spy.should.have.been.calledWith('add', testPath); + spy.should.have.been.calledWith(EV_ADD, testPath); expect(spy.args[0][2].size).to.equal(12); }); it('should not emit change event while a file has not been fully written', async () => { const testPath = getFixturePath('add.txt'); - const spy = await aspy(chokidar_watch(), 'all'); + const spy = await aspy(chokidar_watch(), EV_ALL); await write(testPath, 'hello'); await delay(100); await write(testPath, 'edit'); await delay(200); - spy.should.not.have.been.calledWith('change', testPath); + spy.should.not.have.been.calledWith(EV_CHANGE, testPath); }); it('should not emit change event before an existing file is fully updated', async () => { const testPath = getFixturePath('change.txt'); - const spy = await aspy(chokidar_watch(), 'all'); + const spy = await aspy(chokidar_watch(), EV_ALL); await write(testPath, 'hello'); await delay(300); - spy.should.not.have.been.calledWith('change', testPath); + spy.should.not.have.been.calledWith(EV_CHANGE, testPath); }); it('should wait for an existing file to be fully updated before emitting the change event', async () => { const testPath = getFixturePath('change.txt'); - const spy = await aspy(chokidar_watch(), 'all'); + const spy = await aspy(chokidar_watch(), EV_ALL); fs.writeFile(testPath, 'hello', () => {}); await delay(300); spy.should.not.have.been.called; await waitFor([spy]); - spy.should.have.been.calledWith('change', testPath); + spy.should.have.been.calledWith(EV_CHANGE, testPath); }); it('should emit change event after the file is fully written', async () => { const testPath = getFixturePath('add.txt'); - const spy = await aspy(chokidar_watch(), 'all'); + const spy = await aspy(chokidar_watch(), EV_ALL); await delay(); await write(testPath, 'hello'); await waitFor([spy]); - spy.should.have.been.calledWith('add', testPath); + spy.should.have.been.calledWith(EV_ADD, testPath); await write(testPath, 'edit'); - await waitFor([spy.withArgs('change')]); - spy.should.have.been.calledWith('change', testPath); + await waitFor([spy.withArgs(EV_CHANGE)]); + spy.should.have.been.calledWith(EV_CHANGE, testPath); }); it('should not raise any event for a file that was deleted before fully written', async () => { const testPath = getFixturePath('add.txt'); - const spy = await aspy(chokidar_watch(), 'all'); + const spy = await aspy(chokidar_watch(), EV_ALL); await write(testPath, 'hello'); await delay(400); await fs_unlink(testPath); @@ -1673,19 +1713,19 @@ await fs_mkdir(options.cwd); await delay(200); - const spy = await aspy(chokidar_watch(), 'all'); + const spy = await aspy(chokidar_watch(), EV_ALL); await delay(400); await write(testPath, 'hello'); - await waitFor([spy.withArgs('add')]); - spy.should.have.been.calledWith('add', filename); + await waitFor([spy.withArgs(EV_ADD)]); + spy.should.have.been.calledWith(EV_ADD, filename); }); it('should still emit initial add events', async () => { options.ignoreInitial = false; - const spy = await aspy(chokidar_watch(), 'all'); - spy.should.have.been.calledWith('add'); - spy.should.have.been.calledWith('addDir'); + const spy = await aspy(chokidar_watch(), EV_ALL); + spy.should.have.been.calledWith(EV_ADD); + spy.should.have.been.calledWith(EV_ADD_DIR); }); it('should emit an unlink event when a file is updated and deleted just after that', async () => { const testPath = getFixturePath('subdir/add.txt'); @@ -1695,13 +1735,13 @@ await delay(); await write(testPath, 'hello'); await delay(); - const spy = await aspy(chokidar_watch(), 'all'); + const spy = await aspy(chokidar_watch(), EV_ALL); await write(testPath, 'edit'); await delay(); await fs_unlink(testPath); - await waitFor([spy.withArgs('unlink')]); - spy.should.have.been.calledWith('unlink', filename); - spy.should.not.have.been.calledWith('change', filename); + await waitFor([spy.withArgs(EV_UNLINK)]); + spy.should.have.been.calledWith(EV_UNLINK, filename); + spy.should.not.have.been.calledWith(EV_CHANGE, filename); }); describe('race condition', () => { function w(fn, to) { @@ -1748,11 +1788,11 @@ const spy = sinon.spy(); const testPath = getFixturePath('add.txt'); chokidar_watch() - .on('all', spy) - .on('ready', () => { + .on(EV_ALL, spy) + .on(EV_READY, () => { fs.writeFile(testPath, 'hello', simpleCb); _waitFor([spy], () => { - spy.should.have.been.calledWith('add', testPath); + spy.should.have.been.calledWith(EV_ADD, testPath); fs.stat.resetHistory(); fs.writeFile(testPath, 'edit', simpleCb); w(() => { @@ -1763,11 +1803,11 @@ // Once stat call is made, it will take some time to return. Meanwhile, unlink // the file and wait for that to be noticed. fs.unlink(testPath, simpleCb); - _waitFor([spy.withArgs('unlink')], w(() => { + _waitFor([spy.withArgs(EV_UNLINK)], w(() => { // Wait a while after unlink to ensure stat() had time to return. That's where // an uncaught exception used to happen. - spy.should.have.been.calledWith('unlink', testPath); - spy.should.not.have.been.calledWith('change'); + spy.should.have.been.calledWith(EV_UNLINK, testPath); + spy.should.not.have.been.calledWith(EV_CHANGE); done(); }, 400)); }); @@ -1809,17 +1849,17 @@ it('should stop watching unwatched paths', async () => { const watchPaths = [getFixturePath('subdir'), getFixturePath('change.txt')]; const watcher = chokidar_watch(watchPaths, options); - const spy = await aspy(watcher, 'all'); + const spy = await aspy(watcher, EV_ALL); watcher.unwatch(getFixturePath('subdir')); await delay(); - await write(getFixturePath('subdir/add.txt'), Date.now()); - await write(getFixturePath('change.txt'), Date.now()); + await write(getFixturePath('subdir/add.txt'), dateNow()); + await write(getFixturePath('change.txt'), dateNow()); await waitFor([spy]); await delay(300); - spy.should.have.been.calledWith('change', getFixturePath('change.txt')); - spy.should.not.have.been.calledWith('add'); + spy.should.have.been.calledWith(EV_CHANGE, getFixturePath('change.txt')); + spy.should.not.have.been.calledWith(EV_ADD); if (!macosFswatch) spy.should.have.been.calledOnce; }); it('should ignore unwatched paths that are a subset of watched paths', async () => { @@ -1828,21 +1868,21 @@ const addFile = getFixturePath('subdir/add.txt'); const changedFile = getFixturePath('change.txt'); const watcher = chokidar_watch(currentDir, options); - const spy = await aspy(watcher, 'all'); + const spy = await aspy(watcher, EV_ALL); // test with both relative and absolute paths watcher.unwatch([subdirRel, getGlobPath('unl*')]); await delay(); await fs_unlink(unlinkFile); - await write(addFile, Date.now()); - await write(changedFile, Date.now()); - await waitFor([spy.withArgs('change')]); + await write(addFile, dateNow()); + await write(changedFile, dateNow()); + await waitFor([spy.withArgs(EV_CHANGE)]); await delay(300); - spy.should.have.been.calledWith('change', changedFile); - spy.should.not.have.been.calledWith('add', addFile); - spy.should.not.have.been.calledWith('unlink', unlinkFile); + spy.should.have.been.calledWith(EV_CHANGE, changedFile); + spy.should.not.have.been.calledWith(EV_ADD, addFile); + spy.should.not.have.been.calledWith(EV_UNLINK, unlinkFile); if (!macosFswatch) spy.should.have.been.calledOnce; }); it('should unwatch relative paths', async () => { @@ -1851,17 +1891,17 @@ const changeFile = sysPath.join(fixturesDir, 'change.txt'); const watchPaths = [subdir, changeFile]; const watcher = chokidar_watch(watchPaths, options); - const spy = await aspy(watcher, 'all'); + const spy = await aspy(watcher, EV_ALL); await delay(); watcher.unwatch(subdir); - await write(getFixturePath('subdir/add.txt'), Date.now()); - await write(getFixturePath('change.txt'), Date.now()); + await write(getFixturePath('subdir/add.txt'), dateNow()); + await write(getFixturePath('change.txt'), dateNow()); await waitFor([spy]); await delay(300); - spy.should.have.been.calledWith('change', changeFile); - spy.should.not.have.been.calledWith('add'); + spy.should.have.been.calledWith(EV_CHANGE, changeFile); + spy.should.not.have.been.calledWith(EV_ADD); if (!macosFswatch) spy.should.have.been.calledOnce; }); it('should watch paths that were unwatched and added again', async () => { @@ -1874,30 +1914,30 @@ watcher.unwatch(getFixturePath('change.txt')); await delay(); - watcher.on('all', spy).add(getFixturePath('change.txt')); + watcher.on(EV_ALL, spy).add(getFixturePath('change.txt')); await delay(); - await write(getFixturePath('change.txt'), Date.now()); + await write(getFixturePath('change.txt'), dateNow()); await waitFor([spy]); - spy.should.have.been.calledWith('change', getFixturePath('change.txt')); + spy.should.have.been.calledWith(EV_CHANGE, getFixturePath('change.txt')); if (!macosFswatch) spy.should.have.been.calledOnce; }); it('should unwatch paths that are relative to options.cwd', async () => { options.cwd = currentDir; const watcher = chokidar_watch('.', options); - const spy = await aspy(watcher, 'all'); + const spy = await aspy(watcher, EV_ALL); watcher.unwatch(['subdir', getFixturePath('unlink.txt')]); await delay(); await fs_unlink(getFixturePath('unlink.txt')); - await write(getFixturePath('subdir/add.txt'), Date.now()); - await write(getFixturePath('change.txt'), Date.now()); + await write(getFixturePath('subdir/add.txt'), dateNow()); + await write(getFixturePath('change.txt'), dateNow()); await waitFor([spy]); await delay(300); - spy.should.have.been.calledWith('change', 'change.txt'); - spy.should.not.have.been.calledWith('add'); - spy.should.not.have.been.calledWith('unlink'); + spy.should.have.been.calledWith(EV_CHANGE, 'change.txt'); + spy.should.not.have.been.calledWith(EV_ADD); + spy.should.not.have.been.calledWith(EV_UNLINK); if (!macosFswatch) spy.should.have.been.calledOnce; }); }); @@ -1906,11 +1946,11 @@ return new Promise((resolve) => { const spy = sinon.spy(); const watcher = chokidar_watch(currentDir, options); - watcher.once('add', () => { - watcher.once('add', async () => { - await watcher.on('add', spy).close(); + watcher.once(EV_ADD, () => { + watcher.once(EV_ADD, async () => { + await watcher.on(EV_ADD, spy).close(); await delay(900); - await write(getFixturePath('add.txt'), Date.now()); + await write(getFixturePath('add.txt'), dateNow()); spy.should.not.have.been.called; resolve(); }); @@ -1936,6 +1976,13 @@ const {stdout} = obj; expect(stdout.toString()).to.equal('closed'); }); + it('should always return the same promise', async () => { + const watcher = chokidar_watch(currentDir, options); + const closePromise = watcher.close(); + expect(closePromise).to.be.a('promise'); + expect(watcher.close()).to.be.equal(closePromise); + await closePromise; + }); }); describe('env variable option override', () => { beforeEach(() => {