Comparing version 2.0.0 to 2.1.0
@@ -0,1 +1,8 @@ | ||
# 2.1.0 | ||
- Add `--collection` option to allow recording to a mock collection | ||
- Add `--save-query` option to save query parameters when recording | ||
- Add `--depth` option for mock conversion | ||
- Change shorthand param for `--ignore` from `-g` to `-i` | ||
- Fix `--ignore` param for `smoke-conv` tool | ||
# 2.0.0 | ||
@@ -2,0 +9,0 @@ - Add support for single file mock collections |
@@ -30,3 +30,3 @@ const path = require('path'); | ||
if (data !== undefined) { | ||
ext = typeof data === 'function' ? 'js' : ext !== null && typeof data === 'object' ? 'json' : ext; | ||
ext = typeof data === 'function' ? 'js' : !ext && typeof data === 'object' ? 'json' : ext; | ||
} | ||
@@ -82,3 +82,2 @@ | ||
return { | ||
originalFile: file, | ||
file: path.join(basePath, file), | ||
@@ -85,0 +84,0 @@ ext, |
@@ -5,2 +5,5 @@ const path = require('path'); | ||
const {getMocksFromCollections} = require('./mock'); | ||
const mockCollectionExt = '.mocks.js'; | ||
const stringTypes = [ | ||
@@ -19,18 +22,2 @@ 'application/json', | ||
async function record(req, res, data, options) { | ||
let file = req.method.toLowerCase() + '_'; | ||
const pathComponents = req.path | ||
.substring(1) | ||
.split('/') | ||
.filter(c => c); | ||
if (options.depth > 0) { | ||
file = path.join(pathComponents.splice(0, options.depth).join(path.sep), file); | ||
} | ||
file += pathComponents.join('#'); | ||
if (options.set) { | ||
file += '__' + options.set; | ||
} | ||
const contentTypeHeader = Object.keys(res.headers).find(h => h.toLowerCase() === 'content-type'); | ||
@@ -54,15 +41,17 @@ const contentType = contentTypeHeader && res.headers[contentTypeHeader].toLowerCase(); | ||
if (ext) { | ||
file += '.' + ext; | ||
} | ||
file = path.join(options.basePath, file); | ||
try { | ||
await fs.mkdirp(path.dirname(file)); | ||
const mock = { | ||
reqPath: req.path, | ||
methods: [req.method.toLowerCase()], | ||
params: options.saveQueryParams ? req.query : null, | ||
set: options.set, | ||
isTemplate: false, | ||
ext, | ||
data | ||
}; | ||
if (isCustomMock) { | ||
await fs.writeJSON(file, data, {spaces: 2}); | ||
if (options.collection) { | ||
await appendToMockCollection(mock, path.join(options.basePath, options.collection)); | ||
} else { | ||
await fs.writeFile(file, data); | ||
await writeMock(mock, options.basePath, options.depth); | ||
} | ||
@@ -74,5 +63,125 @@ } catch (error) { | ||
async function appendToMockCollection(mock, outputFile) { | ||
if (!outputFile.endsWith(mockCollectionExt)) { | ||
outputFile += mockCollectionExt; | ||
} | ||
const mocks = (await fs.pathExists(outputFile)) ? await getMocksFromCollections(process.cwd(), [outputFile]) : []; | ||
return writeMockCollection(mocks.concat(mock), outputFile); | ||
} | ||
async function writeMock(mock, outputFolder, depth) { | ||
let content = mock.data; | ||
if (content === null) { | ||
content = ''; | ||
} else if (typeof content === 'function') { | ||
content = `module.exports = ${content.toString()};`; | ||
} else if (typeof content === 'object' && !(content instanceof Buffer)) { | ||
content = JSON.stringify(content, null, 2); | ||
} | ||
const outputFile = path.join(outputFolder, buildFile(mock, depth)); | ||
await fs.mkdirp(path.dirname(outputFile)); | ||
await fs.writeFile(outputFile, content); | ||
} | ||
async function writeMockCollection(mocks, outputFile) { | ||
if (!outputFile.endsWith(mockCollectionExt)) { | ||
outputFile += mockCollectionExt; | ||
} | ||
mocks.forEach(mock => { | ||
// Fix string content types | ||
if (mock.ext && isStringContent(mime.lookup(mock.ext)) && mock.data instanceof Buffer) { | ||
mock.data = mock.data.toString('utf8'); | ||
} | ||
}); | ||
let collection = indent(mocks.map(mock => `"${buildFile(mock, 0)}": ${stringifyMockData(mock)}`).join(',\n')); | ||
collection = `module.exports = {\n${collection}\n};\n`; | ||
await fs.mkdirp(path.dirname(outputFile)); | ||
await fs.writeFile(outputFile, collection); | ||
} | ||
function stringifyMockData(mock) { | ||
if (!mock.data) { | ||
return 'null'; | ||
} | ||
if (typeof mock.data === 'string') { | ||
return mock.ext !== 'json' || mock.isTemplate ? `"${escapeString(mock.data)}"` : mock.data.trim(); | ||
} | ||
if (mock.data instanceof Buffer) { | ||
return JSON.stringify( | ||
{ | ||
statusCode: 200, | ||
body: mock.data.toString('base64'), | ||
buffer: true | ||
}, | ||
null, | ||
2 | ||
); | ||
} | ||
if (typeof mock.data === 'object') { | ||
return JSON.stringify(mock.data, null, 2).trim(); | ||
} | ||
return mock.data; | ||
} | ||
function escapeString(str) { | ||
return JSON.stringify(str).slice(1, -1); | ||
} | ||
function indent(str) { | ||
const regex = /^(?!\s*$)/gm; | ||
return str.replace(regex, ' '); | ||
} | ||
function buildFile(mock, depth) { | ||
let file = ''; | ||
if (mock.methods && mock.methods.length > 0) { | ||
file += mock.methods.join('+') + '_'; | ||
} | ||
const reqPath = mock.reqPath[0] === '/' ? mock.reqPath.substring(1) : mock.reqPath; | ||
const pathComponents = reqPath.split('/').filter(c => c); | ||
if (depth > 0) { | ||
file = path.join(pathComponents.splice(0, depth).join(path.sep), file); | ||
} | ||
file += pathComponents.join('#'); | ||
if (mock.params) { | ||
Object.entries(mock.params).forEach(([key, value], index) => { | ||
file += `${index === 0 ? '$' : '&'}${encodeURIComponent(key)}=${encodeURIComponent(value)}`; | ||
}); | ||
} | ||
if (mock.set) { | ||
file += '__' + mock.set; | ||
} | ||
if (mock.ext) { | ||
file += '.' + mock.ext; | ||
if (mock.isTemplate) { | ||
file += '_'; | ||
} | ||
} | ||
return file; | ||
} | ||
module.exports = { | ||
isStringContent, | ||
mockCollectionExt, | ||
writeMock, | ||
writeMockCollection, | ||
record | ||
}; |
{ | ||
"name": "smoke", | ||
"version": "2.0.0", | ||
"version": "2.1.0", | ||
"description": "Simple yet powerful file-based mock server with recording abilities", | ||
"main": "smoke.js", | ||
"main": "lib/smoke.js", | ||
"bin": { | ||
@@ -44,4 +44,4 @@ "smoke": "./bin/smoke", | ||
"devDependencies": { | ||
"jest": "^23.6.0", | ||
"supertest": "^3.4.1", | ||
"jest": "^24.1.0", | ||
"supertest": "^3.4.2", | ||
"xo": "^0.24.0" | ||
@@ -68,2 +68,6 @@ }, | ||
}, | ||
"prettier": { | ||
"singleQuote": true, | ||
"bracketSpacing": false | ||
}, | ||
"engines": { | ||
@@ -70,0 +74,0 @@ "node": ">=8.0.0" |
@@ -59,3 +59,3 @@ # :dash: smoke | ||
-n, --not-found <glob> Mocks for 404 errors [default: "404.*"] | ||
-g, --ignore <glob> Files to ignore [default: none] | ||
-i, --ignore <glob> Files to ignore [default: none] | ||
-k, --hooks <file> Middleware hooks [default: none] | ||
@@ -71,2 +71,3 @@ -x, --proxy <host> Fallback proxy if no mock found | ||
-a, --save-headers Save response headers | ||
-q, --save-query Save query parameters | ||
``` | ||
@@ -232,3 +233,3 @@ | ||
In addition, you can define dynamic mocks using JavaScript by using the `.js` extension, that will be loaded as a regular | ||
NodeJS module. | ||
Node.js module. | ||
@@ -273,4 +274,4 @@ In that case, your JS module is expected to export a function that take an input data object with the | ||
Note that by default response headers are not saved and simple mocks are generated. To change this behavior, you can | ||
enable the `--save-headers` option. | ||
Note that by default response headers and request query parameters are not saved. To change this behavior, you can | ||
use the `--save-headers` and `--save-query` options. | ||
@@ -337,3 +338,3 @@ ### Middleware hooks | ||
If you cannot find what you need here, you might want to check out one of these other NodeJS mock servers: | ||
If you cannot find what you need here, you might want to check out one of these other Node.js mock servers: | ||
@@ -340,0 +341,0 @@ - [JSON Server](https://github.com/typicode/json-server) |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
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
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
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
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
40533
14
680
341
4