This is a fairly non-trivial change in terms of consequence, so a greater than average chance something breaks. This is necessary since some cameras (e.g. Reolink) are materially slower to fetch media, and this change substantially improves card responsiveness.
722 lines
24 KiB
TypeScript
722 lines
24 KiB
TypeScript
import { ViewContext } from 'view';
|
|
import { describe, expect, it, vi } from 'vitest';
|
|
import { mock } from 'vitest-mock-extended';
|
|
import { ViewFactory } from '../../../src/card-controller/view/factory';
|
|
import { ViewManager } from '../../../src/card-controller/view/view-manager';
|
|
import { FrigateCardView } from '../../../src/config/types';
|
|
import { ViewMedia } from '../../../src/view/media';
|
|
import { MediaQueriesResults } from '../../../src/view/media-queries-results';
|
|
import {
|
|
createCameraManager,
|
|
createCapabilities,
|
|
createCardAPI,
|
|
createStore,
|
|
createView,
|
|
} from '../../test-utils';
|
|
import { ViewQueryExecutor } from '../../../src/card-controller/view/view-query-executor';
|
|
import { EventMediaQueries } from '../../../src/view/media-queries';
|
|
import { QueryType } from '../../../src/camera-manager/types';
|
|
import { SetQueryViewModifier } from '../../../src/card-controller/view/modifiers/set-query';
|
|
import { View } from '../../../src/view/view';
|
|
import {
|
|
QueryExecutorOptions,
|
|
ViewModifier,
|
|
} from '../../../src/card-controller/view/types';
|
|
|
|
describe('should act correctly when view is set', () => {
|
|
it('basic view', () => {
|
|
const view = createView({
|
|
view: 'live',
|
|
camera: 'camera',
|
|
displayMode: 'grid',
|
|
});
|
|
|
|
const factory = mock<ViewFactory>();
|
|
factory.getViewDefault.mockReturnValue(view);
|
|
|
|
const api = createCardAPI();
|
|
const manager = new ViewManager(api, { viewFactory: factory });
|
|
|
|
manager.setViewDefault();
|
|
|
|
expect(manager.getView()).toBe(view);
|
|
expect(manager.hasView()).toBeTruthy();
|
|
expect(api.getMediaLoadedInfoManager().clear).toBeCalled();
|
|
expect(api.getCardElementManager().scrollReset).toBeCalled();
|
|
expect(api.getMessageManager().reset).toBeCalled();
|
|
expect(api.getStyleManager().setExpandedMode).toBeCalled();
|
|
expect(api.getConditionsManager()?.setState).toBeCalledWith({
|
|
view: 'live',
|
|
camera: 'camera',
|
|
displayMode: 'grid',
|
|
});
|
|
expect(api.getCardElementManager().update).toBeCalled();
|
|
});
|
|
|
|
it('view with minor changes without media clearing or scroll', () => {
|
|
const view_1 = createView({
|
|
view: 'live',
|
|
camera: 'camera',
|
|
});
|
|
const factory = mock<ViewFactory>();
|
|
factory.getViewDefault.mockReturnValue(view_1);
|
|
|
|
const api = createCardAPI();
|
|
const manager = new ViewManager(api, { viewFactory: factory });
|
|
|
|
manager.setViewDefault();
|
|
|
|
vi.mocked(api.getMediaLoadedInfoManager().clear).mockClear();
|
|
vi.mocked(api.getCardElementManager().scrollReset).mockClear();
|
|
|
|
const view_2 = createView({
|
|
view: 'live',
|
|
camera: 'camera',
|
|
displayMode: 'single',
|
|
});
|
|
factory.getViewDefault.mockReturnValue(view_2);
|
|
|
|
manager.setViewDefault();
|
|
|
|
expect(manager.getView()).toBe(view_2);
|
|
|
|
// The new view is neither a major media change, nor a different view name,
|
|
// so media clearing and scrolling should not happen.
|
|
expect(api.getMediaLoadedInfoManager().clear).not.toBeCalled();
|
|
expect(api.getCardElementManager().scrollReset).not.toBeCalled();
|
|
});
|
|
});
|
|
|
|
it('setViewWithMergedContext', () => {
|
|
const api = createCardAPI();
|
|
const factory = mock<ViewFactory>();
|
|
|
|
const manager = new ViewManager(api, { viewFactory: factory });
|
|
const context: ViewContext = { timeline: {} };
|
|
|
|
// Setting context with no existing view does nothing.
|
|
manager.setViewWithMergedContext(context);
|
|
expect(manager.getView()).toBeNull();
|
|
|
|
const view = createView({
|
|
view: 'live',
|
|
camera: 'camera',
|
|
});
|
|
factory.getViewDefault.mockReturnValue(view);
|
|
manager.setViewDefault();
|
|
manager.setViewWithMergedContext(context);
|
|
|
|
expect(manager.getView()?.camera).toBe('camera');
|
|
expect(manager.getView()?.view).toBe('live');
|
|
expect(manager.getView()?.context).toEqual(context);
|
|
});
|
|
|
|
it('getEpoch', () => {
|
|
const factory = mock<ViewFactory>();
|
|
const manager = new ViewManager(createCardAPI(), { viewFactory: factory });
|
|
expect(manager.getEpoch()).toBeTruthy();
|
|
expect(manager.getEpoch().manager).toBe(manager);
|
|
});
|
|
|
|
it('reset', () => {
|
|
const factory = mock<ViewFactory>();
|
|
const manager = new ViewManager(createCardAPI(), { viewFactory: factory });
|
|
|
|
manager.reset();
|
|
expect(manager.getView()).toBeNull();
|
|
expect(manager.hasView()).toBeFalsy();
|
|
|
|
factory.getViewDefault.mockReturnValue(createView());
|
|
manager.setViewDefault();
|
|
|
|
expect(manager.getView()).not.toBeNull();
|
|
expect(manager.hasView()).toBeTruthy();
|
|
|
|
manager.reset();
|
|
|
|
expect(manager.getView()).toBeNull();
|
|
expect(manager.hasView()).toBeFalsy();
|
|
});
|
|
|
|
it('setViewDefault', () => {
|
|
const factory = mock<ViewFactory>();
|
|
factory.getViewDefault.mockReturnValue(createView());
|
|
|
|
const manager = new ViewManager(createCardAPI(), { viewFactory: factory });
|
|
manager.setViewDefault();
|
|
|
|
expect(manager.getView()?.view).toBe('live');
|
|
expect(manager.getView()?.camera).toBe('camera');
|
|
});
|
|
|
|
it('setViewByParameters', () => {
|
|
const factory = mock<ViewFactory>();
|
|
factory.getViewByParameters.mockReturnValue(createView());
|
|
|
|
const manager = new ViewManager(createCardAPI(), { viewFactory: factory });
|
|
manager.setViewByParameters();
|
|
|
|
expect(manager.getView()?.view).toBe('live');
|
|
expect(manager.getView()?.camera).toBe('camera');
|
|
});
|
|
|
|
it('setViewDefaultWithNewQuery', async () => {
|
|
const viewFactory = mock<ViewFactory>();
|
|
viewFactory.getViewDefault.mockReturnValue(createView());
|
|
|
|
const viewQueryExecutor = mock<ViewQueryExecutor>();
|
|
viewQueryExecutor.getNewQueryModifiers.mockResolvedValue([]);
|
|
|
|
const manager = new ViewManager(createCardAPI(), {
|
|
viewFactory: viewFactory,
|
|
viewQueryExecutor: viewQueryExecutor,
|
|
});
|
|
await manager.setViewDefaultWithNewQuery();
|
|
|
|
expect(manager.getView()?.view).toBe('live');
|
|
expect(manager.getView()?.camera).toBe('camera');
|
|
});
|
|
|
|
it('setViewByParametersWithNewQuery', async () => {
|
|
const viewFactory = mock<ViewFactory>();
|
|
viewFactory.getViewByParameters.mockReturnValue(createView());
|
|
|
|
const viewQueryExecutor = mock<ViewQueryExecutor>();
|
|
viewQueryExecutor.getNewQueryModifiers.mockResolvedValue([]);
|
|
|
|
const manager = new ViewManager(createCardAPI(), {
|
|
viewFactory: viewFactory,
|
|
viewQueryExecutor: viewQueryExecutor,
|
|
});
|
|
await manager.setViewByParametersWithNewQuery();
|
|
|
|
expect(manager.getView()?.view).toBe('live');
|
|
expect(manager.getView()?.camera).toBe('camera');
|
|
});
|
|
|
|
it('setViewByParametersWithExistingQuery', async () => {
|
|
const viewFactory = mock<ViewFactory>();
|
|
viewFactory.getViewByParameters.mockReturnValue(createView());
|
|
|
|
const viewQueryExecutor = mock<ViewQueryExecutor>();
|
|
viewQueryExecutor.getNewQueryModifiers.mockResolvedValue([]);
|
|
|
|
const manager = new ViewManager(createCardAPI(), {
|
|
viewFactory: viewFactory,
|
|
viewQueryExecutor: viewQueryExecutor,
|
|
});
|
|
|
|
await manager.setViewByParametersWithExistingQuery();
|
|
|
|
expect(manager.getView()?.view).toBe('live');
|
|
expect(manager.getView()?.camera).toBe('camera');
|
|
});
|
|
|
|
describe('should handle exceptions', () => {
|
|
it('should handle exceptions in sync calls', () => {
|
|
const error = new Error();
|
|
const viewFactory = mock<ViewFactory>();
|
|
viewFactory.getViewDefault.mockImplementation(() => {
|
|
throw error;
|
|
});
|
|
|
|
const api = createCardAPI();
|
|
const manager = new ViewManager(api, { viewFactory: viewFactory });
|
|
manager.setViewDefault();
|
|
|
|
expect(manager.hasView()).toBeFalsy();
|
|
expect(api.getMessageManager().setErrorIfHigherPriority).toBeCalledWith(error);
|
|
});
|
|
|
|
it('should handle viewFactory exceptions in async calls', async () => {
|
|
const error = new Error();
|
|
const viewFactory = mock<ViewFactory>();
|
|
viewFactory.getViewDefault.mockImplementation(() => {
|
|
throw error;
|
|
});
|
|
|
|
const api = createCardAPI();
|
|
const manager = new ViewManager(api, { viewFactory: viewFactory });
|
|
await manager.setViewDefaultWithNewQuery();
|
|
|
|
expect(manager.hasView()).toBeFalsy();
|
|
expect(api.getMessageManager().setErrorIfHigherPriority).toBeCalledWith(error);
|
|
});
|
|
|
|
it('should handle viewQueryExecutor exceptions in async calls', async () => {
|
|
const error = new Error();
|
|
const viewFactory = mock<ViewFactory>();
|
|
viewFactory.getViewDefault.mockReturnValue(createView());
|
|
const viewQueryExecutor = mock<ViewQueryExecutor>();
|
|
viewQueryExecutor.getNewQueryModifiers.mockRejectedValue(error);
|
|
|
|
const api = createCardAPI();
|
|
const manager = new ViewManager(api, {
|
|
viewFactory: viewFactory,
|
|
viewQueryExecutor: viewQueryExecutor,
|
|
});
|
|
|
|
await manager.setViewDefaultWithNewQuery();
|
|
|
|
// The initial view will have been set.
|
|
expect(manager.hasView()).toBeTruthy();
|
|
|
|
// But an error will also be generated.
|
|
expect(api.getMessageManager().setErrorIfHigherPriority).toBeCalledWith(error);
|
|
});
|
|
});
|
|
|
|
describe('isViewSupportedByCamera', () => {
|
|
it.each([
|
|
['live' as const, false],
|
|
['image' as const, true],
|
|
['diagnostics' as const, true],
|
|
['clip' as const, false],
|
|
['clips' as const, false],
|
|
['snapshot' as const, false],
|
|
['snapshots' as const, false],
|
|
['recording' as const, false],
|
|
['recordings' as const, false],
|
|
['timeline' as const, false],
|
|
['media' as const, false],
|
|
])('%s', (viewName: FrigateCardView, expected: boolean) => {
|
|
const api = createCardAPI();
|
|
vi.mocked(api.getCameraManager).mockReturnValue(createCameraManager());
|
|
vi.mocked(api.getCameraManager().getStore).mockReturnValue(
|
|
createStore([
|
|
{
|
|
cameraID: 'camera.kitchen',
|
|
capabilities: createCapabilities({
|
|
live: false,
|
|
'favorite-events': false,
|
|
'favorite-recordings': false,
|
|
seek: false,
|
|
clips: false,
|
|
recordings: false,
|
|
snapshots: false,
|
|
}),
|
|
},
|
|
]),
|
|
);
|
|
const manager = new ViewManager(api);
|
|
|
|
expect(manager.isViewSupportedByCamera('camera', viewName)).toBe(expected);
|
|
});
|
|
});
|
|
|
|
describe('hasMajorMediaChange', () => {
|
|
it('should consider undefined views as major', () => {
|
|
const manager = new ViewManager(createCardAPI());
|
|
|
|
expect(manager.hasMajorMediaChange(undefined)).toBeFalsy();
|
|
expect(manager.hasMajorMediaChange(createView())).toBeTruthy();
|
|
expect(manager.hasMajorMediaChange()).toBeFalsy();
|
|
});
|
|
|
|
it('should consider view change as major', () => {
|
|
const factory = mock<ViewFactory>();
|
|
factory.getViewDefault.mockReturnValue(createView({ view: 'live' }));
|
|
|
|
const manager = new ViewManager(createCardAPI(), { viewFactory: factory });
|
|
manager.setViewDefault();
|
|
|
|
expect(manager.hasMajorMediaChange(createView({ view: 'clips' }))).toBeTruthy();
|
|
});
|
|
|
|
it('should consider camera change as major', () => {
|
|
const factory = mock<ViewFactory>();
|
|
factory.getViewDefault.mockReturnValue(createView({ camera: 'camera-1' }));
|
|
|
|
const manager = new ViewManager(createCardAPI(), { viewFactory: factory });
|
|
manager.setViewDefault();
|
|
|
|
expect(manager.hasMajorMediaChange(createView({ camera: 'camera-2' }))).toBeTruthy();
|
|
});
|
|
|
|
it('should consider live substream change as major in live view', () => {
|
|
const overrides_1: Map<string, string> = new Map();
|
|
overrides_1.set('camera', 'camera-2');
|
|
|
|
const overrides_2: Map<string, string> = new Map();
|
|
overrides_2.set('camera', 'camera-3');
|
|
|
|
const factory = mock<ViewFactory>();
|
|
factory.getViewDefault.mockReturnValue(
|
|
createView({ context: { live: { overrides: overrides_1 } } }),
|
|
);
|
|
|
|
const manager = new ViewManager(createCardAPI(), { viewFactory: factory });
|
|
manager.setViewDefault();
|
|
|
|
expect(
|
|
manager.hasMajorMediaChange(
|
|
createView({ context: { live: { overrides: overrides_2 } } }),
|
|
),
|
|
).toBeTruthy();
|
|
});
|
|
|
|
it('should not consider live substream change as major in other view', () => {
|
|
const overrides_1: Map<string, string> = new Map();
|
|
overrides_1.set('camera', 'camera-2');
|
|
|
|
const overrides_2: Map<string, string> = new Map();
|
|
overrides_2.set('camera', 'camera-3');
|
|
|
|
const factory = mock<ViewFactory>();
|
|
factory.getViewDefault.mockReturnValue(
|
|
createView({ view: 'clips', context: { live: { overrides: overrides_1 } } }),
|
|
);
|
|
|
|
const manager = new ViewManager(createCardAPI(), { viewFactory: factory });
|
|
manager.setViewDefault();
|
|
|
|
expect(
|
|
manager.hasMajorMediaChange(
|
|
createView({ view: 'clips', context: { live: { overrides: overrides_2 } } }),
|
|
),
|
|
).toBeFalsy();
|
|
});
|
|
|
|
it('should consider result change as major in other view', () => {
|
|
const media = [new ViewMedia('clip', 'camera-1'), new ViewMedia('clip', 'camera-2')];
|
|
const queryResults_1 = new MediaQueriesResults({ results: media, selectedIndex: 0 });
|
|
const queryResults_2 = new MediaQueriesResults({ results: media, selectedIndex: 1 });
|
|
|
|
const factory = mock<ViewFactory>();
|
|
factory.getViewDefault.mockReturnValue(
|
|
createView({ view: 'media', queryResults: queryResults_1 }),
|
|
);
|
|
|
|
const manager = new ViewManager(createCardAPI(), { viewFactory: factory });
|
|
manager.setViewDefault();
|
|
|
|
expect(
|
|
manager.hasMajorMediaChange(
|
|
createView({ view: 'media', queryResults: queryResults_2 }),
|
|
),
|
|
).toBeTruthy();
|
|
});
|
|
|
|
it('should not consider selected result change as major in live view', () => {
|
|
const media = [new ViewMedia('clip', 'camera-1'), new ViewMedia('clip', 'camera-2')];
|
|
const queryResults_1 = new MediaQueriesResults({ results: media, selectedIndex: 0 });
|
|
const queryResults_2 = new MediaQueriesResults({ results: media, selectedIndex: 1 });
|
|
|
|
const factory = mock<ViewFactory>();
|
|
factory.getViewDefault.mockReturnValue(createView({ queryResults: queryResults_1 }));
|
|
|
|
const manager = new ViewManager(createCardAPI(), { viewFactory: factory });
|
|
manager.setViewDefault();
|
|
|
|
expect(
|
|
manager.hasMajorMediaChange(createView({ queryResults: queryResults_2 })),
|
|
).toBeFalsy();
|
|
});
|
|
});
|
|
|
|
describe('should initialize', () => {
|
|
it('without querystring', async () => {
|
|
const view = createView({
|
|
view: 'live',
|
|
camera: 'camera',
|
|
});
|
|
|
|
const viewFactory = mock<ViewFactory>();
|
|
viewFactory.getViewDefault.mockReturnValue(view);
|
|
|
|
const api = createCardAPI();
|
|
const manager = new ViewManager(api, {
|
|
viewFactory: viewFactory,
|
|
});
|
|
|
|
expect(await manager.initialize()).toBeTruthy();
|
|
|
|
expect(manager.getView()).toBe(view);
|
|
});
|
|
|
|
it('with querystring', async () => {
|
|
const api = createCardAPI();
|
|
const factory = mock<ViewFactory>();
|
|
const manager = new ViewManager(api, { viewFactory: factory });
|
|
vi.mocked(api.getQueryStringManager().hasViewRelatedActionsToRun).mockReturnValue(
|
|
true,
|
|
);
|
|
|
|
expect(await manager.initialize()).toBeTruthy();
|
|
|
|
expect(manager.hasView()).toBeFalsy();
|
|
});
|
|
});
|
|
|
|
it('should adopt query and results when changing to gallery from viewer', async () => {
|
|
const baseView = createView({
|
|
view: 'media',
|
|
camera: 'camera.office',
|
|
query: new EventMediaQueries([
|
|
{
|
|
type: QueryType.Event,
|
|
cameraIDs: new Set(['camera.office']),
|
|
hasClip: true,
|
|
},
|
|
]),
|
|
queryResults: new MediaQueriesResults(),
|
|
});
|
|
|
|
const viewFactory = mock<ViewFactory>();
|
|
viewFactory.getViewDefault
|
|
.mockReturnValueOnce(baseView)
|
|
.mockReturnValueOnce(createView({ view: 'clips' }));
|
|
|
|
const viewQueryExecutor = mock<ViewQueryExecutor>();
|
|
viewQueryExecutor.getNewQueryModifiers.mockResolvedValue([]);
|
|
|
|
const manager = new ViewManager(createCardAPI(), {
|
|
viewFactory: viewFactory,
|
|
viewQueryExecutor: viewQueryExecutor,
|
|
});
|
|
|
|
manager.setViewDefault();
|
|
expect(manager.getView()?.is('media')).toBeTruthy();
|
|
|
|
await manager.setViewDefaultWithNewQuery({
|
|
params: {
|
|
view: 'clips',
|
|
},
|
|
});
|
|
|
|
expect(manager.getView()?.is('clips')).toBeTruthy();
|
|
expect(manager.getView()?.query).toBe(baseView.query);
|
|
expect(manager.getView()?.queryResults).toBe(baseView.queryResults);
|
|
expect(viewQueryExecutor.getNewQueryModifiers).not.toHaveBeenCalled();
|
|
expect(viewQueryExecutor.getExistingQueryModifiers).not.toHaveBeenCalled();
|
|
});
|
|
|
|
describe('should apply async view modifications', () => {
|
|
it('should apply modifications successfully', async () => {
|
|
const viewFactory = mock<ViewFactory>();
|
|
viewFactory.getViewDefault.mockReturnValue(createView({ view: 'live' }));
|
|
|
|
const query = new EventMediaQueries();
|
|
const queryResults = new MediaQueriesResults();
|
|
|
|
const viewQueryExecutor = mock<ViewQueryExecutor>();
|
|
viewQueryExecutor.getNewQueryModifiers.mockResolvedValue([
|
|
new SetQueryViewModifier({
|
|
query: query,
|
|
queryResults: queryResults,
|
|
}),
|
|
]);
|
|
|
|
const manager = new ViewManager(createCardAPI(), {
|
|
viewFactory: viewFactory,
|
|
viewQueryExecutor: viewQueryExecutor,
|
|
});
|
|
|
|
await manager.setViewDefaultWithNewQuery();
|
|
|
|
expect(manager.getView()?.query).toBe(query);
|
|
expect(manager.getView()?.queryResults).toBe(queryResults);
|
|
expect(manager.getView()?.context?.loading?.query).toBeUndefined();
|
|
});
|
|
|
|
it('should not apply modifications if there is a major media change', async () => {
|
|
const viewFactory = mock<ViewFactory>();
|
|
viewFactory.getViewDefault.mockReturnValueOnce(createView({ view: 'live' }));
|
|
|
|
const query = new EventMediaQueries();
|
|
const queryResults = new MediaQueriesResults();
|
|
|
|
const viewQueryExecutor = mock<ViewQueryExecutor>();
|
|
|
|
const manager = new ViewManager(createCardAPI(), {
|
|
viewFactory: viewFactory,
|
|
viewQueryExecutor: viewQueryExecutor,
|
|
});
|
|
|
|
viewQueryExecutor.getNewQueryModifiers.mockImplementation(
|
|
async (
|
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
_view: View,
|
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
_executorqueryExecutorOptions?: QueryExecutorOptions,
|
|
): Promise<ViewModifier[] | null> => {
|
|
// Simulate a major media change while the async operation is running.
|
|
viewFactory.getViewDefault.mockReturnValueOnce(
|
|
createView({ view: 'clips', context: { loading: { query: 1 } } }),
|
|
);
|
|
manager.setViewDefault();
|
|
|
|
// Now return the modifiers (which should be ignored since there has
|
|
// been a major change in the meantime).
|
|
return [
|
|
new SetQueryViewModifier({
|
|
query: query,
|
|
queryResults: queryResults,
|
|
}),
|
|
];
|
|
},
|
|
);
|
|
|
|
await manager.setViewDefaultWithNewQuery();
|
|
|
|
// View set during the async operation should not be touched.
|
|
expect(manager.getView()?.is('clips')).toBeTruthy();
|
|
expect(manager.getView()?.query).toBeNull();
|
|
expect(manager.getView()?.queryResults).toBeNull();
|
|
expect(manager.getView()?.context?.loading?.query).toBeUndefined();
|
|
});
|
|
|
|
describe('should manage loading state correctly', () => {
|
|
it('should mark as not loading when with major media change', async () => {
|
|
const viewFactory = mock<ViewFactory>();
|
|
viewFactory.getViewDefault.mockReturnValueOnce(createView({ view: 'live' }));
|
|
const viewQueryExecutor = mock<ViewQueryExecutor>();
|
|
|
|
const manager = new ViewManager(createCardAPI(), {
|
|
viewFactory: viewFactory,
|
|
viewQueryExecutor: viewQueryExecutor,
|
|
});
|
|
|
|
viewQueryExecutor.getNewQueryModifiers.mockImplementation(
|
|
async (
|
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
_view: View,
|
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
_executorqueryExecutorOptions?: QueryExecutorOptions,
|
|
): Promise<ViewModifier[] | null> => {
|
|
expect(manager.getView()?.context?.loading?.query).not.toBeUndefined();
|
|
|
|
// Simulate a major media change while the async operation is running.
|
|
viewFactory.getViewDefault.mockReturnValueOnce(
|
|
createView({ view: 'clips', context: { loading: { query: 1 } } }),
|
|
);
|
|
manager.setViewDefault();
|
|
|
|
// Now return the modifiers (which should be ignored since there has
|
|
// been a major change in the meantime).
|
|
return [];
|
|
},
|
|
);
|
|
|
|
await manager.setViewDefaultWithNewQuery();
|
|
|
|
expect(manager.getView()?.context?.loading?.query).toBeUndefined();
|
|
});
|
|
|
|
it('should not change loading status if something else is being loaded', async () => {
|
|
const viewFactory = mock<ViewFactory>();
|
|
viewFactory.getViewDefault.mockReturnValueOnce(createView({ view: 'live' }));
|
|
const viewQueryExecutor = mock<ViewQueryExecutor>();
|
|
|
|
const manager = new ViewManager(createCardAPI(), {
|
|
viewFactory: viewFactory,
|
|
viewQueryExecutor: viewQueryExecutor,
|
|
});
|
|
|
|
viewQueryExecutor.getNewQueryModifiers.mockImplementation(
|
|
async (
|
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
_view: View,
|
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
_executorqueryExecutorOptions?: QueryExecutorOptions,
|
|
): Promise<ViewModifier[] | null> => {
|
|
expect(manager.getView()?.context?.loading?.query).not.toBeUndefined();
|
|
|
|
// Simulate a major media change while the async operation is running.
|
|
viewFactory.getViewDefault.mockReturnValueOnce(
|
|
createView({ view: 'clips', context: { loading: { query: 2 } } }),
|
|
);
|
|
manager.setViewDefault();
|
|
|
|
// Now return the modifiers (which should be ignored since there has
|
|
// been a major change in the meantime).
|
|
return [];
|
|
},
|
|
);
|
|
|
|
await manager.setViewDefaultWithNewQuery();
|
|
|
|
expect(manager.getView()?.context?.loading?.query).toBe(2);
|
|
});
|
|
|
|
it('should not change loading status if it is unexpected', async () => {
|
|
const viewFactory = mock<ViewFactory>();
|
|
viewFactory.getViewDefault.mockReturnValueOnce(createView({ view: 'live' }));
|
|
const viewQueryExecutor = mock<ViewQueryExecutor>();
|
|
|
|
const manager = new ViewManager(createCardAPI(), {
|
|
viewFactory: viewFactory,
|
|
viewQueryExecutor: viewQueryExecutor,
|
|
});
|
|
|
|
viewQueryExecutor.getNewQueryModifiers.mockImplementation(
|
|
async (
|
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
_view: View,
|
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
_executorqueryExecutorOptions?: QueryExecutorOptions,
|
|
): Promise<ViewModifier[] | null> => {
|
|
expect(manager.getView()?.context?.loading?.query).not.toBeUndefined();
|
|
|
|
// Simulate another view update (without a major media change)
|
|
viewFactory.getViewDefault.mockReturnValueOnce(
|
|
createView({ context: { loading: { query: 100 } } }),
|
|
);
|
|
manager.setViewDefault();
|
|
|
|
// Now return the modifiers (which should be ignored since there has
|
|
// been a major change in the meantime).
|
|
return [];
|
|
},
|
|
);
|
|
|
|
await manager.setViewDefaultWithNewQuery();
|
|
|
|
expect(manager.getView()?.context?.loading?.query).toBe(100);
|
|
});
|
|
});
|
|
});
|
|
|
|
it('should adopt query and results when changing to gallery from viewer', async () => {
|
|
const baseView = createView({
|
|
view: 'media',
|
|
camera: 'camera.office',
|
|
query: new EventMediaQueries([
|
|
{
|
|
type: QueryType.Event,
|
|
cameraIDs: new Set(['camera.office']),
|
|
hasClip: true,
|
|
},
|
|
]),
|
|
queryResults: new MediaQueriesResults(),
|
|
});
|
|
|
|
const viewFactory = mock<ViewFactory>();
|
|
viewFactory.getViewDefault
|
|
.mockReturnValueOnce(baseView)
|
|
.mockReturnValueOnce(createView({ view: 'clips' }));
|
|
|
|
const viewQueryExecutor = mock<ViewQueryExecutor>();
|
|
viewQueryExecutor.getNewQueryModifiers.mockResolvedValue([]);
|
|
|
|
const manager = new ViewManager(createCardAPI(), {
|
|
viewFactory: viewFactory,
|
|
viewQueryExecutor: viewQueryExecutor,
|
|
});
|
|
|
|
manager.setViewDefault();
|
|
expect(manager.getView()?.is('media')).toBeTruthy();
|
|
|
|
await manager.setViewDefaultWithNewQuery({
|
|
params: {
|
|
view: 'clips',
|
|
},
|
|
});
|
|
|
|
expect(manager.getView()?.is('clips')).toBeTruthy();
|
|
expect(manager.getView()?.query).toBe(baseView.query);
|
|
expect(manager.getView()?.queryResults).toBe(baseView.queryResults);
|
|
expect(viewQueryExecutor.getNewQueryModifiers).not.toHaveBeenCalled();
|
|
expect(viewQueryExecutor.getExistingQueryModifiers).not.toHaveBeenCalled();
|
|
});
|