Comparing version 1.0.4 to 2.0.0
@@ -28,4 +28,5 @@ module.exports = { | ||
"space-before-function-paren": [2, "never"], | ||
"space-after-keywords": 2, | ||
"no-console": 0 | ||
} | ||
}; |
@@ -62,7 +62,7 @@ 'use strict'; | ||
var settings = _extends({}, DEFAULT_OPTIONS, options); | ||
var corsWhitelist = settings.corsWhitelist; | ||
var encoding = settings.encoding; | ||
var latency = settings.latency; | ||
var overrides = settings.overrides; | ||
var ports = settings.ports; | ||
var corsWhitelist = settings.corsWhitelist, | ||
encoding = settings.encoding, | ||
latency = settings.latency, | ||
overrides = settings.overrides, | ||
ports = settings.ports; | ||
@@ -73,5 +73,7 @@ | ||
} | ||
if (isValidDuration(latency)) { | ||
simulateLatency(app, latency); | ||
} | ||
if (overrides) { | ||
@@ -83,10 +85,22 @@ delegateRouteOverrides(app, settings); | ||
var path = getURLPathWithQueryString(req); | ||
var fileName = getFileName(path, settings); | ||
_fs2.default.readFile(fileName, encoding, function (err, data) { | ||
if (err) { | ||
fetchResponse(req, res, _extends({}, settings, { fileName: fileName, path: path })); | ||
} else { | ||
serveResponse(res, _extends({}, settings, { fileName: fileName, data: data })); | ||
} | ||
}); | ||
var jsonFileName = getFileName(path, 'json', settings); | ||
var jsFileName = getFileName(path, 'js', settings); | ||
var htmlFileName = getFileName(path, 'html', settings); | ||
// Handles JSON, JS, and HTML files. | ||
// If the file is not found, fetch a JSON response from production. | ||
if (_fs2.default.existsSync(jsonFileName)) { | ||
_fs2.default.readFile(jsonFileName, encoding, function (err, data) { | ||
serveResponse(res, data, jsonFileName, _extends({}, settings)); | ||
}); | ||
} else if (_fs2.default.existsSync(jsFileName)) { | ||
delete require.cache[require.resolve(jsFileName)]; // clear cache to keep JS require dynamic | ||
var data = require(jsFileName).default(); | ||
serveResponse(res, data, jsFileName, _extends({}, settings)); | ||
} else if (_fs2.default.existsSync(htmlFileName)) { | ||
_fs2.default.readFile(htmlFileName, encoding, function (err, data) { | ||
serveResponse(res, data, htmlFileName, _extends({}, settings)); | ||
}); | ||
} else { | ||
fetchResponse(req, res, jsonFileName, _extends({}, settings, { path: path })); | ||
} | ||
}); | ||
@@ -112,4 +126,4 @@ | ||
var tasks = activeServers.map(function (serverEntry) { | ||
var server = serverEntry.server; | ||
var port = serverEntry.port; | ||
var server = serverEntry.server, | ||
port = serverEntry.port; | ||
@@ -220,5 +234,6 @@ | ||
function delegateRouteOverrides(app, options) { | ||
var overrides = options.overrides; | ||
var encoding = options.encoding; | ||
var quiet = options.quiet; | ||
// Setup default values | ||
var overrides = options.overrides, | ||
encoding = options.encoding, | ||
quiet = options.quiet; | ||
@@ -233,15 +248,18 @@ var methods = ['get', 'post', 'put', 'delete', 'all']; | ||
Object.keys(overrides).forEach(function (method) { | ||
// Check for invalid protocol | ||
if (!methods.includes(method)) { | ||
throw Error('Couldn\'t override route with invalid HTTP method: \'' + method + '\''); | ||
} | ||
// Iterate through get, post, etc | ||
overrides[method].forEach(function (params) { | ||
var fixture = void 0; | ||
var routeParams = _extends({}, defaults, params); | ||
var route = routeParams.route; | ||
var status = routeParams.status; | ||
var response = routeParams.response; | ||
var headers = routeParams.headers; | ||
var mergeParams = routeParams.mergeParams; | ||
var _routeParams$withQuer = routeParams.withQueryParams; | ||
var queryParams = _routeParams$withQuer === undefined ? {} : _routeParams$withQuer; | ||
var route = routeParams.route, | ||
status = routeParams.status, | ||
response = routeParams.response, | ||
headers = routeParams.headers, | ||
mergeParams = routeParams.mergeParams, | ||
_routeParams$withQuer = routeParams.withQueryParams, | ||
queryParams = _routeParams$withQuer === undefined ? {} : _routeParams$withQuer; | ||
@@ -255,3 +273,3 @@ var responseIsJson = JSON_CONTENT_TYPE_REGEXP.test(headers['Content-Type']); | ||
if (!response) { | ||
var fileName = getFileName(route, options); | ||
var fileName = getFileName(route, options, 'json'); | ||
_fs2.default.readFile(fileName, encoding, function (err, data) { | ||
@@ -272,3 +290,3 @@ if (err) { | ||
if (!quiet) { | ||
console.info('==> 📁 Serving local fixture for ' + method.toUpperCase() + ' -> \'' + route + '\''); | ||
console.info('==> \uD83D\uDCC1 Serving local fixture for ' + method.toUpperCase() + ' -> \'' + route + '\''); | ||
} | ||
@@ -281,7 +299,6 @@ var _iteratorNormalCompletion2 = true; | ||
for (var _iterator2 = (0, _entries2.default)(queryParams)[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { | ||
var _step2$value = _slicedToArray(_step2.value, 2); | ||
var _step2$value = _slicedToArray(_step2.value, 2), | ||
param = _step2$value[0], | ||
value = _step2$value[1]; | ||
var param = _step2$value[0]; | ||
var value = _step2$value[1]; | ||
if (req.query[param] !== value) { | ||
@@ -313,5 +330,5 @@ return next(); | ||
function fetchResponse(req, res, options) { | ||
function fetchResponse(req, res, fileName, options) { | ||
if (req.method !== 'GET') { | ||
console.error('==> ⛔️ Couldn\'t complete fetch with non-GET method'); | ||
console.error('==> \u26D4\uFE0F Couldn\'t complete fetch with non-GET method'); | ||
return res.status(500).end(); | ||
@@ -321,6 +338,5 @@ } | ||
var responseIsJson = void 0; | ||
var prodRootURL = options.prodRootURL; | ||
var saveFixtures = options.saveFixtures; | ||
var path = options.path; | ||
var fileName = options.fileName; | ||
var prodRootURL = options.prodRootURL, | ||
saveFixtures = options.saveFixtures, | ||
path = options.path; | ||
@@ -330,6 +346,6 @@ var prodURL = prodRootURL + path; | ||
console.info('==> 📡 GET ' + prodRootURL + ' -> ' + path); | ||
console.info('==> \uD83D\uDCE1 GET ' + prodRootURL + ' -> ' + path); | ||
(0, _nodeFetch2.default)(prodRootURL + path).then(function (response) { | ||
if (response.ok) { | ||
console.info('==> 📡 STATUS ' + response.status); | ||
console.info('==> \uD83D\uDCE1 STATUS ' + response.status); | ||
} else { | ||
@@ -352,5 +368,5 @@ throw Error('Couldn\'t complete fetch with status ' + response.status); | ||
} | ||
serveResponse(res, _extends({}, options, { data: data, newResponse: true })); | ||
serveResponse(res, data, fileName, _extends({}, options, { newResponse: true })); | ||
}).catch(function (err) { | ||
console.error('==> ⛔️ ' + err); | ||
console.error('==> \u26D4\uFE0F ' + err); | ||
res.status(500).end(); | ||
@@ -360,20 +376,38 @@ }); | ||
function serveResponse(res, options) { | ||
var data = options.data; | ||
var fileName = options.fileName; | ||
var quiet = options.quiet; | ||
var newResponse = options.newResponse; | ||
function serveResponse(res, data, fileName, options) { | ||
var quiet = options.quiet, | ||
newResponse = options.newResponse; | ||
if (!quiet && !newResponse) { | ||
console.info('==> 📁 Serving local response from ' + fileName); | ||
console.info('==> \uD83D\uDCC1 Serving local response from ' + fileName); | ||
} | ||
if (fileName.match(/callback\=/)) { | ||
res.set({ 'Content-Type': 'application/javascript' }).send(data); | ||
} else { | ||
return res.set({ 'Content-Type': 'application/javascript' }).send(data); | ||
} | ||
if (fileName.match(/.json/)) { | ||
try { | ||
res.json(JSON.parse(data)); | ||
if (newResponse) { | ||
// data is from fetch's response.json() and does not need parsing | ||
return res.json(data); | ||
} | ||
// data is from fs.readFile() and needs parsing | ||
return res.json(JSON.parse(data)); | ||
} catch (e) { | ||
res.set({ 'Content-Type': 'text/html' }).send(data); | ||
console.error('\u26D4\uFE0F Could not parse and serve invalid JSON: ' + e); | ||
return res.json({}); | ||
} | ||
} | ||
if (fileName.match(/.js/)) { | ||
return res.json(data); | ||
} | ||
if (fileName.match(/.html/)) { | ||
return res.set({ 'Content-Type': 'text/html' }).send(data); | ||
} | ||
console.error('⛔️ Filename extension was not recognized. Please check that your fixture ends in .js, .json, or .html!'); | ||
} | ||
@@ -384,13 +418,14 @@ | ||
_fs2.default.writeFile(fileName, data, function (err) { | ||
if (err) { | ||
throw Error('Couldn\'t write response locally, received fs error: \'' + err + '\''); | ||
} | ||
console.info('==> 💾 Saved response to ' + fileName); | ||
}); | ||
try { | ||
_fs2.default.writeFile(fileName, data, function () { | ||
console.info('==> \uD83D\uDCBE Saved response to ' + fileName); | ||
}); | ||
} catch (e) { | ||
throw Error('Couldn\'t write response locally, received fs error: \'' + e + '\''); | ||
} | ||
} | ||
function getFileName(path, options) { | ||
var queryStringIgnore = options.queryStringIgnore; | ||
var fixturesPath = options.fixturesPath; | ||
function getFileName(path, ext, options) { | ||
var queryStringIgnore = options.queryStringIgnore, | ||
fixturesPath = options.fixturesPath; | ||
@@ -400,3 +435,4 @@ var fileNameInDirectory = queryStringIgnore.reduce(function (fileName, regex) { | ||
}, path).replace(/\//, '').replace(/\//g, ':'); | ||
return fixturesPath + '/' + fileNameInDirectory + '.json'; | ||
return fixturesPath + '/' + fileNameInDirectory + '.' + ext; | ||
} | ||
@@ -403,0 +439,0 @@ |
{ | ||
"name": "highwind", | ||
"version": "1.0.4", | ||
"version": "2.0.0", | ||
"description": "Mock API express server", | ||
@@ -5,0 +5,0 @@ "main": "lib/mock_api.js", |
@@ -54,4 +54,61 @@ Highwind | ||
By default, Highwind listens for requests on port **4567**. You can change that | ||
in the config, however. | ||
in the [config, however](#configuration-options). | ||
## Fixtures | ||
You may supply your fixtures as JSON, JS, or HTML files: | ||
#### JSON Fixture | ||
*Filepath:* `./fixtures/myApiResponse.json` | ||
```json | ||
{ | ||
"result": { | ||
"foo": "bar" | ||
} | ||
} | ||
``` | ||
In this example, requesting `localhost:4567/myApiResponse` would return the above JSON. | ||
#### HTML Fixture | ||
*Filepath:* `./fixtures/myApiResponse.html` | ||
```json | ||
<html> | ||
<head> | ||
<title>Test</title> | ||
</head> | ||
<body> | ||
</body> | ||
</html> | ||
``` | ||
In this example, requesting `localhost:4567/myApiResponse` would return the above HTML. | ||
#### JS Fixture | ||
*Filepath:* `./fixtures/myApiResponse.js` | ||
```js | ||
export default () => { | ||
return { | ||
result: { | ||
foo: "bar" | ||
} | ||
}; | ||
} | ||
``` | ||
In this example, requesting `localhost:4567/myApiResponse` would return the following JSON. | ||
```json | ||
{ | ||
"result": { | ||
"foo": "bar" | ||
} | ||
} | ||
``` | ||
## Configuration Options | ||
@@ -87,3 +144,7 @@ These are dropped in to the options object passed to `highwind.start()` during instantiation. | ||
## HTTP Route Overrides | ||
In some instances you may want to override the behavior for a specific route. | ||
Here are some examples of HTTP route overrides and their use cases. | ||
@@ -162,7 +223,31 @@ | ||
## Serving a JSONP response | ||
## JS as JSON Responses | ||
Highwind serves all routes with a `callback` specified in the query string as JSONP by default. This is easy to disable, though, either by specifying a non-JS `'Content-Type'` header in an override for a specific route or by adding something like `/callback\=([^\&]+)/` to your `queryStringIgnore` collection. | ||
Highwind recognizes when a fixture file ends in `.js` instead of `.json`. When this is the case, Highwind evaluates the `export default` function of that file and attempts to return its output as JSON. | ||
This can be useful for creating many fixtures based off of a few base fixtures without the hassle of copying and maintaining 100s of lines of identical JSON in each fixture file. | ||
``` | ||
export default () => { | ||
// Import a complex JSON fixture as a JS object | ||
const baseJSON = JSON.parse(fs.readFileSync('./fixtures/persisted_json_route.json')); | ||
// Modify a few values | ||
baseJSON.title = "I am overriding the title value"; | ||
// Return the modified JS object back to Highwind for parsing back into JSON | ||
return baseJSON; | ||
} | ||
``` | ||
## JSONP and the Callback Query | ||
Highwind serves all routes with `callback` specified in the query string as | ||
JSONP, not JSON, by default. | ||
You can disable this by specifying a non-JS `'Content-Type'` header in an | ||
override for a specific route or by adding something like | ||
`/callback\=([^\&]+)/` to your `queryStringIgnore` collection. | ||
## License | ||
[MIT License](http://mit-license.org/) © Refinery29, Inc. 2016-2017 | ||
[MIT License](http://mit-license.org/) © Refinery29, Inc. 2016-2018 |
@@ -68,3 +68,4 @@ import 'babel-polyfill'; | ||
const route = '/non_persisted_json_route'; | ||
const response = { source: 'Remote API' }; | ||
const response = JSON.stringify({'source': 'Remote API'}); | ||
const responsePath = RESPONSES_DIR + route + '.json'; | ||
@@ -79,3 +80,5 @@ const responsePathWithCallback = RESPONSES_DIR + route + JSONP_CALLBACK + '.json'; | ||
.query(true) | ||
.reply(200, response); | ||
.reply(200, response, { | ||
'Content-Type': 'application/json' | ||
}); | ||
@@ -106,3 +109,3 @@ start(DEFAULT_OPTIONS, (err, result) => { | ||
.end((err, res) => { | ||
expect(res.text).to.equal(JSON.stringify(response)); | ||
expect(res.text).to.equal(response); | ||
fs.access(responsePath, fs.F_OK, done); | ||
@@ -118,3 +121,3 @@ }); | ||
.end((err, res) => { | ||
expect(res.text).to.equal(JSON.stringify(response)); | ||
expect(res.text).to.equal(response); | ||
fs.access(responsePath, fs.F_OK, done); | ||
@@ -130,3 +133,3 @@ }); | ||
.end((err, res) => { | ||
expect(res.text).to.equal(JSON.stringify(response)); | ||
expect(res.text).to.equal(response); | ||
fs.access(responsePathWithCallback, fs.F_OK, done); | ||
@@ -142,3 +145,5 @@ }); | ||
.query(true) | ||
.reply(200, response); | ||
.reply(200, response, { | ||
'Content-Type': 'application/json' | ||
}); | ||
@@ -158,6 +163,6 @@ start({ ...DEFAULT_OPTIONS, saveFixtures: false }, (err, result) => { | ||
.get(route) | ||
.expect('Content-Type', /application\/json/) | ||
.expect('Content-Type', 'application/json') | ||
.expect(200) | ||
.end((err, res) => { | ||
expect(res.text).to.equal(JSON.stringify(response)); | ||
expect(res.text).to.equal(response); | ||
fs.access(responsePath, fs.F_OK, (err) => { | ||
@@ -196,3 +201,3 @@ if (err) { | ||
describe('And there is a JSON response matching a given route', function() { | ||
describe('And there is a JSON file matching a given route', function() { | ||
let mockAPI; | ||
@@ -243,6 +248,77 @@ const route = '/persisted_json_route'; | ||
describe('And there is a non-JSON response matching a given route', function() { | ||
describe('And there is a invalid JSON file matching a given route', function() { | ||
let mockAPI; | ||
const route = '/persisted_invalid_json_route'; | ||
beforeEach(function(done) { | ||
nock(PROD_ROOT_URL) | ||
.get(route) | ||
.query(true) | ||
.replyWithError('Fake API hit the production API'); | ||
start(DEFAULT_OPTIONS, (err, result) => { | ||
mockAPI = result; | ||
done(); | ||
}); | ||
spyOn(console, 'error'); | ||
}); | ||
afterEach(function() { | ||
close(mockAPI.servers); | ||
console.error.restore(); | ||
}); | ||
it('serves a blank resonse and logs an error', function(done) { | ||
request(mockAPI.app) | ||
.get(route) | ||
.expect('Content-Type', /application\/json/) | ||
.expect(200, {}, () => { | ||
expect(console.error.calledWith("⛔️ Could not parse and serve invalid JSON: SyntaxError: Unexpected token r in JSON at position 32")).to.be.true; | ||
done() | ||
}); | ||
}); | ||
}); | ||
describe('And there is a JS file matching a given route', function() { | ||
let mockAPI; | ||
const route = '/persisted_js_route'; | ||
const responsePath = RESPONSES_DIR + route + '.js'; | ||
const jsResponse = require(responsePath).default(); | ||
beforeEach(function(done) { | ||
nock(PROD_ROOT_URL) | ||
.get(route) | ||
.query(true) | ||
.replyWithError('Fake API hit the production API'); | ||
start(DEFAULT_OPTIONS, (err, result) => { | ||
mockAPI = result; | ||
done(); | ||
}); | ||
}); | ||
afterEach(function() { | ||
close(mockAPI.servers); | ||
}); | ||
it('evaluates the JS then serves the output as JSON and does not hit the production API', function(done) { | ||
request(mockAPI.app) | ||
.get(route) | ||
.expect('Content-Type', /application\/json/) | ||
.expect(200, jsResponse, done); | ||
}); | ||
it('ignores truncated query string expressions when identifying the persisted response filename and does not hit the production API', function(done) { | ||
request(mockAPI.app) | ||
.get(route + IGNORED_QUERY_PARAMS) | ||
.expect('Content-Type', /application\/json/) | ||
.expect(200, jsResponse, done); | ||
}); | ||
}); | ||
describe('And there is a non-JSON/non-JS file matching a given route', function() { | ||
let mockAPI; | ||
const route = '/persisted_html_route'; | ||
const responsePath = RESPONSES_DIR + route + '.json'; | ||
const responsePath = RESPONSES_DIR + route + '.html'; | ||
const response = fs.readFileSync(responsePath, 'utf8'); | ||
@@ -249,0 +325,0 @@ |
@@ -39,5 +39,7 @@ import 'babel-polyfill'; | ||
} | ||
if (isValidDuration(latency)) { | ||
simulateLatency(app, latency); | ||
} | ||
if (overrides) { | ||
@@ -49,10 +51,22 @@ delegateRouteOverrides(app, settings); | ||
const path = getURLPathWithQueryString(req); | ||
const fileName = getFileName(path, settings); | ||
fs.readFile(fileName, encoding, (err, data) => { | ||
if (err) { | ||
fetchResponse(req, res, { ...settings, fileName, path }); | ||
} else { | ||
serveResponse(res, { ...settings, fileName, data }); | ||
} | ||
}); | ||
const jsonFileName = getFileName(path, 'json', settings); | ||
const jsFileName = getFileName(path, 'js', settings); | ||
const htmlFileName = getFileName(path, 'html', settings); | ||
// Handles JSON, JS, and HTML files. | ||
// If the file is not found, fetch a JSON response from production. | ||
if (fs.existsSync(jsonFileName)) { | ||
fs.readFile(jsonFileName, encoding, (err, data) => { | ||
serveResponse(res, data, jsonFileName, { ...settings }); | ||
}); | ||
} else if (fs.existsSync(jsFileName)) { | ||
delete require.cache[require.resolve(jsFileName)] // clear cache to keep JS require dynamic | ||
const data = require(jsFileName).default(); | ||
serveResponse(res, data, jsFileName, { ...settings }); | ||
} else if (fs.existsSync(htmlFileName)) { | ||
fs.readFile(htmlFileName, encoding, (err, data) => { | ||
serveResponse(res, data, htmlFileName, { ...settings }); | ||
}); | ||
} else { | ||
fetchResponse(req, res, jsonFileName, { ...settings, path }); | ||
} | ||
}); | ||
@@ -152,3 +166,5 @@ | ||
function delegateRouteOverrides(app, options) { | ||
// Setup default values | ||
const { overrides, encoding, quiet } = options; | ||
@@ -166,5 +182,8 @@ const methods = ['get', 'post', 'put', 'delete', 'all']; | ||
Object.keys(overrides).forEach(method => { | ||
// Check for invalid protocol | ||
if (!methods.includes(method)) { | ||
throw Error(`Couldn't override route with invalid HTTP method: '${method}'`); | ||
} | ||
// Iterate through get, post, etc | ||
overrides[method].forEach(params => { | ||
@@ -188,3 +207,3 @@ let fixture; | ||
if (!response) { | ||
const fileName = getFileName(route, options); | ||
const fileName = getFileName(route, options, 'json'); | ||
fs.readFile(fileName, encoding, (err, data) => { | ||
@@ -224,3 +243,3 @@ if (err) { | ||
function fetchResponse(req, res, options) { | ||
function fetchResponse(req, res, fileName, options) { | ||
if (req.method !== 'GET') { | ||
@@ -232,3 +251,3 @@ console.error(`==> ⛔️ Couldn't complete fetch with non-GET method`); | ||
let responseIsJson; | ||
const { prodRootURL, saveFixtures, path, fileName } = options; | ||
const { prodRootURL, saveFixtures, path } = options; | ||
const prodURL = prodRootURL + path; | ||
@@ -260,3 +279,3 @@ const responseIsJsonp = prodURL.match(/callback\=([^\&]+)/); | ||
} | ||
serveResponse(res, { ...options, data, newResponse: true }); | ||
serveResponse(res, data, fileName, { ...options, newResponse: true }); | ||
}) | ||
@@ -269,20 +288,40 @@ .catch(err => { | ||
function serveResponse(res, options) { | ||
const { data, fileName, quiet, newResponse } = options; | ||
function serveResponse(res, data, fileName, options) { | ||
const { quiet, newResponse } = options; | ||
if (!quiet && !newResponse) { | ||
console.info(`==> 📁 Serving local response from ${fileName}`); | ||
} | ||
if (fileName.match(/callback\=/)) { | ||
res | ||
return res | ||
.set({ 'Content-Type': 'application/javascript' }) | ||
.send(data); | ||
} else { | ||
} | ||
if (fileName.match(/.json/)) { | ||
try { | ||
res.json(JSON.parse(data)); | ||
if (newResponse) { | ||
// data is from fetch's response.json() and does not need parsing | ||
return res.json(data); | ||
} | ||
// data is from fs.readFile() and needs parsing | ||
return res.json(JSON.parse(data)); | ||
} catch (e) { | ||
res | ||
.set({ 'Content-Type': 'text/html' }) | ||
.send(data); | ||
console.error(`⛔️ Could not parse and serve invalid JSON: ${e}`); | ||
return res.json({}); | ||
} | ||
} | ||
if (fileName.match(/.js/)) { | ||
return res.json(data); | ||
} | ||
if (fileName.match(/.html/)) { | ||
return res | ||
.set({ 'Content-Type': 'text/html' }) | ||
.send(data); | ||
} | ||
console.error('⛔️ Filename extension was not recognized. Please check that your fixture ends in .js, .json, or .html!'); | ||
} | ||
@@ -295,11 +334,12 @@ | ||
fs.writeFile(fileName, data, (err) => { | ||
if (err) { | ||
throw Error(`Couldn't write response locally, received fs error: '${err}'`) | ||
} | ||
console.info(`==> 💾 Saved response to ${fileName}`); | ||
}); | ||
try { | ||
fs.writeFile(fileName, data, () => { | ||
console.info(`==> 💾 Saved response to ${fileName}`); | ||
}); | ||
} catch (e) { | ||
throw Error(`Couldn't write response locally, received fs error: '${e}'`) | ||
} | ||
} | ||
function getFileName(path, options) { | ||
function getFileName(path, ext, options) { | ||
const { queryStringIgnore, fixturesPath } = options; | ||
@@ -310,3 +350,4 @@ const fileNameInDirectory = queryStringIgnore | ||
.replace(/\//g, ':'); | ||
return `${fixturesPath}/${fileNameInDirectory}.json`; | ||
return `${fixturesPath}/${fileNameInDirectory}.${ext}`; | ||
} | ||
@@ -313,0 +354,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
55387
1243
251
8
2