/* * Squidex Headless CMS * * @license * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ import { NavigationExtras, Params, Router } from '@angular/router'; import { LocalStoreService, MathHelper, Pager } from '@app/framework/internal'; import { BehaviorSubject } from 'rxjs'; import { IMock, It, Mock, Times } from 'typemoq'; import { State } from './../../state'; import { PagerSynchronizer, Router2State, StringKeysSynchronizer, StringSynchronizer } from './router-2-state'; describe('Router2State', () => { describe('Strings', () => { const synchronizer = new StringSynchronizer('key'); it('should write string to route', () => { const params: Params = {}; const value = 'my-string'; synchronizer.writeValue(value, params); expect(params).toEqual({ key: 'my-string' }); }); it('Should write undefined when not a string', () => { const params: Params = {}; const value = 123; synchronizer.writeValue(value, params); expect(params).toEqual({ key: undefined }); }); it('should get string from route', () => { const params: Params = { key: 'my-string' }; const value = synchronizer.getValue(params); expect(value).toEqual('my-string'); }); }); describe('StringKeys', () => { const synchronizer = new StringKeysSynchronizer('key'); it('should write object keys to route', () => { const params: Params = {}; const value = { flag1: true, flag2: false }; synchronizer.writeValue(value, params); expect(params).toEqual({ key: 'flag1,flag2' }); }); it('Should write undefined when empty', () => { const params: Params = {}; const value = {}; synchronizer.writeValue(value, params); expect(params).toEqual({ key: undefined }); }); it('Should write undefined when not an object', () => { const params: Params = {}; const value = 123; synchronizer.writeValue(value, params); expect(params).toEqual({ key: undefined }); }); it('should get object from route', () => { const params: Params = { key: 'flag1,flag2' }; const value = synchronizer.getValue(params); expect(value).toEqual({ flag1: true, flag2: true }); }); it('should get object with empty keys from route', () => { const params: Params = { key: 'flag1,,,flag2' }; const value = synchronizer.getValue(params); expect(value).toEqual({ flag1: true, flag2: true }); }); }); describe('Pager', () => { let synchronizer: PagerSynchronizer; let localStore: IMock; beforeEach(() => { localStore = Mock.ofType(); synchronizer = new PagerSynchronizer(localStore.object, 'contents', 30); }); it('should write pager to route and local store', () => { const params: Params = {}; const value = new Pager(0, 10, 20, true); synchronizer.writeValue(value, params); expect(params).toEqual({ take: '20', page: '10' }); localStore.verify(x => x.setInt('contents.pageSize', 20), Times.once()); }); it('Should write undefined when page number is zero', () => { const params: Params = {}; const value = new Pager(0, 0, 20, true); synchronizer.writeValue(value, params); expect(params).toEqual({ take: '20', page: undefined }); localStore.verify(x => x.setInt('contents.pageSize', 20), Times.once()); }); it('should write undefined when value not a pager', () => { const params: Params = {}; const value = 123; synchronizer.writeValue(value, params); expect(params).toEqual({ take: undefined, page: undefined }); localStore.verify(x => x.setInt('contents.pageSize', 20), Times.never()); }); it('should write undefined when value is null', () => { const params: Params = {}; const value = null; synchronizer.writeValue(value, params); expect(params).toEqual({ take: undefined, page: undefined }); localStore.verify(x => x.setInt('contents.pageSize', 20), Times.never()); }); it('should get page and size from route', () => { const params: Params = { page: '10', take: '40' }; const value = synchronizer.getValue(params); expect(value).toEqual(new Pager(0, 10, 40, true)); }); it('should get page size from local store as fallback', () => { localStore.setup(x => x.getInt('contents.pageSize', It.isAny())) .returns(() => 40); const params: Params = { page: '10' }; const value = synchronizer.getValue(params); expect(value).toEqual(new Pager(0, 10, 40, true)); }); it('should get page size from default if local store is invalid', () => { localStore.setup(x => x.getInt('contents.pageSize', It.isAny())) .returns(() => -5); const params: Params = { page: '10' }; const value = synchronizer.getValue(params); expect(value).toEqual(new Pager(0, 10, 30, true)); }); it('should get page size from default as last fallback', () => { const params: Params = { page: '10' }; const value = synchronizer.getValue(params); expect(value).toEqual(new Pager(0, 10, 30, true)); }); it('should fix page number if invalid', () => { const params: Params = { page: '-10' }; const value = synchronizer.getValue(params); expect(value).toEqual(new Pager(0, 0, 30, true)); }); }); describe('Implementation', () => { let localStore: IMock; let routerQueryParams: BehaviorSubject; let routeActivated: any; let router: IMock; let router2State: Router2State; let state: State; let invoked = 0; beforeEach(() => { localStore = Mock.ofType(); router = Mock.ofType(); state = new State({}); routerQueryParams = new BehaviorSubject({}); routeActivated = { queryParams: routerQueryParams, id: MathHelper.guid() }; router2State = new Router2State(routeActivated, router.object, localStore.object); router2State.mapTo(state) .keep('keep') .withString('state1', 'key1') .withString('state2', 'key2') .whenSynced(() => { invoked++; }) .build(); invoked = 0; }); afterEach(() => { router2State.ngOnDestroy(); }); it('should unsubscribe from route and state', () => { router2State.ngOnDestroy(); expect(state.changes['observers'].length).toBe(0); expect(routeActivated.queryParams.observers.length).toBe(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 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' }); routerQueryParams.next({ key1: 'hello', key2: 'squidex' }); expect(invoked).toEqual(1); }); it('Should sync again when new query changed', () => { routerQueryParams.next({ key1: 'hello', key2: 'squidex' }); routerQueryParams.next({ key1: 'hello', key2: 'squidex', key3: '!' }); expect(invoked).toEqual(2); }); 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); }); it('Should sync from state', () => { let routeExtras: NavigationExtras; router.setup(x => x.navigate([], It.isAny())) .callback((_, extras) => { routeExtras = extras; }); state.next({ state1: 'hello', state2: 'squidex' }); expect(routeExtras!.relativeTo).toBeDefined(); expect(routeExtras!.replaceUrl).toBeTrue(); expect(routeExtras!.queryParamsHandling).toBe('merge'); expect(routeExtras!.queryParams).toEqual({ key1: 'hello', key2: 'squidex' }); }); }); });