Compare commits
81 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
acebd1e5c0 | ||
|
d678d066c3 | ||
|
2245de31d7 | ||
|
065b500561 | ||
|
75b130ef91 | ||
|
72598f09e1 | ||
|
d7cfd6d9f3 | ||
|
21f5dea449 | ||
|
a1e56dc6c3 | ||
|
34b7580d18 | ||
|
4ab3fa2626 | ||
|
5b45250bf5 | ||
|
787a22ed44 | ||
|
79062fe337 | ||
|
f90743d4cf | ||
|
5620117e6c | ||
|
b22725acd0 | ||
|
abc9ffc405 | ||
|
a9b90040bd | ||
|
41b4a5ed19 | ||
|
29f07a7259 | ||
|
c6fa4e2919 | ||
|
3fad8330f6 | ||
|
03c6284aa8 | ||
|
316e8d1ac0 | ||
|
c35bb0dd69 | ||
|
92cae2c0a4 | ||
|
b2ab39b942 | ||
|
02b3fc1edb | ||
|
465c53cd4f | ||
|
a170883d93 | ||
|
12621d0883 | ||
|
ff83e279f1 | ||
|
00f5a9d1cc | ||
|
6659ca98b7 | ||
|
2702bb8ff6 | ||
|
474274c71f | ||
|
ef16262c24 | ||
|
0949461127 | ||
|
c7310adf2a | ||
|
9fc6a6fb2b | ||
|
ab2feb1c30 | ||
|
009ff8fdb9 | ||
|
fc3f7e1d7d | ||
|
b463c05733 | ||
|
27da6f94bf | ||
|
893d39e765 | ||
|
8dfd3731fd | ||
|
1d10bfe2fa | ||
|
a5da97c3bd | ||
|
4cc0a9f511 | ||
|
79ac822adb | ||
|
089098a94f | ||
|
f15cfed2e9 | ||
|
5d75c4a259 | ||
|
dd1d972f57 | ||
|
965dd5f08d | ||
|
b820c2c6d0 | ||
|
e17d46ad20 | ||
|
a8edd83f86 | ||
|
74bcccec72 | ||
|
be20f98498 | ||
|
2bcf695198 | ||
|
390cd54a48 | ||
|
9c7681e2b4 | ||
|
534f529b0c | ||
|
9223011ebe | ||
|
c42133a421 | ||
|
6d2dd79194 | ||
|
409c4cd891 | ||
|
0a5bfc585e | ||
|
13c39d9e13 | ||
|
6e04aa53cf | ||
|
ad83bfb020 | ||
|
15637d53bd | ||
|
bf423fb19c | ||
|
7402f4ba22 | ||
|
48f8d7e333 | ||
|
117e5c6974 | ||
|
32ee6a8264 | ||
|
cd94fe18ed |
9
.codeclimate.yml
Normal file
9
.codeclimate.yml
Normal file
@ -0,0 +1,9 @@
|
||||
engines:
|
||||
duplication:
|
||||
enabled: true
|
||||
config:
|
||||
languages:
|
||||
- javascript
|
||||
ratings:
|
||||
paths:
|
||||
- "lib/**/*"
|
@ -1,2 +1,5 @@
|
||||
frontexpress.js
|
||||
frontexpress.min.js
|
||||
frontexpress.min.js.map
|
||||
coverage
|
||||
dist
|
@ -1,2 +0,0 @@
|
||||
test
|
||||
coverage
|
@ -6,4 +6,4 @@ before_script:
|
||||
- npm install coveralls
|
||||
|
||||
after_script:
|
||||
- cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js
|
||||
- cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js
|
||||
|
400
README.md
400
README.md
@ -1,32 +1,272 @@
|
||||
# frontexpress
|
||||

|
||||
|
||||
Minimalist front end router framework a la [express](http://expressjs.com/)
|
||||
A simple vanilla JavaScript router a la [ExpressJS](http://expressjs.com/).
|
||||
|
||||
Code the front-end like the back-end.
|
||||
|
||||
[frontexpress demo](https://github.com/camelaissani/frontexpress-demo)
|
||||
|
||||
[](https://travis-ci.org/camelaissani/frontexpress)
|
||||
[](https://codeclimate.com/github/camelaissani/frontexpress)
|
||||
[](https://coveralls.io/github/camelaissani/frontexpress?branch=master)
|
||||
|
||||
```js
|
||||
import frontexpress from 'frontexpress';
|
||||
const app = frontexpress();
|
||||
|
||||
// listen HTTP GET request on path (/)
|
||||
app.get('/', (req, res) => {
|
||||
window.alert('Hello World');
|
||||
});
|
||||
|
||||
// start listening frontend application requests
|
||||
app.listen();
|
||||
```
|
||||

|
||||

|
||||
|
||||
## Installation
|
||||
|
||||
### From npm repository
|
||||
|
||||
```bash
|
||||
$ npm install frontexpress
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
### From bower repository
|
||||
|
||||
The quickest way to get started with frontexpress is to clone [frontexpress-demo](https://github.com/camelaissani/frontexpress-demo)
|
||||
```bash
|
||||
$ bower install frontexpress
|
||||
```
|
||||
|
||||
### From CDN
|
||||
|
||||
On [jsDelivr](https://cdn.jsdelivr.net/npm/frontexpress@1.2.0/frontexpress.min.js)
|
||||
|
||||
## Usage
|
||||
|
||||
```js
|
||||
import frontexpress from 'frontexpress';
|
||||
|
||||
// Front-end application
|
||||
const app = frontexpress();
|
||||
|
||||
// front-end logic on navigation path "/page1"
|
||||
app.get('/page1', (req, res) => {
|
||||
document.querySelector('.content').innerHTML = res.responseText;
|
||||
});
|
||||
|
||||
// front-end logic on navigation path "/page2"
|
||||
app.get('/page2', (req, res) => {
|
||||
document.querySelector('.content').innerHTML = res.responseText;
|
||||
});
|
||||
|
||||
// start front-end application
|
||||
app.listen();
|
||||
```
|
||||
|
||||
### Routes
|
||||
|
||||
Listen GET requests on path /hello:
|
||||
|
||||
```js
|
||||
app.get('/hello', (req, res) => {
|
||||
window.alert('Hello World');
|
||||
});
|
||||
```
|
||||
|
||||
Listen POST requests on path /item:
|
||||
|
||||
```js
|
||||
app.post('/item', (req, res) => {
|
||||
window.alert('Got a POST request at /item');
|
||||
});
|
||||
```
|
||||
|
||||
Listen GET requests on path starting with /api/:
|
||||
|
||||
```js
|
||||
app.get(/^api\//, (req, res) => {
|
||||
console.log(`api was requested ${req.uri}`);
|
||||
});
|
||||
```
|
||||
|
||||
Get parameters from path
|
||||
|
||||
```js
|
||||
app.get('/product/:id', (req, res) => {
|
||||
// if we have /product/42 then
|
||||
// req.params.id = 42
|
||||
});
|
||||
```
|
||||
|
||||
```js
|
||||
app.get('/user/:firstname?/:lastname', (req, res) => {
|
||||
// if we have /user/camel/aissani then
|
||||
// req.params.firstname = 'camel'
|
||||
// req.params.lastname = 'aissani'
|
||||
|
||||
// if we have /user/aissani then
|
||||
// req.params.firstname = undefined
|
||||
// req.params.lastname = 'aissani'
|
||||
});
|
||||
```
|
||||
|
||||
```js
|
||||
app.get('/user/:id', (req, res) => {
|
||||
// if we have /user/1,2,3 then
|
||||
// req.params.id = [1,2,3]
|
||||
});
|
||||
```
|
||||
You can have the full capabilities of Express-style path with this plugin [frontexpress-path-to-regexp](https://github.com/camelaissani/frontexpress-path-to-regexp)
|
||||
|
||||
### Middleware object
|
||||
|
||||
The middleware object gives access to more hooks
|
||||
|
||||
```js
|
||||
class MyMiddleware = new Middleware {
|
||||
entered(req) {
|
||||
// before request sent
|
||||
}
|
||||
|
||||
updated(req, res) {
|
||||
// after request sent
|
||||
// res has the request response
|
||||
window.alert('Hello World');
|
||||
}
|
||||
|
||||
exited(req) {
|
||||
// before a new request sent
|
||||
}
|
||||
|
||||
failed(req, res) {
|
||||
// on request failed
|
||||
}
|
||||
|
||||
next() {
|
||||
// for chaining
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
app.get('/hello', new MyMiddleware());
|
||||
```
|
||||
|
||||
### Chain handlers
|
||||
|
||||
You can provide multiple handlers functions on a navigation path. Invoking ```next()``` function allows to chain the handlers.
|
||||
At the opposite, when the ```next()``` method is not called the handler chain is stopped.
|
||||
|
||||
```js
|
||||
const h1 = (req, res, next) => { console.log('h1!'); next(); };
|
||||
const h2 = (req, res, next) => { console.log('h2!') };
|
||||
const h3 = (req, res, next) => { console.log('h3!'); next(); };
|
||||
|
||||
app.get('/example/a', h1);
|
||||
app.get('/example/a', h2);
|
||||
app.get('/example/a', h3);
|
||||
```
|
||||
|
||||
On navigation on path /example/a, the browser console displays the following:
|
||||
|
||||
```
|
||||
h1!
|
||||
h2!
|
||||
```
|
||||
|
||||
h3 is ignored because ```next()``` function was not invoked.
|
||||
|
||||
#### app.route()
|
||||
|
||||
You can create chainable route handlers for a route path by using ```app.route()```.
|
||||
|
||||
```js
|
||||
app.route('/book')
|
||||
.get((req, res) => { console.log('Get a random book') })
|
||||
.post((req, res) => { console.log('Add a book') })
|
||||
.put((req, res) => { console.log('Update the book') });
|
||||
```
|
||||
|
||||
#### frontexpress.Router
|
||||
|
||||
Use the ```frontexpress.Router``` class to create modular, mountable route handlers.
|
||||
|
||||
Create a router file named ```birds.js```:
|
||||
|
||||
```js
|
||||
import frontexpress from 'frontexpress';
|
||||
|
||||
const router = frontexpress.Router();
|
||||
|
||||
// specific middleware for this router
|
||||
router.use((req, res, next) => {
|
||||
console.log(`Time: ${Date.now()}`);
|
||||
next();
|
||||
});
|
||||
|
||||
// listen navigation on the home page
|
||||
router.get('/', (req, res) => {
|
||||
document.querySelector('.content').innerHTML = '<p>Birds home page</p>';
|
||||
});
|
||||
|
||||
// listen navigation on the about page
|
||||
router.get('/about', (req, res) => {
|
||||
document.querySelector('.content').innerHTML = '<p>About birds</p>';
|
||||
});
|
||||
|
||||
export default router;
|
||||
```
|
||||
|
||||
Then, load the router module in the app:
|
||||
|
||||
```js
|
||||
import birds from './birds';
|
||||
...
|
||||
app.use('/birds', birds);
|
||||
```
|
||||
|
||||
## Plugins
|
||||
|
||||
### Extend frontexpress via plugins:
|
||||
|
||||
- [frontexpress-path-to-regexp](https://github.com/camelaissani/frontexpress-path-to-regexp): Add the ability to support Express-style path string such as /user/:name, /user*...
|
||||
|
||||
Others are coming
|
||||
|
||||
### Write your own plugin
|
||||
|
||||
It consists to simply create an object with two properties:
|
||||
- **name**: the name of your plugin
|
||||
- **plugin**: the function containing the implementation
|
||||
|
||||
Let's assume that we have implemented this plugin in the `frontexpress-my-plugin.js` file as below:
|
||||
|
||||
```js
|
||||
export default {
|
||||
name: 'My plugin',
|
||||
plugin(app) {
|
||||
// the plugin implementation goes here
|
||||
|
||||
// Some ideas
|
||||
// you can get settings
|
||||
// const transformer = app.get('http GET transformer');
|
||||
//
|
||||
// you can set settings
|
||||
// app.set('http requester', {
|
||||
// fetch() {
|
||||
// ...
|
||||
// }});
|
||||
//
|
||||
// you can complete routes
|
||||
// app.get(...)
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
To use it:
|
||||
|
||||
```js
|
||||
import frontexpress from 'frontexpress';
|
||||
import myPlugin from 'frontexpress-my-plugin';
|
||||
|
||||
// Front-end application
|
||||
const app = frontexpress();
|
||||
|
||||
// tell to frontexpress to use your plugin
|
||||
app.use(myPlugin);
|
||||
```
|
||||
|
||||
## More
|
||||
|
||||
[API](https://github.com/camelaissani/frontexpress/blob/master/docs/api.md)
|
||||
|
||||
## Tests
|
||||
|
||||
@ -44,130 +284,6 @@ $ npm install
|
||||
$ npm test
|
||||
```
|
||||
|
||||
## Disclaimer
|
||||
|
||||
>
|
||||
> In this first version of frontexpress, the API is not completely the miror of the expressjs one.
|
||||
>
|
||||
> There are some missing methods. Currently, the use, get, post... methods having a middlewares array as parameter are not available.
|
||||
> The string pattern to define route paths is not yet implemented.
|
||||
>
|
||||
> Obviously, the objective is to have the same API as expressjs when the methods make sense browser side.
|
||||
>
|
||||
|
||||
## Routing
|
||||
|
||||
### Basic routing
|
||||
|
||||
Routing allows to link the frontend application with HTTP requests to a particular URI (or path).
|
||||
The link can be specific to an HTTP request method (GET, POST, and so on).
|
||||
|
||||
The following examples illustrate how to define simple routes.
|
||||
|
||||
Listen an HTTP GET request on URI (/):
|
||||
|
||||
```js
|
||||
app.get('/', (req, res) => {
|
||||
window.alert('Hello World');
|
||||
});
|
||||
```
|
||||
|
||||
Listen an HTTP POST request on URI (/):
|
||||
|
||||
```js
|
||||
app.post('/', (req, res) => {
|
||||
window.alert('Got a POST request at /');
|
||||
});
|
||||
```
|
||||
|
||||
### Route paths
|
||||
|
||||
Route paths, in combination with a request method, define the endpoints at which requests can be made.
|
||||
Route paths can be strings (see basic routing section), or regular expressions.
|
||||
|
||||
This route path matches all GET request paths which start with (/api/):
|
||||
|
||||
```js
|
||||
app.get(/^api\//, (req, res) => {
|
||||
console.log(`api was requested ${req.uri}`);
|
||||
});
|
||||
```
|
||||
|
||||
### Route handlers
|
||||
|
||||
You can provide multiple callback functions to handle a request. Invoking ```next()``` function allows to pass the control to subsequent routes.
|
||||
Whether ```next()``` method is not called the handler chain is stoped
|
||||
|
||||
```js
|
||||
const h1 = (req, res, next) => { console.log('h1!'); next(); };
|
||||
const h2 = (req, res, next) => { console.log('h2!') };
|
||||
const h3 = (req, res, next) => { console.log('h3!'); next(); };
|
||||
|
||||
app.get('/example/a', h1);
|
||||
app.get('/example/a', h2);
|
||||
app.get('/example/a', h3);
|
||||
```
|
||||
|
||||
A response to a GET request on path (/example/a) displays:
|
||||
|
||||
```
|
||||
h1!
|
||||
h2!
|
||||
```
|
||||
|
||||
h3 is ignored because ```next()``` function was not invoked.
|
||||
|
||||
### app.route()
|
||||
|
||||
You can create chainable route handlers for a route path by using ```app.route()```.
|
||||
|
||||
```js
|
||||
app.route('/book')
|
||||
.get((req, res) => { console.log('Get a random book') })
|
||||
.post((req, res) => { console.log('Add a book') })
|
||||
.put((req, res) => { console.log('Update the book') });
|
||||
```
|
||||
|
||||
### frontexpress.Router
|
||||
|
||||
Use the ```frontexpress.Router``` class to create modular, mountable route handlers.
|
||||
|
||||
Create a router file named ```birds.js``` in the app directory, with the following content:
|
||||
|
||||
```js
|
||||
import frontexpress from 'frontexpress';
|
||||
|
||||
const router = frontexpress.Router();
|
||||
|
||||
// middleware that is specific to this router
|
||||
router.use((req, res, next) => {
|
||||
console.log(`Time: ${Date.now()}`);
|
||||
next();
|
||||
});
|
||||
|
||||
// react on home page route
|
||||
router.get('/', (req, res) => {
|
||||
document.querySelector('.content').innerHTML = '<p>Birds home page</p>';
|
||||
});
|
||||
|
||||
// react on about route
|
||||
router.get('/about', (req, res) => {
|
||||
document.querySelector('.content').innerHTML = '<p>About birds</p>';
|
||||
});
|
||||
|
||||
export default router;
|
||||
```
|
||||
|
||||
Then, load the router module in the app:
|
||||
|
||||
```js
|
||||
import birds from './birds';
|
||||
...
|
||||
app.use('/birds', birds);
|
||||
```
|
||||
|
||||
The app will now be able to react on requests (/birds) and (/birds/about)
|
||||
|
||||
## License
|
||||
|
||||
[MIT](LICENSE)
|
||||
[MIT](LICENSE)
|
||||
|
33
bower.json
Normal file
33
bower.json
Normal file
@ -0,0 +1,33 @@
|
||||
{
|
||||
"name": "frontexpress",
|
||||
"description": "Frontexpress manages routes in browser like ExpressJS on Node",
|
||||
"main": "dist/frontexpress.js",
|
||||
"authors": [
|
||||
"Camel Aissani <camel.aissani@gmail.com> (https://nuageprive.fr)"
|
||||
],
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
"front",
|
||||
"framework",
|
||||
"web",
|
||||
"router",
|
||||
"middleware",
|
||||
"app",
|
||||
"api",
|
||||
"express",
|
||||
"frontexpress"
|
||||
],
|
||||
"homepage": "https://github.com/camelaissani/frontexpress",
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"index.js",
|
||||
"gzipsize.js",
|
||||
"rollup.config.dev.js",
|
||||
"rollup.config.prod.js",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"test",
|
||||
"tests",
|
||||
"coverage"
|
||||
]
|
||||
}
|
109
docs/api.md
Normal file
109
docs/api.md
Normal file
@ -0,0 +1,109 @@
|
||||
# API
|
||||
|
||||
| | Method | Short description |
|
||||
| :------------- | :--------------| :----------------- |
|
||||
|Frontexpress |||
|
||||
||[frontexpress()](https://github.com/camelaissani/frontexpress/blob/master/docs/frontexpress.md#frontexpress-1)|Creates an instance of application|
|
||||
||[frontexpress.Router()](https://github.com/camelaissani/frontexpress/blob/master/docs/frontexpress.md#frontexpressrouter)|Creates a Router object|
|
||||
||[frontexpress.Middleware](https://github.com/camelaissani/frontexpress/blob/master/docs/frontexpress.md#frontexpressmiddleware)|Returns the Middleware class |
|
||||
||||
|
||||
|Application |||
|
||||
||[set(setting, value)](https://github.com/camelaissani/frontexpress/blob/master/docs/application.md#applicationsetsetting-val)|Assigns a setting|
|
||||
||[listen(callback)](https://github.com/camelaissani/frontexpress/blob/master/docs/application.md#applicationlistencallback)|Starts the application|
|
||||
||[route(uri)](https://github.com/camelaissani/frontexpress/blob/master/docs/application.md#applicationrouteuri)|Gets a Router initialized with a root path|
|
||||
||[use(uri, middleware)](https://github.com/camelaissani/frontexpress/blob/master/docs/application.md#applicationuseuri-middleware)|Sets a middleware|
|
||||
||||
|
||||
||[get(uri, middleware)](https://github.com/camelaissani/frontexpress/blob/master/docs/application.md#applicationgeturi-middleware-applicationposturi-middleware)|Applies a middleware on given path for a GET request|
|
||||
||[post(uri, middleware)](https://github.com/camelaissani/frontexpress/blob/master/docs/application.md#applicationgeturi-middleware-applicationposturi-middleware)|Applies a middleware on given path for a POST request|
|
||||
||[put(uri, middleware)](https://github.com/camelaissani/frontexpress/blob/master/docs/application.md#applicationgeturi-middleware-applicationposturi-middleware)|Applies a middleware on given path for a PUT request|
|
||||
||[delete(uri, middleware)](https://github.com/camelaissani/frontexpress/blob/master/docs/application.md#applicationgeturi-middleware-applicationposturi-middleware)|Applies a middleware on given path for a DELETE request|
|
||||
||||
|
||||
||[httpGet(request, success, failure)](https://github.com/camelaissani/frontexpress/blob/master/docs/application.md#applicationhttpgetrequest-success-failure-applicationhttppostrequest-success-failure)|Invokes a GET ajax request|
|
||||
||[httpPost(request, success, failure)](https://github.com/camelaissani/frontexpress/blob/master/docs/application.md#applicationhttpgetrequest-success-failure-applicationhttppostrequest-success-failure)|Invokes a POST ajax request|
|
||||
||[httpPut(request, success, failure)](https://github.com/camelaissani/frontexpress/blob/master/docs/application.md#applicationhttpgetrequest-success-failure-applicationhttppostrequest-success-failure)|Invokes a PUT ajax request|
|
||||
||[httpDelete(request, success, failure)](https://github.com/camelaissani/frontexpress/blob/master/docs/application.md#applicationhttpgetrequest-success-failure-applicationhttppostrequest-success-failure)|Invokes a DELETE ajax request|
|
||||
||||
|
||||
|Router |||
|
||||
||[use(middleware)](https://github.com/camelaissani/frontexpress/blob/master/docs/router.md#routerusemiddleware)|Sets a middleware|
|
||||
||[all(middleware)](https://github.com/camelaissani/frontexpress/blob/master/docs/router.md#routerallmiddleware)|Sets a middleware on all HTTP method requests|
|
||||
||||
|
||||
||[get(uri, middleware)](https://github.com/camelaissani/frontexpress/blob/master/docs/router.md#routergeturi-middleware-routerposturi-middleware)|Applies a middleware on given path for a GET request|
|
||||
||[post(uri, middleware)](https://github.com/camelaissani/frontexpress/blob/master/docs/router.md#routergeturi-middleware-routerposturi-middleware)|Applies a middleware on given path for a POST request|
|
||||
||[put(uri, middleware)](https://github.com/camelaissani/frontexpress/blob/master/docs/router.md#routergeturi-middleware-routerposturi-middleware)|Applies a middleware on given path for a PUT request|
|
||||
||[delete(uri, middleware)](https://github.com/camelaissani/frontexpress/blob/master/docs/router.md#routergeturi-middleware-routerposturi-middleware)|Applies a middleware on given path for a DELETE request|
|
||||
||||
|
||||
|Middleware |||
|
||||
||[entered(request)](https://github.com/camelaissani/frontexpress/blob/master/docs/middleware.md#middlewareenteredrequest)|Invoked by the app before an ajax request is sent|
|
||||
||[exited(request)](https://github.com/camelaissani/frontexpress/blob/master/docs/middleware.md#middlewareexitedrequest)|Invoked by the app before a new ajax request is sent|
|
||||
||[updated(request, response)](https://github.com/camelaissani/frontexpress/blob/master/docs/middleware.md#middlewareupdatedrequest-response)|Invoked by the app after an ajax request has responded|
|
||||
||[failed(request, response)](https://github.com/camelaissani/frontexpress/blob/master/docs/middleware.md#middlewarefailedrequest-response)|Invoked by the app after an ajax request has failed|
|
||||
||[next()](https://github.com/camelaissani/frontexpress/blob/master/docs/middleware.md#middlewarenext)|Allows to break the middleware chain execution|
|
||||
|
||||
# middleware function
|
||||
|
||||
After registering a middleware function, the application invokes it with these parameters:
|
||||
|
||||
```js
|
||||
(request, response, next) => {
|
||||
next();
|
||||
}
|
||||
```
|
||||
|
||||
**request**: `Object`, the ajax request information sent by the app
|
||||
|
||||
**response**: `Object`, the response of request
|
||||
|
||||
**next**: `Function`, the `next()` function to call to not break the middleware execution chain
|
||||
|
||||
# request object
|
||||
|
||||
```js
|
||||
{
|
||||
method,
|
||||
uri,
|
||||
headers,
|
||||
data,
|
||||
history: {
|
||||
state,
|
||||
title,
|
||||
uri
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**method**: `String`, HTTP methods 'GET', 'POST'...
|
||||
|
||||
**uri**: `String`, path
|
||||
|
||||
**headers**: `Object`, custom HTTP headers
|
||||
|
||||
**data**: `Object`, data attached to the request
|
||||
|
||||
**history**: `Object`, object with properties state, title and uri
|
||||
|
||||
>**If the history object is set, it will activate the browser history management.** See [browser pushState() method](https://developer.mozilla.org/en-US/docs/Web/API/History_API#The_pushState()_method) for more information about state, title, and uri (url).
|
||||
> uri and history.uri can be different.
|
||||
|
||||
**params**: `Object`, object containing the path parameters
|
||||
|
||||
# response object
|
||||
|
||||
```js
|
||||
{
|
||||
status,
|
||||
statusText,
|
||||
responseText,
|
||||
errorThrown,
|
||||
errors
|
||||
}
|
||||
```
|
||||
|
||||
**status**: `Number`, HTTP status 200, 404, 401, 500...
|
||||
|
||||
**statusText**: `String`
|
||||
|
||||
**responseText**: `String` response content
|
||||
|
||||
**errorThrown**: `Object` exception thrown (if request fails)
|
||||
|
||||
**errors**: `String` error description (if request fails)
|
133
docs/application.md
Normal file
133
docs/application.md
Normal file
@ -0,0 +1,133 @@
|
||||
# Application
|
||||
|
||||
|
||||
## Application.set(setting, val)
|
||||
|
||||
Assign `setting` to `val`, or return `setting`'s value.
|
||||
|
||||
```js
|
||||
app.set('foo', 'bar');
|
||||
app.set('foo');
|
||||
// => "bar"
|
||||
```
|
||||
|
||||
**Parameters**
|
||||
|
||||
**setting**: `String`, setting name
|
||||
|
||||
**val**: `*`, setting value
|
||||
|
||||
**Returns**: `app`, for chaining
|
||||
|
||||
|
||||
## Application.listen(callback)
|
||||
|
||||
Listen to the DOM initialization and the browser history state changes.
|
||||
|
||||
The callback function is called once the DOM has
|
||||
the `document.readyState` equals to 'interactive'.
|
||||
|
||||
```js
|
||||
app.listen(()=> {
|
||||
console.log('App is listening requests');
|
||||
console.log('DOM is ready!');
|
||||
});
|
||||
```
|
||||
|
||||
**Parameters**
|
||||
|
||||
**callback**: `function`, DOM is ready callback
|
||||
|
||||
|
||||
## Application.route(uri)
|
||||
|
||||
Create a new `Router` instance for the _uri_.
|
||||
See the Router api docs for details.
|
||||
|
||||
```js
|
||||
app.route('/');
|
||||
// => new Router instance
|
||||
```
|
||||
|
||||
**Parameters**
|
||||
|
||||
**uri**: `String`, path
|
||||
|
||||
**Returns**: `Router`, for chaining
|
||||
|
||||
|
||||
## Application.use(uri, middleware)
|
||||
|
||||
Use the given middleware function or object, with optional _uri_.
|
||||
Default _uri_ is "/".
|
||||
|
||||
```js
|
||||
// middleware function will be applied on path "/"
|
||||
app.use((req, res, next) => {console.log('Hello')});
|
||||
|
||||
// middleware object will be applied on path "/"
|
||||
app.use(new Middleware());
|
||||
```
|
||||
|
||||
**Parameters**
|
||||
|
||||
**uri**: `String`, path
|
||||
|
||||
**middleware**: `Middleware | function`, Middleware object or function
|
||||
|
||||
**Returns**: `app`, for chaining
|
||||
|
||||
|
||||
## Application.get(uri, middleware), Application.post(uri, middleware)...
|
||||
|
||||
Use the given middleware function or object, with optional _uri_ on
|
||||
HTTP methods: get, post, put, delete...
|
||||
Default _uri_ is "/".
|
||||
|
||||
```js
|
||||
// middleware function will be applied on path "/"
|
||||
app.get((req, res, next) => {console.log('Hello')});
|
||||
|
||||
// middleware object will be applied on path "/" and
|
||||
app.get(new Middleware());
|
||||
|
||||
// get a setting value
|
||||
app.set('foo', 'bar');
|
||||
app.get('foo');
|
||||
// => "bar"
|
||||
```
|
||||
|
||||
**Parameters**
|
||||
|
||||
**uri**: `String`, path (or setting only for get method)
|
||||
|
||||
**middleware**: `Middleware | function`, Middleware object or function
|
||||
|
||||
**Returns**: `app`, for chaining
|
||||
|
||||
|
||||
## Application.httpGet(request, success, failure), Application.httpPost(request, success, failure)...
|
||||
|
||||
Make an ajax request (get, post, put, delete...).
|
||||
|
||||
```js
|
||||
// HTTP GET method
|
||||
httpGet('/route1');
|
||||
|
||||
// HTTP GET method
|
||||
httpGet({uri: '/route1', data: {'p1': 'val1'});
|
||||
// uri invoked => /route1?p1=val1
|
||||
|
||||
// HTTP GET method with browser history management
|
||||
httpGet({uri: '/api/users', history: {state: {foo: "bar"}, title: 'users page', uri: '/view/users'});
|
||||
```
|
||||
|
||||
Samples above can be applied on other HTTP methods.
|
||||
|
||||
**Parameters**
|
||||
|
||||
**request**: `String | Object` uri or object containing uri, http headers, data, history
|
||||
|
||||
**success**: `Function` success callback
|
||||
|
||||
**failure**: `Function` failure callback
|
22
docs/frontexpress.md
Normal file
22
docs/frontexpress.md
Normal file
@ -0,0 +1,22 @@
|
||||
# Frontexpress
|
||||
|
||||
|
||||
## frontexpress()
|
||||
|
||||
Create a frontexpress application.
|
||||
|
||||
**Returns**: `Application`, the application
|
||||
|
||||
|
||||
## frontexpress.Router()
|
||||
|
||||
Expose the Router constructor
|
||||
|
||||
**Returns**: `Router`
|
||||
|
||||
|
||||
## frontexpress.Middleware
|
||||
|
||||
Expose the Middleware class
|
||||
|
||||
**Returns**: `Middleware`
|
63
docs/middleware.md
Normal file
63
docs/middleware.md
Normal file
@ -0,0 +1,63 @@
|
||||
# Middleware
|
||||
|
||||
## Middleware.entered(request)
|
||||
|
||||
Invoked by the app before ajax request are sent or
|
||||
during the DOM loading (document.readyState === 'loading').
|
||||
See Application#_callMiddlewareEntered documentation for details.
|
||||
|
||||
Override this method to add your custom behaviour
|
||||
|
||||
**Parameters**
|
||||
|
||||
**request**: `Object`
|
||||
|
||||
|
||||
## Middleware.exited(request)
|
||||
|
||||
Invoked by the app before a new ajax request is sent or before the DOM unloading.
|
||||
See Application#_callMiddlewareExited documentation for details.
|
||||
|
||||
Override this method to add your custom behaviour
|
||||
|
||||
**Parameters**
|
||||
|
||||
**request**: `Object`
|
||||
|
||||
|
||||
## Middleware.updated(request, response)
|
||||
|
||||
Invoked on ajax request responding or on DOM ready
|
||||
(document.readyState === 'interactive').
|
||||
See Application#_callMiddlewareUpdated documentation for details.
|
||||
|
||||
Override this method to add your custom behaviour
|
||||
|
||||
**Parameters**
|
||||
|
||||
**request**: `Object`
|
||||
|
||||
**response**: `Object`
|
||||
|
||||
|
||||
## Middleware.failed(request, response)
|
||||
|
||||
Invoked when ajax request fails.
|
||||
|
||||
Override this method to add your custom behaviour
|
||||
|
||||
**Parameters**
|
||||
|
||||
**request**: `Object`
|
||||
|
||||
**response**: `Object`
|
||||
|
||||
|
||||
## Middleware.next()
|
||||
|
||||
Allow the hand over to the next middleware object or function.
|
||||
|
||||
Override this method and return `false` to break execution of
|
||||
middleware chain.
|
||||
|
||||
**Returns**: `Boolean`, `true` by default
|
69
docs/router.md
Normal file
69
docs/router.md
Normal file
@ -0,0 +1,69 @@
|
||||
# Router
|
||||
|
||||
|
||||
## Router.use(middleware)
|
||||
|
||||
Use the given middleware function or object on this router.
|
||||
|
||||
```js
|
||||
// middleware function
|
||||
router.use((req, res, next) => {console.log('Hello')});
|
||||
|
||||
// middleware object
|
||||
router.use(new Middleware());
|
||||
```
|
||||
|
||||
**Parameters**
|
||||
|
||||
**middleware**: `Middleware | function`, Middleware object or function
|
||||
|
||||
**Returns**: `Router`, for chaining
|
||||
|
||||
|
||||
## Router.all(middleware)
|
||||
|
||||
Use the given middleware function or object on this router for
|
||||
all HTTP methods.
|
||||
|
||||
```js
|
||||
// middleware function
|
||||
router.all((req, res, next) => {console.log('Hello')});
|
||||
|
||||
// middleware object
|
||||
router.all(new Middleware());
|
||||
```
|
||||
|
||||
**Parameters**
|
||||
|
||||
**middleware**: `Middleware | function`, Middleware object or function
|
||||
|
||||
**Returns**: `Router`, for chaining
|
||||
|
||||
|
||||
## Router.get(uri, middleware), Router.post(uri, middleware)...
|
||||
|
||||
Use the given middleware function or object, with optional _uri_ on
|
||||
HTTP methods: get, post, put, delete...
|
||||
Default _uri_ is "/".
|
||||
|
||||
```js
|
||||
// middleware function will be applied on path "/"
|
||||
router.get((req, res, next) => {console.log('Hello')});
|
||||
|
||||
// middleware object will be applied on path "/" and
|
||||
router.get(new Middleware());
|
||||
|
||||
// middleware function will be applied on path "/user"
|
||||
router.post('/user', (req, res, next) => {console.log('Hello')});
|
||||
|
||||
// middleware object will be applied on path "/user" and
|
||||
router.post('/user', new Middleware());
|
||||
```
|
||||
|
||||
**Parameters**
|
||||
|
||||
**uri**: `String`, path
|
||||
|
||||
**middleware**: `Middleware | function`, Middleware object or function
|
||||
|
||||
**Returns**: `Router`, for chaining
|
1264
frontexpress.js
Normal file
1264
frontexpress.js
Normal file
File diff suppressed because it is too large
Load Diff
2
frontexpress.min.js
vendored
Normal file
2
frontexpress.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
frontexpress.min.js.map
Normal file
1
frontexpress.min.js.map
Normal file
File diff suppressed because one or more lines are too long
5
gzipsize.js
Normal file
5
gzipsize.js
Normal file
@ -0,0 +1,5 @@
|
||||
import bytesize from 'bytesize';
|
||||
|
||||
bytesize.gzipSize(__dirname + '/frontexpress.min.js', true, (err, size) => {
|
||||
console.log(`frontexpress size: ${size}(min+gzip)`);
|
||||
});
|
1
index.js
Normal file
1
index.js
Normal file
@ -0,0 +1 @@
|
||||
import frontexpress from 'expose?frontexpress!./lib/frontexpress.js';
|
@ -1,198 +1,292 @@
|
||||
/**
|
||||
* Module dependencies.
|
||||
* @private
|
||||
*/
|
||||
|
||||
import HTTP_METHODS from './methods';
|
||||
import Settings from './settings';
|
||||
import Router, {Route} from './router';
|
||||
import Middleware from './middleware';
|
||||
import Requester, {HTTP_METHODS} from './requester';
|
||||
|
||||
|
||||
/**
|
||||
* Application class.
|
||||
*/
|
||||
|
||||
export default class Application {
|
||||
|
||||
|
||||
/**
|
||||
* Initialize the application.
|
||||
*
|
||||
* - setup default configuration
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
|
||||
constructor() {
|
||||
this.routers = [];
|
||||
this.requester = new Requester();
|
||||
this.DOMLoading = false;
|
||||
|
||||
this.settingsDef = {
|
||||
'http-requester': (requester) => {this.requester = requester;}
|
||||
};
|
||||
this.settings = new Settings();
|
||||
this.plugins = [];
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
// Setting
|
||||
set(name, value) {
|
||||
const settingFn = this.settingsDef[name];
|
||||
if (!settingFn) {
|
||||
throw new ReferenceError(`unsupported setting ${name}`);
|
||||
|
||||
/**
|
||||
* Assign `setting` to `val`, or return `setting`'s value.
|
||||
*
|
||||
* app.set('foo', 'bar');
|
||||
* app.set('foo');
|
||||
* // => "bar"
|
||||
*
|
||||
* @param {String} setting
|
||||
* @param {*} [val]
|
||||
* @return {app} for chaining
|
||||
* @public
|
||||
*/
|
||||
|
||||
set(...args) {
|
||||
// get behaviour
|
||||
if (args.length === 1) {
|
||||
return this.settings.get([args]);
|
||||
}
|
||||
settingFn(value);
|
||||
|
||||
// set behaviour
|
||||
this.settings.set(...args);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
// Start listening requests
|
||||
|
||||
/**
|
||||
* Listen for DOM initialization and history state changes.
|
||||
*
|
||||
* The callback function is called once the DOM has
|
||||
* the `document.readyState` equals to 'interactive'.
|
||||
*
|
||||
* app.listen(()=> {
|
||||
* console.log('App is listening requests');
|
||||
* console.log('DOM is ready!');
|
||||
* });
|
||||
*
|
||||
*
|
||||
* @param {Function} callback
|
||||
* @public
|
||||
*/
|
||||
|
||||
listen(callback) {
|
||||
document.onreadystatechange = () => {
|
||||
const uri = window.location.pathname + window.location.search;
|
||||
const method = 'GET';
|
||||
const request = {method, uri};
|
||||
const response = {status: 200, statusText: 'OK'};
|
||||
const request = {method: 'GET', uri: window.location.pathname + window.location.search};
|
||||
const response = {status: 200, statusText: 'OK'};
|
||||
const currentRoutes = this._routes(request);
|
||||
|
||||
// gathers all routes impacted by the current browser location
|
||||
const currentRoutes = this._routes(uri, method);
|
||||
this._callMiddlewareMethod('entered', currentRoutes, request);
|
||||
|
||||
// listen dom events
|
||||
if (document.readyState === 'loading') {
|
||||
this.DOMLoading = true;
|
||||
this._callMiddlewareEntered(currentRoutes, request);
|
||||
} else if (document.readyState === 'interactive') {
|
||||
if (!this.DOMLoading) {
|
||||
this._callMiddlewareEntered(currentRoutes, request);
|
||||
}
|
||||
this._callMiddlewareUpdated(currentRoutes, request, response);
|
||||
if (callback) {
|
||||
callback(request, response);
|
||||
}
|
||||
// manage history
|
||||
window.onpopstate = (event) => {
|
||||
if (event.state) {
|
||||
const {request, response} = event.state;
|
||||
[
|
||||
'exited',
|
||||
'entered',
|
||||
'updated'
|
||||
].forEach(middlewareMethod => this._callMiddlewareMethod(middlewareMethod, this._routes(request), request, response));
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('beforeunload', () => {
|
||||
this._callMiddlewareExited();
|
||||
});
|
||||
// manage page loading/refreshing
|
||||
window.onbeforeunload = () => {
|
||||
this._callMiddlewareMethod('exited');
|
||||
};
|
||||
|
||||
const whenPageIsInteractiveFn = () => {
|
||||
this.plugins.forEach(pluginObject => pluginObject.plugin(this));
|
||||
this._callMiddlewareMethod('updated', currentRoutes, request, response);
|
||||
if (callback) {
|
||||
callback(request, response);
|
||||
}
|
||||
};
|
||||
|
||||
document.onreadystatechange = () => {
|
||||
// DOM ready state
|
||||
if (document.readyState === 'interactive') {
|
||||
whenPageIsInteractiveFn();
|
||||
}
|
||||
};
|
||||
|
||||
if (['interactive', 'complete'].indexOf(document.readyState) !== -1) {
|
||||
whenPageIsInteractiveFn();
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
// Routes
|
||||
|
||||
/**
|
||||
* Returns a new `Router` instance for the _uri_.
|
||||
* See the Router api docs for details.
|
||||
*
|
||||
* app.route('/');
|
||||
* // => new Router instance
|
||||
*
|
||||
* @param {String} uri
|
||||
* @return {Router} for chaining
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
|
||||
route(uri) {
|
||||
const router = new Router(uri);
|
||||
this.routers.push(router);
|
||||
return router;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Use the given middleware function or object, with optional _uri_.
|
||||
* Default _uri_ is "/".
|
||||
* Or use the given plugin
|
||||
*
|
||||
* // middleware function will be applied on path "/"
|
||||
* app.use((req, res, next) => {console.log('Hello')});
|
||||
*
|
||||
* // middleware object will be applied on path "/"
|
||||
* app.use(new Middleware());
|
||||
*
|
||||
* // use a plugin
|
||||
* app.use({
|
||||
* name: 'My plugin name',
|
||||
* plugin(application) {
|
||||
* // here plugin implementation
|
||||
* }
|
||||
* });
|
||||
*
|
||||
* @param {String} uri
|
||||
* @param {Middleware|Function|plugin} middleware object, middleware function, plugin
|
||||
* @return {app} for chaining
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
|
||||
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;
|
||||
let {baseUri, router, middleware, plugin} = toParameters(args);
|
||||
if (plugin) {
|
||||
this.plugins.push(plugin);
|
||||
} 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);
|
||||
}
|
||||
|
||||
_routes(uri, method) {
|
||||
const currentRoutes = [];
|
||||
for (const router of this.routers) {
|
||||
const routes = router.routes(uri, method);
|
||||
currentRoutes.push(...routes);
|
||||
}
|
||||
return currentRoutes;
|
||||
}
|
||||
|
||||
_callMiddlewareEntered(currentRoutes, request) {
|
||||
for (const route of currentRoutes) {
|
||||
if (route.middleware.entered) {
|
||||
route.middleware.entered(request);
|
||||
}
|
||||
if (route.middleware.next && !route.middleware.next()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_callMiddlewareUpdated(currentRoutes, request, response) {
|
||||
for (const route of currentRoutes) {
|
||||
route.visited = request;
|
||||
// calls middleware updated method
|
||||
if (route.middleware.updated) {
|
||||
route.middleware.updated(request, response);
|
||||
if (route.middleware.next && !route.middleware.next()) {
|
||||
break;
|
||||
}
|
||||
if (router) {
|
||||
router.baseUri = baseUri;
|
||||
} else if (middleware) {
|
||||
router = new Router(baseUri);
|
||||
HTTP_METHODS.forEach((method) => {
|
||||
router[method.toLowerCase()](middleware);
|
||||
});
|
||||
} else {
|
||||
// calls middleware method
|
||||
throw new TypeError('method takes at least a middleware or a router');
|
||||
}
|
||||
this.routers.push(router);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gather routes from all routers filtered by _uri_ and HTTP _method_.
|
||||
* See Router#routes() documentation for details.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
|
||||
_routes(request) {
|
||||
return this.routers.reduce((acc, router) => {
|
||||
acc.push(...router.routes(this, request));
|
||||
return acc;
|
||||
}, []);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Call `Middleware` method or middleware function on _currentRoutes_.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
|
||||
_callMiddlewareMethod(meth, currentRoutes, request, response) {
|
||||
if (meth === 'exited') {
|
||||
// currentRoutes, request, response params not needed
|
||||
this.routers.forEach((router) => {
|
||||
router.visited().forEach((route) => {
|
||||
if (route.middleware.exited) {
|
||||
route.middleware.exited(route.visited);
|
||||
route.visited = null;
|
||||
}
|
||||
});
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
currentRoutes.some((route) => {
|
||||
if (meth === 'updated') {
|
||||
route.visited = request;
|
||||
}
|
||||
|
||||
if (route.middleware[meth]) {
|
||||
route.middleware[meth](request, response);
|
||||
if (route.middleware.next && !route.middleware.next()) {
|
||||
return true;
|
||||
}
|
||||
} else if (meth !== 'entered') {
|
||||
// calls middleware method
|
||||
let breakMiddlewareLoop = true;
|
||||
const next = () => {
|
||||
breakMiddlewareLoop = false;
|
||||
};
|
||||
route.middleware(request, response, next);
|
||||
if (breakMiddlewareLoop) {
|
||||
break;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
_callMiddlewareExited() {
|
||||
|
||||
/**
|
||||
* Make an ajax request. Manage History#pushState if history object set.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
|
||||
_fetch(req, resolve, reject) {
|
||||
let {method, uri, headers, data, history} = req;
|
||||
|
||||
const httpMethodTransformer = this.get(`http ${method} transformer`);
|
||||
if (httpMethodTransformer) {
|
||||
const {uri: _uriFn, headers: _headersFn, data: _dataFn } = httpMethodTransformer;
|
||||
req.uri = _uriFn ? _uriFn({uri, headers, data}) : uri;
|
||||
req.headers = _headersFn ? _headersFn({uri, headers, data}) : headers;
|
||||
req.data = _dataFn ? _dataFn({uri, headers, data}) : data;
|
||||
}
|
||||
|
||||
// calls middleware exited method
|
||||
for (const router of this.routers) {
|
||||
const routes = router.visited();
|
||||
for (const route of routes) {
|
||||
if (route.middleware.exited) {
|
||||
route.middleware.exited(route.visited);
|
||||
route.visited = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_callMiddlewareFailed(currentRoutes, request, response) {
|
||||
for (const route of currentRoutes) {
|
||||
// calls middleware failed method
|
||||
if (route.middleware.failed) {
|
||||
route.middleware.failed(request, response);
|
||||
if (route.middleware.next && !route.middleware.next()) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// calls middleware method
|
||||
let breakMiddlewareLoop = true;
|
||||
const next = () => {
|
||||
breakMiddlewareLoop = false;
|
||||
};
|
||||
route.middleware(request, response, next);
|
||||
if (breakMiddlewareLoop) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
// Ajax request
|
||||
_fetch({method, uri, headers, data}, resolve, reject) {
|
||||
// calls middleware exited method
|
||||
this._callMiddlewareExited();
|
||||
this._callMiddlewareMethod('exited');
|
||||
|
||||
// gathers all routes impacted by the uri
|
||||
const currentRoutes = this._routes(uri, method);
|
||||
const currentRoutes = this._routes(req);
|
||||
|
||||
// calls middleware entered method
|
||||
this._callMiddlewareEntered(currentRoutes, {method, uri, headers, data});
|
||||
this._callMiddlewareMethod('entered', currentRoutes, req);
|
||||
|
||||
// invokes http request
|
||||
this.requester.fetch({uri, method},
|
||||
this.settings.get('http requester').fetch(req,
|
||||
(request, response) => {
|
||||
this._callMiddlewareUpdated(currentRoutes, request, response);
|
||||
if (history) {
|
||||
window.history.pushState({request, response}, history.title, history.uri);
|
||||
}
|
||||
this._callMiddlewareMethod('updated', currentRoutes, request, response);
|
||||
if (resolve) {
|
||||
resolve(request, response);
|
||||
}
|
||||
},
|
||||
(request, response) => {
|
||||
this._callMiddlewareFailed(currentRoutes, request, response);
|
||||
this._callMiddlewareMethod('failed', currentRoutes, request, response);
|
||||
if (reject) {
|
||||
reject(request, response);
|
||||
}
|
||||
@ -200,37 +294,71 @@ export default class Application {
|
||||
}
|
||||
}
|
||||
|
||||
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`);
|
||||
HTTP_METHODS.reduce((reqProto, method) => {
|
||||
|
||||
|
||||
/**
|
||||
* Use the given middleware function or object, with optional _uri_ on
|
||||
* HTTP methods: get, post, put, delete...
|
||||
* Default _uri_ is "/".
|
||||
*
|
||||
* // middleware function will be applied on path "/"
|
||||
* app.get((req, res, next) => {console.log('Hello')});
|
||||
*
|
||||
* // middleware object will be applied on path "/" and
|
||||
* app.get(new Middleware());
|
||||
*
|
||||
* // get a setting value
|
||||
* app.set('foo', 'bar');
|
||||
* app.get('foo');
|
||||
* // => "bar"
|
||||
*
|
||||
* @param {String} uri or setting
|
||||
* @param {Middleware|Function} middleware object or function
|
||||
* @return {app} for chaining
|
||||
* @public
|
||||
*/
|
||||
|
||||
const middlewareMethodName = method.toLowerCase();
|
||||
reqProto[middlewareMethodName] = function(...args) {
|
||||
let {baseUri, middleware, which} = toParameters(args);
|
||||
if (middlewareMethodName === 'get' && typeof which === 'string') {
|
||||
return this.settings.get(which);
|
||||
}
|
||||
|
||||
let baseUri, middleware, which;
|
||||
|
||||
if (args.length === 1) {
|
||||
[which,] = args;
|
||||
} else {
|
||||
[baseUri, which,] = args;
|
||||
if (!middleware) {
|
||||
throw new TypeError(`method takes a middleware ${middlewareMethodName === 'get' ? 'or a string' : ''}`);
|
||||
}
|
||||
|
||||
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);
|
||||
router[middlewareMethodName](baseUri, middleware);
|
||||
|
||||
this.routers.push(router);
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
// HTTP methods
|
||||
/**
|
||||
* Ajax request (get, post, put, delete...).
|
||||
*
|
||||
* // HTTP GET method
|
||||
* httpGet('/route1');
|
||||
*
|
||||
* // HTTP GET method
|
||||
* httpGet({uri: '/route1', data: {'p1': 'val1'});
|
||||
* // uri invoked => /route1?p1=val1
|
||||
*
|
||||
* // HTTP GET method with browser history management
|
||||
* httpGet({uri: '/api/users', history: {state: {foo: "bar"}, title: 'users page', uri: '/view/users'});
|
||||
*
|
||||
* Samples above can be applied on other HTTP methods.
|
||||
*
|
||||
* @param {String|Object} uri or object containing uri, http headers, data, history
|
||||
* @param {Function} success callback
|
||||
* @param {Function} failure callback
|
||||
* @public
|
||||
*/
|
||||
const httpMethodName = 'http'+method.charAt(0).toUpperCase() + method.slice(1).toLowerCase();
|
||||
reqProto[httpMethodName] = function(request, resolve, reject) {
|
||||
let {uri, headers, data} = request;
|
||||
let {uri, headers, data, history} = request;
|
||||
if (!uri) {
|
||||
uri = request;
|
||||
}
|
||||
@ -238,9 +366,27 @@ Object.keys(HTTP_METHODS).reduce((reqProto, method) => {
|
||||
uri,
|
||||
method,
|
||||
headers,
|
||||
data
|
||||
data,
|
||||
history
|
||||
}, resolve, reject);
|
||||
};
|
||||
|
||||
return reqProto;
|
||||
}, Application.prototype);
|
||||
}, Application.prototype);
|
||||
|
||||
|
||||
export function toParameters(args) {
|
||||
let baseUri, middleware, router, plugin, which;
|
||||
|
||||
args.length === 1 ? [which,] = args : [baseUri, which,] = args;
|
||||
|
||||
if (which instanceof Router) {
|
||||
router = which;
|
||||
} else if (which instanceof Middleware || typeof which === 'function') {
|
||||
middleware = which;
|
||||
} else if(which && which.plugin && typeof which.plugin === 'function') {
|
||||
plugin = which;
|
||||
}
|
||||
|
||||
return {baseUri, middleware, router, plugin, which};
|
||||
}
|
||||
|
@ -1,9 +1,25 @@
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
import Application from './application';
|
||||
import Router from './router';
|
||||
import Middleware from './middleware';
|
||||
|
||||
|
||||
/**
|
||||
* Create a frontexpress application.
|
||||
*
|
||||
* @return {Function}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
const frontexpress = () => new Application();
|
||||
|
||||
/**
|
||||
* Expose Router, Middleware constructors.
|
||||
*/
|
||||
frontexpress.Router = (baseUri) => new Router(baseUri);
|
||||
frontexpress.Middleware = (name) => new Middleware(name);
|
||||
frontexpress.Middleware = Middleware;
|
||||
|
||||
export default frontexpress;
|
||||
|
8
lib/methods.js
Normal file
8
lib/methods.js
Normal file
@ -0,0 +1,8 @@
|
||||
/**
|
||||
* HTTP method list
|
||||
* @private
|
||||
*/
|
||||
|
||||
export default ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'];
|
||||
// not supported yet
|
||||
// HEAD', 'CONNECT', 'OPTIONS', 'TRACE';
|
@ -1,19 +1,85 @@
|
||||
/**
|
||||
* Middleware object.
|
||||
* @public
|
||||
*/
|
||||
|
||||
export default class Middleware {
|
||||
|
||||
|
||||
/**
|
||||
* Middleware initialization
|
||||
*
|
||||
* @param {String} middleware name
|
||||
*/
|
||||
|
||||
constructor(name='') {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
entered(request) {
|
||||
}
|
||||
/**
|
||||
* Invoked by the app before an ajax request is sent or
|
||||
* during the DOM loading (document.readyState === 'loading').
|
||||
* See Application#_callMiddlewareEntered documentation for details.
|
||||
*
|
||||
* Override this method to add your custom behaviour
|
||||
*
|
||||
* @param {Object} request
|
||||
* @public
|
||||
*/
|
||||
|
||||
exited(request) {
|
||||
}
|
||||
entered(request) { }
|
||||
|
||||
updated(request, response) {
|
||||
}
|
||||
|
||||
failed(request, response) {
|
||||
}
|
||||
/**
|
||||
* Invoked by the app before a new ajax request is sent or before the DOM is unloaded.
|
||||
* See Application#_callMiddlewareExited documentation for details.
|
||||
*
|
||||
* Override this method to add your custom behaviour
|
||||
*
|
||||
* @param {Object} request
|
||||
* @public
|
||||
*/
|
||||
|
||||
exited(request) { }
|
||||
|
||||
|
||||
/**
|
||||
* Invoked by the app after an ajax request has responded or on DOM ready
|
||||
* (document.readyState === 'interactive').
|
||||
* See Application#_callMiddlewareUpdated documentation for details.
|
||||
*
|
||||
* Override this method to add your custom behaviour
|
||||
*
|
||||
* @param {Object} request
|
||||
* @param {Object} response
|
||||
* @public
|
||||
*/
|
||||
|
||||
updated(request, response) { }
|
||||
|
||||
|
||||
/**
|
||||
* Invoked by the app when an ajax request has failed.
|
||||
*
|
||||
* Override this method to add your custom behaviour
|
||||
*
|
||||
* @param {Object} request
|
||||
* @param {Object} response
|
||||
* @public
|
||||
*/
|
||||
failed(request, response) { }
|
||||
|
||||
|
||||
/**
|
||||
* Allow the hand over to the next middleware object or function.
|
||||
*
|
||||
* Override this method and return `false` to break execution of
|
||||
* middleware chain.
|
||||
*
|
||||
* @return {Boolean} `true` by default
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
|
||||
next() {
|
||||
return true;
|
||||
|
163
lib/requester.js
163
lib/requester.js
@ -1,94 +1,48 @@
|
||||
export const HTTP_METHODS = {
|
||||
'GET': {
|
||||
uri({uri, headers, data}) {
|
||||
if (!data) {
|
||||
return uri;
|
||||
}
|
||||
|
||||
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;
|
||||
}, uriWithoutAnchor);
|
||||
|
||||
return uriWithoutAnchor + anchor;
|
||||
},
|
||||
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
|
||||
};
|
||||
/**
|
||||
* Module dependencies.
|
||||
* @private
|
||||
*/
|
||||
|
||||
export default class Requester {
|
||||
|
||||
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;
|
||||
data = transformer.data ? transformer.data({uri, headers, data}) : data;
|
||||
/**
|
||||
* Make an ajax request.
|
||||
*
|
||||
* @param {Object} request
|
||||
* @param {Function} success callback
|
||||
* @param {Function} failure callback
|
||||
* @private
|
||||
*/
|
||||
|
||||
fetch(request, resolve, reject) {
|
||||
const {method, uri, headers, data} = request;
|
||||
|
||||
const success = (responseText) => {
|
||||
resolve(
|
||||
{method, uri, headers, data},
|
||||
{status: 200, statusText: 'OK', responseText}
|
||||
request,
|
||||
{
|
||||
status: 200,
|
||||
statusText: 'OK',
|
||||
responseText
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
const fail = ({status, statusText, errorThrown}) => {
|
||||
const errors = this._analyzeErrors({status, statusText, errorThrown});
|
||||
reject(
|
||||
{method, uri, headers, data},
|
||||
{status, statusText, errorThrown, errors}
|
||||
request,
|
||||
{
|
||||
status,
|
||||
statusText,
|
||||
errorThrown,
|
||||
errors: `HTTP ${status} ${statusText?statusText:''}`
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
const xmlhttp = new XMLHttpRequest();
|
||||
xmlhttp.onreadystatechange = () => {
|
||||
if (xmlhttp.readyState === 4) {
|
||||
if (xmlhttp.readyState === 4) { //XMLHttpRequest.DONE
|
||||
if (xmlhttp.status === 200) {
|
||||
success(xmlhttp.responseText);
|
||||
} else {
|
||||
@ -99,9 +53,9 @@ export default class Requester {
|
||||
try {
|
||||
xmlhttp.open(method, uri, true);
|
||||
if (headers) {
|
||||
for (const header of Object.keys(headers)) {
|
||||
Object.keys(headers).forEach((header) => {
|
||||
xmlhttp.setRequestHeader(header, headers[header]);
|
||||
}
|
||||
});
|
||||
}
|
||||
if (data) {
|
||||
xmlhttp.send(data);
|
||||
@ -112,32 +66,35 @@ export default class Requester {
|
||||
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:''}`;
|
||||
export const httpGetTransformer = {
|
||||
uri({uri, headers, data}) {
|
||||
if (!data) {
|
||||
return uri;
|
||||
}
|
||||
return error;
|
||||
let [uriWithoutAnchor, anchor] = [uri, ''];
|
||||
const match = /^(.*)(#.*)$/.exec(uri);
|
||||
if (match) {
|
||||
[,uriWithoutAnchor, anchor] = /^(.*)(#.*)$/.exec(uri);
|
||||
}
|
||||
uriWithoutAnchor = Object.keys(data).reduce((gUri, d, index) => {
|
||||
gUri += `${(index === 0 && gUri.indexOf('?') === -1)?'?':'&'}${d}=${data[d]}`;
|
||||
return gUri;
|
||||
}, uriWithoutAnchor);
|
||||
return uriWithoutAnchor + anchor;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// export const httpPostTransformer = {
|
||||
// headers({uri, headers, data}) {
|
||||
// if (!data) {
|
||||
// return headers;
|
||||
// }
|
||||
// const updatedHeaders = headers || {};
|
||||
// if (!updatedHeaders['Content-Type']) {
|
||||
// updatedHeaders['Content-Type'] = 'application/x-www-form-urlencoded';
|
||||
// }
|
||||
// return updatedHeaders;
|
||||
// }
|
||||
// };
|
||||
|
314
lib/router.js
314
lib/router.js
@ -1,7 +1,27 @@
|
||||
import {HTTP_METHODS} from './requester';
|
||||
/**
|
||||
* Module dependencies.
|
||||
* @private
|
||||
*/
|
||||
|
||||
import HTTP_METHODS from './methods';
|
||||
import {toParameters} from './application';
|
||||
import Middleware from './middleware';
|
||||
|
||||
class Route {
|
||||
|
||||
/**
|
||||
* Route object.
|
||||
* @private
|
||||
*/
|
||||
|
||||
export class Route {
|
||||
|
||||
|
||||
/**
|
||||
* Initialize the route.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
|
||||
constructor(router, uriPart, method, middleware) {
|
||||
this.router = router;
|
||||
this.uriPart = uriPart;
|
||||
@ -10,6 +30,13 @@ class Route {
|
||||
this.visited = false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return route's uri.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
|
||||
get uri() {
|
||||
if (!this.uriPart && !this.method) {
|
||||
return undefined;
|
||||
@ -23,26 +50,46 @@ class Route {
|
||||
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();
|
||||
const baseUri = this.router.baseUri.trim();
|
||||
if (this.uriPart) {
|
||||
return ( baseUri + this.uriPart.trim()).replace(/\/{2,}/, '/');
|
||||
}
|
||||
return baseUri;
|
||||
}
|
||||
|
||||
return this.uriPart;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Router object.
|
||||
* @public
|
||||
*/
|
||||
|
||||
const error_middleware_message = 'method takes at least a middleware';
|
||||
export default class Router {
|
||||
|
||||
|
||||
/**
|
||||
* Initialize the router.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
|
||||
constructor(uri) {
|
||||
if (uri) {
|
||||
this._baseUri = uri;
|
||||
}
|
||||
this._baseUri = uri;
|
||||
this._routes = [];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Do some checks and set _baseUri.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
|
||||
set baseUri(uri) {
|
||||
if (!uri) {
|
||||
return;
|
||||
@ -53,68 +100,79 @@ export default class Router {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._baseUri instanceof RegExp) {
|
||||
throw new TypeError(`the router already contains a regexp uri ${this._baseUri.toString()} It cannot be mixed with ${uri.toString()}`);
|
||||
}
|
||||
|
||||
if (uri instanceof RegExp) {
|
||||
throw new TypeError(`the router already contains an uri ${this._baseUri.toString()} It cannot be mixed with regexp ${uri.toString()}`);
|
||||
if (typeof this._baseUri !== typeof uri) {
|
||||
throw new TypeError('router cannot mix regexp and uri');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return router's _baseUri.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
|
||||
get baseUri() {
|
||||
return this._baseUri;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add a route to the router.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
|
||||
_add(route) {
|
||||
this._routes.push(route);
|
||||
return this;
|
||||
}
|
||||
|
||||
routes(uri, method) {
|
||||
|
||||
/**
|
||||
* Gather routes from routers filtered by _uri_ and HTTP _method_.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
|
||||
routes(application, request) {
|
||||
request.params = request.params || {};
|
||||
const isRouteMatch = application.get('route matcher');
|
||||
return this._routes.filter((route) => {
|
||||
if (!route.uri && !route.method) {
|
||||
return true;
|
||||
}
|
||||
if (route.method !== method) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!route.uri) {
|
||||
return true;
|
||||
}
|
||||
|
||||
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;
|
||||
return isRouteMatch(request, route);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gather visited routes from routers.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
|
||||
visited() {
|
||||
return this._routes.filter((route) => {
|
||||
return route.visited;
|
||||
});
|
||||
return this._routes.filter(route => route.visited);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Use the given middleware function or object on this router.
|
||||
*
|
||||
* // middleware function
|
||||
* router.use((req, res, next) => {console.log('Hello')});
|
||||
*
|
||||
* // middleware object
|
||||
* router.use(new Middleware());
|
||||
*
|
||||
* @param {Middleware|Function} middleware object or function
|
||||
* @return {Router} for chaining
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
|
||||
use(middleware) {
|
||||
if (!(middleware instanceof Middleware) && (typeof middleware !== 'function') ) {
|
||||
throw new TypeError('use method takes at least a middleware');
|
||||
throw new TypeError(error_middleware_message);
|
||||
}
|
||||
|
||||
this._add(new Route(this, undefined, undefined, middleware));
|
||||
@ -122,53 +180,143 @@ export default class Router {
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Use the given middleware function or object on this router for
|
||||
* all HTTP methods.
|
||||
*
|
||||
* // middleware function
|
||||
* router.all((req, res, next) => {console.log('Hello')});
|
||||
*
|
||||
* // middleware object
|
||||
* router.all(new Middleware());
|
||||
*
|
||||
* @param {Middleware|Function} middleware object or function
|
||||
* @return {Router} for chaining
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
|
||||
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;
|
||||
const {middleware} = toParameters(args);
|
||||
if (!middleware) {
|
||||
throw new TypeError(error_middleware_message);
|
||||
}
|
||||
|
||||
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)) {
|
||||
HTTP_METHODS.forEach((method) => {
|
||||
this[method.toLowerCase()](...args);
|
||||
}
|
||||
});
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
for (const method of Object.keys(HTTP_METHODS)) {
|
||||
HTTP_METHODS.forEach((method) => {
|
||||
|
||||
|
||||
/**
|
||||
* Use the given middleware function or object, with optional _uri_ on
|
||||
* HTTP methods: get, post, put, delete...
|
||||
* Default _uri_ is "/".
|
||||
*
|
||||
* // middleware function will be applied on path "/"
|
||||
* router.get((req, res, next) => {console.log('Hello')});
|
||||
*
|
||||
* // middleware object will be applied on path "/" and
|
||||
* router.get(new Middleware());
|
||||
*
|
||||
* // middleware function will be applied on path "/user"
|
||||
* router.post('/user', (req, res, next) => {console.log('Hello')});
|
||||
*
|
||||
* // middleware object will be applied on path "/user" and
|
||||
* router.post('/user', new Middleware());
|
||||
*
|
||||
* @param {String} uri
|
||||
* @param {Middleware|Function} middleware object or function
|
||||
* @return {Router} for chaining
|
||||
* @public
|
||||
*/
|
||||
|
||||
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;
|
||||
const {baseUri, middleware} = toParameters(args);
|
||||
if (!middleware) {
|
||||
throw new TypeError(error_middleware_message);
|
||||
}
|
||||
|
||||
if (!(middleware instanceof Middleware) && (typeof middleware !== 'function') ) {
|
||||
throw new TypeError(`use ${methodName} method takes at least a middleware`);
|
||||
if (baseUri && this._baseUri && this._baseUri instanceof RegExp) {
|
||||
throw new TypeError('router cannot mix uri/regexp');
|
||||
}
|
||||
|
||||
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));
|
||||
this._add(new Route(this, baseUri, method, middleware));
|
||||
|
||||
return this;
|
||||
};
|
||||
});
|
||||
|
||||
export function routeMatcher(request, route) {
|
||||
// check if http method are equals
|
||||
if (route.method && route.method !== request.method) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// route and uri not defined always match
|
||||
if (!route.uri || !request.uri) {
|
||||
return true;
|
||||
}
|
||||
|
||||
//remove query string and anchor from uri to test
|
||||
const match = /^(.*)\?.*#.*|(.*)(?=\?|#)|(.*[^\?#])$/.exec(request.uri);
|
||||
const baseUriToCheck = match[1] || match[2] || match[3];
|
||||
|
||||
// if route is a regexp path
|
||||
if (route.uri instanceof RegExp) {
|
||||
return baseUriToCheck.match(route.uri) !== null;
|
||||
}
|
||||
|
||||
// if route is parameterized path
|
||||
if (route.uri.indexOf(':') !== -1) {
|
||||
|
||||
const decodeParmeterValue = (v) => {
|
||||
return !isNaN(parseFloat(v)) && isFinite(v) ? (Number.isInteger(v) ? Number.parseInt(v, 10) : Number.parseFloat(v)) : v;
|
||||
};
|
||||
|
||||
// figure out key names
|
||||
const keys = [];
|
||||
const keysRE = /:([^\/\?]+)\??/g;
|
||||
let keysMatch = keysRE.exec(route.uri);
|
||||
while (keysMatch != null) {
|
||||
keys.push(keysMatch[1]);
|
||||
keysMatch = keysRE.exec(route.uri);
|
||||
}
|
||||
|
||||
// change parameterized path to regexp
|
||||
const regExpUri = route.uri
|
||||
// :parameter?
|
||||
.replace(/\/:[^\/]+\?/g, '(?:\/([^\/]+))?')
|
||||
// :parameter
|
||||
.replace(/:[^\/]+/g, '([^\/]+)')
|
||||
// escape all /
|
||||
.replace('/', '\\/');
|
||||
|
||||
// checks if uri match
|
||||
const routeMatch = baseUriToCheck.match(new RegExp(`^${regExpUri}$`));
|
||||
if (!routeMatch) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// update params in request with keys
|
||||
request.params = Object.assign(request.params, keys.reduce((acc, key, index) => {
|
||||
let value = routeMatch[index + 1];
|
||||
if (value) {
|
||||
value = value.indexOf(',') !== -1 ? value.split(',').map(v => decodeParmeterValue(v)) : value = decodeParmeterValue(value);
|
||||
}
|
||||
acc[key] = value;
|
||||
return acc;
|
||||
}, {}));
|
||||
return true;
|
||||
}
|
||||
|
||||
// if route is a simple path
|
||||
return route.uri === baseUriToCheck;
|
||||
}
|
||||
|
83
lib/settings.js
Normal file
83
lib/settings.js
Normal file
@ -0,0 +1,83 @@
|
||||
/**
|
||||
* Module dependencies.
|
||||
* @private
|
||||
*/
|
||||
import {routeMatcher} from './router';
|
||||
import Requester, {httpGetTransformer} from './requester';
|
||||
|
||||
|
||||
function errorIfNotFunction(toTest, message) {
|
||||
if(typeof toTest !== 'function') {
|
||||
throw new TypeError(message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Settings object.
|
||||
* @private
|
||||
*/
|
||||
|
||||
export default class Settings {
|
||||
|
||||
|
||||
/**
|
||||
* Initialize the settings.
|
||||
*
|
||||
* - setup default configuration
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
|
||||
constructor() {
|
||||
// default settings
|
||||
this.settings = {
|
||||
'http requester': new Requester(),
|
||||
'http GET transformer': httpGetTransformer,
|
||||
// 'http POST transformer': httpPostTransformer,
|
||||
'route matcher': routeMatcher
|
||||
};
|
||||
|
||||
this.rules = {
|
||||
'http requester': (requester) => {
|
||||
errorIfNotFunction(requester.fetch , 'setting http requester has no fetch function');
|
||||
},
|
||||
'http GET transformer': (transformer) => {
|
||||
if (!transformer || (!transformer.uri && !transformer.headers && !transformer.data)) {
|
||||
throw new TypeError('setting http transformer one of functions: uri, headers, data is missing');
|
||||
}
|
||||
},
|
||||
'route matcher': (routeMatcher) => {
|
||||
errorIfNotFunction(routeMatcher, 'setting route matcher is not a function');
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Assign `setting` to `val`
|
||||
*
|
||||
* @param {String} setting
|
||||
* @param {*} [val]
|
||||
* @private
|
||||
*/
|
||||
|
||||
set(name, value) {
|
||||
const checkRules = this.rules[name];
|
||||
if (checkRules) {
|
||||
checkRules(value);
|
||||
}
|
||||
this.settings[name] = value;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return `setting`'s value.
|
||||
*
|
||||
* @param {String} setting
|
||||
* @private
|
||||
*/
|
||||
|
||||
get(name) {
|
||||
return this.settings[name];
|
||||
}
|
||||
};
|
57
package.json
57
package.json
@ -1,18 +1,21 @@
|
||||
{
|
||||
"name": "frontexpress",
|
||||
"version": "0.1.0",
|
||||
"description": "Minimalist front end router framework a la express",
|
||||
"version": "1.2.0",
|
||||
"description": "Frontexpress manages routes in browser like ExpressJS on Node",
|
||||
"main": "dist/frontexpress.js",
|
||||
"jsnext:main": "lib/frontexpress.js",
|
||||
"scripts": {
|
||||
"lint": "eslint .",
|
||||
"only-test": "mocha --compilers js:babel-core/register",
|
||||
"test-only": "mocha --compilers js:babel-core/register",
|
||||
"test": "npm run lint && babel-node node_modules/.bin/babel-istanbul cover node_modules/.bin/_mocha",
|
||||
"prepublish": "rimraf dist && babel lib -d dist"
|
||||
"gzipsize": "babel-node gzipsize.js",
|
||||
"frontpackage": "rollup -c rollup.config.dev.js && rollup -c rollup.config.prod.js && npm run gzipsize",
|
||||
"prepublish": "rimraf dist && babel lib -d dist && npm run frontpackage"
|
||||
},
|
||||
"author": "Camel Aissani <camel.aissani@gmail.com> (https://nuageprive.fr)",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
"node": ">=4.2.6"
|
||||
},
|
||||
"repository": "camelaissani/frontexpress",
|
||||
"keywords": [
|
||||
@ -21,26 +24,38 @@
|
||||
"web",
|
||||
"router",
|
||||
"middleware",
|
||||
"rest",
|
||||
"restful",
|
||||
"app",
|
||||
"api",
|
||||
"express",
|
||||
"frontexpress"
|
||||
],
|
||||
"devDependencies": {
|
||||
"babel-cli": "^6.10.1",
|
||||
"babel-core": "^6.10.4",
|
||||
"babel-eslint": "^6.1.0",
|
||||
"babel-istanbul": "^0.8.0",
|
||||
"babel-preset-es2015": "^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",
|
||||
"mocha": "^2.5.3",
|
||||
"rimraf": "^2.5.2",
|
||||
"sinon": "^1.17.4"
|
||||
}
|
||||
"babel-cli": "^6.18.0",
|
||||
"babel-core": "^6.21.0",
|
||||
"babel-eslint": "^7.1.1",
|
||||
"babel-istanbul": "^0.12.1",
|
||||
"babel-preset-es2015": "^6.18.0",
|
||||
"babel-preset-es2015-rollup": "^3.0.0",
|
||||
"babel-register": "^6.18.0",
|
||||
"bytesize": "^0.2.0",
|
||||
"chai": "^4.*",
|
||||
"eslint": "^3.*",
|
||||
"istanbul": "^0.4.5",
|
||||
"mocha": "^3.2.0",
|
||||
"rimraf": "^2.5.4",
|
||||
"rollup": "^0.*",
|
||||
"rollup-plugin-babel": "^2.7.1",
|
||||
"rollup-plugin-uglify-es": "0.0.1",
|
||||
"sinon": "^1.*"
|
||||
},
|
||||
"files": [
|
||||
"dist/",
|
||||
"docs/",
|
||||
"lib/",
|
||||
"README.md",
|
||||
"LICENCE",
|
||||
"frontexpress.js",
|
||||
"frontexpress.min.js",
|
||||
"frontexpress.min.js.map"
|
||||
]
|
||||
}
|
||||
|
12
rollup.config.dev.js
Normal file
12
rollup.config.dev.js
Normal file
@ -0,0 +1,12 @@
|
||||
import babel from 'rollup-plugin-babel';
|
||||
|
||||
export default {
|
||||
entry: 'lib/frontexpress.js',
|
||||
format: 'iife',
|
||||
moduleName:'frontexpress',
|
||||
plugins: [babel({
|
||||
babelrc: false,
|
||||
presets: ['es2015-rollup']
|
||||
})],
|
||||
dest: 'frontexpress.js'
|
||||
};
|
17
rollup.config.prod.js
Normal file
17
rollup.config.prod.js
Normal file
@ -0,0 +1,17 @@
|
||||
import uglify from 'rollup-plugin-uglify-es';
|
||||
import babel from 'rollup-plugin-babel';
|
||||
|
||||
export default {
|
||||
entry: 'lib/frontexpress.js',
|
||||
format: 'iife',
|
||||
sourceMap: true,
|
||||
moduleName:'frontexpress',
|
||||
dest: 'frontexpress.min.js',
|
||||
plugins: [
|
||||
babel({
|
||||
babelrc: false,
|
||||
presets: ['es2015-rollup']
|
||||
}),
|
||||
uglify()
|
||||
]
|
||||
};
|
@ -1,10 +1,18 @@
|
||||
/*eslint-env mocha*/
|
||||
import chai, {assert} from 'chai';
|
||||
import sinon from 'sinon';
|
||||
//import jsdom from 'jsdom';
|
||||
import frontexpress from '../lib/frontexpress';
|
||||
import Requester from '../lib/requester';
|
||||
|
||||
describe('Application', () => {
|
||||
class MyMiddleware extends frontexpress.Middleware {
|
||||
entered() {}
|
||||
exited() {}
|
||||
failed() {}
|
||||
updated() {}
|
||||
}
|
||||
|
||||
let requester;
|
||||
|
||||
describe('generated methods', () => {
|
||||
@ -25,18 +33,108 @@ describe('Application', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('listen method', () => {
|
||||
let eventFn = {};
|
||||
// // JSDOM cannot manage pushState/onpopstate/window.location
|
||||
// see https://github.com/tmpvar/jsdom/issues/1565
|
||||
//
|
||||
// describe('listen method with JSDOM', () => {
|
||||
// before(() => {
|
||||
// // Init DOM with a fake document
|
||||
// // <base> and uri (initial uri) allow to do pushState in jsdom
|
||||
// jsdom.env({
|
||||
// html:`
|
||||
// <html>
|
||||
// <head>
|
||||
// <base href="http://localhost:8080/"></base>
|
||||
// </head>
|
||||
// </html>
|
||||
// `,
|
||||
// url: 'http://localhost:8080/',
|
||||
// done(err, window) {
|
||||
// global.window = window;
|
||||
// global.document = window.document;
|
||||
// window.console = global.console;
|
||||
// }
|
||||
// });
|
||||
// });
|
||||
|
||||
// let requester;
|
||||
// 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('history management without state object', (done) => {
|
||||
// const spy_pushState = sinon.spy(window.history, 'pushState');
|
||||
|
||||
// const app = frontexpress();
|
||||
// const m = new MyMiddleware();
|
||||
// const spy_middleware = sinon.stub(m, 'updated');
|
||||
|
||||
// app.set('http requester', requester);
|
||||
// app.use('/api/route1', m);
|
||||
// app.listen();
|
||||
|
||||
// const spy_onpopstate = sinon.spy(window, 'onpopstate');
|
||||
|
||||
// app.httpGet({uri:'/api/route1', history: {
|
||||
// uri: '/route1',
|
||||
// title: 'route1'
|
||||
// }},
|
||||
// (req, res) => {
|
||||
// // On request succeeded
|
||||
// assert(spy_onpopstate.callCount === 0);
|
||||
// assert(spy_pushState.calledOnce);
|
||||
// assert(spy_middleware.calledOnce);
|
||||
|
||||
// spy_middleware.reset();
|
||||
// window.history.back();
|
||||
// window.history.forward();
|
||||
// assert(spy_onpopstate.calledOnce);
|
||||
// assert(spy_middleware.calledOnce);
|
||||
|
||||
// done();
|
||||
// });
|
||||
// });
|
||||
// });
|
||||
|
||||
describe('listen method', () => {
|
||||
// Here I cannot use jsdon to make these tests :(
|
||||
// JSDOM cannot simulate readyState changes
|
||||
beforeEach(() => {
|
||||
const browserHistory = [{uri: '/'}];
|
||||
let browserHistoryIndex = 0;
|
||||
|
||||
global.document = {};
|
||||
global.window = {
|
||||
addEventListener(eventType, callback) {
|
||||
eventFn[eventType] = callback;
|
||||
},
|
||||
location: {
|
||||
pathname: '/route1',
|
||||
search: '?a=b'
|
||||
},
|
||||
history: {
|
||||
pushState(state, title, pathname) {
|
||||
browserHistory.push({uri: pathname, state});
|
||||
browserHistoryIndex++;
|
||||
global.window.location.pathname = browserHistory[browserHistoryIndex].uri;
|
||||
},
|
||||
forward() {
|
||||
browserHistoryIndex++;
|
||||
global.window.location.pathname = browserHistory[browserHistoryIndex].uri;
|
||||
if (browserHistory[browserHistoryIndex].state) {
|
||||
window.onpopstate({state:browserHistory[browserHistoryIndex].state});
|
||||
}
|
||||
},
|
||||
back() {
|
||||
browserHistoryIndex--;
|
||||
global.window.location.pathname = browserHistory[browserHistoryIndex].uri;
|
||||
if (browserHistory[browserHistoryIndex].state) {
|
||||
window.onpopstate({state: browserHistory[browserHistoryIndex].state});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
@ -55,7 +153,7 @@ describe('Application', () => {
|
||||
|
||||
it('with middleware object readyState===loading', (done) => {
|
||||
const app = frontexpress();
|
||||
const m = frontexpress.Middleware();
|
||||
const m = new MyMiddleware();
|
||||
sinon.stub(m, 'entered', () => {
|
||||
done();
|
||||
});
|
||||
@ -70,7 +168,7 @@ describe('Application', () => {
|
||||
|
||||
it('with middleware object readyState===interactive', (done) => {
|
||||
const app = frontexpress();
|
||||
const m = frontexpress.Middleware();
|
||||
const m = new MyMiddleware();
|
||||
sinon.stub(m, 'updated', () => {
|
||||
done();
|
||||
});
|
||||
@ -85,7 +183,7 @@ describe('Application', () => {
|
||||
|
||||
it('with middleware object event beforeunload', (done) => {
|
||||
const app = frontexpress();
|
||||
const m = frontexpress.Middleware();
|
||||
const m = new MyMiddleware();
|
||||
sinon.stub(m, 'exited', () => {
|
||||
done();
|
||||
});
|
||||
@ -93,26 +191,131 @@ describe('Application', () => {
|
||||
app.use('/route1', m);
|
||||
app.listen(() => {
|
||||
//simulate beforeunload
|
||||
eventFn['beforeunload']();
|
||||
window.onbeforeunload();
|
||||
});
|
||||
|
||||
//simulate readystatechange
|
||||
document.readyState = 'interactive';
|
||||
document.onreadystatechange();
|
||||
});
|
||||
});
|
||||
|
||||
describe('set method', () => {
|
||||
it('unsupported setting', () => {
|
||||
it('history management without state object', (done) => {
|
||||
let historyObj;
|
||||
const requester = new Requester();
|
||||
sinon.stub(requester, 'fetch', ({uri, method, headers, data, history}, resolve, reject) => {
|
||||
resolve(
|
||||
{uri, method, headers, data, history},
|
||||
{status: 200, statusText: 'OK', responseText:''}
|
||||
);
|
||||
});
|
||||
|
||||
const spy_pushState = sinon.spy(window.history, 'pushState');
|
||||
|
||||
const app = frontexpress();
|
||||
chai.expect(() => app.set('blabla', 'value')).to.throw(ReferenceError);
|
||||
const m = new MyMiddleware();
|
||||
const spy_middleware = sinon.stub(m, 'updated', (req, res) => {
|
||||
historyObj = req.history;
|
||||
});
|
||||
|
||||
app.set('http requester', requester);
|
||||
app.use('/api/route1', m);
|
||||
app.listen();
|
||||
|
||||
const spy_onpopstate = sinon.spy(window, 'onpopstate');
|
||||
|
||||
app.httpGet({uri:'/api/route1', history: {
|
||||
uri: '/route1',
|
||||
title: 'route1'
|
||||
}},
|
||||
(req, res) => {
|
||||
// On request succeeded
|
||||
assert(spy_onpopstate.callCount === 0);
|
||||
assert(spy_pushState.calledOnce);
|
||||
assert(spy_middleware.calledOnce);
|
||||
|
||||
spy_middleware.reset();
|
||||
window.history.back();
|
||||
window.history.forward();
|
||||
assert(spy_onpopstate.calledOnce);
|
||||
assert(spy_middleware.calledOnce);
|
||||
assert(historyObj);
|
||||
assert(historyObj.uri === '/route1');
|
||||
assert(historyObj.title === 'route1');
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('supported setting', () => {
|
||||
it('history management with state object', (done) => {
|
||||
let historyObj;
|
||||
const requester = new Requester();
|
||||
sinon.stub(requester, 'fetch', ({uri, method, headers, data, history}, resolve, reject) => {
|
||||
resolve(
|
||||
{uri, method, headers, data, history},
|
||||
{status: 200, statusText: 'OK', responseText:''}
|
||||
);
|
||||
});
|
||||
|
||||
const spy_pushState = sinon.spy(window.history, 'pushState');
|
||||
|
||||
const app = frontexpress();
|
||||
const m = new MyMiddleware();
|
||||
const spy_middleware = sinon.stub(m, 'updated', (req, res) => {
|
||||
historyObj = req.history;
|
||||
});
|
||||
|
||||
app.set('http requester', requester);
|
||||
app.use('/api/route1', m);
|
||||
app.listen();
|
||||
|
||||
const spy_onpopstate = sinon.spy(window, 'onpopstate');
|
||||
|
||||
app.httpGet({uri:'/api/route1', history: {
|
||||
uri: '/route1',
|
||||
title: 'route1',
|
||||
state: {a: 'b', c: 'd'}
|
||||
}},
|
||||
(req, res) => {
|
||||
// On request succeeded
|
||||
assert(spy_onpopstate.callCount === 0);
|
||||
assert(spy_pushState.calledOnce);
|
||||
assert(spy_middleware.calledOnce);
|
||||
|
||||
spy_middleware.reset();
|
||||
window.history.back();
|
||||
window.history.forward();
|
||||
assert(spy_onpopstate.calledOnce);
|
||||
assert(spy_middleware.calledOnce);
|
||||
assert(historyObj);
|
||||
assert(historyObj.uri === '/route1');
|
||||
assert(historyObj.title === 'route1');
|
||||
assert(historyObj.state);
|
||||
assert(historyObj.state.a === 'b');
|
||||
assert(historyObj.state.c === 'd');
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('set/get setting method', () => {
|
||||
it('custom setting', () => {
|
||||
const app = frontexpress();
|
||||
app.set('blabla', 'value');
|
||||
assert(app.get('blabla') === 'value');
|
||||
});
|
||||
|
||||
it('core setting', () => {
|
||||
const requester = new Requester();
|
||||
const app = frontexpress();
|
||||
app.set('http-requester', requester);
|
||||
assert(app.requester === requester);
|
||||
app.set('http requester', requester);
|
||||
assert(app.get('http requester') === requester);
|
||||
assert(app.set('http requester') === requester);
|
||||
});
|
||||
|
||||
it('bad core setting', () => {
|
||||
const app = frontexpress();
|
||||
chai.expect(() => (app.set('http requester', 'not an object with fetch function'))).to.throw(TypeError);
|
||||
});
|
||||
});
|
||||
|
||||
@ -129,25 +332,25 @@ describe('Application', () => {
|
||||
|
||||
it('no arguments', () => {
|
||||
const app = frontexpress();
|
||||
app.set('http-requester', requester);
|
||||
app.set('http requester', requester);
|
||||
assert.throws(app.use, TypeError);
|
||||
});
|
||||
|
||||
it('bad arguments', () => {
|
||||
const app = frontexpress();
|
||||
app.set('http-requester', requester);
|
||||
app.set('http requester', requester);
|
||||
chai.expect(() => app.use('eee')).to.throw(TypeError);
|
||||
});
|
||||
|
||||
it('mixing uri and regexp', () => {
|
||||
let router = frontexpress.Router('/subroute');
|
||||
let app = frontexpress();
|
||||
app.set('http-requester', requester);
|
||||
app.set('http requester', requester);
|
||||
chai.expect(() => app.use(/route/, router)).to.throw(TypeError);
|
||||
|
||||
router = frontexpress.Router(/subroute/);
|
||||
app = frontexpress();
|
||||
app.set('http-requester', requester);
|
||||
app.set('http requester', requester);
|
||||
chai.expect(() => app.use('/route', router)).to.throw(TypeError);
|
||||
});
|
||||
|
||||
@ -155,7 +358,7 @@ describe('Application', () => {
|
||||
const spy = sinon.spy();
|
||||
|
||||
const app = frontexpress();
|
||||
app.set('http-requester', requester);
|
||||
app.set('http requester', requester);
|
||||
app.use((request, response, next) => {spy();});
|
||||
|
||||
app.httpGet('/', (request, response) => {
|
||||
@ -172,7 +375,7 @@ describe('Application', () => {
|
||||
const spy = sinon.spy();
|
||||
|
||||
const app = frontexpress();
|
||||
app.set('http-requester', requester);
|
||||
app.set('http requester', requester);
|
||||
app.use('/route1', (request, response, next) => {spy();});
|
||||
|
||||
app.httpGet('/route1', (request, response) => {
|
||||
@ -197,7 +400,7 @@ describe('Application', () => {
|
||||
const spy = sinon.spy();
|
||||
|
||||
const app = frontexpress();
|
||||
app.set('http-requester', requester);
|
||||
app.set('http requester', requester);
|
||||
app.use((request, response, next) => {spy();});
|
||||
|
||||
app.httpGet('/', null, (request, response) => {
|
||||
@ -208,11 +411,11 @@ describe('Application', () => {
|
||||
});
|
||||
|
||||
it('middleware as object on path /', (done) => {
|
||||
const middleware = frontexpress.Middleware('on path /');
|
||||
const middleware = new MyMiddleware('on path /');
|
||||
const spy = sinon.spy(middleware, 'updated');
|
||||
|
||||
const app = frontexpress();
|
||||
app.set('http-requester', requester);
|
||||
app.set('http requester', requester);
|
||||
app.use(middleware);
|
||||
|
||||
app.httpGet('/', (request, response) => {
|
||||
@ -226,11 +429,11 @@ describe('Application', () => {
|
||||
});
|
||||
|
||||
it('middleware as object on path /route1', (done) => {
|
||||
const middleware = frontexpress.Middleware('on path /route1');
|
||||
const middleware = new MyMiddleware('on path /route1');
|
||||
const spy = sinon.spy(middleware, 'updated');
|
||||
|
||||
const app = frontexpress();
|
||||
app.set('http-requester', requester);
|
||||
app.set('http requester', requester);
|
||||
app.use('/route1', middleware);
|
||||
|
||||
app.httpGet('/route1', (request, response) => {
|
||||
@ -252,11 +455,11 @@ describe('Application', () => {
|
||||
);
|
||||
});
|
||||
|
||||
const middleware = frontexpress.Middleware('on path /');
|
||||
const middleware = new MyMiddleware('on path /');
|
||||
const spy = sinon.spy(middleware, 'failed');
|
||||
|
||||
const app = frontexpress();
|
||||
app.set('http-requester', requester);
|
||||
app.set('http requester', requester);
|
||||
app.use(middleware);
|
||||
|
||||
app.httpGet('/', null, (request, response) => {
|
||||
@ -274,7 +477,7 @@ describe('Application', () => {
|
||||
.post((request, response, next) => {spy();});
|
||||
|
||||
const app = frontexpress();
|
||||
app.set('http-requester', requester);
|
||||
app.set('http requester', requester);
|
||||
app.use(router);
|
||||
|
||||
app.httpGet('/', (request, response) => {
|
||||
@ -295,7 +498,7 @@ describe('Application', () => {
|
||||
.post((request, response, next) => {spy();});
|
||||
|
||||
const app = frontexpress();
|
||||
app.set('http-requester', requester);
|
||||
app.set('http requester', requester);
|
||||
app.use('/route1', router);
|
||||
|
||||
app.httpGet('/route1', (request, response) => {
|
||||
@ -309,11 +512,11 @@ describe('Application', () => {
|
||||
});
|
||||
|
||||
it('router with base uri', (done)=> {
|
||||
const middleware = frontexpress.Middleware('get middleware');
|
||||
const middleware = new MyMiddleware('get middleware');
|
||||
const spy = sinon.spy(middleware, 'updated');
|
||||
|
||||
const app = frontexpress();
|
||||
app.set('http-requester', requester);
|
||||
app.set('http requester', requester);
|
||||
|
||||
const router = frontexpress.Router();
|
||||
router.get('/subroute1', middleware);
|
||||
@ -332,12 +535,12 @@ describe('Application', () => {
|
||||
it('use two routers', (done)=> {
|
||||
|
||||
const router1 = frontexpress.Router();
|
||||
const m1 = frontexpress.Middleware();
|
||||
const m1 = new MyMiddleware();
|
||||
const spy1 = sinon.spy(m1, 'updated');
|
||||
router1.get('/subroute1', m1);
|
||||
|
||||
const router2 = frontexpress.Router();
|
||||
const m2 = frontexpress.Middleware();
|
||||
const m2 = new MyMiddleware();
|
||||
const spy2 = sinon.spy(m2, 'updated');
|
||||
router2.get('/subroute2', m2);
|
||||
|
||||
@ -345,7 +548,7 @@ describe('Application', () => {
|
||||
|
||||
|
||||
const app = frontexpress();
|
||||
app.set('http-requester', requester);
|
||||
app.set('http requester', requester);
|
||||
app.use(/^\/route1/, router1);
|
||||
app.use(/^\/route2/, router2);
|
||||
|
||||
@ -379,14 +582,13 @@ describe('Application', () => {
|
||||
|
||||
it('no arguments', () => {
|
||||
const app = frontexpress();
|
||||
app.set('http-requester', requester);
|
||||
app.set('http requester', requester);
|
||||
assert.throws(app.get, TypeError);
|
||||
});
|
||||
|
||||
it('bad arguments', () => {
|
||||
const app = frontexpress();
|
||||
app.set('http-requester', requester);
|
||||
chai.expect(() => app.get('eee')).to.throw(TypeError);
|
||||
app.set('http requester', requester);
|
||||
chai.expect(() => app.get(frontexpress.Router())).to.throw(TypeError);
|
||||
chai.expect(() => app.get('/route1', frontexpress.Router())).to.throw(TypeError);
|
||||
});
|
||||
@ -395,7 +597,7 @@ describe('Application', () => {
|
||||
const spy = sinon.spy();
|
||||
|
||||
const app = frontexpress();
|
||||
app.set('http-requester', requester);
|
||||
app.set('http requester', requester);
|
||||
app.get((request, respons, next) => {spy();});
|
||||
|
||||
app.httpGet('/', (request, response) => {
|
||||
@ -412,7 +614,7 @@ describe('Application', () => {
|
||||
const spy = sinon.spy();
|
||||
|
||||
const app = frontexpress();
|
||||
app.set('http-requester', requester);
|
||||
app.set('http requester', requester);
|
||||
app.get('/route1', (request, response, next) => {spy();});
|
||||
|
||||
app.httpGet('/route1', (request, response) => {
|
||||
@ -426,11 +628,11 @@ describe('Application', () => {
|
||||
});
|
||||
|
||||
it('middleware as object on path /', (done) => {
|
||||
const middleware = frontexpress.Middleware('on path /');
|
||||
const middleware = new MyMiddleware('on path /');
|
||||
const spy = sinon.spy(middleware, 'updated');
|
||||
|
||||
const app = frontexpress();
|
||||
app.set('http-requester', requester);
|
||||
app.set('http requester', requester);
|
||||
app.get(middleware);
|
||||
|
||||
app.httpGet('/', (request, response) => {
|
||||
@ -444,11 +646,11 @@ describe('Application', () => {
|
||||
});
|
||||
|
||||
it('middleware as object on path /route1', (done) => {
|
||||
const middleware = frontexpress.Middleware('on path /route1');
|
||||
const middleware = new MyMiddleware('on path /route1');
|
||||
const spy = sinon.spy(middleware, 'updated');
|
||||
|
||||
const app = frontexpress();
|
||||
app.set('http-requester', requester);
|
||||
app.set('http requester', requester);
|
||||
app.get(middleware);
|
||||
|
||||
app.httpGet('/route1', (request, response) => {
|
||||
@ -462,6 +664,24 @@ describe('Application', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('post 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.post, TypeError);
|
||||
});
|
||||
});
|
||||
|
||||
describe('route method', () => {
|
||||
beforeEach(()=>{
|
||||
requester = new Requester();
|
||||
@ -477,7 +697,7 @@ describe('Application', () => {
|
||||
const spy = sinon.spy();
|
||||
|
||||
const app = frontexpress();
|
||||
app.set('http-requester', requester);
|
||||
app.set('http requester', requester);
|
||||
app.route().get((request, response, next) => {spy();});
|
||||
|
||||
app.httpGet('/', (request, response) => {
|
||||
@ -494,7 +714,7 @@ describe('Application', () => {
|
||||
const spy = sinon.spy();
|
||||
|
||||
const app = frontexpress();
|
||||
app.set('http-requester', requester);
|
||||
app.set('http requester', requester);
|
||||
app.route('/').get((request, response, next) => {spy();});
|
||||
|
||||
app.httpGet('/', (request, response) => {
|
||||
@ -511,7 +731,7 @@ describe('Application', () => {
|
||||
const spy = sinon.spy();
|
||||
|
||||
const app = frontexpress();
|
||||
app.set('http-requester', requester);
|
||||
app.set('http requester', requester);
|
||||
app.route('/route1').get((request, response, next) => {spy();});
|
||||
|
||||
app.httpGet('/route1', (request, response) => {
|
||||
@ -528,7 +748,7 @@ describe('Application', () => {
|
||||
const spy = sinon.spy();
|
||||
|
||||
const app = frontexpress();
|
||||
app.set('http-requester', requester);
|
||||
app.set('http requester', requester);
|
||||
app.route().get('/subroute1', (request, response, next) => {spy();});
|
||||
|
||||
app.httpGet('/subroute1', (request, response) => {
|
||||
@ -544,7 +764,7 @@ describe('Application', () => {
|
||||
const spy = sinon.spy();
|
||||
|
||||
const app = frontexpress();
|
||||
app.set('http-requester', requester);
|
||||
app.set('http requester', requester);
|
||||
app.route('/').get('/subroute1', (request, response, next) => {spy();});
|
||||
|
||||
app.httpGet('/subroute1', (request, response) => {
|
||||
@ -560,7 +780,7 @@ describe('Application', () => {
|
||||
const spy = sinon.spy();
|
||||
|
||||
const app = frontexpress();
|
||||
app.set('http-requester', requester);
|
||||
app.set('http requester', requester);
|
||||
app.route('/route1').get('/subroute1', (request, response, next) => {spy();});
|
||||
|
||||
app.httpGet('/route1/subroute1', (request, response) => {
|
||||
@ -586,9 +806,9 @@ describe('Application', () => {
|
||||
|
||||
it('http GET request', (done) => {
|
||||
const app = frontexpress();
|
||||
app.set('http-requester', requester);
|
||||
app.set('http requester', requester);
|
||||
|
||||
const getMiddleware = frontexpress.Middleware('get middleware');
|
||||
const getMiddleware = new MyMiddleware('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');
|
||||
@ -614,19 +834,19 @@ describe('Application', () => {
|
||||
|
||||
it('http GET followed by http POST requests', (done) => {
|
||||
const app = frontexpress();
|
||||
app.set('http-requester', requester);
|
||||
app.set('http requester', requester);
|
||||
|
||||
const allMiddleware = frontexpress.Middleware('all middleware');
|
||||
const allMiddleware = new MyMiddleware('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 = frontexpress.Middleware('get middleware');
|
||||
const getMiddleware = new MyMiddleware('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 = frontexpress.Middleware('post middleware');
|
||||
const postMiddleware = new MyMiddleware('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');
|
||||
@ -697,9 +917,9 @@ describe('Application', () => {
|
||||
);
|
||||
});
|
||||
const app = frontexpress();
|
||||
app.set('http-requester', requester);
|
||||
app.set('http requester', requester);
|
||||
|
||||
const getMiddleware = frontexpress.Middleware('get middleware');
|
||||
const getMiddleware = new MyMiddleware('get middleware');
|
||||
const spy_get_failed = sinon.spy(getMiddleware, 'failed');
|
||||
|
||||
|
||||
@ -716,12 +936,12 @@ describe('Application', () => {
|
||||
|
||||
it('next method', (done) => {
|
||||
const app = frontexpress();
|
||||
app.set('http-requester', requester);
|
||||
app.set('http requester', requester);
|
||||
|
||||
const m1 = frontexpress.Middleware('m1');
|
||||
const m2 = frontexpress.Middleware('m2');
|
||||
const m1 = new MyMiddleware('m1');
|
||||
const m2 = new MyMiddleware('m2');
|
||||
m2.next = () => false;
|
||||
const m3 = frontexpress.Middleware('m3');
|
||||
const m3 = new MyMiddleware('m3');
|
||||
|
||||
const spy_m1 = sinon.spy(m1, 'updated');
|
||||
const spy_m2 = sinon.spy(m2, 'updated');
|
||||
@ -750,7 +970,7 @@ describe('Application', () => {
|
||||
|
||||
it('next method', (done) => {
|
||||
const app = frontexpress();
|
||||
app.set('http-requester', requester);
|
||||
app.set('http requester', requester);
|
||||
|
||||
const m1 = (req, res, next) => {next();};
|
||||
const m2 = (req, res, next) => {};
|
||||
@ -769,4 +989,22 @@ describe('Application', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('plugin management', () => {
|
||||
it('setup a plugin', (done) => {
|
||||
const app = frontexpress();
|
||||
app.use({
|
||||
name: 'my plugin',
|
||||
plugin(application) {
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
app.listen();
|
||||
|
||||
//simulate readystatechange
|
||||
document.readyState = 'interactive';
|
||||
document.onreadystatechange();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -18,10 +18,10 @@ describe('frontexpress', () => {
|
||||
|
||||
it('test Middleware class exposed', () => {
|
||||
assert(frontexpress.Middleware);
|
||||
assert(frontexpress.Middleware() instanceof Middleware);
|
||||
assert(new frontexpress.Middleware() instanceof Middleware);
|
||||
|
||||
const m1 = frontexpress.Middleware();
|
||||
const m2 = frontexpress.Middleware();
|
||||
const m1 = new frontexpress.Middleware();
|
||||
const m2 = new frontexpress.Middleware();
|
||||
assert(m1 !== m2);
|
||||
});
|
||||
|
||||
@ -33,4 +33,4 @@ describe('frontexpress', () => {
|
||||
const app2 = frontexpress();
|
||||
assert(app1 !== app2);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
20
test/middleware-test.js
Normal file
20
test/middleware-test.js
Normal file
@ -0,0 +1,20 @@
|
||||
/*eslint-env mocha*/
|
||||
import {assert} from 'chai';
|
||||
import Middleware from '../lib/middleware';
|
||||
|
||||
describe('Middleware', () => {
|
||||
it('check exposed methods', () => {
|
||||
const middleware = new Middleware();
|
||||
assert(middleware.entered);
|
||||
assert(middleware.exited);
|
||||
assert(middleware.updated);
|
||||
assert(middleware.failed);
|
||||
assert(middleware.next);
|
||||
|
||||
middleware.entered();
|
||||
middleware.exited();
|
||||
middleware.updated();
|
||||
middleware.failed();
|
||||
assert(middleware.next());
|
||||
});
|
||||
});
|
@ -50,7 +50,7 @@ describe('Test sample from README', () => {
|
||||
const h3 = (req, res, next) => { spy_log('h3!'); next(); };
|
||||
|
||||
const app = frontexpress();
|
||||
app.set('http-requester', requester);
|
||||
app.set('http requester', requester);
|
||||
|
||||
app.get('/example/a', h1);
|
||||
app.get('/example/a', h2);
|
||||
@ -77,7 +77,7 @@ describe('Test sample from README', () => {
|
||||
const spy_log = sinon.spy();
|
||||
|
||||
const app = frontexpress();
|
||||
app.set('http-requester', requester);
|
||||
app.set('http requester', requester);
|
||||
|
||||
app.route('/book')
|
||||
.get((req, res) => { spy_log('Get a random book');})
|
||||
@ -111,7 +111,7 @@ describe('Test sample from README', () => {
|
||||
|
||||
const router = frontexpress.Router();
|
||||
|
||||
// middleware that is specific to this router
|
||||
// middleware which is specific to this router
|
||||
router.use((req, res, next) => {
|
||||
spy_log(`Time: ${Date.now()}`);
|
||||
next();
|
||||
@ -128,7 +128,7 @@ describe('Test sample from README', () => {
|
||||
});
|
||||
|
||||
const app = frontexpress();
|
||||
app.set('http-requester', requester);
|
||||
app.set('http requester', requester);
|
||||
app.use('/birds', router);
|
||||
|
||||
// make an ajax request on /birds
|
||||
@ -148,4 +148,4 @@ describe('Test sample from README', () => {
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1,35 +1,8 @@
|
||||
/*eslint-env mocha*/
|
||||
/*global global*/
|
||||
import {assert} from 'chai';
|
||||
import chai, {assert} from 'chai';
|
||||
import sinon from 'sinon';
|
||||
import FakeXMLHttpRequest from 'fake-xml-http-request';
|
||||
import Requester, {HTTP_METHODS} from '../lib/requester';
|
||||
|
||||
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);
|
||||
});
|
||||
|
||||
it('GET method uri with query string', () => {
|
||||
const uriFn = HTTP_METHODS['GET'].uri;
|
||||
const dataFn = HTTP_METHODS['GET'].data;
|
||||
|
||||
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);
|
||||
});
|
||||
|
||||
it('GET method uri with query string and anchor', () => {
|
||||
const uriFn = HTTP_METHODS['GET'].uri;
|
||||
const dataFn = HTTP_METHODS['GET'].data;
|
||||
|
||||
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);
|
||||
});
|
||||
});
|
||||
import Requester from '../lib/requester';
|
||||
|
||||
describe('Requester', () => {
|
||||
function xHttpWillRespond(xhttp, readyState, status, statusText, responseText) {
|
||||
@ -51,13 +24,17 @@ describe('Requester', () => {
|
||||
function xHttpWillThrow(xhttp, sendErrorName, openErrorName) {
|
||||
const stub_send = sinon.stub(xhttp, 'send', function() {
|
||||
if (sendErrorName) {
|
||||
throw {name: sendErrorName};
|
||||
const e = new Error(sendErrorName);
|
||||
e.name = sendErrorName;
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
|
||||
const stub_open = sinon.stub(xhttp, 'open', function() {
|
||||
if (openErrorName) {
|
||||
throw {name: openErrorName};
|
||||
const e = new Error(openErrorName);
|
||||
e.name = openErrorName;
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
|
||||
@ -67,8 +44,11 @@ describe('Requester', () => {
|
||||
let xhttp;
|
||||
|
||||
beforeEach(() => {
|
||||
// Stub XMLHttpRequest
|
||||
xhttp = new FakeXMLHttpRequest();
|
||||
xhttp = {
|
||||
setRequestHeader(){},
|
||||
open(){},
|
||||
send(){}
|
||||
};
|
||||
global.XMLHttpRequest = () => {
|
||||
return xhttp;
|
||||
};
|
||||
@ -79,15 +59,27 @@ describe('Requester', () => {
|
||||
it('with data', (done) => {
|
||||
const requester = new Requester();
|
||||
|
||||
const {stub_open, stub_send} = xHttpWillRespond(xhttp, 4, 200, '', '<p>content!</p>');
|
||||
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');
|
||||
assert(request.method === 'GET');
|
||||
assert(request.uri === '/route1');
|
||||
assert(request.data !== undefined);
|
||||
assert(request.data.p1 === 'a');
|
||||
assert(request.data.p2 === 'b');
|
||||
assert(request.data.p3 === 'c');
|
||||
|
||||
assert(response.status === 200);
|
||||
assert(response.statusText === 'OK');
|
||||
assert(response.responseText === '<p>content!</p>');
|
||||
assert(response.errorThrown === undefined);
|
||||
assert(response.errors === undefined);
|
||||
|
||||
done();
|
||||
},
|
||||
(err) => {
|
||||
done(err);
|
||||
(request, response) => {
|
||||
done(response.error);
|
||||
});
|
||||
});
|
||||
|
||||
@ -101,8 +93,8 @@ describe('Requester', () => {
|
||||
assert(stub_send.calledOnce);
|
||||
assert(stub_open.calledBefore(stub_send));
|
||||
|
||||
assert(request.uri === '/route1');
|
||||
assert(request.method === 'GET');
|
||||
assert(request.uri === '/route1');
|
||||
assert(request.data === undefined);
|
||||
|
||||
assert(response.status === 200);
|
||||
@ -113,8 +105,8 @@ describe('Requester', () => {
|
||||
|
||||
done();
|
||||
},
|
||||
(error) => {
|
||||
done(error);
|
||||
(request, response) => {
|
||||
done(response.error);
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -126,7 +118,7 @@ describe('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'}},
|
||||
requester.fetch({method: 'POST', uri:'/route1', headers:{'head1':'value1'}, data:{p1: 'a', p2: 'b', p3: 'c'}},
|
||||
(request, response) => {
|
||||
assert(stub_open.calledOnce);
|
||||
assert(stub_setRequestHeader.calledOnce);
|
||||
@ -135,13 +127,23 @@ describe('Requester', () => {
|
||||
assert(stub_setRequestHeader.calledAfter(stub_open));
|
||||
assert(stub_setRequestHeader.calledBefore(stub_send));
|
||||
|
||||
assert(request.method === 'POST');
|
||||
assert(request.uri === '/route1');
|
||||
assert(request.headers['Content-type'] === 'application/x-www-form-urlencoded');
|
||||
assert(request.data === 'p1=a&p2=b&p3=c');
|
||||
assert(request.data !== undefined);
|
||||
assert(request.data.p1 === 'a');
|
||||
assert(request.data.p2 === 'b');
|
||||
assert(request.data.p3 === 'c');
|
||||
|
||||
assert(response.status === 200);
|
||||
assert(response.statusText === 'OK');
|
||||
assert(response.responseText === '<p>content!</p>');
|
||||
assert(response.errorThrown === undefined);
|
||||
assert(response.errors === undefined);
|
||||
|
||||
done();
|
||||
},
|
||||
(err) => {
|
||||
done(err);
|
||||
(request, response) => {
|
||||
done(response.error);
|
||||
});
|
||||
});
|
||||
|
||||
@ -153,19 +155,18 @@ describe('Requester', () => {
|
||||
requester.fetch({method: 'POST', uri:'/route1'},
|
||||
(request, response) => {
|
||||
assert(stub_open.calledOnce);
|
||||
assert(stub_setRequestHeader.calledOnce);
|
||||
assert(stub_setRequestHeader.callCount === 0);
|
||||
assert(stub_send.calledOnce);
|
||||
assert(stub_open.calledBefore(stub_send));
|
||||
assert(stub_setRequestHeader.calledAfter(stub_open));
|
||||
assert(stub_setRequestHeader.calledBefore(stub_send));
|
||||
|
||||
assert(request.method === 'POST');
|
||||
assert(request.uri === '/route1');
|
||||
assert(request.headers['Content-type'] === 'application/x-www-form-urlencoded');
|
||||
assert(request.data === undefined);
|
||||
|
||||
done();
|
||||
},
|
||||
(err) => {
|
||||
done(err);
|
||||
(request, response) => {
|
||||
done(response.error);
|
||||
});
|
||||
});
|
||||
|
||||
@ -177,20 +178,20 @@ describe('Requester', () => {
|
||||
requester.fetch({method: 'POST', uri:'/route1', headers: {'Accept-Charset': 'utf-8'}},
|
||||
(request, response) => {
|
||||
assert(stub_open.calledOnce);
|
||||
assert(stub_setRequestHeader.calledTwice);
|
||||
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.method === 'POST');
|
||||
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);
|
||||
(request, response) => {
|
||||
done(response.error);
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -377,24 +378,16 @@ describe('Requester', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('request returns unknown error', (done) => {
|
||||
const requester = new Requester();
|
||||
// Removed for reducing size of frontexpress
|
||||
// it('request returns unknown error', () => {
|
||||
// const requester = new Requester();
|
||||
|
||||
const {stub_open, stub_send} = xHttpWillThrow(xhttp, 'BlaBlaError');
|
||||
// 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);
|
||||
// chai.expect(() => {
|
||||
// requester.fetch({method: 'GET', uri:'/route1'});
|
||||
// }).to.throw(/BlaBlaError/);
|
||||
|
||||
assert(response.status === undefined);
|
||||
assert(response.statusText === undefined);
|
||||
assert(response.responseText === undefined);
|
||||
assert(response.errorThrown.name === 'BlaBlaError');
|
||||
assert(response.errors.length !== 0);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
// });
|
||||
});
|
||||
});
|
||||
|
@ -2,7 +2,10 @@
|
||||
import chai, {assert} from 'chai';
|
||||
import sinon from 'sinon';
|
||||
import frontexpress from '../lib/frontexpress';
|
||||
import {HTTP_METHODS} from '../lib/requester';
|
||||
import HTTP_METHODS from '../lib/methods';
|
||||
|
||||
const application = frontexpress();
|
||||
const routeMatcher = application.get('route matcher');
|
||||
|
||||
describe('Router', () => {
|
||||
|
||||
@ -12,11 +15,22 @@ describe('Router', () => {
|
||||
assert(typeof router.all === 'function');
|
||||
assert(typeof router.get === 'function');
|
||||
assert(typeof router.put === 'function');
|
||||
assert(typeof router.patch === 'function');
|
||||
assert(typeof router.post === 'function');
|
||||
assert(typeof router.delete === 'function');
|
||||
});
|
||||
});
|
||||
|
||||
describe('use method', () => {
|
||||
const router = frontexpress.Router();
|
||||
chai.expect(() => {
|
||||
router.use();
|
||||
}).to.throw(TypeError);
|
||||
chai.expect(() => {
|
||||
router.use('dddd');
|
||||
}).to.throw(TypeError);
|
||||
});
|
||||
|
||||
describe('visited method', () => {
|
||||
it('get visited route', ()=> {
|
||||
const router = frontexpress.Router();
|
||||
@ -36,7 +50,7 @@ describe('Router', () => {
|
||||
|
||||
router.get(middleware);
|
||||
|
||||
const r1 = router.routes('/', 'GET');
|
||||
const r1 = router.routes(application, {uri: '/', method: 'GET'});
|
||||
assert(r1.length === 1);
|
||||
assert(r1[0].uri === undefined);
|
||||
assert(r1[0].method === 'GET');
|
||||
@ -54,37 +68,37 @@ describe('Router', () => {
|
||||
.post('/route2', middleware2)
|
||||
.all('/route3', middleware3);
|
||||
|
||||
const r1 = router.routes('/route1', 'GET');
|
||||
const r1 = router.routes(application, {uri: '/route1', method: 'GET'});
|
||||
assert(r1.length === 1);
|
||||
assert(r1[0].uri === '/route1');
|
||||
assert(r1[0].method === 'GET');
|
||||
assert(r1[0].middleware === middleware1);
|
||||
|
||||
const r2 = router.routes('/route2', 'POST');
|
||||
const r2 = router.routes(application, {uri: '/route2', method: 'POST'});
|
||||
assert(r2.length === 1);
|
||||
assert(r2[0].uri === '/route2');
|
||||
assert(r2[0].method === 'POST');
|
||||
assert(r2[0].middleware === middleware2);
|
||||
|
||||
let r3 = router.routes('/route3', 'GET');
|
||||
let r3 = router.routes(application, {uri: '/route3', method: 'GET'});
|
||||
assert(r3.length === 1);
|
||||
assert(r3[0].uri === '/route3');
|
||||
assert(r3[0].method === 'GET');
|
||||
assert(r3[0].middleware === middleware3);
|
||||
|
||||
r3 = router.routes('/route3', 'POST');
|
||||
r3 = router.routes(application, {uri: '/route3', method: 'POST'});
|
||||
assert(r3.length === 1);
|
||||
assert(r3[0].uri === '/route3');
|
||||
assert(r3[0].method === 'POST');
|
||||
assert(r3[0].middleware === middleware3);
|
||||
|
||||
r3 = router.routes('/route3', 'PUT');
|
||||
r3 = router.routes(application, {uri: '/route3', method: 'PUT'});
|
||||
assert(r3.length === 1);
|
||||
assert(r3[0].uri === '/route3');
|
||||
assert(r3[0].method === 'PUT');
|
||||
assert(r3[0].middleware === middleware3);
|
||||
|
||||
r3 = router.routes('/route3', 'DELETE');
|
||||
r3 = router.routes(application, {uri: '/route3', method: 'DELETE'});
|
||||
assert(r3.length === 1);
|
||||
assert(r3[0].uri === '/route3');
|
||||
assert(r3[0].method === 'DELETE');
|
||||
@ -93,11 +107,11 @@ describe('Router', () => {
|
||||
|
||||
it('no root path and regexp uri', ()=> {
|
||||
const router = frontexpress.Router();
|
||||
const middleware = frontexpress.Middleware();
|
||||
const middleware = new frontexpress.Middleware();
|
||||
|
||||
router.get(/^\/route1/, middleware);
|
||||
|
||||
const r = router.routes('/route1', 'GET');
|
||||
const r = router.routes(application, {uri: '/route1', method: 'GET'});
|
||||
assert(r.length === 1);
|
||||
assert(r[0].uri instanceof RegExp);
|
||||
assert(r[0].uri.toString() === new RegExp('^\/route1').toString());
|
||||
@ -108,9 +122,9 @@ describe('Router', () => {
|
||||
it('with root path /route1 and path /subroute', () => {
|
||||
const router = frontexpress.Router('/route1');
|
||||
|
||||
router.get('/subroute', frontexpress.Middleware());
|
||||
router.get('/subroute', new frontexpress.Middleware());
|
||||
|
||||
const r = router.routes('/route1/subroute', 'GET');
|
||||
const r = router.routes(application, {uri: '/route1/subroute', method: 'GET'});
|
||||
assert(r.length === 1);
|
||||
assert(r[0].uri === '/route1/subroute');
|
||||
});
|
||||
@ -118,9 +132,9 @@ describe('Router', () => {
|
||||
it('with root path /route1 and no path uri', () => {
|
||||
const router = frontexpress.Router('/route1');
|
||||
|
||||
router.get(frontexpress.Middleware());
|
||||
router.get(new frontexpress.Middleware());
|
||||
|
||||
const r = router.routes('/route1', 'GET');
|
||||
const r = router.routes(application, {uri: '/route1', method: 'GET'});
|
||||
assert(r.length === 1);
|
||||
assert(r[0].uri === '/route1');
|
||||
});
|
||||
@ -128,9 +142,9 @@ describe('Router', () => {
|
||||
it('duplicate / in route', () => {
|
||||
const router = frontexpress.Router('/route1/');
|
||||
|
||||
router.get('/subroute', frontexpress.Middleware());
|
||||
router.get('/subroute', new frontexpress.Middleware());
|
||||
|
||||
const r = router.routes('/route1/subroute', 'GET');
|
||||
const r = router.routes(application, {uri: '/route1/subroute', method: 'GET'});
|
||||
assert(r.length === 1);
|
||||
assert(r[0].uri === '/route1/subroute');
|
||||
});
|
||||
@ -138,9 +152,9 @@ describe('Router', () => {
|
||||
it('spaces in route', () => {
|
||||
let router = frontexpress.Router(' /route1 ');
|
||||
|
||||
router.get('/subroute ', frontexpress.Middleware());
|
||||
router.get('/subroute ', new frontexpress.Middleware());
|
||||
|
||||
let r = router.routes('/route1/subroute', 'GET');
|
||||
let r = router.routes(application, {uri: '/route1/subroute', method: 'GET'});
|
||||
assert(r.length === 1);
|
||||
assert(r[0].uri === '/route1/subroute');
|
||||
|
||||
@ -148,9 +162,9 @@ describe('Router', () => {
|
||||
|
||||
router = frontexpress.Router(' /route1 ');
|
||||
|
||||
router.get(frontexpress.Middleware());
|
||||
router.get(new frontexpress.Middleware());
|
||||
|
||||
r = router.routes('/route1', 'GET');
|
||||
r = router.routes(application, {uri: '/route1', method: 'GET'});
|
||||
assert(r.length === 1);
|
||||
assert(r[0].uri === '/route1');
|
||||
});
|
||||
@ -158,9 +172,9 @@ describe('Router', () => {
|
||||
it('route with query string', () => {
|
||||
let router = frontexpress.Router('/route1 ');
|
||||
|
||||
router.get('/subroute', frontexpress.Middleware());
|
||||
router.get('/subroute', new frontexpress.Middleware());
|
||||
|
||||
let r = router.routes('/route1/subroute?a=b&c=d', 'GET');
|
||||
let r = router.routes(application, {uri: '/route1/subroute?a=b&c=d', method: 'GET'});
|
||||
assert(r.length === 1);
|
||||
assert(r[0].uri === '/route1/subroute');
|
||||
assert(r[0].data === undefined);
|
||||
@ -169,9 +183,9 @@ describe('Router', () => {
|
||||
it('route with anchor', () => {
|
||||
let router = frontexpress.Router('/route1 ');
|
||||
|
||||
router.get('/subroute', frontexpress.Middleware());
|
||||
router.get('/subroute', new frontexpress.Middleware());
|
||||
|
||||
let r = router.routes('/route1/subroute#a=b&c=d', 'GET');
|
||||
let r = router.routes(application, {uri: '/route1/subroute#a=b&c=d', method: 'GET'});
|
||||
assert(r.length === 1);
|
||||
assert(r[0].uri === '/route1/subroute');
|
||||
assert(r[0].data === undefined);
|
||||
@ -180,9 +194,9 @@ describe('Router', () => {
|
||||
it('route with query string and anchor', () => {
|
||||
let router = frontexpress.Router('/route1 ');
|
||||
|
||||
router.get('/subroute', frontexpress.Middleware());
|
||||
router.get('/subroute', new frontexpress.Middleware());
|
||||
|
||||
let r = router.routes('/route1/subroute?a=b&c=d#anchor1', 'GET');
|
||||
let r = router.routes(application, {uri: '/route1/subroute?a=b&c=d#anchor1', method: 'GET'});
|
||||
assert(r.length === 1);
|
||||
assert(r[0].uri === '/route1/subroute');
|
||||
assert(r[0].data === undefined);
|
||||
@ -204,10 +218,10 @@ describe('Router', () => {
|
||||
|
||||
it('only middleware as argument', () => {
|
||||
const router = frontexpress.Router('/route1');
|
||||
const middleware = frontexpress.Middleware();
|
||||
const middleware = new frontexpress.Middleware();
|
||||
|
||||
const spied_methods = [];
|
||||
for (const method of Object.keys(HTTP_METHODS)) {
|
||||
for (const method of HTTP_METHODS) {
|
||||
spied_methods.push(sinon.spy(router, method.toLowerCase()));
|
||||
}
|
||||
|
||||
@ -220,10 +234,10 @@ describe('Router', () => {
|
||||
|
||||
it('with path /route1 and middleware as arguments', () => {
|
||||
const router = frontexpress.Router();
|
||||
const middleware = frontexpress.Middleware();
|
||||
const middleware = new frontexpress.Middleware();
|
||||
|
||||
const spied_methods = [];
|
||||
for (const method of Object.keys(HTTP_METHODS)) {
|
||||
for (const method of HTTP_METHODS) {
|
||||
spied_methods.push(sinon.spy(router, method.toLowerCase()));
|
||||
}
|
||||
|
||||
@ -250,11 +264,11 @@ describe('Router', () => {
|
||||
|
||||
it('only middleware as argument', () => {
|
||||
const router = frontexpress.Router('/');
|
||||
const middleware = frontexpress.Middleware();
|
||||
const middleware = new frontexpress.Middleware();
|
||||
|
||||
router.get(middleware);
|
||||
|
||||
const r = router.routes('/', 'GET');
|
||||
const r = router.routes(application, {uri: '/', method: 'GET'});
|
||||
assert(r.length === 1);
|
||||
assert(r[0].uri === '/');
|
||||
assert(r[0].method === 'GET');
|
||||
@ -263,11 +277,11 @@ describe('Router', () => {
|
||||
|
||||
it('with path /route1 and middleware as arguments', () => {
|
||||
const router = frontexpress.Router();
|
||||
const middleware = frontexpress.Middleware();
|
||||
const middleware = new frontexpress.Middleware();
|
||||
|
||||
router.get('/route1', middleware);
|
||||
|
||||
const r = router.routes('/route1', 'GET');
|
||||
const r = router.routes(application, {uri: '/route1', method: 'GET'});
|
||||
assert(r.length === 1);
|
||||
assert(r[0].uri === '/route1');
|
||||
assert(r[0].method === 'GET');
|
||||
@ -276,16 +290,16 @@ describe('Router', () => {
|
||||
|
||||
it('router with regexp and route with /route1', () => {
|
||||
const router = frontexpress.Router(/^\//);
|
||||
const middleware = frontexpress.Middleware();
|
||||
const middleware = new frontexpress.Middleware();
|
||||
chai.expect(() => router.get('/route1', middleware)).to.throw(TypeError);
|
||||
});
|
||||
|
||||
it('router with regexp and route without uri', () => {
|
||||
const router = frontexpress.Router(/^\/part/);
|
||||
const middleware = frontexpress.Middleware();
|
||||
const middleware = new frontexpress.Middleware();
|
||||
router.get(middleware);
|
||||
|
||||
const r = router.routes('/part1', 'GET');
|
||||
const r = router.routes(application, {uri: '/part1', method: 'GET'});
|
||||
assert(r.length === 1);
|
||||
assert(r[0].uri instanceof RegExp);
|
||||
assert(r[0].uri.toString() === new RegExp('^\/part').toString());
|
||||
@ -293,4 +307,113 @@ describe('Router', () => {
|
||||
assert(r[0].middleware === middleware);
|
||||
});
|
||||
});
|
||||
|
||||
describe('check route matcher', () => {
|
||||
it('/', () => {
|
||||
const route = {uri: '/', method: 'GET'};
|
||||
|
||||
const request = {uri: '/', method: 'GET', params: {}};
|
||||
assert(routeMatcher(request, route));
|
||||
assert.deepEqual(request.params, {});
|
||||
});
|
||||
|
||||
it('/a/b/c', () => {
|
||||
const route = {uri: '/a/b/c', method: 'GET'};
|
||||
|
||||
let request = {uri: '/a/b/c', method: 'GET', params: {}};
|
||||
assert(routeMatcher(request, route));
|
||||
assert.deepEqual(request.params, {});
|
||||
|
||||
request = {uri: '/a/b/c/', method: 'GET', params: {}};
|
||||
assert.strictEqual(routeMatcher(request, route), false);
|
||||
});
|
||||
|
||||
it('/^\//', () => {
|
||||
const route = {uri: /^\//, method: 'GET'};
|
||||
|
||||
const request = {uri: '/a/b/c', method: 'GET', params: {}};
|
||||
assert(routeMatcher(request, route));
|
||||
assert.deepEqual(request.params, {});
|
||||
});
|
||||
|
||||
it('/:id', () => {
|
||||
const route = {uri: '/:id', method: 'GET'};
|
||||
|
||||
const request = {uri: '/1000', method: 'GET', params: {}};
|
||||
assert(routeMatcher(request, route));
|
||||
assert.strictEqual(request.params.id, 1000);
|
||||
});
|
||||
|
||||
it('/user/:id', () => {
|
||||
const route = {uri: '/user/:id', method: 'GET'};
|
||||
|
||||
let request = {uri: '/user/1000', method: 'GET', params: {}};
|
||||
assert(routeMatcher(request, route));
|
||||
assert.strictEqual(request.params.id, 1000);
|
||||
|
||||
request = {uri: '/user/100.2122', method: 'GET', params: {}};
|
||||
assert(routeMatcher(request, route));
|
||||
assert.strictEqual(request.params.id, 100.2122);
|
||||
|
||||
request = {uri: '/user', method: 'GET', params: {}};
|
||||
assert.strictEqual(routeMatcher(request, route), false);
|
||||
|
||||
request = {uri: '/user/', method: 'GET', params: {}};
|
||||
assert.strictEqual(routeMatcher(request, route), false);
|
||||
});
|
||||
|
||||
it('/user/:id with id as coma separated values', () => {
|
||||
const route = {uri: '/user/:id', method: 'GET'};
|
||||
|
||||
let request = {uri: '/user/1,2,3', method: 'GET', params: {}};
|
||||
assert(routeMatcher(request, route));
|
||||
assert.deepEqual(request.params, {id: [1,2,3]});
|
||||
|
||||
request = {uri: '/user/1.5,2.55,4.25', method: 'GET', params: {}};
|
||||
assert(routeMatcher(request, route));
|
||||
assert.deepEqual(request.params, {id: [1.5,2.55,4.25]});
|
||||
|
||||
request = {uri: '/user/a,b,c', method: 'GET', params: {}};
|
||||
assert(routeMatcher(request, route));
|
||||
assert.deepEqual(request.params, {id: ['a','b','c']});
|
||||
});
|
||||
|
||||
it('/user/:id?', () => {
|
||||
const route = {uri: '/user/:id?', method: 'GET'};
|
||||
|
||||
let request = {uri: '/user/1000', method: 'GET', params: {}};
|
||||
assert(routeMatcher(request, route));
|
||||
assert.strictEqual(request.params.id, 1000);
|
||||
|
||||
request = {uri: '/user', method: 'GET', params: {}};
|
||||
assert.strictEqual(routeMatcher(request, route), true);
|
||||
assert.deepEqual(request.params, {id: undefined});
|
||||
|
||||
request = {uri: '/user/', method: 'GET', params: {}};
|
||||
assert.strictEqual(routeMatcher(request, route), false);
|
||||
});
|
||||
|
||||
it('/user/:firstname/:lastname', () => {
|
||||
const route = {uri: '/user/:firstname/:lastname', method: 'GET'};
|
||||
|
||||
let request = {uri: '/user/camel/aissani', method: 'GET', params: {}};
|
||||
assert(routeMatcher(request, route));
|
||||
assert.deepEqual(request.params, {firstname: 'camel', lastname:'aissani'} );
|
||||
|
||||
request = {uri: '/user/camel', method: 'GET', params: {}};
|
||||
assert.strictEqual(routeMatcher(request, route), false);
|
||||
});
|
||||
|
||||
it('/user/:firstname?/:lastname', () => {
|
||||
const route = {uri: '/user/:firstname?/:lastname', method: 'GET'};
|
||||
|
||||
let request = {uri: '/user/camel/aissani', method: 'GET', params: {}};
|
||||
assert(routeMatcher(request, route));
|
||||
assert.deepEqual(request.params, {firstname: 'camel', lastname:'aissani'} );
|
||||
|
||||
request = {uri: '/user/aissani', method: 'GET', params: {}};
|
||||
assert(routeMatcher(request, route));
|
||||
assert.deepEqual(request.params, {firstname: undefined, lastname:'aissani'} );
|
||||
});
|
||||
});
|
||||
});
|
||||
|
65
test/settings-test.js
Normal file
65
test/settings-test.js
Normal file
@ -0,0 +1,65 @@
|
||||
/*eslint-env mocha*/
|
||||
import chai, {assert} from 'chai';
|
||||
import Settings from '../lib/settings';
|
||||
|
||||
describe('Settings', () => {
|
||||
const settings = new Settings();
|
||||
|
||||
describe('http GET method transformer', () => {
|
||||
it('check setting rule', () => {
|
||||
const defaultHttpGetTransformer = settings.get('http GET transformer');
|
||||
|
||||
chai.expect(() => settings.set('http GET transformer', null)).to.throw(TypeError);
|
||||
chai.expect(() => settings.set('http GET transformer', {})).to.throw(TypeError);
|
||||
chai.expect(() => settings.set('http GET transformer', {foo:()=>{}})).to.throw(TypeError);
|
||||
|
||||
const uri = () => {};
|
||||
settings.set('http GET transformer', {uri});
|
||||
assert.deepEqual(settings.get('http GET transformer'), {uri});
|
||||
|
||||
const headers = () => {};
|
||||
settings.set('http GET transformer', {headers});
|
||||
assert.deepEqual(settings.get('http GET transformer'), {headers});
|
||||
|
||||
const data = () => {};
|
||||
settings.set('http GET transformer', {data});
|
||||
assert.deepEqual(settings.get('http GET transformer'), {data});
|
||||
|
||||
settings.set('http GET transformer', defaultHttpGetTransformer);
|
||||
|
||||
|
||||
const defaultRouteMatcher = settings.get('route matcher');
|
||||
chai.expect(() => settings.set('route matcher', null)).to.throw(TypeError);
|
||||
chai.expect(() => settings.set('route matcher', {})).to.throw(TypeError);
|
||||
chai.expect(() => settings.set('route matcher', 1)).to.throw(TypeError);
|
||||
|
||||
const routeMatcher = () => {};
|
||||
settings.set('route matcher', routeMatcher);
|
||||
assert.strictEqual(settings.get('route matcher'), routeMatcher);
|
||||
});
|
||||
|
||||
it('simple uri', () => {
|
||||
const uriFn = settings.get('http GET transformer').uri;
|
||||
const dataFn = settings.get('http GET transformer').data;
|
||||
|
||||
assert(uriFn({uri: '/route', data:{a:'b', c:'d'}}) === '/route?a=b&c=d');
|
||||
assert(dataFn === undefined);
|
||||
});
|
||||
|
||||
it('uri with query string', () => {
|
||||
const uriFn = settings.get('http GET transformer').uri;
|
||||
const dataFn = settings.get('http GET transformer').data;
|
||||
|
||||
assert(uriFn({uri: '/route?x=y&z=a', data:{a:'b', c:'d'}}) === '/route?x=y&z=a&a=b&c=d');
|
||||
assert(dataFn === undefined);
|
||||
});
|
||||
|
||||
it('uri with query string and anchor', () => {
|
||||
const uriFn = settings.get('http GET transformer').uri;
|
||||
const dataFn = settings.get('http GET transformer').data;
|
||||
|
||||
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 === undefined);
|
||||
});
|
||||
});
|
||||
});
|
Loading…
x
Reference in New Issue
Block a user