Skip to content

Commit 90a5b79

Browse files
committed
Rename fixture to recording
1 parent 10f23ce commit 90a5b79

File tree

3 files changed

+72
-70
lines changed

3 files changed

+72
-70
lines changed

README.md

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
</p>
44

55
- Automatically record new HTTP(s) requests.
6-
- Replay fixtures when testing.
6+
- Replay recordings when testing.
77
- Customize responses.
88
- Works well with [supertest](https://github.com/visionmedia/supertest).
99
- Predictable, deterministic filepaths.
@@ -30,15 +30,15 @@ $ npm install node-recorder --save-dev
3030
## Getting Started
3131

3232
- By simply including `node-recorder`, **all HTTP(s) requests are intercepted**.
33-
- By default, `RECORD` mode records new fixtures, and replays existing fixures.
34-
- When in `NODE_ENV=test` or `CI=true`, `REPLAY` mode replays existing fixtures, and throws an error when one doesn't exist.
33+
- By default, `RECORD` mode records new recordings, and replays existing fixures.
34+
- When in `NODE_ENV=test` or `CI=true`, `REPLAY` mode replays existing recordings, and throws an error when one doesn't exist.
3535
_(So that local tests don't suddenly fail in CI)_
3636

3737
### Recorder Modes
3838

3939
- `bypass` - All network requests bypass the recorder and respond as usual.
40-
- `record` - Record only new network requests (i.e. those without fixtures), while replaying existing fixtures.
41-
- `replay` - Replay all network requests using fixtures. **If a fixture is missing, an error is thrown**.
40+
- `record` - Record only new network requests (i.e. those without recordings), while replaying existing recordings.
41+
- `replay` - Replay all network requests using recordings. **If a recording is missing, an error is thrown**.
4242
- `rerecord` - Re-record all network requests.
4343

4444
### Using `node --require`
@@ -102,7 +102,7 @@ module.exports = {
102102
}
103103
```
104104

105-
- `request` is the same as the fixture (e.g. `body`, `headers`, `href`, `method`), but
105+
- `request` is the same as the recording (e.g. `body`, `headers`, `href`, `method`), but
106106
with an additional `url` property from https://github.com/unshiftio/url-parse to simplify conditional logic.
107107
- `response` contains `body`, `headers`, & `statusCode`.
108108

@@ -113,9 +113,9 @@ This is useful when network requests are stateful, in that they rely on an autho
113113
1. Suppose you login by calling `/login?user=foo&password=bar`.
114114
2. The response contains `{ "token": "abc123" }3. Now, to get data, you call`/api?token=abc123`.
115115

116-
When recording fixtures, the token `abc123` isn't clearly associated with the user `foo`.
116+
When recording recordings, the token `abc123` isn't clearly associated with the user `foo`.
117117

118-
To address this, you can `identify` the `request` and `response`, so that the fixtures are aliased accordingly:
118+
To address this, you can `identify` the `request` and `response`, so that the recordings are aliased accordingly:
119119

120120
```js
121121
identify(request, response) {
@@ -138,7 +138,7 @@ identify(request, response) {
138138
}
139139
```
140140

141-
Now, when recorded fixtures will look like:
141+
Now, when recorded recordings will look like:
142142

143143
- `127.0.0.1/login/${hash}.${user}.json`
144144
- `127.0.0.1/api/${hash}.${user}.json`
@@ -147,7 +147,7 @@ This way, similar-looking network requests (e.g. login & GraphQL) can be differe
147147

148148
#### `ignore` a `request`
149149

150-
Typically, you don't want to record fixtures for things like analytics or reporting.
150+
Typically, you don't want to record recordings for things like analytics or reporting.
151151

152152
```js
153153
// recorder.conig.js
@@ -164,11 +164,11 @@ module.exports = {
164164

165165
#### `normalize` a `request` or `response`
166166

167-
Fixtures are meant to make development & testing _easier_, so modification is necessary.
167+
Recordings are meant to make development & testing _easier_, so modification is necessary.
168168

169-
- **Changing `request` changes the filename `hash` of the fixture**. You may need to `record` again.
169+
- **Changing `request` changes the filename `hash` of the recording**. You may need to `record` again.
170170
- `normalize` is called **before** the network request and **after**. This means that `response` may be `undefined`!
171-
- You can **change `response` by hand, or via `normalize` without affecting the filename `hash` of the fixture**.
171+
- You can **change `response` by hand, or via `normalize` without affecting the filename `hash` of the recording**.
172172

173173
```js
174174
module.exports = {

__fixtures__/api.github.com/rate_limit/3898697523.json renamed to __recordings__/api.github.com/rate_limit/3898697523.json

File renamed without changes.

src/Recorder.ts

Lines changed: 59 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -24,28 +24,28 @@ interface Config {
2424
mode?: Mode;
2525
ignore?: Ignore;
2626
identify?: Identify;
27-
fixturesPath?: string;
2827
normalizer?: Normalizer;
28+
recordingsPath?: string;
2929
}
3030

3131
interface Ignore {
3232
(request: NormalizedRequest): boolean;
3333
}
3434

3535
interface Identify {
36-
(request: NormalizedRequest, response?: ResponseFixture):
36+
(request: NormalizedRequest, response?: ResponseRecording):
3737
| undefined
3838
| string
3939
| [string, string];
4040
}
4141

42-
interface NormalizedRequest extends RequestFixture {
42+
interface NormalizedRequest extends RequestRecording {
4343
url: URL;
4444
}
4545

4646
// TODO Use { request, response, url } to avoid mudying the request
4747
interface Normalizer {
48-
(request: NormalizedRequest, response?: ResponseFixture): void;
48+
(request: NormalizedRequest, response?: ResponseRecording): void;
4949
}
5050

5151
enum Methods {
@@ -65,22 +65,22 @@ interface RequestOptions extends http.RequestOptions {
6565
proto?: string;
6666
}
6767

68-
interface RequestFixture {
68+
interface RequestRecording {
6969
method: Methods;
7070
href: string;
7171
headers: http.IncomingHttpHeaders;
7272
body: string;
7373
}
7474

75-
interface ResponseFixture {
75+
interface ResponseRecording {
7676
statusCode: number;
7777
headers: http.IncomingHttpHeaders;
7878
body: object | string;
7979
}
8080

81-
interface Fixture {
82-
request: RequestFixture;
83-
response: ResponseFixture;
81+
interface Recording {
82+
request: RequestRecording;
83+
response: ResponseRecording;
8484
}
8585

8686
interface InterceptedRequest {
@@ -113,7 +113,7 @@ export class Recorder {
113113

114114
private config: Config = {
115115
mode: RECORDER as Mode,
116-
fixturesPath: path.resolve(process.cwd(), "__fixtures__")
116+
recordingsPath: path.resolve(process.cwd(), "__recordings__")
117117
};
118118

119119
constructor() {
@@ -167,56 +167,58 @@ export class Recorder {
167167
Object.assign(this.config, config);
168168
};
169169

170-
getFixture = (interceptedRequest: InterceptedRequest): Fixture => {
171-
const { request } = this.normalize(interceptedRequest) as Fixture;
172-
const fixturePath = this.getFixturePath(request);
170+
getRecording = (interceptedRequest: InterceptedRequest): Recording => {
171+
const { request } = this.normalize(interceptedRequest) as Recording;
172+
const recordingPath = this.getRecordingPath(request);
173173

174-
if (!fs.existsSync(fixturePath)) {
175-
throw new Error(`Missing fixture ${this.getFixtureLink(fixturePath)}`);
174+
if (!fs.existsSync(recordingPath)) {
175+
throw new Error(
176+
`Missing recording ${this.getRecordingLink(recordingPath)}`
177+
);
176178
}
177179

178-
const fixture = JSON.parse(fs.readFileSync(fixturePath, "utf8"));
180+
const recording = JSON.parse(fs.readFileSync(recordingPath, "utf8"));
179181

180-
return fixture;
182+
return recording;
181183
};
182184

183-
getFixtureLink(fixturePath: string): string {
184-
const relativePath = fixturePath.replace(process.cwd(), "").slice(1);
185+
getRecordingLink(recordingPath: string): string {
186+
const relativePath = recordingPath.replace(process.cwd(), "").slice(1);
185187

186-
return terminalLink(relativePath, `vscode://file/${fixturePath}`, {
188+
return terminalLink(relativePath, `vscode://file/${recordingPath}`, {
187189
fallback: (text: string) => text
188190
});
189191
}
190192

191-
getFixturePath(request: RequestFixture): string {
193+
getRecordingPath(request: RequestRecording): string {
192194
const { href } = request;
193195
const url = new URL(href, true);
194196
const { hostname, pathname } = url;
195197

196198
if (!hostname) {
197199
throw new Error(
198-
`Cannot parse hostname from fixture's "href": ${JSON.stringify(href)}`
200+
`Cannot parse hostname from recording's "href": ${JSON.stringify(href)}`
199201
);
200202
}
201203

202204
if (!pathname) {
203205
throw new Error(
204-
`Cannot parse pathname from fixture's "href": ${JSON.stringify(href)}`
206+
`Cannot parse pathname from recording's "href": ${JSON.stringify(href)}`
205207
);
206208
}
207209

208210
const hash = fnv1a(JSON.stringify(request));
209211
const identity = this.identify(request);
210212
const filename = identity ? `${hash}-${identity}` : hash;
211213

212-
const fixturePath = path.join(
213-
this.config.fixturesPath as string,
214+
const recordingPath = path.join(
215+
this.config.recordingsPath as string,
214216
hostname,
215217
pathname,
216218
`${filename}.json`
217219
);
218220

219-
return fixturePath;
221+
return recordingPath;
220222
}
221223

222224
getHrefFromOptions(options: RequestOptions) {
@@ -276,7 +278,7 @@ export class Recorder {
276278
handleRequest = (interceptedRequest: InterceptedRequest) => {
277279
let mode = this.getMode();
278280
const { method, options } = interceptedRequest;
279-
const fixturePath = this.hasFixture(interceptedRequest);
281+
const recordingPath = this.hasRecording(interceptedRequest);
280282
const href = this.getHrefFromOptions(options);
281283
const link = terminalLink(href, href, {
282284
fallback: (text: string) => text
@@ -298,8 +300,8 @@ export class Recorder {
298300
return this.bypassRequest(interceptedRequest);
299301

300302
case Mode.RECORD:
301-
if (fixturePath) {
302-
log(`Replaying ${this.getFixtureLink(fixturePath)}`);
303+
if (recordingPath) {
304+
log(`Replaying ${this.getRecordingLink(recordingPath)}`);
303305
return this.replayRequest(interceptedRequest);
304306
}
305307

@@ -311,8 +313,8 @@ export class Recorder {
311313
return this.recordRequest(interceptedRequest);
312314

313315
case Mode.REPLAY:
314-
if (fixturePath) {
315-
log(`Replaying ${this.getFixtureLink(fixturePath)}`);
316+
if (recordingPath) {
317+
log(`Replaying ${this.getRecordingLink(recordingPath)}`);
316318
} else {
317319
log(`Replaying ${link}`);
318320
}
@@ -326,25 +328,25 @@ export class Recorder {
326328

327329
handleResponse = (
328330
interceptedRequest: InterceptedRequest,
329-
fixture: Fixture
331+
recording: Recording
330332
) => {
331333
const { respond } = interceptedRequest;
332-
const { request, response } = fixture;
334+
const { request, response } = recording;
333335
const { body, headers, statusCode } = response;
334336

335337
this.identify(request, response);
336338

337339
respond(null, [statusCode, body, headers]);
338340
};
339341

340-
hasFixture(interceptedRequest: InterceptedRequest): string | false {
341-
const { request } = this.normalize(interceptedRequest) as Fixture;
342-
const fixturePath = this.getFixturePath(request);
342+
hasRecording(interceptedRequest: InterceptedRequest): string | false {
343+
const { request } = this.normalize(interceptedRequest) as Recording;
344+
const recordingPath = this.getRecordingPath(request);
343345

344-
return fs.existsSync(fixturePath) ? fixturePath : false;
346+
return fs.existsSync(recordingPath) ? recordingPath : false;
345347
}
346348

347-
identify(request: RequestFixture, response?: ResponseFixture) {
349+
identify(request: RequestRecording, response?: ResponseRecording) {
348350
const { identify } = this.config;
349351

350352
if (!identify) {
@@ -396,7 +398,7 @@ export class Recorder {
396398

397399
async makeRequest(
398400
interceptedRequest: InterceptedRequest
399-
): Promise<ResponseFixture> {
401+
): Promise<ResponseRecording> {
400402
const { body, headers, method, options } = interceptedRequest;
401403

402404
const request = (options.proto === "https"
@@ -481,7 +483,7 @@ export class Recorder {
481483

482484
normalize(
483485
interceptedRequest: InterceptedRequest,
484-
response?: ResponseFixture
486+
response?: ResponseRecording
485487
) {
486488
// Poor-man's clone for immutability
487489
const request = JSON.parse(JSON.stringify(interceptedRequest));
@@ -498,24 +500,24 @@ export class Recorder {
498500
url.set("port", undefined);
499501
}
500502

501-
const fixture = {
503+
const recording = {
502504
request: { method, href, headers, body, url },
503505
response
504506
};
505507

506508
const { normalizer } = this.config;
507509

508510
if (normalizer) {
509-
normalizer(fixture.request, fixture.response);
511+
normalizer(recording.request, recording.response);
510512
}
511513

512514
// Update href to match url object
513-
fixture.request.href = fixture.request.url.toString();
515+
recording.request.href = recording.request.url.toString();
514516

515517
// Don't save parsed url
516-
delete fixture.request.url;
518+
delete recording.request.url;
517519

518-
return fixture;
520+
return recording;
519521
}
520522

521523
record() {
@@ -527,14 +529,14 @@ export class Recorder {
527529
const response = await this.makeRequest(request);
528530
const { statusCode, body, headers } = response;
529531

530-
// Respond with *real* response for recording, not fixture.
532+
// Respond with *real* response for recording, not recording.
531533
respond(null, [statusCode, body, headers]);
532534

533-
const fixture = this.normalize(request, response) as Fixture;
535+
const recording = this.normalize(request, response) as Recording;
534536

535-
this.identify(fixture.request, fixture.response);
537+
this.identify(recording.request, recording.response);
536538

537-
process.nextTick(() => this.saveFixture(fixture));
539+
process.nextTick(() => this.saveRecording(recording));
538540
}
539541

540542
replay() {
@@ -545,11 +547,11 @@ export class Recorder {
545547
const { req } = interceptedRequest;
546548

547549
try {
548-
const fixture = await this.getFixture(interceptedRequest);
550+
const recording = await this.getRecording(interceptedRequest);
549551

550-
this.identify(fixture.request, fixture.response);
552+
this.identify(recording.request, recording.response);
551553

552-
return this.handleResponse(interceptedRequest, fixture);
554+
return this.handleResponse(interceptedRequest, recording);
553555
} catch (error) {
554556
req.emit("error", error);
555557
}
@@ -580,12 +582,12 @@ export class Recorder {
580582
http.ClientRequest[IS_STUBBED] = true;
581583
}
582584

583-
saveFixture(fixture: Fixture) {
584-
const fixturePath = this.getFixturePath(fixture.request);
585-
const serialized = JSON.stringify(fixture, null, 2);
585+
saveRecording(recording: Recording) {
586+
const recordingPath = this.getRecordingPath(recording.request);
587+
const serialized = JSON.stringify(recording, null, 2);
586588

587-
mkdirp.sync(path.dirname(fixturePath));
588-
fs.writeFileSync(fixturePath, serialized);
589+
mkdirp.sync(path.dirname(recordingPath));
590+
fs.writeFileSync(recordingPath, serialized);
589591
}
590592

591593
setupNock() {

0 commit comments

Comments
 (0)