diff --git a/package.json b/package.json index 6f6be21..4ff431c 100644 --- a/package.json +++ b/package.json @@ -47,6 +47,7 @@ "ghooks": "^1.2.1", "mkdirp": "^0.5.1", "mocha": "^3.4.2", + "prettier": "^1.5.3", "rxjs": "^5.4.0", "switch-path": "^1.2.0", "testem": "^1.16.1", @@ -58,10 +59,12 @@ }, "config": { "ghooks": { - "commit-msg": "node ./node_modules/.bin/validate-commit-msg" + "commit-msg": "node ./node_modules/.bin/validate-commit-msg", + "pre-commit": "npm run format" } }, "scripts": { + "format": "prettier --tab-width 4 --single-quote --write '{src,test}/**/*.{js,ts,tsx}'", "lint": "tslint -c tslint.json src/*.ts src/**/*.ts", "test-node": "mocha -r babel-register test/index.js", "test-browser": "testem", diff --git a/src/RouterSource.ts b/src/RouterSource.ts index f911d4b..8fa7581 100644 --- a/src/RouterSource.ts +++ b/src/RouterSource.ts @@ -1,59 +1,80 @@ -import {Location, Pathname} from '@cycle/history'; -import {LocationDescriptorObject} from 'history'; -import {RouteDefinitionsMap, RouteDefinitionsArray, RouteMatcher} from './interfaces'; +import { Location, Pathname } from '@cycle/history'; +import { LocationDescriptorObject } from 'history'; +import { + RouteDefinitionsMap, + RouteDefinitionsArray, + RouteMatcher +} from './interfaces'; import * as util from './util'; -import {adapt} from '@cycle/run/lib/adapt'; +import { adapt } from '@cycle/run/lib/adapt'; function isStrictlyInScope(namespace: Pathname[], path: Pathname): boolean { - const pathParts = util.splitPath(path); - return namespace.every((v, i) => { - return pathParts[i] === v; - }); + const pathParts = util.splitPath(path); + return namespace.every((v, i) => { + return pathParts[i] === v; + }); } function getFilteredPath(namespace: Pathname[], path: Pathname): Pathname { - const pathParts = util.splitPath(path); - return '/' + util.filterPath(pathParts, namespace); + const pathParts = util.splitPath(path); + return '/' + util.filterPath(pathParts, namespace); } export class RouterSource { - constructor(private _history$: any, - private _namespace: Pathname[], - private _createHref: (path: LocationDescriptorObject) => Pathname, - private _routeMatcher: RouteMatcher) {} - - history$ = adapt(this._history$); - - path(pathname: Pathname): RouterSource { - const scopedNamespace = this._namespace.concat(util.splitPath(pathname)); - const scopedHistory$ = this._history$ - .filter(({pathname: _path}: Location) => isStrictlyInScope(scopedNamespace, _path)) - .remember(); - - const createHref = this._createHref; - return new RouterSource(scopedHistory$, scopedNamespace, createHref, this._routeMatcher); - } - - define(routes: RouteDefinitionsMap | RouteDefinitionsArray, routeMatcher?: RouteMatcher): any { - const namespace = this._namespace; - const _createHref = this._createHref; - const createHref = util.makeCreateHref(namespace, _createHref); - - let match$ = this._history$ - .map((location: Location) => { - const matcher = routeMatcher || this._routeMatcher; - const filteredPath = getFilteredPath(namespace, location.pathname); - const {path, value} = matcher(filteredPath, routes); - return {path, value, location, createHref}; - }) - .remember(); - - const out$ = adapt(match$); - out$.createHref = createHref; - return out$; - } - - createHref(path: Pathname): Pathname { - return util.makeCreateHref(this._namespace, this._createHref)(path); - } + constructor( + private _history$: any, + private _namespace: Pathname[], + private _createHref: (path: LocationDescriptorObject) => Pathname, + private _routeMatcher: RouteMatcher + ) {} + + history$ = adapt(this._history$); + + path(pathname: Pathname): RouterSource { + const scopedNamespace = this._namespace.concat( + util.splitPath(pathname) + ); + const scopedHistory$ = this._history$ + .filter(({ pathname: _path }: Location) => + isStrictlyInScope(scopedNamespace, _path) + ) + .remember(); + + const createHref = this._createHref; + return new RouterSource( + scopedHistory$, + scopedNamespace, + createHref, + this._routeMatcher + ); + } + + define( + routes: RouteDefinitionsMap | RouteDefinitionsArray, + routeMatcher?: RouteMatcher + ): any { + const namespace = this._namespace; + const _createHref = this._createHref; + const createHref = util.makeCreateHref(namespace, _createHref); + + let match$ = this._history$ + .map((location: Location) => { + const matcher = routeMatcher || this._routeMatcher; + const filteredPath = getFilteredPath( + namespace, + location.pathname + ); + const { path, value } = matcher(filteredPath, routes); + return { path, value, location, createHref }; + }) + .remember(); + + const out$ = adapt(match$); + out$.createHref = createHref; + return out$; + } + + createHref(path: Pathname): Pathname { + return util.makeCreateHref(this._namespace, this._createHref)(path); + } } diff --git a/src/index.ts b/src/index.ts index 894d4bb..82a598c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,3 +1,3 @@ export * from './routerify'; -export {RouterSource} from './RouterSource'; +export { RouterSource } from './RouterSource'; export * from './interfaces'; diff --git a/src/interfaces.ts b/src/interfaces.ts index ff90bf4..3c7ae27 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -1,16 +1,19 @@ export interface RouteDefinitionsMap { - [sourcePath: string]: any; + [sourcePath: string]: any; } export interface RouteDefinitionsArray { - [sourceIndex: number]: any; + [sourceIndex: number]: any; } export interface RouteMatcherReturn { - path: string; - value: any; + path: string; + value: any; } export interface RouteMatcher { - (path: string, routes: RouteDefinitionsMap | RouteDefinitionsArray): RouteMatcherReturn; + ( + path: string, + routes: RouteDefinitionsMap | RouteDefinitionsArray + ): RouteMatcherReturn; } diff --git a/src/routerify.ts b/src/routerify.ts index 77e7224..2cab48c 100644 --- a/src/routerify.ts +++ b/src/routerify.ts @@ -1,19 +1,19 @@ import xs from 'xstream'; -import {adapt} from '@cycle/run/lib/adapt'; -import {RouteMatcher} from './interfaces'; -import {RouterSource} from './RouterSource'; -import {Location, createPath} from 'history'; -import {HistoryInput, GenericInput} from '@cycle/history'; -import {Stream} from 'xstream'; +import { adapt } from '@cycle/run/lib/adapt'; +import { RouteMatcher } from './interfaces'; +import { RouterSource } from './RouterSource'; +import { Location, createPath } from 'history'; +import { HistoryInput, GenericInput } from '@cycle/history'; +import { Stream } from 'xstream'; export declare type HistoryAction = HistoryInput | GenericInput | string; export declare type RouterSink = Stream; export interface RouterOptions { - basename?: string; - historyName?: string; - routerName?: string; - omitHistory?: boolean; + basename?: string; + historyName?: string; + routerName?: string; + omitHistory?: boolean; } /** @@ -23,50 +23,52 @@ export interface RouterOptions { * @return {main} The augmented main function */ function routerify( - main: (a: any) => any, - routeMatcher: RouteMatcher, - options?: RouterOptions + main: (a: any) => any, + routeMatcher: RouteMatcher, + options?: RouterOptions ) { - if (typeof main !== 'function') { - throw new Error('First argument to routerify must be a valid cycle app'); - } - const opts: RouterOptions = { - basename: '/', - historyName: 'history', - routerName: 'router', - omitHistory: true, - ...options - }; - const createHref = (location: Location) => - opts.basename + createPath(location); - return function(sources: any): any { - const routerSource = new RouterSource( - xs.from(sources[opts.historyName]), - [], - createHref, - routeMatcher - ); - const sinks = main({ - ...sources, - [opts.routerName]: routerSource, - [opts.historyName]: opts.omitHistory - ? undefined - : sources[opts.historyName] - }); - return { - ...sinks, - [opts.historyName]: adapt( - xs.merge( - sinks[opts.historyName] && !opts.omitHistory - ? xs.fromObservable(sinks[opts.historyName]) - : xs.never(), - sinks[opts.routerName] - ? xs.fromObservable(sinks[opts.routerName]) - : xs.never() - ) - ) + if (typeof main !== 'function') { + throw new Error( + 'First argument to routerify must be a valid cycle app' + ); + } + const opts: RouterOptions = { + basename: '/', + historyName: 'history', + routerName: 'router', + omitHistory: true, + ...options + }; + const createHref = (location: Location) => + opts.basename + createPath(location); + return function(sources: any): any { + const routerSource = new RouterSource( + xs.from(sources[opts.historyName]), + [], + createHref, + routeMatcher + ); + const sinks = main({ + ...sources, + [opts.routerName]: routerSource, + [opts.historyName]: opts.omitHistory + ? undefined + : sources[opts.historyName] + }); + return { + ...sinks, + [opts.historyName]: adapt( + xs.merge( + sinks[opts.historyName] && !opts.omitHistory + ? xs.fromObservable(sinks[opts.historyName]) + : xs.never(), + sinks[opts.routerName] + ? xs.fromObservable(sinks[opts.routerName]) + : xs.never() + ) + ) + }; }; - }; } -export {routerify}; +export { routerify }; diff --git a/src/switch-path.d.ts b/src/switch-path.d.ts index 44a8148..3a616fb 100644 --- a/src/switch-path.d.ts +++ b/src/switch-path.d.ts @@ -1,3 +1,3 @@ declare module 'switch-path' { - export default function switchPath(sourcePath: string, routes: {}): {}; + export default function switchPath(sourcePath: string, routes: {}): {}; } diff --git a/src/util.ts b/src/util.ts index 5a1c306..c7c1615 100644 --- a/src/util.ts +++ b/src/util.ts @@ -1,21 +1,24 @@ -import {Pathname} from '@cycle/history'; -import {LocationDescriptorObject} from 'history'; +import { Pathname } from '@cycle/history'; +import { LocationDescriptorObject } from 'history'; function splitPath(path: Pathname): Pathname[] { - return path.split('/').filter(p => p.length > 0); + return path.split('/').filter(p => p.length > 0); } function filterPath(pathParts: Pathname[], namespace: Pathname[]): Pathname { - return pathParts.filter(part => namespace.indexOf(part) < 0).join('/'); + return pathParts.filter(part => namespace.indexOf(part) < 0).join('/'); } const startsWith = (param: string, value: string) => param[0] === value; const startsWith2 = (param: string, value1: string, value2: string) => - param[0] === value1 && param[1] === value2; + param[0] === value1 && param[1] === value2; -function makeCreateHref(namespace: Pathname[], _createHref: (pathname: LocationDescriptorObject) => Pathname) { - /** +function makeCreateHref( + namespace: Pathname[], + _createHref: (pathname: LocationDescriptorObject) => Pathname +) { + /** * Function used to create HREFs that are properly namespaced * @typedef {createHref} * @name createHref @@ -25,33 +28,31 @@ function makeCreateHref(namespace: Pathname[], _createHref: (pathname: LocationD * @return {string} a fully qualified HREF composed from the current * namespace and the path provided */ - return function createHref(location: Pathname | LocationDescriptorObject): Pathname { - if (typeof location === 'object' && location !== null) { - const fullPath = `${namespace.join('/')}${location.pathname}`; - return startsWith(fullPath, '/') || startsWith2(fullPath, '#', '/') - ? _createHref({ - ...location, - pathname: fullPath, - }) - : _createHref({ - ...location, - pathname: '/' + fullPath - }); - } else if (typeof location === 'string') { - const fullPath = `${namespace.join('/')}${location}`; - return startsWith(fullPath, '/') || startsWith2(fullPath, '#', '/') - ? _createHref({ - pathname: fullPath - }) - : _createHref({ - pathname: '/' + fullPath - }); - } - }; + return function createHref( + location: Pathname | LocationDescriptorObject + ): Pathname { + if (typeof location === 'object' && location !== null) { + const fullPath = `${namespace.join('/')}${location.pathname}`; + return startsWith(fullPath, '/') || startsWith2(fullPath, '#', '/') + ? _createHref({ + ...location, + pathname: fullPath + }) + : _createHref({ + ...location, + pathname: '/' + fullPath + }); + } else if (typeof location === 'string') { + const fullPath = `${namespace.join('/')}${location}`; + return startsWith(fullPath, '/') || startsWith2(fullPath, '#', '/') + ? _createHref({ + pathname: fullPath + }) + : _createHref({ + pathname: '/' + fullPath + }); + } + }; } -export { - splitPath, - filterPath, - makeCreateHref, -} +export { splitPath, filterPath, makeCreateHref }; diff --git a/test/rxjs.js b/test/rxjs.js index c08ee01..a3af087 100644 --- a/test/rxjs.js +++ b/test/rxjs.js @@ -1,291 +1,338 @@ /* eslint max-nested-callbacks: 0 */ /*global describe, it */ -import assert from 'assert' -import {setAdapt, adapt} from '@cycle/run/lib/adapt' -import {Observable} from 'rxjs' -import {routerify} from '../lib' -import {makeServerHistoryDriver} from '@cycle/history' -import switchPath from 'switch-path' +import assert from 'assert'; +import { setAdapt, adapt } from '@cycle/run/lib/adapt'; +import { Observable } from 'rxjs'; +import { routerify } from '../lib'; +import { makeServerHistoryDriver } from '@cycle/history'; +import switchPath from 'switch-path'; describe('Cyclic Router - RxJS 5', () => { - before(() => setAdapt(stream => Observable.from(stream))) - describe('routerify', () => { - it('should throw if not given a main function', () => { - assert.throws( - () => { - makeRouterDriver(null) - }, - Error, - /First argument to routerify must be a valid cycle app/i - ) - }) + before(() => setAdapt(stream => Observable.from(stream))); + describe('routerify', () => { + it('should throw if not given a main function', () => { + assert.throws( + () => { + makeRouterDriver(null); + }, + Error, + /First argument to routerify must be a valid cycle app/i + ); + }); - it('should return a function returning sinks', () => { - const app = () => ({router: Observable.never()}) - const augmentedApp = routerify(app, switchPath) - const sinks = augmentedApp({history: Observable.never()}) - assert.notStrictEqual(augmentedApp, null) - assert.strictEqual(typeof augmentedApp, 'function') - assert.notStrictEqual(sinks, null) - assert.strictEqual(typeof sinks, 'object') - assert.notStrictEqual(sinks.history, null) - assert.strictEqual(typeof sinks.history, 'object') - assert.strictEqual(typeof sinks.history.subscribe, 'function') - }) - }) + it('should return a function returning sinks', () => { + const app = () => ({ router: Observable.never() }); + const augmentedApp = routerify(app, switchPath); + const sinks = augmentedApp({ history: Observable.never() }); + assert.notStrictEqual(augmentedApp, null); + assert.strictEqual(typeof augmentedApp, 'function'); + assert.notStrictEqual(sinks, null); + assert.strictEqual(typeof sinks, 'object'); + assert.notStrictEqual(sinks.history, null); + assert.strictEqual(typeof sinks.history, 'object'); + assert.strictEqual(typeof sinks.history.subscribe, 'function'); + }); + }); - describe('path()', () => { - it( - 'should return an object with `path` `define` `observable` ' + - '`createHref` and `dispose`', - () => { - const app = ({router}) => { - assert.notStrictEqual(router.path, null) - assert.strictEqual(typeof router.path, 'function') - assert.notStrictEqual(router.define, null) - assert.strictEqual(typeof router.define, 'function') - assert.notStrictEqual(router.history$, null) - assert.strictEqual(typeof router.history$, 'object') - assert.strictEqual(typeof router.history$.subscribe, 'function') - assert.notStrictEqual(router.createHref, null) - assert.strictEqual(typeof router.createHref, 'function') - return {} - } - routerify(app, switchPath)({history: Observable.never()}) - } - ) + describe('path()', () => { + it( + 'should return an object with `path` `define` `observable` ' + + '`createHref` and `dispose`', + () => { + const app = ({ router }) => { + assert.notStrictEqual(router.path, null); + assert.strictEqual(typeof router.path, 'function'); + assert.notStrictEqual(router.define, null); + assert.strictEqual(typeof router.define, 'function'); + assert.notStrictEqual(router.history$, null); + assert.strictEqual(typeof router.history$, 'object'); + assert.strictEqual( + typeof router.history$.subscribe, + 'function' + ); + assert.notStrictEqual(router.createHref, null); + assert.strictEqual(typeof router.createHref, 'function'); + return {}; + }; + routerify(app, switchPath)({ history: Observable.never() }); + } + ); - it('should filter the history$', () => { - const routes = ['/somewhere/else', '/path/that/is/correct'] + it('should filter the history$', () => { + const routes = ['/somewhere/else', '/path/that/is/correct']; - const app = sources => { - sources.router.path('/path').history$.subscribe(location => { - assert.notStrictEqual(location.pathname, '/somewhere/else') - assert.strictEqual(location.pathname, '/path/that/is/correct') - }) - return {} - } - routerify(app, switchPath)({ - history: adapt(makeServerHistoryDriver()(Observable.from(routes))) - }) - }) + const app = sources => { + sources.router.path('/path').history$.subscribe(location => { + assert.notStrictEqual(location.pathname, '/somewhere/else'); + assert.strictEqual( + location.pathname, + '/path/that/is/correct' + ); + }); + return {}; + }; + routerify(app, switchPath)({ + history: adapt( + makeServerHistoryDriver()(Observable.from(routes)) + ) + }); + }); - it('multiple path()s should filter the history$', () => { - const routes = [ - '/the/wrong/path', - '/some/really/really/deeply/nested/route/that/is/correct', - '/some/really/really/deeply/nested/incorrect/route' - ] + it('multiple path()s should filter the history$', () => { + const routes = [ + '/the/wrong/path', + '/some/really/really/deeply/nested/route/that/is/correct', + '/some/really/really/deeply/nested/incorrect/route' + ]; - const app = sources => { - sources.router - .path('/some') - .path('/really') - .path('/really') - .path('/deeply') - .path('/nested') - .path('/route') - .path('/that') - .history$.subscribe(({pathname}) => { - assert.strictEqual( - pathname, - '/some/really/really/deeply/nested/route/that/is/correct' - ) - }) - return {} - } - routerify(app, switchPath)({ - history: adapt(makeServerHistoryDriver()(Observable.from(routes))) - }) - }) + const app = sources => { + sources.router + .path('/some') + .path('/really') + .path('/really') + .path('/deeply') + .path('/nested') + .path('/route') + .path('/that') + .history$.subscribe(({ pathname }) => { + assert.strictEqual( + pathname, + '/some/really/really/deeply/nested/route/that/is/correct' + ); + }); + return {}; + }; + routerify(app, switchPath)({ + history: adapt( + makeServerHistoryDriver()(Observable.from(routes)) + ) + }); + }); - it('should create a proper path using createHref()', () => { - const routes = [ - '/the/wrong/path', - '/some/really/really/deeply/nested/route/that/is/correct', - '/some/really/really/deeply/nested/incorrect/route' - ] + it('should create a proper path using createHref()', () => { + const routes = [ + '/the/wrong/path', + '/some/really/really/deeply/nested/route/that/is/correct', + '/some/really/really/deeply/nested/incorrect/route' + ]; - const app = sources => { - sources.router - .path('/some') - .path('/really') - .path('/really') - .path('/deeply') - .path('/nested') - .path('/route') - .path('/that') - .history$.subscribe(({pathname}) => { - assert.strictEqual( - pathname, - '/some/really/really/deeply/nested/route/that/is/correct' - ) - assert.strictEqual( - router.createHref('/is/correct'), - '/some/really/really/deeply/nested/route/that/is/correct' - ) - }) - return {} - } - routerify(app, switchPath)({ - history: adapt(makeServerHistoryDriver()(Observable.from(routes))) - }) - }) - }) + const app = sources => { + sources.router + .path('/some') + .path('/really') + .path('/really') + .path('/deeply') + .path('/nested') + .path('/route') + .path('/that') + .history$.subscribe(({ pathname }) => { + assert.strictEqual( + pathname, + '/some/really/really/deeply/nested/route/that/is/correct' + ); + assert.strictEqual( + router.createHref('/is/correct'), + '/some/really/really/deeply/nested/route/that/is/correct' + ); + }); + return {}; + }; + routerify(app, switchPath)({ + history: adapt( + makeServerHistoryDriver()(Observable.from(routes)) + ) + }); + }); + }); - describe('define()', () => { - it( - 'should return an object with `path$` `value$` `fullPath$` ' + - '`createHref` and `dispose`', - () => { - const app = sources => { - const router = sources.router.define({}) - assert.strictEqual(router instanceof Observable, true) - assert.strictEqual(typeof router.subscribe, 'function') - assert.notStrictEqual(router.createHref, null) - assert.strictEqual(typeof router.createHref, 'function') - return {} - } - routerify(app, switchPath)({history: Observable.never()}) - } - ) + describe('define()', () => { + it( + 'should return an object with `path$` `value$` `fullPath$` ' + + '`createHref` and `dispose`', + () => { + const app = sources => { + const router = sources.router.define({}); + assert.strictEqual(router instanceof Observable, true); + assert.strictEqual(typeof router.subscribe, 'function'); + assert.notStrictEqual(router.createHref, null); + assert.strictEqual(typeof router.createHref, 'function'); + return {}; + }; + routerify(app, switchPath)({ history: Observable.never() }); + } + ); - it('should match routes against a definition object', done => { - const defintion = { - '/some': { - '/route': 123 - } - } + it('should match routes against a definition object', done => { + const defintion = { + '/some': { + '/route': 123 + } + }; - const app = ({router}) => { - const match$ = router.define(defintion) - match$.subscribe(({path, value, location}) => { - assert.strictEqual(path, '/some/route') - assert.strictEqual(value, 123) - assert.strictEqual(location.pathname, '/some/route') - done() - }) - return {} - } - routerify(app, switchPath)({ - history: adapt(makeServerHistoryDriver()(Observable.of('/some/route'))) - }) - }) + const app = ({ router }) => { + const match$ = router.define(defintion); + match$.subscribe(({ path, value, location }) => { + assert.strictEqual(path, '/some/route'); + assert.strictEqual(value, 123); + assert.strictEqual(location.pathname, '/some/route'); + done(); + }); + return {}; + }; + routerify(app, switchPath)({ + history: adapt( + makeServerHistoryDriver()(Observable.of('/some/route')) + ) + }); + }); - it('should respect prior filtering by path()', done => { - const defintion = { - '/correct': { - '/route': 123 - } - } + it('should respect prior filtering by path()', done => { + const defintion = { + '/correct': { + '/route': 123 + } + }; - const routes = ['/some/nested/correct/route'] + const routes = ['/some/nested/correct/route']; - const app = ({router}) => { - const match$ = router.path('/some').path('/nested').define(defintion) - match$.subscribe(({path, value, location}) => { - assert.strictEqual(path, '/correct/route') - assert.strictEqual(value, 123) - assert.strictEqual(location.pathname, '/some/nested/correct/route') - done() - }) - return {} - } - routerify(app, switchPath)({ - history: adapt( - makeServerHistoryDriver()( - Observable.of('/wrong/path', '/some/nested/correct/route').delay(0) - ) - ) - }) - }) + const app = ({ router }) => { + const match$ = router + .path('/some') + .path('/nested') + .define(defintion); + match$.subscribe(({ path, value, location }) => { + assert.strictEqual(path, '/correct/route'); + assert.strictEqual(value, 123); + assert.strictEqual( + location.pathname, + '/some/nested/correct/route' + ); + done(); + }); + return {}; + }; + routerify(app, switchPath)({ + history: adapt( + makeServerHistoryDriver()( + Observable.of( + '/wrong/path', + '/some/nested/correct/route' + ).delay(0) + ) + ) + }); + }); - it('should match a default route if one is not found', done => { - const definition = { - '/correct': { - '/route': 123 - }, - '*': 999 - } + it('should match a default route if one is not found', done => { + const definition = { + '/correct': { + '/route': 123 + }, + '*': 999 + }; - const app = ({router}) => { - const match$ = router.path('/some').path('/nested').define(definition) - match$.subscribe(({path, value, location}) => { - assert.strictEqual(path, '/incorrect/route') - assert.strictEqual(value, 999) - assert.strictEqual(location.pathname, '/some/nested/incorrect/route') - done() - }) - return {router: Observable.of('/wrong/path')} - } - routerify(app, switchPath)({ - history: adapt( - makeServerHistoryDriver()( - Observable.of('/wrong/route', '/some/nested/incorrect/route').delay( - 0 - ) - ) - ) - }) - }) + const app = ({ router }) => { + const match$ = router + .path('/some') + .path('/nested') + .define(definition); + match$.subscribe(({ path, value, location }) => { + assert.strictEqual(path, '/incorrect/route'); + assert.strictEqual(value, 999); + assert.strictEqual( + location.pathname, + '/some/nested/incorrect/route' + ); + done(); + }); + return { router: Observable.of('/wrong/path') }; + }; + routerify(app, switchPath)({ + history: adapt( + makeServerHistoryDriver()( + Observable.of( + '/wrong/route', + '/some/nested/incorrect/route' + ).delay(0) + ) + ) + }); + }); - it('should create a proper href using createHref()', done => { - const definition = { - '/correct': { - '/route': 123 - }, - '*': 999 - } + it('should create a proper href using createHref()', done => { + const definition = { + '/correct': { + '/route': 123 + }, + '*': 999 + }; - const app = ({router}) => { - const match$ = router.path('/some').path('/nested').define(definition) + const app = ({ router }) => { + const match$ = router + .path('/some') + .path('/nested') + .define(definition); - assert(match$.createHref('/hello'), '/some/nested/hello') - match$.subscribe(({path, value, location, createHref}) => { - assert.strictEqual(path, '/incorrect/route') - assert.strictEqual(value, 999) - assert.strictEqual(location.pathname, '/some/nested/incorrect/route') - //assert.strictEqual(location.pathname, createHref('/incorrect/route')) - done() - }) - return {router: Observable.of('/wrong/path')} - } - routerify(app, switchPath)({ - history: adapt( - makeServerHistoryDriver()( - Observable.of('/wrong/route', '/some/nested/incorrect/route').delay( - 0 - ) - ) - ) - }) - }) + assert(match$.createHref('/hello'), '/some/nested/hello'); + match$.subscribe(({ path, value, location, createHref }) => { + assert.strictEqual(path, '/incorrect/route'); + assert.strictEqual(value, 999); + assert.strictEqual( + location.pathname, + '/some/nested/incorrect/route' + ); + //assert.strictEqual(location.pathname, createHref('/incorrect/route')) + done(); + }); + return { router: Observable.of('/wrong/path') }; + }; + routerify(app, switchPath)({ + history: adapt( + makeServerHistoryDriver()( + Observable.of( + '/wrong/route', + '/some/nested/incorrect/route' + ).delay(0) + ) + ) + }); + }); - it('should match partials', done => { - const defintion = { - '/correct': { - '/route': 123 - }, - '*': 999 - } + it('should match partials', done => { + const defintion = { + '/correct': { + '/route': 123 + }, + '*': 999 + }; - const app = ({router}) => { - const match$ = router.path('/some').path('/nested').define(defintion) - match$.subscribe(({path, location: {pathname}, createHref}) => { - assert.strictEqual(path, '/correct/route') - assert.strictEqual(pathname, '/some/nested/correct/route/partial') - done() - }) + const app = ({ router }) => { + const match$ = router + .path('/some') + .path('/nested') + .define(defintion); + match$.subscribe( + ({ path, location: { pathname }, createHref }) => { + assert.strictEqual(path, '/correct/route'); + assert.strictEqual( + pathname, + '/some/nested/correct/route/partial' + ); + done(); + } + ); - return {router: Observable.of('/wrong/path')} - } - routerify(app, switchPath)({ - history: adapt( - makeServerHistoryDriver()( - Observable.of('/some/nested/correct/route/partial').delay(0) - ) - ) - }) - }) - }) -}) + return { router: Observable.of('/wrong/path') }; + }; + routerify(app, switchPath)({ + history: adapt( + makeServerHistoryDriver()( + Observable.of( + '/some/nested/correct/route/partial' + ).delay(0) + ) + ) + }); + }); + }); +}); diff --git a/test/xstream.js b/test/xstream.js index 5db52b0..150c88d 100644 --- a/test/xstream.js +++ b/test/xstream.js @@ -1,322 +1,353 @@ /* eslint max-nested-callbacks: 0 */ /*global describe, it */ -import assert from 'assert' -import {setAdapt} from '@cycle/run/lib/adapt' -import xs from 'xstream' -import delay from 'xstream/extra/delay' -import {routerify} from '../lib' -import {makeServerHistoryDriver} from '@cycle/history' -import switchPath from 'switch-path' +import assert from 'assert'; +import { setAdapt } from '@cycle/run/lib/adapt'; +import xs from 'xstream'; +import delay from 'xstream/extra/delay'; +import { routerify } from '../lib'; +import { makeServerHistoryDriver } from '@cycle/history'; +import switchPath from 'switch-path'; describe('Cyclic Router - XStream', () => { - before(() => setAdapt(stream => stream)) - describe('routerify', () => { - it('should throw if not given a main function', () => { - assert.throws( - () => { - makeRouterDriver(null) - }, - Error, - /First argument to routerify must be a valid cycle app/i - ) - }) + before(() => setAdapt(stream => stream)); + describe('routerify', () => { + it('should throw if not given a main function', () => { + assert.throws( + () => { + makeRouterDriver(null); + }, + Error, + /First argument to routerify must be a valid cycle app/i + ); + }); - it('should return a function returning sinks', () => { - const app = () => ({router: xs.never()}) - const augmentedApp = routerify(app, switchPath) - const sinks = augmentedApp({history: xs.never()}) - assert.notStrictEqual(augmentedApp, null) - assert.strictEqual(typeof augmentedApp, 'function') - assert.notStrictEqual(sinks, null) - assert.strictEqual(typeof sinks, 'object') - assert.notStrictEqual(sinks.history, null) - assert.strictEqual(typeof sinks.history, 'object') - assert.strictEqual(typeof sinks.history.addListener, 'function') - }) - }) + it('should return a function returning sinks', () => { + const app = () => ({ router: xs.never() }); + const augmentedApp = routerify(app, switchPath); + const sinks = augmentedApp({ history: xs.never() }); + assert.notStrictEqual(augmentedApp, null); + assert.strictEqual(typeof augmentedApp, 'function'); + assert.notStrictEqual(sinks, null); + assert.strictEqual(typeof sinks, 'object'); + assert.notStrictEqual(sinks.history, null); + assert.strictEqual(typeof sinks.history, 'object'); + assert.strictEqual(typeof sinks.history.addListener, 'function'); + }); + }); - describe('path()', () => { - it( - 'should return an object with `path` `define` `observable` ' + - '`createHref` and `dispose`', - () => { - const app = ({router}) => { - assert.notStrictEqual(router.path, null) - assert.strictEqual(typeof router.path, 'function') - assert.notStrictEqual(router.define, null) - assert.strictEqual(typeof router.define, 'function') - assert.notStrictEqual(router.history$, null) - assert.strictEqual(typeof router.history$, 'object') - assert.strictEqual(typeof router.history$.addListener, 'function') - assert.notStrictEqual(router.createHref, null) - assert.strictEqual(typeof router.createHref, 'function') - return {} - } - routerify(app, switchPath)({history: xs.never()}) - } - ) + describe('path()', () => { + it( + 'should return an object with `path` `define` `observable` ' + + '`createHref` and `dispose`', + () => { + const app = ({ router }) => { + assert.notStrictEqual(router.path, null); + assert.strictEqual(typeof router.path, 'function'); + assert.notStrictEqual(router.define, null); + assert.strictEqual(typeof router.define, 'function'); + assert.notStrictEqual(router.history$, null); + assert.strictEqual(typeof router.history$, 'object'); + assert.strictEqual( + typeof router.history$.addListener, + 'function' + ); + assert.notStrictEqual(router.createHref, null); + assert.strictEqual(typeof router.createHref, 'function'); + return {}; + }; + routerify(app, switchPath)({ history: xs.never() }); + } + ); - it('should filter the history$', () => { - const routes = ['/somewhere/else', '/path/that/is/correct'] + it('should filter the history$', () => { + const routes = ['/somewhere/else', '/path/that/is/correct']; - const app = sources => { - sources.router.path('/path').history$.addListener({ - next: location => { - assert.notStrictEqual(location.pathname, '/somewhere/else') - assert.strictEqual(location.pathname, '/path/that/is/correct') - }, - error: () => {}, - complete: () => {} - }) - return {} - } - routerify(app, switchPath)({ - history: makeServerHistoryDriver()(xs.fromArray(routes)) - }) - }) + const app = sources => { + sources.router.path('/path').history$.addListener({ + next: location => { + assert.notStrictEqual( + location.pathname, + '/somewhere/else' + ); + assert.strictEqual( + location.pathname, + '/path/that/is/correct' + ); + }, + error: () => {}, + complete: () => {} + }); + return {}; + }; + routerify(app, switchPath)({ + history: makeServerHistoryDriver()(xs.fromArray(routes)) + }); + }); - it('multiple path()s should filter the history$', () => { - const routes = [ - '/the/wrong/path', - '/some/really/really/deeply/nested/route/that/is/correct', - '/some/really/really/deeply/nested/incorrect/route' - ] + it('multiple path()s should filter the history$', () => { + const routes = [ + '/the/wrong/path', + '/some/really/really/deeply/nested/route/that/is/correct', + '/some/really/really/deeply/nested/incorrect/route' + ]; - const app = sources => { - sources.router - .path('/some') - .path('/really') - .path('/really') - .path('/deeply') - .path('/nested') - .path('/route') - .path('/that') - .history$.addListener({ - next: ({pathname}) => { - assert.strictEqual( - pathname, - '/some/really/really/deeply/nested/route/that/is/correct' - ) - }, - error: () => {}, - complete: () => {} - }) - return {} - } - routerify(app, switchPath)({ - history: makeServerHistoryDriver()(xs.fromArray(routes)) - }) - }) + const app = sources => { + sources.router + .path('/some') + .path('/really') + .path('/really') + .path('/deeply') + .path('/nested') + .path('/route') + .path('/that') + .history$.addListener({ + next: ({ pathname }) => { + assert.strictEqual( + pathname, + '/some/really/really/deeply/nested/route/that/is/correct' + ); + }, + error: () => {}, + complete: () => {} + }); + return {}; + }; + routerify(app, switchPath)({ + history: makeServerHistoryDriver()(xs.fromArray(routes)) + }); + }); - it('should create a proper path using createHref()', () => { - const routes = [ - '/the/wrong/path', - '/some/really/really/deeply/nested/route/that/is/correct', - '/some/really/really/deeply/nested/incorrect/route' - ] + it('should create a proper path using createHref()', () => { + const routes = [ + '/the/wrong/path', + '/some/really/really/deeply/nested/route/that/is/correct', + '/some/really/really/deeply/nested/incorrect/route' + ]; - const app = sources => { - sources.router - .path('/some') - .path('/really') - .path('/really') - .path('/deeply') - .path('/nested') - .path('/route') - .path('/that') - .history$.addListener({ - next: ({pathname}) => { - assert.strictEqual( - pathname, - '/some/really/really/deeply/nested/route/that/is/correct' - ) - assert.strictEqual( - router.createHref('/is/correct'), - '/some/really/really/deeply/nested/route/that/is/correct' - ) - }, - error: () => {}, - complete: () => {} - }) - return {} - } - routerify(app, switchPath)({ - history: makeServerHistoryDriver()(xs.fromArray(routes)) - }) - }) - }) + const app = sources => { + sources.router + .path('/some') + .path('/really') + .path('/really') + .path('/deeply') + .path('/nested') + .path('/route') + .path('/that') + .history$.addListener({ + next: ({ pathname }) => { + assert.strictEqual( + pathname, + '/some/really/really/deeply/nested/route/that/is/correct' + ); + assert.strictEqual( + router.createHref('/is/correct'), + '/some/really/really/deeply/nested/route/that/is/correct' + ); + }, + error: () => {}, + complete: () => {} + }); + return {}; + }; + routerify(app, switchPath)({ + history: makeServerHistoryDriver()(xs.fromArray(routes)) + }); + }); + }); - describe('define()', () => { - it( - 'should return an object with `path$` `value$` `fullPath$` ' + - '`createHref` and `dispose`', - () => { - const app = sources => { - const router = sources.router.define({}) - assert.strictEqual(router instanceof xs, true) - assert.strictEqual(typeof router.addListener, 'function') - assert.notStrictEqual(router.createHref, null) - assert.strictEqual(typeof router.createHref, 'function') - return {} - } - routerify(app, switchPath)({history: xs.never()}) - } - ) + describe('define()', () => { + it( + 'should return an object with `path$` `value$` `fullPath$` ' + + '`createHref` and `dispose`', + () => { + const app = sources => { + const router = sources.router.define({}); + assert.strictEqual(router instanceof xs, true); + assert.strictEqual(typeof router.addListener, 'function'); + assert.notStrictEqual(router.createHref, null); + assert.strictEqual(typeof router.createHref, 'function'); + return {}; + }; + routerify(app, switchPath)({ history: xs.never() }); + } + ); - it('should match routes against a definition object', done => { - const defintion = { - '/some': { - '/route': 123 - } - } + it('should match routes against a definition object', done => { + const defintion = { + '/some': { + '/route': 123 + } + }; - const app = ({router}) => { - const match$ = router.define(defintion) - match$.addListener({ - next: ({path, value, location}) => { - assert.strictEqual(path, '/some/route') - assert.strictEqual(value, 123) - assert.strictEqual(location.pathname, '/some/route') - done() - }, - error: () => {}, - complete: () => {} - }) - return {} - } - routerify(app, switchPath)({ - history: makeServerHistoryDriver()(xs.of('/some/route')) - }) - }) + const app = ({ router }) => { + const match$ = router.define(defintion); + match$.addListener({ + next: ({ path, value, location }) => { + assert.strictEqual(path, '/some/route'); + assert.strictEqual(value, 123); + assert.strictEqual(location.pathname, '/some/route'); + done(); + }, + error: () => {}, + complete: () => {} + }); + return {}; + }; + routerify(app, switchPath)({ + history: makeServerHistoryDriver()(xs.of('/some/route')) + }); + }); - it('should respect prior filtering by path()', done => { - const defintion = { - '/correct': { - '/route': 123 - } - } + it('should respect prior filtering by path()', done => { + const defintion = { + '/correct': { + '/route': 123 + } + }; - const routes = ['/some/nested/correct/route'] + const routes = ['/some/nested/correct/route']; - const app = ({router}) => { - const match$ = router.path('/some').path('/nested').define(defintion) - match$.addListener({ - next: ({path, value, location}) => { - assert.strictEqual(path, '/correct/route') - assert.strictEqual(value, 123) - assert.strictEqual(location.pathname, '/some/nested/correct/route') - done() - }, - error: () => {}, - complete: () => {} - }) - return {} - } - routerify(app, switchPath)({ - history: makeServerHistoryDriver()( - xs.of('/wrong/path', '/some/nested/correct/route').compose(delay(0)) - ) - }) - }) + const app = ({ router }) => { + const match$ = router + .path('/some') + .path('/nested') + .define(defintion); + match$.addListener({ + next: ({ path, value, location }) => { + assert.strictEqual(path, '/correct/route'); + assert.strictEqual(value, 123); + assert.strictEqual( + location.pathname, + '/some/nested/correct/route' + ); + done(); + }, + error: () => {}, + complete: () => {} + }); + return {}; + }; + routerify(app, switchPath)({ + history: makeServerHistoryDriver()( + xs + .of('/wrong/path', '/some/nested/correct/route') + .compose(delay(0)) + ) + }); + }); - it('should match a default route if one is not found', done => { - const definition = { - '/correct': { - '/route': 123 - }, - '*': 999 - } + it('should match a default route if one is not found', done => { + const definition = { + '/correct': { + '/route': 123 + }, + '*': 999 + }; - const app = ({router}) => { - const match$ = router.path('/some').path('/nested').define(definition) - match$.addListener({ - next: ({path, value, location}) => { - assert.strictEqual(path, '/incorrect/route') - assert.strictEqual(value, 999) - assert.strictEqual( - location.pathname, - '/some/nested/incorrect/route' - ) - done() - }, - error: () => {}, - complete: () => {} - }) - return {router: xs.of('/wrong/path')} - } - routerify(app, switchPath)({ - history: makeServerHistoryDriver()( - xs - .of('/wrong/route', '/some/nested/incorrect/route') - .compose(delay(0)) - ) - }) - }) + const app = ({ router }) => { + const match$ = router + .path('/some') + .path('/nested') + .define(definition); + match$.addListener({ + next: ({ path, value, location }) => { + assert.strictEqual(path, '/incorrect/route'); + assert.strictEqual(value, 999); + assert.strictEqual( + location.pathname, + '/some/nested/incorrect/route' + ); + done(); + }, + error: () => {}, + complete: () => {} + }); + return { router: xs.of('/wrong/path') }; + }; + routerify(app, switchPath)({ + history: makeServerHistoryDriver()( + xs + .of('/wrong/route', '/some/nested/incorrect/route') + .compose(delay(0)) + ) + }); + }); - it('should create a proper href using createHref()', done => { - const definition = { - '/correct': { - '/route': 123 - }, - '*': 999 - } + it('should create a proper href using createHref()', done => { + const definition = { + '/correct': { + '/route': 123 + }, + '*': 999 + }; - const app = ({router}) => { - const match$ = router.path('/some').path('/nested').define(definition) + const app = ({ router }) => { + const match$ = router + .path('/some') + .path('/nested') + .define(definition); - assert(match$.createHref('/hello'), '/some/nested/hello') - match$.addListener({ - next: ({path, value, location, createHref}) => { - assert.strictEqual(path, '/incorrect/route') - assert.strictEqual(value, 999) - assert.strictEqual( - location.pathname, - '/some/nested/incorrect/route' - ) - //assert.strictEqual(location.pathname, createHref('/incorrect/route')) - done() - }, - error: () => {}, - complete: () => {} - }) - return {router: xs.of('/wrong/path')} - } - routerify(app, switchPath)({ - history: makeServerHistoryDriver()( - xs - .of('/wrong/route', '/some/nested/incorrect/route') - .compose(delay(0)) - ) - }) - }) + assert(match$.createHref('/hello'), '/some/nested/hello'); + match$.addListener({ + next: ({ path, value, location, createHref }) => { + assert.strictEqual(path, '/incorrect/route'); + assert.strictEqual(value, 999); + assert.strictEqual( + location.pathname, + '/some/nested/incorrect/route' + ); + //assert.strictEqual(location.pathname, createHref('/incorrect/route')) + done(); + }, + error: () => {}, + complete: () => {} + }); + return { router: xs.of('/wrong/path') }; + }; + routerify(app, switchPath)({ + history: makeServerHistoryDriver()( + xs + .of('/wrong/route', '/some/nested/incorrect/route') + .compose(delay(0)) + ) + }); + }); - it('should match partials', done => { - const defintion = { - '/correct': { - '/route': 123 - }, - '*': 999 - } + it('should match partials', done => { + const defintion = { + '/correct': { + '/route': 123 + }, + '*': 999 + }; - const app = ({router}) => { - const match$ = router.path('/some').path('/nested').define(defintion) - match$.addListener({ - next: ({path, location: {pathname}, createHref}) => { - assert.strictEqual(path, '/correct/route') - assert.strictEqual(pathname, '/some/nested/correct/route/partial') - done() - }, - error: () => {}, - complete: () => {} - }) + const app = ({ router }) => { + const match$ = router + .path('/some') + .path('/nested') + .define(defintion); + match$.addListener({ + next: ({ path, location: { pathname }, createHref }) => { + assert.strictEqual(path, '/correct/route'); + assert.strictEqual( + pathname, + '/some/nested/correct/route/partial' + ); + done(); + }, + error: () => {}, + complete: () => {} + }); - return {router: xs.of('/wrong/path')} - } - routerify(app, switchPath)({ - history: makeServerHistoryDriver()( - xs.of('/some/nested/correct/route/partial').compose(delay(0)) - ) - }) - }) - }) -}) + return { router: xs.of('/wrong/path') }; + }; + routerify(app, switchPath)({ + history: makeServerHistoryDriver()( + xs + .of('/some/nested/correct/route/partial') + .compose(delay(0)) + ) + }); + }); + }); +});