UNPKG

6.12 kBMarkdownView Raw
1# Freddo
2
3> Minimal assertion testing framework for APIs
4
5[![Build Status](https://travis-ci.org/Meeshkan/freddo.svg?branch=master)](https://travis-ci.org/Meeshkan/freddo) [![Install Size](https://packagephobia.now.sh/badge?p=freddo)](https://packagephobia.now.sh/result?p=freddo)
6
7## Install
8
9```
10~ ❯❯❯ npm install freddo --save-dev
11```
12
13## Usage
14
15```js
16const { freddo, expr, exists } = require('freddo');
17
18(async () => {
19 const isSvg = str => str.trim().startsWith('<svg ')
20 /*
21 <svg width="120.5" height="20" viewBox="0 0 1205 200" xmlns="http://www.w3.org/2000/svg">
22 ...
23 </svg>
24 */
25 await freddo('https://badgen.net/packagephobia/install/sha-regex')
26 .status(200)
27 .header('content-type', 'image/svg+xml;charset=utf-8')
28 .body(isSvg)
29 .ensure()
30})();
31
32(async () => {
33 /*
34 {
35 "hash":"0000000000000538200a48202ca6340e983646ca088c7618ae82d68e0c76ef5a",
36 "time":1325794737,
37 "block_index":841841,
38 "height":160778,
39 "txIndexes":[13950369,13950510,13951472]
40 }
41 */
42 await freddo('https://blockchain.info/latestblock')
43 .status(200)
44 .body(exists, expr('.hash'))
45 .body(exists, expr('.time'))
46 .body(([time]) => {
47 const DAY = 24 * 60 * 60 * 1000
48 return {
49 result: time > Date.now()/1000 - DAY,
50 error: 'Most recent blockchain block is unrealistically old'
51 }
52 }, expr('.time'))
53 .body(exists, expr('.block_index'))
54 .body(([blockHeight]) => {
55 return {
56 result: blockHeight >= 500000,
57 error: 'Block height of blockchain tip is insufficient'
58 }
59 }, expr('.height'))
60 .body(exists, expr('.txIndexes'))
61 .ensure()
62})();
63
64(async () => {
65 /*
66 HTTP/1.1 301 Moved Permanently
67 */
68 await freddo('https://httpstat.us/301')
69 .redirectsTo('https://httpstat.us/301')
70 .ensure()
71})();
72```
73
74### Example uses with testing frameworks
75
76#### [AVA](https://github.com/avajs/ava)
77
78```js
79import * as test from 'ava'
80import m from '.'
81import { freddo, expr, exists } from 'freddo'
82import micro from 'micro'
83import testListen from 'test-listen'
84import validator from 'validator'
85
86let url
87test.before(async () => {
88 url = await testListen(micro(m))
89})
90
91test('/ip/json', async t => {
92 t.is(await freddo(url)
93 .status(200)
94 .header('content-type', 'application/json; charset=utf-8')
95 .body(validator.isJSON)
96 .body(exists, expr('.ip')), true)
97})
98```
99
100#### [Mocha](https://github.com/mochajs/mocha)
101
102```js
103import { freddo, expr, exists } from 'freddo'
104import validator from 'validator'
105import assert from 'assert'
106
107describe('/ip/json', function() {
108 it('should serve a JSON response', async function() {
109 assert.strict.ok(await freddo("https://locate.now.sh/ip/json/")
110 .status(200)
111 .header('content-type', 'application/json; charset=utf-8')
112 .body(validator.isJSON)
113 .body(exists, expr('.ip')))
114 })
115})
116```
117
118## API
119
120### freddo(url[, options])
121
122Returns a `Promise` for a modified [`got`](https://github.com/sindresorhus/got) request object.
123
124#### url
125
126Type: `string | object`
127
128The URL to request, as a string, a [`https.request` options object](https://nodejs.org/api/https.html#https_https_request_options_callback), or a [WHATWG `URL`](https://nodejs.org/api/url.html#url_class_url). (Adapted from [`got`'s documentation](https://github.com/sindresorhus/got))
129
130#### options
131
132Type: `object`
133
134Any of the [`https.request`](https://nodejs.org/api/https.html#https_https_request_options_callback) options. (Adapted from [`got`'s documentation](https://github.com/sindresorhus/got))
135
136### freddo.status(expected)
137
138Compares equality of `status-code` with `expected`, and returns a `boolean`.
139
140#### expected
141
142Type: `number | function`
143
144### freddo.header(entity, expected)
145
146Compares equality of `entity` `header` with `expected`, and returns a `boolean`.
147
148#### entity
149
150Type: `string`
151
152e.g. `content-type`, `content-length`, `origin`, etc.
153
154#### expected
155
156Type: `string | number | function`
157
158### freddo.body(expected[, expression])
159
160Compares equality of `body` with `expected`, and returns a `boolean`.
161
162#### expected
163
164Type: `string | function`
165
166#### expression
167
168Type: `Expression object`
169
170*Note*: When an `expression` is given, an array containing the matched values is returned. Therefore, in this case, an `expected` parameter function should treat its argument as an array and destructure it accordingly (e.g. `([x]) => x == 'bar'`).
171
172### freddo.redirectsTo(url)
173
174Checks whether `status` code contains a redirection code (i.e. `301`, `302`, `303`, `307`, or `308`) and whether there exists a `location` `header` entity containing `url`.
175
176#### url
177
178Type: `string`
179
180### freddo.expect(key, expected)
181
182Compares equality of `response.key` (where `response` is [`got`'s `response` object](https://github.com/sindresorhus/got#response)) with `expected`, and returns a `boolean`.
183
184*Note*: `freddo.ensure('body', expected)` is the same as `freddo.body(expected)` (and the same applies for `freddo.status`).
185
186#### key
187
188Type: `string`
189
190Any of [`got`'s `response` object](https://github.com/sindresorhus/got#response) keys.
191
192#### expected
193
194Type: `string | function`
195
196### freddo.ensure()
197
198Asserts the `boolean` response of the preceding functions.
199
200### expr(pattern)
201
202Returns an `Expression object` that can be passed as an `expression` parameter to the `freddo.body` function (see above) so as to find a value matching the `pattern`.
203
204#### pattern
205
206Type: [`JSPath path expression`](https://github.com/dfilatov/jspath#documentation)
207
208### exists()
209
210Returns a function that can be passed as an `expected` parameter to the `freddo.body` function to check whether a `pattern` match is found.
211
212
213## Contributing
214
215Thanks for wanting to contribute! We will soon have a contributing page
216detailing how to contribute. Meanwhile, feel free to star this repository, open issues,
217and ask for more features and support.
218
219Please note that this project is governed by the [Meeshkan Community Code of Conduct](https://github.com/unmock/code-of-conduct). By participating in this project, you agree to abide by its terms.
220
221## License
222
223MIT © [Meeshkan](https://meeshkan.com/)