UNPKG

ohayolibs

Version:

Ohayo is a set of essential modules for ohayojp.

530 lines (517 loc) 18.8 kB
import { TestBed } from '@angular/core/testing'; import { ActivatedRoute, Router, RouteReuseStrategy } from '@angular/router'; import { MenuService } from '@ohayo/theme'; import { NzSafeAny } from 'ng-zorro-antd/core/types'; import { filter } from 'rxjs/operators'; import { ReuseTabMatchMode, ReuseTitle } from './reuse-tab.interfaces'; import { ReuseTabService } from './reuse-tab.service'; import { ReuseTabStrategy } from './reuse-tab.strategy'; class MockMenuService { getPathByUrl(url: string): Array<{ text: string }> { return url === '/a/0' ? [] : [{ text: `标题` }]; } } class MockRouter { navigateByUrl = jasmine.createSpy(); get events(): any { return { subscribe: () => { return { unsubscribe: () => { } }; }, }; } } describe('components: reuse-tab(service)', () => { let srv: ReuseTabService; let menuSrv: MenuService; let router: MockRouter; afterEach(() => srv.ngOnDestroy()); function genModule(providers: any[] = [{ provide: MenuService, useClass: MockMenuService }]): void { TestBed.configureTestingModule({ providers: [ ReuseTabService, { provide: RouteReuseStrategy, useClass: ReuseTabStrategy, deps: [ReuseTabService], }, { provide: ActivatedRoute, useValue: { snapshot: { url: [] } } }, { provide: Router, useFactory: () => new MockRouter() }, ].concat(providers), }); srv = TestBed.inject<ReuseTabService>(ReuseTabService); menuSrv = TestBed.inject<MenuService>(MenuService, undefined); router = TestBed.inject<Router>(Router) as NzSafeAny; } function genCached(count: number, urlTpl: string = `a/{index}`): void { srv.clear(); Array(count) .fill({}) .forEach((_item: any, index: number) => { srv.store(getSnapshot(index + 1, urlTpl), { a: 1 }); }); } /** * 模拟 Snapshot * - 1 => a/1 */ function getSnapshot(index: number, urlTpl: string = `a/{index}`): any { return { routeConfig: {}, url: [urlTpl.replace(`{index}`, index + '')], } as any; } describe('[property]', () => { beforeEach(() => genModule()); describe('#max', () => { it('should working', () => { genCached(10); expect(srv.count).toBe(10); srv.max = 5; expect(srv.count).toBe(5); }); it('should be close oldest page', () => { srv.max = 2; srv.store(getSnapshot(1), {}); srv.store(getSnapshot(2), {}); srv.store(getSnapshot(3), {}); expect(srv.count).toBe(2); srv.store(getSnapshot(4), {}); expect(srv.count).toBe(2); }); it('should be ingore close when all is not closable', () => { srv.max = 2; srv.store(getSnapshot(1), {}); srv.store(getSnapshot(2), {}); srv.items.forEach(i => (i.closable = false)); srv.store(getSnapshot(3), {}); expect(srv.count).toBe(3); srv.store(getSnapshot(4), {}); expect(srv.count).toBe(3); }); }); describe('#mode', () => { describe('when ReuseTabMatchMode.Menu', () => { beforeEach(() => (srv.mode = ReuseTabMatchMode.Menu)); it('can hit because menu data not allow', () => { const snapshot = getSnapshot(1); expect(srv.can(snapshot)).toBe(true); }); it(`can't hit because not found in menu`, () => { const snapshot = getSnapshot(0); expect(srv.can(snapshot)).toBe(false); }); it(`can't hit because not allowed reuse in menu`, () => { const spy = spyOn(menuSrv, 'getPathByUrl'); spy.and.returnValue([{ text: '1', link: '/a/1', reuse: false }]); expect(srv.can(getSnapshot(1))).toBe(false); spy.and.returnValue([{ text: '2', link: '/a/2', reuse: true }]); expect(srv.can(getSnapshot(2))).toBe(true); }); }); describe('when ReuseTabMatchMode.MenuForce', () => { beforeEach(() => (srv.mode = ReuseTabMatchMode.MenuForce)); it('can hit because menu data muse allow', () => { const spy = spyOn(menuSrv, 'getPathByUrl'); spy.and.returnValue([{ text: '1', link: '/a/1', reuse: true }]); expect(srv.can(getSnapshot(1))).toBe(true); }); it(`can't hit because not found in menu`, () => { const snapshot = getSnapshot(0); expect(srv.can(snapshot)).toBe(false); }); it(`can't hit because not allowed reuse in menu`, () => { const spy = spyOn(menuSrv, 'getPathByUrl'); spy.and.returnValue([{ text: '1', link: '/a/1', reuse: false }]); expect(srv.can(getSnapshot(1))).toBe(false); spy.and.returnValue([{ text: '2', link: '/a/2', reuse: true }]); expect(srv.can(getSnapshot(2))).toBe(true); }); }); describe('when ReuseTabMatchMode.URL', () => { beforeEach(() => (srv.mode = ReuseTabMatchMode.URL)); it('can hit because not exclude', () => { const snapshot = getSnapshot(1); expect(srv.can(snapshot)).toBe(true); }); it(`can't hit because exclude`, () => { srv.excludes = [/\/a\/0/i]; const snapshot = getSnapshot(0); expect(srv.can(snapshot)).toBe(false); }); }); }); describe('#debug', () => { beforeEach(() => { spyOn(console, 'warn'); genCached(1); }); it('with true', () => { srv.debug = true; srv.close('/a/1'); expect(console.warn).toHaveBeenCalled(); }); it('with false', () => { srv.debug = false; srv.close('/a/1'); expect(console.warn).not.toHaveBeenCalled(); }); }); describe('#excludes', () => { beforeEach(() => (srv.mode = ReuseTabMatchMode.URL)); it('can hit because not exclude', () => { srv.excludes = []; const snapshot = getSnapshot(1); expect(srv.can(snapshot)).toBe(true); }); it(`can't hit because exclude`, () => { srv.excludes = [/\/a\/0/i]; const snapshot = getSnapshot(0); expect(srv.can(snapshot)).toBe(false); expect(srv.excludes.length).toBe(1); }); }); it('basic methods', () => { let count = 5; genCached(count); // index expect(srv.index('/a/1')).toBe(0, `'index' muse be contain '/a/1'`); expect(srv.index('/a/b')).toBe(-1, `'index' muse be not contain '/a/b'`); // exists expect(srv.exists('/a/1')).toBe(true, `'exists' muse be contain '/a/1'`); expect(srv.exists('/a/b')).toBe(false, `'exists' muse be not contain '/a/b'`); // get expect(srv.get('/a/1')).not.toBeNull(`'get' muse be return cache data`); expect(srv.get('/a/b')).toBeNull(`'get' muse be return null`); expect(srv.get()).toBeNull(`'get' muse be return null if null`); // remove srv.close('/a/1'); --count; expect(srv.count).toBe(count, `'remove' muse be return ${count} when has removed`); srv.close('/a/b'); expect(srv.count).toBe(count, `'remove' muse be return ${count} when invalid url`); // items expect(srv.items.length).toBe(count, `'items' muse be return ${count} length`); // count expect(srv.count).toBe(count, `'count' muse be return ${count}`); // clear srv.clear(); expect(srv.count).toBe(0, `'clear' muse be return 0`); }); describe('#title', () => { it('should reset title via service', () => { genCached(1, '/'); expect(srv.getTitle('/').text).toBe('标题'); srv.title = 'a'; expect(srv.getTitle('/').text).toBe('a'); }); it('should get title from route data', () => { const title = 'aa'; expect(srv.getTitle('/', { data: { title } } as any).text).toBe(title); }); it('should used ReuseTitle set title via service', () => { genCached(1, '/'); expect(srv.getTitle('/').text).toBe('标题'); srv.title = { text: 'a' } as ReuseTitle; expect(srv.getTitle('/').text).toBe('a'); }); it('should use url as title when can be no found title', () => { const url = '/a/0'; expect(srv.getTitle(url).text).toBe(url); }); it('#clearTitleCached', () => { srv.clearTitleCached(); expect(true).toBe(true); }); }); describe('#closable', () => { it('should reset closable via service', () => { genCached(1, '/'); expect(srv.getClosable('/')).toBe(true); srv.closable = false; expect(srv.getClosable('/')).toBe(false); }); it('should get closable from route data', () => { const closable = false; expect(srv.getClosable('/', { data: { reuseClosable: closable } } as any)).toBe(closable); }); it('should get closable from menu data', () => { const spy = spyOn(menuSrv, 'getPathByUrl'); spy.and.returnValue([{ text: '1', link: '/1', reuseClosable: false }]); expect(srv.getClosable('/1')).toBe(false); spy.and.returnValue([{ text: '2', link: '/2', reuseClosable: true }]); expect(srv.getClosable('/2')).toBe(true); }); it('should use url as title when can be no found title', () => { const url = '/a/0'; expect(srv.getClosable(url)).toBe(true); }); it('should be keep closable: false when cleared', () => { genCached(5); srv.items[0].closable = false; expect(srv.count).toBe(5); srv.clear(); expect(srv.count).toBe(1); }); it('#clearClosableCached', () => { srv.clearClosableCached(); expect(true).toBe(true); }); it('should be forced clear even though closable: false', () => { genCached(5); srv.items[0].closable = false; expect(srv.count).toBe(5); srv.clear(true); expect(srv.count).toBe(0); }); }); describe('#remove', () => { const INITCOUNT = 2; beforeEach(() => genCached(INITCOUNT)); it('should be remove a route', () => { expect(srv.count).toBe(INITCOUNT); srv.close('/a/1'); expect(srv.count).toBe(INITCOUNT - 1); }); it('should be forced remove even though closable: false', () => { expect(srv.count).toBe(INITCOUNT); srv.items[0].closable = false; srv.close('/a/1', true); expect(srv.count).toBe(INITCOUNT - 1); }); it('should be remove a non-exists page', () => { expect(srv.count).toBe(INITCOUNT); srv.close('/a/10'); expect(srv.count).toBe(INITCOUNT); }); it('should be destroy component instance when remove a page', () => { const instance = { componentRef: { destroy: jasmine.createSpy('destroy'), }, }; srv.store(getSnapshot(3), instance); srv.close('/a/3'); expect(instance.componentRef.destroy).toHaveBeenCalled(); }); it('should be close right', () => { genCached(10); srv.closeRight('/a/5'); expect(srv.count).toBe(5); expect(srv.items[srv.count - 1].url).toBe('/a/5'); }); it('should forced close right even though closable: false', () => { genCached(10); srv.items[7].closable = false; srv.items[8].closable = false; srv.closeRight('/a/5', true); expect(srv.count).toBe(5); expect(srv.items[srv.count - 1].url).toBe('/a/5'); }); }); describe('#move', () => { const INITCOUNT = 5; beforeEach(() => genCached(INITCOUNT)); it('should be [/a/1] from 0 to 2', () => { const source = '/a/1'; const target = '/a/3'; expect(srv.items[0].url).toBe(source); expect(srv.items[2].url).toBe(target); srv.move(source, 2); expect(srv.items[0].url).toBe('/a/2'); expect(srv.items[1].url).toBe(target); expect(srv.items[2].url).toBe(source); }); it('should be [/a/1] from 0 to -1', () => { const source = '/a/1'; const target = '/a/5'; expect(srv.items[0].url).toBe(source); expect(srv.items[INITCOUNT - 1].url).toBe(target); srv.move(source, -1); expect(srv.items[0].url).toBe('/a/2'); expect(srv.items[INITCOUNT - 2].url).toBe(target); expect(srv.items[INITCOUNT - 1].url).toBe(source); }); it('should be move a invalid url', () => { const source = '/a/1'; const target = '/a/3'; const invalidUrl = '/a/a'; expect(srv.items[0].url).toBe(source); expect(srv.items[2].url).toBe(target); srv.move(invalidUrl, 2); expect(srv.items[0].url).toBe(source); expect(srv.items[2].url).toBe(target); }); }); it('#refresh', () => { const _$ = srv.change.pipe(filter(w => w !== null)).subscribe( res => { expect(res!.active).toBe('refresh'); _$.unsubscribe(); }, () => expect(false).toBe(true), ); srv.refresh(true); }); describe('#replace', () => { it('should be navigate to new url', () => { expect(router.navigateByUrl).not.toHaveBeenCalled(); srv.replace('/a/1'); expect(router.navigateByUrl).toHaveBeenCalled(); }); it('should be closed current router after navigate to new url', () => { genCached(1, ''); expect(router.navigateByUrl).not.toHaveBeenCalled(); srv.replace('/b'); expect(router.navigateByUrl).toHaveBeenCalled(); }); }); describe('#keepingScroll', () => { it('should get keepingScroll from service', () => { srv.keepingScroll = true; expect(srv.getKeepingScroll('')).toBe(true); srv.keepingScroll = false; expect(srv.getKeepingScroll('')).toBe(false); }); it('should get keepingScroll from route data', () => { expect(srv.getKeepingScroll('/', { data: { keepingScroll: false } } as any)).toBe(false); expect(srv.getKeepingScroll('/', { data: { keepingScroll: true } } as any)).toBe(true); }); it('should get keepingScroll from menu data', () => { const spy = spyOn(menuSrv, 'getPathByUrl'); spy.and.returnValue([{ text: '1', link: '/1', keepingScroll: false }]); expect(srv.getKeepingScroll('/1')).toBe(false); spy.and.returnValue([{ text: '2', link: '/2', keepingScroll: true }]); expect(srv.getKeepingScroll('/2')).toBe(true); }); }); }); describe('##boundary##', () => { it(`can't neve hit without MenuService in ReuseTabMatchMode.Menu mode`, () => { genModule([]); srv.mode = ReuseTabMatchMode.Menu; expect(srv.can(getSnapshot(1))).toBe(false); }); }); describe('#shouldDetach', () => { beforeEach(() => { genModule(); srv.mode = ReuseTabMatchMode.URL; }); it(`can hit when target route`, () => { expect(srv.shouldDetach(getSnapshot(0))).toBe(true); }); it(`can't hit when is child route`, () => { const snapshot = getSnapshot(0); snapshot.routeConfig.children = []; expect(srv.shouldDetach(snapshot)).toBe(false); }); it(`can't hit when is lazy loading route`, () => { const snapshot = getSnapshot(0); snapshot.routeConfig.loadChildren = {}; expect(srv.shouldDetach(snapshot)).toBe(false); }); it(`should route data first`, () => { const snapshotTrue = getSnapshot(0); snapshotTrue.data = { reuse: true, }; expect(srv.shouldDetach(snapshotTrue)).toBe(true); const snapshotFalse = getSnapshot(0); snapshotFalse.data = { reuse: false, }; expect(srv.shouldDetach(snapshotFalse)).toBe(false); }); it(`can't hit when remove current page`, () => { const snapshot = getSnapshot(1); expect(srv.shouldDetach(snapshot)).toBe(true); srv.store(snapshot, {}); srv.close(srv.getUrl(snapshot)); expect(srv.shouldDetach(snapshot)).toBe(false); }); }); describe('#store', () => { beforeEach(() => { genModule(); srv.mode = ReuseTabMatchMode.URL; genCached(2); }); it(`should be store a new route`, () => { expect(srv.count).toBe(2); srv.store(getSnapshot(3), {}); expect(srv.count).toBe(3); }); it(`should be store a exists route`, () => { expect(srv.count).toBe(2); srv.store(getSnapshot(1), {}); expect(srv.count).toBe(2); }); it(`should be store a route when out of cache count`, () => { srv.max = 2; expect(srv.count).toBe(2); srv.store(getSnapshot(3), { componentRef: {} }); expect(srv.count).toBe(2); }); it(`should be run _onReuseDestroy event hook`, () => { const handle = { componentRef: { instance: { _onReuseDestroy: jasmine.createSpy('_onReuseDestroy'), }, }, }; srv.store(getSnapshot(3), handle); expect(handle.componentRef.instance._onReuseDestroy).toHaveBeenCalled(); }); }); describe('#shouldAttach', () => { beforeEach(() => { genModule(); srv.mode = ReuseTabMatchMode.URL; }); it('should be attached when is cached', () => { genCached(1); expect(srv.shouldAttach(getSnapshot(1))).toBe(true); expect(srv.shouldAttach(getSnapshot(2))).toBe(false); }); it('should be not attach when not cached', () => { expect(srv.shouldAttach(getSnapshot(1))).toBe(false); expect(srv.shouldAttach(getSnapshot(2))).toBe(false); }); }); describe('#retrieve', () => { beforeEach(() => { genModule(); srv.mode = ReuseTabMatchMode.URL; }); it('should be retrieve when is cached', () => { genCached(1); const handle: any = srv.retrieve(getSnapshot(1)); expect(handle).not.toBeNull(); expect(handle.a).toBe(1); }); it('should be not retrieve when not cached', () => { expect(srv.retrieve(getSnapshot(2))).toBeNull(); }); it(`can't retrieve when is lazy loading route`, () => { const snapshot = getSnapshot(0); snapshot.routeConfig.loadChildren = {}; expect(srv.retrieve(getSnapshot(2))).toBeNull(); }); it(`should be run _onReuseInit event hook`, () => { const handle = { componentRef: { instance: { _onReuseInit: jasmine.createSpy('_onReuseInit'), }, }, }; const snapshot = getSnapshot(3); // handle srv.store(snapshot, handle); srv.shouldAttach(snapshot); expect(handle.componentRef.instance._onReuseInit).toHaveBeenCalled(); }); }); });