test: Split test utilities to improve test times (#2622)

This commit is contained in:
Dermot Duffy
2026-07-26 16:29:53 -07:00
committed by GitHub
parent 8d44fcecf1
commit ad89aa9538
234 changed files with 2730 additions and 2516 deletions
+2 -1
View File
@@ -2,7 +2,8 @@ import { describe, expect, it } from 'vitest';
import { ViewFolder, ViewMediaType } from '../../src/view/item';
import { ViewItemClassifier } from '../../src/view/item-classifier';
import { createFolder, TestViewMedia } from '../test-utils';
import { createFolder } from '../test-utils';
import { TestViewMedia } from './test-utils';
describe('ViewItemClassifier', () => {
it('isMedia', () => {
+2 -1
View File
@@ -1,7 +1,8 @@
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { ViewFolder, ViewMedia, ViewMediaType } from '../../src/view/item';
import { createFolder, TestViewMedia } from '../test-utils';
import { createFolder } from '../test-utils';
import { TestViewMedia } from './test-utils';
describe('ViewMedia', () => {
beforeEach(() => {
+2 -1
View File
@@ -2,7 +2,8 @@ import { assert, beforeEach, describe, expect, it, vi } from 'vitest';
import { ViewFolder, type ViewItem } from '../../src/view/item';
import { QueryResults } from '../../src/view/query-results';
import { createFolder, generateViewMediaArray, TestViewMedia } from '../test-utils';
import { createFolder } from '../test-utils';
import { generateViewMediaArray, TestViewMedia } from './test-utils';
describe('dispatchViewContextChangeEvent', () => {
beforeEach(() => {
+1 -1
View File
@@ -1,7 +1,7 @@
import { describe, expect, it } from 'vitest';
import { getStreamCameraID, hasSubstream } from '../../src/view/substream';
import { createView } from '../test-utils';
import { createView } from './test-utils';
describe('getStreamCameraID / hasSubstream', () => {
it('should report a substream override', () => {
+1 -1
View File
@@ -5,7 +5,7 @@ import {
getViewTargetID,
IMAGE_VIEW_TARGET_ID_SENTINEL,
} from '../../src/view/target-id';
import { createView, generateViewMediaArray } from '../test-utils';
import { createView, generateViewMediaArray } from './test-utils';
describe('getViewTargetID', () => {
describe('live', () => {
+240
View File
@@ -0,0 +1,240 @@
import {
QueryType,
type EventQuery,
type RecordingQuery,
type ReviewQuery,
} from '../../src/camera-manager/types';
import type { FolderQuery } from '../../src/card-controller/folders/types';
import type { FolderConfig } from '../../src/config/schema/folders';
import { QuerySource } from '../../src/query-source';
import type { Severity } from '../../src/severity';
import {
ViewMedia,
ViewMediaType,
type EventViewMedia,
type ReviewViewMedia,
} from '../../src/view/item';
import { QueryResults } from '../../src/view/query-results';
import { View, type ViewParameters } from '../../src/view/view';
export class TestViewMedia extends ViewMedia implements EventViewMedia, ReviewViewMedia {
private _icon: string | null = null;
private _id: string | null;
private _startTime: Date | null;
private _endTime: Date | null;
private _inProgress: boolean | null;
private _contentID: string | null;
private _title: string | null;
private _thumbnail: string | null;
private _what: string[] | null = null;
private _score: number | null = null;
private _tags: string[] | null = null;
private _where: string[] | null = null;
private _severity: Severity | null = null;
private _reviewed: boolean | null = null;
private _description: string | null = null;
private _favorite: boolean | null = null;
constructor(options?: {
id?: string | null;
startTime?: Date | null;
mediaType?: ViewMediaType;
cameraID?: string | null;
folder?: FolderConfig | null;
endTime?: Date | null;
inProgress?: boolean;
contentID?: string;
title?: string | null;
description?: string | null;
thumbnail?: string | null;
icon?: string | null;
what?: string[] | null;
score?: number | null;
tags?: string[] | null;
where?: string[] | null;
severity?: Severity | null;
reviewed?: boolean | null;
favorite?: boolean | null;
}) {
super(options?.mediaType ?? ViewMediaType.Clip, {
...(options?.cameraID !== null &&
!options?.folder && { cameraID: options?.cameraID ?? 'camera' }),
...(options?.folder && { folder: options.folder }),
});
this._id = options?.id !== undefined ? options.id : 'id';
this._startTime = options?.startTime ?? null;
this._endTime = options?.endTime ?? null;
this._inProgress = options?.inProgress !== undefined ? options.inProgress : false;
this._contentID = options?.contentID ?? null;
this._title = options?.title !== undefined ? options.title : null;
this._description = options?.description !== undefined ? options.description : null;
this._thumbnail = options?.thumbnail !== undefined ? options.thumbnail : null;
this._icon = options?.icon !== undefined ? options.icon : null;
this._what = options?.what !== undefined ? options.what : null;
this._score = options?.score !== undefined ? options.score : null;
this._tags = options?.tags !== undefined ? options.tags : null;
this._where = options?.where !== undefined ? options.where : null;
this._severity = options?.severity !== undefined ? options.severity : null;
this._reviewed = options?.reviewed !== undefined ? options.reviewed : null;
this._favorite = options?.favorite !== undefined ? options.favorite : null;
}
public getIcon(): string | null {
return this._icon;
}
public getID(): string | null {
return this._id;
}
public getStartTime(): Date | null {
return this._startTime;
}
public getEndTime(): Date | null {
return this._endTime;
}
public inProgress(): boolean | null {
return this._inProgress;
}
public getContentID(): string | null {
return this._contentID;
}
public getTitle(): string | null {
return this._title;
}
public getDescription(): string | null {
return this._description;
}
public getThumbnail(): string | null {
return this._thumbnail;
}
public getWhat(): string[] | null {
return this._what;
}
public getScore(): number | null {
return this._score;
}
public getTags(): string[] | null {
return this._tags;
}
public getWhere(): string[] | null {
return this._where;
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
public isGroupableWith(_that: EventViewMedia): boolean {
return false;
}
public getSeverity(): Severity | null {
return this._severity;
}
public isReviewed(): boolean | null {
return this._reviewed;
}
public setReviewed(reviewed: boolean): void {
this._reviewed = reviewed;
}
public isFavorite(): boolean | null {
return this._favorite;
}
public setFavorite(favorite: boolean): void {
this._favorite = favorite;
}
}
// jsdom does not implement `window.matchMedia`, so it has to be installed
// before a test can control what it returns. Must be called from inside a test
// or a test hook, as the stub is removed once the test finishes.
export const generateViewMediaArray = (options?: {
cameraIDs?: string[];
count?: number;
}): ViewMedia[] => {
const media: ViewMedia[] = [];
for (let i = 0; i < (options?.count ?? 100); ++i) {
for (const cameraID of options?.cameraIDs ?? ['kitchen', 'office']) {
media.push(
new TestViewMedia({
cameraID: cameraID,
id: `id-${cameraID}-${i}`,
}),
);
}
}
return media;
};
// ViewMedia itself has no native way to set startTime and ID that aren't linked
// to an engine.
export const createView = (options?: Partial<ViewParameters>): View => {
return new View({
view: 'live',
camera: 'camera',
...options,
});
};
export const createViewWithMedia = (options?: Partial<ViewParameters>): View => {
const media = generateViewMediaArray({ count: 5 });
return createView({
queryResults: new QueryResults({
results: media,
selectedIndex: 0,
}),
...options,
});
};
export const createEventQuery = (
cameraID: string,
options?: Partial<EventQuery>,
): EventQuery => ({
source: QuerySource.Camera,
type: QueryType.Event,
cameraIDs: new Set([cameraID]),
...options,
});
export const createRecordingQuery = (
cameraID: string,
options?: Partial<RecordingQuery>,
): RecordingQuery => ({
source: QuerySource.Camera,
type: QueryType.Recording,
cameraIDs: new Set([cameraID]),
...options,
});
export const createReviewQuery = (
cameraID: string,
options?: Partial<ReviewQuery>,
): ReviewQuery => ({
source: QuerySource.Camera,
type: QueryType.Review,
cameraIDs: new Set([cameraID]),
...options,
});
export const createFolderQuery = (folderId: string): FolderQuery => ({
source: QuerySource.Folder,
folder: { id: folderId, type: 'ha', title: folderId },
path: [{ ha: { id: 'Root' } }],
});
export const isEventQuery = (node: {
source: QuerySource;
type?: QueryType;
}): node is EventQuery =>
node.source === QuerySource.Camera && node.type === QueryType.Event;
export const isRecordingQuery = (node: {
source: QuerySource;
type?: QueryType;
}): node is RecordingQuery =>
node.source === QuerySource.Camera && node.type === QueryType.Recording;
export const isReviewQuery = (node: {
source: QuerySource;
type?: QueryType;
}): node is ReviewQuery =>
node.source === QuerySource.Camera && node.type === QueryType.Review;
export const isFolderQuery = (node: { source: QuerySource }): node is FolderQuery =>
node.source === QuerySource.Folder;
+4 -7
View File
@@ -15,13 +15,10 @@ import {
MediaTypeSpec,
UnifiedQueryBuilder,
} from '../../src/view/unified-query-builder';
import {
createCameraConfig,
createCapabilities,
createFolder,
isRecordingQuery,
isReviewQuery,
} from '../test-utils';
import { createCapabilities } from '../camera-manager/test-utils';
import { createCameraConfig } from '../config/test-utils';
import { createFolder } from '../test-utils';
import { isRecordingQuery, isReviewQuery } from './test-utils';
// Helper to create FolderQuery for tests
const createFolderQueryParams = (
+1 -1
View File
@@ -8,7 +8,7 @@ import { QuerySource } from '../../src/query-source';
import type { ViewMedia } from '../../src/view/item';
import { UnifiedQuery } from '../../src/view/unified-query';
import { UnifiedQueryRunner } from '../../src/view/unified-query-runner';
import { createEventQuery, createFolderQuery } from '../test-utils';
import { createEventQuery, createFolderQuery } from './test-utils';
describe('UnifiedQueryRunner', () => {
describe('execute', () => {
+3 -7
View File
@@ -6,13 +6,9 @@ import type { CameraManagerStore } from '../../src/camera-manager/store';
import type { FoldersManager } from '../../src/card-controller/folders/manager';
import { UnifiedQueryBuilder } from '../../src/view/unified-query-builder';
import { UnifiedQueryTransformer } from '../../src/view/unified-query-transformer';
import {
createCapabilities,
createFolder,
isEventQuery,
isFolderQuery,
isRecordingQuery,
} from '../test-utils';
import { createCapabilities } from '../camera-manager/test-utils';
import { createFolder } from '../test-utils';
import { isEventQuery, isFolderQuery, isRecordingQuery } from './test-utils';
const createMocks = () => {
const cameraManager = mock<CameraManager>();
+1 -1
View File
@@ -10,7 +10,7 @@ import {
createReviewQuery,
isEventQuery,
isFolderQuery,
} from '../test-utils';
} from './test-utils';
describe('UnifiedQuery', () => {
describe('Node Management', () => {
+2 -1
View File
@@ -7,7 +7,8 @@ import {
getReviewedQueryFilterFromConfig,
getReviewedQueryFilterFromQuery,
} from '../../../src/view/utils/query-filter';
import { createEventQuery, createFolder, TestViewMedia } from '../../test-utils';
import { createFolder } from '../../test-utils';
import { createEventQuery, TestViewMedia } from '../test-utils';
describe('query-filter', () => {
describe('getReviewedQueryFilterFromQuery', () => {
+1 -1
View File
@@ -4,7 +4,7 @@ import { mock } from 'vitest-mock-extended';
import type { CameraManager } from '../../../src/camera-manager/manager';
import type { FoldersManager } from '../../../src/card-controller/folders/manager';
import { resolveViewName } from '../../../src/view/utils/resolve-default';
import { createStore } from '../../test-utils';
import { createStore } from '../../camera-manager/test-utils';
describe('resolveViewName', () => {
it('should return the view name directly if not auto', () => {
+2 -2
View File
@@ -11,11 +11,11 @@ import {
isViewSupportedByCamera,
} from '../../src/view/view-support';
import {
createCameraConfig,
createCameraManager,
createCapabilities,
createStore,
} from '../test-utils';
} from '../camera-manager/test-utils';
import { createCameraConfig } from '../config/test-utils';
describe('getCameraIDsWithCapabilityForView', () => {
describe('views that are always supported', () => {
+1 -1
View File
@@ -2,7 +2,7 @@ import { describe, expect, it } from 'vitest';
import { QueryResults } from '../../src/view/query-results';
import { UnifiedQuery } from '../../src/view/unified-query';
import { createView } from '../test-utils';
import { createView } from './test-utils';
describe('View Basics', () => {
it('should construct from parameters', () => {