Files

553 lines
18 KiB
TypeScript

import { beforeEach, describe, expect, it, vi } from 'vitest';
import { StyleManager } from '../../src/card-controller/style-manager';
import type { AdvancedCameraCardView } from '../../src/config/schema/common/const';
import type { ThemeName } from '../../src/config/schema/view';
import { createConfig } from '../config/test-utils';
import { createCardAPI } from '../test-utils';
import { createView } from '../view/test-utils';
// @vitest-environment jsdom
describe('StyleManager', () => {
beforeEach(() => {
vi.resetAllMocks();
});
describe('should set dimmable', () => {
it('should be dimmable when dim is true', () => {
const api = createCardAPI();
const element = document.createElement('div');
vi.mocked(api.getCardElementManager().getElement).mockReturnValue(element);
vi.mocked(api.getConfigManager().getConfig).mockReturnValue(
createConfig({
view: {
dim: true,
},
}),
);
const manager = new StyleManager(api);
manager.updateFromConfig();
expect(element.getAttribute('dimmable')).not.toBeNull();
});
it('should not be dimmable when dim is false', () => {
const api = createCardAPI();
const element = document.createElement('div');
vi.mocked(api.getCardElementManager().getElement).mockReturnValue(element);
vi.mocked(api.getConfigManager().getConfig).mockReturnValue(
createConfig({
view: {
dim: false,
},
}),
);
const manager = new StyleManager(api);
manager.updateFromConfig();
expect(element.getAttribute('dimmable')).toBeNull();
});
});
describe('should set expanded mode', () => {
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('--advanced-camera-card-expand-aspect-ratio'),
).toBe('unset');
expect(element.style.getPropertyValue('--advanced-camera-card-expand-width')).toBe(
'var(--advanced-camera-card-expand-max-width)',
);
expect(
element.style.getPropertyValue('--advanced-camera-card-expand-height'),
).toBe('var(--advanced-camera-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('--advanced-camera-card-expand-aspect-ratio'),
).toBe('unset');
expect(element.style.getPropertyValue('--advanced-camera-card-expand-width')).toBe(
'none',
);
expect(
element.style.getPropertyValue('--advanced-camera-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('--advanced-camera-card-expand-aspect-ratio'),
).toBe('800 / 600');
expect(element.style.getPropertyValue('--advanced-camera-card-expand-width')).toBe(
'none',
);
expect(
element.style.getPropertyValue('--advanced-camera-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('--advanced-camera-card-expand-aspect-ratio'),
).toBe('unset');
expect(element.style.getPropertyValue('--advanced-camera-card-expand-width')).toBe(
'var(--advanced-camera-card-expand-max-width)',
);
expect(
element.style.getPropertyValue('--advanced-camera-card-expand-height'),
).toBe('none');
});
it('with a non-media view', () => {
const api = createCardAPI();
const element = document.createElement('div');
vi.mocked(api.getCardElementManager().getElement).mockReturnValue(element);
const view = createView({ view: 'timeline' });
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('--advanced-camera-card-expand-aspect-ratio'),
).toBe('unset');
expect(element.style.getPropertyValue('--advanced-camera-card-expand-width')).toBe(
'var(--advanced-camera-card-expand-max-width)',
);
expect(
element.style.getPropertyValue('--advanced-camera-card-expand-height'),
).toBe('var(--advanced-camera-card-expand-max-height)');
});
});
describe('should set min and max height', () => {
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.updateFromConfig();
expect(
element.style.getPropertyValue('--advanced-camera-card-max-height'),
).toBeFalsy();
expect(
element.style.getPropertyValue('--advanced-camera-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: {
height: '800px',
},
}),
);
const manager = new StyleManager(api);
manager.updateFromConfig();
expect(element.style.getPropertyValue('--advanced-camera-card-height')).toBe(
'800px',
);
});
});
describe('should set performance', () => {
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.updateFromConfig();
expect(
element.style.getPropertyValue('--advanced-camera-card-box-shadow-override'),
).toBeFalsy();
expect(
element.style.getPropertyValue('--advanced-camera-card-border-radius-override'),
).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.updateFromConfig();
expect(
element.style.getPropertyValue('--advanced-camera-card-box-shadow-override'),
).toEqual('none');
expect(
element.style.getPropertyValue('--advanced-camera-card-border-radius-override'),
).toBeFalsy();
});
it('box_shadow disabled sets override', () => {
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,
},
},
}),
);
const manager = new StyleManager(api);
manager.updateFromConfig();
expect(
element.style.getPropertyValue('--advanced-camera-card-box-shadow-override'),
).toEqual('none');
});
it('border_radius disabled sets override', () => {
const api = createCardAPI();
const element = document.createElement('div');
vi.mocked(api.getCardElementManager().getElement).mockReturnValue(element);
vi.mocked(api.getConfigManager().getCardWideConfig).mockReturnValue(
createConfig({
performance: {
style: {
border_radius: false,
},
},
}),
);
const manager = new StyleManager(api);
manager.updateFromConfig();
expect(
element.style.getPropertyValue('--advanced-camera-card-border-radius-override'),
).toEqual('0px');
});
});
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: AdvancedCameraCardView) => {
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: AdvancedCameraCardView) => {
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: AdvancedCameraCardView) => {
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' });
});
});
describe('should apply themes', () => {
it('should not apply themes without a config', () => {
const api = createCardAPI();
const element = document.createElement('div');
vi.mocked(api.getCardElementManager().getElement).mockReturnValue(element);
const manager = new StyleManager(api);
manager.applyTheme();
expect(element.getAttribute('themes')).toBeNull();
});
describe('should apply named theme', () => {
it.each([
['light' as const],
['dark' as const],
['traditional' as const],
['ha' as const],
])('%s', (theme: ThemeName) => {
const api = createCardAPI();
const element = document.createElement('div');
vi.mocked(api.getCardElementManager().getElement).mockReturnValue(element);
vi.mocked(api.getConfigManager().getConfig).mockReturnValue(
createConfig({
view: {
theme: {
themes: [theme],
},
},
}),
);
const manager = new StyleManager(api);
manager.applyTheme();
expect(element.getAttribute('themes')).toBe(theme);
});
});
it('should apply multiple themes', () => {
const api = createCardAPI();
const element = document.createElement('div');
vi.mocked(api.getCardElementManager().getElement).mockReturnValue(element);
vi.mocked(api.getConfigManager().getConfig).mockReturnValue(
createConfig({
view: {
theme: {
themes: ['light', 'traditional'],
},
},
}),
);
const manager = new StyleManager(api);
manager.applyTheme();
expect(element.getAttribute('themes')).toBe('light traditional');
});
it('should treat empty themes list as default', () => {
const api = createCardAPI();
const element = document.createElement('div');
vi.mocked(api.getCardElementManager().getElement).mockReturnValue(element);
vi.mocked(api.getConfigManager().getConfig).mockReturnValue(
createConfig({
view: {
theme: {
themes: [],
},
},
}),
);
const manager = new StyleManager(api);
manager.applyTheme();
expect(element.getAttribute('themes')).toBe('traditional');
});
it('should apply overrides', () => {
const api = createCardAPI();
const element = document.createElement('div');
vi.mocked(api.getCardElementManager().getElement).mockReturnValue(element);
vi.mocked(api.getConfigManager().getConfig).mockReturnValue(
createConfig({
view: {
theme: {
overrides: {
'--test-key': 'test-value',
},
},
},
}),
);
const manager = new StyleManager(api);
manager.applyTheme();
expect(element.style.getPropertyValue('--test-key')).toBe('test-value');
});
});
});