Set timeline to now not to most recent event.
This commit is contained in:
@@ -0,0 +1,377 @@
|
||||
import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { LiveController } from '../../../src/components-lib/live/live-controller';
|
||||
import { dispatchMessageEvent } from '../../../src/components/message';
|
||||
import {
|
||||
changeViewToRecentEventsForCameraAndDependents,
|
||||
changeViewToRecentRecordingForCameraAndDependents,
|
||||
} from '../../../src/utils/media-to-view';
|
||||
import { EventMediaQueries } from '../../../src/view/media-queries';
|
||||
import {
|
||||
IntersectionObserverMock,
|
||||
callIntersectionHandler,
|
||||
createCameraManager,
|
||||
createConfig,
|
||||
createLitElement,
|
||||
createMediaLoadedInfo,
|
||||
createMediaLoadedInfoEvent,
|
||||
createParent,
|
||||
createView,
|
||||
createViewChangeEvent,
|
||||
} from '../../test-utils';
|
||||
|
||||
vi.mock('../../../src/utils/media-to-view');
|
||||
|
||||
// @vitest-environment jsdom
|
||||
describe('LiveController', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
vi.stubGlobal('IntersectionObserver', IntersectionObserverMock);
|
||||
});
|
||||
|
||||
it('should be constructable', () => {
|
||||
const controller = new LiveController(createLitElement());
|
||||
expect(controller).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should connect and disconnect', () => {
|
||||
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);
|
||||
dispatchMessageEvent(host, 'message', 'info');
|
||||
|
||||
expect(eventListener).toBeCalledTimes(0);
|
||||
|
||||
controller.hostDisconnected();
|
||||
dispatchMessageEvent(host, 'message', 'info');
|
||||
|
||||
expect(eventListener).toBeCalledTimes(1);
|
||||
});
|
||||
|
||||
describe('should handle background / foreground', () => {
|
||||
it('should start in the foreground', () => {
|
||||
const controller = new LiveController(createLitElement());
|
||||
expect(controller.isInBackground()).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should handle changing to background', () => {
|
||||
const element = createLitElement();
|
||||
const controller = new LiveController(element);
|
||||
expect(controller.isInBackground()).toBeFalsy();
|
||||
expect(element.requestUpdate).toBeCalledTimes(0);
|
||||
|
||||
callIntersectionHandler(true);
|
||||
expect(controller.isInBackground()).toBeFalsy();
|
||||
expect(element.requestUpdate).toBeCalledTimes(0);
|
||||
|
||||
callIntersectionHandler(false);
|
||||
expect(controller.isInBackground()).toBeTruthy();
|
||||
expect(element.requestUpdate).toBeCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should dispatch media loaded on background change', () => {
|
||||
const host = createLitElement();
|
||||
const parent = createParent({ children: [host] });
|
||||
const eventListener = vi.fn();
|
||||
parent.addEventListener('frigate-card:media:loaded', eventListener);
|
||||
|
||||
const controller = new LiveController(host);
|
||||
const mediaLoadedInfo = createMediaLoadedInfo();
|
||||
|
||||
controller.hostConnected();
|
||||
|
||||
callIntersectionHandler(false);
|
||||
expect(controller.isInBackground()).toBeTruthy();
|
||||
|
||||
host.dispatchEvent(createMediaLoadedInfoEvent(mediaLoadedInfo));
|
||||
expect(eventListener).toBeCalledTimes(0);
|
||||
|
||||
callIntersectionHandler(true);
|
||||
expect(eventListener).toBeCalledTimes(1);
|
||||
expect(eventListener).toHaveBeenLastCalledWith(
|
||||
expect.objectContaining({
|
||||
detail: mediaLoadedInfo,
|
||||
}),
|
||||
);
|
||||
|
||||
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);
|
||||
});
|
||||
|
||||
it('should handle view change', () => {
|
||||
const host = createLitElement();
|
||||
const parent = createParent({ children: [host] });
|
||||
const eventListener = vi.fn();
|
||||
parent.addEventListener('frigate-card:view:change', eventListener);
|
||||
|
||||
const controller = new LiveController(host);
|
||||
controller.hostConnected();
|
||||
const view = createView();
|
||||
|
||||
callIntersectionHandler(false);
|
||||
expect(controller.isInBackground()).toBeTruthy();
|
||||
host.dispatchEvent(createViewChangeEvent(view));
|
||||
|
||||
expect(eventListener).toBeCalledTimes(0);
|
||||
|
||||
callIntersectionHandler(true);
|
||||
expect(controller.isInBackground()).toBeFalsy();
|
||||
|
||||
host.dispatchEvent(createViewChangeEvent(view));
|
||||
|
||||
expect(eventListener).toBeCalledTimes(1);
|
||||
});
|
||||
|
||||
describe('should fetch media', () => {
|
||||
it('when in background', async () => {
|
||||
const controller = new LiveController(createLitElement());
|
||||
|
||||
callIntersectionHandler(false);
|
||||
|
||||
await controller.fetchMediaInBackgroundIfNecessary(
|
||||
createView(),
|
||||
createCameraManager(),
|
||||
{},
|
||||
createConfig().live,
|
||||
);
|
||||
|
||||
expect(changeViewToRecentEventsForCameraAndDependents).not.toBeCalled();
|
||||
expect(changeViewToRecentRecordingForCameraAndDependents).not.toBeCalled();
|
||||
});
|
||||
|
||||
it('when has existing query', async () => {
|
||||
const controller = new LiveController(createLitElement());
|
||||
|
||||
await controller.fetchMediaInBackgroundIfNecessary(
|
||||
createView({ query: new EventMediaQueries() }),
|
||||
createCameraManager(),
|
||||
{},
|
||||
createConfig().live,
|
||||
);
|
||||
|
||||
expect(changeViewToRecentEventsForCameraAndDependents).not.toBeCalled();
|
||||
expect(changeViewToRecentRecordingForCameraAndDependents).not.toBeCalled();
|
||||
});
|
||||
|
||||
it('when has no thumbnails', async () => {
|
||||
const controller = new LiveController(createLitElement());
|
||||
|
||||
await controller.fetchMediaInBackgroundIfNecessary(
|
||||
createView(),
|
||||
createCameraManager(),
|
||||
{},
|
||||
createConfig({
|
||||
live: {
|
||||
controls: {
|
||||
thumbnails: {
|
||||
mode: 'none',
|
||||
},
|
||||
},
|
||||
},
|
||||
}).live,
|
||||
);
|
||||
|
||||
expect(changeViewToRecentEventsForCameraAndDependents).not.toBeCalled();
|
||||
expect(changeViewToRecentRecordingForCameraAndDependents).not.toBeCalled();
|
||||
});
|
||||
|
||||
it('when fetch disabled in context', async () => {
|
||||
const controller = new LiveController(createLitElement());
|
||||
|
||||
await controller.fetchMediaInBackgroundIfNecessary(
|
||||
createView({
|
||||
context: {
|
||||
live: {
|
||||
fetchThumbnails: false,
|
||||
},
|
||||
},
|
||||
}),
|
||||
createCameraManager(),
|
||||
{},
|
||||
createConfig().live,
|
||||
);
|
||||
|
||||
expect(changeViewToRecentEventsForCameraAndDependents).not.toBeCalled();
|
||||
expect(changeViewToRecentRecordingForCameraAndDependents).not.toBeCalled();
|
||||
});
|
||||
|
||||
describe('with fetch', () => {
|
||||
const now = new Date('2024-04-07T19:43');
|
||||
beforeAll(() => {
|
||||
vi.useFakeTimers();
|
||||
vi.setSystemTime(now);
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
it('events', async () => {
|
||||
const host = createLitElement();
|
||||
const controller = new LiveController(host);
|
||||
const view = createView();
|
||||
const cameraManager = createCameraManager();
|
||||
const cardWideConfig = {};
|
||||
|
||||
await controller.fetchMediaInBackgroundIfNecessary(
|
||||
view,
|
||||
cameraManager,
|
||||
cardWideConfig,
|
||||
createConfig({
|
||||
live: {
|
||||
controls: {
|
||||
thumbnails: {
|
||||
media_type: 'events',
|
||||
events_media_type: 'all',
|
||||
},
|
||||
timeline: {
|
||||
window_seconds: 3600,
|
||||
},
|
||||
},
|
||||
},
|
||||
}).live,
|
||||
);
|
||||
|
||||
expect(changeViewToRecentEventsForCameraAndDependents).toBeCalledWith(
|
||||
host,
|
||||
cameraManager,
|
||||
cardWideConfig,
|
||||
view,
|
||||
expect.objectContaining({
|
||||
allCameras: false,
|
||||
targetView: 'live',
|
||||
eventsMediaType: 'all',
|
||||
select: 'latest',
|
||||
viewContext: expect.objectContaining({
|
||||
timeline: {
|
||||
window: {
|
||||
start: new Date('2024-04-07T18:43'),
|
||||
end: now,
|
||||
},
|
||||
},
|
||||
}),
|
||||
}),
|
||||
);
|
||||
expect(changeViewToRecentRecordingForCameraAndDependents).not.toBeCalled();
|
||||
});
|
||||
|
||||
it('recordings', async () => {
|
||||
const host = createLitElement();
|
||||
const controller = new LiveController(host);
|
||||
const view = createView();
|
||||
const cameraManager = createCameraManager();
|
||||
const cardWideConfig = {};
|
||||
|
||||
await controller.fetchMediaInBackgroundIfNecessary(
|
||||
view,
|
||||
cameraManager,
|
||||
cardWideConfig,
|
||||
createConfig({
|
||||
live: {
|
||||
controls: {
|
||||
thumbnails: {
|
||||
media_type: 'recordings',
|
||||
},
|
||||
timeline: {
|
||||
window_seconds: 3600,
|
||||
},
|
||||
},
|
||||
},
|
||||
}).live,
|
||||
);
|
||||
|
||||
expect(changeViewToRecentEventsForCameraAndDependents).not.toBeCalled();
|
||||
expect(changeViewToRecentRecordingForCameraAndDependents).toBeCalledWith(
|
||||
host,
|
||||
cameraManager,
|
||||
cardWideConfig,
|
||||
view,
|
||||
expect.objectContaining({
|
||||
allCameras: false,
|
||||
targetView: 'live',
|
||||
select: 'latest',
|
||||
viewContext: expect.objectContaining({
|
||||
timeline: {
|
||||
window: {
|
||||
start: new Date('2024-04-07T18:43'),
|
||||
end: now,
|
||||
},
|
||||
},
|
||||
}),
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user