From ff10ccdfb801cd6546318b48932c511edeeb45ba Mon Sep 17 00:00:00 2001 From: Izel Nakri Date: Sun, 29 Oct 2017 20:08:04 +0100 Subject: [PATCH] MemServer.Server route shortcuts tests done --- lib/mem-server/model.js | 10 +- lib/mem-server/pretender-hacks.js | 41 +++--- lib/mem-server/server.js | 64 +++++---- test/mem-server.server.js | 228 ++++++++++++++---------------- 4 files changed, 176 insertions(+), 167 deletions(-) diff --git a/lib/mem-server/model.js b/lib/mem-server/model.js index 7da97b3..16f9183 100644 --- a/lib/mem-server/model.js +++ b/lib/mem-server/model.js @@ -152,12 +152,20 @@ export default function(options) { return this.serialize(objectOrArray); }, serialize(object) { // NOTE: add links object ? + const objectWithAllAttributes = this.attributes.reduce((result, attribute) => { + if (result[attribute] === undefined) { + result[attribute] = null; + } + + return object; + }, object); + return Object.keys(this.embedReferences).reduce((result, embedKey) => { const embedModel = this.embedReferences[embedKey]; const embeddedRecords = this.getRelationship(object, embedKey, embedModel); return Object.assign(result, { [embedKey]: embedModel.serializer(embeddedRecords) }); - }, object); + }, objectWithAllAttributes); }, getRelationship(parentObject, relationshipName, relationshipModel) { if (Array.isArray(parentObject)) { diff --git a/lib/mem-server/pretender-hacks.js b/lib/mem-server/pretender-hacks.js index 24cccc2..266a613 100644 --- a/lib/mem-server/pretender-hacks.js +++ b/lib/mem-server/pretender-hacks.js @@ -36,22 +36,20 @@ window.Pretender.prototype.handleRequest = function(request) { var pretender = this; var verb = request.method.toUpperCase(); var path = request.url; - var handler = this._handlerFor(verb, path, request); + var handler = pretender._handlerFor(verb, path, request); var _handleRequest = function(result) { - var statusCode, headers, body; - if (Array.isArray(result) && result.length === 3) { - statusCode = result[0], - headers = pretender.prepareHeaders(result[1]), - body = pretender.prepareBody(result[2], headers); + var statusCode = result[0]; + var headers = pretender.prepareHeaders(result[1]); + var body = pretender.prepareBody(result[2], headers); return pretender.handleResponse(request, async, function() { request.respond(statusCode, headers, body); pretender.handledRequest(verb, path, request); }) } else if (!result) { - headers = pretender.prepareHeaders({ 'Content-Type': 'application/json' }); + var headers = pretender.prepareHeaders({ 'Content-Type': 'application/json' }); if (verb === 'DELETE') { return pretender.handleResponse(request, async, function() { @@ -68,11 +66,10 @@ window.Pretender.prototype.handleRequest = function(request) { }); } + var statusCode = getDefaultStatusCode(verb); + var headers = pretender.prepareHeaders({ 'Content-Type': 'application/json' }); var targetResult = typeof result === 'string' ? result : JSON.stringify(result); - - statusCode = getDefaultStatusCode(verb); - headers = pretender.prepareHeaders({ 'Content-Type': 'application/json' }); - body = pretender.prepareBody(targetResult, headers); + var body = pretender.prepareBody(targetResult, headers); return pretender.handleResponse(request, async, function() { request.respond(statusCode, headers, body); @@ -81,8 +78,8 @@ window.Pretender.prototype.handleRequest = function(request) { } if (handler) { - handler.handler.numberOfCalls++; var async = handler.handler.async; + handler.handler.numberOfCalls++; this.handledRequests.push(request); var result = handler.handler(request); @@ -98,17 +95,17 @@ window.Pretender.prototype.handleRequest = function(request) { this.unhandledRequest(verb, path, request); } } +} - function getDefaultStatusCode(verb) { - if (['GET', 'PUT', 'PATCH'].includes(verb)) { - return 200; - } else if (verb === 'POST') { - return 201; - } else if (verb === 'DELETE') { - return 204; - } - - return 500; +function getDefaultStatusCode(verb) { + if (['GET', 'PUT', 'PATCH'].includes(verb)) { + return 200; + } else if (verb === 'POST') { + return 201; + } else if (verb === 'DELETE') { + return 204; } + + return 500; } // END: Pretender Response Defaults UX Hack diff --git a/lib/mem-server/server.js b/lib/mem-server/server.js index f47d9ce..2d59b42 100644 --- a/lib/mem-server/server.js +++ b/lib/mem-server/server.js @@ -1,4 +1,9 @@ import chalk from 'chalk'; +import { classify, underscore } from 'ember-cli-string-utils'; + +const { pluralize, singularize } = require('i')(); // NOTE: move to ES6 imports + +const targetNamespace = typeof global === 'object' ? global : window; export default function(options) { const Server = require(`${process.cwd()}/memserver/server`).default; // NOTE: make this ES6 import @@ -8,7 +13,7 @@ export default function(options) { window.Pretender.prototype[verb] = function (path, handler, async) { const { urlPrefix, namespace } = options; // NOTE: this doesnt allow for this.namespace declaration in the server.js const fullPath = (urlPrefix || '') + (namespace ? ('/' + namespace) : '') + path; - const targetHandler = handler || getDefaultRouteHandler(verb, fullPath); + const targetHandler = handler || getDefaultRouteHandler(verb.toUpperCase(), fullPath); return this.register(verb.toUpperCase(), fullPath, targetHandler, async || options.timing); } @@ -43,52 +48,61 @@ export default function(options) { return pretender; } -function colorStatusCode(statusCode) { - if (statusCode === 200 || statusCode === 201) { - return chalk.green(statusCode); - } else if (statusCode === 404 || statusCode === 204) { - return chalk.cyan(statusCode); - } - - return chalk.red(statusCode); -} - function getDefaultRouteHandler(verb, path) { const paths = path.split(/\//g); - const resourceReference = paths[paths.length - 1]; - console.log('resourceReference'); - console.log(resourceReference); + const lastPath = paths[paths.length - 1]; - const ResourceModel = undefined; // TODO: change this - // TODO: if resourceModel not found throw error? + // TODO: if resourceModel not found throw error? with tests if (verb === 'GET') { - if (resourceReference.includes(':')) { + if (lastPath.includes(':')) { return (request) => { - const resourceKey = undefined; // TODO + const resourceKey = singularize(paths[paths.length - 2]); + const ResourceModel = targetNamespace.MemServer.Models[classify(resourceKey)]; - return { [resourceKey]: ResourceModel.serialize(ResourceModel.find(request.params.id)) }; + return { [resourceKey]: ResourceModel.serializer(ResourceModel.find(request.params.id)) }; } } return (request) => { - return { [resourceReference]: ResourceModel.serialize(ResourceModel.findAll()) }; + const ResourceModel = targetNamespace.MemServer.Models[classify(singularize(lastPath))]; + + return { [lastPath]: ResourceModel.serializer(ResourceModel.findAll()) }; }; } else if (verb === 'POST') { return (request) => { - return { [resourceReference]: ResourceModel.serialize(ResourceModel.insert(request.params)) }; + const resourceKey = singularize(lastPath); + const ResourceModel = targetNamespace.MemServer.Models[classify(resourceKey)]; + const resourceParams = request.params[resourceKey]; + + return { [resourceKey]: ResourceModel.serializer(ResourceModel.insert(resourceParams)) }; }; } else if (verb === 'PUT') { return (request) => { - const resourceKey = undefined; // TODO + const resourceKey = singularize(paths[paths.length - 2]); + const ResourceModel = targetNamespace.MemServer.Models[classify(resourceKey)]; + const resourceParams = request.params[resourceKey]; - return { [resourceKey]: ResourceModel.serialize(ResourceModel.update(request.params)) }; + return { [resourceKey]: ResourceModel.serializer(ResourceModel.update(resourceParams)) }; }; } else if (verb === 'DELETE') { - return (request) => { ResourceModel.delete(request.params) }; + const resourceKey = singularize(paths[paths.length - 2]); + const ResourceModel = targetNamespace.MemServer.Models[classify(resourceKey)]; + + return (request) => { ResourceModel.delete({ id: request.params.id }) }; } } +function colorStatusCode(statusCode) { + if (statusCode === 200 || statusCode === 201) { + return chalk.green(statusCode); + } else if (statusCode === 404 || statusCode === 204) { + return chalk.cyan(statusCode); + } + + return chalk.red(statusCode); +} + // TODO: // const defaultPassthroughs = [ @@ -96,8 +110,6 @@ function getDefaultRouteHandler(verb, path) { // 'http://localhost:30820/socket.io' // electron // ]; -// NOTE: maybe do { trackRequests: false } - // MOTE: Check/test that only routes defined after this.namespace are affected. // This is useful if you have a few one-off routes that you don’t want under your namespace: diff --git a/test/mem-server.server.js b/test/mem-server.server.js index 1803295..3233162 100644 --- a/test/mem-server.server.js +++ b/test/mem-server.server.js @@ -1,4 +1,3 @@ -// TODO: test Response first const assert = require('assert'); const fs = require('fs'); const rimraf = require('rimraf'); @@ -120,122 +119,115 @@ describe('MemServer.Server functionality', function() { done(); }); - // describe('route shortcuts work', function() { - // before(function() { - // fs.writeFileSync(`${process.cwd()}/memserver/server.js`, ` - // export default function(Models) { - // this.post('/photos'); - // this.get('/photos'); - // this.get('/photos/:id'); - // this.put('/photos/:id'); - // this.delete('/photos/:id'); - // - // } - // `); - // }); - // NOTE: add this.resource and this.passthrough here - - // it('POST /resources work with shortcut', function(done) { - // const MemServer = require('../index.js'); - // const { Photo } = MemServer.Models; - // - // MemServer.start(); - // window.$.ajaxSetup({ headers: { 'Content-Type': 'application/json' } }); - // - // assert.equal(Photo.count(), 3); - // - // window.$.ajax({ - // type: 'POST', url: '/photos', headers: { 'Content-Type': 'application/json' } - // }, (data, textStatus, jqXHR) => { - // assert.equal(jqXHR.status, 201); - // assert.deepEqual(data, { is_public: true, name: 'Some default name', id: 4, user_id: 1 }); - // assert.equal(Photo.count(), 4); - // - // done(); - // }); - // }); + describe('route shortcuts work', function() { + before(function() { + fs.writeFileSync(`${process.cwd()}/memserver/server.js`, ` + export default function(Models) { + this.post('/photos'); + this.get('/photos'); + this.get('/photos/:id'); + this.put('/photos/:id'); + this.delete('/photos/:id'); - // it('GET /resources works with shortcut', function(done) { - // const MemServer = require('../index.js'); - // const { Photo } = MemServer.Models; - // - // MemServer.start(); - // window.$.ajaxSetup({ headers: { 'Content-Type': 'application/json' } }); - // - // assert.equal(Photo.count(), 3); - // - // window.$.getJSON('/photos', (data, textStatus, jqXHR) => { - // assert.equal(jqXHR.status, 200); - // assert.deepEqual(data, { photos: Photo.serializer(Photo.findAll()) }); - // assert.equal(Photo.count(), 3); - // - // done(); - // }); - // }); - // - // it('GET /resources/:id works with shortcut', function(done) { - // const MemServer = require('../index.js'); - // const { Photo } = MemServer.Models; - // - // MemServer.start(); - // window.$.ajaxSetup({ headers: { 'Content-Type': 'application/json' } }); - // - // window.$.getJSON('/photos/1', (data, textStatus, jqXHR) => { - // assert.equal(jqXHR.status, 200); - // assert.deepEqual(data, { photos: Photo.serializer(Photo.find(1)) }); - // - // done(); - // }); - // }); - // - // it('PUT /resources/:id works with shortcut', function(done) { - // const MemServer = require('../index.js'); - // const { Photo } = MemServer.Models; - // - // MemServer.start(); - // window.$.ajaxSetup({ headers: { 'Content-Type': 'application/json' } }); - // - // assert.equal(Photo.find(1).name, 'Ski trip') - // - // window.$.ajax({ - // type: 'PUT', - // url: '/photos/1', - // headers: { 'Content-Type': 'application/json' }, - // data: { photo: { id: 1, title: 'New custom title'} } - // }, (data, textStatus, jqXHR) => { - // const photo = Photo.find(1); - // - // assert.equal(jqXHR.status, 200); - // assert.deepEqual(data, { photo: Photo.serializer(photo) }); - // assert.equal(photo.name, 'New custom title'); - // - // done(); - // }); - // }); - // - // it('DELETE /resources/:id works with shortcut', function(done) { - // const MemServer = require('../index.js'); - // const { Photo } = MemServer.Models; - // - // MemServer.start(); - // window.$.ajaxSetup({ headers: { 'Content-Type': 'application/json' } }); - // - // assert.equal(Photo.count(), 3); - // - // window.$.ajax({ - // type: 'DELETE', - // url: '/photos/1', - // headers: { 'Content-Type': 'application/json' } - // }, (data, textStatus, jqXHR) => { - // assert.equal(jqXHR.status, 204); - // assert.deepEqual(data, {}); - // assert.equal(Photo.count(), 2); - // assert.equal(PHoto.find(1), undefined); - // - // done(); - // }); - // }); - // }); + } + `); + }); // TODO: add this.resource and this.passthrough here + + it('POST /resources work with shortcut', async function() { + this.timeout(5000); + + const MemServer = require('../index.js'); + const { Photo } = MemServer.Models; + + MemServer.start(); + + assert.equal(Photo.count(), 3); + + await window.$.ajax({ + type: 'POST', url: '/photos', headers: { 'Content-Type': 'application/json' }, + data: JSON.stringify({ photo: { name: 'Izel Nakri' }}) + }).then((data, textStatus, jqXHR) => { + assert.equal(jqXHR.status, 201); + assert.deepEqual(data, { photo: Photo.serializer(Photo.find(4)) }); + assert.equal(Photo.count(), 4); + assert.deepEqual(Photo.find(4), { + id: 4, name: 'Izel Nakri', is_public: true, href: null, user_id: null + }) + }); + }); + + it('GET /resources works with shortcut', async function() { + const MemServer = require('../index.js'); + const { Photo } = MemServer.Models; + + MemServer.start(); + + assert.equal(Photo.count(), 3); + + await window.$.ajax({ + type: 'GET', url: '/photos', headers: { 'Content-Type': 'application/json' } + }).then((data, textStatus, jqXHR) => { + assert.equal(jqXHR.status, 200); + assert.deepEqual(data, { photos: Photo.serializer(Photo.findAll()) }); + assert.equal(Photo.count(), 3); + }); + }); + + it('GET /resources/:id works with shortcut', async function() { + const MemServer = require('../index.js'); + const { Photo } = MemServer.Models; + + MemServer.start(); + + await window.$.ajax({ + type: 'GET', url: '/photos/1', headers: { 'Content-Type': 'application/json' } + }).then((data, textStatus, jqXHR) => { + assert.equal(jqXHR.status, 200); + assert.deepEqual(data, { photo: Photo.serializer(Photo.find(1)) }); + }); + }); + + it('PUT /resources/:id works with shortcut', async function() { + const MemServer = require('../index.js'); + const { Photo } = MemServer.Models; + + MemServer.start(); + + assert.equal(Photo.find(1).name, 'Ski trip') + + await window.$.ajax({ + type: 'PUT', url: '/photos/1', headers: { 'Content-Type': 'application/json' }, + data: JSON.stringify({ photo: { id: 1, name: 'New custom title'} }) + }, (data, textStatus, jqXHR) => { + const photo = Photo.find(1); + + assert.equal(jqXHR.status, 200); + assert.deepEqual(data, { photo: Photo.serializer(photo) }); + assert.equal(photo.name, 'New custom title'); + }); + }); + + it('DELETE /resources/:id works with shortcut', async function() { + const MemServer = require('../index.js'); + const { Photo } = MemServer.Models; + + MemServer.start(); + window.$.ajaxSetup({ headers: { 'Content-Type': 'application/json' } }); + + assert.equal(Photo.count(), 3); + + await window.$.ajax({ + type: 'DELETE', + url: '/photos/1', + headers: { 'Content-Type': 'application/json' } + }, (data, textStatus, jqXHR) => { + assert.equal(jqXHR.status, 204); + assert.deepEqual(data, {}); + assert.equal(Photo.count(), 2); + assert.equal(PHoto.find(1), undefined); + }); + }); + }); describe('server can process custom headers and responses', function() { before(function() { @@ -326,7 +318,7 @@ describe('MemServer.Server functionality', function() { }).then((data, textStatus, jqXHR) => { assert.equal(jqXHR.status, 201); assert.deepEqual(data, { - photo: { is_public: true, name: 'Some default name', id: 4, user_id: 1 } + photo: { is_public: true, name: 'Some default name', id: 4, user_id: 1, href: null } }); }); }); @@ -512,5 +504,5 @@ describe('MemServer.Server functionality', function() { // }); // TODO: passthrough works - // NOTE: by default returning undefined should return Response(500) ? + // TODO: by default returning undefined should return Response(500) ? });