863 lines
24 KiB
JavaScript
863 lines
24 KiB
JavaScript
|
import path from 'path'
|
||
|
import assert from '../lib/assert.mjs'
|
||
|
import t from '../lib/eltro.mjs'
|
||
|
import { Builder, Counter } from './watch/builder.mjs'
|
||
|
import watch from '../lib/watch/index.mjs'
|
||
|
import * as is from '../lib/watch/is.mjs'
|
||
|
import hasNativeRecursive from '../lib/watch/has-native-recursive.mjs'
|
||
|
|
||
|
const builder = new Builder()
|
||
|
let watcher
|
||
|
|
||
|
function htTimeToMs(end) {
|
||
|
return end[0] * 1000 + Math.round(end[1] / 1000000)
|
||
|
}
|
||
|
|
||
|
t.before(function() {
|
||
|
return builder.init()
|
||
|
})
|
||
|
|
||
|
t.afterEach(function(done) {
|
||
|
if (watcher && !watcher.isClosed()) {
|
||
|
watcher.on('close', done)
|
||
|
watcher.close()
|
||
|
} else {
|
||
|
done()
|
||
|
}
|
||
|
})
|
||
|
|
||
|
t.after(function() {
|
||
|
if (builder) {
|
||
|
builder.cleanup()
|
||
|
}
|
||
|
})
|
||
|
|
||
|
function wait(fn, timeout) {
|
||
|
try {
|
||
|
fn()
|
||
|
} catch (error) {
|
||
|
timeout -= 30
|
||
|
if (timeout >= 0) {
|
||
|
setTimeout(function() {
|
||
|
wait(fn, timeout)
|
||
|
}, 30)
|
||
|
} else {
|
||
|
throw error
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
t.describe('process events', function() {
|
||
|
t.test('should emit `close` event', function(done) {
|
||
|
var file = 'home/a/file1'
|
||
|
var fpath = builder.getPath(file)
|
||
|
watcher = watch(fpath, function() {})
|
||
|
watcher.on('close', done)
|
||
|
watcher.close()
|
||
|
})
|
||
|
|
||
|
t.test('should emit `ready` event when watching a file', function(done) {
|
||
|
var file = 'home/a/file1'
|
||
|
var fpath = builder.getPath(file)
|
||
|
watcher = watch(fpath)
|
||
|
watcher.on('ready', done)
|
||
|
})
|
||
|
|
||
|
t.test('should emit `ready` event when watching a directory recursively', function(done) {
|
||
|
var dir = builder.getPath('home')
|
||
|
watcher = watch(dir, { recursive: true })
|
||
|
watcher.on('ready', done)
|
||
|
})
|
||
|
|
||
|
t.test('should emit `ready` properly in a composed watcher', function(done) {
|
||
|
var dir1 = builder.getPath('home/a')
|
||
|
var dir2 = builder.getPath('home/b')
|
||
|
var file = builder.getPath('home/b/file1')
|
||
|
watcher = watch([dir1, dir2, file], { recursive: true })
|
||
|
watcher.on('ready', done)
|
||
|
})
|
||
|
})
|
||
|
|
||
|
t.describe('watch for files', function() {
|
||
|
t.test('should watch a single file and keep watching', function(done) {
|
||
|
var counter = new Counter(done, 3)
|
||
|
var file = 'home/a/file1'
|
||
|
var fpath = builder.getPath(file)
|
||
|
watcher = watch(fpath, { delay: 0 }, function(evt, name) {
|
||
|
assert.strictEqual(fpath, name)
|
||
|
counter.count()
|
||
|
})
|
||
|
watcher.on('ready', function() {
|
||
|
counter.waitForCount(builder.modify(file))
|
||
|
.then(() => counter.waitForCount(builder.modify(file)))
|
||
|
.then(() => counter.waitForCount(builder.modify(file)))
|
||
|
})
|
||
|
})
|
||
|
|
||
|
t.test('should watch files inside a directory', function(done) {
|
||
|
var fpath = builder.getPath('home/a')
|
||
|
var set = new Set()
|
||
|
set.add(builder.getPath('home/a/file1'))
|
||
|
set.add(builder.getPath('home/a/file2'))
|
||
|
|
||
|
Promise.all([
|
||
|
builder.newFile('home/a/file1'),
|
||
|
builder.newFile('home/a/file2'),
|
||
|
]).then(() => {
|
||
|
watcher = watch(fpath, { delay: 0 }, function(evt, name) {
|
||
|
set.delete(name)
|
||
|
if (!set.size) {
|
||
|
done()
|
||
|
}
|
||
|
})
|
||
|
|
||
|
watcher.on('ready', function() {
|
||
|
Promise.all([
|
||
|
builder.modify('home/a/file1'),
|
||
|
builder.modify('home/a/file2'),
|
||
|
]).then()
|
||
|
})
|
||
|
})
|
||
|
})
|
||
|
|
||
|
t.test('should debounce multiple triggers', function(done) {
|
||
|
var counter = new Counter()
|
||
|
var file = 'home/a/file2'
|
||
|
var fpath = builder.getPath(file)
|
||
|
|
||
|
var start = process.hrtime()
|
||
|
var middle = start
|
||
|
var end = start
|
||
|
|
||
|
watcher = watch(fpath, { delay: 100 }, function(evt, name) {
|
||
|
if (fpath === name) counter.count()
|
||
|
})
|
||
|
|
||
|
watcher.on('ready', function() {
|
||
|
builder.modify(file)
|
||
|
.then(() => builder.modify(file))
|
||
|
.then(() => builder.modify(file))
|
||
|
.then(() => {
|
||
|
middle = htTimeToMs(process.hrtime(start))
|
||
|
return counter.waitForCount()
|
||
|
})
|
||
|
.then(() => {
|
||
|
assert.strictEqual(counter.counter, 1)
|
||
|
end = htTimeToMs(process.hrtime(start))
|
||
|
|
||
|
assert.ok(end - middle > 50)
|
||
|
assert.ok(end >= 100)
|
||
|
done()
|
||
|
})
|
||
|
.catch(done)
|
||
|
})
|
||
|
})
|
||
|
|
||
|
t.test('should listen to new created files', function(done) {
|
||
|
var home = builder.getPath('home')
|
||
|
var counter = new Counter()
|
||
|
var newfile1 = 'home/a/newfile' + builder.randomName()
|
||
|
var newfile2 = 'home/a/newfile' + builder.randomName()
|
||
|
var set = new Set([
|
||
|
builder.getPath(newfile1),
|
||
|
builder.getPath(newfile2),
|
||
|
])
|
||
|
var changes = []
|
||
|
var extra = []
|
||
|
watcher = watch(home, { delay: 0, recursive: true }, function(evt, name) {
|
||
|
if (set.has(name)) {
|
||
|
changes.push(name)
|
||
|
counter.count()
|
||
|
} else {
|
||
|
extra.push(name)
|
||
|
}
|
||
|
})
|
||
|
watcher.on('ready', function() {
|
||
|
counter.waitForCount(builder.newFile(newfile1))
|
||
|
.then(() => counter.waitForCount(builder.newFile(newfile2)))
|
||
|
.then(() => {
|
||
|
assert.deepStrictEqual(changes, [...set.values()])
|
||
|
assert.deepStrictEqual(extra, [])
|
||
|
done()
|
||
|
})
|
||
|
.catch(done)
|
||
|
})
|
||
|
})
|
||
|
|
||
|
t.test('should error when parent gets deleted before calling fs.watch', function(done) {
|
||
|
var fpath = builder.getPath('home/a/file1')
|
||
|
builder.newFile('home/a/file1')
|
||
|
.then(() => {
|
||
|
watcher = watch(fpath, Object.defineProperty({}, 'test', {
|
||
|
enumerable: true,
|
||
|
get: function() {
|
||
|
builder.removeSync('home/a')
|
||
|
return 'test'
|
||
|
}
|
||
|
}))
|
||
|
watcher.on('error', function() {
|
||
|
done()
|
||
|
})
|
||
|
})
|
||
|
})
|
||
|
})
|
||
|
|
||
|
t.describe('watch for directories', function() {
|
||
|
t.test('should watch directories inside a directory', function(done) {
|
||
|
var home = builder.getPath('home')
|
||
|
var dir = builder.getPath('home/c')
|
||
|
|
||
|
builder.createDirectory(dir).then(() => {
|
||
|
watcher = watch(home, { delay: 0, recursive: true }, function(evt, name) {
|
||
|
if (name === dir && evt === 'remove') {
|
||
|
done()
|
||
|
}
|
||
|
})
|
||
|
watcher.on('ready', function() {
|
||
|
builder.remove('home/c').then()
|
||
|
})
|
||
|
})
|
||
|
})
|
||
|
|
||
|
t.test('should watch new created directories', function(done) {
|
||
|
var home = builder.getPath('home')
|
||
|
|
||
|
builder.remove('home/new').then(() => {
|
||
|
watcher = watch(home, { delay: 0, recursive: true }, function(evt, name) {
|
||
|
if (name === builder.getPath('home/new/file1')) {
|
||
|
done()
|
||
|
}
|
||
|
})
|
||
|
watcher.on('ready', function() {
|
||
|
builder.newFile('home/new/file1')
|
||
|
.then(() => builder.modify('home/new/file1'))
|
||
|
.catch(done)
|
||
|
})
|
||
|
})
|
||
|
})
|
||
|
|
||
|
t.test('should not watch new created directories which are being skipped in the filter', function(done) {
|
||
|
var counter = new Counter()
|
||
|
var home = builder.getPath('home')
|
||
|
|
||
|
var options = {
|
||
|
delay: 0,
|
||
|
recursive: true,
|
||
|
filter: function(filePath, SKIP_FLAG) {
|
||
|
if (/ignored/.test(filePath)) {
|
||
|
counter.count()
|
||
|
return SKIP_FLAG
|
||
|
}
|
||
|
return false
|
||
|
}
|
||
|
}
|
||
|
|
||
|
builder.remove('home/ignored/file').then(() => {
|
||
|
watcher = watch(home, options, function(evt, name) {
|
||
|
assert.fail("should not watch new created directories which are being skipped in the filter event detect: " + name)
|
||
|
})
|
||
|
|
||
|
watcher.on('ready', function() {
|
||
|
builder.newFile('home/ignored/file')
|
||
|
.then(() => {
|
||
|
assert.ok(counter.counter)
|
||
|
done()
|
||
|
})
|
||
|
.catch(done)
|
||
|
})
|
||
|
})
|
||
|
})
|
||
|
|
||
|
t.test('should keep watching after removal of sub directory', function(done) {
|
||
|
var counter = new Counter(done, 3)
|
||
|
var home = builder.getPath('home')
|
||
|
var file1 = builder.getPath('home/e/file1')
|
||
|
var file2 = builder.getPath('home/e/file2')
|
||
|
var dir = builder.getPath('home/e/sub')
|
||
|
var set = new Set()
|
||
|
|
||
|
Promise.all([
|
||
|
builder.newFile('home/e/sub/testfile'),
|
||
|
builder.newFile('home/e/file1'),
|
||
|
builder.newFile('home/e/file2'),
|
||
|
]).then(() => {
|
||
|
|
||
|
watcher = watch(home, { delay: 0, recursive: true }, function(evt, name) {
|
||
|
if (name === dir || name === file1 || name === file2) {
|
||
|
if (!set.has(name)) {
|
||
|
set.add(name)
|
||
|
counter.count()
|
||
|
}
|
||
|
}
|
||
|
})
|
||
|
|
||
|
watcher.on('ready', function() {
|
||
|
builder.remove('home/e/sub')
|
||
|
builder.modify('home/e/file1')
|
||
|
builder.modify('home/e/file2')
|
||
|
})
|
||
|
})
|
||
|
})
|
||
|
|
||
|
t.test('should watch new directories without delay', function(done) {
|
||
|
var counter = new Counter(done, 1)
|
||
|
var home = builder.getPath('home')
|
||
|
|
||
|
builder.remove('home/new').then(() => {
|
||
|
watcher = watch(home, { delay: 200, recursive: true }, function(evt, name) {
|
||
|
if (name === builder.getPath('home/new/file1')) {
|
||
|
counter.count()
|
||
|
}
|
||
|
})
|
||
|
watcher.on('ready', function() {
|
||
|
builder.newFile('home/new/file1')
|
||
|
.then(() => builder.modify('home/new/file1'))
|
||
|
.then(() => builder.modify('home/new/file1'))
|
||
|
})
|
||
|
})
|
||
|
})
|
||
|
|
||
|
t.test('should error when directory gets deleted before calling fs.watch', function(done) {
|
||
|
var dir = 'home/c'
|
||
|
var fpath = builder.getPath(dir)
|
||
|
|
||
|
builder.createDirectory(dir).then(() => {
|
||
|
watcher = watch(fpath, Object.defineProperty({}, 'test', {
|
||
|
enumerable: true,
|
||
|
get: function() {
|
||
|
builder.removeSync(dir)
|
||
|
return 'test'
|
||
|
}
|
||
|
}))
|
||
|
watcher.on('error', function() {
|
||
|
done()
|
||
|
})
|
||
|
})
|
||
|
})
|
||
|
})
|
||
|
|
||
|
t.describe('file events', function() {
|
||
|
var file = 'home/a/file1'
|
||
|
var fpath = builder.getPath(file)
|
||
|
|
||
|
t.beforeEach(function() {
|
||
|
return builder.newFile(file)
|
||
|
})
|
||
|
|
||
|
t.test('should identify `remove` event', function(done) {
|
||
|
watcher = watch(fpath, { delay: 0 }, function(evt, name) {
|
||
|
if (evt === 'remove' && name === fpath) {
|
||
|
done()
|
||
|
}
|
||
|
})
|
||
|
watcher.on('ready', function() {
|
||
|
builder.remove(file).catch(done)
|
||
|
})
|
||
|
})
|
||
|
|
||
|
t.test('should identify `remove` event on directory', function(done) {
|
||
|
var dir = 'home/a'
|
||
|
var home = builder.getPath('home')
|
||
|
var fpath = builder.getPath(dir)
|
||
|
|
||
|
watcher = watch(home, { delay: 0 }, function(evt, name) {
|
||
|
if (evt === 'remove' && name === fpath) done()
|
||
|
})
|
||
|
watcher.on('ready', function() {
|
||
|
builder.remove(dir).catch(done)
|
||
|
})
|
||
|
})
|
||
|
|
||
|
t.test('should be able to handle many events on deleting', function(done) {
|
||
|
var dir = 'home/a'
|
||
|
var fpath = builder.getPath(dir)
|
||
|
|
||
|
builder.newRandomFiles(dir, 100).then(names => {
|
||
|
var counter = new Counter(done, names.length)
|
||
|
|
||
|
watcher = watch(fpath, { delay: 10 }, function(evt, name) {
|
||
|
if (evt === 'remove') counter.count()
|
||
|
})
|
||
|
|
||
|
watcher.on('ready', function() {
|
||
|
Promise.all(names.map(x => builder.remove(path.join(dir, x)))).catch(done)
|
||
|
})
|
||
|
})
|
||
|
})
|
||
|
|
||
|
t.test('should identify `update` event', function(done) {
|
||
|
var file = 'home/b/file1'
|
||
|
var fpath = builder.getPath(file)
|
||
|
builder.newFile(fpath).then(() => {
|
||
|
watcher = watch(fpath, { delay: 0 }, function(evt, name) {
|
||
|
if (evt === 'update' && name === fpath) done()
|
||
|
})
|
||
|
watcher.on('ready', function() {
|
||
|
builder.modify(file).catch(done)
|
||
|
})
|
||
|
})
|
||
|
})
|
||
|
|
||
|
t.test('should report `update` on new files', function(done) {
|
||
|
var dir = builder.getPath('home/a')
|
||
|
var file = 'home/a/newfile_' + builder.randomName()
|
||
|
var fpath = builder.getPath(file)
|
||
|
|
||
|
watcher = watch(dir, { delay: 0 }, function(evt, name) {
|
||
|
if (evt === 'update' && name === fpath) done()
|
||
|
})
|
||
|
watcher.on('ready', function() {
|
||
|
builder.newFile(file).catch(done)
|
||
|
})
|
||
|
})
|
||
|
})
|
||
|
|
||
|
t.describe('options', function() {
|
||
|
t.describe('recursive', function() {
|
||
|
t.test('should watch recursively with `recursive: true` option', function(done) {
|
||
|
var dir = builder.getPath('home')
|
||
|
var file = builder.getPath('home/bb/file1')
|
||
|
watcher = watch(dir, { delay: 0, recursive: true }, function(evt, name) {
|
||
|
if (file === name) {
|
||
|
done()
|
||
|
}
|
||
|
})
|
||
|
watcher.on('ready', function() {
|
||
|
builder.modify('home/bb/file1').catch(done)
|
||
|
})
|
||
|
})
|
||
|
})
|
||
|
|
||
|
t.describe('encoding', function() {
|
||
|
let options = {
|
||
|
delay: 0,
|
||
|
encoding: 'unknown'
|
||
|
};
|
||
|
|
||
|
var dir = 'home/a'
|
||
|
var fdir = builder.getPath('home/a')
|
||
|
var file = 'home/a/file1'
|
||
|
var fpath = builder.getPath(file)
|
||
|
|
||
|
t.before(() => {
|
||
|
return builder.newFile(file)
|
||
|
})
|
||
|
|
||
|
t.test('should throw on invalid encoding', function(done) {
|
||
|
options.encoding = 'unknown'
|
||
|
try {
|
||
|
watcher = watch(fdir, options)
|
||
|
} catch (e) {
|
||
|
done()
|
||
|
}
|
||
|
})
|
||
|
|
||
|
t.test('should accept an encoding string', function(done) {
|
||
|
options.encoding = 'utf8'
|
||
|
watcher = watch(fdir, options, done.finish(function(evt, name) {
|
||
|
assert.strictEqual(name.toString(), fpath)
|
||
|
}))
|
||
|
watcher.on('ready', function() {
|
||
|
builder.modify(file).catch(done)
|
||
|
})
|
||
|
})
|
||
|
|
||
|
t.test('should support buffer encoding', function(done) {
|
||
|
options.encoding = 'buffer'
|
||
|
watcher = watch(fdir, options, done.finish(function(evt, name) {
|
||
|
assert.ok(Buffer.isBuffer(name), 'not a Buffer')
|
||
|
assert.strictEqual(name.toString(), fpath)
|
||
|
}))
|
||
|
|
||
|
watcher.on('ready', function() {
|
||
|
builder.modify(file).catch(done)
|
||
|
})
|
||
|
})
|
||
|
|
||
|
t.test('should support base64 encoding', function(done) {
|
||
|
options.encoding = 'base64'
|
||
|
watcher = watch(fdir, options, done.finish(function(evt, name) {
|
||
|
assert.strictEqual(
|
||
|
name,
|
||
|
Buffer.from(fpath).toString('base64'),
|
||
|
'wrong base64 encoding'
|
||
|
)
|
||
|
}))
|
||
|
watcher.on('ready', function() {
|
||
|
builder.modify(file).catch(done)
|
||
|
})
|
||
|
})
|
||
|
|
||
|
t.test('should support hex encoding', function(done) {
|
||
|
options.encoding = 'hex'
|
||
|
watcher = watch(fdir, options, done.finish(function(evt, name) {
|
||
|
assert.strictEqual(
|
||
|
name,
|
||
|
Buffer.from(fpath).toString('hex'),
|
||
|
'wrong hex encoding'
|
||
|
)
|
||
|
}))
|
||
|
watcher.on('ready', function() {
|
||
|
builder.modify(file).catch(done)
|
||
|
})
|
||
|
})
|
||
|
})
|
||
|
|
||
|
t.describe('filter', function() {
|
||
|
t.test('should only watch filtered directories', function(done) {
|
||
|
var matchRegularDir = false
|
||
|
var matchIgnoredDir = false
|
||
|
var counter = new Counter(done.finish(function() {
|
||
|
assert(matchRegularDir, 'watch failed to detect regular file')
|
||
|
assert(!matchIgnoredDir, 'fail to ignore path `deep_node_modules`')
|
||
|
}), 1, true)
|
||
|
|
||
|
var options = {
|
||
|
delay: 0,
|
||
|
recursive: true,
|
||
|
filter: function(name) {
|
||
|
return !/deep_node_modules/.test(name)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
watcher = watch(builder.getPath('home'), options, function(evt, name) {
|
||
|
if (/deep_node_modules/.test(name)) {
|
||
|
matchIgnoredDir = true
|
||
|
} else {
|
||
|
matchRegularDir = true
|
||
|
}
|
||
|
counter.count()
|
||
|
})
|
||
|
watcher.on('ready', function() {
|
||
|
counter.startCounting()
|
||
|
builder.modify('home/deep_node_modules/ma/file1')
|
||
|
.then(() => builder.modify('home/b/file1'))
|
||
|
.catch(done)
|
||
|
})
|
||
|
})
|
||
|
|
||
|
t.test('should only report filtered files', function(done) {
|
||
|
var dir = builder.getPath('home')
|
||
|
var file1 = 'home/bb/file1'
|
||
|
var file2 = 'home/bb/file2'
|
||
|
|
||
|
|
||
|
var counter = new Counter(done.finish(function() {
|
||
|
assert.strictEqual(matchIgnoredFile, false, 'home/bb/file1 should be ignored')
|
||
|
}), 1, true)
|
||
|
|
||
|
var options = {
|
||
|
delay: 0,
|
||
|
recursive: true,
|
||
|
filter: function(name) {
|
||
|
return /file2/.test(name)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
var times = 0
|
||
|
var matchIgnoredFile = false
|
||
|
watcher = watch(dir, options, function(evt, name) {
|
||
|
if (name === builder.getPath(file1)) {
|
||
|
matchIgnoredFile = true
|
||
|
}
|
||
|
counter.count()
|
||
|
})
|
||
|
watcher.on('ready', function() {
|
||
|
counter.startCounting()
|
||
|
|
||
|
builder.modify(file2)
|
||
|
.then(() => builder.modify(file1))
|
||
|
.catch(done)
|
||
|
})
|
||
|
})
|
||
|
|
||
|
t.test('should be able to filter directly with regexp', function(done) {
|
||
|
var dir = builder.getPath('home')
|
||
|
var file1 = 'home/bb/file1'
|
||
|
var file2 = 'home/bb/file2'
|
||
|
|
||
|
|
||
|
var counter = new Counter(done.finish(function() {
|
||
|
assert.strictEqual(matchIgnoredFile, false, 'home/bb/file1 should be ignored')
|
||
|
}), 1, true)
|
||
|
|
||
|
var options = {
|
||
|
delay: 0,
|
||
|
recursive: true,
|
||
|
filter: /file2/
|
||
|
}
|
||
|
|
||
|
var times = 0
|
||
|
var matchIgnoredFile = false
|
||
|
watcher = watch(dir, options, function(evt, name) {
|
||
|
if (name === builder.getPath(file1)) {
|
||
|
matchIgnoredFile = true
|
||
|
}
|
||
|
counter.count()
|
||
|
})
|
||
|
watcher.on('ready', function() {
|
||
|
counter.startCounting()
|
||
|
|
||
|
builder.modify(file2)
|
||
|
.then(() => builder.modify(file1))
|
||
|
.catch(done)
|
||
|
})
|
||
|
})
|
||
|
|
||
|
t.test('should be able to skip subdirectories with `skip` flag', function(done) {
|
||
|
var home = builder.getPath('home')
|
||
|
var options = {
|
||
|
delay: 0,
|
||
|
recursive: true,
|
||
|
filter: function(name, skip) {
|
||
|
if (/\/deep_node_modules/.test(name)) return skip
|
||
|
}
|
||
|
}
|
||
|
watcher = watch(home, options)
|
||
|
|
||
|
watcher.getWatchedPaths(function(paths) {
|
||
|
hasNativeRecursive(function(supportRecursive) {
|
||
|
var watched = supportRecursive
|
||
|
// The skip flag has no effect to the platforms which support recursive option,
|
||
|
// so the home directory is the only one that's in the watching list.
|
||
|
? [home]
|
||
|
// The deep_node_modules and all its subdirectories should not be watched
|
||
|
// with skip flag specified in the filter.
|
||
|
: builder.getAllDirectories().filter(function(name) {
|
||
|
return !/\/deep_node_modules/.test(name)
|
||
|
})
|
||
|
|
||
|
assert.deepStrictEqual(
|
||
|
watched.sort(), paths.sort()
|
||
|
)
|
||
|
|
||
|
done()
|
||
|
})
|
||
|
})
|
||
|
})
|
||
|
})
|
||
|
})
|
||
|
|
||
|
t.describe('parameters', function() {
|
||
|
t.test('should throw error on non-existed file', function(done) {
|
||
|
var somedir = builder.getPath('home/somedir')
|
||
|
watcher = watch(somedir)
|
||
|
watcher.on('error', done.finish(function(err) {
|
||
|
assert.match(err.message, /not exist/)
|
||
|
}))
|
||
|
})
|
||
|
|
||
|
t.test('should accept filename as Buffer', function(done) {
|
||
|
var fpath = builder.getPath('home/a/file1')
|
||
|
watcher = watch(Buffer.from(fpath), { delay: 0 }, done.finish(function(evt, name) {
|
||
|
assert.strictEqual(name, fpath)
|
||
|
}))
|
||
|
watcher.on('ready', function() {
|
||
|
builder.modify('home/a/file1').catch(done)
|
||
|
})
|
||
|
})
|
||
|
|
||
|
t.test('should compose array of files or directories', function(done) {
|
||
|
var counter = new Counter(done, 2)
|
||
|
var file1 = 'home/a/file1'
|
||
|
var file2 = 'home/a/file2'
|
||
|
var fpaths = [
|
||
|
builder.getPath(file1),
|
||
|
builder.getPath(file2)
|
||
|
]
|
||
|
|
||
|
var times = 0
|
||
|
watcher = watch(fpaths, { delay: 0 }, function(evt, name) {
|
||
|
if (fpaths.indexOf(name) !== -1) counter.count()
|
||
|
})
|
||
|
|
||
|
watcher.on('ready', function() {
|
||
|
Promise.all([
|
||
|
builder.modify(file1),
|
||
|
builder.modify(file2),
|
||
|
]).catch(done)
|
||
|
})
|
||
|
})
|
||
|
|
||
|
t.test('should filter duplicate events for composed watcher', function(done) {
|
||
|
var home = 'home'
|
||
|
var dir = 'home/a'
|
||
|
var file1 = 'home/a/file1'
|
||
|
var file2 = 'home/a/file2'
|
||
|
var fpaths = [
|
||
|
builder.getPath(home),
|
||
|
builder.getPath(dir),
|
||
|
builder.getPath(file1),
|
||
|
builder.getPath(file2)
|
||
|
]
|
||
|
|
||
|
var changes = []
|
||
|
watcher = watch(fpaths, { delay: 100, recursive: true }, function(evt, name) {
|
||
|
changes.push(name)
|
||
|
})
|
||
|
|
||
|
watcher.on('ready', function() {
|
||
|
builder.modify(file1)
|
||
|
builder.modify(file2, 50)
|
||
|
|
||
|
wait(function() {
|
||
|
assert.deepStrictEqual(
|
||
|
changes,
|
||
|
[builder.getPath(file1), builder.getPath(file2)]
|
||
|
)
|
||
|
done()
|
||
|
}, 200)
|
||
|
})
|
||
|
})
|
||
|
})
|
||
|
|
||
|
t.describe('watcher object', function() {
|
||
|
t.test('should using watcher object to watch', function(done) {
|
||
|
var dir = builder.getPath('home/a')
|
||
|
var file = 'home/a/file1'
|
||
|
var fpath = builder.getPath(file)
|
||
|
|
||
|
watcher = watch(dir, { delay: 0 })
|
||
|
watcher.on('ready', function() {
|
||
|
watcher.on('change', function(evt, name) {
|
||
|
assert.strictEqual(evt, 'update')
|
||
|
assert.strictEqual(name, fpath)
|
||
|
done()
|
||
|
})
|
||
|
builder.modify(file)
|
||
|
})
|
||
|
})
|
||
|
|
||
|
t.describe('close()', function() {
|
||
|
t.test('should close a watcher using .close()', function(done) {
|
||
|
var dir = builder.getPath('home/a')
|
||
|
var file = 'home/a/file1'
|
||
|
var times = 0
|
||
|
watcher = watch(dir, { delay: 0 })
|
||
|
watcher.on('change', function(evt, name) {
|
||
|
times++
|
||
|
})
|
||
|
watcher.on('ready', function() {
|
||
|
|
||
|
watcher.close()
|
||
|
|
||
|
builder.modify(file)
|
||
|
builder.modify(file, 100)
|
||
|
|
||
|
wait(function() {
|
||
|
assert(watcher.isClosed(), 'watcher should be closed')
|
||
|
assert.strictEqual(times, 0, 'failed to close the watcher')
|
||
|
done()
|
||
|
}, 150)
|
||
|
})
|
||
|
})
|
||
|
|
||
|
t.test('should not watch after .close() is called', function(done) {
|
||
|
var dir = builder.getPath('home')
|
||
|
watcher = watch(dir, { delay: 0, recursive: true })
|
||
|
watcher.close()
|
||
|
|
||
|
watcher.getWatchedPaths(function(dirs) {
|
||
|
assert(dirs.length === 0)
|
||
|
done()
|
||
|
})
|
||
|
})
|
||
|
|
||
|
t.test('Do not emit after close', function(done) {
|
||
|
var dir = builder.getPath('home/a')
|
||
|
var file = 'home/a/file1'
|
||
|
var times = 0
|
||
|
watcher = watch(dir, { delay: 0 })
|
||
|
watcher.on('change', function(evt, name) {
|
||
|
times++
|
||
|
})
|
||
|
watcher.on('ready', function() {
|
||
|
|
||
|
watcher.close()
|
||
|
|
||
|
var timer = setInterval(function() {
|
||
|
builder.modify(file)
|
||
|
})
|
||
|
|
||
|
wait(function() {
|
||
|
clearInterval(timer)
|
||
|
assert(watcher.isClosed(), 'watcher should be closed')
|
||
|
assert.strictEqual(times, 0, 'failed to close the watcher')
|
||
|
done()
|
||
|
}, 100)
|
||
|
})
|
||
|
})
|
||
|
|
||
|
})
|
||
|
|
||
|
t.describe('getWatchedPaths()', function() {
|
||
|
t.test('should get all the watched paths', function(done) {
|
||
|
var home = builder.getPath('home')
|
||
|
watcher = watch(home, {
|
||
|
delay: 0,
|
||
|
recursive: true
|
||
|
})
|
||
|
watcher.getWatchedPaths(function(paths) {
|
||
|
hasNativeRecursive(function(supportRecursive) {
|
||
|
var watched = supportRecursive
|
||
|
// The home directory is the only one that's being watched
|
||
|
// if the recursive option is natively supported.
|
||
|
? [home]
|
||
|
// Otherwise it should include all its subdirectories.
|
||
|
: builder.getAllDirectories()
|
||
|
|
||
|
assert.deepStrictEqual(
|
||
|
watched.sort(), paths.sort()
|
||
|
)
|
||
|
|
||
|
done()
|
||
|
})
|
||
|
})
|
||
|
})
|
||
|
|
||
|
t.test('should get its parent path instead of the file itself', function(done) {
|
||
|
var file = builder.getPath('home/a/file1')
|
||
|
// The parent path is actually being watched instead.
|
||
|
var parent = builder.getPath('home/a')
|
||
|
|
||
|
watcher = watch(file, { delay: 0 })
|
||
|
|
||
|
watcher.getWatchedPaths(function(paths) {
|
||
|
assert.deepStrictEqual([parent], paths)
|
||
|
done()
|
||
|
})
|
||
|
})
|
||
|
|
||
|
t.test('should work correctly with composed watcher', function(done) {
|
||
|
var a = builder.getPath('home/a')
|
||
|
|
||
|
var b = builder.getPath('home/b')
|
||
|
var file = builder.getPath('home/b/file1')
|
||
|
|
||
|
var nested = builder.getPath('home/deep_node_modules')
|
||
|
var ma = builder.getPath('home/deep_node_modules/ma')
|
||
|
var mb = builder.getPath('home/deep_node_modules/mb')
|
||
|
var mc = builder.getPath('home/deep_node_modules/mc')
|
||
|
|
||
|
watcher = watch([a, file, nested], {
|
||
|
delay: 0,
|
||
|
recursive: true
|
||
|
})
|
||
|
|
||
|
watcher.getWatchedPaths(function(paths) {
|
||
|
hasNativeRecursive(function(supportRecursive) {
|
||
|
var watched = supportRecursive
|
||
|
? [a, b, nested]
|
||
|
: [a, b, nested, ma, mb, mc]
|
||
|
|
||
|
assert.deepStrictEqual(
|
||
|
watched.sort(), paths.sort()
|
||
|
)
|
||
|
|
||
|
done()
|
||
|
})
|
||
|
})
|
||
|
})
|
||
|
})
|
||
|
})
|