Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
355 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
node-fake-fs | ||
fake-fs | ||
============ | ||
|
||
Fake node.js file system for testing |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
module.exports = require('./lib/fake-fs') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
var stat = require('./stat') | ||
var normalize = require('path').normalize | ||
|
||
function FsError (code) { | ||
var err = new Error(code) | ||
err.code = code | ||
return err | ||
} | ||
|
||
|
||
module.exports = Fs | ||
|
||
function Fs (paths) { | ||
this.root = new stat.Dir | ||
} | ||
|
||
Fs.prototype.dir = function (path, opts) { | ||
return this._add(path, new stat.Dir(opts)) | ||
} | ||
|
||
Fs.prototype.file = function (path, content, encoding) { | ||
return this._add(path, new stat.File(content, encoding)) | ||
} | ||
|
||
Fs.prototype._add = function (path, item) { | ||
var segs = path.split('/') | ||
var dir = this.root | ||
for (var i = 0; i < segs.length - 1; i++) { | ||
dir = dir.childs[segs[i]] || (dir.childs[segs[i]] = new stat.Dir) | ||
if (!dir.isDirectory()) { | ||
throw new Error('There is already ' + dir + ' defined at ' + segs.slice(i).join('/')) | ||
} | ||
} | ||
dir.childs[segs[i]] = item | ||
return this | ||
} | ||
|
||
Fs.prototype._itemAt = function (path) { | ||
path = normalize(path).replace('\\', '/') // windows support | ||
var segs = path.split('/') | ||
var item = this.root | ||
if (segs[0] == '.') return item | ||
for (var i = 0; i < segs.length; i++) { | ||
item = item.childs && item.childs[segs[i]] | ||
if (!item) return | ||
} | ||
return item | ||
} | ||
|
||
Fs.prototype._get = function (path) { | ||
var item = this._itemAt(path) | ||
if (!item) throw FsError('ENOENT') | ||
return item | ||
} | ||
|
||
|
||
Fs.prototype.statSync = function (path) { | ||
return this._get(path) | ||
} | ||
|
||
Fs.prototype.existsSync = function (path) { | ||
return !!this._itemAt(path) | ||
} | ||
|
||
Fs.prototype.readdirSync = function (dir) { | ||
var item = this._get(dir) | ||
if (!item.isDirectory()) throw FsError('ENOTDIR') | ||
return Object.keys(item.childs) | ||
} | ||
|
||
Fs.prototype.readFileSync = function (filename, encoding) { | ||
var item = this._get(filename) | ||
if (item.isDirectory()) throw FsError('EISDIR') | ||
return item.read(encoding) | ||
} | ||
|
||
;['readdir', 'stat'].forEach(function (meth) { | ||
var sync = meth + 'Sync' | ||
Fs.prototype[meth] = function (p, cb) { | ||
var res, err | ||
try { | ||
res = this[sync].call(this, p) | ||
} catch (e) { | ||
err = e | ||
} | ||
cb && cb(err, res) | ||
} | ||
}) | ||
|
||
Fs.prototype.readFile = function (filename, encoding, cb) { | ||
if (typeof encoding != 'string') { | ||
cb = encoding | ||
encoding = undefined | ||
} | ||
var res, err | ||
try { | ||
res = this.readFileSync(filename, encoding) | ||
} catch (e) { | ||
err = e | ||
} | ||
cb && cb(err, res) | ||
} | ||
|
||
Fs.prototype.exists = function (path, cb) { | ||
cb && cb(this.existsSync(path)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
function mix (target, src) { | ||
for (var key in src) { | ||
target[key] = src[key] | ||
} | ||
} | ||
|
||
function stat (Klass, props) { | ||
Klass.prototype = { | ||
isDirectory: function () { | ||
return false | ||
}, | ||
isFile: function () { | ||
return false | ||
} | ||
} | ||
mix(Klass.prototype, props) | ||
return Klass | ||
} | ||
|
||
|
||
exports.Dir = stat(function Dir (opts) { | ||
mix(this, opts) | ||
this.childs = {} | ||
}, { | ||
isDirectory: function () { | ||
return true | ||
}, | ||
|
||
toString: function () { | ||
return 'directory' | ||
} | ||
}) | ||
|
||
|
||
exports.File = stat(function File (content, encoding) { | ||
if (typeof content == 'string') { | ||
this.content = content | ||
this.encoding = encoding | ||
} else if (Buffer.isBuffer(content)) { | ||
this.content = content | ||
} else { | ||
mix(this, content) | ||
} | ||
if (this.content != null && | ||
typeof this.content != 'string' && | ||
!Buffer.isBuffer(this.content)) { | ||
throw new Error('File content can be a string or buffer') | ||
} | ||
}, { | ||
isFile: function () { | ||
return true | ||
}, | ||
|
||
read: function (encoding) { | ||
return encoding | ||
? this._buffer().toString(encoding) | ||
: this._buffer() | ||
}, | ||
|
||
_buffer: function () { | ||
if (Buffer.isBuffer(this.content)) return this.content | ||
return this.content = new Buffer(this.content || '', this.encoding || 'utf8') | ||
}, | ||
|
||
toString: function () { | ||
return 'file' | ||
} | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
{ | ||
"name": "fake-fs", | ||
"version": "0.0.0", | ||
"description": "Fake file system for testing", | ||
"scripts": { | ||
"test": "mocha -R spec" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "git://github.com/eldargab/node-fake-fs.git" | ||
}, | ||
"keywords": [ | ||
"fs", | ||
"fake", | ||
"mocks", | ||
"test" | ||
], | ||
"devDependecies": { | ||
"mocha": "1.4.2", | ||
"sinon": "1.4.2" | ||
}, | ||
"author": "Eldar Gabdullin <eldargab@gmail.com>", | ||
"license": "MIT" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
var should = require('should') | ||
var sinon = require('sinon') | ||
var Fs = require('..') | ||
|
||
function Cb () { | ||
var spy = sinon.spy() | ||
|
||
spy.error = function (err) { | ||
this.calledOnce.should.be.true | ||
this.firstCall.args[0].code.should.equal(err) | ||
} | ||
|
||
spy.result = function () { | ||
this.calledOnce.should.be.true | ||
should.not.exist(this.firstCall.args[0]) | ||
return this.firstCall.args[1] | ||
} | ||
|
||
return spy | ||
} | ||
|
||
describe('Fake FS', function () { | ||
var fs, cb | ||
|
||
beforeEach(function () { | ||
fs = new Fs | ||
cb = Cb() | ||
}) | ||
|
||
describe('.dir(path, [opts])', function () { | ||
it('Should define dir', function () { | ||
fs.dir('a/b/c').statSync('a/b/c').isDirectory().should.be.true | ||
}) | ||
|
||
it('Should support options', function () { | ||
var stat = fs.dir('a', { | ||
mtime: 10, | ||
atime: 30 | ||
}).statSync('a') | ||
stat.should.have.property('mtime').equal(10) | ||
stat.should.have.property('atime').equal(30) | ||
}) | ||
|
||
it('Should work like mkdir -p', function () { | ||
fs.dir('a', { mtime: 100 }) | ||
fs.dir('a/b/c') | ||
fs.statSync('a').mtime.should.equal(100) | ||
fs.statSync('a/b').isDirectory().should.be.true | ||
}) | ||
}) | ||
|
||
describe('.file(path, [opts | content, [encoding]]', function () { | ||
it('Should define file', function () { | ||
fs.file('a/b.txt').statSync('a/b.txt').isFile().should.be.true | ||
}) | ||
|
||
it('Should work like mkdir -p for parent dir', function () { | ||
fs.dir('a', { mtime: 100 }) | ||
fs.file('a/b.txt') | ||
fs.statSync('a').mtime.should.equal(100) | ||
}) | ||
|
||
it('Should support content & encoding params', function () { | ||
fs.file('hello.txt', 'hello') | ||
.readFileSync('hello.txt', 'utf8') | ||
.should.equal('hello') | ||
|
||
fs.file('bin', 'TWFu', 'base64') | ||
.readFileSync('bin', 'utf8').should.equal('Man') | ||
|
||
fs.file('bin2', new Buffer([10])) | ||
fs.readFileSync('bin2')[0].should.equal(10) | ||
}) | ||
|
||
it('Should support options param', function () { | ||
fs.file('hello.txt', { | ||
atime: 10, | ||
mtime: 20, | ||
content: 'a' | ||
}) | ||
var stat = fs.statSync('hello.txt') | ||
stat.should.have.property('atime').equal(10) | ||
stat.should.have.property('mtime').equal(20) | ||
fs.readFileSync('hello.txt')[0].should.equal(97) | ||
}) | ||
}) | ||
|
||
describe('.stat()', function () { | ||
it('Should return stats', function () { | ||
fs.file('a/b/c', {ctime: 123}).stat('a/b/c', cb) | ||
cb.result().should.have.property('ctime').equal(123) | ||
}) | ||
|
||
it('Should throw ENOENT on non-existent path', function () { | ||
fs.stat('undefined', cb) | ||
cb.error('ENOENT') | ||
}) | ||
}) | ||
|
||
describe('.readdir()', function () { | ||
it('Should list a dir contents', function () { | ||
fs.dir('a').file('b.txt').readdir('.', cb) | ||
cb.result().should.eql(['a', 'b.txt']) | ||
}) | ||
|
||
it('Should throw ENOENT on non-existent path', function () { | ||
fs.readdir('a', cb) | ||
cb.error('ENOENT') | ||
}) | ||
|
||
it('Should throw ENOTDIR on non-dir', function () { | ||
fs.file('a.txt').readdir('a.txt', cb) | ||
cb.error('ENOTDIR') | ||
}) | ||
}) | ||
|
||
describe('.exists()', function () { | ||
it('Should return true on existent path', function (done) { | ||
fs.dir('asd').exists('asd', function (exists) { | ||
exists.should.be.true | ||
done() | ||
}) | ||
}) | ||
|
||
it('Should return false for non-existent path', function (done) { | ||
fs.exists('non-existent', function (exists) { | ||
exists.should.be.false | ||
done() | ||
}) | ||
}) | ||
}) | ||
|
||
describe('.readFile()', function () { | ||
it('Should read file contents', function () { | ||
var content = new Buffer([1, 2, 3]) | ||
fs.file('bin', content).readFile('bin', cb) | ||
cb.result().should.equal(content) | ||
}) | ||
|
||
it('Should decode file contents', function () { | ||
fs.file('file.txt', new Buffer([97])).readFile('file.txt', 'ascii', cb) | ||
cb.result().should.equal('a') | ||
}) | ||
|
||
it('Should throw ENOENT on non-existent file', function () { | ||
fs.readFile('foo', cb) | ||
cb.error('ENOENT') | ||
}) | ||
|
||
it('Should throw EISDIR on directory', function () { | ||
fs.dir('dir').readFile('dir', cb) | ||
cb.error('EISDIR') | ||
}) | ||
}) | ||
}) |