fix: Substantially reduce cases where errors block whole card (#1764)

This commit is contained in:
Dermot Duffy
2024-12-15 20:39:18 -08:00
committed by GitHub
parent b6fb821074
commit dab9ff3045
25 changed files with 329 additions and 407 deletions
@@ -1,6 +1,6 @@
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { LiveController } from '../../../src/components-lib/live/live-controller';
import { dispatchMessageEvent } from '../../../src/components/message';
import { dispatchExistingMediaLoadedInfoAsEvent } from '../../../src/utils/media-info';
import {
IntersectionObserverMock,
callIntersectionHandler,
@@ -26,7 +26,7 @@ describe('LiveController', () => {
const host = createLitElement();
const parent = createParent({ children: [host] });
const eventListener = vi.fn();
parent.addEventListener('frigate-card:message', eventListener);
parent.addEventListener('frigate-card:media:loaded', eventListener);
const controller = new LiveController(host);
expect(host.addController).toBeCalled();
@@ -34,12 +34,13 @@ describe('LiveController', () => {
controller.hostConnected();
callIntersectionHandler(false);
dispatchMessageEvent(host, 'message', 'info');
dispatchExistingMediaLoadedInfoAsEvent(host, createMediaLoadedInfo());
expect(eventListener).toBeCalledTimes(0);
controller.hostDisconnected();
dispatchMessageEvent(host, 'message', 'info');
dispatchExistingMediaLoadedInfoAsEvent(host, createMediaLoadedInfo());
expect(eventListener).toBeCalledTimes(1);
});
@@ -92,70 +93,6 @@ describe('LiveController', () => {
host.dispatchEvent(createMediaLoadedInfoEvent(mediaLoadedInfo));
expect(eventListener).toBeCalledTimes(2);
callIntersectionHandler(false);
dispatchMessageEvent(host, 'message', 'info');
callIntersectionHandler(true);
expect(eventListener).toBeCalledTimes(2);
controller.clearMessageReceived();
callIntersectionHandler(true);
expect(eventListener).toBeCalledTimes(3);
});
});
describe('should correctly allow updates', () => {
it('when not in background', () => {
const controller = new LiveController(createLitElement());
expect(controller.shouldUpdate()).toBeTruthy();
});
it('when in background without message', () => {
const host = createLitElement();
const controller = new LiveController(host);
controller.hostConnected();
callIntersectionHandler(false);
expect(controller.shouldUpdate()).toBeTruthy();
});
it('when in background with message', () => {
const host = createLitElement();
const controller = new LiveController(host);
controller.hostConnected();
callIntersectionHandler(false);
dispatchMessageEvent(host, 'message', 'info');
expect(controller.shouldUpdate()).toBeFalsy();
});
});
it('should handle message', () => {
const host = createLitElement();
const parent = createParent({ children: [host] });
const eventListener = vi.fn();
parent.addEventListener('frigate-card:message', eventListener);
const controller = new LiveController(host);
controller.hostConnected();
callIntersectionHandler(false);
expect(controller.isInBackground()).toBeTruthy();
const firstRenderEpoch = controller.getRenderEpoch();
dispatchMessageEvent(host, 'message', 'info');
expect(eventListener).toBeCalledTimes(0);
const secondRenderEpoch = controller.getRenderEpoch();
expect(secondRenderEpoch).not.toBe(firstRenderEpoch);
callIntersectionHandler(true);
dispatchMessageEvent(host, 'message', 'info');
expect(eventListener).toBeCalledTimes(1);
expect(controller.getRenderEpoch()).toBe(secondRenderEpoch);
});
});
@@ -0,0 +1,12 @@
import { expect, it, vi } from 'vitest';
import { dispatchLiveErrorEvent } from '../../../../src/components-lib/live/utils/dispatch-live-error';
// @vitest-environment jsdom
it('should dispatch live error event', () => {
const element = document.createElement('div');
const handler = vi.fn();
element.addEventListener('frigate-card:live:error', handler);
dispatchLiveErrorEvent(element);
expect(handler).toBeCalled();
});
+12 -37
View File
@@ -1,22 +1,17 @@
import { afterEach, describe, expect, it, vi } from 'vitest';
import { getEndpointAddressOrDispatchError } from '../../src/utils/endpoint';
import { convertEndpointAddressToSignedWebsocket } from '../../src/utils/endpoint';
import { homeAssistantSignPath } from '../../src/utils/ha';
import { createHASS } from '../test-utils';
vi.mock('../../src/utils/ha');
// @vitest-environment jsdom
describe('getEndpointAddressOrDispatchError', () => {
describe('convertEndpointAddressToSignedWebsocket', () => {
it('without signing', async () => {
expect(
await getEndpointAddressOrDispatchError(
document.createElement('div'),
createHASS(),
{
endpoint: 'http://example.com',
sign: false,
},
),
await convertEndpointAddressToSignedWebsocket(createHASS(), {
endpoint: 'http://example.com',
sign: false,
}),
).toBe('http://example.com');
});
@@ -29,51 +24,31 @@ describe('getEndpointAddressOrDispatchError', () => {
vi.mocked(homeAssistantSignPath).mockResolvedValue('http://signed.com');
expect(
await getEndpointAddressOrDispatchError(
document.createElement('div'),
createHASS(),
{
endpoint: 'http://example.com',
sign: true,
},
),
await convertEndpointAddressToSignedWebsocket(createHASS(), {
endpoint: 'http://example.com',
sign: true,
}),
).toBe('ws://signed.com');
});
it('with null response', async () => {
const element = document.createElement('div');
const listener = vi.fn();
element.addEventListener('frigate-card:message', listener);
vi.mocked(homeAssistantSignPath).mockResolvedValue(null);
expect(
await getEndpointAddressOrDispatchError(element, createHASS(), {
await convertEndpointAddressToSignedWebsocket(createHASS(), {
endpoint: 'http://example.com',
sign: true,
}),
).toBeNull();
expect(listener).toBeCalledWith(
expect.objectContaining({
detail: expect.objectContaining({
message: 'Could not sign Home Assistant URL',
}),
}),
);
});
it('with exception on signing', async () => {
const consoleSpy = vi.spyOn(global.console, 'warn').mockReturnValue(undefined);
const element = document.createElement('div');
const listener = vi.fn();
element.addEventListener('frigate-card:message', listener);
vi.mocked(homeAssistantSignPath).mockRejectedValue(new Error());
expect(
await getEndpointAddressOrDispatchError(element, createHASS(), {
await convertEndpointAddressToSignedWebsocket(createHASS(), {
endpoint: 'http://example.com',
sign: true,
}),
-72
View File
@@ -1,72 +0,0 @@
import { describe, expect, it, vi } from 'vitest';
import { createCameraConfig, createHASS, createStateEntity } from '../test-utils';
import { getStateObjOrDispatchError } from '../../src/utils/get-state-obj';
// @vitest-environment jsdom
describe('getStateObjOrDispatchError', () => {
it('should retrieve valid state object', () => {
const messageHandler = vi.fn();
const element = document.createElement('div');
element.addEventListener('frigate-card:message', messageHandler);
const state = createStateEntity();
expect(
getStateObjOrDispatchError(
element,
createHASS({
'camera.test': state,
}),
createCameraConfig({
camera_entity: 'camera.test',
}),
),
).toBe(state);
expect(messageHandler).not.toBeCalled();
});
it('should dispatch unspecified entity', () => {
const messageHandler = vi.fn();
const element = document.createElement('div');
element.addEventListener('frigate-card:message', messageHandler);
expect(
getStateObjOrDispatchError(element, createHASS(), createCameraConfig()),
).toBeNull();
expect(messageHandler).toBeCalledWith(
expect.objectContaining({
detail: expect.objectContaining({
message:
'The camera_entity parameter must be set and valid for this live provider',
type: 'error',
}),
}),
);
});
it('should dispatch not found state', () => {
const messageHandler = vi.fn();
const element = document.createElement('div');
element.addEventListener('frigate-card:message', messageHandler);
expect(
getStateObjOrDispatchError(
element,
createHASS(),
createCameraConfig({
camera_entity: 'camera.will-not-be-found',
}),
),
).toBeNull();
expect(messageHandler).toBeCalledWith(
expect.objectContaining({
detail: expect.objectContaining({
message: 'The configured camera_entity was not found',
type: 'error',
}),
}),
);
});
});