mirror of
https://gitlab.silvrtree.co.uk/martind2000/frontexpress.git
synced 2025-01-25 18:46:17 +00:00
initial commit
This commit is contained in:
parent
176b251ae8
commit
6e1f4fc779
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
node_modules
|
||||
coverage
|
9
.travis.yml
Normal file
9
.travis.yml
Normal file
@ -0,0 +1,9 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- "4.2"
|
||||
|
||||
before_script:
|
||||
- npm install coveralls
|
||||
|
||||
after_script:
|
||||
- cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js
|
160
lib/application.js
Executable file
160
lib/application.js
Executable file
@ -0,0 +1,160 @@
|
||||
import Router, {Route} from './router';
|
||||
import Middleware from './middleware';
|
||||
import Requester, {HTTP_METHODS} from './requester';
|
||||
|
||||
export default class Application {
|
||||
constructor() {
|
||||
this.routers = [];
|
||||
this.requester = new Requester();
|
||||
this.lastVisited = null;
|
||||
|
||||
this.settingsDef = {
|
||||
'http-requester': (requester) => {this.requester = requester}
|
||||
};
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
// Setting
|
||||
set(name, value) {
|
||||
const settingFn = this.settingsDef[name];
|
||||
if (!settingFn) {
|
||||
throw new ReferenceError(`unsupported setting ${name}`);
|
||||
}
|
||||
settingFn(value);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
// Routes
|
||||
route(uri) {
|
||||
const router = new Router(uri);
|
||||
this.routers.push(router);
|
||||
return router;
|
||||
}
|
||||
|
||||
use(...args) {
|
||||
if (args.length === 0) {
|
||||
throw new TypeError(`use method takes at least a middleware or a router`);
|
||||
}
|
||||
|
||||
let baseUri, middleware, router, which;
|
||||
|
||||
if (args.length === 1) {
|
||||
[which,] = args;
|
||||
} else {
|
||||
[baseUri, which,] = args;
|
||||
}
|
||||
|
||||
if (!(which instanceof Middleware) && (typeof which !== 'function') && !(which instanceof Router)) {
|
||||
throw new TypeError(`use method takes at least a middleware or a router`);
|
||||
}
|
||||
|
||||
if (which instanceof Router) {
|
||||
router = which;
|
||||
router.baseUri = baseUri;
|
||||
} else {
|
||||
middleware = which;
|
||||
router = new Router(baseUri);
|
||||
for (const method of Object.keys(HTTP_METHODS)) {
|
||||
router[method.toLowerCase()](middleware);
|
||||
}
|
||||
}
|
||||
this.routers.push(router);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
// Ajax request
|
||||
_fetch({method, uri, headers, data}, resolve, reject) {
|
||||
if (this.lastVisited) {
|
||||
for (const router of this.routers) {
|
||||
const routes = router.getRoutes(this.lastVisited.uri, this.lastVisited.method);
|
||||
for (const route of routes) {
|
||||
if (route.middleware.exited) {
|
||||
route.middleware.exited(this.lastVisited);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const currentRoutes = [];
|
||||
for (const router of this.routers) {
|
||||
const routes = router.getRoutes(uri, method);
|
||||
currentRoutes.push(...routes);
|
||||
}
|
||||
|
||||
for (const route of currentRoutes) {
|
||||
if (route.middleware.entered) {
|
||||
route.middleware.entered({method, uri, headers, data});
|
||||
}
|
||||
}
|
||||
|
||||
this.requester.fetch({uri, method}, (request, response) => {
|
||||
this.lastVisited = {method, uri, headers, data};
|
||||
|
||||
for (const route of currentRoutes) {
|
||||
if (route.middleware.updated) {
|
||||
route.middleware.updated(request, response);
|
||||
} else {
|
||||
route.middleware(request, response);
|
||||
}
|
||||
}
|
||||
if (resolve) {
|
||||
resolve(request, response);
|
||||
}
|
||||
}, (request, response) => {
|
||||
for (const route of currentRoutes) {
|
||||
if (route.middleware.failed) {
|
||||
route.middleware.failed(request, response);
|
||||
}
|
||||
}
|
||||
if (reject) {
|
||||
reject(request, response);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Object.keys(HTTP_METHODS).reduce((reqProto, method) => {
|
||||
// Middleware methods
|
||||
const middlewareMethodeName = method.toLowerCase();
|
||||
reqProto[middlewareMethodeName] = function(...args) {
|
||||
if (args.length === 0) {
|
||||
throw new TypeError(`${middlewareMethodeName} method takes at least a middleware`);
|
||||
}
|
||||
|
||||
let baseUri, middleware, which;
|
||||
|
||||
if (args.length === 1) {
|
||||
[which,] = args;
|
||||
} else {
|
||||
[baseUri, which,] = args;
|
||||
}
|
||||
|
||||
if (!(which instanceof Middleware) && (typeof which !== 'function')) {
|
||||
throw new TypeError(`${middlewareMethodeName} method takes at least a middleware`);
|
||||
}
|
||||
|
||||
const router = new Router();
|
||||
middleware = which;
|
||||
router[middlewareMethodeName](baseUri, middleware);
|
||||
|
||||
this.routers.push(router);
|
||||
}
|
||||
|
||||
// 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;
|
||||
if (!uri) {
|
||||
uri = request;
|
||||
}
|
||||
return this._fetch({
|
||||
uri,
|
||||
method,
|
||||
headers,
|
||||
data,
|
||||
}, resolve, reject);
|
||||
}
|
||||
|
||||
return reqProto;
|
||||
}, Application.prototype);
|
12
lib/frontexpress.js
Executable file
12
lib/frontexpress.js
Executable file
@ -0,0 +1,12 @@
|
||||
import Application from './application';
|
||||
import Router from './router';
|
||||
import Middleware from './middleware';
|
||||
|
||||
function frontexpress() {
|
||||
return new Application();
|
||||
}
|
||||
|
||||
frontexpress.Router = Router;
|
||||
frontexpress.Middleware = Middleware;
|
||||
|
||||
export default frontexpress;
|
19
lib/middleware.js
Executable file
19
lib/middleware.js
Executable file
@ -0,0 +1,19 @@
|
||||
import Application from './application';
|
||||
|
||||
export default class Middleware {
|
||||
constructor(name='') {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
entered(request) {
|
||||
}
|
||||
|
||||
exited(request) {
|
||||
}
|
||||
|
||||
updated(request, response) {
|
||||
}
|
||||
|
||||
failed(request, response) {
|
||||
}
|
||||
}
|
132
lib/requester.js
Executable file
132
lib/requester.js
Executable file
@ -0,0 +1,132 @@
|
||||
export const HTTP_METHODS = {
|
||||
'GET': {
|
||||
uri({uri, headers, data}) {
|
||||
if (!data) {
|
||||
return uri;
|
||||
}
|
||||
return Object.keys(data).reduce((gUri, d, index) => {
|
||||
if (index === 0) {
|
||||
gUri += '?';
|
||||
} else {
|
||||
gUri += '&';
|
||||
}
|
||||
gUri += `${d}=${data[d]}`;
|
||||
return gUri;
|
||||
}, uri);
|
||||
},
|
||||
data({uri, headers, data}) {
|
||||
return undefined;
|
||||
}
|
||||
},
|
||||
'POST': {
|
||||
headers({uri, headers, data}) {
|
||||
const postHeaders = {};
|
||||
postHeaders['Content-type'] = 'application/x-www-form-urlencoded';
|
||||
if (headers) {
|
||||
Object.keys(headers).reduce((phds, headKey) => {
|
||||
phds[headKey] = headers[headKey];
|
||||
return phds;
|
||||
}, postHeaders);
|
||||
}
|
||||
return postHeaders;
|
||||
},
|
||||
data({uri, headers, data}) {
|
||||
if (!data) {
|
||||
return data;
|
||||
}
|
||||
return Object.keys(data).reduce((newData, d, index) => {
|
||||
if (index !== 0) {
|
||||
newData += '&';
|
||||
}
|
||||
newData += `${d}=${data[d]}`;
|
||||
return newData;
|
||||
}, '');
|
||||
}
|
||||
},
|
||||
'PUT': {
|
||||
//TODO
|
||||
},
|
||||
'DELETE': {
|
||||
//TODO
|
||||
}
|
||||
// non exhaustive list
|
||||
};
|
||||
|
||||
export default class Requester {
|
||||
|
||||
fetch({uri, method, 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;
|
||||
data = transformer.data ? transformer.data({uri, headers, data}) : data;
|
||||
|
||||
const success = (responseText) => {
|
||||
resolve(
|
||||
{uri, method, headers, data},
|
||||
{status: 200, statusText: 'OK', responseText}
|
||||
);
|
||||
};
|
||||
|
||||
const fail = ({status, statusText, errorThrown}) => {
|
||||
const errors = this._analyzeErrors({status, statusText, errorThrown});
|
||||
reject(
|
||||
{uri, method, headers, data},
|
||||
{status, statusText, errorThrown, errors}
|
||||
);
|
||||
};
|
||||
|
||||
const xmlhttp = new XMLHttpRequest();
|
||||
xmlhttp.onreadystatechange = () => {
|
||||
if (xmlhttp.readyState === 4) {
|
||||
if (xmlhttp.status === 200) {
|
||||
success(xmlhttp.responseText);
|
||||
} else {
|
||||
fail({status: xmlhttp.status, statusText: xmlhttp.statusText});
|
||||
}
|
||||
}
|
||||
};
|
||||
try {
|
||||
xmlhttp.open(method, uri, true);
|
||||
if (headers) {
|
||||
for (const header of Object.keys(headers)) {
|
||||
xmlhttp.setRequestHeader(header, headers[header]);
|
||||
}
|
||||
}
|
||||
if (data) {
|
||||
xmlhttp.send(data);
|
||||
} else {
|
||||
xmlhttp.send();
|
||||
}
|
||||
} catch (errorThrown) {
|
||||
fail({errorThrown});
|
||||
}
|
||||
}
|
||||
|
||||
_analyzeErrors(response) {
|
||||
let error = '';
|
||||
if (response.status === 0) {
|
||||
error = 'Server access problem. Check your network connection';
|
||||
} else if (response.status === 401) {
|
||||
error = 'Your session has expired, Please reconnect. [code: 401]';
|
||||
} else if (response.status === 404) {
|
||||
error = 'Page not found on server. [code: 404]';
|
||||
} else if (response.status === 500) {
|
||||
error = 'Internal server error. [code: 500]';
|
||||
} else if (response.errorThrown) {
|
||||
if (response.errorThrown.name === 'SyntaxError') {
|
||||
error = 'Problem during data decoding [JSON]';
|
||||
} else if (response.errorThrown.name === 'TimeoutError') {
|
||||
error = 'Server is taking too long to reply';
|
||||
} else if (response.errorThrown.name === 'AbortError') {
|
||||
error = 'Request cancelled on server';
|
||||
} else if (response.errorThrown.name === 'NetworkError') {
|
||||
error = 'A network error occurred';
|
||||
} else {
|
||||
error = `${response.errorThrown.name} ${response.errorThrown.message}`;
|
||||
}
|
||||
} else {
|
||||
error = `Unknown error. ${response.statusText?response.statusText:''}`;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
}
|
108
lib/router.js
Executable file
108
lib/router.js
Executable file
@ -0,0 +1,108 @@
|
||||
import {HTTP_METHODS} from './requester';
|
||||
import Middleware from './middleware';
|
||||
|
||||
class Route {
|
||||
constructor(router, uriPart, method, middleware) {
|
||||
this.router = router;
|
||||
this.uriPart = uriPart;
|
||||
this.method = method;
|
||||
this.middleware = middleware;
|
||||
}
|
||||
|
||||
get uri() {
|
||||
if (this.uriPart instanceof RegExp) {
|
||||
return this.uriPart;
|
||||
}
|
||||
|
||||
if (this.router.baseUri instanceof RegExp) {
|
||||
return this.router.baseUri;
|
||||
}
|
||||
|
||||
if (this.router.baseUri && this.uriPart) {
|
||||
return (this.router.baseUri.trim() + this.uriPart.trim()).replace(/\/{2,}/, '/');
|
||||
}
|
||||
|
||||
if (this.router.baseUri) {
|
||||
return this.router.baseUri.trim();
|
||||
}
|
||||
|
||||
return this.uriPart;
|
||||
}
|
||||
}
|
||||
|
||||
export default class Router {
|
||||
constructor(baseUri) {
|
||||
if (baseUri) {
|
||||
this.baseUri = baseUri;
|
||||
}
|
||||
this.routes = [];
|
||||
}
|
||||
|
||||
_add(route) {
|
||||
this.routes.push(route);
|
||||
return this;
|
||||
}
|
||||
|
||||
getRoutes(uri, method) {
|
||||
return this.routes.filter((route) => {
|
||||
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;
|
||||
});
|
||||
}
|
||||
|
||||
all(...args) {
|
||||
if (args.length === 0) {
|
||||
throw new TypeError(`use all method takes at least a middleware`);
|
||||
}
|
||||
let middleware;
|
||||
|
||||
if (args.length === 1) {
|
||||
[middleware,] = args;
|
||||
} else {
|
||||
[, middleware,] = args;
|
||||
}
|
||||
|
||||
if (!(middleware instanceof Middleware) && (typeof middleware !== 'function') ) {
|
||||
throw new TypeError(`use all method takes at least a middleware`);
|
||||
}
|
||||
|
||||
for (const method of Object.keys(HTTP_METHODS)) {
|
||||
this[method.toLowerCase()](...args);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
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`);
|
||||
}
|
||||
let uri, middleware;
|
||||
|
||||
if (args.length === 1) {
|
||||
[middleware,] = args;
|
||||
} else {
|
||||
[uri, middleware,] = args;
|
||||
}
|
||||
|
||||
if (!(middleware instanceof Middleware) && (typeof middleware !== 'function') ) {
|
||||
throw new TypeError(`use ${methodName} method takes at least a middleware`);
|
||||
}
|
||||
|
||||
this._add(new Route(this, uri, method, middleware));
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
49
package.json
Executable file
49
package.json
Executable file
@ -0,0 +1,49 @@
|
||||
{
|
||||
"name": "frontexpress",
|
||||
"version": "1.0.0",
|
||||
"description": "Minimalist front end router framework a la express",
|
||||
"main": "dist/frontexpress.js",
|
||||
"scripts": {
|
||||
"build": "webpack",
|
||||
"dev": "webpack-dev-server index.js --watch",
|
||||
"test": "mocha --compilers js:babel-core/register",
|
||||
"cover": "babel-node node_modules/.bin/babel-istanbul cover node_modules/.bin/_mocha"
|
||||
},
|
||||
"author": "Camel Aissani <camel.aissani@gmail.com> (https://nuageprive.fr)",
|
||||
"license": "MIT",
|
||||
"repository": "camelaissani/frontexpress",
|
||||
"keywords": [
|
||||
"front",
|
||||
"framework",
|
||||
"web",
|
||||
"router",
|
||||
"middleware",
|
||||
"rest",
|
||||
"restful",
|
||||
"app",
|
||||
"api",
|
||||
"express",
|
||||
"frontexpress"
|
||||
],
|
||||
"devDependencies": {
|
||||
"babel-cli": "^6.9.0",
|
||||
"babel-core": "^6.9.0",
|
||||
"babel-eslint": "^6.0.4",
|
||||
"babel-istanbul": "^0.8.0",
|
||||
"babel-loader": "^6.2.4",
|
||||
"babel-preset-es2015": "^6.9.0",
|
||||
"babel-register": "^6.9.0",
|
||||
"chai": "^3.5.0",
|
||||
"eslint": "^2.11.0",
|
||||
"eslint-loader": "^1.3.0",
|
||||
"fake-xml-http-request": "^1.4.0",
|
||||
"istanbul": "^0.4.3",
|
||||
"jsdom": "^9.2.0",
|
||||
"mocha": "^2.5.3",
|
||||
"mocha-jsdom": "^1.1.0",
|
||||
"rewire": "^2.5.1",
|
||||
"sinon": "^1.17.4",
|
||||
"webpack": "^1.13.1",
|
||||
"webpack-dev-server": "^1.14.1"
|
||||
}
|
||||
}
|
564
test/application-test.js
Executable file
564
test/application-test.js
Executable file
@ -0,0 +1,564 @@
|
||||
/*eslint-env mocha*/
|
||||
import {assert} from 'chai';
|
||||
import sinon from 'sinon';
|
||||
import frontexpress from '../lib/frontexpress';
|
||||
import Requester from '../lib/requester';
|
||||
|
||||
describe('Application', () => {
|
||||
let requester;
|
||||
|
||||
describe('generated methods', () => {
|
||||
it('checks http methods are exposed', ()=> {
|
||||
const app = frontexpress();
|
||||
assert(typeof app.httpGet === 'function');
|
||||
assert(typeof app.httpPut === 'function');
|
||||
assert(typeof app.httpPost === 'function');
|
||||
assert(typeof app.httpDelete === 'function');
|
||||
});
|
||||
|
||||
it('checks middleware methods are exposed', ()=> {
|
||||
const app = frontexpress();
|
||||
assert(typeof app.get === 'function');
|
||||
assert(typeof app.put === 'function');
|
||||
assert(typeof app.post === 'function');
|
||||
assert(typeof app.delete === 'function');
|
||||
});
|
||||
});
|
||||
|
||||
describe('set method', () => {
|
||||
it('unsupported setting', () => {
|
||||
const app = frontexpress();
|
||||
try {
|
||||
app.set('blabla', 'value');
|
||||
} catch (ex) {
|
||||
assert(ex instanceof ReferenceError);
|
||||
}
|
||||
});
|
||||
|
||||
it('supported setting', () => {
|
||||
const requester = new Requester();
|
||||
const app = frontexpress();
|
||||
app.set('http-requester', requester);
|
||||
assert(app.requester === requester);
|
||||
});
|
||||
});
|
||||
|
||||
describe('use method', () => {
|
||||
beforeEach(()=>{
|
||||
requester = new Requester();
|
||||
sinon.stub(requester, 'fetch', ({uri, method, headers, data}, resolve, reject) => {
|
||||
resolve(
|
||||
{uri, method, headers, data},
|
||||
{status: 200, statusText: 'OK', responseText:''}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('no arguments', () => {
|
||||
const app = frontexpress();
|
||||
app.set('http-requester', requester);
|
||||
assert.throws(app.use, TypeError);
|
||||
});
|
||||
|
||||
it('bad arguments', () => {
|
||||
const app = frontexpress();
|
||||
app.set('http-requester', requester);
|
||||
try {
|
||||
app.use('eee');
|
||||
} catch (ex) {
|
||||
assert(ex instanceof TypeError);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
it('middleware as function on path /', (done) => {
|
||||
const spy = sinon.spy();
|
||||
|
||||
const app = frontexpress();
|
||||
app.set('http-requester', requester);
|
||||
app.use((request, response) => {spy()});
|
||||
|
||||
app.httpGet('/', (request, response) => {
|
||||
assert(spy.callCount === 1);
|
||||
|
||||
app.httpPost('/route1', (request, response) => {
|
||||
assert(spy.callCount === 2);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('middleware as function on path /route1', (done) => {
|
||||
const spy = sinon.spy();
|
||||
|
||||
const app = frontexpress();
|
||||
app.set('http-requester', requester);
|
||||
app.use('/route1', (request, response) => {spy()});
|
||||
|
||||
app.httpGet('/route1', (request, response) => {
|
||||
assert(spy.callCount === 1);
|
||||
|
||||
app.httpPost('/route1', (request, response) => {
|
||||
assert(spy.callCount === 2);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('middleware as object on path /', (done) => {
|
||||
const middleware = new frontexpress.Middleware('on path /');
|
||||
const spy = sinon.spy(middleware, 'updated');
|
||||
|
||||
const app = frontexpress();
|
||||
app.set('http-requester', requester);
|
||||
app.use(middleware);
|
||||
|
||||
app.httpGet('/', (request, response) => {
|
||||
assert(spy.callCount === 1);
|
||||
|
||||
app.httpPost('/route1', (request, response) => {
|
||||
assert(spy.callCount === 2);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('middleware as object on path /route1', (done) => {
|
||||
const middleware = new frontexpress.Middleware('on path /route1');
|
||||
const spy = sinon.spy(middleware, 'updated');
|
||||
|
||||
const app = frontexpress();
|
||||
app.set('http-requester', requester);
|
||||
app.use('/route1', middleware);
|
||||
|
||||
app.httpGet('/route1', (request, response) => {
|
||||
assert(spy.callCount === 1);
|
||||
|
||||
app.httpPost('/route1', (request, response) => {
|
||||
assert(spy.callCount === 2);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('router on path /', (done) => {
|
||||
const spy = sinon.spy();
|
||||
const router = new frontexpress.Router();
|
||||
router
|
||||
.get((request, response) => {spy()})
|
||||
.post((request, response) => {spy()});
|
||||
|
||||
const app = frontexpress();
|
||||
app.set('http-requester', requester);
|
||||
app.use(router);
|
||||
|
||||
app.httpGet('/', (request, response) => {
|
||||
assert(spy.callCount === 1);
|
||||
|
||||
app.httpPost('/route1', (request, response) => {
|
||||
assert(spy.callCount === 2);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('router on path /route1', (done) => {
|
||||
const spy = sinon.spy();
|
||||
const router = new frontexpress.Router();
|
||||
router
|
||||
.get((request, response) => {spy()})
|
||||
.post((request, response) => {spy()});
|
||||
|
||||
const app = frontexpress();
|
||||
app.set('http-requester', requester);
|
||||
app.use('/route1', router);
|
||||
|
||||
app.httpGet('/route1', (request, response) => {
|
||||
assert(spy.callCount === 1);
|
||||
|
||||
app.httpPost('/route1', (request, response) => {
|
||||
assert(spy.callCount === 2);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('router with base uri', (done)=> {
|
||||
const middleware = new frontexpress.Middleware('get middleware');
|
||||
const spy = sinon.spy(middleware, 'updated');
|
||||
|
||||
const app = frontexpress();
|
||||
app.set('http-requester', requester);
|
||||
|
||||
const router = new frontexpress.Router();
|
||||
router.get('/subroute1', middleware);
|
||||
|
||||
app.use('/route1', router);
|
||||
|
||||
app.httpGet('/route1/subroute1', (request, response) => {
|
||||
assert(spy.calledOnce);
|
||||
done();
|
||||
},
|
||||
(request, response) => {
|
||||
done(response);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('get method', () => {
|
||||
beforeEach(()=>{
|
||||
requester = new Requester();
|
||||
sinon.stub(requester, 'fetch', ({uri, method, headers, data}, resolve, reject) => {
|
||||
resolve(
|
||||
{uri, method, headers, data},
|
||||
{status: 200, statusText: 'OK', responseText:''}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('no arguments', () => {
|
||||
const app = frontexpress();
|
||||
app.set('http-requester', requester);
|
||||
assert.throws(app.get, TypeError);
|
||||
});
|
||||
|
||||
it('bad arguments', () => {
|
||||
const app = frontexpress();
|
||||
app.set('http-requester', requester);
|
||||
try {
|
||||
app.get('eee');
|
||||
} catch (ex) {
|
||||
assert(ex instanceof TypeError);
|
||||
}
|
||||
try {
|
||||
app.get(new frontexpress.Router());
|
||||
} catch (ex) {
|
||||
assert(ex instanceof TypeError);
|
||||
}
|
||||
try {
|
||||
app.get('/route1', new frontexpress.Router());
|
||||
} catch (ex) {
|
||||
assert(ex instanceof TypeError);
|
||||
}
|
||||
});
|
||||
|
||||
it('middleware as function on path /', (done) => {
|
||||
const spy = sinon.spy();
|
||||
|
||||
const app = frontexpress();
|
||||
app.set('http-requester', requester);
|
||||
app.get((request, response) => {spy()});
|
||||
|
||||
app.httpGet('/', (request, response) => {
|
||||
assert(spy.callCount === 1);
|
||||
|
||||
app.httpPost('/route1', (request, response) => {
|
||||
assert(spy.callCount === 1);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('middleware as function on path /route1', (done) => {
|
||||
const spy = sinon.spy();
|
||||
|
||||
const app = frontexpress();
|
||||
app.set('http-requester', requester);
|
||||
app.get('/route1', (request, response) => {spy()});
|
||||
|
||||
app.httpGet('/route1', (request, response) => {
|
||||
assert(spy.callCount === 1);
|
||||
|
||||
app.httpPost('/route1', (request, response) => {
|
||||
assert(spy.callCount === 1);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('middleware as object on path /', (done) => {
|
||||
const middleware = new frontexpress.Middleware('on path /');
|
||||
const spy = sinon.spy(middleware, 'updated');
|
||||
|
||||
const app = frontexpress();
|
||||
app.set('http-requester', requester);
|
||||
app.get(middleware);
|
||||
|
||||
app.httpGet('/', (request, response) => {
|
||||
assert(spy.callCount === 1);
|
||||
|
||||
app.httpPost('/route1', (request, response) => {
|
||||
assert(spy.callCount === 1);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('middleware as object on path /route1', (done) => {
|
||||
const middleware = new frontexpress.Middleware('on path /route1');
|
||||
const spy = sinon.spy(middleware, 'updated');
|
||||
|
||||
const app = frontexpress();
|
||||
app.set('http-requester', requester);
|
||||
app.get(middleware);
|
||||
|
||||
app.httpGet('/route1', (request, response) => {
|
||||
assert(spy.callCount === 1);
|
||||
|
||||
app.httpPost('/route1', (request, response) => {
|
||||
assert(spy.callCount === 1);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('route method', () => {
|
||||
beforeEach(()=>{
|
||||
requester = new Requester();
|
||||
sinon.stub(requester, 'fetch', ({uri, method, headers, data}, resolve, reject) => {
|
||||
resolve(
|
||||
{uri, method, headers, data},
|
||||
{status: 200, statusText: 'OK', responseText:''}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('on root path not specified', (done) => {
|
||||
const spy = sinon.spy();
|
||||
|
||||
const app = frontexpress();
|
||||
app.set('http-requester', requester);
|
||||
app.route().get((request, response) => {spy()});
|
||||
|
||||
app.httpGet('/', (request, response) => {
|
||||
assert(spy.calledOnce);
|
||||
app.httpPost('/', (request, response) => {
|
||||
assert(spy.calledOnce);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
it('on root path /', (done) => {
|
||||
const spy = sinon.spy();
|
||||
|
||||
const app = frontexpress();
|
||||
app.set('http-requester', requester);
|
||||
app.route('/').get((request, response) => {spy()});
|
||||
|
||||
app.httpGet('/', (request, response) => {
|
||||
assert(spy.calledOnce);
|
||||
app.httpPost('/', (request, response) => {
|
||||
assert(spy.calledOnce);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
it('on root path /route1', (done) => {
|
||||
const spy = sinon.spy();
|
||||
|
||||
const app = frontexpress();
|
||||
app.set('http-requester', requester);
|
||||
app.route('/route1').get((request, response) => {spy()});
|
||||
|
||||
app.httpGet('/route1', (request, response) => {
|
||||
assert(spy.calledOnce);
|
||||
app.httpPost('/route1', (request, response) => {
|
||||
assert(spy.calledOnce);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
it('on root path undefined with sub path /subroute1', (done) => {
|
||||
const spy = sinon.spy();
|
||||
|
||||
const app = frontexpress();
|
||||
app.set('http-requester', requester);
|
||||
app.route().get('/subroute1', (request, response) => {spy()});
|
||||
|
||||
app.httpGet('/subroute1', (request, response) => {
|
||||
assert(spy.calledOnce);
|
||||
app.httpPost('/subroute1', (request, response) => {
|
||||
assert(spy.calledOnce);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('on root path / with sub path /subroute1', (done) => {
|
||||
const spy = sinon.spy();
|
||||
|
||||
const app = frontexpress();
|
||||
app.set('http-requester', requester);
|
||||
app.route('/').get('/subroute1', (request, response) => {spy()});
|
||||
|
||||
app.httpGet('/subroute1', (request, response) => {
|
||||
assert(spy.calledOnce);
|
||||
app.httpPost('/subroute1', (request, response) => {
|
||||
assert(spy.calledOnce);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('on root path /route1 with sub path /subroute1', (done) => {
|
||||
const spy = sinon.spy();
|
||||
|
||||
const app = frontexpress();
|
||||
app.set('http-requester', requester);
|
||||
app.route('/route1').get('/subroute1', (request, response) => {spy()});
|
||||
|
||||
app.httpGet('/route1/subroute1', (request, response) => {
|
||||
assert(spy.calledOnce);
|
||||
app.httpPost('/route1/subroute1', (request, response) => {
|
||||
assert(spy.calledOnce);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('middleware object lifecycle', () => {
|
||||
beforeEach(()=>{
|
||||
requester = new Requester();
|
||||
sinon.stub(requester, 'fetch', ({uri, method, headers, data}, resolve, reject) => {
|
||||
resolve(
|
||||
{uri, method, headers, data},
|
||||
{status: 200, statusText: 'OK', responseText:''}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('http GET request', (done) => {
|
||||
const app = frontexpress();
|
||||
app.set('http-requester', requester);
|
||||
|
||||
const getMiddleware = new frontexpress.Middleware('get middleware');
|
||||
const spy_get_entered = sinon.spy(getMiddleware, 'entered');
|
||||
const spy_get_updated = sinon.spy(getMiddleware, 'updated');
|
||||
const spy_get_exited = sinon.spy(getMiddleware, 'exited');
|
||||
|
||||
app.route('/route1').get(getMiddleware);
|
||||
|
||||
app.httpGet({uri:'/route1', data: {p1: 'a', p2: 'b', p3: 'c'}}, (request, response) => {
|
||||
assert(spy_get_entered.callCount === 1);
|
||||
assert(spy_get_updated.callCount === 1);
|
||||
assert(spy_get_exited.callCount === 0);
|
||||
|
||||
assert(spy_get_entered.calledBefore(spy_get_updated));
|
||||
done();
|
||||
},
|
||||
(request, response) => {
|
||||
done(response);
|
||||
});
|
||||
});
|
||||
|
||||
it('http GET followed by http POST requests', (done) => {
|
||||
const app = frontexpress();
|
||||
app.set('http-requester', requester);
|
||||
|
||||
const allMiddleware = new frontexpress.Middleware('all middleware');
|
||||
const spy_all_entered = sinon.spy(allMiddleware, 'entered');
|
||||
const spy_all_updated = sinon.spy(allMiddleware, 'updated');
|
||||
const spy_all_exited = sinon.spy(allMiddleware, 'exited');
|
||||
|
||||
const getMiddleware = new frontexpress.Middleware('get middleware');
|
||||
const spy_get_entered = sinon.spy(getMiddleware, 'entered');
|
||||
const spy_get_updated = sinon.spy(getMiddleware, 'updated');
|
||||
const spy_get_exited = sinon.spy(getMiddleware, 'exited');
|
||||
|
||||
const postMiddleware = new frontexpress.Middleware('post middleware');
|
||||
const spy_post_entered = sinon.spy(postMiddleware, 'entered');
|
||||
const spy_post_updated = sinon.spy(postMiddleware, 'updated');
|
||||
const spy_post_exited = sinon.spy(postMiddleware, 'exited');
|
||||
|
||||
app.route('/route1')
|
||||
.all(allMiddleware)
|
||||
.get(getMiddleware)
|
||||
.post(postMiddleware);
|
||||
|
||||
app.httpGet('/route1', (request, response) => {
|
||||
assert(spy_all_entered.callCount === 1);
|
||||
assert(spy_all_updated.callCount === 1);
|
||||
assert(spy_all_exited.callCount === 0);
|
||||
assert(spy_all_entered.calledBefore(spy_get_entered));
|
||||
assert(spy_all_entered.calledBefore(spy_post_entered));
|
||||
|
||||
assert(spy_get_entered.callCount === 1);
|
||||
assert(spy_get_updated.callCount === 1);
|
||||
assert(spy_get_exited.callCount === 0);
|
||||
assert(spy_get_entered.calledBefore(spy_post_entered));
|
||||
|
||||
assert(spy_post_entered.callCount === 0);
|
||||
assert(spy_post_updated.callCount === 0);
|
||||
assert(spy_post_exited.callCount === 0);
|
||||
|
||||
spy_all_entered.reset();
|
||||
spy_all_updated.reset();
|
||||
spy_all_exited.reset();
|
||||
|
||||
spy_get_entered.reset();
|
||||
spy_get_updated.reset();
|
||||
spy_get_exited.reset();
|
||||
|
||||
spy_post_entered.reset();
|
||||
spy_post_updated.reset();
|
||||
spy_post_exited.reset();
|
||||
|
||||
app.httpPost('/route1', (request, response) => {
|
||||
assert(spy_all_entered.callCount === 1);
|
||||
assert(spy_all_updated.callCount === 1);
|
||||
assert(spy_all_exited.callCount === 1);
|
||||
assert(spy_all_exited.calledBefore(spy_all_entered));
|
||||
assert(spy_all_exited.calledBefore(spy_all_updated));
|
||||
|
||||
assert(spy_get_entered.callCount === 0);
|
||||
assert(spy_get_updated.callCount === 0);
|
||||
assert(spy_get_exited.callCount === 1);
|
||||
|
||||
assert(spy_post_entered.callCount === 1);
|
||||
assert(spy_post_updated.callCount === 1);
|
||||
assert(spy_post_exited.callCount === 0);
|
||||
|
||||
done();
|
||||
}, (request, response) => {
|
||||
done('should fail');
|
||||
});
|
||||
}, (request, response) => {
|
||||
done('should fail');
|
||||
});
|
||||
});
|
||||
|
||||
it('request returning error', (done) => {
|
||||
requester = new Requester();
|
||||
sinon.stub(requester, 'fetch', ({uri, method, headers, data}, resolve, reject) => {
|
||||
reject(
|
||||
{uri, method, headers, data},
|
||||
{status: 404, statusText: 'page not found'}
|
||||
);
|
||||
});
|
||||
const app = frontexpress();
|
||||
app.set('http-requester', requester);
|
||||
|
||||
const getMiddleware = new frontexpress.Middleware('get middleware');
|
||||
const spy_get_failed = sinon.spy(getMiddleware, 'failed');
|
||||
|
||||
|
||||
app.route('/route1').get(getMiddleware);
|
||||
|
||||
app.httpGet('/route1', (request, response) => {
|
||||
done('should fail');
|
||||
},
|
||||
(request, response) => {
|
||||
assert(spy_get_failed.callCount === 1);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
389
test/requester-test.js
Executable file
389
test/requester-test.js
Executable file
@ -0,0 +1,389 @@
|
||||
/*eslint-env mocha*/
|
||||
/*global global*/
|
||||
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';
|
||||
|
||||
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};
|
||||
}
|
||||
|
||||
describe('Requester', () => {
|
||||
|
||||
let xhttp;
|
||||
|
||||
// Init DOM with a fake document
|
||||
// <base> and uri (initial uri) allow to do pushState in jsdom
|
||||
jsdom({
|
||||
html:`
|
||||
<html>
|
||||
<head>
|
||||
<base href="http://localhost:8080/"></base>
|
||||
</head>
|
||||
</html>
|
||||
`,
|
||||
url: 'http://localhost:8080/'
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
// Stub XMLHttpRequest
|
||||
xhttp = new FakeXMLHttpRequest();
|
||||
global.XMLHttpRequest = () => {
|
||||
return xhttp;
|
||||
};
|
||||
});
|
||||
|
||||
describe('GET Requests', () => {
|
||||
|
||||
it('with data', (done) => {
|
||||
const requester = new Requester();
|
||||
|
||||
const {stub_open, stub_send} = xHttpWillRespond(xhttp, 4, 200, '', '<p>content!</p>');
|
||||
|
||||
requester.fetch({method: 'GET', uri:'/route1', data:{p1: 'a', p2: 'b', p3: 'c'}},
|
||||
(request, response) => {
|
||||
assert(request.uri === '/route1?p1=a&p2=b&p3=c');
|
||||
done();
|
||||
},
|
||||
(err) => {
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
|
||||
it('without data', (done) => {
|
||||
const requester = new Requester();
|
||||
|
||||
const {stub_open, stub_send} = xHttpWillRespond(xhttp, 4, 200, '', '<p>content!</p>');
|
||||
|
||||
requester.fetch({method: 'GET', uri:'/route1'}, (request, response) => {
|
||||
assert(stub_open.calledOnce);
|
||||
assert(stub_send.calledOnce);
|
||||
assert(stub_open.calledBefore(stub_send));
|
||||
|
||||
assert(request.uri === '/route1');
|
||||
assert(request.method === 'GET');
|
||||
assert(request.data === undefined);
|
||||
|
||||
assert(response.status === 200);
|
||||
assert(response.statusText === 'OK');
|
||||
assert(response.responseText === '<p>content!</p>');
|
||||
assert(response.errorThrown === undefined);
|
||||
assert(response.errors === undefined);
|
||||
|
||||
done();
|
||||
},
|
||||
(error) => {
|
||||
done(error);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('POST Requests', () => {
|
||||
|
||||
it('with data', (done) => {
|
||||
const requester = new Requester();
|
||||
|
||||
const {stub_open, stub_send, stub_setRequestHeader} = xHttpWillRespond(xhttp, 4, 200, '', '<p>content!</p>');
|
||||
|
||||
requester.fetch({method: 'POST', uri:'/route1', data:{p1: 'a', p2: 'b', p3: 'c'}},
|
||||
(request, response) => {
|
||||
assert(stub_open.calledOnce);
|
||||
assert(stub_setRequestHeader.calledOnce);
|
||||
assert(stub_send.calledOnce);
|
||||
assert(stub_open.calledBefore(stub_send));
|
||||
assert(stub_setRequestHeader.calledAfter(stub_open));
|
||||
assert(stub_setRequestHeader.calledBefore(stub_send));
|
||||
|
||||
assert(request.uri === '/route1');
|
||||
assert(request.headers['Content-type'] === 'application/x-www-form-urlencoded');
|
||||
assert(request.data === 'p1=a&p2=b&p3=c');
|
||||
done();
|
||||
},
|
||||
(err) => {
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
|
||||
it('without data', (done) => {
|
||||
const requester = new Requester();
|
||||
|
||||
const {stub_open, stub_send, stub_setRequestHeader} = xHttpWillRespond(xhttp, 4, 200, '', '<p>content!</p>');
|
||||
|
||||
requester.fetch({method: 'POST', uri:'/route1'},
|
||||
(request, response) => {
|
||||
assert(stub_open.calledOnce);
|
||||
assert(stub_setRequestHeader.calledOnce);
|
||||
assert(stub_send.calledOnce);
|
||||
assert(stub_open.calledBefore(stub_send));
|
||||
assert(stub_setRequestHeader.calledAfter(stub_open));
|
||||
assert(stub_setRequestHeader.calledBefore(stub_send));
|
||||
|
||||
assert(request.uri === '/route1');
|
||||
assert(request.headers['Content-type'] === 'application/x-www-form-urlencoded');
|
||||
assert(request.data === undefined);
|
||||
done();
|
||||
},
|
||||
(err) => {
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
|
||||
it('with custom headers', (done) => {
|
||||
const requester = new Requester();
|
||||
|
||||
const {stub_open, stub_send, stub_setRequestHeader} = xHttpWillRespond(xhttp, 4, 200, '', '<p>content!</p>');
|
||||
|
||||
requester.fetch({method: 'POST', uri:'/route1', headers: {'Accept-Charset': 'utf-8'}},
|
||||
(request, response) => {
|
||||
assert(stub_open.calledOnce);
|
||||
assert(stub_setRequestHeader.calledTwice);
|
||||
assert(stub_send.calledOnce);
|
||||
assert(stub_open.calledBefore(stub_send));
|
||||
assert(stub_setRequestHeader.calledAfter(stub_open));
|
||||
assert(stub_setRequestHeader.calledBefore(stub_send));
|
||||
|
||||
assert(request.uri === '/route1');
|
||||
assert(request.headers['Content-type'] === 'application/x-www-form-urlencoded');
|
||||
assert(request.headers['Accept-Charset'] === 'utf-8');
|
||||
assert(request.data === undefined);
|
||||
done();
|
||||
},
|
||||
(err) => {
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('HTTP errors', () => {
|
||||
it('request returns no network', (done) => {
|
||||
const requester = new Requester();
|
||||
|
||||
const {stub_open, stub_send} = xHttpWillRespond(xhttp, 4, 0);
|
||||
|
||||
requester.fetch({method: 'GET', uri: '/route1'}, null,
|
||||
(request, response) => {
|
||||
assert(stub_open.calledOnce);
|
||||
assert(stub_send.calledOnce);
|
||||
assert(stub_open.calledBefore(stub_send));
|
||||
|
||||
assert(response.status === 0);
|
||||
assert(response.statusText === undefined);
|
||||
assert(response.responseText === undefined);
|
||||
assert(response.errorThrown === undefined);
|
||||
assert(response.errors.length !== 0);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('request returns 401', (done) => {
|
||||
const requester = new Requester();
|
||||
|
||||
const {stub_open, stub_send} = xHttpWillRespond(xhttp, 4, 401, 'not authenticated', '');
|
||||
|
||||
requester.fetch({method: 'GET', uri:'/route1'}, null,
|
||||
(request, response) => {
|
||||
assert(stub_send.calledOnce);
|
||||
assert(stub_open.calledOnce);
|
||||
|
||||
assert(response.status === 401);
|
||||
assert(response.statusText === 'not authenticated');
|
||||
assert(response.responseText === undefined);
|
||||
assert(response.errorThrown === undefined);
|
||||
assert(response.errors.length !== 0);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('request returns 404', (done) => {
|
||||
const requester = new Requester();
|
||||
|
||||
const {stub_open, stub_send} = xHttpWillRespond(xhttp, 4, 404, 'page not found', '');
|
||||
|
||||
requester.fetch({method: 'GET', uri:'/route1'}, null,
|
||||
(request, response) => {
|
||||
assert(stub_send.calledOnce);
|
||||
assert(stub_open.calledOnce);
|
||||
|
||||
assert(response.status === 404);
|
||||
assert(response.statusText === 'page not found');
|
||||
assert(response.responseText === undefined);
|
||||
assert(response.errorThrown === undefined);
|
||||
assert(response.errors.length !== 0);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('request returns 500', (done) => {
|
||||
const requester = new Requester();
|
||||
|
||||
const {stub_open, stub_send} = xHttpWillRespond(xhttp, 4, 500, 'server error', '');
|
||||
|
||||
requester.fetch({method: 'GET', uri:'/route1'}, null,
|
||||
(request, response) => {
|
||||
assert(stub_send.calledOnce);
|
||||
assert(stub_open.calledOnce);
|
||||
|
||||
assert(response.status === 500);
|
||||
assert(response.statusText === 'server error');
|
||||
assert(response.responseText === undefined);
|
||||
assert(response.errorThrown === undefined);
|
||||
assert(response.errors.length !== 0);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('request returns 501 (http status not managed)', (done) => {
|
||||
const requester = new Requester();
|
||||
|
||||
const {stub_open, stub_send} = xHttpWillRespond(xhttp, 4, 501, 'Not Implemented', '');
|
||||
|
||||
requester.fetch({method: 'GET', uri:'/route1'}, null,
|
||||
(request, response) => {
|
||||
assert(stub_send.calledOnce);
|
||||
assert(stub_open.calledOnce);
|
||||
|
||||
assert(response.status === 501);
|
||||
assert(response.statusText === 'Not Implemented');
|
||||
assert(response.responseText === undefined);
|
||||
assert(response.errorThrown === undefined);
|
||||
assert(response.errors.length !== 0);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('request returns syntax error', (done) => {
|
||||
const requester = new Requester();
|
||||
|
||||
const {stub_open, stub_send} = xHttpWillThrow(xhttp, 'SyntaxError');
|
||||
|
||||
requester.fetch({method: 'GET', uri:'/route1'}, null,
|
||||
(request, response) => {
|
||||
assert(stub_send.calledOnce);
|
||||
assert(stub_open.calledOnce);
|
||||
|
||||
assert(response.status === undefined);
|
||||
assert(response.statusText === undefined);
|
||||
assert(response.responseText === undefined);
|
||||
assert(response.errorThrown.name === 'SyntaxError');
|
||||
assert(response.errors.length !== 0);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('request returns timeout error', (done) => {
|
||||
const requester = new Requester();
|
||||
|
||||
const {stub_open, stub_send} = xHttpWillThrow(xhttp, 'TimeoutError');
|
||||
|
||||
requester.fetch({method: 'GET', uri:'/route1'}, null,
|
||||
(request, response) => {
|
||||
assert(stub_send.calledOnce);
|
||||
assert(stub_open.calledOnce);
|
||||
|
||||
assert(response.status === undefined);
|
||||
assert(response.statusText === undefined);
|
||||
assert(response.responseText === undefined);
|
||||
assert(response.errorThrown.name === 'TimeoutError');
|
||||
assert(response.errors.length !== 0);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('request returns abort error', (done) => {
|
||||
const requester = new Requester();
|
||||
|
||||
const {stub_open, stub_send} = xHttpWillThrow(xhttp, 'AbortError');
|
||||
|
||||
requester.fetch({method: 'GET', uri:'/route1'}, null,
|
||||
(request, response) => {
|
||||
assert(stub_send.calledOnce);
|
||||
assert(stub_open.calledOnce);
|
||||
|
||||
assert(response.status === undefined);
|
||||
assert(response.statusText === undefined);
|
||||
assert(response.responseText === undefined);
|
||||
assert(response.errorThrown.name === 'AbortError');
|
||||
assert(response.errors.length !== 0);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('request returns network error', (done) => {
|
||||
const requester = new Requester();
|
||||
|
||||
const {stub_open, stub_send} = xHttpWillThrow(xhttp, 'NetworkError');
|
||||
|
||||
requester.fetch({method: 'GET', uri:'/route1'}, null,
|
||||
(request, response) => {
|
||||
assert(stub_send.calledOnce);
|
||||
assert(stub_open.calledOnce);
|
||||
|
||||
assert(response.status === undefined);
|
||||
assert(response.statusText === undefined);
|
||||
assert(response.responseText === undefined);
|
||||
assert(response.errorThrown.name === 'NetworkError');
|
||||
assert(response.errors.length !== 0);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('request returns unknown error', (done) => {
|
||||
const requester = new Requester();
|
||||
|
||||
const {stub_open, stub_send} = xHttpWillThrow(xhttp, 'BlaBlaError');
|
||||
|
||||
requester.fetch({method: 'GET', uri:'/route1'}, null,
|
||||
(request, response) => {
|
||||
assert(stub_send.calledOnce);
|
||||
assert(stub_open.calledOnce);
|
||||
|
||||
assert(response.status === undefined);
|
||||
assert(response.statusText === undefined);
|
||||
assert(response.responseText === undefined);
|
||||
assert(response.errorThrown.name === 'BlaBlaError');
|
||||
assert(response.errors.length !== 0);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
255
test/router-test.js
Executable file
255
test/router-test.js
Executable file
@ -0,0 +1,255 @@
|
||||
/*eslint-env mocha*/
|
||||
import {assert} from 'chai';
|
||||
import sinon from 'sinon';
|
||||
import frontexpress from '../lib/frontexpress';
|
||||
import {HTTP_METHODS} from '../lib/requester';
|
||||
|
||||
describe('Router', () => {
|
||||
|
||||
describe('generated methods', () => {
|
||||
it('checks http methods are exposed', ()=> {
|
||||
assert(typeof frontexpress.Router.prototype.all === 'function');
|
||||
assert(typeof frontexpress.Router.prototype.get === 'function');
|
||||
assert(typeof frontexpress.Router.prototype.put === 'function');
|
||||
assert(typeof frontexpress.Router.prototype.post === 'function');
|
||||
assert(typeof frontexpress.Router.prototype.delete === 'function');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getRoutes method', () => {
|
||||
it('no root path and no path uri', ()=> {
|
||||
const router = new frontexpress.Router();
|
||||
const middleware = (request, response) => {};
|
||||
|
||||
router.get(middleware);
|
||||
|
||||
const r1 = router.getRoutes('/', 'GET');
|
||||
assert(r1.length === 1);
|
||||
assert(r1[0].uri === undefined);
|
||||
assert(r1[0].method === 'GET');
|
||||
assert(r1[0].middleware === middleware);
|
||||
});
|
||||
|
||||
it('no root path and path /routeX', ()=> {
|
||||
const router = new frontexpress.Router();
|
||||
const middleware1 = (request, response) => {};
|
||||
const middleware2 = (request, response) => {};
|
||||
const middleware3 = (request, response) => {};
|
||||
|
||||
router
|
||||
.get('/route1', middleware1)
|
||||
.post('/route2', middleware2)
|
||||
.all('/route3', middleware3);
|
||||
|
||||
const r1 = router.getRoutes('/route1', 'GET');
|
||||
assert(r1.length === 1);
|
||||
assert(r1[0].uri === '/route1');
|
||||
assert(r1[0].method === 'GET');
|
||||
assert(r1[0].middleware === middleware1);
|
||||
|
||||
const r2 = router.getRoutes('/route2', 'POST');
|
||||
assert(r2.length === 1);
|
||||
assert(r2[0].uri === '/route2');
|
||||
assert(r2[0].method === 'POST');
|
||||
assert(r2[0].middleware === middleware2);
|
||||
|
||||
let r3 = router.getRoutes('/route3', 'GET');
|
||||
assert(r3.length === 1);
|
||||
assert(r3[0].uri === '/route3');
|
||||
assert(r3[0].method === 'GET');
|
||||
assert(r3[0].middleware === middleware3);
|
||||
|
||||
r3 = router.getRoutes('/route3', 'POST');
|
||||
assert(r3.length === 1);
|
||||
assert(r3[0].uri === '/route3');
|
||||
assert(r3[0].method === 'POST');
|
||||
assert(r3[0].middleware === middleware3);
|
||||
|
||||
r3 = router.getRoutes('/route3', 'PUT');
|
||||
assert(r3.length === 1);
|
||||
assert(r3[0].uri === '/route3');
|
||||
assert(r3[0].method === 'PUT');
|
||||
assert(r3[0].middleware === middleware3);
|
||||
|
||||
r3 = router.getRoutes('/route3', 'DELETE');
|
||||
assert(r3.length === 1);
|
||||
assert(r3[0].uri === '/route3');
|
||||
assert(r3[0].method === 'DELETE');
|
||||
assert(r3[0].middleware === middleware3);
|
||||
});
|
||||
|
||||
it('no root path and regexp uri', ()=> {
|
||||
const router = new frontexpress.Router();
|
||||
const middleware = new frontexpress.Middleware();
|
||||
|
||||
router.get(/^\/route1/, middleware);
|
||||
|
||||
const r = router.getRoutes('/route1', 'GET');
|
||||
assert(r.length === 1);
|
||||
assert(r[0].uri instanceof RegExp);
|
||||
assert(r[0].uri.toString() === new RegExp('^\/route1').toString());
|
||||
assert(r[0].method === 'GET');
|
||||
assert(r[0].middleware === middleware);
|
||||
});
|
||||
|
||||
it('with root path /route1 and path /subroute', () => {
|
||||
const router = new frontexpress.Router('/route1');
|
||||
|
||||
router.get('/subroute', new frontexpress.Middleware());
|
||||
|
||||
const r = router.getRoutes('/route1/subroute', 'GET');
|
||||
assert(r.length === 1);
|
||||
assert(r[0].uri === '/route1/subroute');
|
||||
});
|
||||
|
||||
it('with root path /route1 and no path uri', () => {
|
||||
const router = new frontexpress.Router('/route1');
|
||||
|
||||
router.get(new frontexpress.Middleware());
|
||||
|
||||
const r = router.getRoutes('/route1', 'GET');
|
||||
assert(r.length === 1);
|
||||
assert(r[0].uri === '/route1');
|
||||
});
|
||||
|
||||
it('duplicate / in route', () => {
|
||||
const router = new frontexpress.Router('/route1/');
|
||||
|
||||
router.get('/subroute', new frontexpress.Middleware());
|
||||
|
||||
const r = router.getRoutes('/route1/subroute', 'GET');
|
||||
assert(r.length === 1);
|
||||
assert(r[0].uri === '/route1/subroute');
|
||||
});
|
||||
|
||||
it('spaces in route', () => {
|
||||
let router = new frontexpress.Router(' /route1 ');
|
||||
|
||||
router.get('/subroute ', new frontexpress.Middleware());
|
||||
|
||||
let r = router.getRoutes('/route1/subroute', 'GET');
|
||||
assert(r.length === 1);
|
||||
assert(r[0].uri === '/route1/subroute');
|
||||
|
||||
// ----
|
||||
|
||||
router = new frontexpress.Router(' /route1 ');
|
||||
|
||||
router.get(new frontexpress.Middleware());
|
||||
|
||||
r = router.getRoutes('/route1', 'GET');
|
||||
assert(r.length === 1);
|
||||
assert(r[0].uri === '/route1');
|
||||
});
|
||||
});
|
||||
|
||||
describe('all method', () => {
|
||||
it('no arguments', () => {
|
||||
const router = new frontexpress.Router('/route1');
|
||||
assert.throws(router.all, TypeError);
|
||||
});
|
||||
|
||||
it('bad argument', () => {
|
||||
const router = new frontexpress.Router('/route1');
|
||||
try {
|
||||
router.all('ddd');
|
||||
} catch(ex) {
|
||||
assert(ex instanceof TypeError);
|
||||
}
|
||||
try {
|
||||
router.all('ddd', 'eee');
|
||||
} catch(ex) {
|
||||
assert(ex instanceof TypeError);
|
||||
}
|
||||
try {
|
||||
router.all('ddd', new frontexpress.Router());
|
||||
} catch(ex) {
|
||||
assert(ex instanceof TypeError);
|
||||
}
|
||||
});
|
||||
|
||||
it('only middleware as argument', () => {
|
||||
const router = new frontexpress.Router('/route1');
|
||||
const middleware = new frontexpress.Middleware();
|
||||
|
||||
const spied_methods = [];
|
||||
for (const method of Object.keys(HTTP_METHODS)) {
|
||||
spied_methods.push(sinon.spy(router, method.toLowerCase()));
|
||||
}
|
||||
|
||||
router.all(middleware);
|
||||
|
||||
for (const spied_method of spied_methods) {
|
||||
assert(spied_method.calledOnce);
|
||||
}
|
||||
});
|
||||
|
||||
it('with path /route1 and middleware as arguments', () => {
|
||||
const router = new frontexpress.Router();
|
||||
const middleware = new frontexpress.Middleware();
|
||||
|
||||
const spied_methods = [];
|
||||
for (const method of Object.keys(HTTP_METHODS)) {
|
||||
spied_methods.push(sinon.spy(router, method.toLowerCase()));
|
||||
}
|
||||
|
||||
router.all('/route1', middleware);
|
||||
|
||||
for (const spied_method of spied_methods) {
|
||||
assert(spied_method.calledOnce);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('one http (get) method', () => {
|
||||
it('no arguments', () => {
|
||||
const router = new frontexpress.Router('/route1');
|
||||
assert.throws(router.get, TypeError);
|
||||
});
|
||||
|
||||
it('bad argument', () => {
|
||||
const router = new frontexpress.Router('/route1');
|
||||
try {
|
||||
router.get('ddd');
|
||||
} catch(ex) {
|
||||
assert(ex instanceof TypeError);
|
||||
}
|
||||
try {
|
||||
router.get('ddd', 'eee');
|
||||
} catch(ex) {
|
||||
assert(ex instanceof TypeError);
|
||||
}
|
||||
try {
|
||||
router.get('ddd', new frontexpress.Router());
|
||||
} catch(ex) {
|
||||
assert(ex instanceof TypeError);
|
||||
}
|
||||
});
|
||||
|
||||
it('only middleware as argument', () => {
|
||||
const router = new frontexpress.Router('/');
|
||||
const middleware = new frontexpress.Middleware();
|
||||
|
||||
router.get(middleware);
|
||||
|
||||
const r1 = router.getRoutes('/', 'GET');
|
||||
assert(r1.length === 1);
|
||||
assert(r1[0].uri === '/');
|
||||
assert(r1[0].method === 'GET');
|
||||
assert(r1[0].middleware === middleware);
|
||||
});
|
||||
|
||||
it('with path /route1 and middleware as arguments', () => {
|
||||
const router = new frontexpress.Router();
|
||||
const middleware = new frontexpress.Middleware();
|
||||
|
||||
router.get('/route1', middleware);
|
||||
|
||||
const r1 = router.getRoutes('/route1', 'GET');
|
||||
assert(r1.length === 1);
|
||||
assert(r1[0].uri === '/route1');
|
||||
assert(r1[0].method === 'GET');
|
||||
assert(r1[0].middleware === middleware);
|
||||
});
|
||||
});
|
||||
});
|
44
webpack.config.babel.js
Executable file
44
webpack.config.babel.js
Executable file
@ -0,0 +1,44 @@
|
||||
import webpack from 'webpack';
|
||||
|
||||
const cmd = (arr) => { return process.argv.indexOf(arr) > -1; }
|
||||
const production = cmd('-p') || cmd('--optimize-minimize');
|
||||
const debug = cmd('-d') || cmd('--debug');
|
||||
|
||||
const options = {
|
||||
uglifyjs: undefined
|
||||
};
|
||||
|
||||
const webpackConfig = {
|
||||
entry: "./lib/frontexpress.js",
|
||||
output: {
|
||||
path: __dirname,
|
||||
filename: 'dist/frontexpress.js'
|
||||
},
|
||||
devtool: 'inline-source-map',
|
||||
module: {
|
||||
preLoaders: [
|
||||
// Javascript
|
||||
{ test: /\.js$/, loader: 'eslint', exclude: /node_modules/ }
|
||||
],
|
||||
loaders: [
|
||||
{ test: /lib\/.+\.js$/, loader: 'babel', query: { presets: ['es2015'] } },
|
||||
{ test: /\.css$/, loader: 'style!css' }
|
||||
]
|
||||
},
|
||||
eslint: {
|
||||
failOnWarning: false,
|
||||
failOnError: true
|
||||
},
|
||||
plugins: []
|
||||
};
|
||||
|
||||
if (production) {
|
||||
webpackConfig.plugins.push(
|
||||
// Prevents the inclusion of duplicate code
|
||||
new webpack.optimize.DedupePlugin(),
|
||||
// Add UglifyJs options to the compiler
|
||||
new webpack.optimize.UglifyJsPlugin(options.uglifyjs)
|
||||
);
|
||||
}
|
||||
|
||||
export default webpackConfig;
|
Loading…
Reference in New Issue
Block a user