-
Notifications
You must be signed in to change notification settings - Fork 86
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #304 from nonara/master
Helper to bypass mock FS & expose real files/directories
- Loading branch information
Showing
10 changed files
with
477 additions
and
5 deletions.
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 |
---|---|---|
@@ -0,0 +1,55 @@ | ||
const realBinding = process.binding('fs'); | ||
let storedBinding; | ||
|
||
/** | ||
* Perform action, bypassing mock FS | ||
* @example | ||
* // This file exists on the real FS, not on the mocked FS | ||
* const filePath = '/path/file.json'; | ||
* const data = mock.bypass(() => fs.readFileSync(filePath, 'utf-8')); | ||
*/ | ||
exports = module.exports = function bypass(fn) { | ||
if (typeof fn !== 'function') { | ||
throw new Error(`Must provide a function to perform for mock.bypass()`); | ||
} | ||
|
||
exports.disable(); | ||
|
||
try { | ||
// Perform action | ||
const res = fn(); | ||
|
||
// Handle promise return | ||
if (typeof res.then === 'function') { | ||
res.then(exports.enable); | ||
res.catch(exports.enable); | ||
} else { | ||
exports.enable(); | ||
} | ||
|
||
return res; | ||
} catch (e) { | ||
exports.enable(); | ||
throw e; | ||
} | ||
}; | ||
|
||
/** | ||
* Temporarily disable Mocked FS | ||
*/ | ||
exports.disable = () => { | ||
if (realBinding._mockedBinding) { | ||
storedBinding = realBinding._mockedBinding; | ||
delete realBinding._mockedBinding; | ||
} | ||
}; | ||
|
||
/** | ||
* Enables Mocked FS after being disabled by mock.disable() | ||
*/ | ||
exports.enable = () => { | ||
if (storedBinding) { | ||
realBinding._mockedBinding = storedBinding; | ||
storedBinding = undefined; | ||
} | ||
}; |
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
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
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
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,118 @@ | ||
const {fixWin32Permissions} = require('./item'); | ||
const path = require('path'); | ||
const FileSystem = require('./filesystem'); | ||
const fs = require('fs'); | ||
const bypass = require('./bypass'); | ||
|
||
const createContext = ({output, options = {}, target}, newContext) => | ||
Object.assign( | ||
{ | ||
// Assign options and set defaults if needed | ||
options: { | ||
recursive: options.recursive !== false, | ||
lazyLoad: options.lazyLoad !== false | ||
}, | ||
output, | ||
target | ||
}, | ||
newContext | ||
); | ||
|
||
function addFile(context, stats, isRoot) { | ||
const {output, target} = context; | ||
const {lazyLoad} = context.options; | ||
|
||
if (!stats.isFile()) { | ||
throw new Error(`${target} is not a valid file!`); | ||
} | ||
|
||
const outputPropKey = isRoot ? target : path.basename(target); | ||
|
||
output[outputPropKey] = () => { | ||
const content = !lazyLoad ? fs.readFileSync(target) : ''; | ||
const file = FileSystem.file(Object.assign({}, stats, {content}))(); | ||
|
||
if (lazyLoad) { | ||
Object.defineProperty(file, '_content', { | ||
get() { | ||
const res = bypass(() => fs.readFileSync(target)); | ||
Object.defineProperty(file, '_content', { | ||
value: res, | ||
writable: true | ||
}); | ||
return res; | ||
}, | ||
set(data) { | ||
Object.defineProperty(file, '_content', { | ||
value: data, | ||
writable: true | ||
}); | ||
}, | ||
configurable: true | ||
}); | ||
} | ||
|
||
return file; | ||
}; | ||
|
||
return output[outputPropKey]; | ||
} | ||
|
||
function addDir(context, stats, isRoot) { | ||
const {target, output} = context; | ||
const {recursive} = context.options; | ||
|
||
if (!stats.isDirectory()) { | ||
throw new Error(`${target} is not a valid directory!`); | ||
} | ||
|
||
stats = Object.assign({}, stats); | ||
const outputPropKey = isRoot ? target : path.basename(target); | ||
|
||
// On windows platforms, directories do not have the executable flag, which causes FileSystem.prototype.getItem | ||
// to think that the directory cannot be traversed. This is a workaround, however, a better solution may be to | ||
// re-think the logic in FileSystem.prototype.getItem | ||
// This workaround adds executable privileges if read privileges are found | ||
stats.mode = fixWin32Permissions(stats.mode); | ||
|
||
// Create directory factory | ||
const directoryItems = {}; | ||
output[outputPropKey] = FileSystem.directory( | ||
Object.assign(stats, {items: directoryItems}) | ||
); | ||
|
||
fs.readdirSync(target).forEach(p => { | ||
const absPath = path.join(target, p); | ||
const stats = fs.statSync(absPath); | ||
const newContext = createContext(context, { | ||
target: absPath, | ||
output: directoryItems | ||
}); | ||
|
||
if (recursive && stats.isDirectory()) { | ||
addDir(newContext, stats); | ||
} else if (stats.isFile()) { | ||
addFile(newContext, stats); | ||
} | ||
}); | ||
|
||
return output[outputPropKey]; | ||
} | ||
|
||
/** | ||
* Load directory or file from real FS | ||
*/ | ||
exports.load = function(p, options) { | ||
return bypass(() => { | ||
p = path.resolve(p); | ||
|
||
const stats = fs.statSync(p); | ||
const context = createContext({output: {}, options, target: p}); | ||
|
||
if (stats.isDirectory()) { | ||
return addDir(context, stats, true); | ||
} else if (stats.isFile()) { | ||
return addFile(context, stats, true); | ||
} | ||
}); | ||
}; |
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
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 @@ | ||
data2 |
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 @@ | ||
data3 |
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 @@ | ||
data1 |
Oops, something went wrong.