test(core): test cases for `urlParams.factory` and `router.helper`
ADD
packages/core/src/router/router.helper.spec.ts
+import { isRouteGroup, isRouteCombinerConfig } from './router.helpers';
+
+describe('Router helper', () => {
+
+ test('#isRouteGroup checks if provided argument is typeof RouteGroup', () => {
+ expect(isRouteGroup({ path: '/', effects: [], middlewares: [] })).toEqual(true);
+ expect(isRouteGroup({ path: '/', effects: [] })).toEqual(false);
+ expect(isRouteGroup({ path: '/', method: 'GET', effect: req$ => req$ })).toEqual(false);
+ });
+
+ test('#isRouteCombinerConfig checks if provided argument is typeof RouteCombinerConfig', () => {
+ expect(isRouteCombinerConfig({ effects: [], middlewares: [] })).toEqual(true);
+ expect(isRouteCombinerConfig([req$ => req$])).toEqual(false);
+ });
+
+});

MODIFY
packages/core/src/router/router.helpers.ts
packages/core/src/router/router.helpers.ts
-import { ParametricRegExp, RouteCombinerConfig, RouteGroup, Routing } from './router.interface';
+import { RouteCombinerConfig, RouteGroup, Routing } from './router.interface';
export { Routing };
@@ -9,23 +9,3 @@ export const isRouteGroup = (item): item is RouteGroup =>
export const isRouteCombinerConfig = (item): item is RouteCombinerConfig =>
Array.isArray(item.effects) &&
Array.isArray(item.middlewares);
-
-export const createRegExpWithParams = (path: string): ParametricRegExp => {
- const pathParameters = /:([^\/]+)/g;
- const parameters: string[] = [];
- let pathParameterMatch;
- while ((pathParameterMatch = pathParameters.exec(path)) !== null) {
- parameters.push(pathParameterMatch[1]);
- }
- const pattern = path
- .replace(/\/\//g, '/') /* Remove duplicate backslashes */
- .replace(/\\\\/g, '\\') /* Remove duplicate slashes */
- .replace(/([?./\\()[\]{}^$])/g, '\\$1') /* Escape all regex characters */
- .replace(pathParameters, `([^\\/]+)`) /* Translate all path parameters to regex groups */
- .replace(/\*/g, '[^\\/]*') /* Translate all stars to a wildcard */
- .replace(/([/\\])$/, '$1?'); /* Last slash/backslash is always optional */
- return {
- regExp: new RegExp('^' + pattern + '$'),
- parameters: parameters.length > 0 ? parameters : undefined,
- };
-};

MODIFY
packages/core/src/router/router.ts
packages/core/src/router/router.ts
import { EMPTY, Observable, of } from 'rxjs';
import { HttpMethod, HttpRequest, HttpResponse } from '../http.interface';
import { Effect, EffectResponse } from '../effects/effects.interface';
-import { createRegExpWithParams, isRouteCombinerConfig, isRouteGroup } from './router.helpers';
+import { isRouteCombinerConfig, isRouteGroup } from './router.helpers';
import {
RouteCombinerConfig,
RouteConfig,
@@ -14,6 +14,7 @@ import {
} from './router.interface';
import { queryParamsFactory } from '../router/queryParams.factory';
import { combineMiddlewareEffects } from '../effects/effects.combiner';
+import { factorizeRegExpWithParams } from './urlParams.factory';
export const combineRoutes = (
path: string,
@@ -38,7 +39,7 @@ export const routingFactory = (
[...middleware, ...route.middlewares]
));
} else {
- const { regExp, parameters } = createRegExpWithParams(parentPath + route.path);
+ const { regExp, parameters } = factorizeRegExpWithParams(parentPath + route.path);
const foundRoute = routing.find(route => route.regExp.source === regExp.source);
const method: RoutingMethod = {
effect: route.effect,

ADD
packages/core/src/router/urlParams.factory.spec.ts
+import { factorizeRegExpWithParams } from './urlParams.factory';
+
+describe('Url parameters factory', () => {
+
+ describe('#factorizeRegExpWithParams', () => {
+ test('factorizes path without parameters', () => {
+ // given
+ const path = '/foo/bar/baz';
+
+ // when
+ const { parameters } = factorizeRegExpWithParams(path);
+
+ // then
+ expect(parameters).toBeUndefined();
+ });
+
+ test('factorizes path with parameters', () => {
+ // given
+ const path = '/foo/:param1/bar/:param2/baz/';
+
+ // when
+ const { regExp, parameters } = factorizeRegExpWithParams(path);
+ const match = '/foo/test1/bar/test2/baz'.match(regExp);
+
+ // then
+ expect(parameters).toEqual([ 'param1', 'param2' ]);
+ expect(match[1]).toEqual('test1');
+ expect(match[2]).toEqual('test2');
+ });
+
+ test('factorizes path with connected parameters as a one', () => {
+ // given
+ const wrongPath = '/foo/:param1:param2/bar';
+ const correctPath = '/foo/:param1/:param2/bar';
+
+ // when
+ const wrongRegexp = factorizeRegExpWithParams(wrongPath);
+ const correctRegexp = factorizeRegExpWithParams(correctPath);
+
+ // then
+ expect(wrongRegexp.parameters).toEqual(['param1:param2']);
+ expect(correctRegexp.parameters).toEqual(['param1', 'param2']);
+ });
+
+ test('factorizes path with parameter and wildcard', () => {
+ // given
+ const path = '/foo/:param1/*/bar';
+
+ // when
+ const { parameters, regExp } = factorizeRegExpWithParams(path);
+
+ // then
+ expect(parameters).toEqual(['param1']);
+ expect(regExp.test('/foo/param/test1/test2/bar')).toEqual(true);
+ });
+
+ test('escapes RegExp backslashes and removes duplicates', () => {
+ // given
+ const path = '/foo/bar//baz';
+
+ // when
+ const { regExp } = factorizeRegExpWithParams(path);
+
+ // then
+ expect(regExp).toEqual(/^\/foo\/bar\/baz$/);
+ expect(regExp.test('/foo/bar/baz')).toEqual(true);
+ });
+
+ test('translates path parameters to RegExp groups', () => {
+ // given
+ const path = '/foo/:param1/bar';
+
+ // when
+ const { regExp } = factorizeRegExpWithParams(path);
+
+ // then
+ expect(regExp).toEqual(/^\/foo\/([^\/]+)\/bar$/);
+ expect(regExp.test('/foo/test/bar')).toEqual(true);
+ });
+
+ test('matches RegExp wildcard placed inside the path', () => {
+ // given
+ const path = '/foo/*/bar';
+
+ // when
+ const { regExp } = factorizeRegExpWithParams(path);
+
+ // then
+ expect(regExp).toEqual(/^\/foo\/.*?\/bar$/);
+ expect(regExp.test('/foo/test/bar')).toEqual(true);
+ });
+
+ test('matched RegExpt wildcard at the end of the path', () => {
+ // given
+ const path = '/foo/*';
+
+ // when
+ const { regExp } = factorizeRegExpWithParams(path);
+
+ // then
+ expect(regExp).toEqual(/^\/foo\/.*?$/);
+ expect(regExp.test('/foo/test1/test2')).toEqual(true);
+ expect(regExp.test('/foo/test1')).toEqual(true);
+ expect(regExp.test('/foo/')).toEqual(true);
+ });
+ });
+
+});

ADD
packages/core/src/router/urlParams.factory.ts
+import { ParametricRegExp } from './router.interface';
+
+export const factorizeRegExpWithParams = (path: string): ParametricRegExp => {
+ const pathParameters = /:([^\/]+)/g;
+ const parameters: string[] = [];
+
+ let pathParameterMatch;
+ while ((pathParameterMatch = pathParameters.exec(path)) !== null) {
+ parameters.push(pathParameterMatch[1]);
+ }
+
+ const pattern = path
+ .replace(/\/\//g, '/') /* Remove duplicate backslashes */
+ .replace(/\\\\/g, '\\') /* Remove duplicate slashes */
+ .replace(/([?./\\()[\]{}^$])/g, '\\$1') /* Escape all regex characters */
+ .replace(pathParameters, `([^\\/]+)`) /* Translate all path parameters to regex groups */
+ .replace(/\*/g, '.*?') /* Translate all stars to a wildcard */
+ .replace(/([/\\])$/, '$1?'); /* Last slash/backslash is always optional */
+
+ return {
+ regExp: new RegExp('^' + pattern + '$'),
+ parameters: parameters.length > 0 ? parameters : undefined,
+ };
+};

test(core): test cases for `urlParams.factory` and `router.helper`