watcher
Version:
The file system watcher that strives for perfection, with no native dependencies and optional rename detection support.
1,117 lines (1,015 loc) • 97.7 kB
JavaScript
/* IMPORT */
import {describe} from 'fava';
import {execSync} from 'node:child_process';
import {HAS_NATIVE_RECURSION} from '../dist/constants.js';
import {before, withContext} from './hooks.js';
/* MAIN */
describe ( 'Watcher', () => {
describe.before ( before );
describe ( 'watching files', it => {
it ( 'should watch a single non-existent file inside a directory', withContext ( async t => {
const file = 'home/a/file_missing' + Math.random ();
t.context.watch ( file, { debounce: 0 } );
await t.context.wait.ready ();
t.context.hasWatchObjects ( 0, 1, 0 );
t.context.deepEqualResults ( [], [] );
t.context.tree.newFile ( file );
await t.context.wait.time ();
t.context.hasWatchObjects ( 0, 0, 2 );
t.context.deepEqualResults ( ['add'], [file] );
t.context.tree.remove ( file );
await t.context.wait.time ();
t.context.hasWatchObjects ( 0, 1, 1 );
t.context.deepEqualResults ( ['unlink'], [file] );
t.context.tree.newFile ( file );
await t.context.wait.time ();
t.context.hasWatchObjects ( 0, 0, 2 );
t.context.deepEqualResults ( ['add'], [file] );
t.context.tree.modify ( file );
await t.context.wait.time ();
t.context.hasWatchObjects ( 0, 0, 2 );
t.context.deepEqualResults ( ['change'], [file] );
}));
it ( 'should watch a single non-existent file inside a non-existent directory', withContext ( async t => {
const dir = 'home/a/dir_missing' + Math.random ();
const file = dir + '/file_missing' + Math.random ();
t.context.watch ( file, { debounce: 0, pollingInterval: 100 } );
await t.context.wait.ready ();
t.context.hasWatchObjects ( 1, 0, 0 );
t.context.deepEqualResults ( [], [] );
t.context.tree.newFile ( file );
await t.context.wait.time ();
t.context.hasWatchObjects ( 0, 0, 2 );
t.context.deepEqualResults ( ['add'], [file] );
t.context.tree.remove ( dir );
await t.context.wait.time ();
t.context.hasWatchObjects ( 1, 0, 0 );
t.context.deepEqualResults ( ['unlink'], [file] );
t.context.tree.newFile ( file );
await t.context.wait.time ();
t.context.hasWatchObjects ( 0, 0, 2 );
t.context.deepEqualResults ( ['add'], [file] );
t.context.tree.modify ( file );
await t.context.wait.time ();
t.context.hasWatchObjects ( 0, 0, 2 );
t.context.deepEqualResults ( ['change'], [file] );
}));
it ( 'should watch a single file', withContext ( async t => {
const file1 = 'home/a/file1';
const file2 = 'home/a/file2';
t.context.watch ( file1, { debounce: 0, ignoreInitial: true } );
await t.context.wait.ready ();
t.context.hasWatchObjects ( 0, 0, 2 );
t.context.tree.modify ( file1 );
t.context.tree.modify ( file2 );
t.context.tree.modify ( file1, 100 );
t.context.tree.modify ( file2, 100 );
t.context.tree.modify ( file1, 200 );
t.context.tree.modify ( file2, 200 );
await t.context.wait.time ();
t.context.deepEqualChanges ( [file1, file1, file1] );
}));
it ( 'should watch multiple files', withContext ( async t => {
const file1 = 'home/a/file1';
const file2 = 'home/a/file2';
t.context.watch ( [file1, file2], { debounce: 0, ignoreInitial: true } );
await t.context.wait.ready ();
t.context.hasWatchObjects ( 0, 0, 3 );
t.context.tree.modify ( 'home/a/file1' );
t.context.tree.modify ( 'home/a/file2', 100 );
await t.context.wait.time ();
t.context.deepEqualChanges ( [file1, file2] );
}));
it ( 'should watch all files inside a directory', withContext ( async t => {
const dir = 'home/a';
const file1 = 'home/a/file1';
const file2 = 'home/a/file2';
t.context.watch ( dir, { debounce: 0, ignoreInitial: true } );
await t.context.wait.ready ();
t.context.hasWatchObjects ( 0, 0, 3 );
t.context.tree.modify ( 'home/a/file1' );
t.context.tree.modify ( 'home/a/file2', 100 );
await t.context.wait.time ();
t.context.deepEqualChanges ( [file1, file2] );
}));
it ( 'should watch new files inside a directory', withContext ( async t => {
const dir = 'home/a';
const newfile1 = 'home/a/newfile' + Math.random ();
const newfile2 = 'home/a/newfile' + Math.random ();
t.context.watch ( dir, { debounce: 0, ignoreInitial: true } );
await t.context.wait.ready ();
t.context.hasWatchObjects ( 0, 0, 3 );
t.context.tree.newFile ( newfile1 );
t.context.tree.newFile ( newfile2 );
await t.context.wait.time ();
t.context.deepEqualUnorderedChanges ( [newfile1, newfile2] );
}));
it ( 'should watch new files inside an initially empty directory', withContext ( async t => {
const dir = 'home/empty';
const newfile1 = 'home/empty/newfile' + Math.random ();
const newfile2 = 'home/empty/newfile' + Math.random ();
t.context.watch ( dir, { debounce: 0, ignoreInitial: true } );
await t.context.wait.ready ();
t.context.hasWatchObjects ( 0, 0, 3 );
t.context.tree.newFile ( newfile1 );
t.context.tree.newFile ( newfile2 );
await t.context.wait.time ();
t.context.deepEqualUnorderedChanges ( [newfile1, newfile2] );
}));
it ( 'should watch new files inside a new directory', withContext ( async t => {
const dir = 'home/a';
const newdir = 'home/a/newdir' + Math.random ();
const newfile1 = newdir + '/newfile' + Math.random ();
const newfile2 = newdir + '/newfile' + Math.random ();
t.context.watch ( dir, { debounce: 0, ignoreInitial: true, recursive: true } );
await t.context.wait.ready ();
t.context.tree.newFile ( newfile1 );
t.context.tree.newFile ( newfile2 );
await t.context.wait.time ();
t.context.deepEqualUnorderedChanges ( [newdir, newfile1, newfile2] );
}));
it ( 'should watch all files inside a deep directory', withContext ( async t => {
const dir = 'home';
const file1 = 'home/a/file1';
const file2 = 'home/a/file2';
t.context.watch ( dir, { debounce: 0, ignoreInitial: true, recursive: true } );
await t.context.wait.ready ();
t.context.tree.modify ( 'home/a/file1' );
t.context.tree.modify ( 'home/a/file2', 100 );
await t.context.wait.time ();
t.context.deepEqualChanges ( [file1, file2] );
}));
it ( 'should watch new files inside a deep directory', withContext ( async t => {
const dir = 'home';
const newfile1 = 'home/a/newfile' + Math.random ();
const newfile2 = 'home/a/newfile' + Math.random ();
t.context.watch ( dir, { debounce: 0, ignoreInitial: true, recursive: true } );
await t.context.wait.ready ();
t.context.tree.newFile ( newfile1 );
t.context.tree.newFile ( newfile2 );
await t.context.wait.time ();
t.context.deepEqualUnorderedChanges ( [newfile1, newfile2] );
}));
it ( 'should watch new files inside an initially empty deep directory', withContext ( async t => {
const dir = 'home';
const newfile1 = 'home/empty/newfile' + Math.random ();
const newfile2 = 'home/empty/newfile' + Math.random ();
t.context.watch ( dir, { debounce: 0, ignoreInitial: true, recursive: true } );
await t.context.wait.ready ();
t.context.hasWatchObjects ( 0, 0, 3 );
t.context.tree.newFile ( newfile1 );
t.context.tree.newFile ( newfile2 );
await t.context.wait.time ();
t.context.deepEqualUnorderedChanges ( [newfile1, newfile2] );
}));
it ( 'should watch (touched) new files inside an initially empty deep directory', withContext ( async t => {
const dir = 'home';
const newfile1 = 'home/empty/newfile' + Math.random ();
const newfile2 = 'home/empty/newfile' + Math.random ();
t.context.watch ( dir, { debounce: 0, ignoreInitial: true, recursive: true } );
await t.context.wait.ready ();
t.context.hasWatchObjects ( 0, 0, 3 );
execSync ( `touch "${t.context.tree.path ( newfile1 )}"` );
execSync ( `touch "${t.context.tree.path ( newfile2 )}"` );
await t.context.wait.time ();
t.context.deepEqualUnorderedChanges ( [newfile1, newfile2] );
}));
it ( 'should watch new files inside a new deep directory', withContext ( async t => {
const dir = 'home';
const newdir = 'home/a/newdir' + Math.random ();
const newfile1 = newdir + '/newfile' + Math.random ();
const newfile2 = newdir + '/newfile' + Math.random ();
t.context.watch ( dir, { debounce: 0, ignoreInitial: true, recursive: true } );
await t.context.wait.ready ();
t.context.tree.newFile ( newfile1 );
t.context.tree.newFile ( newfile2 );
await t.context.wait.time ();
t.context.deepEqualUnorderedChanges ( [newdir, newfile1, newfile2] );
}));
it ( 'should deduplicate events', withContext ( async t => {
const file = 'home/a/file2';
t.context.watch ( file, { debounce: 300, ignoreInitial: true } );
await t.context.wait.ready ();
t.context.tree.modify ( file );
t.context.tree.modify ( file, 50 );
t.context.tree.modify ( file, 100 );
await t.context.wait.time ();
t.context.deepEqualChanges ( [file] );
}));
it ( 'should deduplicate events inside a directory', withContext ( async t => {
const dir = 'home/a';
const file = 'home/a/file1';
t.context.watch ( dir, { debounce: 300, ignoreInitial: true } );
await t.context.wait.ready ();
t.context.tree.modify ( file );
t.context.tree.modify ( file, 50 );
t.context.tree.modify ( file, 100 );
await t.context.wait.time ();
t.context.deepEqualResults ( ['change'], [file] );
}));
it ( 'should deduplicate events inside a deep directory', withContext ( async t => {
const dir = 'home';
const file = 'home/a/file1';
t.context.watch ( dir, { debounce: 300, ignoreInitial: true, recursive: true } );
await t.context.wait.ready ();
t.context.tree.modify ( file );
t.context.tree.modify ( file, 50 );
t.context.tree.modify ( file, 100 );
await t.context.wait.time ();
t.context.deepEqualResults ( ['change'], [file] );
}));
});
describe ( 'watching directories', it => {
it ( 'should watch a single non-existent directory inside a directory', withContext ( async t => {
const dir = 'home/a/dir_missing' + Math.random ();
t.context.watch ( dir, { debounce: 0 } );
await t.context.wait.ready ();
t.context.hasWatchObjects ( 0, 1, 0 );
t.context.deepEqualResults ( [], [] );
t.context.tree.newDir ( dir );
await t.context.wait.time ();
t.context.hasWatchObjects ( 0, 0, 3 );
t.context.deepEqualResults ( ['addDir'], [dir] );
t.context.tree.remove ( dir );
await t.context.wait.time ();
t.context.hasWatchObjects ( 0, 1, 1 );
t.context.deepEqualResults ( ['unlinkDir'], [dir] );
t.context.tree.newDir ( dir );
await t.context.wait.time ();
t.context.hasWatchObjects ( 0, 0, 3 );
t.context.deepEqualResults ( ['addDir'], [dir] );
}));
it ( 'should watch a single non-existent directory inside a non-existent directory', withContext ( async t => {
const pdir = 'home/a/dir_missing' + Math.random ();
const dir = pdir + '/dir_missing' + Math.random ();
t.context.watch ( dir, { debounce: 0, pollingInterval: 100 } );
await t.context.wait.ready ();
t.context.hasWatchObjects ( 1, 0, 0 );
t.context.deepEqualResults ( [], [] );
t.context.tree.newDir ( dir );
await t.context.wait.time ();
t.context.hasWatchObjects ( 0, 0, 3 );
t.context.deepEqualResults ( ['addDir'], [dir] );
t.context.tree.remove ( pdir );
await t.context.wait.time ();
t.context.hasWatchObjects ( 1, 0, 0 );
t.context.deepEqualResults ( ['unlinkDir'], [dir] );
t.context.tree.newDir ( dir );
await t.context.wait.time ();
t.context.hasWatchObjects ( 0, 0, 3 );
t.context.deepEqualResults ( ['addDir'], [dir] );
}));
it ( 'should watch new directories inside a directory', withContext ( async t => {
const dir = 'home/a';
const newdir1 = 'home/a/dir1' + Math.random ();
const newdir2 = 'home/a/dir2' + Math.random ();
t.context.watch ( dir, { debounce: 0, ignoreInitial: true } );
await t.context.wait.ready ();
t.context.hasWatchObjects ( 0, 0, 3 );
t.context.tree.newDir ( newdir1 );
t.context.tree.newDir ( newdir2 );
await t.context.wait.time ();
t.context.hasWatchObjects ( 0, 0, 3 );
t.context.deepEqualUnorderedChanges ( [newdir1, newdir2] );
}));
it ( 'should watch new directories inside an initially empty directory', withContext ( async t => {
const dir = 'home/empty';
const newdir1 = 'home/empty/dir1' + Math.random ();
const newdir2 = 'home/empty/dir2' + Math.random ();
t.context.watch ( dir, { debounce: 0, ignoreInitial: true } );
await t.context.wait.ready ();
t.context.hasWatchObjects ( 0, 0, 3 );
t.context.tree.newDir ( newdir1 );
t.context.tree.newDir ( newdir2 );
await t.context.wait.time ();
t.context.hasWatchObjects ( 0, 0, 3 );
t.context.deepEqualUnorderedChanges ( [newdir1, newdir2] );
}));
it ( 'should watch new directories inside a new directory', withContext ( async t => {
const dir = 'home/a';
const newdir0 = 'home/a/newdir' + Math.random ();
const newdir1 = newdir0 + '/newdir' + Math.random ();
const newdir2 = newdir0 + '/newdir' + Math.random ();
t.context.watch ( dir, { debounce: 0, ignoreInitial: true, recursive: true } );
await t.context.wait.ready ();
t.context.tree.newDir ( newdir1 );
t.context.tree.newDir ( newdir2 );
await t.context.wait.time ();
t.context.deepEqualUnorderedChanges ( [newdir0, newdir1, newdir2] );
}));
it ( 'should watch new directories inside a deep directory', withContext ( async t => {
const dir = 'home';
const newdir1 = 'home/a/dir1' + Math.random ();
const newdir2 = 'home/a/dir2' + Math.random ();
t.context.watch ( dir, { debounce: 0, ignoreInitial: true, recursive: true } );
await t.context.wait.ready ();
t.context.tree.newDir ( newdir1 );
t.context.tree.newDir ( newdir2 );
await t.context.wait.time ();
t.context.deepEqualUnorderedChanges ( [newdir1, newdir2] );
}));
it ( 'should watch new directories inside an initially empty deep directory', withContext ( async t => {
const dir = 'home';
const newdir1 = 'home/empty/dir1' + Math.random ();
const newdir2 = 'home/empty/dir2' + Math.random ();
t.context.watch ( dir, { debounce: 0, ignoreInitial: true, recursive: true } );
await t.context.wait.ready ();
t.context.tree.newDir ( newdir1 );
t.context.tree.newDir ( newdir2 );
await t.context.wait.time ();
t.context.deepEqualUnorderedChanges ( [newdir1, newdir2] );
}));
it ( 'should watch new directories inside a new deep directory', withContext ( async t => {
const dir = 'home';
const newdir0 = 'home/a/newdir' + Math.random ();
const newdir1 = newdir0 + '/newdir' + Math.random ();
const newdir2 = newdir0 + '/newdir' + Math.random ();
t.context.watch ( dir, { debounce: 0, ignoreInitial: true, recursive: true } );
await t.context.wait.ready ();
t.context.tree.newDir ( newdir1 );
t.context.tree.newDir ( newdir2 );
await t.context.wait.time ();
t.context.deepEqualUnorderedChanges ( [newdir0, newdir1, newdir2] );
}));
it ( 'should deduplicate events inside a directory', withContext ( async t => {
const dir = 'home/a';
const newdir1 = 'home/a/newdir1' + Math.random ();
const newdir2 = 'home/a/newdir2' + Math.random ();
t.context.watch ( dir, { debounce: 300, ignoreInitial: true } );
await t.context.wait.ready ();
t.context.tree.newDir ( newdir1 );
t.context.tree.newDir ( newdir2 );
t.context.tree.remove ( newdir1, 50 );
t.context.tree.remove ( newdir2, 50 );
t.context.tree.newDir ( newdir1, 100 );
t.context.tree.newDir ( newdir2, 100 );
await t.context.wait.time ();
t.context.deepEqualUnorderedResults ( ['addDir', 'addDir'], [newdir1, newdir2] );
}));
it ( 'should deduplicate events inside a deep directory', withContext ( async t => {
const dir = 'home';
const newdir1 = 'home/a/newdir1' + Math.random ();
const newdir2 = 'home/a/newdir2' + Math.random ();
t.context.watch ( dir, { debounce: 300, ignoreInitial: true, recursive: true } );
await t.context.wait.ready ();
t.context.tree.newDir ( newdir1 );
t.context.tree.newDir ( newdir2 );
t.context.tree.remove ( newdir1, 50 );
t.context.tree.remove ( newdir2, 50 );
t.context.tree.newDir ( newdir1, 100 );
t.context.tree.newDir ( newdir2, 100 );
await t.context.wait.time ();
t.context.deepEqualUnorderedResults ( ['addDir', 'addDir'], [newdir1, newdir2] );
}));
it ( 'should keep watching after removal of sub directory', withContext ( async t => {
const home = 'home';
const dir = t.context.tree.path ( 'home/e/sub' );
const file1 = t.context.tree.path ( 'home/e/file1' );
const file2 = t.context.tree.path ( 'home/e/file2' );
const subfile = t.context.tree.path ( 'home/e/sub/file1' );
const changes = [];
t.context.watch ( home, { debounce: 0, ignoreInitial: true, recursive: true } );
t.context.watcher.on ( 'all', ( event, name ) => {
if ( name === dir || name === file1 || name === file2 ) {
changes.push ( name );
}
});
await t.context.wait.ready ();
t.context.tree.remove ( 'home/e/sub', 50 );
t.context.tree.modify ( 'home/e/file1', 100 );
t.context.tree.modify ( 'home/e/file2', 150 );
await t.context.wait.time ();
t.context.deepEqualUnorderedResults ( ['unlink', 'unlinkDir', 'change', 'change'], [subfile, dir, file1, file2] );
}));
it ( 'should close all eventual additional watchers added for recursiong when no longer needed', withContext ( async t => {
const home = 'home/a';
const dir1 = 'home/a/sub1';
const dir2 = dir1 + '/sub2';
t.context.watch ( home, { debounce: 0, ignoreInitial: true, recursive: true } );
await t.context.wait.ready ();
t.context.hasWatchObjects ( 0, 0, 3 );
t.context.deepEqualResults ( [], [] );
t.context.tree.newDir ( dir2 );
await t.context.wait.time ();
t.context.hasWatchObjects ( 0, 0, HAS_NATIVE_RECURSION ? 3 : 5 );
t.context.deepEqualUnorderedResults ( ['addDir', 'addDir'], [dir1, dir2] );
t.context.tree.remove ( dir2 );
await t.context.wait.time ();
t.context.hasWatchObjects ( 0, 0, HAS_NATIVE_RECURSION ? 3 : 4 );
t.context.deepEqualResults ( ['unlinkDir'], [dir2] );
t.context.tree.remove ( dir1);
await t.context.wait.time ();
t.context.hasWatchObjects ( 0, 0, 3 );
t.context.deepEqualResults ( ['unlinkDir'], [dir1] );
}));
});
describe.todo ( 'watching symlinks' );
describe ( 'file events', it => {
it ( 'should detect initial "add" for a single file', withContext ( async t => {
const file = 'home/a/file1';
t.context.watchForFiles ( file, { debounce: 0 } );
await t.context.wait.ready ();
await t.context.wait.time ();
t.context.deepEqualResults ( ['add'], [file] );
}));
it ( 'should detect initial "add" for multiple files', withContext ( async t => {
const file1 = 'home/a/file1';
const file2 = 'home/a/file2';
t.context.watchForFiles ( [file1, file2], { debounce: 0 } );
await t.context.wait.ready ();
await t.context.wait.time ();
t.context.deepEqualUnorderedResults ( ['add', 'add'], [file1, file2] );
}));
it ( 'should detect initial "add" for all files inside a directory', withContext ( async t => {
const dir = 'home/a';
const file1 = 'home/a/file1';
const file2 = 'home/a/file2';
t.context.watchForFiles ( dir, { debounce: 0 } );
await t.context.wait.ready ();
await t.context.wait.time ();
t.context.deepEqualUnorderedResults ( ['add', 'add'], [file1, file2] );
}));
it ( 'should detect initial "add" for all files inside a deep directory', withContext ( async t => {
const dir = 'home/e';
const file1 = 'home/e/file1';
const file2 = 'home/e/file2';
const filesub1 = 'home/e/sub/file1';
t.context.watchForFiles ( dir, { debounce: 0, recursive: true } );
await t.context.wait.ready ();
await t.context.wait.time ();
t.context.deepEqualUnorderedResults ( ['add', 'add', 'add'], [file1, file2, filesub1] );
}));
it ( 'should detect "add" when creating a new file inside a directory', withContext ( async t => {
const dir = 'home/a';
const newfile = 'home/a/file1' + Math.random ();
t.context.watchForFiles ( dir, { debounce: 0, ignoreInitial: true } );
await t.context.wait.ready ();
t.context.tree.newFile ( newfile );
await t.context.wait.time ();
t.context.deepEqualResults ( ['add'], [newfile] );
}));
it ( 'should detect "add" when creating a new file inside a new directory', withContext ( async t => {
const dir = 'home/a';
const newdir = 'home/a/newdir' + Math.random ();
const newfile = newdir + '/file1' + Math.random ();
t.context.watchForFiles ( dir, { debounce: 0, ignoreInitial: true, recursive: true } );
await t.context.wait.ready ();
t.context.tree.newFile ( newfile );
await t.context.wait.time ();
t.context.deepEqualResults ( ['add'], [newfile] );
}));
it ( 'should detect "add" when creating a new file inside a new deep directory', withContext ( async t => {
const dir = 'home';
const newdir = 'home/a/newdir' + Math.random ();
const newfile = newdir + '/file1' + Math.random ();
t.context.watchForFiles ( dir, { debounce: 0, ignoreInitial: true, recursive: true } );
await t.context.wait.ready ();
t.context.tree.newFile ( newfile );
await t.context.wait.time ();
t.context.deepEqualResults ( ['add'], [newfile] );
}));
it ( 'should detect "add" when copying a file inside a directory', withContext ( async t => {
const dir = 'home/a';
const file = 'home/a/file1';
const copyfile = file + Math.random ();
t.context.watchForFiles ( dir, { debounce: 0, ignoreInitial: true } );
await t.context.wait.ready ();
t.context.tree.copy ( file, copyfile );
await t.context.wait.time ();
t.context.deepEqualResults ( ['add'], [copyfile] );
}));
it ( 'should detect "add" when copying a file inside a deep directory', withContext ( async t => {
const dir = 'home';
const file = 'home/a/file1';
const copyfile = file + Math.random ();
t.context.watchForFiles ( dir, { debounce: 0, ignoreInitial: true, recursive: true } );
await t.context.wait.ready ();
t.context.tree.copy ( file, copyfile );
await t.context.wait.time ();
t.context.deepEqualResults ( ['add'], [copyfile] );
}));
it ( 'should detect "add" when copying a parent directory', withContext ( async t => {
const home = 'home/e';
const dir = 'home/e/sub';
const copydir = dir + Math.random ();
const copyfile = copydir + '/file1';
t.context.watchForFiles ( home, { debounce: 0, ignoreInitial: true } );
await t.context.wait.ready ();
t.context.tree.copy ( dir, copydir );
await t.context.wait.time ();
t.context.deepEqualResults ( ['add'], [copyfile] );
}));
it ( 'should detect "add" when copying a deep parent directory', withContext ( async t => {
const home = 'home';
const dir = 'home/e/sub';
const copydir = dir + Math.random ();
const copyfile = copydir + '/file1';
t.context.watchForFiles ( home, { debounce: 0, ignoreInitial: true, recursive: true } );
await t.context.wait.ready ();
t.context.tree.copy ( dir, copydir );
await t.context.wait.time ();
t.context.deepEqualResults ( ['add'], [copyfile] );
}));
it ( 'should detect "change" when modifying a file inside a directory', withContext ( async t => {
const dir = 'home/a';
const file = 'home/a/file1';
t.context.watchForFiles ( dir, { debounce: 0, ignoreInitial: true } );
await t.context.wait.ready ();
t.context.tree.modify ( file );
await t.context.wait.time ();
t.context.deepEqualResults ( ['change'], [file] );
}));
it ( 'should detect "change" when modifying a file inside a deep directory', withContext ( async t => {
const dir = 'home';
const file = 'home/a/file1';
t.context.watchForFiles ( dir, { debounce: 0, ignoreInitial: true, recursive: true } );
await t.context.wait.ready ();
t.context.tree.modify ( file );
await t.context.wait.time ();
t.context.deepEqualResults ( ['change'], [file] );
}));
it ( 'should detect "change" when renaming a non-empty file and rerenaming it', withContext ( async t => {
const dir = 'home/a';
const file = 'home/a/file1';
const filealt = 'home/a/file1_alt';
t.context.watchForFiles ( dir, { debounce: 300, ignoreInitial: true } );
await t.context.wait.ready ();
t.context.tree.modify ( file );
t.context.tree.rename ( file, filealt );
t.context.tree.rename ( filealt, file );
await t.context.wait.time ();
t.context.deepEqualResults ( ['change'], [file] );
}));
it ( 'should detect "unlink" when removing a single file', withContext ( async t => {
const file = 'home/a/file1';
t.context.watchForFiles ( file, { debounce: 0, ignoreInitial: true } );
await t.context.wait.ready ();
t.context.hasWatchObjects ( 0, 0, 2 );
t.context.tree.remove ( file );
await t.context.wait.time ();
t.context.hasWatchObjects ( 0, 1, 1 );
t.context.deepEqualResults ( ['unlink'], [file] );
}));
it ( 'should detect "unlink" and "add" when removing a single file and much later recreating it', withContext ( async t => {
const file = 'home/a/file1';
t.context.watchForFiles ( file, { debounce: 0, ignoreInitial: true } );
await t.context.wait.ready ();
t.context.hasWatchObjects ( 0, 0, 2 );
t.context.tree.remove ( file );
await t.context.wait.time ();
t.context.hasWatchObjects ( 0, 1, 1 );
t.context.deepEqualResults ( ['unlink'], [file] );
t.context.tree.newFile ( file );
await t.context.wait.time ();
t.context.hasWatchObjects ( 0, 0, 2 );
t.context.deepEqualResults ( ['add'], [file] );
}));
it ( 'should detect "unlink" when removing a file inside a directory', withContext ( async t => {
const dir = 'home/a';
const file = 'home/a/file1';
t.context.watchForFiles ( dir, { debounce: 0, ignoreInitial: true } );
await t.context.wait.ready ();
t.context.tree.remove ( file );
await t.context.wait.time ();
t.context.deepEqualResults ( ['unlink'], [file] );
}));
it ( 'should detect "unlink" when removing a file inside a deep directory', withContext ( async t => {
const dir = 'home';
const file = 'home/a/file1';
t.context.watchForFiles ( dir, { debounce: 0, ignoreInitial: true, recursive: true } );
await t.context.wait.ready ();
t.context.tree.remove ( file );
await t.context.wait.time ();
t.context.deepEqualResults ( ['unlink'], [file] );
}));
it ( 'should detect "unlink" when removing a parent directory', withContext ( async t => {
const dir = 'home';
const file1 = 'home/a/file1';
const file2 = 'home/a/file2';
t.context.watchForFiles ( dir, { debounce: 0, ignoreInitial: true, recursive: true } );
await t.context.wait.ready ();
t.context.tree.remove ( 'home/a' );
await t.context.wait.time ();
t.context.deepEqualUnorderedResults ( ['unlink', 'unlink'], [file1, file2] );
}));
it ( 'should detect "unlink" when removing a parent directory of the watcher', withContext ( async t => {
const dir = 'home/e/sub';
const file = 'home/e/sub/file1';
t.context.watchForFiles ( dir, { debounce: 0, ignoreInitial: true, recursive: true } );
await t.context.wait.ready ();
t.context.hasWatchObjects ( 0, 0, 3 )
t.context.tree.remove ( 'home/e' );
await t.context.wait.time ();
t.context.hasWatchObjects ( 1, 0, 0 );
t.context.deepEqualResults ( ['unlink'], [file] );
}));
it ( 'should detect "unlink" and "add" when renaming a file inside a directory', withContext ( async t => {
const dir = 'home/a';
const file1 = 'home/a/file1';
const file1alt = 'home/a/file1_alt';
t.context.watchForFiles ( dir, { debounce: 0, ignoreInitial: true } );
await t.context.wait.ready ();
t.context.tree.rename ( file1, file1alt );
await t.context.wait.time ();
t.context.deepEqualUnorderedResults ( ['unlink', 'add'], [file1, file1alt] );
}));
it ( 'should detect "unlink" and "add" when renaming a file inside a deep directory', withContext ( async t => {
const dir = 'home';
const file1 = 'home/a/file1';
const file1alt = 'home/a/file1_alt';
t.context.watchForFiles ( dir, { debounce: 0, ignoreInitial: true, recursive: true } );
await t.context.wait.ready ();
t.context.tree.rename ( file1, file1alt );
await t.context.wait.time ();
t.context.deepEqualUnorderedResults ( ['unlink', 'add'], [file1, file1alt] );
}));
it ( 'should detect "unlink" and "add" when renaming a parent directory', withContext ( async t => {
const dir = 'home';
const file1 = 'home/a/file1';
const file1alt = 'home/a_alt/file1';
const file2 = 'home/a/file2';
const file2alt = 'home/a_alt/file2';
t.context.watchForFiles ( dir, { debounce: 300, ignoreInitial: true, recursive: true } );
await t.context.wait.ready ();
t.context.tree.rename ( 'home/a', 'home/a_alt' );
await t.context.wait.time ();
t.context.deepEqualUnorderedResults ( ['unlink', 'add', 'unlink', 'add'], [file1, file1alt, file2, file2alt] );
}));
it ( 'should detect a single "add" when creating a new file and modifying it', withContext ( async t => {
const dir = 'home/a';
const newfile = 'home/a/file1' + Math.random ();
t.context.watchForFiles ( dir, { debounce: 300, ignoreInitial: true } );
await t.context.wait.ready ();
t.context.tree.newFile ( newfile );
t.context.tree.modify ( newfile );
await t.context.wait.time ();
t.context.deepEqualResults ( ['add'], [newfile] );
}));
it ( 'should detect a single "change" when removing a file and creating it', withContext ( async t => {
const dir = 'home/a';
const file = 'home/a/file1';
t.context.watchForFiles ( dir, { debounce: 300, ignoreInitial: true } );
await t.context.wait.ready ();
t.context.tree.remove ( file );
t.context.tree.newFile ( file );
await t.context.wait.time ();
t.context.deepEqualResults ( ['change'], [file] );
}));
it ( 'should detect a single "unlink" when modifying a file and removing it', withContext ( async t => {
const dir = 'home/a';
const file = 'home/a/file1';
t.context.watchForFiles ( dir, { debounce: 300, ignoreInitial: true } );
await t.context.wait.ready ();
t.context.tree.modify ( file );
t.context.tree.remove ( file );
await t.context.wait.time ();
t.context.deepEqualResults ( ['unlink'], [file] );
}));
it ( 'should detect nothing when creating a new file and removing it', withContext ( async t => {
const dir = 'home/a';
const newfile = 'home/a/file1' + Math.random ();
t.context.watchForFiles ( dir, { debounce: 300, ignoreInitial: true } );
await t.context.wait.ready ();
t.context.tree.newFile ( newfile );
t.context.tree.remove ( newfile );
await t.context.wait.time ();
t.context.deepEqualResults ( [], [] );
}));
it ( 'should detect nothing when renaming an empty file and rerenaming it', withContext ( async t => {
const dir = 'home/a';
const file = 'home/a/file1';
const filealt = 'home/a/file1_alt';
t.context.watchForFiles ( dir, { debounce: 300, ignoreInitial: true } );
await t.context.wait.ready ();
t.context.tree.rename ( file, filealt );
t.context.tree.rename ( filealt, file );
await t.context.wait.time ();
t.context.deepEqualResults ( [], [] );
}));
it ( 'should detect nothing when renaming a parent directory and rerenaming it', withContext ( async t => {
const dir = 'home/a';
const diralt = 'home/a_alt';
t.context.watchForFiles ( dir, { debounce: 300, ignoreInitial: true } );
await t.context.wait.ready ();
t.context.tree.rename ( dir, diralt );
t.context.tree.rename ( diralt, dir );
await t.context.wait.time ();
t.context.deepEqualResults ( [], [] );
}));
it ( 'should detect "unlink" when removing a file and creating a directory of the same name', withContext ( async t => {
const dir = 'home/a';
const file = 'home/a/file1';
t.context.watchForFiles ( dir, { debounce: 300, ignoreInitial: true } );
await t.context.wait.ready ();
t.context.tree.remove ( file );
t.context.tree.newDir ( file );
await t.context.wait.time ();
t.context.deepEqualResults ( ['unlink'], [file] );
}));
it ( 'should detect "add" when removing a directory and creating a file of the same name', withContext ( async t => {
const dir = 'home';
const file = 'home/a';
t.context.watchForFiles ( dir, { debounce: 300, ignoreInitial: true } );
await t.context.wait.ready ();
t.context.tree.remove ( file );
t.context.tree.newFile ( file );
await t.context.wait.time ();
t.context.deepEqualResults ( ['add'], [file] );
}));
it ( 'should detect "change" when replacing a parent directory with another one of the same name', withContext ( async t => {
const dir = 'home';
t.context.watchForFiles ( dir, { debounce: 300, ignoreInitial: true, recursive: true } );
await t.context.wait.ready ();
t.context.tree.remove ( 'home/a' );
t.context.tree.copy ( 'home/b', 'home/a' );
await t.context.wait.time ();
t.context.deepEqualUnorderedResults ( ['change', 'change'], ['home/a/file1', 'home/a/file2'] );
}));
it ( 'should be able to handle many "unlink" events', withContext ( async t => {
const dir = 'home/a';
const files = t.context.tree.newFiles ( dir, 100 );
t.context.watchForFiles ( dir, { debounce: 0, ignoreInitial: true } );
await t.context.wait.ready ();
await t.context.wait.longtime ()
files.forEach ( file => t.context.tree.remove ( file ) );
await t.context.wait.longtime ()
t.is ( t.context.events.length, 100 );
}));
});
describe ( 'file events (with renames)', it => {
it ( 'should detect initial "add" for a single file', withContext ( async t => {
const file = 'home/a/file1';
t.context.watchForFiles ( file, { debounce: 0, renameDetection: true } );
await t.context.wait.ready ();
await t.context.wait.longtime ();
t.context.deepEqualResults ( ['add'], [file] );
}));
it ( 'should detect initial "add" for multiple files', withContext ( async t => {
const file1 = 'home/a/file1';
const file2 = 'home/a/file2';
t.context.watchForFiles ( [file1, file2], { debounce: 0, renameDetection: true } );
await t.context.wait.ready ();
await t.context.wait.longtime ();
t.context.deepEqualUnorderedResults ( ['add', 'add'], [file1, file2] );
}));
it ( 'should detect initial "add" for all files inside a directory', withContext ( async t => {
const dir = 'home/a';
const file1 = 'home/a/file1';
const file2 = 'home/a/file2';
t.context.watchForFiles ( dir, { debounce: 0, renameDetection: true } );
await t.context.wait.ready ();
await t.context.wait.longtime ();
t.context.deepEqualUnorderedResults ( ['add', 'add'], [file1, file2] );
}));
it ( 'should detect initial "add" for all files inside a deep directory', withContext ( async t => {
const dir = 'home/e';
const file1 = 'home/e/file1';
const file2 = 'home/e/file2';
const filesub1 = 'home/e/sub/file1';
t.context.watchForFiles ( dir, { debounce: 0, recursive: true, renameDetection: true } );
await t.context.wait.ready ();
await t.context.wait.longtime ();
t.context.deepEqualUnorderedResults ( ['add', 'add', 'add'], [file1, file2, filesub1] );
}));
it ( 'should detect "add" when creating a new file inside a directory', withContext ( async t => {
const dir = 'home/a';
const newfile = 'home/a/file1' + Math.random ();
t.context.watchForFiles ( dir, { debounce: 0, ignoreInitial: true, renameDetection: true } );
await t.context.wait.ready ();
t.context.tree.newFile ( newfile );
await t.context.wait.longtime ();
t.context.deepEqualResults ( ['add'], [newfile] );
}));
it ( 'should detect "add" when creating a new file inside a new directory', withContext ( async t => {
const dir = 'home/a';
const newdir = 'home/a/newdir' + Math.random ();
const newfile = newdir + '/file1' + Math.random ();
t.context.watchForFiles ( dir, { debounce: 0, ignoreInitial: true, recursive: true, renameDetection: true } );
await t.context.wait.ready ();
t.context.tree.newFile ( newfile );
await t.context.wait.longtime ();
t.context.deepEqualResults ( ['add'], [newfile] );
}));
it ( 'should detect "add" when creating a new file inside a new deep directory', withContext ( async t => {
const dir = 'home';
const newdir = 'home/a/newdir' + Math.random ();
const newfile = newdir + '/file1' + Math.random ();
t.context.watchForFiles ( dir, { debounce: 0, ignoreInitial: true, recursive: true, renameDetection: true } );
await t.context.wait.ready ();
t.context.tree.newFile ( newfile );
await t.context.wait.longtime ();
t.context.deepEqualResults ( ['add'], [newfile] );
}));
it ( 'should detect "add" when copying a file inside a directory', withContext ( async t => {
const dir = 'home/a';
const file = 'home/a/file1';
const copyfile = file + Math.random ();
t.context.watchForFiles ( dir, { debounce: 0, ignoreInitial: true, renameDetection: true } );
await t.context.wait.ready ();
t.context.tree.copy ( file, copyfile );
await t.context.wait.longtime ();
t.context.deepEqualResults ( ['add'], [copyfile] );
}));
it ( 'should detect "add" when copying a file inside a deep directory', withContext ( async t => {
const dir = 'home';
const file = 'home/a/file1';
const copyfile = file + Math.random ();
t.context.watchForFiles ( dir, { debounce: 0, ignoreInitial: true, recursive: true, renameDetection: true } );
await t.context.wait.ready ();
t.context.tree.copy ( file, copyfile );
await t.context.wait.longtime ();
t.context.deepEqualResults ( ['add'], [copyfile] );
}));
it ( 'should detect "add" when copying a parent directory', withContext ( async t => {
const home = 'home/e';
const dir = 'home/e/sub';
const copydir = dir + Math.random ();
const copyfile = copydir + '/file1';
t.context.watchForFiles ( home, { debounce: 0, ignoreInitial: true, renameDetection: true } );
await t.context.wait.ready ();
t.context.tree.copy ( dir, copydir );
await t.context.wait.longtime ();
t.context.deepEqualResults ( ['add'], [copyfile] );
}));
it ( 'should detect "add" when copying a deep parent directory', withContext ( async t => {
const home = 'home';
const dir = 'home/e/sub';
const copydir = dir + Math.random ();
const copyfile = copydir + '/file1';
t.context.watchForFiles ( home, { debounce: 0, ignoreInitial: true, recursive: true, renameDetection: true } );
await t.context.wait.ready ();
t.context.tree.copy ( dir, copydir );
await t.context.wait.longtime ();
t.context.deepEqualResults ( ['add'], [copyfile] );
}));
it ( 'should detect "change" when modifying a file inside a directory', withContext ( async t => {
const dir = 'home/a';
const file = 'home/a/file1';
t.context.watchForFiles ( dir, { debounce: 0, ignoreInitial: true, renameDetection: true } );
await t.context.wait.ready ();
t.context.tree.modify ( file );
await t.context.wait.longtime ();
t.context.deepEqualResults ( ['change'], [file] );
}));
it ( 'should detect "change" when modifying a file inside a deep directory', withContext ( async t => {
const dir = 'home';
const file = 'home/a/file1';
t.context.watchForFiles ( dir, { debounce: 0, ignoreInitial: true, recursive: true, renameDetection: true } );
await t.context.wait.ready ();
t.context.tree.modify ( file );
await t.context.wait.longtime ();
t.context.deepEqualResults ( ['change'], [file] );
}));
it ( 'should detect "change" when renaming a non-empty file and rerenaming it', withContext ( async t => {
const dir = 'home/a';
const file = 'home/a/file1';
const filealt = 'home/a/file1_alt';
t.context.watchForFiles ( dir, { debounce: 300, ignoreInitial: true, renameDetection: true } );
await t.context.wait.ready ();
t.context.tree.modify ( file );
t.context.tree.rename ( file, filealt );
t.context.tree.rename ( filealt, file );
await t.context.wait.longtime ();
t.context.deepEqualResults ( ['change'], [file] );
}));
it ( 'should detect "unlink" when removing a single file', withContext ( async t => {
const file = 'home/a/file1';
t.context.watchForFiles ( file, { debounce: 0, ignoreInitial: true, renameDetection: true } );
await t.context.wait.ready ();
t.context.hasWatchObjects ( 0, 0, 2 );
t.context.tree.remove ( file );
await t.context.wait.longtime ();
t.context.hasWatchObjects ( 0, 1, 1 );
t.context.deepEqualResults ( ['unlink'], [file] );
}));
it ( 'should detect "unlink" and "add" when removing a single file and much later recreating it', withContext ( async t => {
const file = 'home/a/file1';
t.context.watchForFiles ( file, { debounce: 0, ignoreInitial: true, renameDetection: true } );
await t.context.wait.ready ();
t.context.hasWatchObjects ( 0, 0, 2 );
t.context.tree.remove ( file );
await t.context.wait.longtime ();
t.context.hasWatchObjects ( 0, 1, 1 );
t.context.deepEqualResults ( ['unlink'], [file] );
t.context.tree.newFile ( file );
await t.context.wait.longlongtime ();
t.context.hasWatchObjects ( 0, 0, 2 );
t.context.deepEqualResults ( ['add'], [file] );
}));
it ( 'should detect "unlink" when removing a file inside a directory', withContext ( async t => {
const dir = 'home/a';
const file = 'home/a/file1';
t.context.watchForFiles ( dir, { debounce: 0, ignoreInitial: true, renameDetection: true } );
await t.context.wait.ready ();
t.context.tree.remove ( file );
await t.context.wait.longtime ();
t.context.deepEqualResults ( ['unlink'], [file] );
}));
it ( 'should detect "unlink" when removing a file inside a deep directory', withContext ( async t => {
const dir = 'home';
const file = 'home/a/file1';
t.context.watchForFiles ( dir, { debounce: 0, ignoreInitial: true, recursive: true, renameDetection: true } );
await t.context.wait.ready ();
t.context.tree.remove ( file );
await t.context.wait.longtime ();
t.context.deepEqualResults ( ['unlink'], [file] );
}));
it ( 'should detect "unlink" when removing a parent directory', withContext ( async t => {
const dir = 'home';
const file1 = 'home/a/file1';
const file2 = 'home/a/file2';
t.context.watchForFiles ( dir, { debounce: 0, ignoreInitial: true, recursive: true, renameDetection: true } );
await t.context.wait.ready ();
t.context.tree.remove ( 'home/a' );
await t.context.wait.longtime ();
t.context.deepEqualUnorderedResults ( ['unlink', 'unlink'], [file1, file2] );
}));
it ( 'should detect "unlink" when removing a parent directory of the watcher', withContext ( async t => {
const dir = 'home/e/sub';
const file = 'home/e/sub/file1';
t.context.watchForFiles ( dir, { debounce: 0, ignoreInitial: true, recursive: true, renameDetection: true } );
await t.context.wait.ready ();
t.context.hasWatchObjects ( 0, 0, 3 )
t.context.tree.remove ( 'home/e' );
await t.context.wait.longtime ();
t.context.hasWatchObjects ( 1, 0, 0 );
t.context.deepEqualResults ( ['unlink'], [file] );
}));
it ( 'should detect "rename" when renaming a file inside a directory', withContext ( async t => {
const dir = 'home/a';
const file1 = 'home/a/file1';
const file1alt = 'home/a/file1_alt';
t.context.watchForFiles ( dir, { debounce: 0, ignoreInitial: true, renameDetection: true } );
await t.context.wait.ready ();
t.context.tree.rename ( file1, file1alt );
await t.context.wait.longtime ();
t.context.deepEqualResults ( ['rename'], [[file1, file1alt]] );
}));
it ( 'should detect "rename" when renaming a file inside a deep directory', withContext ( async t => {
const dir = 'home';
const file1 = 'home/a/file1';
const file1alt = 'home/a/file1_alt';
t.context.watchForFiles ( dir, { debounce: 0, ignoreInitial: true, recursive: true, renameDetection: true } );
await t.context.wait.ready ();
t.context.tree.rename ( file1, file1alt );
await t.context.wait.longtime ();
t.context.deepEqualResults ( ['rename'], [[file1, file1alt]] );
}));
it ( 'should detect "rename" when renaming a parent directory', withContext ( async t => {
const dir = 'home';
const file1 = 'home/a/file1';
const file1alt = 'home/a_alt/file1';
const file2 = 'home/a/file2';
const file2alt = 'home/a_alt/file2';
t.context.watchForFiles ( dir, { debounce: 300, ignoreInitial: true, recursive: true, renameDetection: true } );
await t.context.wait.ready ();
t.context.tree.rename ( 'home/a', 'home/a_alt' );
await t.context.wait.longlongtime ();
t.context.deepEqualUnorderedResults ( ['rename', 'rename'], [[file1, file1alt], [file2, file2alt]] );
}));
it ( 'should detect "rename" when renaming a file inside a directory case-sensitively', withContext ( async t => {
const dir = 'home/a';
const file1 = 'home/a/file1';
const File1 = 'home/a/File1';
t.context.watchForFiles ( dir, { debounce: 0, ignoreInitial: true, renameDetection: true } );
await t.context.wait.ready ();
t.context.tree.rename ( file1, File1 );
await t.context.wait.longtime ();
t.context.deepEqualResults ( ['rename'], [[file1, File1]] );
}));
it ( 'should detect "rename" when renaming a file inside a deep directory case-sensitively', withContext ( async t => {
const dir = 'home';
const file1 = 'home/a/file1';
const File1 = 'home/a/File1';
t.context.watchForFiles ( dir, { debounce: 0, ignoreInitial: true, recursive: true, renameDetection: true } );
await t.context.wait.ready ();
t.context.tree.rename ( file1, File1 );
await t.context.wait.longtime ();
t.context.deepEqualResults ( ['rename'], [[file1, File1]] );
}));
it ( 'should detect "rename" when renaming a parent directory case-sensitively', withContext ( async t => {
const dir = 'home';
const file1 = 'home/a/file1';
const File1 = 'home/A/file1';
const file2 = 'home/a/file2';
const File2 = 'home/A/file2';
t.context.watchForFiles ( dir, { debounce: 300, ignoreInitial: true, recursive: true, renameDetection: true } );
await t.context.wait.ready ();
t.context.tree.rename ( 'home/a', 'home/A' );
await t.context.wait.longlongtime ();
t.context.deepEqualUnorderedResults ( ['rename', 'rename'], [[file1, File1], [file2, File2]] );
}));
it ( 'should detect a single "add" when creating a new file and modifying it', withContext ( async t => {
const dir = 'home/a';
const newfile = 'home/a/file1' + Math.random ();
t.context.watchForFiles ( dir, { debounce: 300, ignoreInitial: true, renameDetection: true } );
await t.context.wait.ready ();
t.context.tree.newFile ( newfile );
t.context.tree.modify ( newfile );
await t.context.wait.longtime ();
t.context.deepEqualResults ( ['add'], [newfile] );
}));
it ( 'should detect a single "change" when removing a file and creating it', withContext ( async t => {
const dir = 'home/a';
const file = 'home/a/file1';
t.context.watchForFiles ( dir, { debounce: 300, ignoreInitial: true, renameDe