refactor: Significantly refactor how conditions work internally (#1886)

- Much improved API cleanliness and testability to allow further
extensibility in future
 - Simplified code in `live` view

This technically contains a small change in how overrides work in the
`live` view. Since that change is _closer_ to the documentation, and
since this is likely to be rarely used, this is not considered a
breaking change. Previously, overrides for a given live camera would
always render _as if_ that camera was selected, vs was actually
selected. Now, overrides will only apply in the live view when the
camera is _actually_ selected. If this is an issue for you in practice,
lets discuss.
This commit is contained in:
Dermot Duffy
2025-02-11 20:07:55 -08:00
committed by GitHub
parent a92074bc8d
commit 8d3cf07b43
51 changed files with 2443 additions and 2118 deletions
@@ -0,0 +1,305 @@
import { describe, expect, it } from 'vitest';
import { mock } from 'vitest-mock-extended';
import { z } from 'zod';
import { getOverriddenConfig } from '../../../src/card-controller/config/get-overridden-config';
import { ConditionsManagerReadonlyInterface } from '../../../src/conditions/types';
describe('getOverriddenConfig', () => {
const config = {
menu: {
style: 'none',
},
};
it('should not override without overrides', () => {
const manager = mock<ConditionsManagerReadonlyInterface>();
manager.getEvaluation.mockReturnValue({ result: true });
expect(getOverriddenConfig(manager, config)).toBe(config);
});
it('should not override when conditions do not match', () => {
const manager = mock<ConditionsManagerReadonlyInterface>();
manager.getEvaluation.mockReturnValue({ result: false });
expect(
getOverriddenConfig(manager, config, {
configOverrides: [
{
merge: {
menu: {
style: 'hidden',
},
},
delete: ['menu.style'],
set: {
'menu.style': 'overlay',
},
conditions: [
{
condition: 'fullscreen' as const,
fullscreen: true,
},
],
},
],
}),
).toBe(config);
});
describe('should merge', () => {
it('with path', () => {
const manager = mock<ConditionsManagerReadonlyInterface>();
manager.getEvaluation.mockReturnValue({ result: true });
expect(
getOverriddenConfig(manager, config, {
configOverrides: [
{
merge: {
'live.controls.thumbnails': {
mode: 'none',
},
},
conditions: [
{
condition: 'fullscreen' as const,
fullscreen: true,
},
],
},
],
}),
).toEqual({
menu: {
style: 'none',
},
live: {
controls: {
thumbnails: {
mode: 'none',
},
},
},
});
});
it('without path', () => {
const manager = mock<ConditionsManagerReadonlyInterface>();
manager.getEvaluation.mockReturnValue({ result: true });
expect(
getOverriddenConfig(manager, config, {
configOverrides: [
{
merge: {
menu: {
style: 'hidden',
},
},
conditions: [
{
condition: 'fullscreen' as const,
fullscreen: true,
},
],
},
],
}),
).toEqual({
menu: {
style: 'hidden',
},
});
});
it('with invalid merge', () => {
const manager = mock<ConditionsManagerReadonlyInterface>();
manager.getEvaluation.mockReturnValue({ result: true });
expect(
getOverriddenConfig(manager, config, {
configOverrides: [
{
merge: 6 as unknown as Record<string, unknown>,
conditions: [
{
condition: 'fullscreen' as const,
fullscreen: true,
},
],
},
],
}),
).toEqual({
menu: {
style: 'none',
},
});
});
});
describe('should set', () => {
it('leaf node', () => {
const manager = mock<ConditionsManagerReadonlyInterface>();
manager.getEvaluation.mockReturnValue({ result: true });
expect(
getOverriddenConfig(manager, config, {
configOverrides: [
{
set: {
'menu.style': 'hidden',
},
conditions: [
{
condition: 'fullscreen' as const,
fullscreen: true,
},
],
},
],
}),
).toEqual({
menu: {
style: 'hidden',
},
});
});
it('root node', () => {
const manager = mock<ConditionsManagerReadonlyInterface>();
manager.getEvaluation.mockReturnValue({ result: true });
expect(
getOverriddenConfig(manager, config, {
configOverrides: [
{
set: {
menu: {
style: 'hidden',
},
},
conditions: [
{
condition: 'fullscreen' as const,
fullscreen: true,
},
],
},
],
}),
).toEqual({
menu: {
style: 'hidden',
},
});
});
});
describe('should delete', () => {
it('leaf node', () => {
const manager = mock<ConditionsManagerReadonlyInterface>();
manager.getEvaluation.mockReturnValue({ result: true });
expect(
getOverriddenConfig(manager, config, {
configOverrides: [
{
delete: ['menu.style' as const],
conditions: [
{
condition: 'fullscreen' as const,
fullscreen: true,
},
],
},
],
}),
).toEqual({
menu: {},
});
});
it('root node', () => {
const manager = mock<ConditionsManagerReadonlyInterface>();
manager.getEvaluation.mockReturnValue({ result: true });
expect(
getOverriddenConfig(manager, config, {
configOverrides: [
{
delete: ['menu' as const],
conditions: [
{
condition: 'fullscreen' as const,
fullscreen: true,
},
],
},
],
}),
).toEqual({});
});
});
describe('should validate schema', () => {
const testSchema = z.object({
menu: z.object({
style: z.enum(['none', 'hidden']),
}),
});
it('passing', () => {
const manager = mock<ConditionsManagerReadonlyInterface>();
manager.getEvaluation.mockReturnValue({ result: true });
expect(
getOverriddenConfig(manager, config, {
configOverrides: [
{
conditions: [
{
condition: 'fullscreen' as const,
fullscreen: true,
},
],
set: {
'menu.style': 'hidden',
},
},
],
schema: testSchema,
}),
).toEqual({
menu: {
style: 'hidden',
},
});
});
it('failing', () => {
const manager = mock<ConditionsManagerReadonlyInterface>();
manager.getEvaluation.mockReturnValue({ result: true });
expect(() =>
getOverriddenConfig(manager, config, {
configOverrides: [
{
conditions: [
{
condition: 'fullscreen' as const,
fullscreen: true,
},
],
set: {
'menu.style': 'NOT_A_STYLE',
},
},
],
schema: testSchema,
}),
).toThrowError(/Invalid override configuration/);
});
});
});