From ac746a22417af98e7dd003542c0c307ec9abc323 Mon Sep 17 00:00:00 2001 From: Camel Aissani Date: Sat, 2 Jul 2016 15:45:54 +0200 Subject: [PATCH] added support of request with querystring and or anchor --- .gitignore | 3 +- lib/application.js | 3 +- lib/requester.js | 23 +++++++++---- lib/router.js | 35 ++++++++++++++----- test/requester-test.js | 77 ++++++++++++++++++++++++++++-------------- test/router-test.js | 76 +++++++++++++++++++++++++++++++++++------ 6 files changed, 164 insertions(+), 53 deletions(-) diff --git a/.gitignore b/.gitignore index 3091757..a4916e6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ node_modules -coverage \ No newline at end of file +coverage +dist \ No newline at end of file diff --git a/lib/application.js b/lib/application.js index 9fea60d..12da116 100755 --- a/lib/application.js +++ b/lib/application.js @@ -174,7 +174,6 @@ Object.keys(HTTP_METHODS).reduce((reqProto, method) => { } // HTTP methods - const transformer = HTTP_METHODS[method]; const httpMethodName = 'http'+method.charAt(0).toUpperCase() + method.slice(1).toLowerCase(); reqProto[httpMethodName] = function(request, resolve, reject) { let {uri, headers, data} = request; @@ -185,7 +184,7 @@ Object.keys(HTTP_METHODS).reduce((reqProto, method) => { uri, method, headers, - data, + data }, resolve, reject); } diff --git a/lib/requester.js b/lib/requester.js index ee31636..014a679 100755 --- a/lib/requester.js +++ b/lib/requester.js @@ -4,15 +4,26 @@ export const HTTP_METHODS = { if (!data) { return uri; } - return Object.keys(data).reduce((gUri, d, index) => { - if (index === 0) { + + let anchor = '' + let uriWithoutAnchor = uri; + const hashIndex = uri.indexOf('#'); + if (hashIndex >=1) { + uriWithoutAnchor = uri.slice(0, hashIndex); + anchor = uri.slice(hashIndex, uri.length); + } + + uriWithoutAnchor = Object.keys(data).reduce((gUri, d, index) => { + if (index === 0 && gUri.indexOf('?') === -1) { gUri += '?'; } else { gUri += '&'; } gUri += `${d}=${data[d]}`; return gUri; - }, uri); + }, uriWithoutAnchor); + + return uriWithoutAnchor + anchor; }, data({uri, headers, data}) { return undefined; @@ -54,7 +65,7 @@ export const HTTP_METHODS = { export default class Requester { - fetch({uri, method, headers, data}, resolve, reject) { + fetch({method, uri, headers, data}, resolve, reject) { const transformer = HTTP_METHODS[method]; uri = transformer.uri ? transformer.uri({uri, headers, data}) : uri; headers = transformer.headers ? transformer.headers({uri, headers, data}) : headers; @@ -62,7 +73,7 @@ export default class Requester { const success = (responseText) => { resolve( - {uri, method, headers, data}, + {method, uri, headers, data}, {status: 200, statusText: 'OK', responseText} ); }; @@ -70,7 +81,7 @@ export default class Requester { const fail = ({status, statusText, errorThrown}) => { const errors = this._analyzeErrors({status, statusText, errorThrown}); reject( - {uri, method, headers, data}, + {method, uri, headers, data}, {status, statusText, errorThrown, errors} ); }; diff --git a/lib/router.js b/lib/router.js index 2953958..a1c8605 100755 --- a/lib/router.js +++ b/lib/router.js @@ -49,15 +49,30 @@ export default class Router { if (route.method !== method) { return false; } - if (route.uri instanceof RegExp) { - return uri.match(route.uri); - } if (!route.uri) { return true; } - return route.uri === uri; + let uriToCheck = uri; + + //remove query string from uri to test + const questionMarkIndex = uriToCheck.indexOf('?'); + if (questionMarkIndex >= 0) { + uriToCheck = uriToCheck.slice(0, questionMarkIndex); + } + + //remove anchor from uri to test + const hashIndex = uriToCheck.indexOf('#'); + if (hashIndex >= 0) { + uriToCheck = uriToCheck.slice(0, hashIndex); + } + + if (route.uri instanceof RegExp) { + return uriToCheck.match(route.uri); + } + + return route.uri === uriToCheck; }); } @@ -69,7 +84,7 @@ export default class Router { all(...args) { if (args.length === 0) { - throw new TypeError(`use all method takes at least a middleware`); + throw new TypeError(`use all method takes at least a middleware`); } let middleware; @@ -80,7 +95,7 @@ export default class Router { } if (!(middleware instanceof Middleware) && (typeof middleware !== 'function') ) { - throw new TypeError(`use all method takes at least a middleware`); + throw new TypeError(`use all method takes at least a middleware`); } for (const method of Object.keys(HTTP_METHODS)) { @@ -94,7 +109,7 @@ for (const method of Object.keys(HTTP_METHODS)) { const methodName = method.toLowerCase(); Router.prototype[methodName] = function(...args) { if (args.length === 0) { - throw new TypeError(`use ${methodName} method takes at least a middleware`); + throw new TypeError(`use ${methodName} method takes at least a middleware`); } let uri, middleware; @@ -105,7 +120,11 @@ for (const method of Object.keys(HTTP_METHODS)) { } if (!(middleware instanceof Middleware) && (typeof middleware !== 'function') ) { - throw new TypeError(`use ${methodName} method takes at least a middleware`); + throw new TypeError(`use ${methodName} method takes at least a middleware`); + } + + if (uri && this.baseUri && this.baseUri instanceof RegExp) { + throw new TypeError(`router contains a regexp cannot mix with route uri/regexp`); } this._add(new Route(this, uri, method, middleware)); diff --git a/test/requester-test.js b/test/requester-test.js index 54dc042..5da998c 100755 --- a/test/requester-test.js +++ b/test/requester-test.js @@ -4,41 +4,66 @@ import {assert} from 'chai'; import sinon from 'sinon'; import jsdom from 'mocha-jsdom'; import FakeXMLHttpRequest from 'fake-xml-http-request'; -import Requester from '../lib/requester'; +import Requester, {HTTP_METHODS} from '../lib/requester'; -function xHttpWillRespond(xhttp, readyState, status, statusText, responseText) { - const stub_send = sinon.stub(xhttp, 'send', function() { - this.readyState = readyState; - this.status = status; - this.statusText = statusText; - this.responseText = responseText; - this.onreadystatechange() +describe('HTTP_METHODS', () => { + it('GET method simple uri', () => { + const uriFn = HTTP_METHODS['GET'].uri; + const dataFn = HTTP_METHODS['GET'].data; + + assert(uriFn({uri: '/route', data:{a:'b', c:'d'}}) === '/route?a=b&c=d'); + assert(dataFn({uri: '/route', data:{a:'b', c:'d'}}) === undefined); }); - const stub_open = sinon.stub(xhttp, 'open', function() {}); + it('GET method uri with query string', () => { + const uriFn = HTTP_METHODS['GET'].uri; + const dataFn = HTTP_METHODS['GET'].data; - const stub_setRequestHeader = sinon.stub(xhttp, 'setRequestHeader', function() {}); - - return {stub_open, stub_send, stub_setRequestHeader}; -} - -function xHttpWillThrow(xhttp, sendErrorName, openErrorName) { - const stub_send = sinon.stub(xhttp, 'send', function() { - if (sendErrorName) { - throw {name: sendErrorName}; - } + assert(uriFn({uri: '/route?x=y&z=a', data:{a:'b', c:'d'}}) === '/route?x=y&z=a&a=b&c=d'); + assert(dataFn({uri: '/route?x=y&z=a', data:{a:'b', c:'d'}}) === undefined); }); - const stub_open = sinon.stub(xhttp, 'open', function() { - if (openErrorName) { - throw {name: openErrorName}; - } - }); + it('GET method uri with query string and anchor', () => { + const uriFn = HTTP_METHODS['GET'].uri; + const dataFn = HTTP_METHODS['GET'].data; - return {stub_open, stub_send}; -} + assert(uriFn({uri: '/route?x=y&z=a#anchor1', data:{a:'b', c:'d'}}) === '/route?x=y&z=a&a=b&c=d#anchor1'); + assert(dataFn({uri: '/route?x=y&z=a#anchor1', data:{a:'b', c:'d'}}) === undefined); + }); +}); describe('Requester', () => { + function xHttpWillRespond(xhttp, readyState, status, statusText, responseText) { + const stub_send = sinon.stub(xhttp, 'send', function() { + this.readyState = readyState; + this.status = status; + this.statusText = statusText; + this.responseText = responseText; + this.onreadystatechange() + }); + + const stub_open = sinon.stub(xhttp, 'open', function() {}); + + const stub_setRequestHeader = sinon.stub(xhttp, 'setRequestHeader', function() {}); + + return {stub_open, stub_send, stub_setRequestHeader}; + } + + function xHttpWillThrow(xhttp, sendErrorName, openErrorName) { + const stub_send = sinon.stub(xhttp, 'send', function() { + if (sendErrorName) { + throw {name: sendErrorName}; + } + }); + + const stub_open = sinon.stub(xhttp, 'open', function() { + if (openErrorName) { + throw {name: openErrorName}; + } + }); + + return {stub_open, stub_send}; + } let xhttp; diff --git a/test/router-test.js b/test/router-test.js index b4a7a2c..d3db1ad 100755 --- a/test/router-test.js +++ b/test/router-test.js @@ -153,6 +153,39 @@ describe('Router', () => { assert(r.length === 1); assert(r[0].uri === '/route1'); }); + + it('route with query string', () => { + let router = new frontexpress.Router('/route1 '); + + router.get('/subroute', new frontexpress.Middleware()); + + let r = router.routes('/route1/subroute?a=b&c=d', 'GET'); + assert(r.length === 1); + assert(r[0].uri === '/route1/subroute'); + assert(r[0].data === undefined) + }); + + it('route with anchor', () => { + let router = new frontexpress.Router('/route1 '); + + router.get('/subroute', new frontexpress.Middleware()); + + let r = router.routes('/route1/subroute#a=b&c=d', 'GET'); + assert(r.length === 1); + assert(r[0].uri === '/route1/subroute'); + assert(r[0].data === undefined) + }); + + it('route with query string and anchor', () => { + let router = new frontexpress.Router('/route1 '); + + router.get('/subroute', new frontexpress.Middleware()); + + let r = router.routes('/route1/subroute?a=b&c=d#anchor1', 'GET'); + assert(r.length === 1); + assert(r[0].uri === '/route1/subroute'); + assert(r[0].data === undefined) + }); }); describe('all method', () => { @@ -244,11 +277,11 @@ describe('Router', () => { router.get(middleware); - const r1 = router.routes('/', 'GET'); - assert(r1.length === 1); - assert(r1[0].uri === '/'); - assert(r1[0].method === 'GET'); - assert(r1[0].middleware === middleware); + const r = router.routes('/', 'GET'); + assert(r.length === 1); + assert(r[0].uri === '/'); + assert(r[0].method === 'GET'); + assert(r[0].middleware === middleware); }); it('with path /route1 and middleware as arguments', () => { @@ -257,11 +290,34 @@ describe('Router', () => { router.get('/route1', middleware); - const r1 = router.routes('/route1', 'GET'); - assert(r1.length === 1); - assert(r1[0].uri === '/route1'); - assert(r1[0].method === 'GET'); - assert(r1[0].middleware === middleware); + const r = router.routes('/route1', 'GET'); + assert(r.length === 1); + assert(r[0].uri === '/route1'); + assert(r[0].method === 'GET'); + assert(r[0].middleware === middleware); + }); + + it('router with regexp and route with /route1', () => { + const router = new frontexpress.Router(/^\//); + const middleware = new frontexpress.Middleware(); + try { + router.get('/route1', middleware); + } catch(ex) { + assert(ex instanceof TypeError) + } + }); + + it('router with regexp and route without uri', () => { + const router = new frontexpress.Router(/^\/part/); + const middleware = new frontexpress.Middleware(); + router.get(middleware); + + const r = router.routes('/part1', 'GET'); + assert(r.length === 1); + assert(r[0].uri instanceof RegExp); + assert(r[0].uri.toString() === new RegExp('^\/part').toString()); + assert(r[0].method === 'GET'); + assert(r[0].middleware === middleware); }); }); });