Skip to content

Commit

Permalink
feat: Move more response methods to shared base class (#74)
Browse files Browse the repository at this point in the history
- Move `send`, `json`,  and `type` methods out of `Response` and into `HTTPBase` class
- Add `jsonBody` alias method to `HTTPBase` class.
  • Loading branch information
offirgolan authored Jul 23, 2018
1 parent 8d91422 commit 4f845e5
Show file tree
Hide file tree
Showing 6 changed files with 287 additions and 126 deletions.
131 changes: 131 additions & 0 deletions docs/server/request.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,136 @@
# Request

## Methods

### getHeader

Get a header with a given name.

| Param | Type | Description |
| --- | --- | --- |
| name | `String` | The name of the header |
| __Returns__ | `String` | The header value |

__Example__

```js
req.getHeader('Content-Type'); // → application/json
```

### setHeader

Set a header with a given name. If the value is falsy, the header will be
removed.

| Param | Type | Description |
| --- | --- | --- |
| name | `String` | The name of the header |
| value | `String` | The value for the header |
| __Returns__ | [Request](server/request) | The current request |

__Example__

```js
req.setHeader('Content-Length', 42);
```

### setHeaders

Add multiple headers at once. A falsy header value will remove that header
altogether.

| Param | Type | Description |
| --- | --- | --- |
| headers | `Object` | The headers to add to the request |
| __Returns__ | [Request](server/request) | The current request |

__Example__

```js
req.setHeaders({
'Content-Type': 'application/json',
'Content-Length': 42
});
```

### hasHeader

Returns 'true' or 'false' depending on if the request has the given header.

| Param | Type | Description |
| --- | --- | --- |
| name | `String` | The name of the header |
| __Returns__ | `Boolean` |   |

__Example__

```js
req.hasHeader('X-AUTH'); // → false
```

### type

Sets the request's Content Type.

| Param | Type | Description |
| --- | --- | --- |
| value | `String` |   |
| __Returns__ | [Request](server/request) | The current request |

### send

Sets the request's body.

- If the body is a `String`, it defaults the content type to `text/html` if does not exist.
- If the body is a `String` and no charset is found, a `utf-8` charset is appended to the content type.
- Body that is a `Boolean`, `Number`, or `Object` gets passed to the [json](#json) method.

| Param | Type | Description |
| --- | --- | --- |
| body | `any` |   |
| __Returns__ | [Request](server/request) | The current request |

__Example__

```js
req.send('Hello World');
req.send(200);
req.send(true);
req.send();
```

### json

A shortcut method to set the content type to `application/json` if it hasn't
been set already, and call [send](#send) with the stringified object.

| Param | Type | Description |
| --- | --- | --- |
| obj | `Object` | Object to send |
| __Returns__ | [Request](server/request) | The current request |

__Example__

```js
req.json({ Hello: 'World' });
```

### jsonBody

A shortcut method that calls JSON.parse on the request's body.

!> This method will throw if the body is an invalid JSON string.

| Param | Type | Description |
| --- | --- | --- |
| __Returns__ | `Object` | The JSON parsed body |

__Example__

```js
req.jsonBody();
```

## Properties

### method
Expand Down
16 changes: 16 additions & 0 deletions docs/server/response.md
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,22 @@ __Example__
res.json({ Hello: 'World' });
```

### jsonBody

A shortcut method that calls JSON.parse on the response's body.

!> This method will throw if the body is an invalid JSON string.

| Param | Type | Description |
| --- | --- | --- |
| __Returns__ | `Object` | The JSON parsed body |

__Example__

```js
res.jsonBody();
```

### end

Freeze the response and headers so they can no longer be modified.
Expand Down
55 changes: 55 additions & 0 deletions packages/@pollyjs/core/src/-private/http-base.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import HTTPHeaders from '../utils/http-headers';
import stringify from 'fast-json-stable-stringify';

const { freeze } = Object;
const { parse } = JSON;

export default class HTTPBase {
constructor() {
Expand Down Expand Up @@ -29,6 +31,59 @@ export default class HTTPBase {
return !!this.getHeader(name);
}

type(type) {
return this.setHeader('Content-Type', type);
}

send(data) {
let body = data;

switch (typeof body) {
case 'string':
// String defaulting to html
if (!this.hasHeader('Content-Type')) {
this.type('text/html');
}

break;
case 'boolean':
case 'number':
case 'object':
if (body === null) {
body = '';
} else {
return this.json(body);
}

break;
}

if (typeof body === 'string') {
const contentType = this.getHeader('Content-Type');

// Write strings in utf-8
if (contentType && !contentType.includes('charset')) {
this.type(`${contentType}; charset=utf-8`);
}
}

this.body = body;

return this;
}

json(obj) {
if (!this.hasHeader('Content-Type')) {
this.type('application/json');
}

return this.send(stringify(obj));
}

jsonBody() {
return parse(this.body);
}

end() {
freeze(this);
freeze(this.headers);
Expand Down
52 changes: 1 addition & 51 deletions packages/@pollyjs/core/src/-private/response.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import stringify from 'fast-json-stable-stringify';
import { assert, HTTP_STATUS_CODES } from '@pollyjs/utils';
import HTTPBase from './http-base';
import { assert, HTTP_STATUS_CODES } from '@pollyjs/utils';

const DEFAULT_STATUS_CODE = 200;

Expand Down Expand Up @@ -36,59 +35,10 @@ export default class PollyResponse extends HTTPBase {
return this;
}

type(type) {
return this.setHeader('Content-Type', type);
}

send(data) {
let body = data;

switch (typeof body) {
case 'string':
// String defaulting to html
if (!this.hasHeader('Content-Type')) {
this.type('text/html');
}

break;
case 'boolean':
case 'number':
case 'object':
if (body === null) {
body = '';
} else {
return this.json(body);
}

break;
}

if (typeof body === 'string') {
const contentType = this.getHeader('Content-Type');

// Write strings in utf-8
if (contentType && !contentType.includes('charset')) {
this.type(`${contentType}; charset=utf-8`);
}
}

this.body = body;

return this;
}

sendStatus(status) {
this.status(status);
this.type('text/plain');

return this.send(status);
}

json(obj) {
if (!this.hasHeader('Content-Type')) {
this.type('application/json');
}

return this.send(stringify(obj));
}
}
84 changes: 84 additions & 0 deletions packages/@pollyjs/core/tests/unit/-private/http-base-test.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import HTTPBase from '../../../src/-private/http-base';
import stringify from 'fast-json-stable-stringify';

let base;

Expand Down Expand Up @@ -69,6 +70,86 @@ describe('Unit | HTTPBase', function() {
expect(headers).to.deep.equal({ one: '1', two: '2' });
});

it('.type()', function() {
base.type('text/plain');
expect(base.getHeader('Content-Type')).to.equal('text/plain');
});

it('.send() - string', function() {
base.send('foo');

expect(base.body).to.equal('foo');
expect(base.getHeader('Content-Type')).to.equal(
'text/html; charset=utf-8'
);
});

it('.send() - boolean, number, & object', function() {
[true, 200, {}].forEach(body => {
base.type();
base.send(body);

expect(base.body).to.equal(stringify(body));
expect(base.getHeader('Content-Type')).to.equal(
'application/json; charset=utf-8'
);
});
});

it('.send() - null & undefined', function() {
base.type();
base.send(null);
expect(base.body).to.equal('');
expect(base.hasHeader('Content-Type')).to.be.false;

base.type();
base.send();
expect(base.body).to.be.undefined;
expect(base.hasHeader('Content-Type')).to.be.false;
});

it('.send() - should not override existing type', function() {
base.type('text/plain; charset=utf-9000');
base.send('foo');

expect(base.body).to.equal('foo');
expect(base.getHeader('Content-Type')).to.equal(
'text/plain; charset=utf-9000'
);
});

it('.json()', function() {
const obj = { foo: 'bar' };

base.json(obj);

expect(base.body).to.equal(stringify(obj));
expect(base.getHeader('Content-Type')).to.equal(
'application/json; charset=utf-8'
);
});

it('.json() - should not override existing type', function() {
const obj = { foo: 'bar' };

base.type('text/plain; charset=utf-9000');
base.send(obj);

expect(base.body).to.equal(stringify(obj));
expect(base.getHeader('Content-Type')).to.equal(
'text/plain; charset=utf-9000'
);
});

it('.jsonBody()', function() {
const obj = { foo: 'bar' };

expect(() => base.jsonBody()).to.throw(Error);

base.json(obj);
expect(base.jsonBody()).to.deep.equal(obj);
});

it('.end()', function() {
base.setHeader('foo', 'bar');

Expand All @@ -82,6 +163,9 @@ describe('Unit | HTTPBase', function() {
it('should be chainable', function() {
expect(base.setHeader('one', '1')).to.equal(base);
expect(base.setHeaders({ one: '1' })).to.equal(base);
expect(base.type('text/plain')).to.equal(base);
expect(base.send('body')).to.equal(base);
expect(base.json({ foo: 'bar' })).to.equal(base);
expect(base.end()).to.equal(base);
});
});
Expand Down
Loading

0 comments on commit 4f845e5

Please sign in to comment.