@ -5,96 +5,80 @@
* Copyright ( c ) Squidex UG ( haftungsbeschränkt ) . All rights reserved .
* /
import { NavigationEnd , NavigationExtras , NavigationStart , Params , Router } from '@angular/router' ;
import { LocalStoreService , MathHelper } from '@app/framework/internal' ;
import { BehaviorSubject , Subject } from 'rxjs' ;
import { NavigationExtras , Params , Router } from '@angular/router' ;
import { LocalStoreService } from '@app/framework/internal' ;
import { IMock , It , Mock , Times } from 'typemoq' ;
import { State } from './../../state' ;
import { PagingSynchronizer , Router2State , StringKeysSynchronizer , StringSynchronizer } from './router-2-state' ;
import { PagingSynchronizer , QueryParams , Router2State , StringKeysSynchronizer , StringSynchronizer } from './router-2-state' ;
describe ( 'Router2State' , ( ) = > {
describe ( 'Strings' , ( ) = > {
const synchronizer = new StringSynchronizer ( 'key' , 'key' ) ;
it ( 'should write string to route' , ( ) = > {
const params : Params = { } ;
const synchronizer = new StringSynchronizer ( 'key' ) ;
it ( 'should parse from state' , ( ) = > {
const value = 'my-string' ;
synchronizer . writeValuesToRou te( { key : value } , params ) ;
const query = synchronizer . parseFromSta te( { key : value } ) ;
expect ( params ) . toEqual ( { key : 'my-string' } ) ;
expect ( query ) . toEqual ( { key : 'my-string' } ) ;
} ) ;
it ( 'Should write undefined when not a string' , ( ) = > {
const params : Params = { } ;
it ( 'should parse from state as undefined when not a string' , ( ) = > {
const value = 123 ;
synchronizer . writeValuesToRoute ( value , params ) ;
const query = synchronizer . parseFromState ( { key : value } ) ;
expect ( params ) . toEqual ( { key : undefined } ) ;
expect ( query ) . toBeUndefined ( ) ;
} ) ;
it ( 'should get string from route' , ( ) = > {
const params : Params = {
key : 'my-string'
} ;
const params : QueryParams = { key : 'my-string' } ;
const value = synchronizer . parseValues FromRoute ( params ) ;
const value = synchronizer . parseFromRoute ( params ) ;
expect ( value ) . toEqual ( { key : 'my-string' } ) ;
} ) ;
} ) ;
describe ( 'StringKeys' , ( ) = > {
const synchronizer = new StringKeysSynchronizer ( 'key' , 'key' ) ;
const synchronizer = new StringKeysSynchronizer ( 'key' ) ;
it ( 'should write object keys to rou te' , ( ) = > {
const params : Params = { } ;
it ( 'should parse from sta te' , ( ) = > {
const value = { flag1 : true , flag2 : true } ;
const value = {
flag1 : true ,
flag2 : false
} ;
synchronizer . writeValuesToRoute ( { key : value } , params ) ;
const query = synchronizer . parseFromState ( { key : value } ) ;
expect ( params ) . toEqual ( { key : 'flag1,flag2' } ) ;
expect ( query ) . toEqual ( { key : 'flag1,flag2' } ) ;
} ) ;
it ( 'Should write undefined when empty' , ( ) = > {
const params : Params = { } ;
const value = { } ;
it ( 'should parse from state as undefined when empty' , ( ) = > {
const value = 123 ;
synchronizer . writeValuesToRou te( { key : value } , params ) ;
const query = synchronizer . parseFromState ( { key : value } ) ;
expect ( params ) . toEqual ( { key : undefined } ) ;
expect ( query ) . toBeUndefined ( ) ;
} ) ;
it ( 'Should write undefined when not an object' , ( ) = > {
const params : Params = { } ;
it ( 'should parse from state as undefined when not an object' , ( ) = > {
const value = 123 ;
synchronizer . writeValuesToRou te( { key : value } , params ) ;
const query = synchronizer . parseFromSta te( { key : value } ) ;
expect ( params ) . toEqual ( { key : undefined } ) ;
expect ( query ) . toBeUndefined ( ) ;
} ) ;
it ( 'should get object from route' , ( ) = > {
const params : Params = { key : 'flag1,flag2' } ;
const params : Query Params = { key : 'flag1,flag2' } ;
const value = synchronizer . parseValues FromRoute ( params ) ;
const value = synchronizer . parseFromRoute ( params ) ;
expect ( value ) . toEqual ( { key : { flag1 : true , flag2 : true } } ) ;
} ) ;
it ( 'should get object with empty keys from route' , ( ) = > {
const params : Params = { key : 'flag1,,,flag2' } ;
const params : Query Params = { key : 'flag1,,,flag2' } ;
const value = synchronizer . parseValues FromRoute ( params ) ;
const value = synchronizer . parseFromRoute ( params ) ;
expect ( value ) . toEqual ( { key : { flag1 : true , flag2 : true } } ) ;
} ) ;
@ -110,10 +94,30 @@ describe('Router2State', () => {
synchronizer = new PagingSynchronizer ( localStore . object , 'contents' , 30 ) ;
} ) ;
it ( 'should parse from state' , ( ) = > {
const state = { page : 10 , pageSize : 20 } ;
const query = synchronizer . parseFromState ( state ) ;
expect ( query ) . toEqual ( { page : '10' , pageSize : '20' } ) ;
localStore . verify ( x = > x . setInt ( 'contents.pageSize' , 20 ) , Times . once ( ) ) ;
} ) ;
it ( 'should parse from state without page when zero' , ( ) = > {
const state = { page : 0 , pageSize : 20 } ;
const query = synchronizer . parseFromState ( state ) ;
expect ( query ) . toEqual ( { page : undefined , pageSize : '20' } ) ;
localStore . verify ( x = > x . setInt ( 'contents.pageSize' , 20 ) , Times . once ( ) ) ;
} ) ;
it ( 'should get page and size from route' , ( ) = > {
const params : Params = { page : '10' , pageSize : '40' } ;
const value = synchronizer . parseValuesFromRoute ( params ) ;
const value = synchronizer . parseFromRoute ( params ) ;
expect ( value ) . toEqual ( { page : 10 , pageSize : 40 } ) ;
} ) ;
@ -124,7 +128,7 @@ describe('Router2State', () => {
const params : Params = { page : '10' } ;
const value = synchronizer . parseValues FromRoute ( params ) ;
const value = synchronizer . parseFromRoute ( params ) ;
expect ( value ) . toEqual ( { page : 10 , pageSize : 40 } ) ;
} ) ;
@ -135,7 +139,7 @@ describe('Router2State', () => {
const params : Params = { page : '10' } ;
const value = synchronizer . parseValues FromRoute ( params ) ;
const value = synchronizer . parseFromRoute ( params ) ;
expect ( value ) . toEqual ( { page : 10 , pageSize : 30 } ) ;
} ) ;
@ -143,7 +147,7 @@ describe('Router2State', () => {
it ( 'should get page size from default as last fallback' , ( ) = > {
const params : Params = { page : '10' } ;
const value = synchronizer . parseValues FromRoute ( params ) ;
const value = synchronizer . parseFromRoute ( params ) ;
expect ( value ) . toEqual ( { page : 10 , pageSize : 30 } ) ;
} ) ;
@ -151,176 +155,58 @@ describe('Router2State', () => {
it ( 'should fix page number if invalid' , ( ) = > {
const params : Params = { page : '-10' } ;
const value = synchronizer . parseValues FromRoute ( params ) ;
const value = synchronizer . parseFromRoute ( params ) ;
expect ( value ) . toEqual ( { page : 0 , pageSize : 30 } ) ;
} ) ;
it ( 'should write pager to route and local store' , ( ) = > {
const params : Params = { } ;
synchronizer . writeValuesToRoute ( { page : 10 , pageSize : 20 } , params ) ;
expect ( params ) . toEqual ( { page : '10' , pageSize : '20' } ) ;
localStore . verify ( x = > x . setInt ( 'contents.pageSize' , 20 ) , Times . once ( ) ) ;
} ) ;
it ( 'Should write undefined when page number is zero' , ( ) = > {
const params : Params = { } ;
synchronizer . writeValuesToRoute ( { page : 0 , pageSize : 20 } , params ) ;
expect ( params ) . toEqual ( { page : undefined , pageSize : '20' } ) ;
localStore . verify ( x = > x . setInt ( 'contents.pageSize' , 20 ) , Times . once ( ) ) ;
} ) ;
} ) ;
describe ( 'Implementation' , ( ) = > {
let localStore : IMock < LocalStoreService > ;
let routerQueryParams : BehaviorSubject < Params > ;
let routerEvents : Subject < any > ;
let queryParams : QueryParams = { } ;
let route : any ;
let router : IMock < Router > ;
let router2State : Router2State ;
let state : State < any > ;
let invoked = 0 ;
beforeEach ( ( ) = > {
localStore = Mock . ofType < LocalStoreService > ( ) ;
routerEvents = new Subject < any > ( ) ;
queryParams = { } ;
router = Mock . ofType < Router > ( ) ;
router . setup ( x = > x . events ) . returns ( ( ) = > routerEvents ) ;
route = {
snapshot : {
queryParams
}
} ;
state = new State < any > ( { } ) ;
routerQueryParams = new BehaviorSubject < Params > ( { } ) ;
route = { queryParams : routerQueryParams , id : MathHelper.guid ( ) } ;
router2State = new Router2State ( route , router . object , localStore . object ) ;
router2State . mapTo ( state )
. keep ( 'keep' )
. withString ( 'state1' , 'key1' )
. withString ( 'state2' , 'key2' )
. whenSynced ( ( ) = > { invoked ++ ; } )
. build ( ) ;
invoked = 0 ;
. withString ( 'state1' )
. withStrings ( 'state2' )
. listen ( ) ;
} ) ;
afterEach ( ( ) = > {
router2State . ngOnDestroy ( ) ;
} ) ;
it ( 'should unsubscribe from route and state' , ( ) = > {
it ( 'should unsubscribe from state' , ( ) = > {
router2State . ngOnDestroy ( ) ;
expect ( state . changes [ 'observers' ] . length ) . toEqual ( 0 ) ;
expect ( route . queryParams . observers . length ) . toEqual ( 0 ) ;
expect ( routerEvents . observers . length ) . toEqual ( 0 ) ;
} ) ;
it ( 'Should sync from route' , ( ) = > {
routerQueryParams . next ( {
key1 : 'hello' ,
key2 : 'squidex'
} ) ;
expect ( state . snapshot . state1 ) . toEqual ( 'hello' ) ;
expect ( state . snapshot . state2 ) . toEqual ( 'squidex' ) ;
} ) ;
it ( 'Should get values from route' , ( ) = > {
queryParams [ 'state1' ] = 'hello' ;
queryParams [ 'state2' ] = 'squidex,cms' ;
it ( 'Should invoke callback after sync from route' , ( ) = > {
routerQueryParams . next ( {
key1 : 'hello' ,
key2 : 'squidex'
} ) ;
expect ( invoked ) . toEqual ( 1 ) ;
} ) ;
it ( 'Should not sync again when nothing changed' , ( ) = > {
routerQueryParams . next ( {
key1 : 'hello' ,
key2 : 'squidex'
} ) ;
const values = router2State . getInitial ( ) ;
routerQueryParams . next ( {
key1 : 'hello' ,
key2 : 'squidex'
} ) ;
expect ( invoked ) . toEqual ( 1 ) ;
} ) ;
it ( 'Should not sync again when no value has changed' , ( ) = > {
routerQueryParams . next ( {
key1 : 'hello' ,
key2 : 'squidex'
} ) ;
routerQueryParams . next ( {
key1 : 'hello' ,
key2 : 'squidex' ,
key3 : undefined ,
key4 : null
} ) ;
expect ( invoked ) . toEqual ( 1 ) ;
} ) ;
it ( 'Should sync again when new query changed' , ( ) = > {
routerQueryParams . next ( {
key1 : 'hello' ,
key2 : 'squidex'
} ) ;
routerQueryParams . next ( {
key1 : 'hello' ,
key2 : 'cms' ,
key3 : '!'
} ) ;
expect ( invoked ) . toEqual ( 2 ) ;
} ) ;
it ( 'Should not sync again when no state as changed' , ( ) = > {
routerQueryParams . next ( {
key1 : 'hello' ,
key2 : 'squidex'
} ) ;
routerQueryParams . next ( {
key1 : 'hello' ,
key2 : 'squidex' ,
key3 : '!'
} ) ;
expect ( invoked ) . toEqual ( 1 ) ;
} ) ;
it ( 'Should reset other values when synced from route' , ( ) = > {
state . next ( { other : 123 } ) ;
routerQueryParams . next ( {
key1 : 'hello' ,
key2 : 'squidex'
} ) ;
expect ( state . snapshot . other ) . toBeUndefined ( ) ;
} ) ;
it ( 'Should keep configued values when synced from route' , ( ) = > {
state . next ( { keep : 123 } ) ;
routerQueryParams . next ( {
key1 : 'hello' ,
key2 : 'squidex'
} ) ;
expect ( state . snapshot . keep ) . toBe ( 123 ) ;
expect ( values ) . toEqual ( { state1 : 'hello' , state2 : { squidex : true , cms : true } } ) ;
} ) ;
it ( 'Should sync from state' , ( ) = > {
@ -331,47 +217,37 @@ describe('Router2State', () => {
state . next ( {
state1 : 'hello' ,
state2 : 'squidex'
state2 : { squidex : true , cms : true }
} ) ;
expect ( routeExtras ! . replaceUrl ) . toBeTrue ( ) ;
expect ( routeExtras ! . queryParamsHandling ) . toBe ( 'merge' ) ;
expect ( routeExtras ! . queryParams ) . toEqual ( { key1 : 'hello' , key2 : 'squidex' } ) ;
} ) ;
expect ( routeExtras ! . queryParams ) . toEqual ( { state1 : 'hello' , state2 : 'squidex,cms' } ) ;
it ( 'Should not sync when navigating' , ( ) = > {
routerEvents . next ( new NavigationStart ( 0 , '' ) ) ;
state . next ( {
state1 : 'hello' ,
state2 : 'squidex'
router . verify ( x = > x . navigate ( It . isAny ( ) , It . isAny ( ) ) , Times . exactly ( 2 ) ) ;
} ) ;
router . verify ( x = > x . navigate ( It . isAny ( ) , It . isAny ( ) ) , Times . never ( ) ) ;
expect ( ) . nothing ( ) ;
} ) ;
it ( 'Should sync from state delayed when navigating' , ( ) = > {
it ( 'Should not sync from state again when nothing has changed' , ( ) = > {
let routeExtras : NavigationExtras ;
router . setup ( x = > x . navigate ( [ ] , It . isAny ( ) ) )
. callback ( ( _ , extras ) = > { routeExtras = extras ; } ) ;
routerEvents . next ( new NavigationStart ( 0 , '' ) ) ;
state . next ( {
state1 : 'hello' ,
state2 : 'squidex'
state2 : { squidex : true , cms : true }
} ) ;
router . verify ( x = > x . navigate ( It . isAny ( ) , It . isAny ( ) ) , Times . never ( ) ) ;
routerEvents . next ( new NavigationEnd ( 0 , '' , '' ) ) ;
state . next ( {
state1 : 'hello' ,
state2 : { squidex : true , cms : true }
} ) ;
expect ( routeExtras ! . replaceUrl ) . toBeTrue ( ) ;
expect ( routeExtras ! . queryParamsHandling ) . toBe ( 'merge' ) ;
expect ( routeExtras ! . queryParams ) . toEqual ( { key1 : 'hello' , key2 : 'squidex' } ) ;
expect ( routeExtras ! . queryParams ) . toEqual ( { state1 : 'hello' , state2 : 'squidex,cms' } ) ;
router . verify ( x = > x . navigate ( It . isAny ( ) , It . isAny ( ) ) , Times . exactly ( 2 ) ) ;
} ) ;
} ) ;
} ) ;