Files
advanced-camera-card/tests/card-controller/style-manager.test.ts
T

417 lines
14 KiB
TypeScript

import { beforeEach, describe, expect, it, vi } from 'vitest';
import { FrigateCardView } from '../../src/config/types';
import { StyleManager } from '../../src/card-controller/style-manager';
import { createCardAPI, createConfig, createHASS, createView } from '../test-utils';
// @vitest-environment jsdom
describe('StyleManager', () => {
beforeEach(() => {
vi.resetAllMocks();
});
describe('setLightOrDarkMode', () => {
it('dark mode unspecified', () => {
const api = createCardAPI();
const element = document.createElement('div');
vi.mocked(api.getCardElementManager().getElement).mockReturnValue(element);
const manager = new StyleManager(api);
manager.setLightOrDarkMode();
expect(element.getAttribute('dark')).toBeNull();
});
it('dark mode explicitly off', () => {
const api = createCardAPI();
const element = document.createElement('div');
vi.mocked(api.getCardElementManager().getElement).mockReturnValue(element);
vi.mocked(api.getConfigManager().getConfig).mockReturnValue(
createConfig({
view: {
dark_mode: 'off',
},
}),
);
const manager = new StyleManager(api);
manager.setLightOrDarkMode();
expect(element.getAttribute('dark')).toBeNull();
});
it('dark mode explicitly set', () => {
const api = createCardAPI();
const element = document.createElement('div');
vi.mocked(api.getCardElementManager().getElement).mockReturnValue(element);
vi.mocked(api.getConfigManager().getConfig).mockReturnValue(
createConfig({
view: {
dark_mode: 'on',
},
}),
);
const manager = new StyleManager(api);
manager.setLightOrDarkMode();
expect(element.getAttribute('dark')).not.toBeNull();
});
it('dark mode auto without interaction', () => {
const api = createCardAPI();
const element = document.createElement('div');
vi.mocked(api.getCardElementManager().getElement).mockReturnValue(element);
vi.mocked(api.getConfigManager().getConfig).mockReturnValue(
createConfig({
view: {
dark_mode: 'auto',
},
}),
);
vi.mocked(api.getInteractionManager().hasInteraction).mockReturnValue(false);
const manager = new StyleManager(api);
manager.setLightOrDarkMode();
expect(element.getAttribute('dark')).not.toBeNull();
});
it('dark mode auto with HA dark mode', () => {
const api = createCardAPI();
const element = document.createElement('div');
vi.mocked(api.getCardElementManager().getElement).mockReturnValue(element);
vi.mocked(api.getConfigManager().getConfig).mockReturnValue(
createConfig({
view: {
dark_mode: 'auto',
},
}),
);
vi.mocked(api.getInteractionManager().hasInteraction).mockReturnValue(true);
const hass = createHASS();
hass.themes.darkMode = true;
vi.mocked(api.getHASSManager().getHASS).mockReturnValue(hass);
const manager = new StyleManager(api);
manager.setLightOrDarkMode();
expect(element.getAttribute('dark')).not.toBeNull();
});
});
describe('setExpandedMode', () => {
it('with no view or known media', () => {
const api = createCardAPI();
const element = document.createElement('div');
vi.mocked(api.getCardElementManager().getElement).mockReturnValue(element);
vi.mocked(api.getMediaLoadedInfoManager().getLastKnown).mockReturnValue(null);
const manager = new StyleManager(api);
manager.setExpandedMode();
expect(element.style.getPropertyValue('--frigate-card-expand-aspect-ratio')).toBe(
'unset',
);
expect(element.style.getPropertyValue('--frigate-card-expand-width')).toBe(
'var(--frigate-card-expand-max-width)',
);
expect(element.style.getPropertyValue('--frigate-card-expand-height')).toBe(
'var(--frigate-card-expand-max-height)',
);
});
it('with view but without media', () => {
const api = createCardAPI();
const element = document.createElement('div');
vi.mocked(api.getCardElementManager().getElement).mockReturnValue(element);
const view = createView({ view: 'media', displayMode: 'single' });
vi.mocked(api.getViewManager().getView).mockReturnValue(view);
vi.mocked(api.getMediaLoadedInfoManager().getLastKnown).mockReturnValue(null);
const manager = new StyleManager(api);
manager.setExpandedMode();
expect(element.style.getPropertyValue('--frigate-card-expand-aspect-ratio')).toBe(
'unset',
);
expect(element.style.getPropertyValue('--frigate-card-expand-width')).toBe('none');
expect(element.style.getPropertyValue('--frigate-card-expand-height')).toBe(
'none',
);
});
it('with view and media', () => {
const api = createCardAPI();
const element = document.createElement('div');
vi.mocked(api.getCardElementManager().getElement).mockReturnValue(element);
const view = createView({ view: 'media', displayMode: 'single' });
vi.mocked(api.getViewManager().getView).mockReturnValue(view);
vi.mocked(api.getMediaLoadedInfoManager().getLastKnown).mockReturnValue({
width: 800,
height: 600,
});
const manager = new StyleManager(api);
manager.setExpandedMode();
expect(element.style.getPropertyValue('--frigate-card-expand-aspect-ratio')).toBe(
'800 / 600',
);
expect(element.style.getPropertyValue('--frigate-card-expand-width')).toBe('none');
expect(element.style.getPropertyValue('--frigate-card-expand-height')).toBe(
'none',
);
});
it('with view and grid display mode', () => {
const api = createCardAPI();
const element = document.createElement('div');
vi.mocked(api.getCardElementManager().getElement).mockReturnValue(element);
const view = createView({ view: 'media', displayMode: 'grid' });
vi.mocked(api.getViewManager().getView).mockReturnValue(view);
vi.mocked(api.getMediaLoadedInfoManager().getLastKnown).mockReturnValue({
width: 800,
height: 600,
});
const manager = new StyleManager(api);
manager.setExpandedMode();
expect(element.style.getPropertyValue('--frigate-card-expand-aspect-ratio')).toBe(
'800 / 600',
);
expect(element.style.getPropertyValue('--frigate-card-expand-width')).toBe(
'var(--frigate-card-expand-max-width)',
);
expect(element.style.getPropertyValue('--frigate-card-expand-height')).toBe(
'var(--frigate-card-expand-max-height)',
);
});
});
describe('setMinMaxHeight', () => {
it('without a config', () => {
const api = createCardAPI();
const element = document.createElement('div');
vi.mocked(api.getCardElementManager().getElement).mockReturnValue(element);
const manager = new StyleManager(api);
manager.setMinMaxHeight();
expect(element.style.getPropertyValue('--frigate-card-max-height')).toBeFalsy();
expect(element.style.getPropertyValue('--frigate-card-expand-height')).toBeFalsy();
});
it('with a config', () => {
const api = createCardAPI();
const element = document.createElement('div');
vi.mocked(api.getCardElementManager().getElement).mockReturnValue(element);
vi.mocked(api.getConfigManager().getConfig).mockReturnValue(
createConfig({
dimensions: {
max_height: '800px',
min_height: '400px',
},
}),
);
const manager = new StyleManager(api);
manager.setMinMaxHeight();
expect(element.style.getPropertyValue('--frigate-card-min-height')).toBe('400px');
expect(element.style.getPropertyValue('--frigate-card-max-height')).toBe('800px');
});
});
describe('setPerformance', () => {
it('no styles set', () => {
const api = createCardAPI();
const element = document.createElement('div');
vi.mocked(api.getCardElementManager().getElement).mockReturnValue(element);
vi.mocked(api.getConfigManager().getCardWideConfig).mockReturnValue({});
const manager = new StyleManager(api);
manager.setPerformance();
expect(
element.style.getPropertyValue('--frigate-card-css-box-shadow'),
).toBeFalsy();
expect(
element.style.getPropertyValue('--frigate-card-css-border-radius'),
).toBeFalsy();
});
it('valid styles set', () => {
const api = createCardAPI();
const element = document.createElement('div');
vi.mocked(api.getCardElementManager().getElement).mockReturnValue(element);
vi.mocked(api.getConfigManager().getCardWideConfig).mockReturnValue(
createConfig({
performance: {
style: {
box_shadow: false,
border_radius: true,
},
},
}),
);
const manager = new StyleManager(api);
manager.setPerformance();
expect(element.style.getPropertyValue('--frigate-card-css-box-shadow')).toEqual(
'none',
);
expect(
element.style.getPropertyValue('--frigate-card-css-border-radius'),
).toBeFalsy();
});
});
describe('getAspectRatioStyle', () => {
it('without config or view', () => {
const api = createCardAPI();
const manager = new StyleManager(api);
expect(manager.getAspectRatioStyle()).toEqual({ 'aspect-ratio': '16 / 9' });
});
it('should be auto with unconstrained aspect ratio', () => {
const api = createCardAPI();
const view = createView({ view: 'media' });
vi.mocked(api.getViewManager().getView).mockReturnValue(view);
vi.mocked(api.getConfigManager().getConfig).mockReturnValue(
createConfig({
dimensions: {
aspect_ratio_mode: 'unconstrained',
},
}),
);
const manager = new StyleManager(api);
expect(manager.getAspectRatioStyle()).toEqual({ 'aspect-ratio': 'auto' });
});
it('should be auto in fullscreen', () => {
const api = createCardAPI();
const view = createView({ view: 'media' });
vi.mocked(api.getViewManager().getView).mockReturnValue(view);
vi.mocked(api.getConfigManager().getConfig).mockReturnValue(createConfig());
vi.mocked(api.getFullscreenManager().isInFullscreen).mockReturnValue(true);
const manager = new StyleManager(api);
expect(manager.getAspectRatioStyle()).toEqual({ 'aspect-ratio': 'auto' });
});
it('should be auto when expanded', () => {
const api = createCardAPI();
const view = createView({ view: 'media' });
vi.mocked(api.getViewManager().getView).mockReturnValue(view);
vi.mocked(api.getConfigManager().getConfig).mockReturnValue(createConfig());
vi.mocked(api.getExpandManager().isExpanded).mockReturnValue(true);
const manager = new StyleManager(api);
expect(manager.getAspectRatioStyle()).toEqual({ 'aspect-ratio': 'auto' });
});
it('should be auto when there is yet to be a view', () => {
const api = createCardAPI();
vi.mocked(api.getViewManager().getView).mockReturnValue(null);
vi.mocked(api.getConfigManager().getConfig).mockReturnValue(createConfig());
const manager = new StyleManager(api);
expect(manager.getAspectRatioStyle()).toEqual({ 'aspect-ratio': 'auto' });
});
describe('should be auto when dynamic in certain views', () => {
it.each([
['clip' as const],
['diagnostics' as const],
['image' as const],
['media' as const],
['live' as const],
['recording' as const],
['snapshot' as const],
['timeline' as const],
])('%s', (viewName: FrigateCardView) => {
const api = createCardAPI();
const view = createView({ view: viewName });
vi.mocked(api.getViewManager().getView).mockReturnValue(view);
vi.mocked(api.getConfigManager().getConfig).mockReturnValue(
createConfig({
dimensions: {
aspect_ratio_mode: 'dynamic',
},
}),
);
const manager = new StyleManager(api);
expect(manager.getAspectRatioStyle()).toEqual({ 'aspect-ratio': 'auto' });
});
});
describe('should be enforced when dynamic in certain views', () => {
it.each([['clips' as const], ['recordings' as const], ['snapshots' as const]])(
'%s',
(viewName: FrigateCardView) => {
const api = createCardAPI();
const view = createView({ view: viewName });
vi.mocked(api.getViewManager().getView).mockReturnValue(view);
vi.mocked(api.getConfigManager().getConfig).mockReturnValue(
createConfig({
dimensions: {
aspect_ratio_mode: 'dynamic',
},
}),
);
const manager = new StyleManager(api);
expect(manager.getAspectRatioStyle()).toEqual({ 'aspect-ratio': '16 / 9' });
},
);
});
describe('should use media dimensions in dynamic', () => {
it.each([['clips' as const], ['recordings' as const], ['snapshots' as const]])(
'%s',
(viewName: FrigateCardView) => {
const api = createCardAPI();
const view = createView({ view: viewName });
vi.mocked(api.getViewManager().getView).mockReturnValue(view);
vi.mocked(api.getConfigManager().getConfig).mockReturnValue(
createConfig({
dimensions: {
aspect_ratio_mode: 'dynamic',
},
}),
);
vi.mocked(api.getMediaLoadedInfoManager().getLastKnown).mockReturnValue({
width: 800,
height: 600,
});
const manager = new StyleManager(api);
expect(manager.getAspectRatioStyle()).toEqual({
'aspect-ratio': '800 / 600',
});
},
);
});
it('should respect default aspect ratio', () => {
const api = createCardAPI();
const view = createView({ view: 'clips' });
vi.mocked(api.getViewManager().getView).mockReturnValue(view);
vi.mocked(api.getConfigManager().getConfig).mockReturnValue(
createConfig({
dimensions: {
aspect_ratio_mode: 'dynamic',
aspect_ratio: '4:3',
},
}),
);
const manager = new StyleManager(api);
expect(manager.getAspectRatioStyle()).toEqual({ 'aspect-ratio': '4 / 3' });
});
});
});