Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add options for creating cwd and tmp directories #72

Merged
merged 4 commits into from
Dec 20, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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