diff --git a/lib/filesystem.js b/lib/filesystem.js index 92425c4c..14c345b7 100644 --- a/lib/filesystem.js +++ b/lib/filesystem.js @@ -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; @@ -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); diff --git a/lib/index.js b/lib/index.js index 32caa676..98226735 100644 --- a/lib/index.js +++ b/lib/index.js @@ -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); @@ -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 diff --git a/readme.md b/readme.md index d16cb5ff..20593243 100644 --- a/readme.md +++ b/readme.md @@ -28,16 +28,23 @@ mock.restore(); ## Docs -### `mock(config)` +### `mock(config, options)` 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.* +### `options` + +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). @@ -169,9 +176,9 @@ afterEach(mock.restore); ### Creating a new `fs` module instead of modifying the original -### `mock.fs(config)` +### `mock.fs(config, options)` -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 diff --git a/test/lib/filesystem.spec.js b/test/lib/filesystem.spec.js index 7bdd7855..6d028497 100644 --- a/test/lib/filesystem.spec.js +++ b/test/lib/filesystem.spec.js @@ -1,6 +1,7 @@ /* eslint-env mocha */ 'use strict'; +var os = require('os'); var path = require('path'); var Directory = require('../../lib/directory'); @@ -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() { @@ -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({ diff --git a/test/lib/index.spec.js b/test/lib/index.spec.js index ef010573..f196e5db 100644 --- a/test/lib/index.spec.js +++ b/test/lib/index.spec.js @@ -22,10 +22,6 @@ describe('The API', function() { mock.restore(); }); - }); - - describe('mock()', function() { - it('creates process.cwd() and os.tmpdir() by default', function() { mock(); @@ -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() { @@ -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({