import { PageManager } from '../PageManager'; import { BehaviorReporter } from '..'; import { storage, UUID_REGEX } from '../../../../tests/shared'; /** 不从 utils 导入,以在修改默认值时被测试用例拦截,修改者需要确认默认值修改后对既有项目的影响,再修改测试用例中的值 */ const PAGE_KEY = '__ml::page'; const getInitialedBehaviorReporter = (): BehaviorReporter => { const reporter = new BehaviorReporter(); reporter.init({ user: { uin: 9999, }, pageStackStorageLimit: 5, }); return reporter; }; describe('PageManager', () => { beforeEach(() => { localStorage.clear(); }); afterAll(() => { localStorage.clear(); }); it('push correctly', () => { const reporter = new BehaviorReporter() as any; const { pageManager } = reporter; pageManager.push({ name: 'Nezuko', extInfo: { is: 'cute', }, }); expect(pageManager.currentPage).toStrictEqual({ name: 'Nezuko', accessId: expect.stringMatching(UUID_REGEX), step: 1, extInfo: { is: 'cute', }, }); expect(storage.getLocal(`${PAGE_KEY}_${pageManager.contextId}`)).toStrictEqual({ name: 'Nezuko', accessId: pageManager.currentPage?.accessId, step: 1, // extInfo: { // is: 'cute', // }, }); const oldAccessId = pageManager.currentPage?.accessId; pageManager.push( { name: 'Tanjiro', }, 'brother', ); expect(pageManager.currentPage?.accessId).not.toBe(oldAccessId); expect(pageManager.currentPage).toStrictEqual({ name: 'Tanjiro', accessId: expect.stringMatching(UUID_REGEX), step: 2, refAccessId: oldAccessId, fromElementId: 'brother', refPageName: 'Nezuko', extInfo: undefined, }); expect(storage.getLocal(`${PAGE_KEY}_${pageManager.contextId}`)).toStrictEqual({ name: 'Tanjiro', accessId: pageManager.currentPage?.accessId, step: 2, refAccessId: oldAccessId, fromElementId: 'brother', refPageName: 'Nezuko', }); }); it('getPageInfo and updatePageInfo correctly', () => { const reporter = new BehaviorReporter() as any; reporter.setOptions({ errorHandler: (e: any) => { throw e; }, }); const { pageManager } = reporter; expect(() => pageManager.getPageInfo()).toThrow(); expect(() => pageManager.updatePageInfo()).toThrow(); pageManager.push({ name: 'Nezuko', extInfo: { is: 'cute', }, }); expect(pageManager.getPageInfo()).toStrictEqual({ pageName: 'Nezuko', refPageName: undefined, accessId: expect.stringMatching(UUID_REGEX), refAccessId: undefined, fromElementId: undefined, step: 1, extInfo: { is: 'cute', }, }); const oldAccessId = pageManager.currentPage?.accessId; pageManager.push( { name: 'Tranjiro', }, 'brother', ); expect(pageManager.getPageInfo()).toEqual({ pageName: 'Tranjiro', refPageName: 'Nezuko', accessId: expect.stringMatching(UUID_REGEX), refAccessId: oldAccessId, fromElementId: 'brother', step: 2, extInfo: undefined, }); const curAccessId = pageManager.currentPage?.accessId; pageManager.updatePageInfo({ is: 'very cute', race: 'oni', }); expect(pageManager.getPageInfo()).toEqual({ pageName: 'Tranjiro', refPageName: 'Nezuko', accessId: curAccessId, refAccessId: oldAccessId, fromElementId: 'brother', step: 2, extInfo: { is: 'very cute', race: 'oni', }, }); pageManager.updatePageInfo(); expect(pageManager.getPageInfo()).toEqual({ pageName: 'Tranjiro', refPageName: 'Nezuko', accessId: curAccessId, refAccessId: oldAccessId, fromElementId: 'brother', step: 2, extInfo: undefined, }); }); it('delete old version page stack in storage correctly', () => { localStorage.setItem('__ml_storage__queue', '["asd","asdasd"]'); localStorage.setItem('asd__ml_storage__page', '{id:1}'); localStorage.setItem('asdasd2__ml_storage__page', '{id:2}'); expect(Object.keys(localStorage)).toHaveLength(3); new PageManager(new BehaviorReporter()); expect(Object.keys(localStorage)).toHaveLength(0); }); it('load undefined when there is no page in current stack', () => { expect(new PageManager(new BehaviorReporter()).load()).toBeUndefined(); }); it('will remove stack not in contextIdQueue when load', () => { storage.setLocal(PAGE_KEY, ['context_id1', 'context_id2']); storage.setLocal(`${PAGE_KEY}_context_id1`, { id: 1 }); storage.setLocal(`${PAGE_KEY}_context_id3`, { id: 3 }); expect(Object.keys(localStorage)).toEqual([PAGE_KEY, `${PAGE_KEY}_context_id1`, `${PAGE_KEY}_context_id3`]); new PageManager(new BehaviorReporter(), PAGE_KEY).load(); expect(Object.keys(localStorage)).toEqual([PAGE_KEY, `${PAGE_KEY}_context_id1`]); }); it('load stack top page of current contextId correctly', () => { history.pushState( {}, 'El Psy Congroo', 'http://localhost/home?a=1&context_id=el-psy-congroo&entrance_id=ei12312&sub_entranceid=se123&from_access_id=fa123&from_element_id=fe123', ); storage.setLocal(PAGE_KEY, ['el-psy-congroo']); // 是否需要考虑 storage 中被污染的情况,即取出的不是 PageItem?即便这应该属于 hack 的情况 const MOCK_PAGE = { name: 'new page', accessId: 'a5', step: 5, refAccessId: 'a4', refPageName: 'old page', fromElementId: 'ele1', extInfo: { id: 1, }, }; storage.setLocal(`${PAGE_KEY}_el-psy-congroo`, MOCK_PAGE); const pageManager = new PageManager(getInitialedBehaviorReporter(), PAGE_KEY); expect((pageManager as any).reporter.adapter?.linkedData.contextId).toBe('el-psy-congroo'); expect(pageManager.load()).toEqual(MOCK_PAGE); expect(pageManager.load()).toEqual(MOCK_PAGE); // load 应该是只读行为,多次读取结果不变 }); it('recycleStorage correctly', () => { const reporter = getInitialedBehaviorReporter(); const storageRemoveLocalSpy = jest.spyOn(reporter, 'removeStorageItem'); storage.setLocal(PAGE_KEY, ['c1', 'c2', 'c3']); storage.setLocal(`${PAGE_KEY}_c1`, { id: 1 }); storage.setLocal(`${PAGE_KEY}_c2`, { id: 2 }); storage.setLocal(`${PAGE_KEY}_c3`, { id: 3 }); const pageManager = new PageManager(reporter, PAGE_KEY) as any; // contextIdQueue.length <= remainCount, nothing will happen expect(pageManager.recycleStorage([], 5)).toEqual([]); expect(pageManager.recycleStorage([1, 2, 3, 4], 5)).toEqual([1, 2, 3, 4]); expect(pageManager.recycleStorage([1, 2, 3, 4, 5], 5)).toEqual([1, 2, 3, 4, 5]); expect(pageManager.recycleStorage([], 0)).toEqual([]); expect(storageRemoveLocalSpy).not.toHaveBeenCalled(); // contextIdQueue.length > remainCount, will remove items until remains remainCount expect(pageManager.recycleStorage([1, 2, 3, 4, 5], 3)).toEqual([1, 2, 3]); expect(storageRemoveLocalSpy).toHaveBeenCalledTimes(2); expect(pageManager.recycleStorage(['c1', 'c2', 'c3'], 1)).toEqual(['c1']); expect(storageRemoveLocalSpy).toHaveBeenCalledTimes(4); expect(Object.keys(localStorage).filter((k) => k.startsWith(PAGE_KEY))).toEqual([PAGE_KEY, `${PAGE_KEY}_c1`]); }); it('save correctly when there is nothing in storage', () => { history.pushState( {}, 'El Psy Congroo', 'http://localhost/home?a=1&context_id=el-psy-congroo&entrance_id=ei12312&sub_entranceid=se123&from_access_id=fa123&from_element_id=fe123', ); const reporter = getInitialedBehaviorReporter(); const pageManager = new PageManager(reporter, PAGE_KEY); pageManager.currentPage = { name: 'Makise Kurisu', accessId: 'a1', step: 1, refAccessId: undefined, fromElementId: undefined, extInfo: undefined, }; (pageManager as any).save(); expect(storage.getLocal(PAGE_KEY)).toEqual(['el-psy-congroo']); expect(storage.getLocal(`${PAGE_KEY}_el-psy-congroo`)).toEqual({ name: 'Makise Kurisu', accessId: 'a1', step: 1, }); }); it('save correctly when there is an exist page in current stack', () => { history.pushState( {}, 'El Psy Congroo', 'http://localhost/home?a=1&context_id=el-psy-congroo&entrance_id=ei12312&sub_entranceid=se123&from_access_id=fa123&from_element_id=fe123', ); const reporter = getInitialedBehaviorReporter(); const pageManager = new PageManager(reporter, PAGE_KEY); pageManager.currentPage = { name: 'Makise Kurisu', accessId: 'a1', step: 1, refAccessId: undefined, fromElementId: undefined, extInfo: undefined, }; (pageManager as any).save(); pageManager.currentPage = { name: 'Hooin Kyoma', accessId: 'a2', step: 2, refAccessId: 'a1', fromElementId: undefined, extInfo: undefined, }; (pageManager as any).save(); expect(storage.getLocal(PAGE_KEY)).toEqual(['el-psy-congroo']); expect(storage.getLocal(`${PAGE_KEY}_el-psy-congroo`)).toEqual({ name: 'Hooin Kyoma', accessId: 'a2', step: 2, refAccessId: 'a1', }); }); test('contextIdQueue works ok', () => { const reporter = getInitialedBehaviorReporter(); const pageManager = new PageManager(reporter, PAGE_KEY); const goToNewPage = (contextId: string, accessId: string) => { history.pushState({}, 'El Psy Congroo', `http://localhost/home?a=1&context_id=${contextId}`); reporter.readLinkedData(); pageManager.currentPage = { name: 'Makise Kurisu', accessId, step: 1, refAccessId: undefined, fromElementId: undefined, extInfo: { a: 1, }, }; (pageManager as any).save(); }; goToNewPage('c1', 'a1'); expect(storage.getLocal(PAGE_KEY)).toEqual(['c1']); expect(storage.getLocal(`${PAGE_KEY}_c1`)).toEqual({ name: 'Makise Kurisu', accessId: 'a1', step: 1, }); goToNewPage('c1', 'a2'); expect(storage.getLocal(PAGE_KEY)).toEqual(['c1']); expect(storage.getLocal(`${PAGE_KEY}_c1`)).toEqual({ name: 'Makise Kurisu', accessId: 'a2', step: 1, }); goToNewPage('c2', 'a1'); expect(storage.getLocal(PAGE_KEY)).toEqual(['c1', 'c2']); expect(storage.getLocal(`${PAGE_KEY}_c2`)).toEqual({ name: 'Makise Kurisu', accessId: 'a1', step: 1, }); goToNewPage('c3', 'a1'); expect(storage.getLocal(PAGE_KEY)).toEqual(['c1', 'c2', 'c3']); expect(storage.getLocal(`${PAGE_KEY}_c3`)).toEqual({ name: 'Makise Kurisu', accessId: 'a1', step: 1, }); goToNewPage('c1', 'a3'); expect(storage.getLocal(PAGE_KEY)).toEqual(['c2', 'c3', 'c1']); expect(storage.getLocal(`${PAGE_KEY}_c1`)).toEqual({ name: 'Makise Kurisu', accessId: 'a3', step: 1, }); goToNewPage('c4', 'a1'); expect(storage.getLocal(PAGE_KEY)).toEqual(['c2', 'c3', 'c1', 'c4']); goToNewPage('c5', 'a1'); expect(storage.getLocal(PAGE_KEY)).toEqual(['c2', 'c3', 'c1', 'c4', 'c5']); goToNewPage('c6', 'a1'); expect(storage.getLocal(PAGE_KEY)).toEqual(['c3', 'c1', 'c4', 'c5', 'c6']); // c2 is removed expect(storage.getLocal(`${PAGE_KEY}_c2`)).toBeUndefined(); expect(new Set(Object.keys(localStorage).filter((k) => k.startsWith(PAGE_KEY)))).toEqual( new Set([PAGE_KEY, `${PAGE_KEY}_c3`, `${PAGE_KEY}_c1`, `${PAGE_KEY}_c4`, `${PAGE_KEY}_c5`, `${PAGE_KEY}_c6`]), ); }); });