feat: Make cameras optional (#2343)

This commit is contained in:
Dermot Duffy
2026-02-13 20:21:05 -08:00
committed by GitHub
parent 21f9469784
commit 93f1f08e51
61 changed files with 1312 additions and 557 deletions
+42
View File
@@ -0,0 +1,42 @@
import { describe, expect, it, vi } from 'vitest';
import { mock } from 'vitest-mock-extended';
import { CameraManager } from '../../../src/camera-manager/manager';
import { FoldersManager } from '../../../src/card-controller/folders/manager';
import { resolveViewName } from '../../../src/view/utils/resolve-default';
import { createStore } from '../../test-utils';
describe('resolveViewName', () => {
it('should return the view name directly if not auto', () => {
expect(resolveViewName('live', mock<CameraManager>(), mock<FoldersManager>())).toBe(
'live',
);
});
it('should return live if auto and cameras are present', () => {
const cameraManager = mock<CameraManager>();
vi.mocked(cameraManager.getStore).mockReturnValue(
createStore([{ cameraID: 'camera-1' }]),
);
const foldersManager = mock<FoldersManager>();
expect(resolveViewName('auto', cameraManager, foldersManager)).toBe('live');
});
it('should return folders if auto, no cameras, but folders are present', () => {
const cameraManager = mock<CameraManager>();
vi.mocked(cameraManager.getStore).mockReturnValue(createStore([]));
const foldersManager = mock<FoldersManager>();
vi.mocked(foldersManager.hasFolders).mockReturnValue(true);
expect(resolveViewName('auto', cameraManager, foldersManager)).toBe('folders');
});
it('should return image if auto and nothing is present', () => {
const cameraManager = mock<CameraManager>();
vi.mocked(cameraManager.getStore).mockReturnValue(createStore([]));
const foldersManager = mock<FoldersManager>();
vi.mocked(foldersManager.hasFolders).mockReturnValue(false);
expect(resolveViewName('auto', cameraManager, foldersManager)).toBe('image');
});
});
+131 -37
View File
@@ -4,18 +4,19 @@ import { FoldersManager } from '../../src/card-controller/folders/manager';
import { AdvancedCameraCardView } from '../../src/config/schema/common/const';
import { CapabilityKey } from '../../src/types';
import {
getCameraIDsForViewName,
getCameraIDsWithCapabilityForView,
isViewAvailable,
isViewSupported,
isViewSupportedByCamera,
} from '../../src/view/view-support';
import {
createCameraConfig,
createCameraManager,
createCapabilities,
createFolder,
createStore,
} from '../test-utils';
describe('getCameraIDsForViewName', () => {
describe('getCameraIDsWithCapabilityForView', () => {
describe('views that are always supported', () => {
it.each([['diagnostics' as const], ['image' as const]])(
'%s',
@@ -32,14 +33,24 @@ describe('getCameraIDsForViewName', () => {
);
const foldersManager = mock<FoldersManager>();
expect(getCameraIDsForViewName(viewName, cameraManager, foldersManager)).toEqual(
new Set(['camera-1', 'camera-2']),
);
expect(
getCameraIDsForViewName(viewName, cameraManager, foldersManager, 'camera-1'),
getCameraIDsWithCapabilityForView(viewName, cameraManager, foldersManager),
).toEqual(new Set(['camera-1', 'camera-2']));
expect(
getCameraIDsForViewName(viewName, cameraManager, foldersManager, 'camera-2'),
getCameraIDsWithCapabilityForView(
viewName,
cameraManager,
foldersManager,
'camera-1',
),
).toEqual(new Set(['camera-1', 'camera-2']));
expect(
getCameraIDsWithCapabilityForView(
viewName,
cameraManager,
foldersManager,
'camera-2',
),
).toEqual(new Set(['camera-1', 'camera-2']));
},
);
@@ -76,9 +87,9 @@ describe('getCameraIDsForViewName', () => {
);
const foldersManager = mock<FoldersManager>();
expect(getCameraIDsForViewName(viewName, cameraManager, foldersManager)).toEqual(
new Set(['camera-2']),
);
expect(
getCameraIDsWithCapabilityForView(viewName, cameraManager, foldersManager),
).toEqual(new Set(['camera-2']));
});
it.each([
@@ -106,20 +117,14 @@ describe('getCameraIDsForViewName', () => {
);
const foldersManager = mock<FoldersManager>();
expect(getCameraIDsForViewName(viewName, cameraManager, foldersManager)).toEqual(
new Set(['camera-1', 'camera-2']),
);
expect(
getCameraIDsForViewName(viewName, cameraManager, foldersManager, 'camera-1'),
).toEqual(new Set(['camera-1', 'camera-2']));
expect(
getCameraIDsForViewName(viewName, cameraManager, foldersManager, 'camera-2'),
).toEqual(new Set(['camera-2']));
getCameraIDsWithCapabilityForView(viewName, cameraManager, foldersManager),
).toEqual(new Set(['camera-2', 'camera-1']));
});
});
describe('views that respect a folder', () => {
describe('should return cameras when a folder is present', () => {
describe('folder views', () => {
describe('should return all cameras when a folder is present', () => {
it.each([
['folder' as const],
['folders' as const],
@@ -140,16 +145,10 @@ describe('getCameraIDsForViewName', () => {
]),
);
const foldersManager = mock<FoldersManager>();
vi.mocked(foldersManager.getFolder).mockReturnValue(createFolder());
vi.mocked(foldersManager.hasFolders).mockReturnValue(true);
expect(getCameraIDsForViewName(viewName, cameraManager, foldersManager)).toEqual(
new Set(['camera-1', 'camera-2']),
);
expect(
getCameraIDsForViewName(viewName, cameraManager, foldersManager, 'camera-1'),
).toEqual(new Set(['camera-1', 'camera-2']));
expect(
getCameraIDsForViewName(viewName, cameraManager, foldersManager, 'camera-2'),
getCameraIDsWithCapabilityForView(viewName, cameraManager, foldersManager),
).toEqual(new Set(['camera-1', 'camera-2']));
});
});
@@ -175,22 +174,63 @@ describe('getCameraIDsForViewName', () => {
]),
);
const foldersManager = mock<FoldersManager>();
vi.mocked(foldersManager.getFolder).mockReturnValue(null);
vi.mocked(foldersManager.hasFolders).mockReturnValue(false);
expect(getCameraIDsForViewName(viewName, cameraManager, foldersManager)).toEqual(
new Set(),
);
expect(
getCameraIDsForViewName(viewName, cameraManager, foldersManager, 'camera-1'),
).toEqual(new Set());
expect(
getCameraIDsForViewName(viewName, cameraManager, foldersManager, 'camera-2'),
getCameraIDsWithCapabilityForView(viewName, cameraManager, foldersManager),
).toEqual(new Set());
});
});
});
});
describe('isViewAvailable', () => {
it('should return true when cameras are present for camera view', () => {
const cameraManager = createCameraManager();
vi.mocked(cameraManager.getStore).mockReturnValue(
createStore([{ cameraID: 'camera-1' }]),
);
const foldersManager = mock<FoldersManager>();
expect(isViewAvailable('live', cameraManager, foldersManager)).toBe(true);
});
it('should return false when cameras are missing for camera view', () => {
const cameraManager = createCameraManager();
vi.mocked(cameraManager.getStore).mockReturnValue(createStore([]));
const foldersManager = mock<FoldersManager>();
expect(isViewAvailable('live', cameraManager, foldersManager)).toBe(false);
});
it('should return true when folders are present for folder view', () => {
const cameraManager = createCameraManager();
const foldersManager = mock<FoldersManager>();
vi.mocked(foldersManager.hasFolders).mockReturnValue(true);
expect(isViewAvailable('folder', cameraManager, foldersManager)).toBe(true);
});
it('should return false when folders are missing for folder view', () => {
const cameraManager = createCameraManager();
const foldersManager = mock<FoldersManager>();
vi.mocked(foldersManager.hasFolders).mockReturnValue(false);
expect(isViewAvailable('folder', cameraManager, foldersManager)).toBe(false);
});
it('should return true when either covers it', () => {
const cameraManager = createCameraManager();
vi.mocked(cameraManager.getStore).mockReturnValue(createStore([]));
const foldersManager = mock<FoldersManager>();
vi.mocked(foldersManager.hasFolders).mockReturnValue(true);
expect(isViewAvailable('gallery', cameraManager, foldersManager)).toBe(true);
});
it('should return true for diagnostics and image view', () => {
const cameraManager = createCameraManager();
const foldersManager = mock<FoldersManager>();
expect(isViewAvailable('diagnostics', cameraManager, foldersManager)).toBe(true);
expect(isViewAvailable('image', cameraManager, foldersManager)).toBe(true);
});
});
describe('isViewSupportedByCamera', () => {
it('should return true for supported view', () => {
const cameraManager = createCameraManager();
@@ -226,3 +266,57 @@ describe('isViewSupportedByCamera', () => {
).toBe(false);
});
});
describe('isViewSupported', () => {
it('should return true when view is available and camera is null', () => {
const cameraManager = createCameraManager();
vi.mocked(cameraManager.getStore).mockReturnValue(
createStore([{ cameraID: 'camera-1' }]),
);
const foldersManager = mock<FoldersManager>();
expect(isViewSupported('live', cameraManager, foldersManager, null)).toBe(true);
});
it('should return true when view is available and camera is supported', () => {
const cameraManager = createCameraManager();
vi.mocked(cameraManager.getStore).mockReturnValue(
createStore([
{
cameraID: 'camera-1',
capabilities: createCapabilities({ live: true }),
},
]),
);
const foldersManager = mock<FoldersManager>();
expect(isViewSupported('live', cameraManager, foldersManager, 'camera-1')).toBe(
true,
);
});
it('should return false when view is available but camera is not supported', () => {
const cameraManager = createCameraManager();
vi.mocked(cameraManager.getStore).mockReturnValue(
createStore([
{
cameraID: 'camera-1',
capabilities: createCapabilities({ live: false }),
},
]),
);
const foldersManager = mock<FoldersManager>();
expect(isViewSupported('live', cameraManager, foldersManager, 'camera-1')).toBe(
false,
);
});
it('should return false when view is not available', () => {
const cameraManager = createCameraManager();
vi.mocked(cameraManager.getStore).mockReturnValue(createStore([]));
const foldersManager = mock<FoldersManager>();
expect(isViewSupported('live', cameraManager, foldersManager, null)).toBe(false);
});
});