Skip to content

Commit

Permalink
Merge pull request #72 from tschaub/options
Browse files Browse the repository at this point in the history
Add options for creating cwd and tmp directories.
  • Loading branch information
tschaub committed Dec 20, 2015
2 parents 6dde8fb + fd9ab8a commit ad183ca
Show file tree
Hide file tree
Showing 5 changed files with 124 additions and 16 deletions.
30 changes: 26 additions & 4 deletions lib/filesystem.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,31 @@ function getPathParts(filepath) {

/**
* Create a new file system.
* @param {Object} options Any filesystem options.
* @param {boolean} options.createCwd Create a directory for `process.cwd()`
* (defaults to `true`).
* @param {boolean} options.createTmp Create a directory for `os.tmpdir()`
* (defaults to `true`).
* @constructor
*/
function FileSystem() {
function FileSystem(options) {
options = options || {};

var createCwd = 'createCwd' in options ? options.createCwd : true;
var createTmp = 'createTmp' in options ? options.createTmp : true;

var root = new Directory();

// populate with default directories
var defaults = [os.tmpdir && os.tmpdir() || os.tmpDir(), process.cwd()];
var defaults = [];
if (createCwd) {
defaults.push(process.cwd());
}

if (createTmp) {
defaults.push(os.tmpdir && os.tmpdir() || os.tmpDir());
}

defaults.forEach(function(dir) {
var parts = getPathParts(dir);
var directory = root;
Expand Down Expand Up @@ -141,10 +158,15 @@ function populate(directory, name, obj) {
/**
* Configure a mock file system.
* @param {Object} paths Config object.
* @param {Object} options Any filesystem options.
* @param {boolean} options.createCwd Create a directory for `process.cwd()`
* (defaults to `true`).
* @param {boolean} options.createTmp Create a directory for `os.tmpdir()`
* (defaults to `true`).
* @return {FileSystem} Mock file system.
*/
FileSystem.create = function(paths) {
var system = new FileSystem();
FileSystem.create = function(paths, options) {
var system = new FileSystem(options);

for (var filepath in paths) {
var parts = getPathParts(filepath);
Expand Down
18 changes: 14 additions & 4 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,14 @@ function setProcess(cwd, chdir) {
/**
* Swap out the fs bindings for a mock file system.
* @param {Object} config Mock file system configuration.
* @param {Object} options Any filesystem options.
* @param {boolean} options.createCwd Create a directory for `process.cwd()`
* (defaults to `true`).
* @param {boolean} options.createTmp Create a directory for `os.tmpdir()`
* (defaults to `true`).
*/
var exports = module.exports = function mock(config) {
var system = FileSystem.create(config);
var exports = module.exports = function mock(config, options) {
var system = FileSystem.create(config, options);
var binding = new Binding(system);
setBinding(binding, binding.Stats);

Expand Down Expand Up @@ -104,10 +109,15 @@ exports.restore = function() {
/**
* Create a mock fs module based on the given file system configuration.
* @param {Object} config File system configuration.
* @param {Object} options Any filesystem options.
* @param {boolean} options.createCwd Create a directory for `process.cwd()`
* (defaults to `true`).
* @param {boolean} options.createTmp Create a directory for `os.tmpdir()`
* (defaults to `true`).
* @return {Object} A fs module with a mock file system.
*/
exports.fs = function(config) {
var system = FileSystem.create(config);
exports.fs = function(config, options) {
var system = FileSystem.create(config, options);
var binding = new Binding(system);

// inject the mock binding
Expand Down
15 changes: 11 additions & 4 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,23 @@ mock.restore();

## Docs

### <a id='mockconfig'>`mock(config)`</a>
### <a id='mockconfigoptions'>`mock(config, options)`</a>

Configure the `fs` module so it is backed by an in-memory file system.

Calling `mock` sets up a mock file system with at least two directories: `process.cwd()` and `os.tmpdir()` (or `os.tmpDir()` for older Node). When called with no arguments, just these two directories are created. When called with a `config` object, additional files, directories, and symlinks are created.
Calling `mock` sets up a mock file system with two directories by default: `process.cwd()` and `os.tmpdir()` (or `os.tmpDir()` for older Node). When called with no arguments, just these two directories are created. When called with a `config` object, additional files, directories, and symlinks are created. To avoid creating a directory for `process.cwd()` and `os.tmpdir()`, see the [`options`](#options) below.

Property names of the `config` object are interpreted as relative paths to resources (relative from `process.cwd()`). Property values of the `config` object are interpreted as content or configuration for the generated resources.

*Note that paths should always use forward slashes (`/`) - even on Windows.*

### <a id='options'>`options`</a>

The second (optional) argument may include the properties below.

* `createCwd` - `boolean` Create a directory for `process.cwd()`. This is `true` by default.
* `createTmp` - `boolean` Create a directory for `os.tmpdir()`. This is `true` by default.

### Creating files

When `config` property values are a `string` or `Buffer`, a file is created with the provided content. For example, the following configuration creates a single file with string content (in addition to the two default directories).
Expand Down Expand Up @@ -169,9 +176,9 @@ afterEach(mock.restore);

### Creating a new `fs` module instead of modifying the original

### <a id='mockfsconfig'>`mock.fs(config)`</a>
### <a id='mockfsconfigoptions'>`mock.fs(config, options)`</a>

Calling `mock()` modifies Node's built-in `fs` module. This is useful when you want to test with a mock file system. If for some reason you want to work with the real file system and an in-memory version at the same time, you can call the `mock.fs()` function. This takes the same `config` object [described above](#mockconfig) and sets up a in-memory file system. Instead of modifying the binding for the built-in `fs` module (as is done when calling `mock(config)`), the `mock.fs(config)` function returns an object with the same interface as the `fs` module, but backed by your mock file system.
Calling `mock()` modifies Node's built-in `fs` module. This is useful when you want to test with a mock file system. If for some reason you want to work with the real file system and an in-memory version at the same time, you can call the `mock.fs()` function. This takes the same `config` and `options` objects [described above](#mockconfigoptions) and sets up a in-memory file system. Instead of modifying the binding for the built-in `fs` module (as is done when calling `mock(config)`), the `mock.fs(config)` function returns an object with the same interface as the `fs` module, but backed by your mock file system.

## Install

Expand Down
35 changes: 35 additions & 0 deletions test/lib/filesystem.spec.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* eslint-env mocha */
'use strict';

var os = require('os');
var path = require('path');

var Directory = require('../../lib/directory');
Expand All @@ -18,6 +19,24 @@ describe('FileSystem', function() {
assert.instanceOf(system, FileSystem);
});

it('accepts a createCwd option', function() {
var cwd = process.cwd();
var withCwd = new FileSystem({createCwd: true});
var withoutCwd = new FileSystem({createCwd: false});

assert.instanceOf(withCwd.getItem(cwd), Directory);
assert.isNull(withoutCwd.getItem(cwd));
});

it('accepts a createTmp option', function() {
var tmp = os.tmpdir ? os.tmpdir() : os.tmpDir();
var withTmp = new FileSystem({createTmp: true});
var withoutTmp = new FileSystem({createTmp: false});

assert.instanceOf(withTmp.getItem(tmp), Directory);
assert.isNull(withoutTmp.getItem(tmp));
});

});

describe('#getItem()', function() {
Expand Down Expand Up @@ -160,6 +179,22 @@ describe('FileSystem.create', function() {

});

it('passes options to the FileSystem constructor', function() {

var cwd = process.cwd();
var tmp = os.tmpdir ? os.tmpdir() : os.tmpDir();

var withoutCwd = FileSystem.create({}, {createCwd: false});
var withoutTmp = FileSystem.create({}, {createTmp: false});

assert.isNull(withoutCwd.getItem(cwd));
assert.instanceOf(withoutCwd.getItem(tmp), Directory);

assert.isNull(withoutTmp.getItem(tmp));
assert.instanceOf(withoutTmp.getItem(cwd), Directory);

});

it('accepts file factory', function() {

var system = FileSystem.create({
Expand Down
42 changes: 38 additions & 4 deletions test/lib/index.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,6 @@ describe('The API', function() {
mock.restore();
});

});

describe('mock()', function() {

it('creates process.cwd() and os.tmpdir() by default', function() {
mock();

Expand All @@ -43,6 +39,30 @@ describe('The API', function() {
mock.restore();
});

it('passes the createCwd option to the FileSystem constructor', function() {
mock({}, {createCwd: false});

assert.isFalse(fs.existsSync(process.cwd()));

mock.restore();
});

it('passes the createTmp option to the FileSystem constructor', function() {
mock({}, {createTmp: false});

var tmp;
if (os.tmpdir) {
tmp = os.tmpdir();
} else if (os.tmpDir) {
tmp = os.tmpDir();
}
if (tmp) {
assert.isFalse(fs.existsSync(tmp));
}

mock.restore();
});

});

describe('mock.restore()', function() {
Expand Down Expand Up @@ -179,6 +199,20 @@ describe('The API', function() {

});

it('passes options to the FileSystem constructor', function() {

var mockFs = mock.fs({
'/path/to/file.txt': 'file content'
}, {
createCwd: false,
createTmp: false
});

assert.isTrue(mockFs.existsSync('/path/to/file.txt'));
assert.deepEqual(mockFs.readdirSync('/'), ['path']);

});

it('accepts an arbitrary nesting of files and directories', function() {

var mockFs = mock.fs({
Expand Down

0 comments on commit ad183ca

Please sign in to comment.