Browse Source

refactor: makes the path propertiy as optional

e22m4u 3 weeks ago
parent
commit
f69c296232

+ 38 - 4
dist/cjs/index.cjs

@@ -925,7 +925,7 @@ function validateRouteDefinition(routeDef) {
       routeDef.method
     );
   }
-  if (typeof routeDef.path !== "string") {
+  if (routeDef.path !== void 0 && typeof routeDef.path !== "string") {
     throw new import_js_format12.InvalidArgumentError(
       'Option "path" must be a String, but %v was given.',
       routeDef.path
@@ -937,6 +937,40 @@ function validateRouteDefinition(routeDef) {
       routeDef.handler
     );
   }
+  if (routeDef.preHandler !== void 0) {
+    if (Array.isArray(routeDef.preHandler)) {
+      routeDef.preHandler.forEach((preHandler) => {
+        if (typeof preHandler !== "function") {
+          throw new import_js_format12.InvalidArgumentError(
+            "Route pre-handler must be a Function, but %v was given.",
+            preHandler
+          );
+        }
+      });
+    } else if (typeof routeDef.preHandler !== "function") {
+      throw new import_js_format12.InvalidArgumentError(
+        'Option "preHandler" must be a Function or an Array, but %v was given.',
+        routeDef.preHandler
+      );
+    }
+  }
+  if (routeDef.postHandler !== void 0) {
+    if (Array.isArray(routeDef.postHandler)) {
+      routeDef.postHandler.forEach((postHandler) => {
+        if (typeof postHandler !== "function") {
+          throw new import_js_format12.InvalidArgumentError(
+            "Route post-handler must be a Function, but %v was given.",
+            postHandler
+          );
+        }
+      });
+    } else if (typeof routeDef.postHandler !== "function") {
+      throw new import_js_format12.InvalidArgumentError(
+        'Option "postHandler" must be a Function or an Array, but %v was given.',
+        routeDef.postHandler
+      );
+    }
+  }
   if (routeDef.meta !== void 0) {
     if (!routeDef.meta || typeof routeDef.meta !== "object" || Array.isArray(routeDef.meta)) {
       throw new import_js_format12.InvalidArgumentError(
@@ -1044,18 +1078,18 @@ var _Route = class _Route extends import_js_debug.Debuggable {
     });
     validateRouteDefinition(routeDef);
     this._method = routeDef.method.toUpperCase();
-    this._path = routeDef.path;
+    this._path = routeDef.path || "";
     if (routeDef.meta !== void 0) {
       this._meta = cloneDeep(routeDef.meta);
     }
     this._handler = routeDef.handler;
-    if (routeDef.preHandler != null) {
+    if (routeDef.preHandler !== void 0) {
       const preHandlerHooks = Array.isArray(routeDef.preHandler) ? routeDef.preHandler : [routeDef.preHandler];
       preHandlerHooks.forEach((hook) => {
         this._hookRegistry.addHook(RouterHookType.PRE_HANDLER, hook);
       });
     }
-    if (routeDef.postHandler != null) {
+    if (routeDef.postHandler !== void 0) {
       const postHandlerHooks = Array.isArray(routeDef.postHandler) ? routeDef.postHandler : [routeDef.postHandler];
       postHandlerHooks.forEach((hook) => {
         this._hookRegistry.addHook(RouterHookType.POST_HANDLER, hook);

+ 1 - 1
src/route/route.d.ts

@@ -48,7 +48,7 @@ export type RouteMeta = {
  */
 export type RouteDefinition = {
   method: string;
-  path: string;
+  path?: string;
   handler: RouteHandler;
   preHandler?: RoutePreHandler | RoutePreHandler[];
   postHandler?: RoutePostHandler | RoutePostHandler[];

+ 4 - 4
src/route/route.js

@@ -11,7 +11,7 @@ import {validateRouteDefinition} from './validate-route-definition.js';
  * @typedef {(ctx: RequestContext, data: *) => *} RoutePostHandler
  * @typedef {{
  *   method: string,
- *   path: string,
+ *   path?: string,
  *   handler: RouteHandler,
  *   preHandler?: RoutePreHandler|(RoutePreHandler[]),
  *   postHandler?: RoutePostHandler|(RoutePostHandler[]),
@@ -139,12 +139,12 @@ export class Route extends Debuggable {
     });
     validateRouteDefinition(routeDef);
     this._method = routeDef.method.toUpperCase();
-    this._path = routeDef.path;
+    this._path = routeDef.path || '';
     if (routeDef.meta !== undefined) {
       this._meta = cloneDeep(routeDef.meta);
     }
     this._handler = routeDef.handler;
-    if (routeDef.preHandler != null) {
+    if (routeDef.preHandler !== undefined) {
       const preHandlerHooks = Array.isArray(routeDef.preHandler)
         ? routeDef.preHandler
         : [routeDef.preHandler];
@@ -152,7 +152,7 @@ export class Route extends Debuggable {
         this._hookRegistry.addHook(RouterHookType.PRE_HANDLER, hook);
       });
     }
-    if (routeDef.postHandler != null) {
+    if (routeDef.postHandler !== undefined) {
       const postHandlerHooks = Array.isArray(routeDef.postHandler)
         ? routeDef.postHandler
         : [routeDef.postHandler];

+ 139 - 137
src/route/route.spec.js

@@ -8,7 +8,7 @@ import {createRequestMock, createResponseMock} from '../utils/index.js';
 
 describe('Route', function () {
   describe('constructor', function () {
-    it('requires the "routeDef" parameter to be an Object', function () {
+    it('should require the "routeDef" parameter to be an Object', function () {
       const throwable = v => () => new Route(v);
       const error = v =>
         format('Route definition must be an Object, but %s was given.', v);
@@ -24,17 +24,15 @@ describe('Route', function () {
       expect(throwable(() => undefined)).to.throw(error('Function'));
       throwable({
         method: HttpMethod.GET,
-        path: '/',
         handler: () => undefined,
       })();
     });
 
     describe('the "method" option', function () {
-      it('requires the "method" option to be a non-empty String', function () {
+      it('should require the "method" option to be a non-empty String', function () {
         const throwable = v => () =>
           new Route({
             method: v,
-            path: '/',
             handler: () => undefined,
           });
         const error = v =>
@@ -55,10 +53,9 @@ describe('Route', function () {
         throwable(HttpMethod.GET)();
       });
 
-      it('sets the "method" option in upper case to the "method" property', function () {
+      it('should set the "method" option to the "method" property in upper case', function () {
         const route = new Route({
           method: 'post',
-          path: '/',
           handler: () => undefined,
         });
         expect(route.method).to.be.eq('POST');
@@ -66,7 +63,7 @@ describe('Route', function () {
     });
 
     describe('the "path" option', function () {
-      it('requires the "path" option to be a String', function () {
+      it('should require the "path" option to be a String', function () {
         const throwable = v => () =>
           new Route({
             method: HttpMethod.GET,
@@ -82,13 +79,13 @@ describe('Route', function () {
         expect(throwable(null)).to.throw(error('null'));
         expect(throwable({})).to.throw(error('Object'));
         expect(throwable([])).to.throw(error('Array'));
-        expect(throwable(undefined)).to.throw(error('undefined'));
         expect(throwable(() => undefined)).to.throw(error('Function'));
         throwable('str')();
         throwable('')();
+        throwable(undefined)();
       });
 
-      it('sets the "path" option to the "path" property', function () {
+      it('should set the "path" option to the "path" property', function () {
         const value = '/myPath';
         const route = new Route({
           method: HttpMethod.GET,
@@ -97,75 +94,21 @@ describe('Route', function () {
         });
         expect(route.path).to.be.eq(value);
       });
-    });
-
-    describe('the "meta" option', function () {
-      it('requires the "meta" option to be a plain Object', function () {
-        const throwable = v => () =>
-          new Route({
-            method: HttpMethod.GET,
-            path: 'path',
-            handler: () => undefined,
-            meta: v,
-          });
-        const error = v =>
-          format('Option "meta" must be an Object, but %s was given.', v);
-        expect(throwable('str')).to.throw(error('"str"'));
-        expect(throwable('')).to.throw(error('""'));
-        expect(throwable(10)).to.throw(error('10'));
-        expect(throwable(0)).to.throw(error('0'));
-        expect(throwable(true)).to.throw(error('true'));
-        expect(throwable(false)).to.throw(error('false'));
-        expect(throwable([])).to.throw(error('Array'));
-        expect(throwable(null)).to.throw(error('null'));
-        expect(throwable(() => undefined)).to.throw(error('Function'));
-        throwable({foo: 'bar'})();
-        throwable({})();
-        throwable(undefined)();
-      });
-
-      it('sets the "meta" option to the "meta" property as a deep copy', function () {
-        const metaData = {foo: {bar: {baz: 'qux'}}};
-        const route = new Route({
-          method: 'post',
-          path: '/',
-          handler: () => undefined,
-          meta: metaData,
-        });
-        expect(route.meta).to.be.not.eq(metaData);
-        expect(route.meta).to.be.eql(metaData);
-        expect(route.meta.foo).to.be.not.eq(metaData.foo);
-        expect(route.meta.foo).to.be.eql(metaData.foo);
-        expect(route.meta.foo.bar).to.be.not.eq(metaData.foo.bar);
-        expect(route.meta.foo.bar).to.be.eql(metaData.foo.bar);
-      });
 
-      it('sets an empty object to the "meta" property if the "meta" option is not provided', function () {
+      it('should set an empty string to the "path" property when the "path" option is not provided', function () {
         const route = new Route({
-          method: 'post',
-          path: '/',
-          handler: () => undefined,
-        });
-        expect(route.meta).to.be.eql({});
-      });
-
-      it('sets an empty object to the "meta" property if the "meta" option is undefined', function () {
-        const route = new Route({
-          method: 'post',
-          path: '/',
+          method: HttpMethod.GET,
           handler: () => undefined,
-          meta: undefined,
         });
-        expect(route.meta).to.be.eql({});
+        expect(route.path).to.be.eq('');
       });
     });
 
     describe('the "handler" option', function () {
-      it('requires the "handler" option to be a non-empty String', function () {
+      it('should require the "handler" option to be a Function', function () {
         const throwable = v => () =>
           new Route({
             method: HttpMethod.GET,
-            path: '/',
             handler: v,
           });
         const error = v =>
@@ -183,11 +126,10 @@ describe('Route', function () {
         throwable(() => undefined)();
       });
 
-      it('sets the "handler" option to the "handler" property', function () {
+      it('should set the "handler" option to the "handler" property', function () {
         const value = () => undefined;
         const route = new Route({
           method: HttpMethod.GET,
-          path: '/',
           handler: value,
         });
         expect(route.handler).to.be.eq(value);
@@ -195,55 +137,58 @@ describe('Route', function () {
     });
 
     describe('the "preHandler" option', function () {
-      it('requires the "preHandler" option to be a Function or an Array of Function', function () {
-        const throwable1 = v => () =>
+      it('should require the "preHandler" option to be a Function or an Array of Function', function () {
+        const throwable = v => () =>
           new Route({
             method: HttpMethod.GET,
-            path: '/',
             preHandler: v,
             handler: () => undefined,
           });
         const error = v =>
           format(
-            'The hook "preHandler" must be a Function, but %s was given.',
+            'Option "preHandler" must be a Function ' +
+              'or an Array, but %s was given.',
             v,
           );
-        expect(throwable1('str')).to.throw(error('"str"'));
-        expect(throwable1('')).to.throw(error('""'));
-        expect(throwable1(10)).to.throw(error('10'));
-        expect(throwable1(0)).to.throw(error('0'));
-        expect(throwable1(true)).to.throw(error('true'));
-        expect(throwable1(false)).to.throw(error('false'));
-        expect(throwable1({})).to.throw(error('Object'));
-        throwable1([])();
-        throwable1(() => undefined)();
-        throwable1(null)();
-        throwable1(undefined)();
-        const throwable2 = v => () =>
+        expect(throwable('str')).to.throw(error('"str"'));
+        expect(throwable('')).to.throw(error('""'));
+        expect(throwable(10)).to.throw(error('10'));
+        expect(throwable(0)).to.throw(error('0'));
+        expect(throwable(true)).to.throw(error('true'));
+        expect(throwable(false)).to.throw(error('false'));
+        expect(throwable({})).to.throw(error('Object'));
+        expect(throwable(null)).to.throw(error('null'));
+        throwable([])();
+        throwable(() => undefined)();
+        throwable(undefined)();
+      });
+
+      it('should require an array of the "preHandler" option to contain a Function', function () {
+        const throwable = v => () =>
           new Route({
             method: HttpMethod.GET,
-            path: '/',
             preHandler: [v],
             handler: () => undefined,
           });
-        expect(throwable2('str')).to.throw(error('"str"'));
-        expect(throwable2('')).to.throw(error('""'));
-        expect(throwable2(10)).to.throw(error('10'));
-        expect(throwable2(0)).to.throw(error('0'));
-        expect(throwable2(true)).to.throw(error('true'));
-        expect(throwable2(false)).to.throw(error('false'));
-        expect(throwable2({})).to.throw(error('Object'));
-        expect(throwable2(null)).to.throw(error('null'));
-        expect(throwable2([])).to.throw(error('Array'));
-        expect(throwable2(undefined)).to.throw(error('undefined'));
-        throwable2(() => undefined)();
+        const error = v =>
+          format('Route pre-handler must be a Function, but %s was given.', v);
+        expect(throwable('str')).to.throw(error('"str"'));
+        expect(throwable('')).to.throw(error('""'));
+        expect(throwable(10)).to.throw(error('10'));
+        expect(throwable(0)).to.throw(error('0'));
+        expect(throwable(true)).to.throw(error('true'));
+        expect(throwable(false)).to.throw(error('false'));
+        expect(throwable({})).to.throw(error('Object'));
+        expect(throwable([])).to.throw(error('Array'));
+        expect(throwable(null)).to.throw(error('null'));
+        expect(throwable(undefined)).to.throw(error('undefined'));
+        throwable(() => undefined)();
       });
 
-      it('adds a Function to "preHandler" hooks', function () {
+      it('should add a Function to "preHandler" hooks', function () {
         const value = () => undefined;
         const route = new Route({
           method: HttpMethod.GET,
-          path: '/',
           preHandler: value,
           handler: () => undefined,
         });
@@ -251,11 +196,10 @@ describe('Route', function () {
           .be.true;
       });
 
-      it('adds a Function Array to "preHandler" hooks', function () {
+      it('should add a Function Array to "preHandler" hooks', function () {
         const value = [() => undefined, () => undefined];
         const route = new Route({
           method: HttpMethod.GET,
-          path: '/',
           preHandler: value,
           handler: () => undefined,
         });
@@ -267,55 +211,58 @@ describe('Route', function () {
     });
 
     describe('the "postHandler" option', function () {
-      it('requires the "postHandler" option to be a Function or an Array of Function', function () {
-        const throwable1 = v => () =>
+      it('should require the "postHandler" option to be a Function or an Array of Function', function () {
+        const throwable = v => () =>
           new Route({
             method: HttpMethod.GET,
-            path: '/',
-            handler: () => undefined,
             postHandler: v,
+            handler: () => undefined,
           });
         const error = v =>
           format(
-            'The hook "postHandler" must be a Function, but %s was given.',
+            'Option "postHandler" must be a Function ' +
+              'or an Array, but %s was given.',
             v,
           );
-        expect(throwable1('str')).to.throw(error('"str"'));
-        expect(throwable1('')).to.throw(error('""'));
-        expect(throwable1(10)).to.throw(error('10'));
-        expect(throwable1(0)).to.throw(error('0'));
-        expect(throwable1(true)).to.throw(error('true'));
-        expect(throwable1(false)).to.throw(error('false'));
-        expect(throwable1({})).to.throw(error('Object'));
-        throwable1([])();
-        throwable1(() => undefined)();
-        throwable1(null)();
-        throwable1(undefined)();
-        const throwable2 = v => () =>
+        expect(throwable('str')).to.throw(error('"str"'));
+        expect(throwable('')).to.throw(error('""'));
+        expect(throwable(10)).to.throw(error('10'));
+        expect(throwable(0)).to.throw(error('0'));
+        expect(throwable(true)).to.throw(error('true'));
+        expect(throwable(false)).to.throw(error('false'));
+        expect(throwable({})).to.throw(error('Object'));
+        expect(throwable(null)).to.throw(error('null'));
+        throwable([])();
+        throwable(() => undefined)();
+        throwable(undefined)();
+      });
+
+      it('should require an array of the "postHandler" option to contain a Function', function () {
+        const throwable = v => () =>
           new Route({
             method: HttpMethod.GET,
-            path: '/',
-            handler: () => undefined,
             postHandler: [v],
+            handler: () => undefined,
           });
-        expect(throwable2('str')).to.throw(error('"str"'));
-        expect(throwable2('')).to.throw(error('""'));
-        expect(throwable2(10)).to.throw(error('10'));
-        expect(throwable2(0)).to.throw(error('0'));
-        expect(throwable2(true)).to.throw(error('true'));
-        expect(throwable2(false)).to.throw(error('false'));
-        expect(throwable2({})).to.throw(error('Object'));
-        expect(throwable2(null)).to.throw(error('null'));
-        expect(throwable2([])).to.throw(error('Array'));
-        expect(throwable2(undefined)).to.throw(error('undefined'));
-        throwable2(() => undefined)();
+        const error = v =>
+          format('Route post-handler must be a Function, but %s was given.', v);
+        expect(throwable('str')).to.throw(error('"str"'));
+        expect(throwable('')).to.throw(error('""'));
+        expect(throwable(10)).to.throw(error('10'));
+        expect(throwable(0)).to.throw(error('0'));
+        expect(throwable(true)).to.throw(error('true'));
+        expect(throwable(false)).to.throw(error('false'));
+        expect(throwable({})).to.throw(error('Object'));
+        expect(throwable([])).to.throw(error('Array'));
+        expect(throwable(null)).to.throw(error('null'));
+        expect(throwable(undefined)).to.throw(error('undefined'));
+        throwable(() => undefined)();
       });
 
-      it('adds a Function to "postHandler" hooks', function () {
+      it('should add a Function to "postHandler" hooks', function () {
         const value = () => undefined;
         const route = new Route({
           method: HttpMethod.GET,
-          path: '/',
           handler: () => undefined,
           postHandler: value,
         });
@@ -323,11 +270,10 @@ describe('Route', function () {
           .to.be.true;
       });
 
-      it('adds a Function Array to "postHandler" hooks', function () {
+      it('should add a Function Array to "postHandler" hooks', function () {
         const value = [() => undefined, () => undefined];
         const route = new Route({
           method: HttpMethod.GET,
-          path: '/',
           handler: () => undefined,
           postHandler: value,
         });
@@ -339,13 +285,69 @@ describe('Route', function () {
         ).to.be.true;
       });
     });
+
+    describe('the "meta" option', function () {
+      it('should require the "meta" option to be a plain Object', function () {
+        const throwable = v => () =>
+          new Route({
+            method: HttpMethod.GET,
+            handler: () => undefined,
+            meta: v,
+          });
+        const error = v =>
+          format('Option "meta" must be an Object, but %s was given.', v);
+        expect(throwable('str')).to.throw(error('"str"'));
+        expect(throwable('')).to.throw(error('""'));
+        expect(throwable(10)).to.throw(error('10'));
+        expect(throwable(0)).to.throw(error('0'));
+        expect(throwable(true)).to.throw(error('true'));
+        expect(throwable(false)).to.throw(error('false'));
+        expect(throwable([])).to.throw(error('Array'));
+        expect(throwable(null)).to.throw(error('null'));
+        expect(throwable(() => undefined)).to.throw(error('Function'));
+        throwable({foo: 'bar'})();
+        throwable({})();
+        throwable(undefined)();
+      });
+
+      it('should set the "meta" option to the "meta" property as a deep copy', function () {
+        const metaData = {foo: {bar: {baz: 'qux'}}};
+        const route = new Route({
+          method: 'post',
+          handler: () => undefined,
+          meta: metaData,
+        });
+        expect(route.meta).to.be.not.eq(metaData);
+        expect(route.meta).to.be.eql(metaData);
+        expect(route.meta.foo).to.be.not.eq(metaData.foo);
+        expect(route.meta.foo).to.be.eql(metaData.foo);
+        expect(route.meta.foo.bar).to.be.not.eq(metaData.foo.bar);
+        expect(route.meta.foo.bar).to.be.eql(metaData.foo.bar);
+      });
+
+      it('should set an empty object to the "meta" property if the "meta" option is not provided', function () {
+        const route = new Route({
+          method: 'post',
+          handler: () => undefined,
+        });
+        expect(route.meta).to.be.eql({});
+      });
+
+      it('should set an empty object to the "meta" property if the "meta" option is undefined', function () {
+        const route = new Route({
+          method: 'post',
+          handler: () => undefined,
+          meta: undefined,
+        });
+        expect(route.meta).to.be.eql({});
+      });
+    });
   });
 
   describe('handle', function () {
-    it('invokes the handler with the given RequestContext and return its result', function () {
+    it('should invoke the handler with the given RequestContext and return its result', function () {
       const route = new Route({
         method: HttpMethod.GET,
-        path: '/',
         handler(ctx) {
           expect(ctx).to.be.instanceof(RequestContext);
           return 'OK';

+ 35 - 1
src/route/validate-route-definition.js

@@ -18,7 +18,7 @@ export function validateRouteDefinition(routeDef) {
       routeDef.method,
     );
   }
-  if (typeof routeDef.path !== 'string') {
+  if (routeDef.path !== undefined && typeof routeDef.path !== 'string') {
     throw new InvalidArgumentError(
       'Option "path" must be a String, but %v was given.',
       routeDef.path,
@@ -30,6 +30,40 @@ export function validateRouteDefinition(routeDef) {
       routeDef.handler,
     );
   }
+  if (routeDef.preHandler !== undefined) {
+    if (Array.isArray(routeDef.preHandler)) {
+      routeDef.preHandler.forEach(preHandler => {
+        if (typeof preHandler !== 'function') {
+          throw new InvalidArgumentError(
+            'Route pre-handler must be a Function, but %v was given.',
+            preHandler,
+          );
+        }
+      });
+    } else if (typeof routeDef.preHandler !== 'function') {
+      throw new InvalidArgumentError(
+        'Option "preHandler" must be a Function or an Array, but %v was given.',
+        routeDef.preHandler,
+      );
+    }
+  }
+  if (routeDef.postHandler !== undefined) {
+    if (Array.isArray(routeDef.postHandler)) {
+      routeDef.postHandler.forEach(postHandler => {
+        if (typeof postHandler !== 'function') {
+          throw new InvalidArgumentError(
+            'Route post-handler must be a Function, but %v was given.',
+            postHandler,
+          );
+        }
+      });
+    } else if (typeof routeDef.postHandler !== 'function') {
+      throw new InvalidArgumentError(
+        'Option "postHandler" must be a Function or an Array, but %v was given.',
+        routeDef.postHandler,
+      );
+    }
+  }
   if (routeDef.meta !== undefined) {
     if (
       !routeDef.meta ||

+ 118 - 4
src/route/validate-route-definition.spec.js

@@ -20,7 +20,6 @@ describe('validateRouteDefinition', function () {
     expect(throwable(() => undefined)).to.throw(error('Function'));
     throwable({
       method: HttpMethod.GET,
-      path: '/',
       handler: () => undefined,
     })();
   });
@@ -29,7 +28,6 @@ describe('validateRouteDefinition', function () {
     const throwable = v => () =>
       validateRouteDefinition({
         method: v,
-        path: '/',
         handler: () => undefined,
       });
     const error = v =>
@@ -66,17 +64,133 @@ describe('validateRouteDefinition', function () {
     expect(throwable(null)).to.throw(error('null'));
     expect(throwable({})).to.throw(error('Object'));
     expect(throwable([])).to.throw(error('Array'));
-    expect(throwable(undefined)).to.throw(error('undefined'));
     expect(throwable(() => undefined)).to.throw(error('Function'));
     throwable('str')();
     throwable('')();
+    throwable(undefined)();
+  });
+
+  it('should require the "handler" option to be a Function', function () {
+    const throwable = v => () =>
+      validateRouteDefinition({
+        method: HttpMethod.GET,
+        handler: v,
+      });
+    const error = v =>
+      format('Option "handler" must be a Function, but %s was given.', v);
+    expect(throwable('str')).to.throw(error('"str"'));
+    expect(throwable('')).to.throw(error('""'));
+    expect(throwable(10)).to.throw(error('10'));
+    expect(throwable(0)).to.throw(error('0'));
+    expect(throwable(true)).to.throw(error('true'));
+    expect(throwable(false)).to.throw(error('false'));
+    expect(throwable(null)).to.throw(error('null'));
+    expect(throwable({})).to.throw(error('Object'));
+    expect(throwable([])).to.throw(error('Array'));
+    expect(throwable(undefined)).to.throw(error('undefined'));
+    throwable(() => undefined)();
+  });
+
+  it('should require the "preHandler" option to be a Function or an Array of Function', function () {
+    const throwable = v => () =>
+      validateRouteDefinition({
+        method: HttpMethod.GET,
+        preHandler: v,
+        handler: () => undefined,
+      });
+    const error = v =>
+      format(
+        'Option "preHandler" must be a Function ' +
+          'or an Array, but %s was given.',
+        v,
+      );
+    expect(throwable('str')).to.throw(error('"str"'));
+    expect(throwable('')).to.throw(error('""'));
+    expect(throwable(10)).to.throw(error('10'));
+    expect(throwable(0)).to.throw(error('0'));
+    expect(throwable(true)).to.throw(error('true'));
+    expect(throwable(false)).to.throw(error('false'));
+    expect(throwable({})).to.throw(error('Object'));
+    expect(throwable(null)).to.throw(error('null'));
+    throwable([])();
+    throwable(() => undefined)();
+    throwable(undefined)();
+  });
+
+  it('should require an array of the "preHandler" option to contain a Function', function () {
+    const throwable = v => () =>
+      validateRouteDefinition({
+        method: HttpMethod.GET,
+        preHandler: [v],
+        handler: () => undefined,
+      });
+    const error = v =>
+      format('Route pre-handler must be a Function, but %s was given.', v);
+    expect(throwable('str')).to.throw(error('"str"'));
+    expect(throwable('')).to.throw(error('""'));
+    expect(throwable(10)).to.throw(error('10'));
+    expect(throwable(0)).to.throw(error('0'));
+    expect(throwable(true)).to.throw(error('true'));
+    expect(throwable(false)).to.throw(error('false'));
+    expect(throwable({})).to.throw(error('Object'));
+    expect(throwable([])).to.throw(error('Array'));
+    expect(throwable(null)).to.throw(error('null'));
+    expect(throwable(undefined)).to.throw(error('undefined'));
+    throwable(() => undefined)();
+  });
+
+  it('should require the "postHandler" option to be a Function or an Array of Function', function () {
+    const throwable = v => () =>
+      validateRouteDefinition({
+        method: HttpMethod.GET,
+        postHandler: v,
+        handler: () => undefined,
+      });
+    const error = v =>
+      format(
+        'Option "postHandler" must be a Function ' +
+          'or an Array, but %s was given.',
+        v,
+      );
+    expect(throwable('str')).to.throw(error('"str"'));
+    expect(throwable('')).to.throw(error('""'));
+    expect(throwable(10)).to.throw(error('10'));
+    expect(throwable(0)).to.throw(error('0'));
+    expect(throwable(true)).to.throw(error('true'));
+    expect(throwable(false)).to.throw(error('false'));
+    expect(throwable({})).to.throw(error('Object'));
+    expect(throwable(null)).to.throw(error('null'));
+    throwable([])();
+    throwable(() => undefined)();
+    throwable(undefined)();
+  });
+
+  it('should require an array of the "postHandler" option to contain a Function', function () {
+    const throwable = v => () =>
+      validateRouteDefinition({
+        method: HttpMethod.GET,
+        postHandler: [v],
+        handler: () => undefined,
+      });
+    const error = v =>
+      format('Route post-handler must be a Function, but %s was given.', v);
+    expect(throwable('str')).to.throw(error('"str"'));
+    expect(throwable('')).to.throw(error('""'));
+    expect(throwable(10)).to.throw(error('10'));
+    expect(throwable(0)).to.throw(error('0'));
+    expect(throwable(true)).to.throw(error('true'));
+    expect(throwable(false)).to.throw(error('false'));
+    expect(throwable({})).to.throw(error('Object'));
+    expect(throwable([])).to.throw(error('Array'));
+    expect(throwable(null)).to.throw(error('null'));
+    expect(throwable(undefined)).to.throw(error('undefined'));
+    throwable(() => undefined)();
   });
 
   it('should require the "meta" option to be a plain Object', function () {
     const throwable = v => () =>
       validateRouteDefinition({
         method: HttpMethod.GET,
-        path: 'path',
         handler: () => undefined,
         meta: v,
       });

+ 4 - 4
src/utils/parse-cookie-string.spec.js

@@ -3,7 +3,7 @@ import {format} from '@e22m4u/js-format';
 import {parseCookieString} from './parse-cookie-string.js';
 
 describe('parseCookieString', function () {
-  it('requires the first parameter to be an IncomingMessage instance', function () {
+  it('should require the first parameter to be an IncomingMessage instance', function () {
     const throwable = v => () => parseCookieString(v);
     const error = v =>
       format(
@@ -23,19 +23,19 @@ describe('parseCookieString', function () {
     throwable('')();
   });
 
-  it('returns cookies as a plain object', function () {
+  it('should return cookies as a plain object', function () {
     const value = 'pkg=math; equation=E%3Dmc%5E2';
     const result = parseCookieString(value);
     expect(result).to.have.property('pkg', 'math');
     expect(result).to.have.property('equation', 'E=mc^2');
   });
 
-  it('returns an empty object for an empty string', function () {
+  it('should return an empty object for an empty string', function () {
     const result = parseCookieString('');
     expect(result).to.be.eql({});
   });
 
-  it('parses an empty cookie as an empty string', function () {
+  it('should parse an empty cookie as an empty string', function () {
     const result = parseCookieString('foo=bar; baz');
     expect(result).to.be.eql({foo: 'bar', baz: ''});
   });