Skip to content

Commit 652ec20

Browse files
committed
test: added tests for createAPI
100% test coverage
1 parent 323e95a commit 652ec20

File tree

2 files changed

+124
-12
lines changed

2 files changed

+124
-12
lines changed

src/createAPI.js

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,34 @@ import callAPIMethod from './callAPIMethod';
22

33
import applyMiddleware from './applyMiddleware';
44

5-
const createAPI = (resources = {}, middleware = [], APINamespace = '', fetchOptions = {}) =>
6-
7-
Object.keys(resources).reduce( (api, resourceId) => {
8-
api[resourceId] = Object.keys(resources[resourceId].methods).reduce( (resource, method) => {
5+
const createAPI = (
6+
resources = {},
7+
middleware = [],
8+
APINamespace = '',
9+
fetchOptions = {}
10+
) => Object.keys(resources).reduce( (api, resourceId) => {
11+
api[resourceId] = Object.keys(resources[resourceId].methods)
12+
.reduce( (resource, method) => {
913
resource[method] = (params, methodOptions) => {
1014
const apiParams = resources[resourceId].methods[method](params);
11-
const boundCallAPIMethod = callAPIMethod.bind(null, APINamespace, fetchOptions, (resources[resourceId].namespace || resources[resourceId].prefix));
12-
return applyMiddleware(boundCallAPIMethod, middleware, methodOptions, apiParams, resourceId, method);
15+
const boundCallAPIMethod = callAPIMethod.bind(
16+
null,
17+
APINamespace,
18+
fetchOptions,
19+
(resources[resourceId].namespace || resources[resourceId].prefix)
20+
);
21+
return applyMiddleware(
22+
boundCallAPIMethod,
23+
middleware,
24+
methodOptions,
25+
apiParams,
26+
resourceId,
27+
method
28+
);
1329
};
1430
return resource;
1531
}, {});
16-
return api;
17-
}, {});
18-
32+
return api;
33+
}, {});
1934

2035
export default createAPI;

test/createAPI.spec.js

Lines changed: 100 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,105 @@
11
import test from 'ava';
2+
import sinon from 'sinon';
3+
import * as applyMiddleware from '../src/applyMiddleware';
4+
import callAPIMethod from '../src/callAPIMethod';
25
import createAPI from '../src/createAPI';
36

4-
const api = createAPI();
7+
let stubApplyMiddleware;
8+
let stubCallAPIMethod;
9+
const boundCallAPIMethod = _ => _;
10+
test.beforeEach(t => {
11+
stubCallAPIMethod = sinon.stub(callAPIMethod, 'bind');
12+
stubCallAPIMethod.returns(boundCallAPIMethod);
13+
stubApplyMiddleware = sinon.stub(applyMiddleware, 'default');
14+
});
15+
test.afterEach.always(t => {
16+
stubApplyMiddleware.restore();
17+
stubCallAPIMethod.restore();
18+
});
519

6-
test('coverage', t => {
7-
t.pass()
20+
test('undefined resources', t => {
21+
const API = createAPI();
22+
t.deepEqual(API, {}, 'empty API object');
23+
});
24+
25+
test('0 resources', t => {
26+
const API = createAPI({});
27+
t.deepEqual(API, {}, 'empty API object');
28+
});
29+
30+
test('supports resource prefix insted of namespace', t => {
31+
const user = {
32+
prefix: 'user-endpoint',
33+
methods: {
34+
get: ({ id }) => ({ path: ['get', id] })
35+
}
36+
};
37+
const resources = { user };
38+
const API = createAPI(resources, [], 'https://example.com');
39+
40+
API.user.get(1);
41+
42+
t.true(stubCallAPIMethod.calledWithExactly(
43+
null, 'https://example.com', {}, 'user-endpoint'
44+
), 'correct arguments passed to callAPIMethod');
45+
});
46+
47+
test('general behaviour', t => {
48+
const userResource = {
49+
namespace: 'user',
50+
methods: {
51+
get: ({ id }) => ({ path: ['get', id] }),
52+
delete: ({ id }) => ({ path: ['delete', id], options: { method: 'DELETE'} })
53+
}
54+
};
55+
const projectResource = {
56+
namespace: 'project',
57+
methods: {
58+
get: ({ id }) => ({ path: ['get', id] }),
59+
delete: ({ id }) => ({ path: ['delete', id], options: { method: 'DELETE'} })
60+
}
61+
};
62+
63+
const resources = {
64+
user: userResource,
65+
project: projectResource
66+
};
67+
68+
const middleware = [ _ => _, _ => _];
69+
70+
const API = createAPI(resources, middleware, 'https://example.com');
71+
72+
t.deepEqual(Object.keys(API), ['user', 'project'], 'resources');
73+
74+
Object.keys(API).forEach(resource => {
75+
t.deepEqual(Object.keys(API[resource]), ['get', 'delete'], 'methods');
76+
Object.keys(API[resource]).forEach(method => {
77+
t.true(API[resource][method] instanceof Function, 'methods are functions');
78+
});
79+
});
80+
81+
const methodOptions = { specific: 'option' };
82+
API.user.delete({ id: 1 }, methodOptions);
83+
84+
t.true(stubCallAPIMethod.calledOnce);
85+
t.true(stubApplyMiddleware.calledOnce);
86+
t.true(stubCallAPIMethod.calledBefore(stubApplyMiddleware));
87+
t.true(stubCallAPIMethod.calledWithExactly(
88+
null, 'https://example.com', {}, 'user'
89+
));
90+
91+
t.is(stubApplyMiddleware.lastCall.args[0], boundCallAPIMethod, 'stubApplyMiddleware called with boundCallAPIMethod');
92+
t.is(stubApplyMiddleware.lastCall.args[1], middleware, 'stubApplyMiddleware called with middleware');
93+
t.is(stubApplyMiddleware.lastCall.args[2], methodOptions, 'stubApplyMiddleware called with methodOptions');
94+
t.deepEqual(stubApplyMiddleware.lastCall.args[3], {
95+
path: ['delete', 1], options: { method: 'DELETE'}
96+
}, 'stubApplyMiddleware called with correct apiParams');
97+
t.is(stubApplyMiddleware.lastCall.args[4], 'user', 'stubApplyMiddleware called with correct resourceId');
98+
t.is(stubApplyMiddleware.lastCall.args[5], 'delete', 'stubApplyMiddleware called with correct method');
99+
100+
API.user.delete({ id: 1 });
101+
102+
t.true(stubCallAPIMethod.calledTwice);
103+
t.true(stubApplyMiddleware.calledTwice);
104+
t.true(stubCallAPIMethod.calledBefore(stubApplyMiddleware));
8105
});

0 commit comments

Comments
 (0)