@@ -80,7 +80,7 @@ describe('ActionsManager', () => {
|
||||
expect(manager.getMergedActions()).toEqual({});
|
||||
});
|
||||
|
||||
describe('should get merged actions with live view', () => {
|
||||
describe('should get merged actions with view', () => {
|
||||
it.each([
|
||||
[
|
||||
'live' as const,
|
||||
@@ -100,6 +100,16 @@ describe('ActionsManager', () => {
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
'folder' as const,
|
||||
{
|
||||
tap_action: {
|
||||
action: 'navigate',
|
||||
// Folder also uses the media gallery.
|
||||
navigation_path: '3',
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
'clip' as const,
|
||||
{
|
||||
@@ -313,6 +323,7 @@ describe('ActionsManager', () => {
|
||||
|
||||
describe('should forward haptics', () => {
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
vi.unstubAllGlobals();
|
||||
});
|
||||
|
||||
@@ -329,6 +340,8 @@ describe('ActionsManager', () => {
|
||||
});
|
||||
|
||||
it('should forward warning haptic', async () => {
|
||||
vi.spyOn(global.console, 'warn').mockReturnValue(undefined);
|
||||
|
||||
const handler = vi.fn();
|
||||
window.addEventListener('haptic', handler);
|
||||
|
||||
|
||||
@@ -1,9 +1,17 @@
|
||||
import { expect, it } from 'vitest';
|
||||
import { expect, it, vi } from 'vitest';
|
||||
import { DownloadAction } from '../../../../src/card-controller/actions/actions/download';
|
||||
import { createCardAPI } from '../../../test-utils';
|
||||
import { QueryResults } from '../../../../src/view/query-results';
|
||||
import { createCardAPI, createView, TestViewMedia } from '../../../test-utils';
|
||||
|
||||
it('should handle download action', async () => {
|
||||
it('should handle download action with selected media', async () => {
|
||||
const api = createCardAPI();
|
||||
const selectedMedia = new TestViewMedia();
|
||||
vi.mocked(api.getViewManager().getView).mockReturnValue(
|
||||
createView({
|
||||
queryResults: new QueryResults({ results: [selectedMedia], selectedIndex: 0 }),
|
||||
}),
|
||||
);
|
||||
|
||||
const action = new DownloadAction(
|
||||
{},
|
||||
{
|
||||
@@ -14,5 +22,22 @@ it('should handle download action', async () => {
|
||||
|
||||
await action.execute(api);
|
||||
|
||||
expect(api.getDownloadManager().downloadViewerMedia).toBeCalled();
|
||||
expect(api.getViewItemManager().download).toBeCalledWith(selectedMedia);
|
||||
});
|
||||
|
||||
it('should handle download action without selected media', async () => {
|
||||
const api = createCardAPI();
|
||||
vi.mocked(api.getViewManager().getView).mockReturnValue(createView());
|
||||
|
||||
const action = new DownloadAction(
|
||||
{},
|
||||
{
|
||||
action: 'fire-dom-event',
|
||||
advanced_camera_card_action: 'download',
|
||||
},
|
||||
);
|
||||
|
||||
await action.execute(api);
|
||||
|
||||
expect(api.getViewItemManager().download).not.toBeCalled();
|
||||
});
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
import { FolderAction } from '../../../../src/card-controller/actions/actions/folder';
|
||||
import { FolderQuery } from '../../../../src/card-controller/folders/types';
|
||||
import { FolderViewQuery } from '../../../../src/view/query';
|
||||
import { createCardAPI, createFolder } from '../../../test-utils';
|
||||
|
||||
describe('should handle folder action', async () => {
|
||||
it('should handle folder action successfully', async () => {
|
||||
const api = createCardAPI();
|
||||
const action = new FolderAction(
|
||||
{},
|
||||
{
|
||||
action: 'fire-dom-event',
|
||||
advanced_camera_card_action: 'folder',
|
||||
},
|
||||
);
|
||||
|
||||
const folder = createFolder();
|
||||
vi.mocked(api.getFoldersManager().getFolder).mockReturnValue(folder);
|
||||
|
||||
const query: FolderQuery = {
|
||||
folder,
|
||||
path: ['path'],
|
||||
};
|
||||
vi.mocked(api.getFoldersManager().generateDefaultFolderQuery).mockReturnValue(query);
|
||||
|
||||
await action.execute(api);
|
||||
|
||||
expect(
|
||||
api.getViewManager().setViewByParametersWithExistingQuery,
|
||||
).toHaveBeenCalledWith({
|
||||
params: {
|
||||
view: 'folder',
|
||||
query: expect.any(FolderViewQuery),
|
||||
},
|
||||
});
|
||||
|
||||
expect(
|
||||
vi
|
||||
.mocked(api.getViewManager().setViewByParametersWithExistingQuery)
|
||||
.mock.calls[0][0]?.params?.query?.getQuery(),
|
||||
).toBe(query);
|
||||
});
|
||||
|
||||
it('should do nothing with non-existent folder', async () => {
|
||||
const api = createCardAPI();
|
||||
const action = new FolderAction(
|
||||
{},
|
||||
{
|
||||
action: 'fire-dom-event',
|
||||
advanced_camera_card_action: 'folder',
|
||||
folder: 'NON-EXISTENT-FOLDER',
|
||||
},
|
||||
);
|
||||
|
||||
vi.mocked(api.getFoldersManager().getFolder).mockReturnValue(null);
|
||||
|
||||
await action.execute(api);
|
||||
|
||||
expect(
|
||||
api.getViewManager().setViewByParametersWithExistingQuery,
|
||||
).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should do nothing with non-existent default query', async () => {
|
||||
const api = createCardAPI();
|
||||
const action = new FolderAction(
|
||||
{},
|
||||
{
|
||||
action: 'fire-dom-event',
|
||||
advanced_camera_card_action: 'folder',
|
||||
},
|
||||
);
|
||||
|
||||
const folder = createFolder();
|
||||
vi.mocked(api.getFoldersManager().getFolder).mockReturnValue(folder);
|
||||
|
||||
vi.mocked(api.getFoldersManager().generateDefaultFolderQuery).mockReturnValue(null);
|
||||
|
||||
await action.execute(api);
|
||||
|
||||
expect(
|
||||
api.getViewManager().setViewByParametersWithExistingQuery,
|
||||
).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@@ -1,18 +1,62 @@
|
||||
import { expect, it } from 'vitest';
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest';
|
||||
import { mock } from 'vitest-mock-extended';
|
||||
import { ScreenshotAction } from '../../../../src/card-controller/actions/actions/screenshot';
|
||||
import { createCardAPI } from '../../../test-utils';
|
||||
import { MediaPlayerController } from '../../../../src/types';
|
||||
import { downloadURL } from '../../../../src/utils/download';
|
||||
import { createCardAPI, createMediaLoadedInfo } from '../../../test-utils';
|
||||
|
||||
it('should handle screenshot action', async () => {
|
||||
const api = createCardAPI();
|
||||
const action = new ScreenshotAction(
|
||||
{},
|
||||
{
|
||||
action: 'fire-dom-event',
|
||||
advanced_camera_card_action: 'screenshot',
|
||||
},
|
||||
);
|
||||
vi.mock('../../../../src/utils/download');
|
||||
|
||||
await action.execute(api);
|
||||
describe('should handle screenshot action', async () => {
|
||||
afterEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
expect(api.getDownloadManager().downloadScreenshot).toBeCalled();
|
||||
it('should handle screenshot action with screenshot URL', async () => {
|
||||
const api = createCardAPI();
|
||||
const action = new ScreenshotAction(
|
||||
{},
|
||||
{
|
||||
action: 'fire-dom-event',
|
||||
advanced_camera_card_action: 'screenshot',
|
||||
},
|
||||
);
|
||||
|
||||
const mediaPlayerController = mock<MediaPlayerController>();
|
||||
mediaPlayerController.getScreenshotURL.mockResolvedValue('screenshot-url');
|
||||
|
||||
vi.mocked(api.getMediaLoadedInfoManager().get).mockReturnValue(
|
||||
createMediaLoadedInfo({
|
||||
mediaPlayerController,
|
||||
}),
|
||||
);
|
||||
|
||||
await action.execute(api);
|
||||
|
||||
expect(downloadURL).toBeCalledWith('screenshot-url', 'screenshot.jpg');
|
||||
});
|
||||
|
||||
it('should handle screenshot action without screenshot URL', async () => {
|
||||
const api = createCardAPI();
|
||||
const action = new ScreenshotAction(
|
||||
{},
|
||||
{
|
||||
action: 'fire-dom-event',
|
||||
advanced_camera_card_action: 'screenshot',
|
||||
},
|
||||
);
|
||||
|
||||
const mediaPlayerController = mock<MediaPlayerController>();
|
||||
mediaPlayerController.getScreenshotURL.mockResolvedValue(null);
|
||||
|
||||
vi.mocked(api.getMediaLoadedInfoManager().get).mockReturnValue(
|
||||
createMediaLoadedInfo({
|
||||
mediaPlayerController,
|
||||
}),
|
||||
);
|
||||
|
||||
await action.execute(api);
|
||||
|
||||
expect(downloadURL).not.toBeCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
import { afterAll, expect, it, vi } from 'vitest';
|
||||
import { createCardAPI } from '../../../test-utils';
|
||||
import { expect, it, vi } from 'vitest';
|
||||
import { SleepAction } from '../../../../src/card-controller/actions/actions/sleep';
|
||||
import { sleep } from '../../../../src/utils/basic';
|
||||
import { sleep } from '../../../../src/utils/sleep';
|
||||
import { createCardAPI } from '../../../test-utils';
|
||||
|
||||
vi.mock('../../../../src/utils/basic');
|
||||
|
||||
afterAll(() => {
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
vi.mock('../../../../src/utils/sleep');
|
||||
|
||||
it('should handle sleep action', async () => {
|
||||
const api = createCardAPI();
|
||||
|
||||
@@ -7,6 +7,7 @@ import { DefaultAction } from '../../../src/card-controller/actions/actions/defa
|
||||
import { DisplayModeSelectAction } from '../../../src/card-controller/actions/actions/display-mode-select';
|
||||
import { DownloadAction } from '../../../src/card-controller/actions/actions/download';
|
||||
import { ExpandAction } from '../../../src/card-controller/actions/actions/expand';
|
||||
import { FolderAction } from '../../../src/card-controller/actions/actions/folder';
|
||||
import { FullscreenAction } from '../../../src/card-controller/actions/actions/fullscreen';
|
||||
import { InternalCallbackAction } from '../../../src/card-controller/actions/actions/internal-callback';
|
||||
import { LogAction } from '../../../src/card-controller/actions/actions/log';
|
||||
@@ -184,6 +185,7 @@ describe('ActionFactory', () => {
|
||||
},
|
||||
InternalCallbackAction,
|
||||
],
|
||||
[{ advanced_camera_card_action: 'folder' as const }, FolderAction],
|
||||
])(
|
||||
'advanced_camera_card_action: $advanced_camera_card_action',
|
||||
(action: Partial<ActionConfig>, classObject: object) => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { CameraEndpoint } from '../../src/camera-manager/types';
|
||||
import { CameraURLManager } from '../../src/card-controller/camera-url-manager';
|
||||
import { CardCameraURLAPI } from '../../src/card-controller/types';
|
||||
import { Endpoint } from '../../src/types';
|
||||
import { createCardAPI, createViewWithMedia } from '../test-utils';
|
||||
|
||||
const createAPIWithMedia = (): CardCameraURLAPI => {
|
||||
@@ -20,7 +20,7 @@ describe('CameraURLManager', () => {
|
||||
const api = createAPIWithMedia();
|
||||
const manager = new CameraURLManager(api);
|
||||
|
||||
const endpoint: CameraEndpoint = {
|
||||
const endpoint: Endpoint = {
|
||||
endpoint: 'http://frigate',
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
import { mock } from 'vitest-mock-extended';
|
||||
import { setFoldersFromConfig } from '../../../src/card-controller/config/load-folders';
|
||||
import { FoldersManager } from '../../../src/card-controller/folders/manager';
|
||||
import { createCardAPI, createConfig, createFolder } from '../../test-utils';
|
||||
|
||||
describe('setFoldersFromConfig', () => {
|
||||
it('should replace folders', () => {
|
||||
const api = createCardAPI();
|
||||
const foldersManager = mock<FoldersManager>();
|
||||
vi.mocked(api.getFoldersManager).mockReturnValue(foldersManager);
|
||||
|
||||
const folders = [createFolder()];
|
||||
const config = createConfig({
|
||||
folders,
|
||||
});
|
||||
vi.mocked(api.getConfigManager().getConfig).mockReturnValue(config);
|
||||
|
||||
setFoldersFromConfig(api);
|
||||
|
||||
expect(foldersManager.deleteFolders).toBeCalled();
|
||||
expect(foldersManager.addFolders).toBeCalledWith(folders);
|
||||
});
|
||||
|
||||
it('should handle exceptions', () => {
|
||||
const api = createCardAPI();
|
||||
const foldersManager = mock<FoldersManager>();
|
||||
vi.mocked(api.getFoldersManager).mockReturnValue(foldersManager);
|
||||
|
||||
const error = new Error('test error');
|
||||
vi.mocked(foldersManager.addFolders).mockImplementation(() => {
|
||||
throw error;
|
||||
});
|
||||
|
||||
const config = createConfig({
|
||||
folders: [createFolder()],
|
||||
});
|
||||
vi.mocked(api.getConfigManager().getConfig).mockReturnValue(config);
|
||||
|
||||
setFoldersFromConfig(api);
|
||||
|
||||
expect(api.getMessageManager().setErrorIfHigherPriority).toBeCalledWith(error);
|
||||
});
|
||||
});
|
||||
@@ -10,8 +10,8 @@ import {
|
||||
import { ConfigManager } from '../../src/card-controller/config/config-manager';
|
||||
import { CardController } from '../../src/card-controller/controller';
|
||||
import { DefaultManager } from '../../src/card-controller/default-manager';
|
||||
import { DownloadManager } from '../../src/card-controller/download-manager';
|
||||
import { ExpandManager } from '../../src/card-controller/expand-manager';
|
||||
import { FoldersManager } from '../../src/card-controller/folders/manager';
|
||||
import { FullscreenManager } from '../../src/card-controller/fullscreen/fullscreen-manager';
|
||||
import { HASSManager } from '../../src/card-controller/hass/hass-manager';
|
||||
import { InitializationManager } from '../../src/card-controller/initialization-manager';
|
||||
@@ -25,12 +25,13 @@ import { QueryStringManager } from '../../src/card-controller/query-string-manag
|
||||
import { StatusBarItemManager } from '../../src/card-controller/status-bar-item-manager';
|
||||
import { StyleManager } from '../../src/card-controller/style-manager';
|
||||
import { TriggersManager } from '../../src/card-controller/triggers-manager';
|
||||
import { ViewItemManager } from '../../src/card-controller/view/item-manager';
|
||||
import { ViewManager } from '../../src/card-controller/view/view-manager';
|
||||
import { ConditionStateManager } from '../../src/conditions/state-manager';
|
||||
import { AdvancedCameraCardEditor } from '../../src/editor';
|
||||
import { DeviceRegistryManager } from '../../src/utils/ha/registry/device';
|
||||
import { EntityRegistryManagerLive } from '../../src/utils/ha/registry/entity';
|
||||
import { ResolvedMediaCache } from '../../src/utils/ha/resolved-media';
|
||||
import { DeviceRegistryManager } from '../../src/ha/registry/device';
|
||||
import { EntityRegistryManagerLive } from '../../src/ha/registry/entity';
|
||||
import { ResolvedMediaCache } from '../../src/ha/resolved-media';
|
||||
|
||||
vi.mock('../../src/camera-manager/manager');
|
||||
vi.mock('../../src/card-controller/actions/actions-manager');
|
||||
@@ -41,6 +42,7 @@ vi.mock('../../src/card-controller/config/config-manager');
|
||||
vi.mock('../../src/card-controller/default-manager');
|
||||
vi.mock('../../src/card-controller/download-manager');
|
||||
vi.mock('../../src/card-controller/expand-manager');
|
||||
vi.mock('../../src/card-controller/folders/manager');
|
||||
vi.mock('../../src/card-controller/fullscreen/fullscreen-manager');
|
||||
vi.mock('../../src/card-controller/hass/hass-manager');
|
||||
vi.mock('../../src/card-controller/initialization-manager');
|
||||
@@ -54,11 +56,12 @@ vi.mock('../../src/card-controller/query-string-manager');
|
||||
vi.mock('../../src/card-controller/status-bar-item-manager');
|
||||
vi.mock('../../src/card-controller/style-manager');
|
||||
vi.mock('../../src/card-controller/triggers-manager');
|
||||
vi.mock('../../src/card-controller/view/item-manager');
|
||||
vi.mock('../../src/card-controller/view/view-manager');
|
||||
vi.mock('../../src/conditions/state-manager');
|
||||
vi.mock('../../src/utils/ha/registry/device');
|
||||
vi.mock('../../src/utils/ha/registry/entity');
|
||||
vi.mock('../../src/utils/ha/resolved-media');
|
||||
vi.mock('../../src/ha/registry/device');
|
||||
vi.mock('../../src/ha/registry/entity');
|
||||
vi.mock('../../src/ha/resolved-media');
|
||||
|
||||
const createCardElement = (): CardHTMLElement => {
|
||||
const element = document.createElement('div') as unknown as CardHTMLElement;
|
||||
@@ -152,12 +155,6 @@ describe('CardController', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('getDownloadManager', () => {
|
||||
expect(createController().getDownloadManager()).toBe(
|
||||
vi.mocked(DownloadManager).mock.instances[0],
|
||||
);
|
||||
});
|
||||
|
||||
it('getEntityRegistryManager', () => {
|
||||
expect(createController().getEntityRegistryManager()).toBe(
|
||||
vi.mocked(EntityRegistryManagerLive).mock.instances[0],
|
||||
@@ -170,6 +167,12 @@ describe('CardController', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('getFoldersManager', () => {
|
||||
expect(createController().getFoldersManager()).toBe(
|
||||
vi.mocked(FoldersManager).mock.instances[0],
|
||||
);
|
||||
});
|
||||
|
||||
it('getFullscreenManager', () => {
|
||||
expect(createController().getFullscreenManager()).toBe(
|
||||
vi.mocked(FullscreenManager).mock.instances[0],
|
||||
@@ -270,6 +273,12 @@ describe('CardController', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('getViewItemManager', () => {
|
||||
expect(createController().getViewItemManager()).toBe(
|
||||
vi.mocked(ViewItemManager).mock.instances[0],
|
||||
);
|
||||
});
|
||||
|
||||
it('getViewManager', () => {
|
||||
expect(createController().getViewManager()).toBe(
|
||||
vi.mocked(ViewManager).mock.instances[0],
|
||||
|
||||
@@ -1,94 +0,0 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { mock } from 'vitest-mock-extended';
|
||||
import { DownloadManager } from '../../src/card-controller/download-manager';
|
||||
import { MediaPlayerController } from '../../src/types';
|
||||
import { downloadMedia, downloadURL } from '../../src/utils/download.js';
|
||||
import {
|
||||
createCardAPI,
|
||||
createHASS,
|
||||
createMediaLoadedInfo,
|
||||
createViewWithMedia,
|
||||
} from '../test-utils';
|
||||
|
||||
vi.mock('../../src/utils/download.js');
|
||||
|
||||
describe('DownloadManager.downloadViewerMedia', () => {
|
||||
beforeEach(() => {
|
||||
vi.resetAllMocks();
|
||||
});
|
||||
|
||||
it('should download', async () => {
|
||||
const api = createCardAPI();
|
||||
vi.mocked(api.getViewManager().getView).mockReturnValue(createViewWithMedia());
|
||||
vi.mocked(api.getHASSManager().getHASS).mockReturnValue(createHASS());
|
||||
const manager = new DownloadManager(api);
|
||||
|
||||
expect(await manager.downloadViewerMedia()).toBeTruthy();
|
||||
expect(downloadMedia).toBeCalledWith(
|
||||
api.getHASSManager().getHASS(),
|
||||
api.getCameraManager(),
|
||||
api.getViewManager().getView()?.queryResults?.getResult(0),
|
||||
);
|
||||
});
|
||||
|
||||
it('should not download due to exception thrown', async () => {
|
||||
const api = createCardAPI();
|
||||
vi.mocked(api.getViewManager().getView).mockReturnValue(createViewWithMedia());
|
||||
vi.mocked(api.getHASSManager().getHASS).mockReturnValue(createHASS());
|
||||
const manager = new DownloadManager(api);
|
||||
|
||||
const error = new Error();
|
||||
vi.mocked(downloadMedia).mockRejectedValue(error);
|
||||
|
||||
expect(await manager.downloadViewerMedia()).toBeFalsy();
|
||||
expect(api.getMessageManager().setErrorIfHigherPriority).toBeCalledWith(error);
|
||||
});
|
||||
|
||||
it('should not download without hass', async () => {
|
||||
const api = createCardAPI();
|
||||
vi.mocked(api.getViewManager().getView).mockReturnValue(createViewWithMedia());
|
||||
vi.mocked(api.getHASSManager().getHASS).mockReturnValue(null);
|
||||
const manager = new DownloadManager(api);
|
||||
|
||||
expect(await manager.downloadViewerMedia()).toBeFalsy();
|
||||
expect(downloadMedia).not.toBeCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('DownloadManager.downloadScreenshot', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it('with url', async () => {
|
||||
const api = createCardAPI();
|
||||
const mediaPlayerController = mock<MediaPlayerController>();
|
||||
mediaPlayerController.getScreenshotURL.mockResolvedValue('http://screenshot');
|
||||
|
||||
vi.mocked(api.getMediaLoadedInfoManager().get).mockReturnValue(
|
||||
createMediaLoadedInfo({
|
||||
mediaPlayerController,
|
||||
}),
|
||||
);
|
||||
const manager = new DownloadManager(api);
|
||||
await manager.downloadScreenshot();
|
||||
|
||||
expect(downloadURL).toBeCalledWith('http://screenshot', 'screenshot.jpg');
|
||||
});
|
||||
|
||||
it('without url', async () => {
|
||||
const api = createCardAPI();
|
||||
const mediaPlayerController = mock<MediaPlayerController>();
|
||||
mediaPlayerController.getScreenshotURL.mockResolvedValue(null);
|
||||
|
||||
vi.mocked(api.getMediaLoadedInfoManager().get).mockReturnValue(
|
||||
createMediaLoadedInfo({
|
||||
mediaPlayerController,
|
||||
}),
|
||||
);
|
||||
const manager = new DownloadManager(api);
|
||||
await manager.downloadScreenshot();
|
||||
|
||||
expect(downloadURL).not.toBeCalled();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,172 @@
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest';
|
||||
import { mock } from 'vitest-mock-extended';
|
||||
import { FoldersExecutor } from '../../../src/card-controller/folders/executor';
|
||||
import { HAFoldersEngine } from '../../../src/card-controller/folders/ha/engine';
|
||||
import { FolderQuery } from '../../../src/card-controller/folders/types';
|
||||
import { FolderConfig } from '../../../src/config/schema/folders';
|
||||
import { Endpoint } from '../../../src/types';
|
||||
import { ViewFolder } from '../../../src/view/item';
|
||||
import { createFolder, createHASS, TestViewMedia } from '../../test-utils';
|
||||
|
||||
vi.mock('../../../src/card-controller/folders/ha/engine');
|
||||
vi.mock('../../../../src/utils/ha/download');
|
||||
|
||||
describe('FoldersExecutor', () => {
|
||||
afterEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('getItemCapabilities', () => {
|
||||
it('should not get capabilities for non-folder media', () => {
|
||||
const item = new TestViewMedia({ folder: null });
|
||||
const executor = new FoldersExecutor();
|
||||
|
||||
expect(executor.getItemCapabilities(item)).toBeNull();
|
||||
});
|
||||
|
||||
it('should not get capabilities for non-existent folder engine', () => {
|
||||
const folder: FolderConfig = {
|
||||
type: 'UNKNOWN',
|
||||
} as unknown as FolderConfig;
|
||||
const item = new TestViewMedia({ folder });
|
||||
const executor = new FoldersExecutor();
|
||||
|
||||
expect(executor.getItemCapabilities(item)).toBeNull();
|
||||
});
|
||||
|
||||
it('should get capabilities for HA folder engine', () => {
|
||||
const folder = createFolder();
|
||||
const item = new TestViewMedia({ folder });
|
||||
|
||||
const capabilities = {
|
||||
canFavorite: false,
|
||||
canDownload: true,
|
||||
};
|
||||
const haFolderEngine = mock<HAFoldersEngine>();
|
||||
haFolderEngine.getItemCapabilities.mockReturnValue(capabilities);
|
||||
|
||||
const executor = new FoldersExecutor({
|
||||
ha: haFolderEngine,
|
||||
});
|
||||
|
||||
expect(executor.getItemCapabilities(item)).toEqual(capabilities);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getDownloadPath', () => {
|
||||
it('should not get download path for non-existent folder engine', async () => {
|
||||
const folder: FolderConfig = {
|
||||
type: 'UNKNOWN',
|
||||
} as unknown as FolderConfig;
|
||||
const item = new TestViewMedia({ folder });
|
||||
const executor = new FoldersExecutor();
|
||||
|
||||
expect(await executor.getDownloadPath(createHASS(), item)).toBeNull();
|
||||
});
|
||||
|
||||
it('should get download path for HA folder engine', async () => {
|
||||
const endpoint: Endpoint = { endpoint: '/media', sign: false };
|
||||
const haFolderEngine = mock<HAFoldersEngine>();
|
||||
haFolderEngine.getDownloadPath.mockResolvedValue(endpoint);
|
||||
|
||||
const executor = new FoldersExecutor({
|
||||
ha: haFolderEngine,
|
||||
});
|
||||
const item = new TestViewMedia({ folder: createFolder() });
|
||||
expect(await executor.getDownloadPath(createHASS(), item)).toEqual(endpoint);
|
||||
});
|
||||
});
|
||||
|
||||
describe('favorite', () => {
|
||||
it('should not favorite non-existent folder engine', async () => {
|
||||
const folder: FolderConfig = {
|
||||
type: 'UNKNOWN',
|
||||
} as unknown as FolderConfig;
|
||||
const item = new TestViewMedia({ folder });
|
||||
const executor = new FoldersExecutor();
|
||||
|
||||
await executor.favorite(createHASS(), item, true);
|
||||
|
||||
// No observable effect.
|
||||
});
|
||||
|
||||
it('should favorite', async () => {
|
||||
const haFolderEngine = mock<HAFoldersEngine>();
|
||||
const executor = new FoldersExecutor({
|
||||
ha: haFolderEngine,
|
||||
});
|
||||
|
||||
const hass = createHASS();
|
||||
const item = new TestViewMedia({ folder: createFolder() });
|
||||
|
||||
await executor.favorite(hass, item, true);
|
||||
|
||||
expect(haFolderEngine.favorite).toBeCalledWith(hass, item, true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('generateDefaultFolderQuery', () => {
|
||||
it('should generate default folder query', () => {
|
||||
const folder: FolderConfig = createFolder();
|
||||
const query: FolderQuery = {
|
||||
folder,
|
||||
path: ['media-source://'],
|
||||
};
|
||||
|
||||
const haFolderEngine = mock<HAFoldersEngine>();
|
||||
haFolderEngine.generateDefaultFolderQuery.mockReturnValue(query);
|
||||
const executor = new FoldersExecutor({ ha: haFolderEngine });
|
||||
|
||||
expect(executor.generateDefaultFolderQuery(folder)).toEqual(query);
|
||||
});
|
||||
|
||||
it('should return null for non-existent folder engine', () => {
|
||||
const folder: FolderConfig = {
|
||||
type: 'UNKNOWN',
|
||||
} as unknown as FolderConfig;
|
||||
const executor = new FoldersExecutor();
|
||||
|
||||
expect(executor.generateDefaultFolderQuery(folder)).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('expandFolder', () => {
|
||||
it('should reject folders of the wrong type', async () => {
|
||||
const query = {
|
||||
folder: { type: 'UNKNOWN' },
|
||||
} as unknown as FolderQuery;
|
||||
|
||||
const executor = new FoldersExecutor();
|
||||
|
||||
expect(await executor.expandFolder(createHASS(), query)).toBeNull();
|
||||
});
|
||||
|
||||
it('should expand folder', async () => {
|
||||
const folder = createFolder();
|
||||
const query: FolderQuery = {
|
||||
folder,
|
||||
path: ['media-source://'],
|
||||
};
|
||||
|
||||
const mediaItem = new TestViewMedia({
|
||||
folder,
|
||||
startTime: new Date('2023-04-29T14:27'),
|
||||
});
|
||||
const folderItem = new ViewFolder(folder);
|
||||
|
||||
const haFolderEngine = mock<HAFoldersEngine>();
|
||||
haFolderEngine.expandFolder.mockResolvedValue([folderItem, mediaItem, folderItem]);
|
||||
|
||||
const executor = new FoldersExecutor({
|
||||
ha: haFolderEngine,
|
||||
});
|
||||
const hass = createHASS();
|
||||
const results = await executor.expandFolder(hass, query);
|
||||
expect(results).toEqual([
|
||||
// Folder will have been sorted to the front and de-duped.
|
||||
folderItem,
|
||||
mediaItem,
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,370 @@
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { HAFoldersEngine } from '../../../../src/card-controller/folders/ha/engine';
|
||||
import { FolderQuery } from '../../../../src/card-controller/folders/types';
|
||||
import { FolderConfig } from '../../../../src/config/schema/folders';
|
||||
import { getMediaDownloadPath } from '../../../../src/ha/download';
|
||||
import { homeAssistantWSRequest } from '../../../../src/ha/ws-request';
|
||||
import { Endpoint } from '../../../../src/types';
|
||||
import { ViewFolder, ViewMedia } from '../../../../src/view/item';
|
||||
import {
|
||||
createBrowseMedia,
|
||||
createFolder,
|
||||
createHASS,
|
||||
TestViewMedia,
|
||||
} from '../../../test-utils';
|
||||
|
||||
vi.mock('../../../../src/ha/download');
|
||||
vi.mock('../../../../src/ha/ws-request');
|
||||
|
||||
describe('HAFoldersEngine', () => {
|
||||
afterEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('getItemCapabilities', () => {
|
||||
it('should not be able to download a folder', () => {
|
||||
const item = new ViewFolder(createFolder());
|
||||
const engine = new HAFoldersEngine();
|
||||
|
||||
expect(engine.getItemCapabilities(item)).toEqual({
|
||||
canFavorite: false,
|
||||
canDownload: false,
|
||||
});
|
||||
});
|
||||
|
||||
it('should be able to download a media item', () => {
|
||||
const item = new TestViewMedia({ folder: createFolder() });
|
||||
const engine = new HAFoldersEngine();
|
||||
|
||||
expect(engine.getItemCapabilities(item)).toEqual({
|
||||
canFavorite: false,
|
||||
canDownload: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getDownloadPath', () => {
|
||||
it('should return null if item is not a media item', async () => {
|
||||
const item = new ViewFolder(createFolder());
|
||||
const engine = new HAFoldersEngine();
|
||||
expect(await engine.getDownloadPath(createHASS(), item)).toBeNull();
|
||||
});
|
||||
|
||||
it('should return a download path for a media item', async () => {
|
||||
const item = new TestViewMedia({ folder: createFolder() });
|
||||
const engine = new HAFoldersEngine();
|
||||
|
||||
const endpoint: Endpoint = { endpoint: '/media', sign: false };
|
||||
vi.mocked(getMediaDownloadPath).mockResolvedValue(endpoint);
|
||||
|
||||
expect(await engine.getDownloadPath(createHASS(), item)).toEqual(endpoint);
|
||||
});
|
||||
});
|
||||
|
||||
describe('favorite', () => {
|
||||
it('should favorite', async () => {
|
||||
const engine = new HAFoldersEngine();
|
||||
const item = new TestViewMedia({ folder: createFolder() });
|
||||
|
||||
await engine.favorite(createHASS(), item, true);
|
||||
|
||||
// No observable effect.
|
||||
});
|
||||
});
|
||||
|
||||
describe('expandFolder', () => {
|
||||
it('should reject folders of the wrong type', async () => {
|
||||
const query = {
|
||||
folder: { type: 'UNKNOWN' },
|
||||
} as unknown as FolderQuery;
|
||||
const engine = new HAFoldersEngine();
|
||||
|
||||
expect(await engine.expandFolder(createHASS(), query)).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('should generate default folder query', () => {
|
||||
it('should generate default folder query', () => {
|
||||
const folder: FolderConfig = { type: 'ha' };
|
||||
const engine = new HAFoldersEngine();
|
||||
|
||||
const query = engine.generateDefaultFolderQuery(folder);
|
||||
expect(query).toEqual({
|
||||
folder,
|
||||
path: [{ id: 'media-source://', ha: { id: 'media-source://' } }],
|
||||
});
|
||||
});
|
||||
|
||||
it('should reject folders of the wrong type', async () => {
|
||||
const folder = createFolder({ type: 'UNKNOWN' } as unknown as FolderConfig);
|
||||
const engine = new HAFoldersEngine();
|
||||
|
||||
expect(engine.generateDefaultFolderQuery(folder)).toBeNull();
|
||||
});
|
||||
|
||||
it('should respect path_url as a priority', async () => {
|
||||
const folder = createFolder({
|
||||
ha: {
|
||||
url: [{ id: 'media-source://1' }],
|
||||
path: [{ id: 'media-source://2' }],
|
||||
},
|
||||
});
|
||||
const engine = new HAFoldersEngine();
|
||||
expect(engine.generateDefaultFolderQuery(folder)).toEqual({
|
||||
folder,
|
||||
path: [
|
||||
{ id: 'media-source://1', ha: { id: 'media-source://1' } },
|
||||
{ id: 'media-source://2', ha: { id: 'media-source://2' } },
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it('should respect configured url', async () => {
|
||||
const folder = createFolder({ ha: { url: [{ id: 'media-source://' }] } });
|
||||
const engine = new HAFoldersEngine();
|
||||
expect(engine.generateDefaultFolderQuery(folder)).toEqual({
|
||||
folder,
|
||||
path: [{ id: 'media-source://', ha: { id: 'media-source://' } }],
|
||||
});
|
||||
});
|
||||
|
||||
it('should add default media root as necessary', async () => {
|
||||
const folder = createFolder({ ha: { path: [{ title: 'Frigate' }] } });
|
||||
const engine = new HAFoldersEngine();
|
||||
expect(engine.generateDefaultFolderQuery(folder)).toEqual({
|
||||
folder,
|
||||
path: [
|
||||
{ id: 'media-source://', ha: { id: 'media-source://' } },
|
||||
{ id: undefined, ha: { title: 'Frigate' } },
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('should expand folder', () => {
|
||||
it('should expand folder with cache by default', async () => {
|
||||
const query: FolderQuery = {
|
||||
folder: { type: 'ha' },
|
||||
path: [{ id: 'media-source://id' }],
|
||||
};
|
||||
|
||||
vi.mocked(homeAssistantWSRequest).mockResolvedValueOnce(
|
||||
createBrowseMedia({
|
||||
media_content_id: 'media-source://id',
|
||||
can_expand: true,
|
||||
children: [
|
||||
createBrowseMedia({
|
||||
media_content_id: 'media-source://media-item',
|
||||
title: 'Media Item',
|
||||
}),
|
||||
createBrowseMedia({
|
||||
media_content_id: 'media-source://frigate',
|
||||
title: 'Frigate',
|
||||
can_expand: true,
|
||||
}),
|
||||
],
|
||||
}),
|
||||
);
|
||||
|
||||
const engine = new HAFoldersEngine();
|
||||
const results = await engine.expandFolder(createHASS(), query);
|
||||
expect(results?.length).toBe(2);
|
||||
expect(results?.[0]).toBeInstanceOf(ViewMedia);
|
||||
expect(results?.[1]).toBeInstanceOf(ViewFolder);
|
||||
|
||||
expect(homeAssistantWSRequest).toBeCalledTimes(1);
|
||||
|
||||
// Expanding the folder again should use the cache.
|
||||
await engine.expandFolder(createHASS(), query);
|
||||
expect(homeAssistantWSRequest).toBeCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should expand folder without cache when requested', async () => {
|
||||
const query: FolderQuery = {
|
||||
folder: { type: 'ha' },
|
||||
path: [{ id: 'media-source://id' }],
|
||||
};
|
||||
|
||||
vi.mocked(homeAssistantWSRequest)
|
||||
.mockResolvedValueOnce(
|
||||
createBrowseMedia({
|
||||
media_content_id: 'media-source://id',
|
||||
can_expand: true,
|
||||
children: [
|
||||
createBrowseMedia({
|
||||
media_content_id: 'media-source://media-item',
|
||||
title: 'Media Item',
|
||||
}),
|
||||
createBrowseMedia({
|
||||
media_content_id: 'media-source://frigate',
|
||||
title: 'Frigate',
|
||||
can_expand: true,
|
||||
}),
|
||||
],
|
||||
}),
|
||||
)
|
||||
.mockResolvedValueOnce([]);
|
||||
|
||||
const engine = new HAFoldersEngine();
|
||||
const results = await engine.expandFolder(createHASS(), query, {
|
||||
useCache: false,
|
||||
});
|
||||
expect(results?.length).toBe(2);
|
||||
expect(results?.[0]).toBeInstanceOf(ViewMedia);
|
||||
expect(results?.[1]).toBeInstanceOf(ViewFolder);
|
||||
|
||||
expect(homeAssistantWSRequest).toBeCalledTimes(1);
|
||||
|
||||
// Expanding the folder again should use the cache.
|
||||
await engine.expandFolder(createHASS(), query);
|
||||
expect(homeAssistantWSRequest).toBeCalledTimes(2);
|
||||
});
|
||||
|
||||
it('should not expand without a folder with an id', async () => {
|
||||
const query: FolderQuery = {
|
||||
folder: { type: 'ha' },
|
||||
// There's no component in the query with an id to start from.
|
||||
path: [{ ha: { title: 'Frigate' } }],
|
||||
};
|
||||
const engine = new HAFoldersEngine();
|
||||
expect(await engine.expandFolder(createHASS(), query)).toBeNull();
|
||||
});
|
||||
|
||||
it('should expand folder with title based query', async () => {
|
||||
const query: FolderQuery = {
|
||||
folder: { type: 'ha' },
|
||||
path: [{ id: 'media-source://' }, { ha: { title: 'Frigate' } }],
|
||||
};
|
||||
|
||||
vi.mocked(homeAssistantWSRequest)
|
||||
.mockResolvedValueOnce(
|
||||
createBrowseMedia({
|
||||
media_content_id: 'media-source://',
|
||||
can_expand: true,
|
||||
children: [
|
||||
createBrowseMedia({
|
||||
media_content_id: 'media-source://frigate',
|
||||
title: 'Frigate',
|
||||
can_expand: true,
|
||||
}),
|
||||
],
|
||||
}),
|
||||
)
|
||||
.mockResolvedValueOnce(
|
||||
createBrowseMedia({
|
||||
media_content_id: 'media-source://frigate',
|
||||
can_expand: true,
|
||||
children: [
|
||||
createBrowseMedia({
|
||||
media_content_id: 'media-source://frigate/result',
|
||||
title: 'Result',
|
||||
}),
|
||||
],
|
||||
}),
|
||||
);
|
||||
|
||||
const engine = new HAFoldersEngine();
|
||||
const results = await engine.expandFolder(createHASS(), query);
|
||||
expect(results?.length).toBe(1);
|
||||
expect(results?.[0]).toBeInstanceOf(ViewMedia);
|
||||
expect(results?.[0].getID()).toBe('media-source://frigate/result');
|
||||
});
|
||||
|
||||
it('should expand folder with title_re based query', async () => {
|
||||
const query: FolderQuery = {
|
||||
folder: { type: 'ha' },
|
||||
path: [{ id: 'media-source://' }, { ha: { title_re: 'Fri.*' } }],
|
||||
};
|
||||
|
||||
vi.mocked(homeAssistantWSRequest)
|
||||
.mockResolvedValueOnce(
|
||||
createBrowseMedia({
|
||||
media_content_id: 'media-source://',
|
||||
can_expand: true,
|
||||
children: [
|
||||
createBrowseMedia({
|
||||
media_content_id: 'media-source://frigate',
|
||||
title: 'Frigate',
|
||||
can_expand: true,
|
||||
}),
|
||||
],
|
||||
}),
|
||||
)
|
||||
.mockResolvedValueOnce(
|
||||
createBrowseMedia({
|
||||
media_content_id: 'media-source://frigate',
|
||||
can_expand: true,
|
||||
children: [
|
||||
createBrowseMedia({
|
||||
media_content_id: 'media-source://frigate/result',
|
||||
title: 'Result',
|
||||
}),
|
||||
],
|
||||
}),
|
||||
);
|
||||
|
||||
const engine = new HAFoldersEngine();
|
||||
const results = await engine.expandFolder(createHASS(), query);
|
||||
expect(results?.length).toBe(1);
|
||||
expect(results?.[0]).toBeInstanceOf(ViewMedia);
|
||||
expect(results?.[0].getID()).toBe('media-source://frigate/result');
|
||||
});
|
||||
|
||||
it('should expand folder with ha id based query', async () => {
|
||||
const query: FolderQuery = {
|
||||
folder: { type: 'ha' },
|
||||
path: [
|
||||
{ id: 'media-source://' },
|
||||
{ ha: { title: 'Frigate' } },
|
||||
{ id: 'media-source://frigate/subdir' },
|
||||
],
|
||||
};
|
||||
|
||||
vi.mocked(homeAssistantWSRequest)
|
||||
.mockResolvedValueOnce(
|
||||
createBrowseMedia({
|
||||
media_content_id: 'media-source://',
|
||||
can_expand: true,
|
||||
children: [
|
||||
createBrowseMedia({
|
||||
media_content_id: 'media-source://frigate',
|
||||
title: 'Frigate',
|
||||
can_expand: true,
|
||||
}),
|
||||
],
|
||||
}),
|
||||
)
|
||||
.mockResolvedValueOnce(
|
||||
createBrowseMedia({
|
||||
media_content_id: 'media-source://frigate',
|
||||
can_expand: true,
|
||||
children: [
|
||||
createBrowseMedia({
|
||||
media_content_id: 'media-source://frigate/subdir',
|
||||
can_expand: true,
|
||||
}),
|
||||
],
|
||||
}),
|
||||
)
|
||||
.mockResolvedValueOnce(
|
||||
createBrowseMedia({
|
||||
media_content_id: 'media-source://frigate/subdir',
|
||||
can_expand: true,
|
||||
children: [
|
||||
createBrowseMedia({
|
||||
media_content_id: 'media-source://frigate/subdir/result',
|
||||
title: 'Result',
|
||||
}),
|
||||
],
|
||||
}),
|
||||
);
|
||||
|
||||
const engine = new HAFoldersEngine();
|
||||
const results = await engine.expandFolder(createHASS(), query);
|
||||
expect(results?.length).toBe(1);
|
||||
expect(results?.[0]).toBeInstanceOf(ViewMedia);
|
||||
expect(results?.[0].getID()).toBe('media-source://frigate/subdir/result');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,266 @@
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
import { mock } from 'vitest-mock-extended';
|
||||
import { FoldersExecutor } from '../../../src/card-controller/folders/executor';
|
||||
import { FoldersManager } from '../../../src/card-controller/folders/manager';
|
||||
import { FolderQuery } from '../../../src/card-controller/folders/types';
|
||||
import { FolderConfig } from '../../../src/config/schema/folders';
|
||||
import { ResolvedMediaCache } from '../../../src/ha/resolved-media';
|
||||
import { Endpoint } from '../../../src/types';
|
||||
import { ViewItemCapabilities } from '../../../src/view/types';
|
||||
import {
|
||||
createCardAPI,
|
||||
createFolder,
|
||||
createHASS,
|
||||
TestViewMedia,
|
||||
} from '../../test-utils';
|
||||
|
||||
describe('FoldersManager', () => {
|
||||
it('should initialize with no folders', () => {
|
||||
const api = createCardAPI();
|
||||
const manager = new FoldersManager(api);
|
||||
expect(manager.getFolderCount()).toBe(0);
|
||||
});
|
||||
|
||||
describe('should add folders', () => {
|
||||
it('should add a folder correctly without a title', () => {
|
||||
const manager = new FoldersManager(createCardAPI());
|
||||
const folder = createFolder({ id: 'folder-1' });
|
||||
|
||||
manager.addFolders([folder]);
|
||||
expect(manager.getFolderCount()).toBe(1);
|
||||
expect(manager.getFolder('folder-1')).toEqual({
|
||||
...folder,
|
||||
title: 'Folder 0',
|
||||
});
|
||||
});
|
||||
|
||||
it('should add a folder correctly with a title', () => {
|
||||
const manager = new FoldersManager(createCardAPI());
|
||||
const folder = createFolder({ id: 'folder-1', title: 'Original Title' });
|
||||
|
||||
manager.addFolders([folder]);
|
||||
expect(manager.getFolderCount()).toBe(1);
|
||||
expect(manager.getFolder('folder-1')).toEqual(folder);
|
||||
});
|
||||
|
||||
it('should add a folder without an id', () => {
|
||||
const manager = new FoldersManager(createCardAPI());
|
||||
const folder = createFolder({ title: 'Title' });
|
||||
|
||||
manager.addFolders([folder]);
|
||||
expect(manager.getFolderCount()).toBe(1);
|
||||
expect(manager.getFolder('folder/0')).toEqual({
|
||||
...folder,
|
||||
id: 'folder/0',
|
||||
});
|
||||
});
|
||||
|
||||
it('should reject duplicate folder IDs', () => {
|
||||
const manager = new FoldersManager(createCardAPI());
|
||||
const folder_1 = createFolder({ id: 'DUP' });
|
||||
const folder_2 = createFolder({ id: 'DUP' });
|
||||
|
||||
expect(() => manager.addFolders([folder_1, folder_2])).toThrowError(
|
||||
/Duplicate folder id/,
|
||||
);
|
||||
});
|
||||
|
||||
it('should preserve previous folders', () => {
|
||||
const manager = new FoldersManager(createCardAPI());
|
||||
|
||||
const folder_original = createFolder();
|
||||
manager.addFolders([folder_original]);
|
||||
|
||||
const folder_new = createFolder({ id: 'DUP' });
|
||||
manager.addFolders([folder_new]);
|
||||
|
||||
expect(manager.getFolderCount()).toBe(2);
|
||||
});
|
||||
});
|
||||
|
||||
it('should delete folders', () => {
|
||||
const manager = new FoldersManager(createCardAPI());
|
||||
const folder_1 = createFolder({ id: 'id-1' });
|
||||
const folder_2 = createFolder({ id: 'id-2' });
|
||||
|
||||
manager.addFolders([folder_1, folder_2]);
|
||||
expect(manager.getFolderCount()).toBe(2);
|
||||
|
||||
manager.deleteFolders();
|
||||
expect(manager.getFolderCount()).toBe(0);
|
||||
});
|
||||
|
||||
describe('should get folders', () => {
|
||||
it('should get default folder', () => {
|
||||
const manager = new FoldersManager(createCardAPI());
|
||||
const folder = createFolder({ id: 'id', title: 'Title' });
|
||||
manager.addFolders([folder]);
|
||||
|
||||
expect(manager.getFolder()).toEqual(folder);
|
||||
});
|
||||
|
||||
it('should get default folders when there are none', () => {
|
||||
const manager = new FoldersManager(createCardAPI());
|
||||
expect(manager.getFolder()).toBeNull();
|
||||
});
|
||||
|
||||
it('should get all folders', () => {
|
||||
const manager = new FoldersManager(createCardAPI());
|
||||
const folder_1 = createFolder({ id: 'id-1', title: 'Title' });
|
||||
const folder_2 = createFolder({ id: 'id-2', title: 'Title' });
|
||||
|
||||
manager.addFolders([folder_1, folder_2]);
|
||||
|
||||
expect([...manager.getFolders()]).toEqual([
|
||||
['id-1', folder_1],
|
||||
['id-2', folder_2],
|
||||
]);
|
||||
});
|
||||
|
||||
it('should get non-existent folder', () => {
|
||||
const manager = new FoldersManager(createCardAPI());
|
||||
expect(manager.getFolder('NON-EXISTENT')).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('generateDefaultFolderQuery', () => {
|
||||
it('should generate default folder query with implicit folder', () => {
|
||||
const folder: FolderConfig = createFolder();
|
||||
const query: FolderQuery = {
|
||||
folder,
|
||||
path: ['media-source://'],
|
||||
};
|
||||
|
||||
const executor = mock<FoldersExecutor>();
|
||||
vi.mocked(executor.generateDefaultFolderQuery).mockReturnValue(query);
|
||||
|
||||
const manager = new FoldersManager(createCardAPI(), executor);
|
||||
manager.addFolders([folder]);
|
||||
|
||||
expect(manager.generateDefaultFolderQuery()).toEqual(query);
|
||||
});
|
||||
|
||||
it('should generate default folder query without any folder', () => {
|
||||
const folder: FolderConfig = createFolder();
|
||||
const query: FolderQuery = {
|
||||
folder,
|
||||
path: ['media-source://'],
|
||||
};
|
||||
|
||||
const executor = mock<FoldersExecutor>();
|
||||
vi.mocked(executor.generateDefaultFolderQuery).mockReturnValue(query);
|
||||
|
||||
const manager = new FoldersManager(createCardAPI(), executor);
|
||||
// Folder is not added to the manager.
|
||||
|
||||
expect(manager.generateDefaultFolderQuery()).toBeNull();
|
||||
});
|
||||
|
||||
it('should generate default folder query with explicit folder', () => {
|
||||
const folder: FolderConfig = createFolder();
|
||||
const query: FolderQuery = {
|
||||
folder,
|
||||
path: ['media-source://'],
|
||||
};
|
||||
|
||||
const executor = mock<FoldersExecutor>();
|
||||
vi.mocked(executor.generateDefaultFolderQuery).mockReturnValue(query);
|
||||
|
||||
const manager = new FoldersManager(createCardAPI(), executor);
|
||||
|
||||
expect(manager.generateDefaultFolderQuery(folder)).toEqual(query);
|
||||
});
|
||||
});
|
||||
|
||||
describe('should expand folder', () => {
|
||||
it('should expand folder with hass', async () => {
|
||||
const hass = createHASS();
|
||||
const api = createCardAPI();
|
||||
vi.mocked(api.getHASSManager().getHASS).mockReturnValue(hass);
|
||||
|
||||
const media = new TestViewMedia();
|
||||
const executor = mock<FoldersExecutor>();
|
||||
executor.expandFolder.mockResolvedValue([media]);
|
||||
|
||||
const manager = new FoldersManager(api, executor);
|
||||
|
||||
const folder = createFolder({ id: 'folder-1' });
|
||||
const engineOptions = {};
|
||||
const query: FolderQuery = {
|
||||
folder,
|
||||
path: ['media-source://'],
|
||||
};
|
||||
|
||||
expect(await manager.expandFolder(query, engineOptions)).toEqual([media]);
|
||||
|
||||
expect(executor.expandFolder).toBeCalledWith(hass, query, engineOptions);
|
||||
});
|
||||
|
||||
it('should not expand folder with hass', async () => {
|
||||
const api = createCardAPI();
|
||||
const executor = mock<FoldersExecutor>();
|
||||
const manager = new FoldersManager(api, executor);
|
||||
const folder = createFolder({ id: 'folder-1' });
|
||||
|
||||
expect(
|
||||
await manager.expandFolder({ folder, path: ['media-source://'] }),
|
||||
).toBeNull();
|
||||
|
||||
expect(executor.expandFolder).not.toBeCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('should get item capabilities', () => {
|
||||
it('should get item capabilities', () => {
|
||||
const api = createCardAPI();
|
||||
const executor = mock<FoldersExecutor>();
|
||||
const capabilities: ViewItemCapabilities = {
|
||||
canFavorite: true,
|
||||
canDownload: true,
|
||||
};
|
||||
executor.getItemCapabilities.mockReturnValue(capabilities);
|
||||
const item = new TestViewMedia();
|
||||
|
||||
const manager = new FoldersManager(api, executor);
|
||||
|
||||
expect(manager.getItemCapabilities(item)).toEqual(capabilities);
|
||||
expect(executor.getItemCapabilities).toBeCalledWith(item);
|
||||
});
|
||||
});
|
||||
|
||||
describe('should get download path', () => {
|
||||
it('should get download path', async () => {
|
||||
const api = createCardAPI();
|
||||
const hass = createHASS();
|
||||
vi.mocked(api.getHASSManager().getHASS).mockReturnValue(hass);
|
||||
const cache = new ResolvedMediaCache();
|
||||
vi.mocked(api.getResolvedMediaCache).mockReturnValue(cache);
|
||||
|
||||
const executor = mock<FoldersExecutor>();
|
||||
const item = new TestViewMedia();
|
||||
const endpoint: Endpoint = { endpoint: 'endpoint' };
|
||||
executor.getDownloadPath.mockResolvedValue(endpoint);
|
||||
|
||||
const manager = new FoldersManager(api, executor);
|
||||
expect(await manager.getDownloadPath(item)).toEqual(endpoint);
|
||||
expect(executor.getDownloadPath).toBeCalledWith(hass, item, {
|
||||
resolvedMediaCache: cache,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('should favorite item', () => {
|
||||
it('should favorite item', async () => {
|
||||
const api = createCardAPI();
|
||||
const hass = createHASS();
|
||||
vi.mocked(api.getHASSManager().getHASS).mockReturnValue(hass);
|
||||
|
||||
const executor = mock<FoldersExecutor>();
|
||||
const item = new TestViewMedia();
|
||||
|
||||
const manager = new FoldersManager(api, executor);
|
||||
await manager.favorite(item, true);
|
||||
expect(executor.favorite).toBeCalledWith(hass, item, true);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -5,13 +5,13 @@ import {
|
||||
InitializationManager,
|
||||
} from '../../src/card-controller/initialization-manager';
|
||||
import { ConditionStateManager } from '../../src/conditions/state-manager';
|
||||
import { sideLoadHomeAssistantElements } from '../../src/ha/side-load-ha-elements.js';
|
||||
import { loadLanguages } from '../../src/localize/localize';
|
||||
import { sideLoadHomeAssistantElements } from '../../src/utils/ha';
|
||||
import { Initializer } from '../../src/utils/initializer/initializer';
|
||||
import { createCardAPI, createConfig, createHASS } from '../test-utils';
|
||||
|
||||
vi.mock('../../src/localize/localize.js');
|
||||
vi.mock('../../src/utils/ha/index.js');
|
||||
vi.mock('../../src/ha/side-load-ha-elements.js');
|
||||
|
||||
// @vitest-environment jsdom
|
||||
describe('InitializationManager', () => {
|
||||
@@ -90,6 +90,12 @@ describe('InitializationManager', () => {
|
||||
|
||||
const manager = new InitializationManager(api);
|
||||
|
||||
expect(manager.isInitialized(InitializationAspect.LANGUAGES)).toBeFalsy();
|
||||
expect(manager.isInitialized(InitializationAspect.SIDE_LOAD_ELEMENTS)).toBeFalsy();
|
||||
expect(manager.isInitialized(InitializationAspect.CAMERAS)).toBeFalsy();
|
||||
expect(manager.isInitialized(InitializationAspect.MICROPHONE_CONNECT)).toBeFalsy();
|
||||
expect(manager.isInitialized(InitializationAspect.VIEW)).toBeFalsy();
|
||||
|
||||
await manager.initializeMandatory();
|
||||
|
||||
expect(loadLanguages).toBeCalled();
|
||||
@@ -109,6 +115,14 @@ describe('InitializationManager', () => {
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
expect(manager.isInitialized(InitializationAspect.LANGUAGES)).toBeTruthy();
|
||||
expect(
|
||||
manager.isInitialized(InitializationAspect.SIDE_LOAD_ELEMENTS),
|
||||
).toBeTruthy();
|
||||
expect(manager.isInitialized(InitializationAspect.CAMERAS)).toBeTruthy();
|
||||
expect(manager.isInitialized(InitializationAspect.MICROPHONE_CONNECT)).toBeFalsy();
|
||||
expect(manager.isInitialized(InitializationAspect.VIEW)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('successfully with microphone if configured', async () => {
|
||||
|
||||
@@ -3,8 +3,8 @@ import { afterEach, describe, expect, it, vi } from 'vitest';
|
||||
import { InteractionManager } from '../../src/card-controller/interaction-manager';
|
||||
import { createCardAPI, createConfig, createLitElement } from '../test-utils';
|
||||
|
||||
vi.mock('lodash-es/throttle', () => ({
|
||||
default: vi.fn((fn) => fn),
|
||||
vi.mock('lodash-es', () => ({
|
||||
throttle: vi.fn((fn) => fn),
|
||||
}));
|
||||
|
||||
// @vitest-environment jsdom
|
||||
|
||||
@@ -6,8 +6,10 @@ import {
|
||||
MEDIA_PLAYER_SUPPORT_STOP,
|
||||
MEDIA_PLAYER_SUPPORT_TURN_OFF,
|
||||
} from '../../src/const';
|
||||
import { EntityRegistryManager } from '../../src/ha/registry/entity/types.js';
|
||||
import { HomeAssistant } from '../../src/ha/types.js';
|
||||
import { EntityRegistryManager } from '../../src/utils/ha/registry/entity/types.js';
|
||||
import { ViewMediaType } from '../../src/view/item.js';
|
||||
import { EntityRegistryManagerMock } from '../ha/registry/entity/mock.js';
|
||||
import {
|
||||
createCameraConfig,
|
||||
createCameraManager,
|
||||
@@ -19,7 +21,6 @@ import {
|
||||
createStore,
|
||||
TestViewMedia,
|
||||
} from '../test-utils.js';
|
||||
import { EntityRegistryManagerMock } from '../utils/ha/registry/entity/mock.js';
|
||||
|
||||
const createHASSWithMediaPlayers = (): HomeAssistant => {
|
||||
const attributesSupported = {
|
||||
@@ -484,38 +485,35 @@ describe('MediaPlayerManager', () => {
|
||||
describe('media', () => {
|
||||
describe('successfully with', () => {
|
||||
it.each([
|
||||
['clip' as const, 'video' as const],
|
||||
['snapshot' as const, 'image' as const],
|
||||
])(
|
||||
'%s',
|
||||
async (mediaType: 'clip' | 'snapshot', contentType: 'video' | 'image') => {
|
||||
const media = new TestViewMedia({
|
||||
title: 'media title',
|
||||
thumbnail: 'http://thumbnail',
|
||||
contentID: 'media-source://contentid',
|
||||
mediaType: mediaType,
|
||||
});
|
||||
const api = createCardAPI();
|
||||
vi.mocked(api.getHASSManager().getHASS).mockReturnValue(createHASS());
|
||||
const manager = new MediaPlayerManager(api);
|
||||
[ViewMediaType.Clip, 'video' as const],
|
||||
[ViewMediaType.Snapshot, 'image' as const],
|
||||
])('%s', async (mediaType: ViewMediaType, contentType: 'video' | 'image') => {
|
||||
const media = new TestViewMedia({
|
||||
title: 'media title',
|
||||
thumbnail: 'http://thumbnail',
|
||||
contentID: 'media-source://contentid',
|
||||
mediaType,
|
||||
});
|
||||
const api = createCardAPI();
|
||||
vi.mocked(api.getHASSManager().getHASS).mockReturnValue(createHASS());
|
||||
const manager = new MediaPlayerManager(api);
|
||||
|
||||
await manager.playMedia('media_player.foo', media);
|
||||
await manager.playMedia('media_player.foo', media);
|
||||
|
||||
expect(api.getHASSManager().getHASS()?.callService).toBeCalledWith(
|
||||
'media_player',
|
||||
'play_media',
|
||||
{
|
||||
entity_id: 'media_player.foo',
|
||||
media_content_id: 'media-source://contentid',
|
||||
media_content_type: contentType,
|
||||
extra: {
|
||||
title: 'media title',
|
||||
thumb: 'http://thumbnail',
|
||||
},
|
||||
expect(api.getHASSManager().getHASS()?.callService).toBeCalledWith(
|
||||
'media_player',
|
||||
'play_media',
|
||||
{
|
||||
entity_id: 'media_player.foo',
|
||||
media_content_id: 'media-source://contentid',
|
||||
media_content_type: contentType,
|
||||
extra: {
|
||||
title: 'media title',
|
||||
thumb: 'http://thumbnail',
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('without hass', async () => {
|
||||
|
||||
@@ -28,8 +28,7 @@ describe('MicrophoneManager', () => {
|
||||
|
||||
afterEach(() => {
|
||||
vi.resetAllMocks();
|
||||
vi.unstubAllGlobals;
|
||||
navigator.mediaDevices;
|
||||
vi.unstubAllGlobals();
|
||||
});
|
||||
|
||||
const createMockStream = (mute?: boolean): MediaStream => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
import { StatusBarItemManager } from '../../src/card-controller/status-bar-item-manager';
|
||||
import { StatusBarString } from '../../src/config/schema/actions/types';
|
||||
import { MediaQueriesResults } from '../../src/view/media-queries-results';
|
||||
import { QueryResults } from '../../src/view/query-results';
|
||||
import {
|
||||
TestViewMedia,
|
||||
createCameraManager,
|
||||
@@ -92,7 +92,7 @@ describe('StatusBarItemManager', () => {
|
||||
const cameraManager = createCameraManager();
|
||||
|
||||
const media = [new TestViewMedia({ title: 'Media Title' })];
|
||||
const queryResults = new MediaQueriesResults({
|
||||
const queryResults = new QueryResults({
|
||||
results: media,
|
||||
});
|
||||
|
||||
@@ -119,7 +119,7 @@ describe('StatusBarItemManager', () => {
|
||||
const cameraManager = createCameraManager();
|
||||
|
||||
const media = [new TestViewMedia()];
|
||||
const queryResults = new MediaQueriesResults({
|
||||
const queryResults = new QueryResults({
|
||||
results: media,
|
||||
});
|
||||
|
||||
|
||||
@@ -14,8 +14,9 @@ import {
|
||||
flushPromises,
|
||||
} from '../test-utils';
|
||||
|
||||
vi.mock('lodash-es/throttle', () => ({
|
||||
default: vi.fn((fn) => fn),
|
||||
vi.mock('lodash-es', async () => ({
|
||||
...(await vi.importActual('lodash-es')),
|
||||
throttle: vi.fn((fn) => fn),
|
||||
}));
|
||||
|
||||
const baseTriggersConfig: TriggersOptions = {
|
||||
|
||||
@@ -0,0 +1,282 @@
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest';
|
||||
import { ViewItemManager } from '../../../src/card-controller/view/item-manager';
|
||||
import { homeAssistantSignPath } from '../../../src/ha/sign-path.js';
|
||||
import { downloadURL } from '../../../src/utils/download';
|
||||
import { ViewFolder, ViewMediaType } from '../../../src/view/item';
|
||||
import {
|
||||
createCardAPI,
|
||||
createFolder,
|
||||
createHASS,
|
||||
TestViewMedia,
|
||||
} from '../../test-utils';
|
||||
|
||||
vi.mock('../../../src/utils/download');
|
||||
vi.mock('../../../src/ha/sign-path.js');
|
||||
|
||||
describe('ViewItemManager', () => {
|
||||
describe('getCapabilities', () => {
|
||||
it('should return null for unsupported item types', () => {
|
||||
const api = createCardAPI();
|
||||
const manager = new ViewItemManager(api);
|
||||
|
||||
expect(manager.getCapabilities(new TestViewMedia({ cameraID: null }))).toBeNull();
|
||||
});
|
||||
|
||||
it('should return capabilities for camera media', () => {
|
||||
const api = createCardAPI();
|
||||
const capabilities = {
|
||||
canDownload: true,
|
||||
canFavorite: true,
|
||||
};
|
||||
vi.mocked(api.getCameraManager().getMediaCapabilities).mockReturnValue(
|
||||
capabilities,
|
||||
);
|
||||
|
||||
const manager = new ViewItemManager(api);
|
||||
const item = new TestViewMedia({ cameraID: 'camera.office' });
|
||||
|
||||
expect(manager.getCapabilities(item)).toEqual(capabilities);
|
||||
});
|
||||
|
||||
it('should return capabilities for folder media', () => {
|
||||
const api = createCardAPI();
|
||||
const capabilities = {
|
||||
canDownload: true,
|
||||
canFavorite: true,
|
||||
};
|
||||
vi.mocked(api.getFoldersManager().getItemCapabilities).mockReturnValue(
|
||||
capabilities,
|
||||
);
|
||||
|
||||
const manager = new ViewItemManager(api);
|
||||
const item = new TestViewMedia({ folder: createFolder() });
|
||||
|
||||
expect(manager.getCapabilities(item)).toEqual(capabilities);
|
||||
});
|
||||
});
|
||||
|
||||
describe('download', () => {
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
it('should not download without hass', async () => {
|
||||
const api = createCardAPI();
|
||||
const manager = new ViewItemManager(api);
|
||||
const item = new TestViewMedia({ cameraID: 'camera.office' });
|
||||
|
||||
expect(await manager.download(item)).toBe(true);
|
||||
expect(downloadURL).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should throw error when signing fails', async () => {
|
||||
const consoleSpy = vi.spyOn(global.console, 'warn').mockReturnValue(undefined);
|
||||
|
||||
const api = createCardAPI();
|
||||
vi.mocked(api.getHASSManager().getHASS).mockReturnValue(createHASS());
|
||||
|
||||
const manager = new ViewItemManager(api);
|
||||
const item = new TestViewMedia({ cameraID: 'camera.office' });
|
||||
|
||||
vi.mocked(api.getCameraManager().getMediaDownloadPath).mockResolvedValue({
|
||||
sign: true,
|
||||
endpoint: 'foo',
|
||||
});
|
||||
|
||||
const signError = new Error('sign-error');
|
||||
vi.mocked(homeAssistantSignPath).mockRejectedValue(signError);
|
||||
|
||||
expect(await manager.download(item)).toBe(false);
|
||||
expect(api.getMessageManager().setErrorIfHigherPriority).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
message: 'Could not sign media URL for download',
|
||||
}),
|
||||
);
|
||||
expect(consoleSpy).toHaveBeenCalledWith('sign-error');
|
||||
});
|
||||
|
||||
it('should download camera media', async () => {
|
||||
const api = createCardAPI();
|
||||
vi.mocked(api.getHASSManager().getHASS).mockReturnValue(createHASS());
|
||||
|
||||
const manager = new ViewItemManager(api);
|
||||
const item = new TestViewMedia({ cameraID: 'camera.office' });
|
||||
|
||||
vi.mocked(api.getCameraManager().getMediaDownloadPath).mockResolvedValue({
|
||||
sign: true,
|
||||
endpoint: 'foo',
|
||||
});
|
||||
|
||||
vi.mocked(homeAssistantSignPath).mockResolvedValue('http://foo/signed-url');
|
||||
|
||||
expect(await manager.download(item)).toBe(true);
|
||||
expect(downloadURL).toBeCalledWith(
|
||||
'http://foo/signed-url',
|
||||
'camera-office_id.mp4',
|
||||
);
|
||||
});
|
||||
|
||||
it('should download folder media', async () => {
|
||||
const api = createCardAPI();
|
||||
vi.mocked(api.getHASSManager().getHASS).mockReturnValue(createHASS());
|
||||
|
||||
const manager = new ViewItemManager(api);
|
||||
const item = new TestViewMedia({ cameraID: null, folder: createFolder() });
|
||||
|
||||
vi.mocked(api.getFoldersManager().getDownloadPath).mockResolvedValue({
|
||||
sign: true,
|
||||
endpoint: 'foo',
|
||||
});
|
||||
|
||||
vi.mocked(homeAssistantSignPath).mockResolvedValue('http://foo/signed-url');
|
||||
|
||||
expect(await manager.download(item)).toBe(true);
|
||||
expect(downloadURL).toBeCalledWith('http://foo/signed-url', 'media_id.mp4');
|
||||
});
|
||||
|
||||
it('should download media without signing', async () => {
|
||||
const api = createCardAPI();
|
||||
vi.mocked(api.getHASSManager().getHASS).mockReturnValue(createHASS());
|
||||
|
||||
const manager = new ViewItemManager(api);
|
||||
const item = new TestViewMedia({ cameraID: 'camera.office' });
|
||||
|
||||
vi.mocked(api.getCameraManager().getMediaDownloadPath).mockResolvedValue({
|
||||
sign: false,
|
||||
endpoint: 'foo',
|
||||
});
|
||||
|
||||
expect(homeAssistantSignPath).not.toBeCalled();
|
||||
|
||||
expect(await manager.download(item)).toBe(true);
|
||||
expect(downloadURL).toBeCalledWith('foo', 'camera-office_id.mp4');
|
||||
});
|
||||
|
||||
it('should download media without camera or folder', async () => {
|
||||
const api = createCardAPI();
|
||||
vi.mocked(api.getHASSManager().getHASS).mockReturnValue(createHASS());
|
||||
|
||||
const manager = new ViewItemManager(api);
|
||||
const item = new TestViewMedia({ cameraID: null, folder: null });
|
||||
|
||||
expect(await manager.download(item)).toBe(false);
|
||||
expect(api.getMessageManager().setErrorIfHigherPriority).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
message: 'No media to download',
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
describe('should generate useful download filenames', () => {
|
||||
it('should generate filename for media without ID', async () => {
|
||||
const api = createCardAPI();
|
||||
vi.mocked(api.getHASSManager().getHASS).mockReturnValue(createHASS());
|
||||
|
||||
const manager = new ViewItemManager(api);
|
||||
const item = new TestViewMedia({ id: null });
|
||||
|
||||
vi.mocked(api.getCameraManager().getMediaDownloadPath).mockResolvedValue({
|
||||
sign: false,
|
||||
endpoint: 'foo',
|
||||
});
|
||||
|
||||
expect(await manager.download(item)).toBe(true);
|
||||
expect(downloadURL).toBeCalledWith('foo', 'camera.mp4');
|
||||
});
|
||||
|
||||
it('should generate filename for media with start time', async () => {
|
||||
const api = createCardAPI();
|
||||
vi.mocked(api.getHASSManager().getHASS).mockReturnValue(createHASS());
|
||||
|
||||
const manager = new ViewItemManager(api);
|
||||
const item = new TestViewMedia({ startTime: new Date('2025-05-03T17:41:00Z') });
|
||||
|
||||
vi.mocked(api.getCameraManager().getMediaDownloadPath).mockResolvedValue({
|
||||
sign: false,
|
||||
endpoint: 'foo',
|
||||
});
|
||||
|
||||
expect(await manager.download(item)).toBe(true);
|
||||
expect(downloadURL).toBeCalledWith('foo', 'camera_id_2025-05-03-17-41-00.mp4');
|
||||
});
|
||||
|
||||
it('should generate filename for snapshot', async () => {
|
||||
const api = createCardAPI();
|
||||
vi.mocked(api.getHASSManager().getHASS).mockReturnValue(createHASS());
|
||||
|
||||
const manager = new ViewItemManager(api);
|
||||
const item = new TestViewMedia({ mediaType: ViewMediaType.Snapshot });
|
||||
|
||||
vi.mocked(api.getCameraManager().getMediaDownloadPath).mockResolvedValue({
|
||||
sign: false,
|
||||
endpoint: 'foo',
|
||||
});
|
||||
|
||||
expect(await manager.download(item)).toBe(true);
|
||||
expect(downloadURL).toBeCalledWith('foo', 'camera_id.jpg');
|
||||
});
|
||||
|
||||
it('should generate filename for folder without title', async () => {
|
||||
const api = createCardAPI();
|
||||
vi.mocked(api.getHASSManager().getHASS).mockReturnValue(createHASS());
|
||||
|
||||
const manager = new ViewItemManager(api);
|
||||
const item = new ViewFolder(createFolder());
|
||||
|
||||
vi.mocked(api.getFoldersManager().getDownloadPath).mockResolvedValue({
|
||||
sign: false,
|
||||
endpoint: 'foo',
|
||||
});
|
||||
|
||||
expect(await manager.download(item)).toBe(true);
|
||||
expect(downloadURL).toBeCalledWith('foo', 'media');
|
||||
});
|
||||
|
||||
it('should generate filename for folder with title', async () => {
|
||||
const api = createCardAPI();
|
||||
vi.mocked(api.getHASSManager().getHASS).mockReturnValue(createHASS());
|
||||
|
||||
const manager = new ViewItemManager(api);
|
||||
const item = new ViewFolder(createFolder(), { title: 'title' });
|
||||
|
||||
vi.mocked(api.getFoldersManager().getDownloadPath).mockResolvedValue({
|
||||
sign: false,
|
||||
endpoint: 'foo',
|
||||
});
|
||||
|
||||
expect(await manager.download(item)).toBe(true);
|
||||
expect(downloadURL).toBeCalledWith('foo', 'title');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('favorite', () => {
|
||||
it('should favorite camera media', async () => {
|
||||
const api = createCardAPI();
|
||||
|
||||
const manager = new ViewItemManager(api);
|
||||
const item = new TestViewMedia({
|
||||
cameraID: 'camera.office',
|
||||
mediaType: ViewMediaType.Snapshot,
|
||||
});
|
||||
|
||||
await manager.favorite(item, true);
|
||||
|
||||
expect(api.getCameraManager().favoriteMedia).toBeCalledWith(item, true);
|
||||
});
|
||||
|
||||
it('should favorite folder media', async () => {
|
||||
const api = createCardAPI();
|
||||
|
||||
const manager = new ViewItemManager(api);
|
||||
const item = new TestViewMedia({
|
||||
folder: createFolder(),
|
||||
mediaType: ViewMediaType.Snapshot,
|
||||
});
|
||||
|
||||
await manager.favorite(item, true);
|
||||
|
||||
expect(api.getFoldersManager().favorite).toBeCalledWith(item, true);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -2,15 +2,15 @@ import { expect, it } from 'vitest';
|
||||
import { applyViewModifiers } from '../../../../src/card-controller/view/modifiers';
|
||||
import { MergeContextViewModifier } from '../../../../src/card-controller/view/modifiers/merge-context';
|
||||
import { SetQueryViewModifier } from '../../../../src/card-controller/view/modifiers/set-query';
|
||||
import { EventMediaQueries } from '../../../../src/view/media-queries';
|
||||
import { MediaQueriesResults } from '../../../../src/view/media-queries-results';
|
||||
import { EventMediaQuery } from '../../../../src/view/query';
|
||||
import { QueryResults } from '../../../../src/view/query-results';
|
||||
import { createView } from '../../../test-utils';
|
||||
|
||||
it('should apply view modifiers', () => {
|
||||
const view = createView();
|
||||
|
||||
const query = new EventMediaQueries();
|
||||
const queryResults = new MediaQueriesResults();
|
||||
const query = new EventMediaQuery();
|
||||
const queryResults = new QueryResults();
|
||||
|
||||
const context = {
|
||||
timeline: { window: { start: new Date(), end: new Date() } },
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { expect, it } from 'vitest';
|
||||
import { SetQueryViewModifier } from '../../../../src/card-controller/view/modifiers/set-query';
|
||||
import { EventMediaQuery } from '../../../../src/view/query';
|
||||
import { QueryResults } from '../../../../src/view/query-results';
|
||||
import { createView } from '../../../test-utils';
|
||||
import { EventMediaQueries } from '../../../../src/view/media-queries';
|
||||
import { MediaQueriesResults } from '../../../../src/view/media-queries-results';
|
||||
|
||||
it('should do nothing without arguments', () => {
|
||||
const view = createView();
|
||||
@@ -16,8 +16,8 @@ it('should do nothing without arguments', () => {
|
||||
|
||||
it('should set query and results', () => {
|
||||
const view = createView();
|
||||
const query = new EventMediaQueries();
|
||||
const queryResults = new MediaQueriesResults();
|
||||
const query = new EventMediaQuery();
|
||||
const queryResults = new QueryResults();
|
||||
|
||||
const modifier = new SetQueryViewModifier({
|
||||
query: query,
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
import { add } from 'date-fns';
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
import { QueryType } from '../../../src/camera-manager/types';
|
||||
import { FolderQuery } from '../../../src/card-controller/folders/types';
|
||||
import { QueryExecutor } from '../../../src/card-controller/view/query-executor';
|
||||
import { ClipsOrSnapshotsOrAll } from '../../../src/types';
|
||||
import { EventMediaQueries } from '../../../src/view/media-queries';
|
||||
import { EventMediaQuery, FolderViewQuery } from '../../../src/view/query';
|
||||
import {
|
||||
TestViewMedia,
|
||||
createCameraManager,
|
||||
createCardAPI,
|
||||
createFolder,
|
||||
createStore,
|
||||
generateViewMediaArray,
|
||||
} from '../../test-utils';
|
||||
@@ -59,7 +61,7 @@ describe('executeDefaultEventQuery', () => {
|
||||
eventsMediaType: mediaType,
|
||||
});
|
||||
|
||||
expect(results?.query.getQueries()).toEqual(rawQueries);
|
||||
expect(results?.query.getQuery()).toEqual(rawQueries);
|
||||
expect(results?.queryResults.getResults()).toEqual(media);
|
||||
expect(api.getCameraManager().generateDefaultEventQueries).toBeCalledWith(
|
||||
new Set(['camera.office']),
|
||||
@@ -90,7 +92,7 @@ describe('executeDefaultEventQuery', () => {
|
||||
const executor = new QueryExecutor(api);
|
||||
const results = await executor.executeDefaultEventQuery();
|
||||
|
||||
expect(results?.query.getQueries()).toEqual(rawQueries);
|
||||
expect(results?.query.getQuery()).toEqual(rawQueries);
|
||||
expect(results?.queryResults.getResults()).toEqual(media);
|
||||
expect(api.getCameraManager().generateDefaultEventQueries).toBeCalledWith(
|
||||
new Set(['camera.office', 'camera.kitchen']),
|
||||
@@ -170,7 +172,7 @@ describe('executeDefaultRecordingQuery', () => {
|
||||
cameraID: 'camera.office',
|
||||
});
|
||||
|
||||
expect(results?.query.getQueries()).toEqual(rawQueries);
|
||||
expect(results?.query.getQuery()).toEqual(rawQueries);
|
||||
expect(results?.queryResults.getResults()).toEqual(media);
|
||||
expect(api.getCameraManager().generateDefaultRecordingQueries).toBeCalledWith(
|
||||
new Set(['camera.office']),
|
||||
@@ -197,7 +199,7 @@ describe('executeDefaultRecordingQuery', () => {
|
||||
const executor = new QueryExecutor(api);
|
||||
const results = await executor.executeDefaultRecordingQuery();
|
||||
|
||||
expect(results?.query.getQueries()).toEqual(rawQueries);
|
||||
expect(results?.query.getQuery()).toEqual(rawQueries);
|
||||
expect(results?.queryResults.getResults()).toEqual(media);
|
||||
expect(api.getCameraManager().generateDefaultRecordingQueries).toBeCalledWith(
|
||||
new Set(['camera.office', 'camera.kitchen']),
|
||||
@@ -233,10 +235,10 @@ describe('executeDefaultRecordingQuery', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('execute', () => {
|
||||
describe('executeQuery', () => {
|
||||
it('should return null when query is empty', async () => {
|
||||
const executor = new QueryExecutor(createCardAPI());
|
||||
expect(await executor.execute(new EventMediaQueries())).toBeNull();
|
||||
expect(await executor.executeQuery(new EventMediaQuery())).toBeNull();
|
||||
});
|
||||
|
||||
describe('should handle result rejections', () => {
|
||||
@@ -245,23 +247,7 @@ describe('execute', () => {
|
||||
const media = generateViewMediaArray();
|
||||
vi.mocked(api.getCameraManager().executeMediaQueries).mockResolvedValue(media);
|
||||
|
||||
const query = new EventMediaQueries([
|
||||
{
|
||||
type: QueryType.Event as const,
|
||||
cameraIDs: new Set(['camera.office']),
|
||||
},
|
||||
]);
|
||||
const executor = new QueryExecutor(api);
|
||||
|
||||
expect(await executor.execute(query, { rejectResults: (_) => true })).toBeNull();
|
||||
});
|
||||
|
||||
it('not rejected', async () => {
|
||||
const api = createPopulatedAPI();
|
||||
const media = generateViewMediaArray();
|
||||
vi.mocked(api.getCameraManager().executeMediaQueries).mockResolvedValue(media);
|
||||
|
||||
const query = new EventMediaQueries([
|
||||
const query = new EventMediaQuery([
|
||||
{
|
||||
type: QueryType.Event as const,
|
||||
cameraIDs: new Set(['camera.office']),
|
||||
@@ -270,7 +256,27 @@ describe('execute', () => {
|
||||
const executor = new QueryExecutor(api);
|
||||
|
||||
expect(
|
||||
await executor.execute(query, { rejectResults: (_) => false }),
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
await executor.executeQuery(query, { rejectResults: (_) => true }),
|
||||
).toBeNull();
|
||||
});
|
||||
|
||||
it('not rejected', async () => {
|
||||
const api = createPopulatedAPI();
|
||||
const media = generateViewMediaArray();
|
||||
vi.mocked(api.getCameraManager().executeMediaQueries).mockResolvedValue(media);
|
||||
|
||||
const query = new EventMediaQuery([
|
||||
{
|
||||
type: QueryType.Event as const,
|
||||
cameraIDs: new Set(['camera.office']),
|
||||
},
|
||||
]);
|
||||
const executor = new QueryExecutor(api);
|
||||
|
||||
expect(
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
await executor.executeQuery(query, { rejectResults: (_) => false }),
|
||||
).not.toBeNull();
|
||||
});
|
||||
});
|
||||
@@ -284,7 +290,7 @@ describe('execute', () => {
|
||||
});
|
||||
vi.mocked(api.getCameraManager().executeMediaQueries).mockResolvedValue(media);
|
||||
|
||||
const query = new EventMediaQueries([
|
||||
const query = new EventMediaQuery([
|
||||
{
|
||||
type: QueryType.Event as const,
|
||||
cameraIDs: new Set(['camera.office']),
|
||||
@@ -292,10 +298,12 @@ describe('execute', () => {
|
||||
]);
|
||||
const executor = new QueryExecutor(api);
|
||||
|
||||
const results = await executor.execute(query, {
|
||||
const results = await executor.executeMediaQuery(query, {
|
||||
selectResult: { id: 'id-camera.office-42' },
|
||||
});
|
||||
expect(results?.getSelectedResult()?.getID()).toBe('id-camera.office-42');
|
||||
expect(results?.queryResults.getSelectedResult()?.getID()).toBe(
|
||||
'id-camera.office-42',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -307,7 +315,7 @@ describe('execute', () => {
|
||||
});
|
||||
vi.mocked(api.getCameraManager().executeMediaQueries).mockResolvedValue(media);
|
||||
|
||||
const query = new EventMediaQueries([
|
||||
const query = new EventMediaQuery([
|
||||
{
|
||||
type: QueryType.Event as const,
|
||||
cameraIDs: new Set(['camera.office']),
|
||||
@@ -315,10 +323,12 @@ describe('execute', () => {
|
||||
]);
|
||||
const executor = new QueryExecutor(api);
|
||||
|
||||
const results = await executor.execute(query, {
|
||||
const results = await executor.executeMediaQuery(query, {
|
||||
selectResult: { func: (media) => media.getID() === 'id-camera.office-43' },
|
||||
});
|
||||
expect(results?.getSelectedResult()?.getID()).toBe('id-camera.office-43');
|
||||
expect(results?.queryResults.getSelectedResult()?.getID()).toBe(
|
||||
'id-camera.office-43',
|
||||
);
|
||||
});
|
||||
|
||||
it('by time', async () => {
|
||||
@@ -344,7 +354,7 @@ describe('execute', () => {
|
||||
];
|
||||
vi.mocked(api.getCameraManager().executeMediaQueries).mockResolvedValue(media);
|
||||
|
||||
const query = new EventMediaQueries([
|
||||
const query = new EventMediaQuery([
|
||||
{
|
||||
type: QueryType.Event as const,
|
||||
cameraIDs: new Set(['camera.office']),
|
||||
@@ -352,9 +362,66 @@ describe('execute', () => {
|
||||
]);
|
||||
const executor = new QueryExecutor(api);
|
||||
|
||||
const results = await executor.execute(query, {
|
||||
const results = await executor.executeMediaQuery(query, {
|
||||
selectResult: { time: { time: add(now, { seconds: 1 }) } },
|
||||
});
|
||||
expect(results?.getSelectedResult()?.getID()).toBe('id-camera.office-1');
|
||||
expect(results?.queryResults.getSelectedResult()?.getID()).toBe(
|
||||
'id-camera.office-1',
|
||||
);
|
||||
});
|
||||
|
||||
describe('should handle folder query', () => {
|
||||
it('should return null without raw query', async () => {
|
||||
const executor = new QueryExecutor(createCardAPI());
|
||||
expect(await executor.executeQuery(new FolderViewQuery())).toBeNull();
|
||||
});
|
||||
|
||||
it('should return null when query expansion fails', async () => {
|
||||
const api = createCardAPI();
|
||||
vi.mocked(api.getFoldersManager().getFolder).mockReturnValue(createFolder());
|
||||
vi.mocked(api.getFoldersManager().expandFolder).mockResolvedValue(null);
|
||||
|
||||
const executor = new QueryExecutor(api);
|
||||
expect(await executor.executeDefaultFolderQuery()).toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('executeDefaultFolderQuery', () => {
|
||||
it('should return null without folders', async () => {
|
||||
const executor = new QueryExecutor(createCardAPI());
|
||||
expect(await executor.executeDefaultFolderQuery()).toBeNull();
|
||||
});
|
||||
|
||||
it('should execute query against first folder', async () => {
|
||||
const api = createCardAPI();
|
||||
const items = [new TestViewMedia()];
|
||||
const folder = createFolder();
|
||||
const query: FolderQuery = {
|
||||
folder,
|
||||
path: ['path'],
|
||||
};
|
||||
vi.mocked(api.getFoldersManager().generateDefaultFolderQuery).mockReturnValue(query);
|
||||
vi.mocked(api.getFoldersManager().expandFolder).mockResolvedValue(items);
|
||||
|
||||
const executor = new QueryExecutor(api);
|
||||
const result = await executor.executeDefaultFolderQuery();
|
||||
|
||||
expect(result?.query.getQuery()).toEqual(query);
|
||||
expect(result?.queryResults.getResults()).toEqual(items);
|
||||
});
|
||||
|
||||
it('should return null without folder results', async () => {
|
||||
const api = createCardAPI();
|
||||
const folder = createFolder();
|
||||
const query: FolderQuery = {
|
||||
folder,
|
||||
path: ['path'],
|
||||
};
|
||||
vi.mocked(api.getFoldersManager().generateDefaultFolderQuery).mockReturnValue(query);
|
||||
vi.mocked(api.getFoldersManager().expandFolder).mockResolvedValue(null);
|
||||
|
||||
const executor = new QueryExecutor(api);
|
||||
expect(await executor.executeDefaultFolderQuery()).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { sortItems } from '../../../src/card-controller/view/sort.js';
|
||||
import { createFolder, TestViewMedia } from '../../test-utils.js';
|
||||
import { ViewFolder } from '../../../src/view/item.js';
|
||||
|
||||
describe('sortMedia', () => {
|
||||
const media_1 = new TestViewMedia({
|
||||
id: 'id-1',
|
||||
startTime: new Date('2023-04-29T14:25'),
|
||||
cameraID: 'camera-1',
|
||||
});
|
||||
const media_2 = new TestViewMedia({
|
||||
id: 'id-2',
|
||||
startTime: new Date('2023-04-29T14:26'),
|
||||
cameraID: 'camera-1',
|
||||
});
|
||||
const media_3_dup_id = new TestViewMedia({
|
||||
id: 'id-2',
|
||||
startTime: new Date('2023-04-29T14:26'),
|
||||
cameraID: 'camera-1',
|
||||
});
|
||||
const media_4_no_id = new TestViewMedia({
|
||||
id: null,
|
||||
startTime: new Date('2023-04-29T14:27'),
|
||||
cameraID: 'camera-1',
|
||||
});
|
||||
const folder_1 = new ViewFolder(createFolder(), { id: 'folder_1' });
|
||||
const folder_2 = new ViewFolder(createFolder(), { id: 'folder_2' });
|
||||
|
||||
it('should sort sorted media', () => {
|
||||
const media = [media_1, media_2];
|
||||
expect(sortItems(media)).toEqual(media);
|
||||
});
|
||||
it('should sort unsorted media', () => {
|
||||
expect(sortItems([media_2, media_1])).toEqual([media_1, media_2]);
|
||||
});
|
||||
it('should remove duplicate id', () => {
|
||||
expect(sortItems([media_1, media_2, media_3_dup_id])).toEqual([media_1, media_2]);
|
||||
});
|
||||
it('should sort by id when time not available', () => {
|
||||
const folder = createFolder();
|
||||
expect(
|
||||
sortItems([
|
||||
new TestViewMedia({ id: 'snake' }),
|
||||
new TestViewMedia({ id: 'zebra' }),
|
||||
new TestViewMedia({ id: 'aardvark' }),
|
||||
new ViewFolder(folder, { id: 'folder' }),
|
||||
]),
|
||||
).toEqual([
|
||||
new ViewFolder(folder, { id: 'folder' }),
|
||||
new TestViewMedia({ id: 'aardvark' }),
|
||||
new TestViewMedia({ id: 'snake' }),
|
||||
new TestViewMedia({ id: 'zebra' }),
|
||||
]);
|
||||
});
|
||||
it('should remove de-duplicate by object if no id', () => {
|
||||
expect(sortItems([media_1, media_2, media_4_no_id, media_4_no_id])).toEqual([
|
||||
media_1,
|
||||
media_2,
|
||||
media_4_no_id,
|
||||
]);
|
||||
});
|
||||
it('should sort folders at the start', () => {
|
||||
expect(sortItems([media_1, media_2, folder_1, folder_2])).toEqual([
|
||||
folder_1,
|
||||
folder_2,
|
||||
media_1,
|
||||
media_2,
|
||||
]);
|
||||
});
|
||||
});
|
||||
@@ -2,6 +2,7 @@ import { ViewContext } from 'view';
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
import { mock } from 'vitest-mock-extended';
|
||||
import { QueryType } from '../../../src/camera-manager/types';
|
||||
import { CardController } from '../../../src/card-controller/controller';
|
||||
import { ViewFactory } from '../../../src/card-controller/view/factory';
|
||||
import { SetQueryViewModifier } from '../../../src/card-controller/view/modifiers/set-query';
|
||||
import {
|
||||
@@ -11,9 +12,9 @@ import {
|
||||
import { ViewManager } from '../../../src/card-controller/view/view-manager';
|
||||
import { ViewQueryExecutor } from '../../../src/card-controller/view/view-query-executor';
|
||||
import { AdvancedCameraCardView } from '../../../src/config/schema/common/const';
|
||||
import { ViewMedia } from '../../../src/view/media';
|
||||
import { EventMediaQueries } from '../../../src/view/media-queries';
|
||||
import { MediaQueriesResults } from '../../../src/view/media-queries-results';
|
||||
import { ViewMedia, ViewMediaType } from '../../../src/view/item';
|
||||
import { EventMediaQuery } from '../../../src/view/query';
|
||||
import { QueryResults } from '../../../src/view/query-results';
|
||||
import { View } from '../../../src/view/view';
|
||||
import {
|
||||
createCameraManager,
|
||||
@@ -23,6 +24,14 @@ import {
|
||||
createView,
|
||||
} from '../../test-utils';
|
||||
|
||||
const createInitializedCardAPI = (initialized?: boolean): CardController => {
|
||||
const api = createCardAPI();
|
||||
vi.mocked(api.getInitializationManager().isInitialized).mockReturnValue(
|
||||
initialized ?? true,
|
||||
);
|
||||
return api;
|
||||
};
|
||||
|
||||
describe('should act correctly when view is set', () => {
|
||||
it('basic view', () => {
|
||||
const view = createView({
|
||||
@@ -34,7 +43,7 @@ describe('should act correctly when view is set', () => {
|
||||
const factory = mock<ViewFactory>();
|
||||
factory.getViewDefault.mockReturnValue(view);
|
||||
|
||||
const api = createCardAPI();
|
||||
const api = createInitializedCardAPI();
|
||||
const manager = new ViewManager(api, { viewFactory: factory });
|
||||
|
||||
manager.setViewDefault();
|
||||
@@ -61,7 +70,7 @@ describe('should act correctly when view is set', () => {
|
||||
const factory = mock<ViewFactory>();
|
||||
factory.getViewDefault.mockReturnValue(view_1);
|
||||
|
||||
const api = createCardAPI();
|
||||
const api = createInitializedCardAPI();
|
||||
const manager = new ViewManager(api, { viewFactory: factory });
|
||||
|
||||
manager.setViewDefault();
|
||||
@@ -88,7 +97,7 @@ describe('should act correctly when view is set', () => {
|
||||
});
|
||||
|
||||
it('setViewWithMergedContext', () => {
|
||||
const api = createCardAPI();
|
||||
const api = createInitializedCardAPI();
|
||||
const factory = mock<ViewFactory>();
|
||||
|
||||
const manager = new ViewManager(api, { viewFactory: factory });
|
||||
@@ -120,7 +129,7 @@ it('getEpoch', () => {
|
||||
|
||||
it('reset', () => {
|
||||
const factory = mock<ViewFactory>();
|
||||
const manager = new ViewManager(createCardAPI(), { viewFactory: factory });
|
||||
const manager = new ViewManager(createInitializedCardAPI(), { viewFactory: factory });
|
||||
|
||||
manager.reset();
|
||||
expect(manager.getView()).toBeNull();
|
||||
@@ -138,11 +147,37 @@ it('reset', () => {
|
||||
expect(manager.hasView()).toBeFalsy();
|
||||
});
|
||||
|
||||
describe('should not set view without cameras being initialized', () => {
|
||||
it('should ignore requests to set generic view without cameras being initialized', () => {
|
||||
const factory = mock<ViewFactory>();
|
||||
factory.getViewDefault.mockReturnValue(createView());
|
||||
|
||||
const manager = new ViewManager(createInitializedCardAPI(false), {
|
||||
viewFactory: factory,
|
||||
});
|
||||
manager.setViewDefault();
|
||||
|
||||
expect(manager.getView()).toBeNull();
|
||||
});
|
||||
|
||||
it('should ignore requests to set view then modify without cameras being initialized', () => {
|
||||
const factory = mock<ViewFactory>();
|
||||
factory.getViewDefault.mockReturnValue(createView());
|
||||
|
||||
const manager = new ViewManager(createInitializedCardAPI(false), {
|
||||
viewFactory: factory,
|
||||
});
|
||||
manager.setViewDefaultWithNewQuery();
|
||||
|
||||
expect(manager.getView()).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
it('setViewDefault', () => {
|
||||
const factory = mock<ViewFactory>();
|
||||
factory.getViewDefault.mockReturnValue(createView());
|
||||
|
||||
const manager = new ViewManager(createCardAPI(), { viewFactory: factory });
|
||||
const manager = new ViewManager(createInitializedCardAPI(), { viewFactory: factory });
|
||||
manager.setViewDefault();
|
||||
|
||||
expect(manager.getView()?.view).toBe('live');
|
||||
@@ -153,7 +188,7 @@ it('setViewByParameters', () => {
|
||||
const factory = mock<ViewFactory>();
|
||||
factory.getViewByParameters.mockReturnValue(createView());
|
||||
|
||||
const manager = new ViewManager(createCardAPI(), { viewFactory: factory });
|
||||
const manager = new ViewManager(createInitializedCardAPI(), { viewFactory: factory });
|
||||
manager.setViewByParameters();
|
||||
|
||||
expect(manager.getView()?.view).toBe('live');
|
||||
@@ -167,7 +202,7 @@ it('setViewDefaultWithNewQuery', async () => {
|
||||
const viewQueryExecutor = mock<ViewQueryExecutor>();
|
||||
viewQueryExecutor.getNewQueryModifiers.mockResolvedValue([]);
|
||||
|
||||
const manager = new ViewManager(createCardAPI(), {
|
||||
const manager = new ViewManager(createInitializedCardAPI(), {
|
||||
viewFactory: viewFactory,
|
||||
viewQueryExecutor: viewQueryExecutor,
|
||||
});
|
||||
@@ -184,7 +219,7 @@ it('setViewByParametersWithNewQuery', async () => {
|
||||
const viewQueryExecutor = mock<ViewQueryExecutor>();
|
||||
viewQueryExecutor.getNewQueryModifiers.mockResolvedValue([]);
|
||||
|
||||
const manager = new ViewManager(createCardAPI(), {
|
||||
const manager = new ViewManager(createInitializedCardAPI(), {
|
||||
viewFactory: viewFactory,
|
||||
viewQueryExecutor: viewQueryExecutor,
|
||||
});
|
||||
@@ -201,7 +236,7 @@ it('setViewByParametersWithExistingQuery', async () => {
|
||||
const viewQueryExecutor = mock<ViewQueryExecutor>();
|
||||
viewQueryExecutor.getNewQueryModifiers.mockResolvedValue([]);
|
||||
|
||||
const manager = new ViewManager(createCardAPI(), {
|
||||
const manager = new ViewManager(createInitializedCardAPI(), {
|
||||
viewFactory: viewFactory,
|
||||
viewQueryExecutor: viewQueryExecutor,
|
||||
});
|
||||
@@ -220,7 +255,7 @@ describe('should handle exceptions', () => {
|
||||
throw error;
|
||||
});
|
||||
|
||||
const api = createCardAPI();
|
||||
const api = createInitializedCardAPI();
|
||||
const manager = new ViewManager(api, { viewFactory: viewFactory });
|
||||
manager.setViewDefault();
|
||||
|
||||
@@ -235,7 +270,7 @@ describe('should handle exceptions', () => {
|
||||
throw error;
|
||||
});
|
||||
|
||||
const api = createCardAPI();
|
||||
const api = createInitializedCardAPI();
|
||||
const manager = new ViewManager(api, { viewFactory: viewFactory });
|
||||
await manager.setViewDefaultWithNewQuery();
|
||||
|
||||
@@ -250,7 +285,7 @@ describe('should handle exceptions', () => {
|
||||
const viewQueryExecutor = mock<ViewQueryExecutor>();
|
||||
viewQueryExecutor.getNewQueryModifiers.mockRejectedValue(error);
|
||||
|
||||
const api = createCardAPI();
|
||||
const api = createInitializedCardAPI();
|
||||
const manager = new ViewManager(api, {
|
||||
viewFactory: viewFactory,
|
||||
viewQueryExecutor: viewQueryExecutor,
|
||||
@@ -280,7 +315,7 @@ describe('isViewSupportedByCamera', () => {
|
||||
['timeline' as const, false],
|
||||
['media' as const, false],
|
||||
])('%s', (viewName: AdvancedCameraCardView, expected: boolean) => {
|
||||
const api = createCardAPI();
|
||||
const api = createInitializedCardAPI();
|
||||
vi.mocked(api.getCameraManager).mockReturnValue(createCameraManager());
|
||||
vi.mocked(api.getCameraManager().getStore).mockReturnValue(
|
||||
createStore([
|
||||
@@ -317,7 +352,9 @@ describe('hasMajorMediaChange', () => {
|
||||
const factory = mock<ViewFactory>();
|
||||
factory.getViewDefault.mockReturnValue(createView({ view: 'live' }));
|
||||
|
||||
const manager = new ViewManager(createCardAPI(), { viewFactory: factory });
|
||||
const manager = new ViewManager(createInitializedCardAPI(), {
|
||||
viewFactory: factory,
|
||||
});
|
||||
manager.setViewDefault();
|
||||
|
||||
expect(manager.hasMajorMediaChange(createView({ view: 'clips' }))).toBeTruthy();
|
||||
@@ -327,7 +364,9 @@ describe('hasMajorMediaChange', () => {
|
||||
const factory = mock<ViewFactory>();
|
||||
factory.getViewDefault.mockReturnValue(createView({ camera: 'camera-1' }));
|
||||
|
||||
const manager = new ViewManager(createCardAPI(), { viewFactory: factory });
|
||||
const manager = new ViewManager(createInitializedCardAPI(), {
|
||||
viewFactory: factory,
|
||||
});
|
||||
manager.setViewDefault();
|
||||
|
||||
expect(manager.hasMajorMediaChange(createView({ camera: 'camera-2' }))).toBeTruthy();
|
||||
@@ -345,7 +384,9 @@ describe('hasMajorMediaChange', () => {
|
||||
createView({ context: { live: { overrides: overrides_1 } } }),
|
||||
);
|
||||
|
||||
const manager = new ViewManager(createCardAPI(), { viewFactory: factory });
|
||||
const manager = new ViewManager(createInitializedCardAPI(), {
|
||||
viewFactory: factory,
|
||||
});
|
||||
manager.setViewDefault();
|
||||
|
||||
expect(
|
||||
@@ -367,7 +408,9 @@ describe('hasMajorMediaChange', () => {
|
||||
createView({ view: 'clips', context: { live: { overrides: overrides_1 } } }),
|
||||
);
|
||||
|
||||
const manager = new ViewManager(createCardAPI(), { viewFactory: factory });
|
||||
const manager = new ViewManager(createInitializedCardAPI(), {
|
||||
viewFactory: factory,
|
||||
});
|
||||
manager.setViewDefault();
|
||||
|
||||
expect(
|
||||
@@ -378,16 +421,25 @@ describe('hasMajorMediaChange', () => {
|
||||
});
|
||||
|
||||
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 media = [
|
||||
new ViewMedia(ViewMediaType.Clip, {
|
||||
cameraID: 'camera-1',
|
||||
}),
|
||||
new ViewMedia(ViewMediaType.Clip, {
|
||||
cameraID: 'camera-2',
|
||||
}),
|
||||
];
|
||||
const queryResults_1 = new QueryResults({ results: media, selectedIndex: 0 });
|
||||
const queryResults_2 = new QueryResults({ results: media, selectedIndex: 1 });
|
||||
|
||||
const factory = mock<ViewFactory>();
|
||||
factory.getViewDefault.mockReturnValue(
|
||||
createView({ view: 'media', queryResults: queryResults_1 }),
|
||||
);
|
||||
|
||||
const manager = new ViewManager(createCardAPI(), { viewFactory: factory });
|
||||
const manager = new ViewManager(createInitializedCardAPI(), {
|
||||
viewFactory: factory,
|
||||
});
|
||||
manager.setViewDefault();
|
||||
|
||||
expect(
|
||||
@@ -398,14 +450,23 @@ describe('hasMajorMediaChange', () => {
|
||||
});
|
||||
|
||||
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 media = [
|
||||
new ViewMedia(ViewMediaType.Clip, {
|
||||
cameraID: 'camera-1',
|
||||
}),
|
||||
new ViewMedia(ViewMediaType.Clip, {
|
||||
cameraID: 'camera-2',
|
||||
}),
|
||||
];
|
||||
const queryResults_1 = new QueryResults({ results: media, selectedIndex: 0 });
|
||||
const queryResults_2 = new QueryResults({ results: media, selectedIndex: 1 });
|
||||
|
||||
const factory = mock<ViewFactory>();
|
||||
factory.getViewDefault.mockReturnValue(createView({ queryResults: queryResults_1 }));
|
||||
|
||||
const manager = new ViewManager(createCardAPI(), { viewFactory: factory });
|
||||
const manager = new ViewManager(createInitializedCardAPI(), {
|
||||
viewFactory: factory,
|
||||
});
|
||||
manager.setViewDefault();
|
||||
|
||||
expect(
|
||||
@@ -424,7 +485,7 @@ describe('should initialize', () => {
|
||||
const viewFactory = mock<ViewFactory>();
|
||||
viewFactory.getViewDefault.mockReturnValue(view);
|
||||
|
||||
const api = createCardAPI();
|
||||
const api = createInitializedCardAPI();
|
||||
const manager = new ViewManager(api, {
|
||||
viewFactory: viewFactory,
|
||||
});
|
||||
@@ -452,14 +513,14 @@ it('should adopt query and results when changing to gallery from viewer', async
|
||||
const baseView = createView({
|
||||
view: 'media',
|
||||
camera: 'camera.office',
|
||||
query: new EventMediaQueries([
|
||||
query: new EventMediaQuery([
|
||||
{
|
||||
type: QueryType.Event,
|
||||
cameraIDs: new Set(['camera.office']),
|
||||
hasClip: true,
|
||||
},
|
||||
]),
|
||||
queryResults: new MediaQueriesResults(),
|
||||
queryResults: new QueryResults(),
|
||||
});
|
||||
|
||||
const viewFactory = mock<ViewFactory>();
|
||||
@@ -470,7 +531,7 @@ it('should adopt query and results when changing to gallery from viewer', async
|
||||
const viewQueryExecutor = mock<ViewQueryExecutor>();
|
||||
viewQueryExecutor.getNewQueryModifiers.mockResolvedValue([]);
|
||||
|
||||
const manager = new ViewManager(createCardAPI(), {
|
||||
const manager = new ViewManager(createInitializedCardAPI(), {
|
||||
viewFactory: viewFactory,
|
||||
viewQueryExecutor: viewQueryExecutor,
|
||||
});
|
||||
@@ -496,8 +557,8 @@ describe('should apply async view modifications', () => {
|
||||
const viewFactory = mock<ViewFactory>();
|
||||
viewFactory.getViewDefault.mockReturnValue(createView({ view: 'live' }));
|
||||
|
||||
const query = new EventMediaQueries();
|
||||
const queryResults = new MediaQueriesResults();
|
||||
const query = new EventMediaQuery();
|
||||
const queryResults = new QueryResults();
|
||||
|
||||
const viewQueryExecutor = mock<ViewQueryExecutor>();
|
||||
viewQueryExecutor.getNewQueryModifiers.mockResolvedValue([
|
||||
@@ -507,7 +568,7 @@ describe('should apply async view modifications', () => {
|
||||
}),
|
||||
]);
|
||||
|
||||
const manager = new ViewManager(createCardAPI(), {
|
||||
const manager = new ViewManager(createInitializedCardAPI(), {
|
||||
viewFactory: viewFactory,
|
||||
viewQueryExecutor: viewQueryExecutor,
|
||||
});
|
||||
@@ -523,12 +584,12 @@ describe('should apply async view modifications', () => {
|
||||
const viewFactory = mock<ViewFactory>();
|
||||
viewFactory.getViewDefault.mockReturnValueOnce(createView({ view: 'live' }));
|
||||
|
||||
const query = new EventMediaQueries();
|
||||
const queryResults = new MediaQueriesResults();
|
||||
const query = new EventMediaQuery();
|
||||
const queryResults = new QueryResults();
|
||||
|
||||
const viewQueryExecutor = mock<ViewQueryExecutor>();
|
||||
|
||||
const manager = new ViewManager(createCardAPI(), {
|
||||
const manager = new ViewManager(createInitializedCardAPI(), {
|
||||
viewFactory: viewFactory,
|
||||
viewQueryExecutor: viewQueryExecutor,
|
||||
});
|
||||
@@ -572,7 +633,7 @@ describe('should apply async view modifications', () => {
|
||||
viewFactory.getViewDefault.mockReturnValueOnce(createView({ view: 'live' }));
|
||||
const viewQueryExecutor = mock<ViewQueryExecutor>();
|
||||
|
||||
const manager = new ViewManager(createCardAPI(), {
|
||||
const manager = new ViewManager(createInitializedCardAPI(), {
|
||||
viewFactory: viewFactory,
|
||||
viewQueryExecutor: viewQueryExecutor,
|
||||
});
|
||||
@@ -608,7 +669,7 @@ describe('should apply async view modifications', () => {
|
||||
viewFactory.getViewDefault.mockReturnValueOnce(createView({ view: 'live' }));
|
||||
const viewQueryExecutor = mock<ViewQueryExecutor>();
|
||||
|
||||
const manager = new ViewManager(createCardAPI(), {
|
||||
const manager = new ViewManager(createInitializedCardAPI(), {
|
||||
viewFactory: viewFactory,
|
||||
viewQueryExecutor: viewQueryExecutor,
|
||||
});
|
||||
@@ -644,7 +705,7 @@ describe('should apply async view modifications', () => {
|
||||
viewFactory.getViewDefault.mockReturnValueOnce(createView({ view: 'live' }));
|
||||
const viewQueryExecutor = mock<ViewQueryExecutor>();
|
||||
|
||||
const manager = new ViewManager(createCardAPI(), {
|
||||
const manager = new ViewManager(createInitializedCardAPI(), {
|
||||
viewFactory: viewFactory,
|
||||
viewQueryExecutor: viewQueryExecutor,
|
||||
});
|
||||
@@ -681,14 +742,14 @@ it('should adopt query and results when changing to gallery from viewer', async
|
||||
const baseView = createView({
|
||||
view: 'media',
|
||||
camera: 'camera.office',
|
||||
query: new EventMediaQueries([
|
||||
query: new EventMediaQuery([
|
||||
{
|
||||
type: QueryType.Event,
|
||||
cameraIDs: new Set(['camera.office']),
|
||||
hasClip: true,
|
||||
},
|
||||
]),
|
||||
queryResults: new MediaQueriesResults(),
|
||||
queryResults: new QueryResults(),
|
||||
});
|
||||
|
||||
const viewFactory = mock<ViewFactory>();
|
||||
@@ -699,7 +760,7 @@ it('should adopt query and results when changing to gallery from viewer', async
|
||||
const viewQueryExecutor = mock<ViewQueryExecutor>();
|
||||
viewQueryExecutor.getNewQueryModifiers.mockResolvedValue([]);
|
||||
|
||||
const manager = new ViewManager(createCardAPI(), {
|
||||
const manager = new ViewManager(createInitializedCardAPI(), {
|
||||
viewFactory: viewFactory,
|
||||
viewQueryExecutor: viewQueryExecutor,
|
||||
});
|
||||
|
||||
@@ -5,10 +5,11 @@ import { QueryExecutor } from '../../../src/card-controller/view/query-executor'
|
||||
import { ViewQueryExecutor } from '../../../src/card-controller/view/view-query-executor';
|
||||
import { AdvancedCameraCardView } from '../../../src/config/schema/common/const';
|
||||
import {
|
||||
EventMediaQueries,
|
||||
RecordingMediaQueries,
|
||||
} from '../../../src/view/media-queries';
|
||||
import { MediaQueriesResults } from '../../../src/view/media-queries-results';
|
||||
EventMediaQuery,
|
||||
FolderViewQuery,
|
||||
RecordingMediaQuery,
|
||||
} from '../../../src/view/query';
|
||||
import { QueryResults } from '../../../src/view/query-results';
|
||||
import { View } from '../../../src/view/view';
|
||||
import { createCardAPI, createView } from '../../test-utils';
|
||||
import { createPopulatedAPI } from './test-utils';
|
||||
@@ -19,15 +20,15 @@ describe('ViewQueryExecutor', () => {
|
||||
const executor = mock<QueryExecutor>();
|
||||
const viewQueryExecutor = new ViewQueryExecutor(createCardAPI(), executor);
|
||||
|
||||
const query = new EventMediaQueries();
|
||||
const queryResults = new MediaQueriesResults();
|
||||
const query = new EventMediaQuery();
|
||||
const queryResults = new QueryResults();
|
||||
const view = createView({
|
||||
query: query,
|
||||
});
|
||||
|
||||
executor.execute.mockResolvedValue(queryResults);
|
||||
executor.executeQuery.mockResolvedValue({ query, queryResults });
|
||||
|
||||
const queryExecutorOptions = {};
|
||||
const queryExecutorOptions = { useCache: true };
|
||||
const modifiers = await viewQueryExecutor.getExistingQueryModifiers(
|
||||
view,
|
||||
queryExecutorOptions,
|
||||
@@ -37,7 +38,7 @@ describe('ViewQueryExecutor', () => {
|
||||
|
||||
expect(view.query).toBe(query);
|
||||
expect(view.queryResults).toBe(queryResults);
|
||||
expect(executor.execute).toBeCalledWith(query, queryExecutorOptions);
|
||||
expect(executor.executeQuery).toBeCalledWith(query, queryExecutorOptions);
|
||||
});
|
||||
|
||||
it('should not return modifier when query absent', async () => {
|
||||
@@ -58,7 +59,7 @@ describe('ViewQueryExecutor', () => {
|
||||
|
||||
expect(view.query).toBeNull();
|
||||
expect(view.queryResults).toBeNull();
|
||||
expect(executor.execute).not.toBeCalled();
|
||||
expect(executor.executeMediaQuery).not.toBeCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -79,8 +80,8 @@ describe('ViewQueryExecutor', () => {
|
||||
});
|
||||
|
||||
it('should set query and queryResults for events', async () => {
|
||||
const query = new EventMediaQueries();
|
||||
const queryResults = new MediaQueriesResults();
|
||||
const query = new EventMediaQuery();
|
||||
const queryResults = new QueryResults();
|
||||
|
||||
const executor = mock<QueryExecutor>();
|
||||
executor.executeDefaultEventQuery.mockResolvedValue({
|
||||
@@ -108,11 +109,12 @@ describe('ViewQueryExecutor', () => {
|
||||
},
|
||||
});
|
||||
expect(executor.executeDefaultRecordingQuery).not.toHaveBeenCalled();
|
||||
expect(executor.executeDefaultFolderQuery).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should set query and queryResults for recordings', async () => {
|
||||
const query = new RecordingMediaQueries();
|
||||
const queryResults = new MediaQueriesResults();
|
||||
const query = new RecordingMediaQuery();
|
||||
const queryResults = new QueryResults();
|
||||
|
||||
const executor = mock<QueryExecutor>();
|
||||
executor.executeDefaultRecordingQuery.mockResolvedValue({
|
||||
@@ -150,6 +152,7 @@ describe('ViewQueryExecutor', () => {
|
||||
},
|
||||
});
|
||||
expect(executor.executeDefaultEventQuery).not.toBeCalled();
|
||||
expect(executor.executeDefaultFolderQuery).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe('should set timeline window', async () => {
|
||||
@@ -227,14 +230,15 @@ describe('ViewQueryExecutor', () => {
|
||||
expect(view?.queryResults).toBeNull();
|
||||
expect(executor.executeDefaultEventQuery).not.toHaveBeenCalled();
|
||||
expect(executor.executeDefaultRecordingQuery).not.toHaveBeenCalled();
|
||||
expect(executor.executeDefaultFolderQuery).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('with a media view', () => {
|
||||
it('should set query and queryResults for events', async () => {
|
||||
const executor = mock<QueryExecutor>();
|
||||
const query = new EventMediaQueries();
|
||||
const queryResults = new MediaQueriesResults();
|
||||
const query = new EventMediaQuery();
|
||||
const queryResults = new QueryResults();
|
||||
|
||||
executor.executeDefaultEventQuery.mockResolvedValue({
|
||||
query: query,
|
||||
@@ -260,6 +264,7 @@ describe('ViewQueryExecutor', () => {
|
||||
},
|
||||
});
|
||||
expect(executor.executeDefaultRecordingQuery).not.toHaveBeenCalled();
|
||||
expect(executor.executeDefaultFolderQuery).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -276,8 +281,8 @@ describe('ViewQueryExecutor', () => {
|
||||
eventsMediaType: 'clips' | 'snapshots',
|
||||
) => {
|
||||
const executor = mock<QueryExecutor>();
|
||||
const query = new EventMediaQueries();
|
||||
const queryResults = new MediaQueriesResults();
|
||||
const query = new EventMediaQuery();
|
||||
const queryResults = new QueryResults();
|
||||
|
||||
executor.executeDefaultEventQuery.mockResolvedValue({
|
||||
query: query,
|
||||
@@ -306,6 +311,7 @@ describe('ViewQueryExecutor', () => {
|
||||
},
|
||||
});
|
||||
expect(executor.executeDefaultRecordingQuery).not.toHaveBeenCalled();
|
||||
expect(executor.executeDefaultFolderQuery).not.toHaveBeenCalled();
|
||||
},
|
||||
);
|
||||
});
|
||||
@@ -315,8 +321,8 @@ describe('ViewQueryExecutor', () => {
|
||||
'%s',
|
||||
async (viewName: AdvancedCameraCardView) => {
|
||||
const executor = mock<QueryExecutor>();
|
||||
const query = new RecordingMediaQueries();
|
||||
const queryResults = new MediaQueriesResults();
|
||||
const query = new RecordingMediaQuery();
|
||||
const queryResults = new QueryResults();
|
||||
|
||||
executor.executeDefaultRecordingQuery.mockResolvedValue({
|
||||
query: query,
|
||||
@@ -344,10 +350,52 @@ describe('ViewQueryExecutor', () => {
|
||||
useCache: false,
|
||||
},
|
||||
});
|
||||
expect(executor.executeDefaultFolderQuery).not.toHaveBeenCalled();
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
describe('with a folder view', () => {
|
||||
it('should execute default folder query with folder view', async () => {
|
||||
const executor = mock<QueryExecutor>();
|
||||
const query = new FolderViewQuery();
|
||||
const queryResults = new QueryResults();
|
||||
|
||||
executor.executeDefaultFolderQuery.mockResolvedValue({
|
||||
query: query,
|
||||
queryResults: queryResults,
|
||||
});
|
||||
|
||||
const viewQueryExecutor = new ViewQueryExecutor(createPopulatedAPI(), executor);
|
||||
const view = createView({ view: 'folder', camera: 'camera.office' });
|
||||
|
||||
const modifiers = await viewQueryExecutor.getNewQueryModifiers(view);
|
||||
applyViewModifiers(view, modifiers);
|
||||
|
||||
expect(view?.query).toBe(query);
|
||||
expect(view?.queryResults).toBe(queryResults);
|
||||
expect(executor.executeDefaultEventQuery).not.toBeCalled();
|
||||
expect(executor.executeDefaultRecordingQuery).not.toBeCalled();
|
||||
expect(executor.executeDefaultFolderQuery).toBeCalledWith({
|
||||
useCache: false,
|
||||
});
|
||||
});
|
||||
|
||||
it('should execute default folder query with folder view and handle null results', async () => {
|
||||
const executor = mock<QueryExecutor>();
|
||||
executor.executeDefaultFolderQuery.mockResolvedValue(null);
|
||||
|
||||
const viewQueryExecutor = new ViewQueryExecutor(createPopulatedAPI(), executor);
|
||||
const view = createView({ view: 'folder', camera: 'camera.office' });
|
||||
|
||||
const modifiers = await viewQueryExecutor.getNewQueryModifiers(view);
|
||||
applyViewModifiers(view, modifiers);
|
||||
|
||||
expect(view?.query).toBeNull();
|
||||
expect(view?.queryResults).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when setting or removing seek time', () => {
|
||||
it('should set seek time when results are selected based on time', async () => {
|
||||
const now = new Date();
|
||||
|
||||
Reference in New Issue
Block a user