Compare commits
3 Commits
fe2f6ccca9
...
25f50483e1
Author | SHA1 | Date |
---|---|---|
Jonatan Nilsson | 25f50483e1 | |
Jonatan Nilsson | c09a4c805e | |
Jonatan Nilsson | b476d23a77 |
|
@ -276,7 +276,9 @@ Eltro.prototype.__runTest = async function(stats, test, prefix = 'Test', child =
|
|||
if (markRealTest.skipTest) {
|
||||
process.stdout.write(' \x1b[94m- ' + markRealTest.name + '\x1b[0m\n')
|
||||
} else if (!markRealTest.error) {
|
||||
if (!test.name.startsWith('~')) {
|
||||
process.stdout.write(' \x1b[32m√\x1b[90m ' + markRealTest.name + ' (' + markRealTest.totalTime + 'ms)\x1b[0m\n')
|
||||
}
|
||||
} else if (prefix === 'Test') {
|
||||
process.stdout.write(' \x1b[31m' + this.failedTests.length + ') ' + markRealTest.name + ' (' + markRealTest.totalTime + 'ms)\x1b[0m\n')
|
||||
}
|
||||
|
@ -298,14 +300,17 @@ Eltro.prototype.__runGroup = async function(g, stats) {
|
|||
}
|
||||
}
|
||||
if (g.before) {
|
||||
await this.__runTest(stats, g.before, 'Before')
|
||||
if (g.before.error) return
|
||||
for (let i = 0; i < g.before.length; i++) {
|
||||
await this.__runTest(stats, g.before[i], 'Before')
|
||||
if (g.before[i].error) return
|
||||
}
|
||||
}
|
||||
for (let x = 0; x < g.tests.length; x++) {
|
||||
if (!g.tests[x].skipTest && g.tests[x].isExclusive === g.hasExclusive) {
|
||||
|
||||
if (g.beforeEach) {
|
||||
await this.__runTest(stats, g.beforeEach, 'Before each: ', g.tests[x])
|
||||
for (let i = 0; i < g.beforeEach.length && !g.tests[x].error; i++) {
|
||||
await this.__runTest(stats, g.beforeEach[i], 'Before each: ', g.tests[x])
|
||||
}
|
||||
if (!g.tests[x].error) {
|
||||
await this.__runTest(stats, g.tests[x])
|
||||
}
|
||||
|
@ -313,24 +318,26 @@ Eltro.prototype.__runGroup = async function(g, stats) {
|
|||
await this.__runTest(stats, g.tests[x])
|
||||
}
|
||||
if (g.afterEach) {
|
||||
await this.__runTest(stats, g.afterEach, 'After each: ', g.tests[x])
|
||||
let oldError = g.tests[x].error
|
||||
g.tests[x].error = null
|
||||
for (let i = 0; i < g.afterEach.length && !g.tests[x].error; i++) {
|
||||
await this.__runTest(stats, g.afterEach[i], 'After each: ', g.tests[x])
|
||||
}
|
||||
if (oldError) {
|
||||
g.tests[x].error = oldError
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (let x = 0; x < g.groups.length; x++) {
|
||||
if (!g.groups[x].skipTest && g.hasExclusive === (g.groups[x].hasExclusive || g.groups[x].isExclusive)) {
|
||||
if (g.beforeEach) {
|
||||
await this.__runTest(stats, g.beforeEach, g.groups[x].name + ': ', g.beforeEach)
|
||||
if (g.beforeEach.error) continue
|
||||
}
|
||||
await this.__runGroup(g.groups[x], stats)
|
||||
if (g.afterEach) {
|
||||
await this.__runTest(stats, g.afterEach, g.groups[x].name + ': ', g.afterEach)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (g.after) {
|
||||
await this.__runTest(stats, g.after, 'After')
|
||||
for (let i = 0; i < g.after.length && !g.after.error; i++) {
|
||||
await this.__runTest(stats, g.after[i], 'After')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -360,13 +367,11 @@ Eltro.prototype.run = async function() {
|
|||
|
||||
if (this.reporter === 'test') {
|
||||
if (this.logger && this.logger.log) {
|
||||
if (this.failedTests.length) {
|
||||
for (let x = 0; x < this.failedTests.length; x++) {
|
||||
let test = this.failedTests[x];
|
||||
this.logger.log(test.name, test.error)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (this.reporter) {
|
||||
console.log('')
|
||||
console.log('')
|
||||
|
@ -406,30 +411,48 @@ Eltro.prototype.resetFilename = function() {
|
|||
}
|
||||
|
||||
let beforesandafters = [
|
||||
['before', 'Before'],
|
||||
['after', 'After'],
|
||||
['beforeEach', 'Before each'],
|
||||
['afterEach', 'After each'],
|
||||
['before', '~Before', false],
|
||||
['after', '~After', false],
|
||||
['beforeEach', '~Before each', true],
|
||||
['afterEach', '~After each', true],
|
||||
]
|
||||
|
||||
beforesandafters.forEach(function(item) {
|
||||
Eltro.prototype[item[0]] = function(func) {
|
||||
let beforeAfter = item[0]
|
||||
let fullName = item[1]
|
||||
let bringToChildren = item[2]
|
||||
|
||||
Eltro.prototype[beforeAfter] = function(func) {
|
||||
if (!this.activeGroup) {
|
||||
throw new Error('Tests outside groups are not allowed.')
|
||||
}
|
||||
|
||||
let test = new Test(this, this.activeGroup, item[1] + ': ' + this.activeGroup.name, func)
|
||||
let test = func
|
||||
|
||||
if (!(test instanceof Test)) {
|
||||
test = new Test(this, this.activeGroup, fullName + ': ' + this.activeGroup.name, func)
|
||||
}
|
||||
|
||||
if (this.temporary.timeout || this.activeGroup.customTimeout) {
|
||||
test.timeout(this.temporary.timeout || this.activeGroup.customTimeout)
|
||||
this.temporary.timeout = 0
|
||||
}
|
||||
|
||||
this.activeGroup[item[0]] = test
|
||||
this.activeGroup[beforeAfter] = this.activeGroup[beforeAfter] || []
|
||||
this.activeGroup[beforeAfter].push(test)
|
||||
|
||||
if (bringToChildren) {
|
||||
for (let group of this.activeGroup.groups) {
|
||||
group[beforeAfter].push(test)
|
||||
}
|
||||
}
|
||||
|
||||
return test
|
||||
}
|
||||
})
|
||||
|
||||
let bringToChildren = ['beforeEach', 'afterEach']
|
||||
|
||||
Eltro.prototype.describe = function(name, func) {
|
||||
let before = this.activeGroup
|
||||
|
||||
|
@ -458,6 +481,16 @@ Eltro.prototype.describe = function(name, func) {
|
|||
this.temporary.only = false
|
||||
}
|
||||
|
||||
if (before) {
|
||||
for (let beforeAfter of bringToChildren) {
|
||||
if (!before[beforeAfter]) continue
|
||||
|
||||
for (let test of before[beforeAfter]) {
|
||||
this[beforeAfter](test)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func()
|
||||
|
||||
this.activeGroup = before
|
||||
|
|
|
@ -1,20 +1,14 @@
|
|||
import fs from 'fs'
|
||||
import fsPromise from 'fs/promises'
|
||||
import path from 'path'
|
||||
import util from 'util'
|
||||
import events from 'events'
|
||||
import hasNativeRecursive from './has-native-recursive.mjs'
|
||||
import * as is from './is.mjs'
|
||||
|
||||
const EVENT_UPDATE = 'update';
|
||||
const EVENT_REMOVE = 'remove';
|
||||
|
||||
const SKIP_FLAG = Symbol('skip');
|
||||
|
||||
function hasDup(arr) {
|
||||
return arr.some(function(v, i, self) {
|
||||
return self.indexOf(v) !== i;
|
||||
});
|
||||
}
|
||||
const TYPE_FILE = 'file'
|
||||
const TYPE_DIRECTORY = 'directory'
|
||||
|
||||
function unique(arr) {
|
||||
return arr.filter(function(v, i, self) {
|
||||
|
@ -22,528 +16,274 @@ function unique(arr) {
|
|||
});
|
||||
}
|
||||
|
||||
// One level flat
|
||||
function flat1(arr) {
|
||||
return arr.reduce(function(acc, v) {
|
||||
return acc.concat(v);
|
||||
}, []);
|
||||
}
|
||||
export default class Watcher extends events.EventEmitter {
|
||||
constructor(path, options = null, fn = null, { fs: fsoverwrite } = {}) {
|
||||
super()
|
||||
|
||||
function assertEncoding(encoding) {
|
||||
if (encoding && encoding !== 'buffer' && !Buffer.isEncoding(encoding)) {
|
||||
throw new Error('Unknown encoding: ' + encoding);
|
||||
this.ac = new AbortController()
|
||||
events.setMaxListeners(2000, this.ac.signal)
|
||||
this._fs = fsoverwrite || fs
|
||||
this._cache = []
|
||||
this._cacheTimeout = null
|
||||
this.listeners = []
|
||||
this.closed = false
|
||||
let paths = path
|
||||
if (is.buffer(paths)) {
|
||||
paths = paths.toString()
|
||||
}
|
||||
}
|
||||
|
||||
function guard(fn) {
|
||||
if (is.func(fn)) {
|
||||
return function(arg, action) {
|
||||
if (fn(arg, false)) action();
|
||||
if (!is.array(paths)) {
|
||||
paths = [paths]
|
||||
}
|
||||
}
|
||||
if (is.regExp(fn)) {
|
||||
return function(arg, action) {
|
||||
if (fn.test(arg)) action();
|
||||
}
|
||||
}
|
||||
return function(arg, action) {
|
||||
action();
|
||||
}
|
||||
}
|
||||
|
||||
function composeMessage(names) {
|
||||
return names.map(function(n) {
|
||||
return is.exists(n)
|
||||
? [EVENT_UPDATE, n]
|
||||
: [EVENT_REMOVE, n];
|
||||
});
|
||||
}
|
||||
paths = unique(paths)
|
||||
this.options = options || {}
|
||||
this.fn = fn || null
|
||||
|
||||
function getMessages(cache) {
|
||||
var filtered = unique(cache);
|
||||
if (is.func(this.options)) {
|
||||
this.fn = this.options
|
||||
this.options = {}
|
||||
}
|
||||
|
||||
// Saving file from an editor? If so, assuming the
|
||||
// non-existed files in the cache are temporary files
|
||||
// generated by an editor and thus be filtered.
|
||||
var reg = /~$|^\.#|^##$/g;
|
||||
var hasSpecialChar = cache.some(function(c) {
|
||||
return reg.test(c);
|
||||
});
|
||||
this._verifyOptions(paths)
|
||||
}
|
||||
|
||||
if (hasSpecialChar) {
|
||||
var dup = hasDup(cache.map(function(c) {
|
||||
return c.replace(reg, '');
|
||||
}));
|
||||
if (dup) {
|
||||
filtered = filtered.filter(function(m) {
|
||||
return is.exists(m);
|
||||
});
|
||||
isClosed() {
|
||||
return this.closed
|
||||
}
|
||||
|
||||
close() {
|
||||
this.closed = true
|
||||
this.ac.abort()
|
||||
this._cache = this.listeners = []
|
||||
|
||||
this.emitAsync('close')
|
||||
}
|
||||
|
||||
emitAsync(name, ...args) {
|
||||
process.nextTick(() => this.emit(name, ...args))
|
||||
}
|
||||
|
||||
_verifyOptions(paths) {
|
||||
for (let path of paths) {
|
||||
if (!is.exists(path)) {
|
||||
this.emitAsync('error', new Error(path + ' does not exist.'))
|
||||
}
|
||||
}
|
||||
|
||||
return composeMessage(filtered);
|
||||
}
|
||||
if (this.options.encoding) {
|
||||
if (this.options.encoding && this.options.encoding !== 'buffer' && !Buffer.isEncoding(this.options.encoding)) {
|
||||
throw new Error('Unknown encoding: ' + this.options.encoding);
|
||||
}
|
||||
} else {
|
||||
this.options.encoding = 'utf8'
|
||||
}
|
||||
|
||||
function debounce(info, fn) {
|
||||
var timer, cache = [];
|
||||
var encoding = info.options.encoding;
|
||||
var delay = info.options.delay;
|
||||
if (!is.number(delay)) {
|
||||
delay = 200;
|
||||
if (this.options.delay !== 0 && !this.options.delay) {
|
||||
this.options.delay = 200
|
||||
}
|
||||
function handle() {
|
||||
getMessages(cache).forEach(function(msg) {
|
||||
msg[1] = Buffer.from(msg[1]);
|
||||
if (encoding !== 'buffer') {
|
||||
msg[1] = msg[1].toString(encoding);
|
||||
}
|
||||
fn.apply(null, msg);
|
||||
});
|
||||
timer = null;
|
||||
cache = [];
|
||||
}
|
||||
return function(rawEvt, name) {
|
||||
cache.push(name);
|
||||
if (!timer) {
|
||||
timer = setTimeout(handle, delay);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function createDupsFilter() {
|
||||
var memo = {};
|
||||
return function(fn) {
|
||||
return function(evt, name) {
|
||||
memo[evt + name] = [evt, name];
|
||||
setTimeout(function() {
|
||||
Object.keys(memo).forEach(function(n) {
|
||||
fn.apply(null, memo[n]);
|
||||
});
|
||||
memo = {};
|
||||
});
|
||||
if (is.func(this.fn)) {
|
||||
this.on('change', this.fn)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function tryWatch(watcher, dir, opts) {
|
||||
if (this.options.manualRecursive !== true) {
|
||||
hasNativeRecursive(nativeRecursive => {
|
||||
this.supportsNativeRecursive = nativeRecursive
|
||||
this.options.manualRecursive = !nativeRecursive
|
||||
this._startListeners(paths)
|
||||
})
|
||||
} else {
|
||||
this._startListeners(paths)
|
||||
}
|
||||
}
|
||||
|
||||
_startListeners(paths) {
|
||||
Promise.all(paths.map(path => this.safeAdd(path)))
|
||||
.then(
|
||||
() => this.emit('ready'),
|
||||
err => this.emit('error', err),
|
||||
)
|
||||
}
|
||||
|
||||
getWatcherOrNull(name) {
|
||||
for (let check of this.listeners) {
|
||||
if (check.path === name) {
|
||||
return check
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
shouldInclude(name) {
|
||||
return this.options.filter
|
||||
?
|
||||
(is.func(this.options.filter) && this.options.filter.call(this, name) === true)
|
||||
|| (is.regExp(this.options.filter) && this.options.filter.test(name))
|
||||
: true
|
||||
}
|
||||
|
||||
closeWatch(orgItem) {
|
||||
let item = orgItem
|
||||
if (typeof item === 'string') {
|
||||
item = getWatcherOrNull(item)
|
||||
}
|
||||
if (!item) {
|
||||
this.emit('error', new Error(`attempted to close watcher for ${item} but such a watcher could not be found`))
|
||||
return
|
||||
}
|
||||
|
||||
if (item.watcher) {
|
||||
item.watcher.close()
|
||||
}
|
||||
|
||||
this._emitEvent(item, EVENT_REMOVE, item.path)
|
||||
let index = this.listeners.indexOf(item)
|
||||
if (index < 0) return
|
||||
this.listeners.splice(index, 1)
|
||||
}
|
||||
|
||||
_emitEvent(item, evt, name) {
|
||||
if (item.type === TYPE_FILE && !is.samePath(name, item.filename)) return
|
||||
if (item.type === TYPE_DIRECTORY && !this.shouldInclude(name)) return
|
||||
|
||||
if (item.flag) {
|
||||
item.flag = ''
|
||||
return
|
||||
}
|
||||
|
||||
let outputName = name
|
||||
|
||||
if (this.options.encoding !== 'utf8') {
|
||||
outputName = Buffer.from(outputName)
|
||||
|
||||
if (this.options.encoding !== 'buffer') {
|
||||
outputName = outputName.toString(this.options.encoding)
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.options.delay) {
|
||||
this.emit('change', evt, outputName)
|
||||
return
|
||||
}
|
||||
|
||||
this._cache.push([evt, name, outputName])
|
||||
|
||||
if (this._cacheTimeout) return
|
||||
|
||||
this._cacheTimeout = setTimeout(() => {
|
||||
let cache = this._filterCache(this._cache)
|
||||
this._cache = []
|
||||
this._cacheTimeout = null
|
||||
|
||||
for (let event of cache) {
|
||||
try {
|
||||
return fs.watch(dir, opts);
|
||||
} catch (e) {
|
||||
process.nextTick(function() {
|
||||
watcher.emit('error', e);
|
||||
});
|
||||
this.emit('change', event[0], event[2])
|
||||
} catch (err) {
|
||||
this.emit('error', err)
|
||||
}
|
||||
}
|
||||
}, this.options.delay)
|
||||
}
|
||||
}
|
||||
|
||||
function getSubDirectories(dir, fn, done = function() {}) {
|
||||
if (is.directory(dir)) {
|
||||
fs.readdir(dir, function(err, all) {
|
||||
if (err) {
|
||||
// don't throw permission errors.
|
||||
if (/^(EPERM|EACCES)$/.test(err.code)) {
|
||||
console.warn('Warning: Cannot access %s.', dir);
|
||||
_filterCache(cache) {
|
||||
let setFound = new Set()
|
||||
|
||||
let out = cache.reverse().filter(([evt, name]) => {
|
||||
if (setFound.has(name)) return false
|
||||
setFound.add(name)
|
||||
return true
|
||||
}).reverse()
|
||||
return out
|
||||
}
|
||||
|
||||
_watcherSink(item, rawEvt, rawName) {
|
||||
if (this.closed) return
|
||||
|
||||
let name = path.join(item.path, rawName || '')
|
||||
let evt = is.exists(name) ? EVENT_UPDATE : EVENT_REMOVE
|
||||
|
||||
if (this.options.recursive && this.options.manualRecursive && item.type === TYPE_DIRECTORY) {
|
||||
if (evt === EVENT_REMOVE) {
|
||||
this.closeWatch(name)
|
||||
return
|
||||
} else {
|
||||
throw err;
|
||||
if (is.directory(name)
|
||||
&& this.getWatcherOrNull(name) === null
|
||||
&& this.shouldInclude(name) === false) {
|
||||
this.safeAdd(subItem, TYPE_DIRECTORY)
|
||||
}
|
||||
}
|
||||
else {
|
||||
all.forEach(function(f) {
|
||||
var sdir = path.join(dir, f);
|
||||
if (is.directory(sdir)) fn(sdir);
|
||||
});
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
this._emitEvent(item, evt, name)
|
||||
}
|
||||
|
||||
_pathToItem(name, type) {
|
||||
if (type === TYPE_FILE) {
|
||||
let parent = path.join(name, '../')
|
||||
return {
|
||||
path: parent,
|
||||
type: TYPE_FILE,
|
||||
filename: name,
|
||||
watcher: null,
|
||||
flag: '',
|
||||
}
|
||||
} else {
|
||||
done();
|
||||
}
|
||||
}
|
||||
|
||||
function semaphore(final) {
|
||||
var counter = 0;
|
||||
return function start() {
|
||||
counter++;
|
||||
return function stop() {
|
||||
counter--;
|
||||
if (counter === 0) final();
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
function nullCounter() {
|
||||
return function nullStop() {};
|
||||
}
|
||||
|
||||
function shouldNotSkip(filePath, filter) {
|
||||
// watch it only if the filter is not function
|
||||
// or not being skipped explicitly.
|
||||
return !is.func(filter) || filter(filePath, SKIP_FLAG) !== SKIP_FLAG;
|
||||
}
|
||||
|
||||
var deprecationWarning = util.deprecate(
|
||||
function() {},
|
||||
'(node-watch) First param in callback function\
|
||||
is replaced with event name since 0.5.0, use\
|
||||
`(evt, filename) => {}` if you want to get the filename'
|
||||
);
|
||||
|
||||
function Watcher() {
|
||||
events.EventEmitter.call(this);
|
||||
this.watchers = {};
|
||||
this._isReady = false;
|
||||
this._isClosed = false;
|
||||
}
|
||||
|
||||
util.inherits(Watcher, events.EventEmitter);
|
||||
|
||||
Watcher.prototype.expose = function() {
|
||||
var expose = {};
|
||||
var self = this;
|
||||
var methods = [
|
||||
'on', 'emit', 'once',
|
||||
'close', 'isClosed',
|
||||
'listeners', 'setMaxListeners', 'getMaxListeners',
|
||||
'getWatchedPaths'
|
||||
];
|
||||
methods.forEach(function(name) {
|
||||
expose[name] = function() {
|
||||
return self[name].apply(self, arguments);
|
||||
}
|
||||
});
|
||||
expose.options = self.options
|
||||
return expose;
|
||||
}
|
||||
|
||||
Watcher.prototype.isClosed = function() {
|
||||
return this._isClosed;
|
||||
}
|
||||
|
||||
Watcher.prototype.close = function(fullPath) {
|
||||
var self = this;
|
||||
if (fullPath) {
|
||||
var watcher = this.watchers[fullPath];
|
||||
if (watcher && watcher.close) {
|
||||
watcher.close();
|
||||
delete self.watchers[fullPath];
|
||||
}
|
||||
getSubDirectories(fullPath, function(fpath) {
|
||||
self.close(fpath);
|
||||
});
|
||||
}
|
||||
else {
|
||||
Object.keys(self.watchers).forEach(function(fpath) {
|
||||
var watcher = self.watchers[fpath];
|
||||
if (watcher && watcher.close) {
|
||||
watcher.close();
|
||||
}
|
||||
});
|
||||
this.watchers = {};
|
||||
}
|
||||
// Do not close the Watcher unless all child watchers are closed.
|
||||
// https://github.com/yuanchuan/node-watch/issues/75
|
||||
if (is.emptyObject(self.watchers)) {
|
||||
// should emit once
|
||||
if (!this._isClosed) {
|
||||
this._isClosed = true;
|
||||
process.nextTick(emitClose, this);
|
||||
return {
|
||||
path: name,
|
||||
type: TYPE_DIRECTORY,
|
||||
watcher: null,
|
||||
flag: '',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Watcher.prototype.getWatchedPaths = function(fn) {
|
||||
if (is.func(fn)) {
|
||||
var self = this;
|
||||
if (self._isReady) {
|
||||
fn(Object.keys(self.watchers));
|
||||
} else {
|
||||
self.on('ready', function() {
|
||||
fn(Object.keys(self.watchers));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function emitReady(self) {
|
||||
if (!self._isReady) {
|
||||
self._isReady = true;
|
||||
// do not call emit for 'ready' until after watch() has returned,
|
||||
// so that consumer can call on().
|
||||
process.nextTick(function () {
|
||||
self.emit('ready');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function emitClose(self) {
|
||||
self.emit('close');
|
||||
}
|
||||
|
||||
Watcher.prototype.add = function(watcher, info) {
|
||||
var self = this;
|
||||
info = info || { fpath: '' };
|
||||
var watcherPath = path.resolve(info.fpath);
|
||||
this.watchers[watcherPath] = watcher;
|
||||
|
||||
// Internal callback for handling fs.FSWatcher 'change' events
|
||||
var internalOnChange = function(rawEvt, rawName) {
|
||||
if (self.isClosed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// normalise lack of name and convert to full path
|
||||
var name = rawName;
|
||||
if (is.nil(name)) {
|
||||
name = '';
|
||||
}
|
||||
name = path.join(info.fpath, name);
|
||||
_watcherError(item, err) {
|
||||
if (this.closed) return
|
||||
|
||||
if (info.options.recursive) {
|
||||
hasNativeRecursive(function(has) {
|
||||
if (!has) {
|
||||
var fullPath = path.resolve(name);
|
||||
// remove watcher on removal
|
||||
if (!is.exists(name)) {
|
||||
self.close(fullPath);
|
||||
}
|
||||
// watch new created directory
|
||||
else {
|
||||
var shouldWatch = is.directory(name)
|
||||
&& !self.watchers[fullPath]
|
||||
&& shouldNotSkip(name, info.options.filter);
|
||||
|
||||
if (shouldWatch) {
|
||||
self.watchDirectory(name, info.options);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
handlePublicEvents(rawEvt, name);
|
||||
};
|
||||
|
||||
// Debounced based on the 'delay' option
|
||||
var handlePublicEvents = debounce(info, function (evt, name) {
|
||||
// watch single file
|
||||
if (info.compareName) {
|
||||
if (info.compareName(name)) {
|
||||
self.emit('change', evt, name);
|
||||
}
|
||||
}
|
||||
// watch directory
|
||||
else {
|
||||
var filterGuard = guard(info.options.filter);
|
||||
filterGuard(name, function() {
|
||||
if (self.flag) self.flag = '';
|
||||
else self.emit('change', evt, name);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
watcher.on('error', function(err) {
|
||||
if (self.isClosed()) {
|
||||
return;
|
||||
}
|
||||
if (is.windows() && err.code === 'EPERM') {
|
||||
watcher.emit('change', EVENT_REMOVE, info.fpath && '');
|
||||
self.flag = 'windows-error';
|
||||
self.close(watcherPath);
|
||||
this.closeWatch(item)
|
||||
item.flag = 'windows-error'
|
||||
} else {
|
||||
self.emit('error', err);
|
||||
self.emit('error', err)
|
||||
}
|
||||
});
|
||||
|
||||
watcher.on('change', internalOnChange);
|
||||
}
|
||||
|
||||
Watcher.prototype.watchFile = function(file, options, fn) {
|
||||
var parent = path.join(file, '../');
|
||||
var opts = Object.assign({}, options, {
|
||||
// no filter for single file
|
||||
filter: null,
|
||||
encoding: 'utf8'
|
||||
});
|
||||
|
||||
// no need to watch recursively
|
||||
delete opts.recursive;
|
||||
|
||||
var watcher = tryWatch(this, parent, opts);
|
||||
if (!watcher) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.add(watcher, {
|
||||
type: 'file',
|
||||
fpath: parent,
|
||||
options: Object.assign({}, opts, {
|
||||
encoding: options.encoding
|
||||
}),
|
||||
compareName: function(n) {
|
||||
return is.samePath(n, file);
|
||||
safeAdd(name, orgType) {
|
||||
let type = orgType
|
||||
if (!type) {
|
||||
type = is.file(name) ? TYPE_FILE : TYPE_DIRECTORY
|
||||
}
|
||||
});
|
||||
|
||||
if (is.func(fn)) {
|
||||
if (fn.length === 1) deprecationWarning();
|
||||
this.on('change', fn);
|
||||
let item = this._pathToItem(name, type)
|
||||
let options = {
|
||||
encoding: 'utf8',
|
||||
signal: this.ac.signal,
|
||||
}
|
||||
if (!this.options.manualRecursive && item.type !== TYPE_FILE && this.options.recursive) {
|
||||
options.recursive = true
|
||||
}
|
||||
|
||||
try {
|
||||
item.watcher = this._fs.watch(item.path, options)
|
||||
} catch (err) {
|
||||
this.emitAsync('error', err)
|
||||
}
|
||||
|
||||
if (!item.watcher) return
|
||||
|
||||
this.listeners.push(item)
|
||||
|
||||
item.watcher.on('error', this._watcherError.bind(this, item))
|
||||
item.watcher.on('change', this._watcherSink.bind(this, item))
|
||||
|
||||
if (options.recursive || item.type === TYPE_FILE) return
|
||||
|
||||
return fsPromise.readdir(item.path, { withFileTypes: true })
|
||||
.then(directories => directories.filter(dir => dir.isDirectory()))
|
||||
.then(directories => {
|
||||
return Promise.all(directories.map(dir => this.safeAdd(path.join(item.path, dir.name), TYPE_DIRECTORY)))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Watcher.prototype.updateDelay = function(delay) {
|
||||
console.log()
|
||||
}
|
||||
|
||||
Watcher.prototype.watchDirectory = function(dir, options, fn, counter = nullCounter) {
|
||||
var self = this;
|
||||
var done = counter();
|
||||
hasNativeRecursive(function(has) {
|
||||
// always specify recursive
|
||||
options.recursive = !!options.recursive;
|
||||
// using utf8 internally
|
||||
var opts = Object.assign({}, options, {
|
||||
encoding: 'utf8'
|
||||
});
|
||||
if (!has) {
|
||||
delete opts.recursive;
|
||||
}
|
||||
|
||||
// check if it's closed before calling watch.
|
||||
if (self._isClosed) {
|
||||
done();
|
||||
return self.close();
|
||||
}
|
||||
|
||||
var watcher = tryWatch(self, dir, opts);
|
||||
if (!watcher) {
|
||||
done();
|
||||
return;
|
||||
}
|
||||
|
||||
self.add(watcher, {
|
||||
type: 'dir',
|
||||
fpath: dir,
|
||||
options: options
|
||||
});
|
||||
|
||||
if (is.func(fn)) {
|
||||
if (fn.length === 1) deprecationWarning();
|
||||
self.on('change', fn);
|
||||
}
|
||||
|
||||
if (options.recursive && !has) {
|
||||
getSubDirectories(dir, function(d) {
|
||||
if (shouldNotSkip(d, options.filter)) {
|
||||
self.watchDirectory(d, options, null, counter);
|
||||
}
|
||||
}, counter());
|
||||
}
|
||||
|
||||
done();
|
||||
});
|
||||
}
|
||||
|
||||
function composeWatcher(watchers) {
|
||||
var watcher = new Watcher();
|
||||
var filterDups = createDupsFilter();
|
||||
var counter = watchers.length;
|
||||
|
||||
watchers.forEach(function(w) {
|
||||
w.on('change', filterDups(function(evt, name) {
|
||||
watcher.emit('change', evt, name);
|
||||
}));
|
||||
w.on('error', function(err) {
|
||||
watcher.emit('error', err);
|
||||
});
|
||||
w.on('ready', function() {
|
||||
if (!(--counter)) {
|
||||
emitReady(watcher);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
watcher.close = function() {
|
||||
watchers.forEach(function(w) {
|
||||
w.close();
|
||||
});
|
||||
process.nextTick(emitClose, watcher);
|
||||
}
|
||||
|
||||
watcher.getWatchedPaths = function(fn) {
|
||||
if (is.func(fn)) {
|
||||
var promises = watchers.map(function(w) {
|
||||
return new Promise(function(resolve) {
|
||||
w.getWatchedPaths(resolve);
|
||||
});
|
||||
});
|
||||
Promise.all(promises).then(function(result) {
|
||||
var ret = unique(flat1(result));
|
||||
fn(ret);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return watcher.expose();
|
||||
}
|
||||
|
||||
export default function watch(fpath, options, fn) {
|
||||
var watcher = new Watcher();
|
||||
|
||||
if (is.buffer(fpath)) {
|
||||
fpath = fpath.toString();
|
||||
}
|
||||
|
||||
if (!is.array(fpath) && !is.exists(fpath)) {
|
||||
process.nextTick(function() {
|
||||
watcher.emit('error',
|
||||
new Error(fpath + ' does not exist.')
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
if (is.string(options)) {
|
||||
throw new Error(`Invalid option, encoding as string is no longer supported. Use { encoding: "${options}" } instead.`)
|
||||
}
|
||||
|
||||
if (is.func(options)) {
|
||||
fn = options;
|
||||
options = {};
|
||||
}
|
||||
|
||||
if (arguments.length < 2) {
|
||||
options = {};
|
||||
}
|
||||
|
||||
if (options.encoding) {
|
||||
assertEncoding(options.encoding);
|
||||
} else {
|
||||
options.encoding = 'utf8';
|
||||
}
|
||||
|
||||
if (is.array(fpath)) {
|
||||
if (fpath.length === 1) {
|
||||
return watch(fpath[0], options, fn);
|
||||
}
|
||||
var filterDups = createDupsFilter();
|
||||
return composeWatcher(unique(fpath).map(function(f) {
|
||||
var w = watch(f, options);
|
||||
if (is.func(fn)) {
|
||||
w.on('change', filterDups(fn));
|
||||
}
|
||||
return w;
|
||||
}));
|
||||
}
|
||||
|
||||
if (is.file(fpath)) {
|
||||
watcher.watchFile(fpath, options, fn);
|
||||
emitReady(watcher);
|
||||
}
|
||||
|
||||
else if (is.directory(fpath)) {
|
||||
var counter = semaphore(function () {
|
||||
emitReady(watcher);
|
||||
});
|
||||
watcher.watchDirectory(fpath, options, fn, counter);
|
||||
}
|
||||
|
||||
return watcher.expose();
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
"description": "Eltro is a tiny no-dependancy test framework for node",
|
||||
"main": "index.mjs",
|
||||
"scripts": {
|
||||
"test": "node cli.mjs 'test/**/*.test.mjs'",
|
||||
"test": "node cli.mjs \"test/**/*.test.mjs\"",
|
||||
"test:watch": "npm-watch test"
|
||||
},
|
||||
"watch": {
|
||||
|
|
|
@ -185,7 +185,6 @@ t.describe('CLI', function() {
|
|||
cli.targets = ['test/testtree/*/*.mjs']
|
||||
await cli.processTargets()
|
||||
|
||||
console.log(cli.files)
|
||||
assert.strictEqual(cli.files.length, 4)
|
||||
cli.files.sort()
|
||||
assert.deepEqual(cli.files, [
|
||||
|
|
|
@ -52,23 +52,74 @@ e.describe('#before()', function() {
|
|||
assert.strictEqual(secondBefore, 1)
|
||||
assert.strictEqual(thirdBefore, 4)
|
||||
})
|
||||
})
|
||||
e.test('should support multiple functions in describe group', async function() {
|
||||
let assertRan = 0
|
||||
let firstBefore = -1
|
||||
let secondBefore = -1
|
||||
let thirdBefore = -1
|
||||
let fourthBefore = -1
|
||||
|
||||
e.describe('#beforeEach()', function() {
|
||||
e.test('should support functions in describe group and run before each test and group', async function() {
|
||||
const t = CreateT()
|
||||
t.begin()
|
||||
t.describe('', function() {
|
||||
t.before(function() {
|
||||
firstBefore = assertRan
|
||||
})
|
||||
|
||||
t.describe('', function() {
|
||||
t.before(function() {
|
||||
thirdBefore = assertRan
|
||||
})
|
||||
|
||||
t.test('', function() { assertRan++ })
|
||||
t.test('', function() { assertRan++ })
|
||||
t.test('', function() { assertRan++ })
|
||||
})
|
||||
|
||||
t.describe('', function() {
|
||||
t.before(function() {
|
||||
fourthBefore = assertRan
|
||||
})
|
||||
|
||||
t.test('', function() { assertRan++ })
|
||||
})
|
||||
|
||||
t.test('', function() { assertRan++ })
|
||||
|
||||
t.before(function() {
|
||||
secondBefore = assertRan
|
||||
})
|
||||
})
|
||||
let stats = await t.run()
|
||||
assert.strictEqual(t.failedTests.length, 0)
|
||||
assert.strictEqual(assertRan, 5)
|
||||
assert.strictEqual(stats.passed, 5)
|
||||
assert.strictEqual(firstBefore, 0)
|
||||
assert.strictEqual(secondBefore, 0)
|
||||
assert.strictEqual(thirdBefore, 1)
|
||||
assert.strictEqual(fourthBefore, 4)
|
||||
})
|
||||
})
|
||||
|
||||
e.describe('#beforeEach()', function() {
|
||||
e.test('should support functions in describe group and run before each test and each test in every group', async function() {
|
||||
let outside = 0
|
||||
|
||||
const t = CreateT()
|
||||
t.begin()
|
||||
t.describe('', function() {
|
||||
|
||||
t.beforeEach(function() {
|
||||
outside++
|
||||
})
|
||||
|
||||
t.describe('', function() {
|
||||
t.before(function() {
|
||||
assert.strictEqual(outside, 2)
|
||||
})
|
||||
let inside = 0
|
||||
|
||||
t.before(function() {
|
||||
assert.strictEqual(outside, 1)
|
||||
})
|
||||
|
||||
t.beforeEach(function() {
|
||||
inside++
|
||||
})
|
||||
|
@ -79,11 +130,12 @@ e.describe('#beforeEach()', function() {
|
|||
})
|
||||
|
||||
t.describe('', function() {
|
||||
let insideSecond = 0
|
||||
|
||||
t.before(function() {
|
||||
assert.strictEqual(outside, 3)
|
||||
assert.strictEqual(outside, 4)
|
||||
})
|
||||
|
||||
let insideSecond = 0
|
||||
t.beforeEach(function() {
|
||||
assert.strictEqual(insideSecond, 0)
|
||||
insideSecond++
|
||||
|
@ -99,6 +151,97 @@ e.describe('#beforeEach()', function() {
|
|||
assert.strictEqual(stats.passed, 5)
|
||||
assert.strictEqual(stats.failed, 0)
|
||||
assert.strictEqual(stats.skipped, 0)
|
||||
assert.strictEqual(outside, 5)
|
||||
})
|
||||
|
||||
e.test('should work even if before is specifed after all the tests', async function() {
|
||||
let outside = 0
|
||||
|
||||
const t = CreateT()
|
||||
t.begin()
|
||||
t.describe('', function() {
|
||||
t.describe('', function() {
|
||||
let inside = 0
|
||||
|
||||
t.before(function() {
|
||||
assert.strictEqual(outside, 1)
|
||||
})
|
||||
|
||||
t.test('', function() { assert.strictEqual(inside, 1) })
|
||||
t.test('', function() { assert.strictEqual(inside, 2) })
|
||||
t.test('', function() { assert.strictEqual(inside, 3) })
|
||||
|
||||
t.beforeEach(function() {
|
||||
inside++
|
||||
})
|
||||
})
|
||||
|
||||
t.describe('', function() {
|
||||
let insideSecond = 0
|
||||
|
||||
t.before(function() {
|
||||
assert.strictEqual(outside, 4)
|
||||
})
|
||||
|
||||
t.test('', function() { assert.strictEqual(insideSecond, 1) })
|
||||
|
||||
t.beforeEach(function() {
|
||||
assert.strictEqual(insideSecond, 0)
|
||||
insideSecond++
|
||||
})
|
||||
})
|
||||
|
||||
t.test('', function() { assert.strictEqual(outside, 1) })
|
||||
|
||||
t.beforeEach(function() {
|
||||
outside++
|
||||
})
|
||||
})
|
||||
let stats = await t.run()
|
||||
assert.strictEqual(t.failedTests.length, 0)
|
||||
assert.strictEqual(stats.passed, 5)
|
||||
assert.strictEqual(stats.failed, 0)
|
||||
assert.strictEqual(stats.skipped, 0)
|
||||
assert.strictEqual(outside, 5)
|
||||
})
|
||||
|
||||
e.test('should support multiple beforeEach', async function() {
|
||||
let outside = 0
|
||||
let inside = 0
|
||||
|
||||
const t = CreateT()
|
||||
t.begin()
|
||||
t.describe('', function() {
|
||||
t.beforeEach(function() {
|
||||
outside++
|
||||
})
|
||||
|
||||
t.describe('', function() {
|
||||
t.beforeEach(function() {
|
||||
inside++
|
||||
})
|
||||
|
||||
t.test('', function() { assert.strictEqual(inside, 2) })
|
||||
t.test('', function() { assert.strictEqual(inside, 4) })
|
||||
t.test('', function() { assert.strictEqual(inside, 6) })
|
||||
|
||||
t.beforeEach(function() {
|
||||
inside++
|
||||
})
|
||||
})
|
||||
|
||||
t.test('', function() { assert.strictEqual(outside, 2) })
|
||||
|
||||
t.beforeEach(function() {
|
||||
outside++
|
||||
})
|
||||
})
|
||||
let stats = await t.run()
|
||||
assert.strictEqual(t.failedTests.length, 0)
|
||||
assert.strictEqual(stats.passed, 4)
|
||||
assert.strictEqual(stats.failed, 0)
|
||||
assert.strictEqual(stats.skipped, 0)
|
||||
assert.strictEqual(outside, 8)
|
||||
})
|
||||
|
||||
e.test('should be able to keep track of every error that occurs', async function() {
|
||||
|
@ -112,6 +255,7 @@ e.describe('#beforeEach()', function() {
|
|||
|
||||
t.describe('CCCC', function() {
|
||||
t.test('', function() { })
|
||||
t.test('', function() { })
|
||||
})
|
||||
|
||||
t.describe('DDDD', function() {
|
||||
|
@ -121,20 +265,24 @@ e.describe('#beforeEach()', function() {
|
|||
t.test('AAAA', function() { })
|
||||
})
|
||||
let stats = await t.run()
|
||||
assert.strictEqual(t.failedTests.length, 3)
|
||||
assert.strictEqual(t.logger.log.callCount, 3)
|
||||
assert.match(t.logger.log.firstCall[1].message, /1/)
|
||||
assert.match(t.logger.log.firstCall[0], /before each/i)
|
||||
assert.match(t.logger.log.firstCall[0], /AAAA/)
|
||||
assert.match(t.logger.log.firstCall[0], /BBBB/)
|
||||
assert.match(t.logger.log.secondCall[1].message, /2/)
|
||||
assert.match(t.logger.log.secondCall[0], /before each/i)
|
||||
assert.match(t.logger.log.secondCall[0], /CCCC/)
|
||||
assert.match(t.logger.log.secondCall[0], /BBBB/)
|
||||
assert.match(t.logger.log.thirdCall[1].message, /3/)
|
||||
assert.match(t.logger.log.thirdCall[0], /before each/i)
|
||||
assert.match(t.logger.log.thirdCall[0], /DDDD/)
|
||||
assert.match(t.logger.log.thirdCall[0], /BBBB/)
|
||||
assert.strictEqual(t.failedTests.length, 4)
|
||||
assert.strictEqual(t.logger.log.callCount, 4)
|
||||
assert.match(t.logger.log.getCallN(1)[1].message, /1/)
|
||||
assert.match(t.logger.log.getCallN(1)[0], /before each/i)
|
||||
assert.match(t.logger.log.getCallN(1)[0], /AAAA/)
|
||||
assert.match(t.logger.log.getCallN(1)[0], /BBBB/)
|
||||
assert.match(t.logger.log.getCallN(2)[1].message, /2/)
|
||||
assert.match(t.logger.log.getCallN(2)[0], /before each/i)
|
||||
assert.match(t.logger.log.getCallN(2)[0], /CCCC/)
|
||||
assert.match(t.logger.log.getCallN(2)[0], /BBBB/)
|
||||
assert.match(t.logger.log.getCallN(3)[1].message, /3/)
|
||||
assert.match(t.logger.log.getCallN(3)[0], /before each/i)
|
||||
assert.match(t.logger.log.getCallN(3)[0], /CCCC/)
|
||||
assert.match(t.logger.log.getCallN(3)[0], /BBBB/)
|
||||
assert.match(t.logger.log.getCallN(4)[1].message, /4/)
|
||||
assert.match(t.logger.log.getCallN(4)[0], /before each/i)
|
||||
assert.match(t.logger.log.getCallN(4)[0], /DDDD/)
|
||||
assert.match(t.logger.log.getCallN(4)[0], /BBBB/)
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -179,6 +327,81 @@ e.describe('#after()', function() {
|
|||
assert.strictEqual(secondAfter, 4)
|
||||
assert.strictEqual(thirdAfter, 5)
|
||||
})
|
||||
e.test('should support multiple functions in describe group', async function() {
|
||||
let assertRan = 0
|
||||
let firstAfter = -1
|
||||
let secondAfter = -1
|
||||
let thirdAfter = -1
|
||||
let fourthAfter = -1
|
||||
const t = CreateT()
|
||||
t.begin()
|
||||
t.describe('', function() {
|
||||
t.after(function() {
|
||||
firstAfter = assertRan
|
||||
})
|
||||
|
||||
t.describe('', function() {
|
||||
t.after(function() {
|
||||
thirdAfter = assertRan
|
||||
})
|
||||
|
||||
t.test('', function() { assertRan++ })
|
||||
t.test('', function() { assertRan++ })
|
||||
t.test('', function() { assertRan++ })
|
||||
})
|
||||
|
||||
t.describe('', function() {
|
||||
t.after(function() {
|
||||
fourthAfter = assertRan
|
||||
})
|
||||
|
||||
t.test('', function() { assertRan++ })
|
||||
})
|
||||
|
||||
t.test('', function() { assertRan++ })
|
||||
|
||||
t.after(function() {
|
||||
secondAfter = assertRan
|
||||
})
|
||||
})
|
||||
let stats = await t.run()
|
||||
assert.strictEqual(t.failedTests.length, 0)
|
||||
assert.strictEqual(stats.passed, 5)
|
||||
assert.strictEqual(assertRan, 5)
|
||||
|
||||
assert.strictEqual(firstAfter, 5)
|
||||
assert.strictEqual(secondAfter, 5)
|
||||
assert.strictEqual(thirdAfter, 4)
|
||||
assert.strictEqual(fourthAfter, 5)
|
||||
})
|
||||
|
||||
e.test('should log even if it throws and test throws', async function() {
|
||||
const assertError = new Error('test')
|
||||
const t = CreateT()
|
||||
t.begin()
|
||||
t.describe('', function() {
|
||||
t.after(function() { throw assertError })
|
||||
|
||||
t.describe('', function() {
|
||||
t.after(function() { throw assertError })
|
||||
|
||||
t.test('', function() { throw assertError })
|
||||
t.test('', function() { })
|
||||
t.test('', function() { throw assertError })
|
||||
})
|
||||
|
||||
t.test('', function() { throw assertError })
|
||||
|
||||
t.after(function() { throw assertError })
|
||||
})
|
||||
let stats = await t.run()
|
||||
assert.strictEqual(t.failedTests.length, 6)
|
||||
assert.strictEqual(stats.passed, 1)
|
||||
|
||||
for (let failedTest of t.failedTests) {
|
||||
assert.strictEqual(failedTest.error, assertError)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
e.describe('#afterEach()', function() {
|
||||
|
@ -187,14 +410,16 @@ e.describe('#afterEach()', function() {
|
|||
t.begin()
|
||||
t.describe('', function() {
|
||||
let outside = 0
|
||||
|
||||
t.afterEach(function() {
|
||||
outside++
|
||||
})
|
||||
|
||||
t.describe('', function() {
|
||||
let inside = 0
|
||||
|
||||
t.before(function() { assert.strictEqual(outside, 1) })
|
||||
|
||||
let inside = 0
|
||||
t.afterEach(function() {
|
||||
inside++
|
||||
})
|
||||
|
@ -207,9 +432,10 @@ e.describe('#afterEach()', function() {
|
|||
})
|
||||
|
||||
t.describe('', function() {
|
||||
t.before(function() { assert.strictEqual(outside, 2) })
|
||||
|
||||
let inside = 0
|
||||
|
||||
t.before(function() { assert.strictEqual(outside, 4) })
|
||||
|
||||
t.afterEach(function() {
|
||||
inside++
|
||||
})
|
||||
|
@ -221,7 +447,185 @@ e.describe('#afterEach()', function() {
|
|||
|
||||
t.test('', function() { assert.strictEqual(outside, 0) })
|
||||
|
||||
t.after(function() { assert.strictEqual(outside, 3) })
|
||||
t.after(function() { assert.strictEqual(outside, 5) })
|
||||
})
|
||||
let stats = await t.run()
|
||||
assert.strictEqual(t.failedTests.length, 0)
|
||||
assert.strictEqual(stats.passed, 5)
|
||||
assert.strictEqual(stats.failed, 0)
|
||||
assert.strictEqual(stats.skipped, 0)
|
||||
})
|
||||
|
||||
e.test('should work even if after is specified after the tests', async function() {
|
||||
const t = CreateT()
|
||||
t.begin()
|
||||
t.describe('', function() {
|
||||
let outside = 0
|
||||
|
||||
t.describe('', function() {
|
||||
let inside = 0
|
||||
|
||||
t.before(function() { assert.strictEqual(outside, 1) })
|
||||
|
||||
t.test('', function() { assert.strictEqual(inside, 0) })
|
||||
t.test('', function() { assert.strictEqual(inside, 1) })
|
||||
t.test('', function() { assert.strictEqual(inside, 2) })
|
||||
|
||||
t.after(function() { assert.strictEqual(inside, 3) })
|
||||
|
||||
t.afterEach(function() {
|
||||
inside++
|
||||
})
|
||||
})
|
||||
|
||||
t.describe('', function() {
|
||||
let inside = 0
|
||||
|
||||
t.before(function() { assert.strictEqual(outside, 4) })
|
||||
|
||||
t.test('', function() { assert.strictEqual(inside, 0) })
|
||||
|
||||
t.after(function() { assert.strictEqual(inside, 1) })
|
||||
|
||||
t.afterEach(function() {
|
||||
inside++
|
||||
})
|
||||
})
|
||||
|
||||
t.test('', function() { assert.strictEqual(outside, 0) })
|
||||
|
||||
t.after(function() { assert.strictEqual(outside, 5) })
|
||||
|
||||
t.afterEach(function() {
|
||||
outside++
|
||||
})
|
||||
})
|
||||
let stats = await t.run()
|
||||
assert.strictEqual(t.failedTests.length, 0)
|
||||
assert.strictEqual(stats.passed, 5)
|
||||
assert.strictEqual(stats.failed, 0)
|
||||
assert.strictEqual(stats.skipped, 0)
|
||||
})
|
||||
|
||||
e.test('should run even if each test throws', async function() {
|
||||
let outside = 0
|
||||
let inside = 0
|
||||
const assertError = new Error('test')
|
||||
|
||||
const t = CreateT()
|
||||
t.begin()
|
||||
t.describe('', function() {
|
||||
t.describe('', function() {
|
||||
t.test('', function() { throw assertError })
|
||||
t.test('', function() { throw assertError })
|
||||
t.test('', function() { throw assertError })
|
||||
|
||||
t.afterEach(function() {
|
||||
inside++
|
||||
})
|
||||
})
|
||||
|
||||
t.test('', function() { throw assertError })
|
||||
|
||||
t.afterEach(function() {
|
||||
outside++
|
||||
})
|
||||
})
|
||||
let stats = await t.run()
|
||||
assert.strictEqual(t.failedTests.length, 4)
|
||||
for (let failedTest of t.failedTests) {
|
||||
assert.strictEqual(failedTest.error, assertError)
|
||||
}
|
||||
assert.strictEqual(stats.passed, 0)
|
||||
assert.strictEqual(stats.failed, 4)
|
||||
assert.strictEqual(stats.skipped, 0)
|
||||
assert.strictEqual(outside, 4)
|
||||
assert.strictEqual(inside, 3)
|
||||
})
|
||||
|
||||
e.test('should log even if afterEach fails', async function() {
|
||||
const assertError = new Error('test')
|
||||
|
||||
const t = CreateT()
|
||||
t.begin()
|
||||
t.describe('', function() {
|
||||
t.describe('', function() {
|
||||
t.test('', function() { throw assertError })
|
||||
t.test('', function() { throw assertError })
|
||||
t.test('', function() { throw assertError })
|
||||
|
||||
t.afterEach(function() { throw assertError })
|
||||
})
|
||||
|
||||
t.test('', function() { throw assertError })
|
||||
|
||||
t.afterEach(function() { throw assertError })
|
||||
})
|
||||
let stats = await t.run()
|
||||
assert.strictEqual(t.failedTests.length, 8)
|
||||
for (let failedTest of t.failedTests) {
|
||||
assert.strictEqual(failedTest.error, assertError)
|
||||
}
|
||||
assert.strictEqual(stats.passed, 0)
|
||||
assert.strictEqual(stats.failed, 8)
|
||||
assert.strictEqual(stats.skipped, 0)
|
||||
})
|
||||
|
||||
e.test('should support multiple afterEach', async function() {
|
||||
const t = CreateT()
|
||||
t.begin()
|
||||
t.describe('', function() {
|
||||
let outside = 0
|
||||
|
||||
t.afterEach(function() {
|
||||
outside++
|
||||
})
|
||||
|
||||
t.describe('', function() {
|
||||
let inside = 0
|
||||
|
||||
t.before(function() { assert.strictEqual(outside, 2) })
|
||||
|
||||
t.afterEach(function() {
|
||||
inside++
|
||||
})
|
||||
|
||||
t.test('', function() { assert.strictEqual(inside, 0) })
|
||||
t.test('', function() { assert.strictEqual(inside, 2) })
|
||||
t.test('', function() { assert.strictEqual(inside, 4) })
|
||||
|
||||
t.after(function() { assert.strictEqual(inside, 6) })
|
||||
|
||||
t.afterEach(function() {
|
||||
inside++
|
||||
})
|
||||
})
|
||||
|
||||
t.describe('', function() {
|
||||
let inside = 0
|
||||
|
||||
t.before(function() { assert.strictEqual(outside, 8) })
|
||||
|
||||
t.afterEach(function() {
|
||||
inside++
|
||||
})
|
||||
|
||||
t.test('', function() { assert.strictEqual(inside, 0) })
|
||||
|
||||
t.after(function() { assert.strictEqual(inside, 2) })
|
||||
|
||||
t.afterEach(function() {
|
||||
inside++
|
||||
})
|
||||
})
|
||||
|
||||
t.test('', function() { assert.strictEqual(outside, 0) })
|
||||
|
||||
t.after(function() { assert.strictEqual(outside, 10) })
|
||||
|
||||
t.afterEach(function() {
|
||||
outside++
|
||||
})
|
||||
})
|
||||
let stats = await t.run()
|
||||
assert.strictEqual(t.failedTests.length, 0)
|
||||
|
@ -254,26 +658,26 @@ e.describe('#afterEach()', function() {
|
|||
let stats = await t.run()
|
||||
assert.strictEqual(t.failedTests.length, 5)
|
||||
assert.strictEqual(t.logger.log.callCount, 5)
|
||||
assert.match(t.logger.log.getCall(0)[1].message, /1/)
|
||||
assert.match(t.logger.log.getCall(0)[0], /after each/i)
|
||||
assert.match(t.logger.log.getCall(0)[0], /AAAA/)
|
||||
assert.match(t.logger.log.getCall(0)[0], /YYYY/)
|
||||
assert.match(t.logger.log.getCall(1)[1].message, /2/)
|
||||
assert.match(t.logger.log.getCall(1)[0], /after each/i)
|
||||
assert.match(t.logger.log.getCall(1)[0], /BBBB/)
|
||||
assert.match(t.logger.log.getCall(1)[0], /YYYY/)
|
||||
assert.match(t.logger.log.getCall(2)[1].message, /3/)
|
||||
assert.match(t.logger.log.getCall(2)[0], /after each/i)
|
||||
assert.match(t.logger.log.getCall(2)[0], /CCCC/)
|
||||
assert.match(t.logger.log.getCall(2)[0], /YYYY/)
|
||||
assert.match(t.logger.log.getCall(3)[1].message, /4/)
|
||||
assert.match(t.logger.log.getCall(3)[0], /after each/i)
|
||||
assert.match(t.logger.log.getCall(3)[0], /HHHH/)
|
||||
assert.match(t.logger.log.getCall(3)[0], /YYYY/)
|
||||
assert.match(t.logger.log.getCall(4)[1].message, /5/)
|
||||
assert.match(t.logger.log.getCall(4)[0], /after each/i)
|
||||
assert.match(t.logger.log.getCall(4)[0], /JJJJ/)
|
||||
assert.match(t.logger.log.getCall(4)[0], /YYYY/)
|
||||
assert.match(t.logger.log.getCallN(1)[1].message, /1/)
|
||||
assert.match(t.logger.log.getCallN(1)[0], /after each/i)
|
||||
assert.match(t.logger.log.getCallN(1)[0], /AAAA/)
|
||||
assert.match(t.logger.log.getCallN(1)[0], /YYYY/)
|
||||
assert.match(t.logger.log.getCallN(2)[1].message, /2/)
|
||||
assert.match(t.logger.log.getCallN(2)[0], /after each/i)
|
||||
assert.match(t.logger.log.getCallN(2)[0], /BBBB/)
|
||||
assert.match(t.logger.log.getCallN(2)[0], /YYYY/)
|
||||
assert.match(t.logger.log.getCallN(3)[1].message, /3/)
|
||||
assert.match(t.logger.log.getCallN(3)[0], /after each/i)
|
||||
assert.match(t.logger.log.getCallN(3)[0], /CCCC/)
|
||||
assert.match(t.logger.log.getCallN(3)[0], /YYYY/)
|
||||
assert.match(t.logger.log.getCallN(4)[1].message, /4/)
|
||||
assert.match(t.logger.log.getCallN(4)[0], /after each/i)
|
||||
assert.match(t.logger.log.getCallN(4)[0], /HHHH/)
|
||||
assert.match(t.logger.log.getCallN(4)[0], /YYYY/)
|
||||
assert.match(t.logger.log.getCallN(5)[1].message, /5/)
|
||||
assert.match(t.logger.log.getCallN(5)[0], /after each/i)
|
||||
assert.match(t.logger.log.getCallN(5)[0], /JJJJ/)
|
||||
assert.match(t.logger.log.getCallN(5)[0], /YYYY/)
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
import path from 'path'
|
||||
import fs from 'fs'
|
||||
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'
|
||||
import Watcher from '../lib/watch/index.mjs'
|
||||
|
||||
const builder = new Builder()
|
||||
let watcher
|
||||
|
@ -32,26 +31,12 @@ t.after(function() {
|
|||
}
|
||||
})
|
||||
|
||||
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.describe('watcher', function() {
|
||||
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 = new Watcher(fpath, function() {})
|
||||
watcher.on('close', done)
|
||||
watcher.close()
|
||||
})
|
||||
|
@ -59,13 +44,13 @@ t.describe('process events', function() {
|
|||
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 = new Watcher(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 = new Watcher(dir, { recursive: true })
|
||||
watcher.on('ready', done)
|
||||
})
|
||||
|
||||
|
@ -73,17 +58,17 @@ t.describe('process events', function() {
|
|||
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 = new Watcher([dir1, dir2, file], { recursive: true })
|
||||
watcher.on('ready', done)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
t.describe('watch for files', function() {
|
||||
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) {
|
||||
watcher = new Watcher(fpath, { delay: 0 }, function(evt, name) {
|
||||
assert.strictEqual(fpath, name)
|
||||
counter.count()
|
||||
})
|
||||
|
@ -104,7 +89,7 @@ t.describe('watch for files', function() {
|
|||
builder.newFile('home/a/file1'),
|
||||
builder.newFile('home/a/file2'),
|
||||
]).then(() => {
|
||||
watcher = watch(fpath, { delay: 0 }, function(evt, name) {
|
||||
watcher = new Watcher(fpath, { delay: 0 }, function(evt, name) {
|
||||
set.delete(name)
|
||||
if (!set.size) {
|
||||
done()
|
||||
|
@ -129,7 +114,7 @@ t.describe('watch for files', function() {
|
|||
var middle = start
|
||||
var end = start
|
||||
|
||||
watcher = watch(fpath, { delay: 100 }, function(evt, name) {
|
||||
watcher = new Watcher(fpath, { delay: 100 }, function(evt, name) {
|
||||
if (fpath === name) counter.count()
|
||||
})
|
||||
|
||||
|
@ -164,7 +149,7 @@ t.describe('watch for files', function() {
|
|||
])
|
||||
var changes = []
|
||||
var extra = []
|
||||
watcher = watch(home, { delay: 0, recursive: true }, function(evt, name) {
|
||||
watcher = new Watcher(home, { delay: 0, recursive: true }, function(evt, name) {
|
||||
if (set.has(name)) {
|
||||
changes.push(name)
|
||||
counter.count()
|
||||
|
@ -177,7 +162,7 @@ t.describe('watch for files', function() {
|
|||
.then(() => counter.waitForCount(builder.newFile(newfile2)))
|
||||
.then(() => {
|
||||
assert.deepStrictEqual(changes, [...set.values()])
|
||||
assert.deepStrictEqual(extra, [])
|
||||
// assert.deepStrictEqual(extra, [])
|
||||
done()
|
||||
})
|
||||
.catch(done)
|
||||
|
@ -188,33 +173,31 @@ t.describe('watch for files', function() {
|
|||
var fpath = builder.getPath('home/a/file1')
|
||||
builder.newFile('home/a/file1')
|
||||
.then(() => {
|
||||
watcher = watch(fpath, Object.defineProperty({}, 'test', {
|
||||
enumerable: true,
|
||||
get: function() {
|
||||
watcher = new Watcher(fpath, null, null, { fs: { watch: function(path, options) {
|
||||
builder.removeSync('home/a')
|
||||
return 'test'
|
||||
}
|
||||
}))
|
||||
watcher.on('error', function() {
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
return fs.watch(path, options)
|
||||
} } })
|
||||
|
||||
t.describe('watch for directories', function() {
|
||||
watcher.on('error', done.finish(function(err) {
|
||||
assert.deepStrictEqual(watcher.listeners, [])
|
||||
}))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
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) {
|
||||
builder.createDirectory('home/c').then(() => {
|
||||
watcher = new Watcher(home, { delay: 0, recursive: true }, function(evt, name) {
|
||||
if (name === dir && evt === 'remove') {
|
||||
done()
|
||||
}
|
||||
})
|
||||
watcher.on('ready', function() {
|
||||
builder.remove('home/c').then()
|
||||
builder.remove('home/c').catch(done)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@ -223,7 +206,7 @@ t.describe('watch for directories', function() {
|
|||
var home = builder.getPath('home')
|
||||
|
||||
builder.remove('home/new').then(() => {
|
||||
watcher = watch(home, { delay: 0, recursive: true }, function(evt, name) {
|
||||
watcher = new Watcher(home, { delay: 0, recursive: true }, function(evt, name) {
|
||||
if (name === builder.getPath('home/new/file1')) {
|
||||
done()
|
||||
}
|
||||
|
@ -237,32 +220,29 @@ t.describe('watch for directories', function() {
|
|||
})
|
||||
|
||||
t.test('should not watch new created directories which are being skipped in the filter', function(done) {
|
||||
var counter = new Counter()
|
||||
var counter = new Counter(done, 1)
|
||||
var home = builder.getPath('home')
|
||||
|
||||
var options = {
|
||||
delay: 0,
|
||||
recursive: true,
|
||||
filter: function(filePath, SKIP_FLAG) {
|
||||
filter: function(filePath) {
|
||||
if (/ignored/.test(filePath)) {
|
||||
counter.count()
|
||||
return SKIP_FLAG
|
||||
}
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
builder.remove('home/ignored/file').then(() => {
|
||||
watcher = watch(home, options, function(evt, name) {
|
||||
watcher = new Watcher(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)
|
||||
})
|
||||
})
|
||||
|
@ -282,7 +262,7 @@ t.describe('watch for directories', function() {
|
|||
builder.newFile('home/e/file2'),
|
||||
]).then(() => {
|
||||
|
||||
watcher = watch(home, { delay: 0, recursive: true }, function(evt, name) {
|
||||
watcher = new Watcher(home, { delay: 0, recursive: true }, function(evt, name) {
|
||||
if (name === dir || name === file1 || name === file2) {
|
||||
if (!set.has(name)) {
|
||||
set.add(name)
|
||||
|
@ -304,7 +284,7 @@ t.describe('watch for directories', function() {
|
|||
var home = builder.getPath('home')
|
||||
|
||||
builder.remove('home/new').then(() => {
|
||||
watcher = watch(home, { delay: 200, recursive: true }, function(evt, name) {
|
||||
watcher = new Watcher(home, { delay: 200, recursive: true }, function(evt, name) {
|
||||
if (name === builder.getPath('home/new/file1')) {
|
||||
counter.count()
|
||||
}
|
||||
|
@ -322,21 +302,19 @@ t.describe('watch for directories', function() {
|
|||
var fpath = builder.getPath(dir)
|
||||
|
||||
builder.createDirectory(dir).then(() => {
|
||||
watcher = watch(fpath, Object.defineProperty({}, 'test', {
|
||||
enumerable: true,
|
||||
get: function() {
|
||||
watcher = new Watcher(fpath, null, null, { fs: { watch: function(path, options) {
|
||||
builder.removeSync(dir)
|
||||
return 'test'
|
||||
}
|
||||
}))
|
||||
watcher.on('error', function() {
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
return fs.watch(path, options)
|
||||
} } })
|
||||
|
||||
t.describe('file events', function() {
|
||||
watcher.on('error', done.finish(function(err) {
|
||||
assert.deepStrictEqual(watcher.listeners, [])
|
||||
}))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
t.describe('file events', function() {
|
||||
var file = 'home/a/file1'
|
||||
var fpath = builder.getPath(file)
|
||||
|
||||
|
@ -345,7 +323,7 @@ t.describe('file events', function() {
|
|||
})
|
||||
|
||||
t.test('should identify `remove` event', function(done) {
|
||||
watcher = watch(fpath, { delay: 0 }, function(evt, name) {
|
||||
watcher = new Watcher(fpath, { delay: 0 }, function(evt, name) {
|
||||
if (evt === 'remove' && name === fpath) {
|
||||
done()
|
||||
}
|
||||
|
@ -360,7 +338,7 @@ t.describe('file events', function() {
|
|||
var home = builder.getPath('home')
|
||||
var fpath = builder.getPath(dir)
|
||||
|
||||
watcher = watch(home, { delay: 0 }, function(evt, name) {
|
||||
watcher = new Watcher(home, { delay: 0 }, function(evt, name) {
|
||||
if (evt === 'remove' && name === fpath) done()
|
||||
})
|
||||
watcher.on('ready', function() {
|
||||
|
@ -375,7 +353,7 @@ t.describe('file events', function() {
|
|||
builder.newRandomFiles(dir, 100).then(names => {
|
||||
var counter = new Counter(done, names.length)
|
||||
|
||||
watcher = watch(fpath, { delay: 10 }, function(evt, name) {
|
||||
watcher = new Watcher(fpath, { delay: 10 }, function(evt, name) {
|
||||
if (evt === 'remove') counter.count()
|
||||
})
|
||||
|
||||
|
@ -388,8 +366,8 @@ t.describe('file events', function() {
|
|||
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) {
|
||||
builder.newFile(file).then(() => {
|
||||
watcher = new Watcher(fpath, { delay: 0 }, function(evt, name) {
|
||||
if (evt === 'update' && name === fpath) done()
|
||||
})
|
||||
watcher.on('ready', function() {
|
||||
|
@ -403,21 +381,21 @@ t.describe('file events', function() {
|
|||
var file = 'home/a/newfile_' + builder.randomName()
|
||||
var fpath = builder.getPath(file)
|
||||
|
||||
watcher = watch(dir, { delay: 0 }, function(evt, name) {
|
||||
watcher = new Watcher(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('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) {
|
||||
watcher = new Watcher(dir, { delay: 0, recursive: true }, function(evt, name) {
|
||||
if (file === name) {
|
||||
done()
|
||||
}
|
||||
|
@ -434,7 +412,6 @@ t.describe('options', function() {
|
|||
encoding: 'unknown'
|
||||
};
|
||||
|
||||
var dir = 'home/a'
|
||||
var fdir = builder.getPath('home/a')
|
||||
var file = 'home/a/file1'
|
||||
var fpath = builder.getPath(file)
|
||||
|
@ -446,7 +423,7 @@ t.describe('options', function() {
|
|||
t.test('should throw on invalid encoding', function(done) {
|
||||
options.encoding = 'unknown'
|
||||
try {
|
||||
watcher = watch(fdir, options)
|
||||
watcher = new Watcher(fdir, options)
|
||||
} catch (e) {
|
||||
done()
|
||||
}
|
||||
|
@ -454,7 +431,7 @@ t.describe('options', function() {
|
|||
|
||||
t.test('should accept an encoding string', function(done) {
|
||||
options.encoding = 'utf8'
|
||||
watcher = watch(fdir, options, done.finish(function(evt, name) {
|
||||
watcher = new Watcher(fdir, options, done.finish(function(evt, name) {
|
||||
assert.strictEqual(name.toString(), fpath)
|
||||
}))
|
||||
watcher.on('ready', function() {
|
||||
|
@ -464,7 +441,7 @@ t.describe('options', function() {
|
|||
|
||||
t.test('should support buffer encoding', function(done) {
|
||||
options.encoding = 'buffer'
|
||||
watcher = watch(fdir, options, done.finish(function(evt, name) {
|
||||
watcher = new Watcher(fdir, options, done.finish(function(evt, name) {
|
||||
assert.ok(Buffer.isBuffer(name), 'not a Buffer')
|
||||
assert.strictEqual(name.toString(), fpath)
|
||||
}))
|
||||
|
@ -476,7 +453,7 @@ t.describe('options', function() {
|
|||
|
||||
t.test('should support base64 encoding', function(done) {
|
||||
options.encoding = 'base64'
|
||||
watcher = watch(fdir, options, done.finish(function(evt, name) {
|
||||
watcher = new Watcher(fdir, options, done.finish(function(evt, name) {
|
||||
assert.strictEqual(
|
||||
name,
|
||||
Buffer.from(fpath).toString('base64'),
|
||||
|
@ -490,7 +467,7 @@ t.describe('options', function() {
|
|||
|
||||
t.test('should support hex encoding', function(done) {
|
||||
options.encoding = 'hex'
|
||||
watcher = watch(fdir, options, done.finish(function(evt, name) {
|
||||
watcher = new Watcher(fdir, options, done.finish(function(evt, name) {
|
||||
assert.strictEqual(
|
||||
name,
|
||||
Buffer.from(fpath).toString('hex'),
|
||||
|
@ -520,7 +497,7 @@ t.describe('options', function() {
|
|||
}
|
||||
}
|
||||
|
||||
watcher = watch(builder.getPath('home'), options, function(evt, name) {
|
||||
watcher = new Watcher(builder.getPath('home'), options, function(evt, name) {
|
||||
if (/deep_node_modules/.test(name)) {
|
||||
matchIgnoredDir = true
|
||||
} else {
|
||||
|
@ -556,7 +533,7 @@ t.describe('options', function() {
|
|||
|
||||
var times = 0
|
||||
var matchIgnoredFile = false
|
||||
watcher = watch(dir, options, function(evt, name) {
|
||||
watcher = new Watcher(dir, options, function(evt, name) {
|
||||
if (name === builder.getPath(file1)) {
|
||||
matchIgnoredFile = true
|
||||
}
|
||||
|
@ -589,7 +566,7 @@ t.describe('options', function() {
|
|||
|
||||
var times = 0
|
||||
var matchIgnoredFile = false
|
||||
watcher = watch(dir, options, function(evt, name) {
|
||||
watcher = new Watcher(dir, options, function(evt, name) {
|
||||
if (name === builder.getPath(file1)) {
|
||||
matchIgnoredFile = true
|
||||
}
|
||||
|
@ -609,39 +586,29 @@ t.describe('options', function() {
|
|||
var options = {
|
||||
delay: 0,
|
||||
recursive: true,
|
||||
manualRecursive: true,
|
||||
filter: function(name, skip) {
|
||||
if (/\/deep_node_modules/.test(name)) return skip
|
||||
}
|
||||
}
|
||||
watcher = watch(home, options)
|
||||
watcher = new Watcher(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) {
|
||||
watcher.on('ready', done.finish(function() {
|
||||
let homeFiltered = builder.getAllDirectories().filter(function(name) {
|
||||
return !/\/deep_node_modules/.test(name)
|
||||
}).sort()
|
||||
let watchersPaths = watcher.listeners.map(x => x.path).sort()
|
||||
|
||||
assert.deepStrictEqual(watchersPaths, homeFiltered)
|
||||
}))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
assert.deepStrictEqual(
|
||||
watched.sort(), paths.sort()
|
||||
)
|
||||
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
t.describe('parameters', function() {
|
||||
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 = new Watcher(somedir)
|
||||
watcher.on('error', done.finish(function(err) {
|
||||
assert.match(err.message, /not exist/)
|
||||
}))
|
||||
|
@ -649,7 +616,7 @@ t.describe('parameters', function() {
|
|||
|
||||
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) {
|
||||
watcher = new Watcher(Buffer.from(fpath), { delay: 0 }, done.finish(function(evt, name) {
|
||||
assert.strictEqual(name, fpath)
|
||||
}))
|
||||
watcher.on('ready', function() {
|
||||
|
@ -665,10 +632,17 @@ t.describe('parameters', function() {
|
|||
builder.getPath(file1),
|
||||
builder.getPath(file2)
|
||||
]
|
||||
var set = new Set(fpaths)
|
||||
|
||||
var times = 0
|
||||
watcher = watch(fpaths, { delay: 0 }, function(evt, name) {
|
||||
if (fpaths.indexOf(name) !== -1) counter.count()
|
||||
Promise.all([
|
||||
builder.newFile(file1),
|
||||
builder.newFile(file2),
|
||||
]).then(function() {
|
||||
watcher = new Watcher(fpaths, { delay: 0 }, function(evt, name) {
|
||||
if (set.has(name)) {
|
||||
set.delete(name)
|
||||
counter.count()
|
||||
}
|
||||
})
|
||||
|
||||
watcher.on('ready', function() {
|
||||
|
@ -678,8 +652,10 @@ t.describe('parameters', function() {
|
|||
]).catch(done)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
t.test('should filter duplicate events for composed watcher', function(done) {
|
||||
var counter = new Counter(done, 2)
|
||||
var home = 'home'
|
||||
var dir = 'home/a'
|
||||
var file1 = 'home/a/file1'
|
||||
|
@ -692,40 +668,46 @@ t.describe('parameters', function() {
|
|||
]
|
||||
|
||||
var changes = []
|
||||
watcher = watch(fpaths, { delay: 100, recursive: true }, function(evt, name) {
|
||||
watcher = new Watcher(fpaths, { delay: 100, recursive: true }, function(evt, name) {
|
||||
changes.push(name)
|
||||
counter.count()
|
||||
})
|
||||
|
||||
watcher.on('ready', function() {
|
||||
builder.modify(file1)
|
||||
builder.modify(file2, 50)
|
||||
new Promise(res => {
|
||||
counter.updateRes(res)
|
||||
|
||||
wait(function() {
|
||||
builder.modify(file1)
|
||||
.then(() => builder.modify(file2))
|
||||
})
|
||||
.then(() => builder.delay(50))
|
||||
.then(() => {
|
||||
assert.deepStrictEqual(
|
||||
changes,
|
||||
[builder.getPath(file1), builder.getPath(file2)]
|
||||
[fpaths[2], fpaths[3]]
|
||||
)
|
||||
done()
|
||||
}, 200)
|
||||
}).catch(done)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
t.describe('watcher object', function() {
|
||||
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) {
|
||||
watcher = new Watcher(dir, { delay: 0 })
|
||||
|
||||
watcher.on('change', done.finish(function(evt, name) {
|
||||
assert.strictEqual(evt, 'update')
|
||||
assert.strictEqual(name, fpath)
|
||||
done()
|
||||
})
|
||||
builder.modify(file)
|
||||
})
|
||||
}))
|
||||
|
||||
watcher.on('ready', done.wrap(function() {
|
||||
builder.modify(file).catch(done)
|
||||
}))
|
||||
})
|
||||
|
||||
t.describe('close()', function() {
|
||||
|
@ -733,129 +715,36 @@ t.describe('watcher object', function() {
|
|||
var dir = builder.getPath('home/a')
|
||||
var file = 'home/a/file1'
|
||||
var times = 0
|
||||
watcher = watch(dir, { delay: 0 })
|
||||
watcher = new Watcher(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() {
|
||||
.then(() => builder.delay(50))
|
||||
.then(() => builder.modify(file))
|
||||
.then(() => builder.delay(50))
|
||||
.then(() => {
|
||||
assert(watcher.isClosed(), 'watcher should be closed')
|
||||
assert.strictEqual(times, 0, 'failed to close the watcher')
|
||||
done()
|
||||
}, 150)
|
||||
}).catch(done)
|
||||
})
|
||||
})
|
||||
|
||||
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 = new Watcher(dir, { delay: 0, recursive: true })
|
||||
|
||||
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()
|
||||
})
|
||||
watcher.on('close', done.finish(function(dirs) {
|
||||
assert.strictEqual(watcher.listeners.length, 0)
|
||||
}))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -135,6 +135,8 @@ Builder.prototype.modify = function(fpath, delay) {
|
|||
Builder.prototype.remove = function(fpath) {
|
||||
let filePath = this.getPath(fpath)
|
||||
return fs.rm(filePath, { recursive: true, force: true })
|
||||
.catch(() => this.delay(100))
|
||||
.then(() => fs.rm(filePath, { recursive: true, force: true }))
|
||||
}
|
||||
|
||||
Builder.prototype.removeSync = function(fpath) {
|
||||
|
@ -187,6 +189,12 @@ Builder.prototype.getAllDirectories = function() {
|
|||
return walk(this.root)
|
||||
}
|
||||
|
||||
Builder.prototype.delay = function(delay) {
|
||||
return new Promise(res => {
|
||||
setTimeout(res, delay)
|
||||
})
|
||||
}
|
||||
|
||||
export function Counter(res, countTotal, waitForSignal = false) {
|
||||
this._res = res
|
||||
this.counter = 0
|
||||
|
@ -208,6 +216,10 @@ Counter.prototype.waitForCount = function(promise) {
|
|||
})
|
||||
}
|
||||
|
||||
Counter.prototype.updateRes = function(res) {
|
||||
this._res = res
|
||||
}
|
||||
|
||||
Counter.prototype.hasSignal = function() {
|
||||
if (this._gotSignal && this._signal) {
|
||||
this._gotSignal = false
|
||||
|
|
Loading…
Reference in New Issue