UNPKG

@burriko/devour-client

Version:

A lightweight, framework agnostic, flexible JSON API client

1,234 lines (1,096 loc) 40.1 kB
/* global describe, context, it, beforeEach, afterEach */ import JsonApi from '../../src/index' import jsonApiGetMiddleware from '../../src/middleware/json-api/req-get' import jsonApiDeleteMiddleware from '../../src/middleware/json-api/req-delete' import mockResponse from '../helpers/mock-response' import expect from 'expect.js' import sinon from 'sinon' describe('JsonApi', () => { var jsonApi = null beforeEach(() => { jsonApi = new JsonApi({apiUrl: 'http://myapi.com'}) }) afterEach(() => { jsonApi.resetBuilder() }) describe('Constructors', () => { it('should allow both object and deprecated constructors to be used', () => { let jsonApi jsonApi = new JsonApi('http://myapi.com') expect(jsonApi).to.be.a(JsonApi) jsonApi = new JsonApi('http://myapi.com', []) expect(jsonApi).to.be.a(JsonApi) jsonApi = new JsonApi({apiUrl: 'http://myapi.com'}) expect(jsonApi).to.be.a(JsonApi) }) it('should allow apiUrl to be set via the initializer object', () => { let jsonApi = new JsonApi({apiUrl: 'http://myapi.com'}) expect(jsonApi.apiUrl).to.eql('http://myapi.com') }) it('should allow middleware to be set via the initializer object', () => { let middleware = [ { name: 'm1', req: function (req) { return req }, res: function (res) { return res } }, { name: 'm2', req: function (req) { return req }, res: function (res) { return res } } ] let jsonApi = new JsonApi({apiUrl: 'http://myapi.com', middleware: middleware}) expect(jsonApi.middleware).to.eql(middleware) expect(jsonApi.apiUrl).to.eql('http://myapi.com') }) it('should set the apiUrl during setup', () => { expect(jsonApi.apiUrl).to.eql('http://myapi.com') }) it('should have a empty models and middleware properties after instantiation', () => { expect(jsonApi.models).to.be.an('object') expect(jsonApi.middleware).to.be.an('array') }) it('should initialize with an empty headers object', () => { expect(jsonApi.headers).to.be.an('object') expect(jsonApi.headers).to.eql({}) }) it('should allow users to add headers', () => { jsonApi.headers['A-Header-Name'] = 'a value' expect(jsonApi.headers).to.eql({ 'A-Header-Name': 'a value' }) }) it('should allow users to add HTTP Basic Auth options', (done) => { jsonApi = new JsonApi({apiUrl: 'http://myapi.com', auth: {username: 'admin', password: 'cheesecake'}}) jsonApi.define('foo', {title: ''}) let inspectorMiddleware = { name: 'inspector-middleware', req: (payload) => { expect(payload.req.auth).to.be.eql({username: 'admin', password: 'cheesecake'}) return {} } } const jsonApiHttpBasicAuthMiddleware = require('./../../src/middleware/json-api/req-http-basic-auth') jsonApi.middleware = [jsonApiHttpBasicAuthMiddleware, inspectorMiddleware] jsonApi.one('foo', 1).get().then(() => done()) }) describe('Pluralize options', () => { context('no options passed -- default behavior', () => { it('should use the pluralize package', () => { jsonApi = new JsonApi({apiUrl: 'http://myapi.com'}) expect(jsonApi.pluralize).to.eql(require('pluralize')) }) }) context('false -- no pluralization', () => { it('should not pluralize text', () => { jsonApi = new JsonApi({apiUrl: 'http://myapi.com', pluralize: false}) expect(jsonApi.pluralize('model')).to.eql('model') expect(jsonApi.pluralize.singular('models')).to.eql('models') }) }) context('custom pluralization', () => { it('should pluralize as requested', () => { const pluralizer = s => 'Q' + s pluralizer.singular = s => s.replace(/^Q/, '') jsonApi = new JsonApi({apiUrl: 'http://myapi.com', pluralize: pluralizer}) expect(jsonApi.pluralize('model')).to.eql('Qmodel') expect(jsonApi.pluralize.singular('Qmodel')).to.eql('model') }) }) }) describe('Trailing Slash options', () => { context('no options passed -- default behavior', () => { it('should use the default of no slashes for either url type', () => { jsonApi = new JsonApi({apiUrl: 'http://myapi.com'}) expect(jsonApi.trailingSlash).to.eql({collection: false, resource: false}) }) }) context('option to add slashes to all urls', () => { it('should use slashes for both url types', () => { jsonApi = new JsonApi({apiUrl: 'http://myapi.com', trailingSlash: true}) expect(jsonApi.trailingSlash).to.eql({collection: true, resource: true}) }) }) context('option to add slashes to only collection urls', () => { it('should only use slashes for collection urls', () => { jsonApi = new JsonApi({apiUrl: 'http://myapi.com', trailingSlash: { collection: true }}) expect(jsonApi.trailingSlash).to.eql({collection: true, resource: false}) }) }) context('option to add slashes to only resource urls', () => { it('should only use slashes for resource urls', () => { jsonApi = new JsonApi({apiUrl: 'http://myapi.com', trailingSlash: { resource: true }}) expect(jsonApi.trailingSlash).to.eql({collection: false, resource: true}) }) }) }) it.skip('should throw Exception if the constructor does not receive proper arguments', () => { expect(function () { throw new Error('boom!') }).toThrow(/boom/) }) }) describe('urlFor, pathFor and path builders', () => { context('default no trailing slashes', () => { it('should construct collection paths for models', () => { jsonApi.define('product', {}) expect(jsonApi.collectionPathFor('product')).to.eql('products') }) it('should allow overrides for collection paths', () => { jsonApi.define('product', {}, {collectionPath: 'my-products'}) expect(jsonApi.collectionPathFor('product')).to.eql('my-products') }) it('should allow arbitrary collections without a model', () => { expect(jsonApi.collectionPathFor('foo')).to.eql('foos') }) it('should construct single resource paths for models', () => { jsonApi.define('product', {}) expect(jsonApi.resourcePathFor('product', 1)).to.eql('products/1') }) it('should construct collection urls for models', () => { jsonApi.define('product', {}) expect(jsonApi.collectionUrlFor('product')).to.eql('http://myapi.com/products') }) it('should construct single resource urls for models', () => { jsonApi.define('product', {}) expect(jsonApi.resourceUrlFor('product', 1)).to.eql('http://myapi.com/products/1') }) it('should allow urlFor to be called with various options', () => { expect(jsonApi.urlFor({model: 'foo', id: 1})).to.eql('http://myapi.com/foos/1') expect(jsonApi.urlFor({model: 'foo'})).to.eql('http://myapi.com/foos') expect(jsonApi.urlFor({})).to.eql('http://myapi.com/') expect(jsonApi.urlFor()).to.eql('http://myapi.com/') expect(jsonApi.all('foo').urlFor()).to.eql('http://myapi.com/foos') }) it('should allow pathFor to be called with various options', () => { expect(jsonApi.pathFor({model: 'foo', id: 1})).to.eql('foos/1') expect(jsonApi.pathFor({model: 'foo'})).to.eql('foos') expect(jsonApi.pathFor({})).to.eql('') expect(jsonApi.pathFor()).to.eql('') expect(jsonApi.all('foo').pathFor()).to.eql('foos') }) }) context('with collection and resource trailing slashes', () => { beforeEach(() => { jsonApi = new JsonApi({apiUrl: 'http://myapi.com', trailingSlash: {collection: true, resource: true}}) }) afterEach(() => { jsonApi.resetBuilder() }) it('should construct collection urls', () => { jsonApi.define('product', {}) expect(jsonApi.collectionUrlFor('product')).to.eql('http://myapi.com/products/') }) it('should construct resource urls', () => { jsonApi.define('product', {}) expect(jsonApi.resourceUrlFor('product', 1)).to.eql('http://myapi.com/products/1/') }) it('should construct collection urls with urlFor', () => { expect(jsonApi.urlFor({model: 'foo'})).to.eql('http://myapi.com/foos/') expect(jsonApi.all('foo').urlFor()).to.eql('http://myapi.com/foos/') }) it('should construct complex collection urls with urlFor', () => { expect(jsonApi.urlFor({model: 'foo'})).to.eql('http://myapi.com/foos/') expect(jsonApi.one('bar', '1').all('foo').urlFor()).to.eql('http://myapi.com/bars/1/foos/') }) it('should construct the relationships URL', () => { expect(jsonApi.one('bar', '1').relationships().all('foo').urlFor()).to.eql('http://myapi.com/bars/1/relationships/foos/') }) it('should construct resource urls with urlFor', () => { expect(jsonApi.urlFor({model: 'foo', id: '1'})).to.eql('http://myapi.com/foos/1/') expect(jsonApi.one('foo', '1').urlFor()).to.eql('http://myapi.com/foos/1/') }) it('should construct complex resource urls with urlFor', () => { expect(jsonApi.all('bars').one('foo', '1').urlFor()).to.eql('http://myapi.com/bars/foos/1/') }) }) context('with only collection trailing slashes', () => { beforeEach(() => { jsonApi = new JsonApi({apiUrl: 'http://myapi.com', trailingSlash: {collection: true, resource: false}}) }) afterEach(() => { jsonApi.resetBuilder() }) it('should construct resource urls with urlFor without trailing slashes', () => { expect(jsonApi.urlFor({model: 'foo', id: '1'})).to.eql('http://myapi.com/foos/1') expect(jsonApi.one('foo', '1').urlFor()).to.eql('http://myapi.com/foos/1') }) }) context('with only resource trailing slashes', () => { beforeEach(() => { jsonApi = new JsonApi({apiUrl: 'http://myapi.com', trailingSlash: {collection: false, resource: true}}) }) afterEach(() => { jsonApi.resetBuilder() }) it('should construct collection urls with urlFor without trailing slashes', () => { expect(jsonApi.urlFor({model: 'foo'})).to.eql('http://myapi.com/foos') expect(jsonApi.all('foo').urlFor()).to.eql('http://myapi.com/foos') }) }) }) describe('Middleware', () => { it('should allow users to register middleware', () => { let catMiddleWare = { name: 'cat-middleware', req: function (req) { return req }, res: function (res) { return res } } jsonApi.middleware.unshift(catMiddleWare) expect(jsonApi.middleware[0].name).to.eql('cat-middleware') }) it('should allow users to register middleware before or after existing middleware', () => { let responseMiddleware = jsonApi.middleware.filter(middleware => middleware.name === 'response')[0] let beforeMiddleware = { name: 'before' } let afterMiddleware = { name: 'after' } let index = jsonApi.middleware.indexOf(responseMiddleware) jsonApi.insertMiddlewareBefore('response', beforeMiddleware) jsonApi.insertMiddlewareAfter('response', afterMiddleware) expect(jsonApi.middleware.indexOf(beforeMiddleware)).to.eql(index) expect(jsonApi.middleware.indexOf(afterMiddleware)).to.eql(index + 2) }) }) describe('Models and serializers', () => { it('should expose the serialize and deserialize objects', () => { expect(jsonApi.serialize.collection).to.be.a('function') expect(jsonApi.serialize.resource).to.be.a('function') expect(jsonApi.deserialize.collection).to.be.a('function') expect(jsonApi.deserialize.resource).to.be.a('function') }) it('should allow users to define models', () => { jsonApi.define('product', { id: '', title: '' }) expect(jsonApi.models['product']).to.be.an('object') expect(jsonApi.models['product']['attributes']).to.have.key('id') expect(jsonApi.models['product']['attributes']).to.have.key('title') }) }) describe('Basic API calls', () => { it('should make basic find calls', (done) => { mockResponse(jsonApi, { data: { data: { id: '1', type: 'products', attributes: { title: 'Some Title' } } } }) jsonApi.define('product', { title: '' }) jsonApi.find('product', 1).then(({ data, errors, meta, links }) => { expect(data.id).to.eql('1') expect(data.title).to.eql('Some Title') done() }).catch(err => console.log(err)) }) it('should make basic findAll calls', (done) => { mockResponse(jsonApi, { data: { data: [ { id: '1', type: 'products', attributes: { title: 'Some Title' } }, { id: '2', type: 'products', attributes: { title: 'Another Title' } } ] } }) jsonApi.define('product', { title: '' }) jsonApi.findAll('product').then(({ data, errors, meta, links }) => { expect(data[0].id).to.eql('1') expect(data[0].title).to.eql('Some Title') expect(data[1].id).to.eql('2') expect(data[1].title).to.eql('Another Title') done() }).catch(err => console.log(err)) }) it('should make basic create call', (done) => { let inspectorMiddleware = { name: 'inspector-middleware', req: (payload) => { expect(payload.req.method).to.be.eql('POST') expect(payload.req.url).to.be.eql('http://myapi.com/foos') expect(payload.req.data).to.be.eql({title: 'foo'}) expect(payload.req.params).to.be.eql({include: 'something'}) return {} } } jsonApi.middleware = [inspectorMiddleware] jsonApi.define('foo', { title: '' }) jsonApi.create('foo', {title: 'foo'}, {include: 'something'}) .then(() => done()).catch(() => done()) }) it('should make basic update call', (done) => { let inspectorMiddleware = { name: 'inspector-middleware', req: (payload) => { expect(payload.req.method).to.be.eql('PATCH') expect(payload.req.url).to.be.eql('http://myapi.com/foos') expect(payload.req.data).to.be.eql({title: 'foo'}) expect(payload.req.params).to.be.eql({include: 'something'}) return {} } } jsonApi.middleware = [inspectorMiddleware] jsonApi.define('foo', { title: '' }) jsonApi.update('foo', {title: 'foo'}, {include: 'something'}) .then(() => done()).catch(() => done()) }) it('should include meta information on response objects', (done) => { mockResponse(jsonApi, { data: { meta: { totalObjects: 1 }, data: [{ id: '1', type: 'products', attributes: { title: 'Some Title' } }] } }) jsonApi.define('product', { title: '' }) jsonApi.findAll('product').then(({ data, errors, meta, links }) => { expect(meta.totalObjects).to.eql(1) expect(data[0].id).to.eql('1') expect(data[0].title).to.eql('Some Title') done() }).catch(err => console.log(err)) }) it('should include meta information on response data objects', (done) => { mockResponse(jsonApi, { data: { meta: { totalObjects: 1 }, data: [{ id: '1', type: 'products', attributes: { title: 'Some Title' }, meta: { totalAttributes: 1 } }] } }) jsonApi.define('product', { title: '' }) jsonApi.findAll('product').then(({ data, errors, meta, links }) => { expect(data[0].meta.totalAttributes).to.eql(1) expect(data[0].id).to.eql('1') expect(data[0].title).to.eql('Some Title') done() }).catch(err => console.log(err)) }) it('should include links information on response objects', (done) => { mockResponse(jsonApi, { data: { meta: { totalObjects: 13 }, data: [{ id: '1', type: 'products', attributes: { title: 'Some Title' } }], links: { self: 'http://example.com/products?page[number]=3&page[size]=1', first: 'http://example.com/products?page[number]=1&page[size]=1', prev: 'http://example.com/products?page[number]=2&page[size]=1', next: 'http://example.com/products?page[number]=4&page[size]=1', last: 'http://example.com/products?page[number]=13&page[size]=1' } } }) jsonApi.define('product', { title: '' }) jsonApi.findAll('product').then(({ data, errors, meta, links }) => { expect(links.self).to.eql('http://example.com/products?page[number]=3&page[size]=1') expect(links.first).to.eql('http://example.com/products?page[number]=1&page[size]=1') expect(links.prev).to.eql('http://example.com/products?page[number]=2&page[size]=1') expect(links.next).to.eql('http://example.com/products?page[number]=4&page[size]=1') expect(links.last).to.eql('http://example.com/products?page[number]=13&page[size]=1') expect(data[0].id).to.eql('1') expect(data[0].title).to.eql('Some Title') done() }).catch(err => console.log(err)) }) it('should include links information on response data objects', (done) => { mockResponse(jsonApi, { data: { meta: { totalObjects: 13 }, data: [{ id: '1', type: 'products', attributes: { title: 'Some Title' }, links: { self: 'http://example.com/products/1' } }], links: { self: 'http://example.com/products?page[number]=3&page[size]=1', first: 'http://example.com/products?page[number]=1&page[size]=1', prev: 'http://example.com/products?page[number]=2&page[size]=1', next: 'http://example.com/products?page[number]=4&page[size]=1', last: 'http://example.com/products?page[number]=13&page[size]=1' } } }) jsonApi.define('product', { title: '' }) jsonApi.findAll('product').then(({ data, errors, meta, links }) => { expect(data[0].links.self).to.eql('http://example.com/products/1') expect(data[0].id).to.eql('1') expect(data[0].title).to.eql('Some Title') done() }).catch(err => console.log(err)) }) it('should include errors information on response objects', (done) => { mockResponse(jsonApi, { data: { data: [{ id: '1', type: 'products', attributes: { title: 'Some Title' } }], errors: [ { status: 422, source: { pointer: '/data/attributes/first-name' }, title: 'Invalid Attribute', detail: 'First name must contain at least three characters.' } ] } }) jsonApi.define('product', { title: '' }) jsonApi.findAll('product').then(({ data, errors, meta, links }) => { expect(errors[0].status).to.eql('422') expect(errors[0].source.pointer).to.eql('/data/attributes/first-name') expect(errors[0].title).to.eql('Invalid Attribute') expect(errors[0].detail).to.eql('First name must contain at least three characters.') expect(data[0].id).to.eql('1') expect(data[0].title).to.eql('Some Title') done() }).catch(err => console.log(err)) }) it('should expose a method for arbitrary HTTP calls', () => { const url = 'https://example.com' const method = 'PATCH' const params = { id: 3 } const data = { body: 'something different' } jsonApi.runMiddleware = sinon.spy() jsonApi.request(url, method, params, data) expect(jsonApi.runMiddleware.called).to.be.truthy expect(jsonApi.runMiddleware.calledWith(url, method, params, data)).to.be.truthy }) it('should handle null primary data', (done) => { mockResponse(jsonApi, { data: { data: null } }) jsonApi.define('product', { title: '' }) jsonApi.find('product', 1).then(({ data, errors, meta, links }) => { expect(data).to.eql(null) done() }).catch(err => console.log(err)) }) it('should have an empty body on GET requests', (done) => { let inspectorMiddleware = { name: 'inspector-middleware', req: (payload) => { expect(payload.req.method).to.be.eql('GET') expect(payload.req.data).to.be(undefined) expect(payload.req.url).to.be.eql('http://myapi.com/foos/1') return {} } } jsonApi.middleware = [jsonApiGetMiddleware, inspectorMiddleware] jsonApi.one('foo', 1).find().then(() => done()).catch(() => done()) }) it('should have an empty body on DELETE requests', (done) => { let inspectorMiddleware = { name: 'inspector-middleware', req: (payload) => { expect(payload.req.method).to.be.eql('DELETE') expect(payload.req.data).to.be(undefined) expect(payload.req.url).to.be.eql('http://myapi.com/foos/1') return {} } } jsonApi.middleware = [jsonApiDeleteMiddleware, inspectorMiddleware] jsonApi.destroy('foo', 1).then(() => done()).catch(() => done()) }) it('should accept a data payload on DELETE requests when provided as a single argument', (done) => { let inspectorMiddleware = { name: 'inspector-middleware', req: (payload) => { expect(payload.req.method).to.be.eql('DELETE') expect(payload.req.data).to.be.an('object') expect(payload.req.data.data).to.be.an('array') expect(payload.req.url).to.be.eql('http://myapi.com/foos/1/relationships/bars') return {} } } jsonApi.middleware = [jsonApiDeleteMiddleware, inspectorMiddleware] const payload = [ {type: 'bar', id: 2}, {type: 'bar', id: 3} ] jsonApi.one('foo', 1).relationships().all('bar').destroy(payload).then(() => done()).catch(() => done()) }) it.skip('should throw an error while attempting to access undefined model', function (done) { expect(function () { jsonApi.findAll('derp').then(done).catch(done) }).to.throwException(/API resource definition for model/) }) }) describe('Complex API calls', () => { it('should work on bidirectional connected entities', (done) => { mockResponse(jsonApi, { data: { data: { id: '1', type: 'product', attributes: { title: 'Some Title' }, relationships: { company: { data: { type: 'company', id: '42' } } } }, included: [{ type: 'company', id: '42', attributes: { brand: 'Cool Company' }, relationships: { products: { data: [{ type: 'product', id: '1' }, { type: 'product', id: '2' }] } } }, { id: '1', type: 'product', attributes: { title: 'Some Title' }, relationships: { company: { data: { type: 'company', id: '42' } } } } ] } }) jsonApi.define('product', { title: '', company: { jsonApi: 'hasOne', type: 'company' } }) jsonApi.define('company', { brand: '', products: { jsonApi: 'hasMany', type: 'product' } }) jsonApi.find('product', 42, { include: 'company,company.products' }).then(({ data, errors, meta, links }) => { expect(data.id).to.eql('1') expect(data.title).to.eql('Some Title') expect(data.company.id).to.eql('42') expect(data.company.brand).to.eql('Cool Company') expect(data.company.products[0].id).to.eql('1') expect(data.company.products[0].title).to.eql('Some Title') done() }).catch(err => console.log(err)) }) it('should not cache the second request', (done) => { mockResponse(jsonApi, { data: { data: [{ id: '42', type: 'clan', attributes: { title: 'MyClan' }, relationships: { leader: { data: { type: 'player', id: '5' } }, memberships: { data: [{ type: 'clanMembership', id: '15' }, { type: 'clanMembership', id: '16' }] } } }], included: [{ type: 'clanMembership', id: '15', relationships: { clan: { data: { type: 'clan', id: '42' } }, player: { data: { type: 'player', id: '5' } } } }, { type: 'clanMembership', id: '16', relationships: { clan: { data: { type: 'clan', id: '42' } }, player: { data: { type: 'player', id: '6' } } } }, { type: 'player', id: '5', attributes: { name: 'Dragonfire' } }] } }) jsonApi.define('clan', { title: '', leader: { jsonApi: 'hasOne', type: 'player' }, memberships: { jsonApi: 'hasMany', type: 'clanMembership' } }) jsonApi.define('clanMembership', { clan: { jsonApi: 'hasOne', type: 'clan' }, player: { jsonApi: 'hasOne', type: 'player' } }) jsonApi.define('player', { name: '' }) jsonApi.findAll('clan', { include: 'memberships' }).then(({ data, errors, meta, links }) => { // console.log('request 1', clans); // console.log('memberships', clans[0].memberships); expect(data[0].memberships.length).to.eql(2) // expect(clans[0].memberships[0].clan.id).to.eql("42") // expect(clans[0].memberships[1].clan.id).to.eql("42") // second request mockResponse(jsonApi, { data: { data: { id: '42', type: 'clan', attributes: { title: 'MyClan' }, relationships: { memberships: { data: [{ type: 'clanMembership', id: '15' }, { type: 'clanMembership', id: '16' }] } } }, included: [{ type: 'clanMembership', id: '15', relationships: { clan: { data: { type: 'clan', id: '42' } }, player: { data: { type: 'player', id: '5' } } } }, { type: 'clanMembership', id: '16', relationships: { clan: { data: { type: 'clan', id: '42' } }, player: { data: { type: 'player', id: '6' } } } }, { type: 'player', id: '5', attributes: { name: 'Dragonfire' } }, { type: 'player', id: '6', attributes: { name: 'nicooga' } }] } }) jsonApi.find('clan', 42, { include: 'memberships,memberships.player' }).then(({ data, errors, meta, links }) => { // console.log('request 2: ', clan); expect(data.memberships[0].player.name).to.eql('Dragonfire') // expect(clan.memberships[0].clan.id).to.eql('42') expect(data.memberships[1].player.name).to.eql('nicooga') // expect(clan.memberships[1].clan.id).to.eql('42') done() }).catch(err => console.log(err)) }).catch(err => console.log(err)) }) }) describe('Builder pattern for route construction', () => { beforeEach(() => { jsonApi.define('foo', {title: ''}) jsonApi.define('bar', {title: ''}) jsonApi.define('baz', {title: ''}) }) it('should respect resetBuilderOnCall', (done) => { let inspectorMiddleware = { name: 'inspector-middleware', req: (payload) => { expect(payload.req.method).to.be.eql('GET') expect(payload.req.url).to.be.eql('http://myapi.com/') return {} } } jsonApi.middleware = [inspectorMiddleware] jsonApi.get() .then(() => { let inspectorMiddleware = { name: 'inspector-middleware', req: (payload) => { expect(payload.req.method).to.be.eql('GET') expect(payload.req.url).to.be.eql('http://myapi.com/foos') return {} } } jsonApi.middleware = [inspectorMiddleware] return jsonApi.all('foo').get() }) .then(() => done()) .catch(() => done()) expect(jsonApi.buildUrl()).to.eql('http://myapi.com/') }) it('should respect resetBuilderOnCall when it is disabled', (done) => { jsonApi = new JsonApi({apiUrl: 'http://myapi.com', resetBuilderOnCall: false}) let inspectorMiddleware = { name: 'inspector-middleware', req: (payload) => { expect(payload.req.method).to.be.eql('GET') expect(payload.req.url).to.be.eql('http://myapi.com/foos/1') return {} } } jsonApi.middleware = [inspectorMiddleware] jsonApi.one('foo', 1).get().then(() => { let inspectorMiddleware = { name: 'inspector-middleware', req: (payload) => { expect(payload.req.method).to.be.eql('GET') expect(payload.req.url).to.be.eql('http://myapi.com/foos/1/bars') return {} } } jsonApi.middleware = [inspectorMiddleware] return jsonApi.all('bar').get().then(() => done()) }).catch(() => done()) }) it('should allow builders to be used', () => { expect(jsonApi.buildUrl()).to.eql('http://myapi.com/') }) it('should allow builders on all', () => { expect(jsonApi.all('foo').all('bar').all('baz').pathFor()).to.eql('foos/bars/bazs') jsonApi.resetBuilder() expect(jsonApi.all('foos').all('bars').all('bazs').pathFor()).to.eql('foos/bars/bazs') }) it('should allow builders on one', () => { expect(jsonApi.one('foo', 1).one('bar', 2).one('baz', 3).pathFor()).to.eql('foos/1/bars/2/bazs/3') jsonApi.resetBuilder() expect(jsonApi.one('foos', 1).one('bars', 2).one('bazs', 3).pathFor()).to.eql('foos/1/bars/2/bazs/3') }) it('should allow builders on all and one', () => { expect(jsonApi.one('foo', 1).one('bar', 2).all('baz').pathFor()).to.eql('foos/1/bars/2/bazs') jsonApi.resetBuilder() expect(jsonApi.one('foos', 1).one('bars', 2).all('bazs').pathFor()).to.eql('foos/1/bars/2/bazs') }) it('should allow builders to be called to the base url', (done) => { mockResponse(jsonApi, { data: { data: [ { id: '1', type: 'foo', attributes: { title: 'foo 1' } } ] } }) jsonApi.get().then(({ data, errors, meta, links }) => { expect(data[0].id).to.eql('1') expect(data[0].title).to.eql('foo 1') done() }).catch(err => console.log(err)) }) it('should allow builders to be called with get', (done) => { let inspectorMiddleware = { name: 'inspector-middleware', req: (payload) => { expect(payload.req.method).to.be.eql('GET') expect(payload.req.url).to.be.eql('http://myapi.com/') return {} } } jsonApi.middleware = [inspectorMiddleware] jsonApi.get().then(() => done()).catch(() => done()) }) it('should allow builders to be called with get with query params', (done) => { let inspectorMiddleware = { name: 'inspector-middleware', req: (payload) => { expect(payload.req.method).to.be.eql('GET') expect(payload.req.url).to.be.eql('http://myapi.com/') expect(payload.req.params).to.be.eql({page: {number: 2}}) return {} } } jsonApi.middleware = [inspectorMiddleware] jsonApi.get({page: {number: 2}}).then(() => done()).catch(() => done()) }) it('should allow builders to be called with get on all', (done) => { let inspectorMiddleware = { name: 'inspector-middleware', req: (payload) => { expect(payload.req.method).to.be.eql('GET') expect(payload.req.url).to.be.eql('http://myapi.com/foos') return {} } } jsonApi.middleware = [inspectorMiddleware] jsonApi.all('foo').get().then(() => done()).catch(() => done()) }) it('should allow builders to be called with get on one', (done) => { let inspectorMiddleware = { name: 'inspector-middleware', req: (payload) => { expect(payload.req.method).to.be.eql('GET') expect(payload.req.url).to.be.eql('http://myapi.com/foos/1') return {} } } jsonApi.middleware = [inspectorMiddleware] jsonApi.one('foo', 1).get().then(() => done()).catch(() => done()) }) it('should allow builders to be called with post', (done) => { let inspectorMiddleware = { name: 'inspector-middleware', req: (payload) => { expect(payload.req.method).to.be.eql('POST') expect(payload.req.url).to.be.eql('http://myapi.com/foos') expect(payload.req.data).to.be.eql({title: 'foo'}) return {} } } jsonApi.middleware = [inspectorMiddleware] jsonApi.all('foo').post({title: 'foo'}).then(() => done()).catch(() => done()) }) it('should allow builders to be called with post with nested one', (done) => { let inspectorMiddleware = { name: 'inspector-middleware', req: (payload) => { expect(payload.req.method).to.be.eql('POST') expect(payload.req.url).to.be.eql('http://myapi.com/foos/1/bars') expect(payload.req.data).to.be.eql({title: 'foo'}) return {} } } jsonApi.middleware = [inspectorMiddleware] jsonApi.one('foo', 1).all('bar').post({title: 'foo'}).then(() => done()).catch(() => done()) }) it('should allow builders to be called with patch', (done) => { let inspectorMiddleware = { name: 'inspector-middleware', req: (payload) => { expect(payload.req.method).to.be.eql('PATCH') expect(payload.req.url).to.be.eql('http://myapi.com/foos/1') expect(payload.req.data).to.be.eql({title: 'bar'}) return {} } } jsonApi.middleware = [inspectorMiddleware] jsonApi.one('foo', 1).patch({title: 'bar'}).then(() => done()).catch(() => done()) }) it('should allow builders to be called with patch with nested one', (done) => { let inspectorMiddleware = { name: 'inspector-middleware', req: (payload) => { expect(payload.req.method).to.be.eql('PATCH') expect(payload.req.url).to.be.eql('http://myapi.com/foos/1/bars') expect(payload.req.data).to.be.eql({title: 'bar'}) return {} } } jsonApi.middleware = [inspectorMiddleware] jsonApi.one('foo', 1).all('bar').patch({title: 'bar'}).then(() => done()).catch(() => done()) }) it('should allow builders to be called with destroy', (done) => { let inspectorMiddleware = { name: 'inspector-middleware', req: (payload) => { expect(payload.req.method).to.be.eql('DELETE') expect(payload.req.url).to.be.eql('http://myapi.com/foos/1') return {} } } jsonApi.middleware = [inspectorMiddleware] jsonApi.one('foo', 1).destroy().then(() => done()).catch(() => done()) }) it('should allow builders to be called with destroy with nested one', (done) => { let inspectorMiddleware = { name: 'inspector-middleware', req: (payload) => { expect(payload.req.method).to.be.eql('DELETE') expect(payload.req.url).to.be.eql('http://myapi.com/foos/1/bars/2') return {} } } jsonApi.middleware = [inspectorMiddleware] jsonApi.one('foo', 1).one('bar', 2).destroy().then(() => done()).catch(() => done()) }) it('should Wacky Waving Inflatable Arm-Flailing Tubeman! Wacky Waving Inflatable Arm-Flailing Tubeman! Wacky Waving Inflatable Arm-Flailing Tubeman!', () => { jsonApi.one('foo', 1).one('bar', 2).all('foo').one('bar', 3).all('baz').one('baz', 1).one('baz', 2).one('baz', 3) expect(jsonApi.pathFor()).to.be.eql('foos/1/bars/2/foos/bars/3/bazs/bazs/1/bazs/2/bazs/3') expect(jsonApi.urlFor()).to.be.eql('http://myapi.com/foos/1/bars/2/foos/bars/3/bazs/bazs/1/bazs/2/bazs/3') }) }) })