Comparing version 0.18.4 to 0.19.0
@@ -8,2 +8,58 @@ const path = require('path'); | ||
const handleContentType = (body, headers) => { | ||
const contentType = headers['content-type']; | ||
// TODO: More spec-conformant detection of JSON content type. | ||
if (contentType && contentType.includes('/json')) { | ||
/* eslint-disable no-empty */ | ||
try { | ||
const json = JSON.parse(body); | ||
return { | ||
json | ||
}; | ||
} catch (err) { | ||
// silence any errors, invalid JSON is ok | ||
} | ||
/* eslint-enable no-empty */ | ||
} | ||
return { | ||
raw: body | ||
}; | ||
}; | ||
const replaceFixtureWithRequireInJson = (json, { relativePath }) => | ||
json.replace( | ||
/"fixture"(\s*):(\s*)"([^"]+)\.json"/g, | ||
`"json"$1:$2require("${relativePath}/$3.json")` | ||
); | ||
const getDataForRecordToFixtures = ({ responseOptions, name, index }) => { | ||
const newResponseOptions = Object.assign({}, responseOptions); | ||
const { raw, json } = responseOptions; | ||
const fixtureName = `${name}/${index}`; | ||
let body; | ||
if (raw) { | ||
newResponseOptions.fixture = `${fixtureName}.txt`; | ||
body = raw; | ||
delete newResponseOptions.raw; | ||
} else if (json) { | ||
newResponseOptions.fixture = `${fixtureName}.json`; | ||
body = JSON.stringify(json, null, 2); | ||
delete newResponseOptions.json; | ||
} | ||
return { | ||
newResponseOptions, | ||
body | ||
}; | ||
}; | ||
exports.getDataForRecordToFixtures = getDataForRecordToFixtures; | ||
exports.replaceFixtureWithRequireInJson = replaceFixtureWithRequireInJson; | ||
exports.handleContentType = handleContentType; | ||
exports.resolveFilePath = resolveFilePath; |
@@ -14,3 +14,3 @@ 'use strict'; | ||
register: function register(method, _path, response) { | ||
app.log(['serve', 'mount', method], _path.path || _path); | ||
app.log(['serve', 'mount', method], _path.path || _path.url || _path); | ||
return routeResolver.register(method, _path, response); | ||
@@ -17,0 +17,0 @@ }, |
const request = require('request'); | ||
const isAbsoluteUrl = require('is-absolute-url'); | ||
const { isEmpty } = require('lodash'); | ||
const { handleContentType } = require('./lib/helpers'); | ||
@@ -71,3 +72,3 @@ const now = () => new Date().getTime(); | ||
const { only } = recordMeta; | ||
const { options: { headers: optionsHeaders, only, useHeaders, useLatency } = {} } = recordMeta; | ||
@@ -94,4 +95,4 @@ if (only && !only(reqUrl)) return; | ||
if (recordMeta.headers && Object.keys(recordMeta.headers).length > 0) { | ||
match.headers = recordMeta.headers; | ||
if (optionsHeaders && Object.keys(optionsHeaders).length > 0) { | ||
match.headers = Object.assign({}, optionsHeaders); | ||
} | ||
@@ -109,11 +110,18 @@ | ||
recordMeta.set.push([ | ||
match, | ||
const responseOptions = Object.assign( | ||
{ | ||
headers, | ||
status, | ||
raw: _body, // TODO: Support JSON response deserialized | ||
latency | ||
} | ||
]); | ||
status | ||
}, | ||
handleContentType(_body, headers) | ||
); | ||
if (useHeaders) { | ||
responseOptions.headers = headers; | ||
} | ||
if (useLatency) { | ||
responseOptions.latency = latency; | ||
} | ||
recordMeta.set.push([match, responseOptions]); | ||
}).pipe(res); | ||
@@ -120,0 +128,0 @@ }; |
@@ -5,7 +5,7 @@ module.exports = app => (name, options = {}) => { | ||
app.locals.recording = true; | ||
if (!name) throw new Error('Must provide a recording name.'); | ||
app.log(['serve', 'record'], name); | ||
if (options.only) { | ||
if (options.only && typeof options.only === 'string') { | ||
// if only is truthy, assume it is a regex pattern | ||
@@ -17,7 +17,9 @@ const regex = new RegExp(options.only); | ||
const enhancedOptions = Object.assign({}, options, { | ||
only | ||
}); | ||
app.locals.recordMeta = { | ||
headers: options.headers, | ||
name, | ||
options, | ||
only, | ||
options: enhancedOptions, | ||
set: [] | ||
@@ -24,0 +26,0 @@ }; |
const fs = require('fs'); | ||
const path = require('path'); | ||
const mkdirp = require('mkdirp'); | ||
const { resolveFilePath } = require('./lib/helpers'); | ||
const { | ||
resolveFilePath, | ||
getDataForRecordToFixtures, | ||
replaceFixtureWithRequireInJson | ||
} = require('./lib/helpers'); | ||
@@ -13,2 +17,4 @@ module.exports = app => cb => { | ||
const { recordToFixtures, recordToFixturesMode } = app.config; | ||
const { | ||
@@ -20,3 +26,4 @@ recordMeta: { name, set } | ||
const { capturesDir } = app.config; | ||
const { capturesDir, fixturesDir } = app.config; | ||
const capturePath = path.join(capturesDir, name); | ||
@@ -28,10 +35,45 @@ | ||
set.forEach(capture => { | ||
app.log(['serve', 'capture'], capture[0].url); | ||
const newSet = set.map((capture, index) => { | ||
const [match, responseOptions] = capture; | ||
app.log(['serve', 'capture'], match.url || match.path || match); | ||
if (recordToFixtures) { | ||
const { newResponseOptions, body } = getDataForRecordToFixtures({ | ||
responseOptions, | ||
name, | ||
index | ||
}); | ||
const { fixture } = newResponseOptions; | ||
if (fixture) { | ||
const fixturesPath = path.join(fixturesDir, fixture); | ||
// TODO: Any easy way to coordinate this asynchronously? | ||
// eslint-disable-next-line no-sync | ||
mkdirp.sync(path.join(fixturesDir, name)); | ||
// TODO: Any easy way to coordinate this asynchronously? | ||
// eslint-disable-next-line no-sync | ||
fs.writeFileSync(fixturesPath, body); | ||
} | ||
return [match, newResponseOptions]; | ||
} | ||
return [match, responseOptions]; | ||
}); | ||
const json = JSON.stringify(set, null, 2); | ||
const js = `module.exports = ${json};`; | ||
let js = JSON.stringify(newSet, null, 2); | ||
fs.writeFile(filePath, js, err => { | ||
if (recordToFixturesMode === 'require') { | ||
js = replaceFixtureWithRequireInJson(js, { | ||
relativePath: path.relative(capturePath, fixturesDir) | ||
}); | ||
} | ||
const jsModule = `module.exports = ${js};`; | ||
fs.writeFile(filePath, jsModule, err => { | ||
if (err) { | ||
@@ -46,3 +88,3 @@ app.log(['record', 'response', 'error'], err); | ||
set.forEach(capture => { | ||
app.log(['record', 'response', 'saved'], capture[0].url); | ||
app.log(['record', 'response', 'saved'], capture[0].path || capture[0].url || capture[0]); | ||
}); | ||
@@ -49,0 +91,0 @@ |
@@ -20,3 +20,5 @@ 'use strict'; | ||
adminHost: 'localhost', | ||
adminPort: 4777 | ||
adminPort: 4777, | ||
recordToFixtures: true, | ||
recordToFixturesMode: 'path' | ||
}; | ||
@@ -23,0 +25,0 @@ |
{ | ||
"name": "mockyeah", | ||
"version": "0.18.4", | ||
"version": "0.19.0", | ||
"description": "A powerful service mocking, recording, and playback utility.", | ||
@@ -86,3 +86,3 @@ "main": "index.js", | ||
"private": false, | ||
"gitHead": "91aebe566a09d77e9a4299957bbe972b43f5f680" | ||
"gitHead": "22fbf7bcd4f2e41d282cd9c324a150caf33ab7c2" | ||
} |
@@ -75,3 +75,3 @@ 'use strict'; | ||
const captureName = 'some-fancy-capture'; | ||
const captureName = 'test-some-fancy-admin-server-capture'; | ||
@@ -164,3 +164,3 @@ // Construct remote service urls | ||
const captureName = 'some-fancy-capture-3'; | ||
const captureName = 'test-some-fancy-admin-server-capture-3'; | ||
@@ -254,3 +254,3 @@ // Construct remote service urls | ||
const captureName = 'some-fancy-capture-all'; | ||
const captureName = 'test-some-fancy-admin-server-capture-all'; | ||
@@ -257,0 +257,0 @@ // Construct remote service urls |
@@ -73,3 +73,3 @@ 'use strict'; | ||
const captureName = 'some-fancy-capture'; | ||
const captureName = 'test-some-fancy-capture'; | ||
@@ -86,3 +86,3 @@ // Construct remote service urls | ||
remote.get('/some/service/one', { text: 'first' }); | ||
remote.get('/some/service/two', { text: 'second' }); | ||
remote.get('/some/service/two', { json: { second: true } }); | ||
remote.get('/some/service/three', { text: 'third', headers: {} }); | ||
@@ -109,3 +109,3 @@ remote.get('/some/service/four'); | ||
cb => proxyReq.get(path1).expect(200, 'first', cb), | ||
cb => proxyReq.get(path2).expect(200, 'second', cb), | ||
cb => proxyReq.get(path2).expect(200, '{"second":true}', cb), | ||
cb => proxyReq.get(path3).expect(200, 'third', cb), | ||
@@ -159,3 +159,3 @@ cb => proxyReq.get(path4).expect(200, cb), | ||
cb => remoteReq.get(path1).expect(200, 'first', cb), | ||
cb => remoteReq.get(path2).expect(200, 'second', cb), | ||
cb => remoteReq.get(path2).expect(200, '{"second":true}', cb), | ||
cb => remoteReq.get(path3).expect(200, 'third', cb), | ||
@@ -172,3 +172,3 @@ cb => remoteReq.get(path4).expect(200, cb), | ||
cb => proxyReq.get(path1).expect(200, 'first', cb), | ||
cb => proxyReq.get(path2).expect(200, 'second', cb), | ||
cb => proxyReq.get(path2).expect(200, '{\n "second": true\n}', cb), | ||
cb => proxyReq.get(path3).expect(200, 'third', cb), | ||
@@ -189,3 +189,3 @@ cb => proxyReq.get(path4).expect(200, cb), | ||
const captureName = 'some-fancy-capture-2'; | ||
const captureName = 'test-some-fancy-capture-2'; | ||
@@ -201,3 +201,3 @@ // Construct remote service urls | ||
remote.get('/some/service/one', { text: 'first' }); | ||
remote.get('/some/service/two', { text: 'second' }); | ||
remote.get('/some/service/two', { json: { second: true } }); | ||
remote.get('/some/service/three', { text: 'third' }); | ||
@@ -218,3 +218,3 @@ remote.get('/some/service/three/:id', { text: 'fourth' }); | ||
cb => proxyReq.get(path1).expect(200, 'first', cb), | ||
cb => proxyReq.get(path2).expect(200, 'second', cb), | ||
cb => proxyReq.get(path2).expect(200, '{"second":true}', cb), | ||
cb => proxyReq.get(path3).expect(200, 'third', cb), | ||
@@ -263,3 +263,3 @@ cb => proxyReq.get(path4).expect(200, 'fourth', cb), | ||
cb => remoteReq.get(path1).expect(200, 'first', cb), | ||
cb => remoteReq.get(path2).expect(200, 'second', cb), | ||
cb => remoteReq.get(path2).expect(200, '{"second":true}', cb), | ||
cb => remoteReq.get(path3).expect(200, 'third', cb), | ||
@@ -282,3 +282,3 @@ cb => remoteReq.get(path4).expect(200, 'fourth', cb), | ||
const captureName = 'some-fancy-capture-3'; | ||
const captureName = 'test-some-fancy-capture-3'; | ||
@@ -368,3 +368,3 @@ // Construct remote service urls | ||
const captureName = 'some-fancy-capture-3'; | ||
const captureName = 'test-some-fancy-capture-3'; | ||
@@ -442,6 +442,131 @@ // Construct remote service urls | ||
it('should record and playback call headers with `useHeaders` option', function(done) { | ||
this.timeout = 10000; | ||
const captureName = 'test-some-fancy-capture-using-headers'; | ||
// Construct remote service urls | ||
// e.g. http://localhost:4041/http://example.com/some/service | ||
const path1 = '/some/service/one'; | ||
// Mount remote service end points | ||
remote.get('/some/service/one', { | ||
headers: { | ||
'x-my-header': 'My-Value' | ||
} | ||
}); | ||
// Initiate recording and playback series | ||
async.series( | ||
[ | ||
// Initiate recording | ||
cb => { | ||
proxy.record(captureName, { | ||
useHeaders: true | ||
}); | ||
cb(); | ||
}, | ||
// Invoke requests to remote services through proxy | ||
// e.g. http://localhost:4041/http://example.com/some/service | ||
cb => proxyReq.get(path1).expect('x-my-header', 'My-Value', cb), | ||
// Stop recording | ||
cb => { | ||
proxy.recordStop(cb); | ||
}, | ||
// Assert capture file exists | ||
cb => { | ||
fs.statSync(getCaptureFilePath(captureName)); | ||
cb(); | ||
}, | ||
// Reset proxy services and play captured capture | ||
cb => { | ||
proxy.reset(); | ||
cb(); | ||
}, | ||
cb => { | ||
proxy.play(captureName); | ||
cb(); | ||
}, | ||
// Test remote url paths and their sub paths route to the same services | ||
// Assert remote url paths are routed the correct responses | ||
// e.g. http://localhost:4041/http://example.com/some/service | ||
cb => remoteReq.get(path1).expect('x-my-header', 'My-Value', cb), | ||
// Assert paths are routed the correct responses | ||
cb => proxyReq.get(path1).expect('x-my-header', 'My-Value', cb) | ||
], | ||
done | ||
); | ||
}); | ||
it('should record and playback call latency with `useLatency` option', function(done) { | ||
this.timeout = 10000; | ||
const captureName = 'test-some-fancy-capture-using-latency'; | ||
// Construct remote service urls | ||
// e.g. http://localhost:4041/http://example.com/some/service | ||
const path1 = '/some/service/one'; | ||
// Mount remote service end points | ||
remote.get('/some/service/one', { | ||
latency: 200 | ||
}); | ||
// Initiate recording and playback series | ||
async.series( | ||
[ | ||
// Initiate recording | ||
cb => { | ||
proxy.record(captureName, { | ||
useLatency: true | ||
}); | ||
cb(); | ||
}, | ||
// Invoke requests to remote services through proxy | ||
// e.g. http://localhost:4041/http://example.com/some/service | ||
cb => proxyReq.get(path1).expect(200, cb), | ||
// Stop recording | ||
cb => { | ||
proxy.recordStop(cb); | ||
}, | ||
// Assert capture file exists | ||
cb => { | ||
fs.statSync(getCaptureFilePath(captureName)); | ||
cb(); | ||
}, | ||
// Reset proxy services and play captured capture | ||
cb => { | ||
proxy.reset(); | ||
cb(); | ||
}, | ||
cb => { | ||
proxy.play(captureName); | ||
cb(); | ||
}, | ||
// Test remote url paths and their sub paths route to the same services | ||
// Assert remote url paths are routed the correct responses | ||
// e.g. http://localhost:4041/http://example.com/some/service | ||
cb => remoteReq.get(path1).expect(200, cb) | ||
], | ||
done | ||
); | ||
}); | ||
it('should record and playback call with playAll', function(done) { | ||
this.timeout = 10000; | ||
const captureName = 'some-fancy-capture-all'; | ||
const captureName = 'test-some-fancy-capture-all'; | ||
@@ -458,3 +583,3 @@ // Construct remote service urls | ||
remote.get('/some/service/one', { text: 'first' }); | ||
remote.get('/some/service/two', { text: 'second' }); | ||
remote.get('/some/service/two', { json: { second: true } }); | ||
remote.get('/some/service/three', { text: 'third' }); | ||
@@ -476,3 +601,3 @@ remote.get('/some/service/four', { text: 'fourth' }); | ||
cb => proxyReq.get(path1).expect(200, 'first', cb), | ||
cb => proxyReq.get(path2).expect(200, 'second', cb), | ||
cb => proxyReq.get(path2).expect(200, '{"second":true}', cb), | ||
cb => proxyReq.get(path3).expect(200, 'third', cb), | ||
@@ -522,3 +647,3 @@ cb => proxyReq.get(path4).expect(200, 'fourth', cb), | ||
cb => remoteReq.get(path1).expect(200, 'first', cb), | ||
cb => remoteReq.get(path2).expect(200, 'second', cb), | ||
cb => remoteReq.get(path2).expect(200, '{"second":true}', cb), | ||
cb => remoteReq.get(path3).expect(200, 'third', cb), | ||
@@ -531,3 +656,3 @@ cb => remoteReq.get(path4).expect(200, 'fourth', cb), | ||
cb => proxyReq.get(path1).expect(200, 'first', cb), | ||
cb => proxyReq.get(path2).expect(200, 'second', cb), | ||
cb => proxyReq.get(path2).expect(200, '{\n "second": true\n}', cb), | ||
cb => proxyReq.get(path3).expect(200, 'third', cb), | ||
@@ -534,0 +659,0 @@ cb => proxyReq.get(path4).expect(200, 'fourth', cb), |
@@ -41,5 +41,8 @@ 'use strict'; | ||
record: false, | ||
verbose: false | ||
verbose: false, | ||
recordToFixtures: true, | ||
recordToFixturesMode: 'path' | ||
}); | ||
}); | ||
it('should work and use defaults with empty config input', () => { | ||
@@ -63,5 +66,7 @@ const config = prepareConfig({}); | ||
record: false, | ||
verbose: false | ||
verbose: false, | ||
recordToFixtures: true, | ||
recordToFixturesMode: 'path' | ||
}); | ||
}); | ||
}); |
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
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
525431
115
4722