Skip to content

Commit

Permalink
feat: Abort and passthrough from an intercept (#57)
Browse files Browse the repository at this point in the history
  • Loading branch information
offirgolan authored Jul 5, 2018
1 parent 0a3d591 commit 4ebacb8
Show file tree
Hide file tree
Showing 7 changed files with 366 additions and 9 deletions.
52 changes: 50 additions & 2 deletions docs/server/route-handler.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,59 @@ __Example__
```js
server
.get('/session/:id')
.intercept((req, res) => {
.intercept((req, res, interceptor) => {
if (req.params.id === '1') {
res.status(200).json({ token: 'ABC123XYZ' });
} else {
} else if (req.params.id === '2') {
res.status(404).json({ error: 'Unknown Session' });
} else {
interceptor.abort();
}
});
```

#### Interceptor

The `intercept` handler receives a third `interceptor` argument that provides
some utilities.

##### abort

Calling the `abort` method on the interceptor tells the Polly instance to
continue handling the request as if it hasn't been intercepted. This allows you
to only intercept specific types of requests while opting out of others.

__Example__

```js
server
.get('/session/:id')
.intercept((req, res, interceptor) => {
if (req.params.id === '1') {
res.status(200).json({ token: 'ABC123XYZ' });
} else {
interceptor.abort();
}
});
```

##### passthrough

Calling the `passthrough` method on the interceptor tells the Polly instance to
continue handling the request as if it has been declared as a passthrough.
This allows you to only intercept specific types of requests while passing
others through.

__Example__

```js
server
.get('/session/:id')
.intercept((req, res, interceptor) => {
if (req.params.id === '1') {
res.status(200).json({ token: 'ABC123XYZ' });
} else {
interceptor.passthrough();
}
});
```
Expand Down
35 changes: 35 additions & 0 deletions packages/@pollyjs/adapter/src/-private/interceptor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
const ABORT = Symbol();
const PASSTHROUGH = Symbol();

function setDefaults(interceptor) {
interceptor[ABORT] = false;
interceptor[PASSTHROUGH] = false;
}

export default class Interceptor {
constructor() {
setDefaults(this);
}

abort() {
setDefaults(this);
this[ABORT] = true;
}

passthrough() {
setDefaults(this);
this[PASSTHROUGH] = true;
}

get shouldAbort() {
return this[ABORT];
}

get shouldPassthrough() {
return this[PASSTHROUGH];
}

get shouldIntercept() {
return !this.shouldAbort && !this.shouldPassthrough;
}
}
27 changes: 20 additions & 7 deletions packages/@pollyjs/adapter/src/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import Interceptor from './-private/interceptor';
import isExpired from './utils/is-expired';
import { ACTIONS, MODES, assert } from '@pollyjs/utils';

Expand Down Expand Up @@ -72,15 +73,25 @@ export default class Adapter {
async [REQUEST_HANDLER](request) {
const { mode } = this.polly;
const pollyRequest = this.polly.registerRequest(request);
let interceptor;

await pollyRequest.setup();

if (mode === MODES.PASSTHROUGH || pollyRequest.shouldPassthrough) {
return this.passthrough(pollyRequest);
if (pollyRequest.shouldIntercept) {
interceptor = new Interceptor();
const response = await this.intercept(pollyRequest, interceptor);

if (interceptor.shouldIntercept) {
return response;
}
}

if (pollyRequest.shouldIntercept) {
return this.intercept(pollyRequest);
if (
mode === MODES.PASSTHROUGH ||
pollyRequest.shouldPassthrough ||
(interceptor && interceptor.shouldPassthrough)
) {
return this.passthrough(pollyRequest);
}

if (mode === MODES.RECORD) {
Expand All @@ -104,11 +115,13 @@ export default class Adapter {
return this.onPassthrough(pollyRequest);
}

async intercept(pollyRequest) {
async intercept(pollyRequest, interceptor) {
pollyRequest.action = ACTIONS.INTERCEPT;
await pollyRequest._intercept();
await pollyRequest._intercept(interceptor);

return this.onIntercept(pollyRequest, pollyRequest.response);
if (interceptor.shouldIntercept) {
return this.onIntercept(pollyRequest, pollyRequest.response);
}
}

async record(pollyRequest) {
Expand Down
54 changes: 54 additions & 0 deletions packages/@pollyjs/adapter/tests/unit/-private/interceptor-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import Interceptor from '../../../src/-private/interceptor';

describe('Unit | Private | Interceptor', function() {
it('should exist', function() {
expect(Interceptor).to.be.a('function');
});

it('should have the correct defaults', function() {
const interceptor = new Interceptor();

expect(interceptor.shouldAbort).to.be.false;
expect(interceptor.shouldPassthrough).to.be.false;
expect(interceptor.shouldIntercept).to.be.true;
});

it('should disable passthrough when calling abort and vise versa', function() {
const interceptor = new Interceptor();

expect(interceptor.shouldAbort).to.be.false;
expect(interceptor.shouldPassthrough).to.be.false;

interceptor.abort();
expect(interceptor.shouldAbort).to.be.true;
expect(interceptor.shouldPassthrough).to.be.false;

interceptor.passthrough();
expect(interceptor.shouldAbort).to.be.false;
expect(interceptor.shouldPassthrough).to.be.true;
});

it('.abort()', function() {
const interceptor = new Interceptor();

expect(interceptor.shouldAbort).to.be.false;
expect(interceptor.shouldIntercept).to.be.true;

interceptor.abort();

expect(interceptor.shouldAbort).to.be.true;
expect(interceptor.shouldIntercept).to.be.false;
});

it('.passthrough()', function() {
const interceptor = new Interceptor();

expect(interceptor.shouldPassthrough).to.be.false;
expect(interceptor.shouldIntercept).to.be.true;

interceptor.passthrough();

expect(interceptor.shouldPassthrough).to.be.true;
expect(interceptor.shouldIntercept).to.be.false;
});
});
51 changes: 51 additions & 0 deletions packages/@pollyjs/core/tests/integration/adapters-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { setupMocha as setupPolly } from '../../src';
import * as setupFetch from '../helpers/setup-fetch';
import File from '../helpers/file';
import Configs from './configs';
import { ACTIONS } from '@pollyjs/utils';

describe('Integration | Adapters', function() {
for (const name in Configs) {
Expand Down Expand Up @@ -64,6 +65,56 @@ describe('Integration | Adapters', function() {
expect(await persister.find(recordingId)).to.be.null;
});

it('should be able to intercept when in passthrough mode', async function() {
const { server } = this.polly;

this.polly.configure({ mode: 'passthrough' });

server
.get('/ping')
.intercept((req, res) => res.status(200).send('pong'));

const res = await this.fetch('/ping');
const text = await res.text();

expect(res.status).to.equal(200);
expect(text).to.equal('pong');
});

it('should be able to abort from an intercept', async function() {
const { server } = this.polly;
let responseCalled = false;

server
.get(this.recordUrl())
.intercept((req, res, interceptor) => interceptor.abort())
.on('response', req => {
responseCalled = true;
expect(req.action).to.not.equal(ACTIONS.INTERCEPT);
});

expect((await this.fetchRecord()).status).to.equal(404);
expect(responseCalled).to.be.true;
});

it('should be able to passthrough from an intercept', async function() {
const { server, persister, recordingId } = this.polly;
let responseCalled = false;

server
.get(this.recordUrl())
.intercept((req, res, interceptor) => interceptor.passthrough())
.on('response', req => {
responseCalled = true;
expect(req.action).to.equal(ACTIONS.PASSTHROUGH);
});

expect(await persister.find(recordingId)).to.be.null;
expect((await this.fetchRecord()).status).to.equal(404);
expect(await persister.find(recordingId)).to.be.null;
expect(responseCalled).to.be.true;
});

it('should handle recording requests posting FormData + Blob/File', async function() {
const { server, recordingName } = this.polly;
const form = new FormData();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
{
"log": {
"_recordingName": "@pollyjs/core/Integration | Adapters/Fetch Adapter + Rest Persister/should be able to abort from an intercept",
"browser": {
"name": "Chrome",
"version": "67.0"
},
"creator": {
"name": "Polly.JS",
"version": "0.5.0"
},
"entries": [
{
"_id": "1bcdb30445bfd8996055be3579e6c9f2",
"_order": 0,
"cache": {},
"request": {
"bodySize": 0,
"cookies": [],
"headers": [],
"headersSize": 212,
"httpVersion": "HTTP/1.1",
"method": "GET",
"queryString": [],
"url": "http://localhost:7357/api/db/-pollyjs_2160168770%2Fcore_3713949822%2FIntegration-Adapters_529408267%2FFetch-Adapter-Rest-Persister_112800326%2Fshould-be-able-to-abort-from-an-intercept_68559697"
},
"response": {
"bodySize": 0,
"content": {
"mimeType": "text/plain",
"size": 0
},
"cookies": [],
"headers": [
{
"name": "access-control-allow-origin",
"value": "*"
},
{
"name": "connection",
"value": "close"
},
{
"name": "content-length",
"value": "0"
},
{
"name": "date",
"value": "Tue, 03 Jul 2018 21:10:04 GMT"
},
{
"name": "x-powered-by",
"value": "Express"
}
],
"headersSize": 132,
"httpVersion": "HTTP/1.1",
"redirectURL": "",
"status": 404,
"statusText": "Not Found"
},
"startedDateTime": "2018-07-03T21:10:04.607Z",
"time": 13,
"timings": {
"blocked": -1,
"connect": -1,
"dns": -1,
"receive": 0,
"send": 0,
"ssl": -1,
"wait": 13
}
}
],
"pages": [],
"version": "1.2"
}
}
Loading

0 comments on commit 4ebacb8

Please sign in to comment.