Add screenshot support.
This commit is contained in:
@@ -4,7 +4,7 @@ import { log } from '../../src/utils/debug.js';
|
||||
describe('log', () => {
|
||||
const spy = vi.spyOn(global.console, 'debug').mockReturnValue(undefined);
|
||||
afterAll(() => {
|
||||
vi.resetAllMocks();
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
it('should do nothing without debug logging set', () => {
|
||||
log({}, 'foo');
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest';
|
||||
import { mock } from 'vitest-mock-extended';
|
||||
import { CameraManager } from '../../src/camera-manager/manager.js';
|
||||
import { downloadMedia, downloadURL } from '../../src/utils/download';
|
||||
import { homeAssistantSignPath } from '../../src/utils/ha';
|
||||
import { ViewMedia } from '../../src/view/media';
|
||||
import { createCameraManager, createHASS } from '../test-utils';
|
||||
|
||||
vi.mock('../../src/camera-manager/manager.js');
|
||||
vi.mock('../../src/utils/ha');
|
||||
|
||||
const media = new ViewMedia('clip', 'camera-1');
|
||||
|
||||
// @vitest-environment jsdom
|
||||
describe('downloadMedia', () => {
|
||||
afterEach(() => {
|
||||
vi.resetAllMocks();
|
||||
global.window.location = mock<Location>();
|
||||
});
|
||||
|
||||
it('should throw error when no media', async () => {
|
||||
const cameraManager = createCameraManager();
|
||||
mock<CameraManager>(cameraManager).getMediaDownloadPath.mockResolvedValue(null);
|
||||
|
||||
expect(downloadMedia(createHASS(), cameraManager, media)).rejects.toThrow(
|
||||
/No media to download/,
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw error when signing fails', () => {
|
||||
vi.spyOn(global.console, 'warn').mockReturnValue(undefined);
|
||||
|
||||
const cameraManager = createCameraManager();
|
||||
mock<CameraManager>(cameraManager).getMediaDownloadPath.mockResolvedValue({
|
||||
sign: true,
|
||||
endpoint: 'foo',
|
||||
});
|
||||
const signError = new Error('sign-error');
|
||||
vi.mocked(homeAssistantSignPath).mockRejectedValue(signError);
|
||||
|
||||
expect(downloadMedia(createHASS(), cameraManager, media)).rejects.toThrow(
|
||||
/Could not sign media URL for download/,
|
||||
);
|
||||
});
|
||||
|
||||
it('should download media', async () => {
|
||||
const cameraManager = createCameraManager();
|
||||
mock<CameraManager>(cameraManager).getMediaDownloadPath.mockResolvedValue({
|
||||
sign: true,
|
||||
endpoint: 'foo',
|
||||
});
|
||||
vi.mocked(homeAssistantSignPath).mockResolvedValue('http://foo/signed-url');
|
||||
const windowSpy = vi.spyOn(window, 'open').mockReturnValue(null);
|
||||
|
||||
await downloadMedia(createHASS(), cameraManager, media);
|
||||
expect(windowSpy).toBeCalledWith('http://foo/signed-url', '_blank');
|
||||
});
|
||||
});
|
||||
|
||||
describe('downloadURL', () => {
|
||||
afterEach(() => {
|
||||
vi.resetAllMocks();
|
||||
global.window.location = mock<Location>();
|
||||
});
|
||||
|
||||
it('should download same origin via link', async () => {
|
||||
const location: Location & { origin: string } = mock<Location>();
|
||||
location.origin = 'http://foo';
|
||||
global.window.location = location;
|
||||
|
||||
const link = document.createElement('a');
|
||||
link.click = vi.fn();
|
||||
link.setAttribute = vi.fn();
|
||||
vi.spyOn(document, 'createElement').mockReturnValue(link);
|
||||
|
||||
downloadURL('http://foo/url.mp4');
|
||||
|
||||
expect(link.href).toBe('http://foo/url.mp4');
|
||||
expect(link.setAttribute).toBeCalledWith('download', 'download');
|
||||
expect(link.click).toBeCalled();
|
||||
});
|
||||
|
||||
it('should download in apps via window.open', async () => {
|
||||
// Set the origin to the same.
|
||||
const location: Location & { origin: string } = mock<Location>();
|
||||
location.origin = 'http://foo';
|
||||
global.window.location = location;
|
||||
|
||||
vi.stubGlobal('navigator', {
|
||||
userAgent: 'Home Assistant/2023.3.0-3260 (Android 13; Pixel 7 Pro)',
|
||||
});
|
||||
|
||||
const windowSpy = vi.spyOn(window, 'open').mockReturnValue(null);
|
||||
|
||||
downloadURL('http://foo/url.mp4');
|
||||
expect(windowSpy).toBeCalledWith('http://foo/url.mp4', '_blank');
|
||||
});
|
||||
});
|
||||
@@ -1067,6 +1067,23 @@ describe('MenuButtonController', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should have screenshot button', () => {
|
||||
const buttons = calculateButtons(controller, {
|
||||
currentMediaLoadedInfo: createMediaLoadedInfo({
|
||||
player: mock<FrigateCardMediaPlayer>(),
|
||||
}),
|
||||
});
|
||||
|
||||
expect(buttons).toContainEqual({
|
||||
icon: 'mdi:monitor-screenshot',
|
||||
enabled: false,
|
||||
priority: 50,
|
||||
type: 'custom:frigate-card-menu-icon',
|
||||
title: 'Screenshot',
|
||||
tap_action: { action: 'fire-dom-event', frigate_card_action: 'screenshot' },
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle dynamic buttons', () => {
|
||||
const button: MenuButton = {
|
||||
...dynamicButton,
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
import { afterAll, afterEach, beforeAll, describe, expect, it, vi } from 'vitest';
|
||||
import { mock } from 'vitest-mock-extended';
|
||||
import { generateScreenshotTitle, screenshotMedia } from '../../src/utils/screenshot';
|
||||
import { MediaQueriesResults } from '../../src/view/media-queries-results';
|
||||
import { View } from '../../src/view/view';
|
||||
import { TestViewMedia, createView } from '../test-utils';
|
||||
|
||||
// @vitest-environment jsdom
|
||||
describe('screenshotMedia', () => {
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
it('should not screenshot without context', () => {
|
||||
const video = document.createElement('video');
|
||||
|
||||
const canvas = document.createElement('canvas');
|
||||
const getContext = vi.fn().mockReturnValue(null);
|
||||
canvas.getContext = getContext;
|
||||
vi.spyOn(document, 'createElement').mockReturnValue(canvas);
|
||||
|
||||
expect(screenshotMedia(video)).toBeNull();
|
||||
});
|
||||
|
||||
it('should screenshot', () => {
|
||||
const video = document.createElement('video');
|
||||
|
||||
const canvas = document.createElement('canvas');
|
||||
const getContext = vi.fn().mockReturnValue(mock<CanvasRenderingContext2D>());
|
||||
canvas.getContext = getContext;
|
||||
canvas.toDataURL = vi.fn().mockReturnValue('data:image/jpeg;base64');
|
||||
vi.spyOn(document, 'createElement').mockReturnValue(canvas);
|
||||
|
||||
expect(screenshotMedia(video)).toBe('data:image/jpeg;base64');
|
||||
});
|
||||
});
|
||||
|
||||
describe('generateScreenshotTitle', () => {
|
||||
beforeAll(() => {
|
||||
vi.useFakeTimers();
|
||||
vi.setSystemTime(new Date('2023-06-13T21:54:01'));
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
it('should get title without view', () => {
|
||||
expect(generateScreenshotTitle()).toBe('screenshot.jpg');
|
||||
});
|
||||
|
||||
it('should get title for live view', () => {
|
||||
expect(generateScreenshotTitle(new View({ view: 'live', camera: 'camera-1' }))).toBe(
|
||||
'live-camera-1-2023-06-13-21-54-01.jpg',
|
||||
);
|
||||
});
|
||||
|
||||
it('should get title for image view', () => {
|
||||
expect(
|
||||
generateScreenshotTitle(new View({ view: 'image', camera: 'camera-1' })),
|
||||
).toBe('image-camera-1-2023-06-13-21-54-01.jpg');
|
||||
});
|
||||
|
||||
it('should get title for media viewer view with id', () => {
|
||||
const media = new TestViewMedia(
|
||||
'id1',
|
||||
new Date('2023-06-16T18:52'),
|
||||
'clip',
|
||||
'camera-1',
|
||||
);
|
||||
const view = createView({
|
||||
view: 'media',
|
||||
camera: 'camera-1',
|
||||
queryResults: new MediaQueriesResults([media], 0),
|
||||
});
|
||||
|
||||
expect(generateScreenshotTitle(view)).toBe('media-camera-1-id1.jpg');
|
||||
});
|
||||
|
||||
it('should get title for media viewer view without id', () => {
|
||||
const media = new TestViewMedia(
|
||||
null,
|
||||
new Date('2023-06-16T18:52'),
|
||||
'clip',
|
||||
'camera-1',
|
||||
);
|
||||
const view = createView({
|
||||
view: 'media',
|
||||
camera: 'camera-1',
|
||||
queryResults: new MediaQueriesResults([media], 0),
|
||||
});
|
||||
|
||||
expect(generateScreenshotTitle(view)).toBe('media-camera-1.jpg');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user