diff --git a/src/components-lib/media-dimensions-container-controller.ts b/src/components-lib/media-dimensions-container-controller.ts index 1430c440..6dfa60e0 100644 --- a/src/components-lib/media-dimensions-container-controller.ts +++ b/src/components-lib/media-dimensions-container-controller.ts @@ -8,6 +8,10 @@ import { updateElementStyleFromMediaLayoutConfig } from '../utils/media-layout'; const ROTATED_ATTRIBUTE = 'rotated'; +// Resizes arrive in bursts as the browser settles the layout, so only the last +// one in a burst is acted upon. +export const RESIZE_DEBOUNCE_SECONDS = 0.1; + interface MediaDimensions { width: number; height: number; @@ -32,7 +36,9 @@ export class MediaDimensionsContainerController implements ReactiveController { private _innerContainer: HTMLElement | null = null; private _outerContainer: HTMLElement | null = null; - public resize = debounce(this._resize.bind(this), 100, { trailing: true }); + public resize = debounce(this._resize.bind(this), RESIZE_DEBOUNCE_SECONDS * 1000, { + trailing: true, + }); private _resizeObserver = new ResizeObserver(this.resize); private _mediaDimensions: MediaDimensions | null = null; diff --git a/src/components-lib/media-height-controller.ts b/src/components-lib/media-height-controller.ts index ecd2dd32..bcaf1a76 100644 --- a/src/components-lib/media-height-controller.ts +++ b/src/components-lib/media-height-controller.ts @@ -1,5 +1,9 @@ import { debounce, isEqual } from 'lodash-es'; +// Balancing act: Debounce to avoid excessive calls to setHeight, when new media +// is loading the player may be a much smaller height momentarily. +export const SET_HEIGHT_DEBOUNCE_SECONDS = 0.3; + export class MediaHeightController { private _host: HTMLElement; private _selector: string; @@ -13,9 +17,7 @@ export class MediaHeightController { private _debouncedSetHeight = debounce( () => this._setHeight(), - // Balancing act: Debounce to avoid excessive calls to setHeight, when new - // media is loading the player may be a much smaller height momentarily. - 300, + SET_HEIGHT_DEBOUNCE_SECONDS * 1000, { trailing: true, leading: false, diff --git a/src/ha/sign-path.ts b/src/ha/sign-path.ts index 001af1f3..ee7123fa 100644 --- a/src/ha/sign-path.ts +++ b/src/ha/sign-path.ts @@ -25,9 +25,6 @@ export async function homeAssistantSignPath( signedPathSchema, request, ); - if (!response) { - return null; - } return hass.hassUrl(response.path); } diff --git a/tests/camera-manager/browse-media/engine-browse-media.test.ts b/tests/camera-manager/browse-media/engine-browse-media.test.ts index 3ab8822d..2ff91b36 100644 --- a/tests/camera-manager/browse-media/engine-browse-media.test.ts +++ b/tests/camera-manager/browse-media/engine-browse-media.test.ts @@ -13,8 +13,9 @@ import { BrowseMediaWalker } from '../../../src/ha/browse-media/walker'; import { ResolvedMediaCache } from '../../../src/ha/resolved-media'; import { QuerySource } from '../../../src/query-source'; import type { ViewMedia } from '../../../src/view/item'; +import { createCameraConfig } from '../../config/test-utils'; import { EntityRegistryManagerMock } from '../../ha/registry/entity/mock'; -import { createCameraConfig, createHASS, createHASSManager } from '../../test-utils'; +import { createHASS, createHASSManager } from '../../test-utils'; const createEngine = (): BrowseMediaCameraManagerEngine => { return new BrowseMediaCameraManagerEngine( diff --git a/tests/camera-manager/camera.test.ts b/tests/camera-manager/camera.test.ts index 878faae8..77471835 100644 --- a/tests/camera-manager/camera.test.ts +++ b/tests/camera-manager/camera.test.ts @@ -8,19 +8,18 @@ import type { EventWatcherSubscriptionInterface } from '../../src/card-controlle import type { StateWatcherSubscriptionInterface } from '../../src/card-controller/hass/state-watcher.js'; import { liveProviderSupports2WayAudio } from '../../src/utils/live-provider.js'; import type * as LiveProviderUtils from '../../src/utils/live-provider.js'; +import { createCameraConfig } from '../config/test-utils'; import { EntityRegistryManagerMock } from '../ha/registry/entity/mock.js'; import { callEventWatcherCallback, callStateWatcherCallback, - createCameraConfig, - createCapabilities, createHASS, createHASSEvent, createHASSManager, - createInitializedCamera, createRegistryEntity, createStateEntity, } from '../test-utils.js'; +import { createCapabilities, createInitializedCamera } from './test-utils'; // Partially mock to keep the real pure helpers (e.g. `isGo2RTCLiveProvider` // used by `getProxyConfig`) while mocking the async metadata fetch. @@ -80,7 +79,7 @@ describe('Camera', () => { createCameraConfig(), new GenericCameraManagerEngine(createHASSManager()), ); - expect(() => camera.getID()).toThrowError( + expect(() => camera.getID()).toThrow( 'Could not determine camera id for the following ' + "camera, may need to set 'id' parameter manually", ); @@ -109,13 +108,13 @@ describe('Camera', () => { }); expect(camera.isInitialized()).toBe(true); - expect(stateWatcher.subscribe).toBeCalledWith(expect.any(Function), [ + expect(stateWatcher.subscribe).toHaveBeenCalledWith(expect.any(Function), [ 'camera.foo', ]); await camera.destroy(); - expect(stateWatcher.unsubscribe).toBeCalled(); + expect(stateWatcher.unsubscribe).toHaveBeenCalled(); }); it('should skip initialization when hass is unavailable', async () => { @@ -134,7 +133,7 @@ describe('Camera', () => { capabilityOptions: { capabilities: createCapabilities({ trigger: true }) }, }); - expect(stateWatcher.subscribe).not.toBeCalled(); + expect(stateWatcher.subscribe).not.toHaveBeenCalled(); expect(camera.getCapabilities()).toBeNull(); expect(camera.isInitialized()).toBe(false); }); @@ -630,7 +629,7 @@ describe('Camera', () => { it('should subscribe to discovered doorbell entities for state changes', async () => { const { stateWatcher } = await initializeDoorbellCamera(); - expect(stateWatcher.subscribe).toBeCalledWith(expect.any(Function), [ + expect(stateWatcher.subscribe).toHaveBeenCalledWith(expect.any(Function), [ 'event.front_door_doorbell', ]); }); @@ -671,7 +670,7 @@ describe('Camera', () => { capabilityOptions: { capabilities: createCapabilities({ trigger: true }) }, }); - expect(stateWatcher.subscribe).toBeCalled(); + expect(stateWatcher.subscribe).toHaveBeenCalled(); const diff = { entityID: 'sensor.force_update', @@ -680,7 +679,7 @@ describe('Camera', () => { }; callStateWatcherCallback(stateWatcher, diff); - expect(eventCallback).toBeCalledWith({ + expect(eventCallback).toHaveBeenCalledWith({ cameraID: 'camera_1', id: 'sensor.force_update', type: eventType, @@ -707,7 +706,7 @@ describe('Camera', () => { capabilityOptions: { capabilities: createCapabilities({ trigger: true }) }, }); - expect(eventWatcher.subscribe).toBeCalledTimes(1); + expect(eventWatcher.subscribe).toHaveBeenCalledTimes(1); const request = vi.mocked(eventWatcher.subscribe).mock.calls[0][0]; expect(request.event_type).toBe('zha_event'); expect(request.matcher).toBeUndefined(); @@ -717,14 +716,14 @@ describe('Camera', () => { createHASSEvent('zha_event', { command: 'press' }), ); - expect(eventCallback).toBeCalledWith({ + expect(eventCallback).toHaveBeenCalledWith({ cameraID: 'camera_1', id: 'event:zha_event', type: 'momentary', }); await camera.destroy(); - expect(eventWatcher.unsubscribe).toBeCalled(); + expect(eventWatcher.unsubscribe).toHaveBeenCalled(); }); it('should attach a context-only matcher when only a context filter is set', async () => { @@ -775,7 +774,7 @@ describe('Camera', () => { capabilityOptions: { capabilities: createCapabilities({ trigger: true }) }, }); - expect(eventWatcher.subscribe).toBeCalledTimes(2); + expect(eventWatcher.subscribe).toHaveBeenCalledTimes(2); expect(vi.mocked(eventWatcher.subscribe).mock.calls[0][0].event_type).toBe( 'zha_event', ); @@ -828,7 +827,7 @@ describe('Camera', () => { capabilityOptions: { capabilities: createCapabilities({ trigger: false }) }, }); - expect(eventWatcher.subscribe).not.toBeCalled(); + expect(eventWatcher.subscribe).not.toHaveBeenCalled(); }); it('should not dispatch when the helper returns null', async () => { @@ -860,7 +859,7 @@ describe('Camera', () => { newState: createStateEntity({ state: '2026-05-24T12:00:05.123+00:00' }), }); - expect(eventCallback).not.toBeCalled(); + expect(eventCallback).not.toHaveBeenCalled(); }); it('should dispatch a momentary event for an event entity fire', async () => { @@ -892,8 +891,8 @@ describe('Camera', () => { newState: createStateEntity({ state: '2026-05-24T12:00:05.123+00:00' }), }); - expect(eventCallback).toBeCalledTimes(1); - expect(eventCallback).toBeCalledWith({ + expect(eventCallback).toHaveBeenCalledTimes(1); + expect(eventCallback).toHaveBeenCalledWith({ cameraID: 'camera_1', id: 'event.front_door_doorbell', type: 'momentary', @@ -921,7 +920,7 @@ describe('Camera', () => { capabilityOptions: { capabilities: createCapabilities({ trigger: false }) }, }); - expect(stateWatcher.subscribe).not.toBeCalled(); + expect(stateWatcher.subscribe).not.toHaveBeenCalled(); }); }); diff --git a/tests/camera-manager/engine-factory.test.ts b/tests/camera-manager/engine-factory.test.ts index c20587b8..6ab11a98 100644 --- a/tests/camera-manager/engine-factory.test.ts +++ b/tests/camera-manager/engine-factory.test.ts @@ -12,9 +12,9 @@ import type { CardWideConfig } from '../../src/config/schema/types.js'; import type { DeviceRegistryManager } from '../../src/ha/registry/device'; import type { EntityRegistryManager } from '../../src/ha/registry/entity/types.js'; import type { ResolvedMediaCache } from '../../src/ha/resolved-media.js'; +import { createCameraConfig } from '../config/test-utils'; import { EntityRegistryManagerMock } from '../ha/registry/entity/mock.js'; import { - createCameraConfig, createHASS, createHASSManager, createRegistryEntity, diff --git a/tests/camera-manager/frigate/camera.test.ts b/tests/camera-manager/frigate/camera.test.ts index aec4f78f..5ccbebd0 100644 --- a/tests/camera-manager/frigate/camera.test.ts +++ b/tests/camera-manager/frigate/camera.test.ts @@ -26,15 +26,15 @@ import type { EntityRegistryManager, } from '../../../src/ha/registry/entity/types'; import { ViewMediaType } from '../../../src/view/item'; +import { createCameraConfig } from '../../config/test-utils'; import { EntityRegistryManagerMock } from '../../ha/registry/entity/mock'; import { - createCameraConfig, - createCapabilities, createHASS, createHASSManager, createRegistryEntity, createStateEntity, } from '../../test-utils'; +import { createCapabilities } from '../test-utils'; vi.mock('../../../src/camera-manager/frigate/requests'); @@ -98,7 +98,7 @@ describe('FrigateCamera', () => { frigateEventWatcher: mock(), frigateReviewWatcher: mock(), }), - ).rejects.toThrowError(/Could not find camera entity/); + ).rejects.toThrow(/Could not find camera entity/); }); it('with a valid camera_entity', async () => { @@ -315,7 +315,7 @@ describe('FrigateCamera', () => { expect(camera.getCapabilities()?.has('snapshots')).toBeTruthy(); expect(camera.getCapabilities()?.has('recordings')).toBeTruthy(); expect(camera.getCapabilities()?.has('trigger')).toBeTruthy(); - expect(vi.mocked(getPTZInfo)).toBeCalled(); + expect(vi.mocked(getPTZInfo)).toHaveBeenCalled(); }); it('basic birdseye', async () => { @@ -342,7 +342,7 @@ describe('FrigateCamera', () => { expect(camera.getCapabilities()?.has('snapshots')).toBeFalsy(); expect(camera.getCapabilities()?.has('recordings')).toBeFalsy(); expect(camera.getCapabilities()?.has('trigger')).toBeTruthy(); - expect(vi.mocked(getPTZInfo)).not.toBeCalled(); + expect(vi.mocked(getPTZInfo)).not.toHaveBeenCalled(); }); describe('with ptz', () => { @@ -368,7 +368,7 @@ describe('FrigateCamera', () => { expect(camera.getCapabilities()?.has('ptz')).toBeFalsy(); expect(camera.getCapabilities()?.hasPTZCapability()).toBeFalsy(); - expect(consoleSpy).toBeCalled(); + expect(consoleSpy).toHaveBeenCalled(); }); it('when getPTZInfo call succeeds with continuous motion', async () => { @@ -824,7 +824,7 @@ describe('FrigateCamera', () => { frigateEventWatcher: eventWatcher, frigateReviewWatcher: mock(), }); - expect(eventWatcher.subscribe).toBeCalledWith( + expect(eventWatcher.subscribe).toHaveBeenCalledWith( expect.objectContaining({ instanceID: 'CLIENT_ID', }), @@ -853,7 +853,7 @@ describe('FrigateCamera', () => { frigateEventWatcher: eventWatcher, frigateReviewWatcher: mock(), }); - expect(eventWatcher.subscribe).not.toBeCalled(); + expect(eventWatcher.subscribe).not.toHaveBeenCalled(); }); it('should not subscribe without trigger capability', async () => { @@ -878,7 +878,7 @@ describe('FrigateCamera', () => { frigateEventWatcher: eventWatcher, frigateReviewWatcher: mock(), }); - expect(eventWatcher.subscribe).not.toBeCalled(); + expect(eventWatcher.subscribe).not.toHaveBeenCalled(); }); it('should not subscribe with no camera name', async () => { @@ -902,7 +902,7 @@ describe('FrigateCamera', () => { frigateEventWatcher: eventWatcher, frigateReviewWatcher: mock(), }); - expect(eventWatcher.subscribe).not.toBeCalled(); + expect(eventWatcher.subscribe).not.toHaveBeenCalled(); }); it('should unsubscribe on destroy', async () => { @@ -926,10 +926,10 @@ describe('FrigateCamera', () => { frigateEventWatcher: eventWatcher, frigateReviewWatcher: mock(), }); - expect(eventWatcher.unsubscribe).not.toBeCalled(); + expect(eventWatcher.unsubscribe).not.toHaveBeenCalled(); await camera.destroy(); - expect(eventWatcher.unsubscribe).toBeCalled(); + expect(eventWatcher.unsubscribe).toHaveBeenCalled(); }); it('should not subscribe when destroyed while base initialization is pending', async () => { @@ -965,22 +965,22 @@ describe('FrigateCamera', () => { frigateReviewWatcher: reviewWatcher, capabilityOptions: { capabilities: createCapabilities({ trigger: true }) }, }); - await vi.waitFor(() => expect(entityRegistryManager.getEntity).toBeCalled()); + await vi.waitFor(() => expect(entityRegistryManager.getEntity).toHaveBeenCalled()); await camera.destroy(); // `_destroyed` short-circuits initialize() after the pending await, so // neither watcher is ever subscribed. - expect(eventWatcher.subscribe).not.toBeCalled(); - expect(reviewWatcher.subscribe).not.toBeCalled(); + expect(eventWatcher.subscribe).not.toHaveBeenCalled(); + expect(reviewWatcher.subscribe).not.toHaveBeenCalled(); resolveEntity(); await initializePromise; - expect(eventWatcher.subscribe).not.toBeCalled(); - expect(eventWatcher.unsubscribe).not.toBeCalled(); - expect(reviewWatcher.subscribe).not.toBeCalled(); - expect(reviewWatcher.unsubscribe).not.toBeCalled(); + expect(eventWatcher.subscribe).not.toHaveBeenCalled(); + expect(eventWatcher.unsubscribe).not.toHaveBeenCalled(); + expect(reviewWatcher.subscribe).not.toHaveBeenCalled(); + expect(reviewWatcher.unsubscribe).not.toHaveBeenCalled(); }); describe('should call handler correctly', () => { @@ -1097,7 +1097,7 @@ describe('FrigateCamera', () => { }); if (call) { - expect(eventCallback).toBeCalledWith({ + expect(eventCallback).toHaveBeenCalledWith({ type: 'new', cameraID: 'CAMERA_1', id: 'event-1', @@ -1106,7 +1106,7 @@ describe('FrigateCamera', () => { fidelity: 'high', }); } else { - expect(eventCallback).not.toBeCalled(); + expect(eventCallback).not.toHaveBeenCalled(); } }, ); @@ -1170,7 +1170,7 @@ describe('FrigateCamera', () => { }, }); - expect(eventCallback).toBeCalledWith({ + expect(eventCallback).toHaveBeenCalledWith({ type: 'end', cameraID: 'CAMERA_1', id: 'event-1', @@ -1378,7 +1378,7 @@ describe('FrigateCamera', () => { frigateEventWatcher: mock(), frigateReviewWatcher: reviewWatcher, }); - expect(reviewWatcher.subscribe).toBeCalledWith( + expect(reviewWatcher.subscribe).toHaveBeenCalledWith( expect.objectContaining({ instanceID: 'CLIENT_ID', }), @@ -1409,7 +1409,7 @@ describe('FrigateCamera', () => { frigateEventWatcher: mock(), frigateReviewWatcher: reviewWatcher, }); - expect(reviewWatcher.subscribe).not.toBeCalled(); + expect(reviewWatcher.subscribe).not.toHaveBeenCalled(); }); it('should not subscribe to reviews with description only (no severities)', async () => { @@ -1438,7 +1438,7 @@ describe('FrigateCamera', () => { frigateReviewWatcher: reviewWatcher, }); // Severities are required - description alone is not enough - expect(reviewWatcher.subscribe).not.toBeCalled(); + expect(reviewWatcher.subscribe).not.toHaveBeenCalled(); }); describe('should call handler correctly', () => { @@ -1495,7 +1495,7 @@ describe('FrigateCamera', () => { }, }); - expect(eventCallback).toBeCalledWith({ + expect(eventCallback).toHaveBeenCalledWith({ type: 'new', cameraID: 'CAMERA_1', id: '123', @@ -1566,7 +1566,7 @@ describe('FrigateCamera', () => { }, }); - expect(eventCallback).toBeCalledWith({ + expect(eventCallback).toHaveBeenCalledWith({ type: 'update', cameraID: 'CAMERA_1', id: '123', @@ -1637,7 +1637,7 @@ describe('FrigateCamera', () => { }, }); - expect(eventCallback).toBeCalledWith({ + expect(eventCallback).toHaveBeenCalledWith({ type: 'update', cameraID: 'CAMERA_1', id: '123', @@ -1995,7 +1995,7 @@ describe('FrigateCamera', () => { }, }); - expect(eventCallback).toBeCalledWith({ + expect(eventCallback).toHaveBeenCalledWith({ type: 'end', cameraID: 'CAMERA_1', id: '123', @@ -2123,7 +2123,7 @@ describe('FrigateCamera', () => { frigateEventWatcher: mock(), frigateReviewWatcher: mock(), }), - ).rejects.toThrowError(/Could not find camera entity/); + ).rejects.toThrow(/Could not find camera entity/); }); }); @@ -2284,7 +2284,7 @@ describe('FrigateCamera', () => { const executor = mock(); await camera.executePTZAction(executor, 'preset'); - expect(executor.executeActions).not.toBeCalled(); + expect(executor.executeActions).not.toHaveBeenCalled(); }); it('should ignore actions with configured action', async () => { @@ -2316,7 +2316,7 @@ describe('FrigateCamera', () => { const executor = mock(); await camera.executePTZAction(executor, 'left', { phase: 'start' }); - expect(executor.executeActions).toBeCalledTimes(1); + expect(executor.executeActions).toHaveBeenCalledTimes(1); expect(executor.executeActions).toHaveBeenLastCalledWith({ actions: { action: 'perform-action', diff --git a/tests/camera-manager/frigate/engine-frigate.test.ts b/tests/camera-manager/frigate/engine-frigate.test.ts index fe6976a7..5e8746dd 100644 --- a/tests/camera-manager/frigate/engine-frigate.test.ts +++ b/tests/camera-manager/frigate/engine-frigate.test.ts @@ -38,17 +38,17 @@ import type { RawAdvancedCameraCardConfig } from '../../../src/config/types'; import { QuerySource } from '../../../src/query-source'; import type { Severity } from '../../../src/severity'; import { ViewMedia, ViewMediaType } from '../../../src/view/item'; +import { createCameraConfig } from '../../config/test-utils'; import { EntityRegistryManagerMock } from '../../ha/registry/entity/mock'; import { - createCameraConfig, createFrigateEvent, createFrigateRecording, createFrigateReview, createHASS, createHASSManager, - createStore, - TestViewMedia, } from '../../test-utils'; +import { TestViewMedia } from '../../view/test-utils'; +import { createStore } from '../test-utils'; vi.mock('../../../src/camera-manager/frigate/requests'); diff --git a/tests/camera-manager/frigate/media.test.ts b/tests/camera-manager/frigate/media.test.ts index f7fceb1d..80624faf 100644 --- a/tests/camera-manager/frigate/media.test.ts +++ b/tests/camera-manager/frigate/media.test.ts @@ -7,8 +7,8 @@ import { FrigateViewMediaFactory, } from '../../../src/camera-manager/frigate/media'; import { ViewMediaType } from '../../../src/view/item'; +import { createCameraConfig } from '../../config/test-utils'; import { - createCameraConfig, createFrigateEvent, createFrigateRecording, createFrigateReview, diff --git a/tests/camera-manager/frigate/requests.test.ts b/tests/camera-manager/frigate/requests.test.ts index 3cf60473..5b25f621 100644 --- a/tests/camera-manager/frigate/requests.test.ts +++ b/tests/camera-manager/frigate/requests.test.ts @@ -46,7 +46,7 @@ describe('frigate requests', () => { expect(await getRecordingsSummary(hass, 'clientID', 'camera.office')).toBe( recordingSummary, ); - expect(homeAssistantWSRequest).toBeCalledWith( + expect(homeAssistantWSRequest).toHaveBeenCalledWith( hass, recordingSummarySchema, expect.objectContaining({ @@ -77,7 +77,7 @@ describe('frigate requests', () => { before: 0, }), ).toBe(recordingSegments); - expect(homeAssistantWSRequest).toBeCalledWith( + expect(homeAssistantWSRequest).toHaveBeenCalledWith( hass, recordingSegmentsSchema, expect.objectContaining({ @@ -101,7 +101,7 @@ describe('frigate requests', () => { const hass = createHASS(); retainEvent(hass, 'clientID', 'eventID', true); - expect(homeAssistantWSRequest).toBeCalledWith( + expect(homeAssistantWSRequest).toHaveBeenCalledWith( hass, retainResultSchema, expect.objectContaining({ @@ -121,10 +121,10 @@ describe('frigate requests', () => { }); const hass = createHASS(); - await expect(retainEvent(hass, 'clientID', 'eventID', true)).rejects.toThrowError( + await expect(retainEvent(hass, 'clientID', 'eventID', true)).rejects.toThrow( /Could not retain event/, ); - expect(homeAssistantWSRequest).toBeCalledWith( + expect(homeAssistantWSRequest).toHaveBeenCalledWith( hass, retainResultSchema, expect.objectContaining({ @@ -157,7 +157,7 @@ describe('frigate requests', () => { favorites: true, }), ).toBe(events); - expect(homeAssistantWSRequest).toBeCalledWith( + expect(homeAssistantWSRequest).toHaveBeenCalledWith( hass, frigateEventsSchema, expect.objectContaining({ @@ -193,7 +193,7 @@ describe('frigate requests', () => { vi.mocked(homeAssistantWSRequest).mockResolvedValue(eventSummary); expect(await getEventSummary(hass, 'clientID')).toBe(eventSummary); - expect(homeAssistantWSRequest).toBeCalledWith( + expect(homeAssistantWSRequest).toHaveBeenCalledWith( hass, eventSummarySchema, expect.objectContaining({ @@ -216,7 +216,7 @@ describe('frigate requests', () => { const hass = createHASS(); vi.mocked(homeAssistantWSRequest).mockResolvedValue(ptzInfo); expect(await getPTZInfo(hass, 'clientID', 'camera.office')).toBe(ptzInfo); - expect(homeAssistantWSRequest).toBeCalledWith( + expect(homeAssistantWSRequest).toHaveBeenCalledWith( hass, ptzInfoSchema, expect.objectContaining({ @@ -259,7 +259,7 @@ describe('frigate requests', () => { reviewed: false, }), ).toBe(reviews); - expect(homeAssistantWSRequest).toBeCalledWith( + expect(homeAssistantWSRequest).toHaveBeenCalledWith( hass, frigateReviewsSchema, expect.objectContaining({ @@ -288,7 +288,7 @@ describe('frigate requests', () => { const hass = createHASS(); setReviewsReviewed(hass, 'clientID', ['review_id'], true); - expect(homeAssistantWSRequest).toBeCalledWith( + expect(homeAssistantWSRequest).toHaveBeenCalledWith( hass, reviewResultSchema, expect.objectContaining({ @@ -309,8 +309,8 @@ describe('frigate requests', () => { const hass = createHASS(); await expect( setReviewsReviewed(hass, 'clientID', ['review_id'], true), - ).rejects.toThrowError(/Failed to receive response from Home Assistant/); - expect(homeAssistantWSRequest).toBeCalledWith( + ).rejects.toThrow(/Failed to receive response from Home Assistant/); + expect(homeAssistantWSRequest).toHaveBeenCalledWith( hass, reviewResultSchema, expect.objectContaining({ diff --git a/tests/camera-manager/frigate/util.test.ts b/tests/camera-manager/frigate/util.test.ts index a6894b3d..e6850d56 100644 --- a/tests/camera-manager/frigate/util.test.ts +++ b/tests/camera-manager/frigate/util.test.ts @@ -15,8 +15,8 @@ import { getReviewTitle, } from '../../../src/camera-manager/frigate/util'; import type { CameraConfig } from '../../../src/config/schema/cameras'; +import { createCameraConfig } from '../../config/test-utils'; import { - createCameraConfig, createFrigateEvent, createFrigateRecording, createFrigateReview, diff --git a/tests/camera-manager/frigate/watcher.test.ts b/tests/camera-manager/frigate/watcher.test.ts index 923d5975..9e0087f4 100644 --- a/tests/camera-manager/frigate/watcher.test.ts +++ b/tests/camera-manager/frigate/watcher.test.ts @@ -93,7 +93,7 @@ describe('FrigateEventWatcher', () => { watcher.subscribe({ instanceID: 'frigate', callback: vi.fn() }); await flushPromises(); - expect(hass.connection.subscribeMessage).toBeCalledWith( + expect(hass.connection.subscribeMessage).toHaveBeenCalledWith( expect.any(Function), expect.objectContaining({ type: 'frigate/events/subscribe', @@ -116,7 +116,7 @@ describe('FrigateEventWatcher', () => { watcher.unsubscribe(request); await flushPromises(); - expect(unsub).toBeCalledTimes(1); + expect(unsub).toHaveBeenCalledTimes(1); }); it('should drop messages from an old-connection subscription after a swap', async () => { @@ -140,7 +140,7 @@ describe('FrigateEventWatcher', () => { await flushPromises(); oldDispatcher(JSON.stringify(createEventChange())); - expect(callback).not.toBeCalled(); + expect(callback).not.toHaveBeenCalled(); }); describe('should call handler', () => { @@ -156,8 +156,8 @@ describe('FrigateEventWatcher', () => { await flushPromises(); fireMessage(hass, 'NOT_JSON'); - expect(callback).not.toBeCalled(); - expect(spy).toBeCalledWith( + expect(callback).not.toHaveBeenCalled(); + expect(spy).toHaveBeenCalledWith( 'Received non-JSON payload from subscription: frigate/events/subscribe', 'NOT_JSON', ); @@ -176,8 +176,8 @@ describe('FrigateEventWatcher', () => { const data = JSON.stringify({}); fireMessage(hass, data); - expect(callback).not.toBeCalled(); - expect(spy).toBeCalledWith( + expect(callback).not.toHaveBeenCalled(); + expect(spy).toHaveBeenCalledWith( 'Received malformed message from subscription: frigate/events/subscribe', data, ); @@ -194,7 +194,7 @@ describe('FrigateEventWatcher', () => { const eventChange = createEventChange(); fireMessage(hass, JSON.stringify(eventChange)); - expect(callback).toBeCalledWith(eventChange); + expect(callback).toHaveBeenCalledWith(eventChange); }); it('with a matcher', async () => { @@ -219,8 +219,8 @@ describe('FrigateEventWatcher', () => { const eventChange = createEventChange(); fireMessage(hass, JSON.stringify(eventChange)); - expect(non_matching_callback).not.toBeCalledWith(eventChange); - expect(matching_callback).toBeCalledWith(eventChange); + expect(non_matching_callback).not.toHaveBeenCalledWith(eventChange); + expect(matching_callback).toHaveBeenCalledWith(eventChange); }); }); }); @@ -239,7 +239,7 @@ describe('FrigateReviewWatcher', () => { watcher.subscribe({ instanceID: 'frigate', callback }); await flushPromises(); - expect(hass.connection.subscribeMessage).toBeCalledWith( + expect(hass.connection.subscribeMessage).toHaveBeenCalledWith( expect.any(Function), expect.objectContaining({ type: 'frigate/reviews/subscribe', @@ -249,6 +249,6 @@ describe('FrigateReviewWatcher', () => { const reviewChange = createReviewChange(); fireMessage(hass, JSON.stringify(reviewChange)); - expect(callback).toBeCalledWith(reviewChange); + expect(callback).toHaveBeenCalledWith(reviewChange); }); }); diff --git a/tests/camera-manager/generic/engine-generic.test.ts b/tests/camera-manager/generic/engine-generic.test.ts index 4ebcb89a..7532e67c 100644 --- a/tests/camera-manager/generic/engine-generic.test.ts +++ b/tests/camera-manager/generic/engine-generic.test.ts @@ -5,14 +5,10 @@ import { Engine, QueryResultsType, QueryType } from '../../../src/camera-manager import type { CameraConfig } from '../../../src/config/schema/cameras'; import type { RawAdvancedCameraCardConfig } from '../../../src/config/types'; import { QuerySource } from '../../../src/query-source'; -import { - createCameraConfig, - createHASS, - createHASSManager, - createStateEntity, - createStore, - TestViewMedia, -} from '../../test-utils'; +import { createCameraConfig } from '../../config/test-utils'; +import { createHASS, createHASSManager, createStateEntity } from '../../test-utils'; +import { TestViewMedia } from '../../view/test-utils'; +import { createStore } from '../test-utils'; const createEngine = (): GenericCameraManagerEngine => { return new GenericCameraManagerEngine(createHASSManager()); diff --git a/tests/camera-manager/manager.test.ts b/tests/camera-manager/manager.test.ts index 0595d7c2..2f1623db 100644 --- a/tests/camera-manager/manager.test.ts +++ b/tests/camera-manager/manager.test.ts @@ -36,17 +36,10 @@ import { QuerySource } from '../../src/query-source.js'; import { PTZMovementType, type Endpoint } from '../../src/types.js'; import { ViewFolder, type ViewItem, type ViewMedia } from '../../src/view/item.js'; import type { ViewItemCapabilities } from '../../src/view/types.js'; -import { - createCameraConfig, - createCapabilities, - createCardAPI, - createConfig, - createFolder, - createHASS, - createInitializedCamera, - generateViewMediaArray, - TestViewMedia, -} from '../test-utils.js'; +import { createCameraConfig, createConfig } from '../config/test-utils'; +import { createCardAPI, createFolder, createHASS } from '../test-utils.js'; +import { generateViewMediaArray, TestViewMedia } from '../view/test-utils'; +import { createCapabilities, createInitializedCamera } from './test-utils'; describe('QueryClassifier', () => { it('should classify event query', () => { @@ -481,7 +474,7 @@ describe('CameraManager', () => { type: 'new', }; eventCallback?.(cameraEvent); - expect(api.getCameraTriggersManager().handleCameraEvent).toBeCalledWith( + expect(api.getCameraTriggersManager().handleCameraEvent).toHaveBeenCalledWith( cameraEvent, ); }); @@ -505,7 +498,7 @@ describe('CameraManager', () => { ]); await manager.initializeCamerasFromConfig(); - expect(api.getEntityRegistryManager().fetchEntityList).toBeCalled(); + expect(api.getEntityRegistryManager().fetchEntityList).toHaveBeenCalled(); }, ); @@ -516,7 +509,7 @@ describe('CameraManager', () => { const manager = createCameraManager(api); await manager.initializeCamerasFromConfig(); - expect(api.getEntityRegistryManager().fetchEntityList).not.toBeCalled(); + expect(api.getEntityRegistryManager().fetchEntityList).not.toHaveBeenCalled(); }); }); @@ -710,7 +703,7 @@ describe('CameraManager', () => { const results = new Map([[baseEventQuery, baseEventQueryResults]]); engine.getEvents.mockResolvedValue(results); expect(await manager.getEvents(baseEventQuery, engineOptions)).toEqual(results); - expect(engine.getEvents).toBeCalledWith( + expect(engine.getEvents).toHaveBeenCalledWith( hass, expect.anything(), baseEventQuery, @@ -743,7 +736,12 @@ describe('CameraManager', () => { await manager.reviewMedia(media, true); - expect(engine.reviewMedia).toBeCalledWith(hass, expect.anything(), media, true); + expect(engine.reviewMedia).toHaveBeenCalledWith( + hass, + expect.anything(), + media, + true, + ); }); }); @@ -1210,7 +1208,7 @@ describe('CameraManager', () => { const manager = createCameraManager(createCardAPI(), engine); manager.favoriteMedia(new TestViewMedia(), true); - expect(engine.favoriteMedia).not.toBeCalled(); + expect(engine.favoriteMedia).not.toHaveBeenCalled(); }); it('should succeed', async () => { @@ -1225,7 +1223,12 @@ describe('CameraManager', () => { const media = new TestViewMedia({ cameraID: 'id' }); manager.favoriteMedia(media, true); - expect(engine.favoriteMedia).toBeCalledWith(hass, expect.anything(), media, true); + expect(engine.favoriteMedia).toHaveBeenCalledWith( + hass, + expect.anything(), + media, + true, + ); }); }); @@ -1396,7 +1399,7 @@ describe('CameraManager', () => { vi.mocked(api.getHASSManager().getHASS).mockReturnValue(null); manager.executePTZAction('another', 'left'); - expect(api.getActionsManager().executeActions).toBeCalledWith({ + expect(api.getActionsManager().executeActions).toHaveBeenCalledWith({ actions: action, }); }); @@ -1425,7 +1428,9 @@ describe('CameraManager', () => { manager.executePTZAction('another', 'left'); - expect(api.getActionsManager().executeActions).toBeCalledWith({ actions: action }); + expect(api.getActionsManager().executeActions).toHaveBeenCalledWith({ + actions: action, + }); }); describe('with rotation', () => { @@ -1544,7 +1549,7 @@ describe('CameraManager', () => { preset: presetAction, }; - expect(api.getActionsManager().executeActions).toBeCalledWith({ + expect(api.getActionsManager().executeActions).toHaveBeenCalledWith({ actions: expectedActionMap[expectedAction], }); }, @@ -1657,7 +1662,7 @@ describe('CameraManager', () => { }); expect(await manager.getMediaSeekTime(media, middleTime)).toBe(42); - expect(engine.getMediaSeekTime).toBeCalledWith( + expect(engine.getMediaSeekTime).toHaveBeenCalledWith( expect.anything(), expect.anything(), media, diff --git a/tests/camera-manager/motioneye/camera.test.ts b/tests/camera-manager/motioneye/camera.test.ts index fa005485..1c5ef704 100644 --- a/tests/camera-manager/motioneye/camera.test.ts +++ b/tests/camera-manager/motioneye/camera.test.ts @@ -3,12 +3,9 @@ import { mock } from 'vitest-mock-extended'; import type { CameraManagerEngine } from '../../../src/camera-manager/engine'; import { MotionEyeCamera } from '../../../src/camera-manager/motioneye/camera'; +import { createCameraConfig } from '../../config/test-utils'; import { EntityRegistryManagerMock } from '../../ha/registry/entity/mock'; -import { - createCameraConfig, - createHASSManager, - createRegistryEntity, -} from '../../test-utils'; +import { createHASSManager, createRegistryEntity } from '../../test-utils'; const cameraEntity = createRegistryEntity({ entity_id: 'camera.motioneye', diff --git a/tests/camera-manager/motioneye/engine-motioneye.test.ts b/tests/camera-manager/motioneye/engine-motioneye.test.ts index dbd496bc..1a392fb8 100644 --- a/tests/camera-manager/motioneye/engine-motioneye.test.ts +++ b/tests/camera-manager/motioneye/engine-motioneye.test.ts @@ -22,9 +22,9 @@ import { import type { Entity } from '../../../src/ha/registry/entity/types'; import { ResolvedMediaCache } from '../../../src/ha/resolved-media'; import { QuerySource } from '../../../src/query-source'; +import { createCameraConfig } from '../../config/test-utils'; import { EntityRegistryManagerMock } from '../../ha/registry/entity/mock'; import { - createCameraConfig, createHASS, createHASSManager, createRegistryEntity, diff --git a/tests/camera-manager/reolink/camera.test.ts b/tests/camera-manager/reolink/camera.test.ts index 1cbb69e4..80abc75b 100644 --- a/tests/camera-manager/reolink/camera.test.ts +++ b/tests/camera-manager/reolink/camera.test.ts @@ -7,9 +7,9 @@ import type { CameraProxyConfig } from '../../../src/camera-manager/types'; import type { ActionsExecutor } from '../../../src/card-controller/actions/types'; import type { DeviceRegistryManager } from '../../../src/ha/registry/device'; import type { EntityRegistryManagerLive } from '../../../src/ha/registry/entity'; +import { createCameraConfig } from '../../config/test-utils'; import { EntityRegistryManagerMock } from '../../ha/registry/entity/mock'; import { - createCameraConfig, createHASS, createHASSManager, createRegistryEntity, @@ -107,7 +107,7 @@ describe('ReolinkCamera', () => { entityRegistryManager: mock(), deviceRegistryManager: mock(), }), - ).rejects.toThrowError('Could not find camera entity'); + ).rejects.toThrow('Could not find camera entity'); }); it('without a unique_id', async () => { @@ -130,7 +130,7 @@ describe('ReolinkCamera', () => { entityRegistryManager, deviceRegistryManager: mock(), }), - ).rejects.toThrowError('Could not initialize Reolink camera'); + ).rejects.toThrow('Could not initialize Reolink camera'); }); it('without a valid unique_id', async () => { @@ -153,7 +153,7 @@ describe('ReolinkCamera', () => { entityRegistryManager, deviceRegistryManager: mock(), }), - ).rejects.toThrowError('Could not initialize Reolink camera'); + ).rejects.toThrow('Could not initialize Reolink camera'); }); it('successfully with a directly connected camera', async () => { @@ -729,7 +729,7 @@ describe('ReolinkCamera', () => { await camera.executePTZAction(executor, 'left'); await camera.executePTZAction(executor, 'left', { phase: 'start' }); - expect(executor.executeActions).not.toBeCalled(); + expect(executor.executeActions).not.toHaveBeenCalled(); }); it('should ignore actions with configured action', async () => { @@ -758,7 +758,7 @@ describe('ReolinkCamera', () => { const executor = mock(); await camera.executePTZAction(executor, 'left', { phase: 'start' }); - expect(executor.executeActions).toBeCalledTimes(1); + expect(executor.executeActions).toHaveBeenCalledTimes(1); expect(executor.executeActions).toHaveBeenLastCalledWith({ actions: { action: 'perform-action', diff --git a/tests/camera-manager/reolink/engine-reolink.test.ts b/tests/camera-manager/reolink/engine-reolink.test.ts index c7fed090..289451e9 100644 --- a/tests/camera-manager/reolink/engine-reolink.test.ts +++ b/tests/camera-manager/reolink/engine-reolink.test.ts @@ -31,15 +31,10 @@ import type { EntityRegistryManager } from '../../../src/ha/registry/entity/type import { ResolvedMediaCache } from '../../../src/ha/resolved-media'; import { homeAssistantWSRequest } from '../../../src/ha/ws-request'; import { QuerySource } from '../../../src/query-source'; +import { createCameraConfig } from '../../config/test-utils'; import { EntityRegistryManagerMock } from '../../ha/registry/entity/mock'; -import { - createCameraConfig, - createHASS, - createHASSManager, - createInitializedCamera, - createRegistryEntity, - createStore, -} from '../../test-utils'; +import { createHASS, createHASSManager, createRegistryEntity } from '../../test-utils'; +import { createInitializedCamera, createStore } from '../test-utils'; vi.mock('../../../src/ha/ws-request'); diff --git a/tests/camera-manager/store.test.ts b/tests/camera-manager/store.test.ts index bbc7a61f..a352e4df 100644 --- a/tests/camera-manager/store.test.ts +++ b/tests/camera-manager/store.test.ts @@ -9,12 +9,10 @@ import { Engine } from '../../src/camera-manager/types.js'; import type { DeviceRegistryManager } from '../../src/ha/registry/device/index.js'; import type { EntityRegistryManager } from '../../src/ha/registry/entity/types.js'; import type { ResolvedMediaCache } from '../../src/ha/resolved-media.js'; -import { - createCameraConfig, - createHASSManager, - createInitializedCamera, - TestViewMedia, -} from '../test-utils.js'; +import { createCameraConfig } from '../config/test-utils'; +import { createHASSManager } from '../test-utils.js'; +import { TestViewMedia } from '../view/test-utils'; +import { createInitializedCamera } from './test-utils'; describe('CameraManagerStore', async () => { const configVisible = createCameraConfig({ @@ -144,7 +142,7 @@ describe('CameraManagerStore', async () => { expect(store.getCameraCount()).toBe(0); for (const camera of cameras) { - expect(camera.destroy).toBeCalled(); + expect(camera.destroy).toHaveBeenCalled(); } }); @@ -381,10 +379,10 @@ describe('CameraManagerStore', async () => { expect(store.getCamera('camera-3')).toBe(camera_3_new); expect(store.getCamera('camera-4')).toBe(camera_4); - expect(camera_1.destroy).toBeCalled(); - expect(camera_2.destroy).not.toBeCalled(); - expect(camera_3.destroy).toBeCalled(); - expect(camera_3_new.destroy).not.toBeCalled(); - expect(camera_4.destroy).not.toBeCalled(); + expect(camera_1.destroy).toHaveBeenCalled(); + expect(camera_2.destroy).not.toHaveBeenCalled(); + expect(camera_3.destroy).toHaveBeenCalled(); + expect(camera_3_new.destroy).not.toHaveBeenCalled(); + expect(camera_4.destroy).not.toHaveBeenCalled(); }); }); diff --git a/tests/camera-manager/test-utils.ts b/tests/camera-manager/test-utils.ts new file mode 100644 index 00000000..c9092008 --- /dev/null +++ b/tests/camera-manager/test-utils.ts @@ -0,0 +1,90 @@ +import { vi } from 'vitest'; +import { mock } from 'vitest-mock-extended'; + +import { Camera } from '../../src/camera-manager/camera'; +import { Capabilities } from '../../src/camera-manager/capabilities'; +import type { CameraManagerEngine } from '../../src/camera-manager/engine'; +import { GenericCameraManagerEngine } from '../../src/camera-manager/generic/engine-generic'; +import type { CameraManager } from '../../src/camera-manager/manager'; +import { CameraManagerStore } from '../../src/camera-manager/store'; +import { type CameraEventCallback } from '../../src/camera-manager/types'; +import type { StateWatcherSubscriptionInterface } from '../../src/card-controller/hass/state-watcher'; +import { type CameraConfig } from '../../src/config/schema/cameras'; +import type { EntityRegistryManager } from '../../src/ha/registry/entity/types'; +import type { CapabilitiesRaw } from '../../src/types'; +import { createCameraConfig } from '../config/test-utils'; +import { createHASSManager } from '../test-utils'; + +export const createCapabilities = (capabilities?: CapabilitiesRaw): Capabilities => { + return new Capabilities({ + 'favorite-events': false, + 'favorite-recordings': false, + 'remote-control-entity': true, + clips: false, + live: false, + recordings: false, + seek: false, + snapshots: false, + trigger: true, + ...capabilities, + }); +}; + +export const createInitializedCamera = async ( + config: CameraConfig, + engine: CameraManagerEngine, + capabilities?: Capabilities, + stateWatcher?: StateWatcherSubscriptionInterface, +): Promise => { + const camera = new Camera(config, engine); + await camera.initialize({ + hassManager: createHASSManager({ stateWatcher }), + ...(capabilities ? { capabilityOptions: { capabilities } } : {}), + }); + return camera; +}; + +export const createStore = ( + cameras?: { + cameraID: string; + engine?: CameraManagerEngine; + config?: CameraConfig; + capabilities?: Capabilities | null; + eventCallback?: CameraEventCallback; + }[], +): CameraManagerStore => { + const store = new CameraManagerStore(); + for (const cameraProps of cameras ?? []) { + const eventCallback = cameraProps.eventCallback ?? vi.fn(); + const capabilities = + cameraProps.capabilities === undefined + ? createCapabilities() + : cameraProps.capabilities ?? undefined; + const camera = new Camera( + cameraProps.config ?? createCameraConfig(), + cameraProps.engine ?? + new GenericCameraManagerEngine( + createHASSManager(), + mock(), + eventCallback, + ), + { eventCallback, capabilities }, + ); + camera.setID(cameraProps.cameraID); + store.addCamera(camera); + } + return store; +}; + +export const createCameraManager = (store?: CameraManagerStore): CameraManager => { + const cameraStore = store ?? createStore(); + const cameraManager = mock(); + vi.mocked(cameraManager.getStore).mockReturnValue(cameraStore); + vi.mocked(cameraManager.getCameraCapabilities).mockImplementation( + (cameraID: string): Capabilities | null => { + return cameraStore.getCamera(cameraID)?.getCapabilities() ?? null; + }, + ); + + return cameraManager; +}; diff --git a/tests/camera-manager/tplink/camera.test.ts b/tests/camera-manager/tplink/camera.test.ts index bcd90e97..988b4bac 100644 --- a/tests/camera-manager/tplink/camera.test.ts +++ b/tests/camera-manager/tplink/camera.test.ts @@ -4,12 +4,9 @@ import { mock } from 'vitest-mock-extended'; import type { CameraManagerEngine } from '../../../src/camera-manager/engine'; import { TPLinkCamera } from '../../../src/camera-manager/tplink/camera'; import type { ActionsExecutor } from '../../../src/card-controller/actions/types'; +import { createCameraConfig } from '../../config/test-utils'; import { EntityRegistryManagerMock } from '../../ha/registry/entity/mock'; -import { - createCameraConfig, - createHASSManager, - createRegistryEntity, -} from '../../test-utils'; +import { createHASSManager, createRegistryEntity } from '../../test-utils'; describe('TPLinkCamera', () => { // Entity patterns from: https://github.com/dermotduffy/advanced-camera-card/issues/2183 @@ -83,7 +80,7 @@ describe('TPLinkCamera', () => { hassManager: createHASSManager(), entityRegistryManager: new EntityRegistryManagerMock(), }), - ).rejects.toThrowError('Could not find camera entity'); + ).rejects.toThrow('Could not find camera entity'); }); it('without a matching entity in registry', async () => { @@ -98,7 +95,7 @@ describe('TPLinkCamera', () => { hassManager: createHASSManager(), entityRegistryManager: new EntityRegistryManagerMock(), }), - ).rejects.toThrowError('Could not find camera entity'); + ).rejects.toThrow('Could not find camera entity'); }); it('successfully with webrtc_card.entity fallback', async () => { @@ -204,7 +201,7 @@ describe('TPLinkCamera', () => { await camera.executePTZAction(executor, 'left', { phase: 'start' }), ).toBeFalsy(); - expect(executor.executeActions).not.toBeCalled(); + expect(executor.executeActions).not.toHaveBeenCalled(); }); it('should ignore zoom actions (not supported by TPLink)', async () => { @@ -226,7 +223,7 @@ describe('TPLinkCamera', () => { await camera.executePTZAction(executor, 'zoom_out', { phase: 'start' }), ).toBeFalsy(); - expect(executor.executeActions).not.toBeCalled(); + expect(executor.executeActions).not.toHaveBeenCalled(); }); it('should ignore preset actions (not supported by TPLink)', async () => { @@ -245,7 +242,7 @@ describe('TPLinkCamera', () => { await camera.executePTZAction(executor, 'preset', { preset: 'home' }), ).toBeFalsy(); - expect(executor.executeActions).not.toBeCalled(); + expect(executor.executeActions).not.toHaveBeenCalled(); }); it('should handle stop phase (no-op for TPLink)', async () => { @@ -265,7 +262,7 @@ describe('TPLinkCamera', () => { await camera.executePTZAction(executor, 'left', { phase: 'stop' }), ).toBeTruthy(); - expect(executor.executeActions).not.toBeCalled(); + expect(executor.executeActions).not.toHaveBeenCalled(); }); it.each([ @@ -353,7 +350,7 @@ describe('TPLinkCamera', () => { const executor = mock(); await camera.executePTZAction(executor, 'left', { phase: 'start' }); - expect(executor.executeActions).toBeCalledTimes(1); + expect(executor.executeActions).toHaveBeenCalledTimes(1); expect(executor.executeActions).toHaveBeenLastCalledWith({ actions: { action: 'perform-action', diff --git a/tests/camera-manager/tplink/engine-tplink.test.ts b/tests/camera-manager/tplink/engine-tplink.test.ts index 13719e3a..cac0a91b 100644 --- a/tests/camera-manager/tplink/engine-tplink.test.ts +++ b/tests/camera-manager/tplink/engine-tplink.test.ts @@ -2,13 +2,9 @@ import { describe, expect, it } from 'vitest'; import { TPLinkCameraManagerEngine } from '../../../src/camera-manager/tplink/engine-tplink'; import { Engine } from '../../../src/camera-manager/types'; +import { createCameraConfig } from '../../config/test-utils'; import { EntityRegistryManagerMock } from '../../ha/registry/entity/mock'; -import { - createCameraConfig, - createHASS, - createHASSManager, - createRegistryEntity, -} from '../../test-utils'; +import { createHASS, createHASSManager, createRegistryEntity } from '../../test-utils'; const createEngine = (options?: { entityRegistryManager?: EntityRegistryManagerMock; diff --git a/tests/camera-manager/utils/go2rtc-endpoint.test.ts b/tests/camera-manager/utils/go2rtc-endpoint.test.ts index 0689bb79..48f6ad73 100644 --- a/tests/camera-manager/utils/go2rtc-endpoint.test.ts +++ b/tests/camera-manager/utils/go2rtc-endpoint.test.ts @@ -4,7 +4,7 @@ import { getGo2RTCMetadataEndpoint, getGo2RTCStreamEndpoint, } from '../../../src/camera-manager/utils/go2rtc/endpoint.js'; -import { createCameraConfig } from '../../test-utils.js'; +import { createCameraConfig } from '../../config/test-utils'; describe('getGo2RTCStreamEndpoint', () => { it('with local configuration', () => { diff --git a/tests/camera-manager/utils/ptz.test.ts b/tests/camera-manager/utils/ptz.test.ts index e271c96b..250565d0 100644 --- a/tests/camera-manager/utils/ptz.test.ts +++ b/tests/camera-manager/utils/ptz.test.ts @@ -8,7 +8,7 @@ import { } from '../../../src/camera-manager/utils/ptz'; import type { PTZAction } from '../../../src/config/schema/actions/custom/ptz'; import { PTZMovementType } from '../../../src/types'; -import { createCameraConfig } from '../../test-utils'; +import { createCameraConfig } from '../../config/test-utils'; const action = { action: 'perform-action' as const, diff --git a/tests/card-controller/actions/actions-manager.test.ts b/tests/card-controller/actions/actions-manager.test.ts index aae1f985..bf464875 100644 --- a/tests/card-controller/actions/actions-manager.test.ts +++ b/tests/card-controller/actions/actions-manager.test.ts @@ -22,14 +22,14 @@ import { createLogAction, } from '../../../src/utils/action'; import { arrayify } from '../../../src/utils/basic'; +import { createConfig } from '../../config/test-utils'; import { createCardAPI, - createConfig, createHASS, createMockTemplateRenderer, - createView, stubConnectedHomeAssistant, } from '../../test-utils'; +import { createView } from '../../view/test-utils'; const createAPI = (): CardController => { const api = createCardAPI(); @@ -215,7 +215,7 @@ describe('ActionsManager', () => { await manager.handleInteractionEvent( new CustomEvent('event', { detail: { action: 'tap' } }), ); - expect(consoleSpy).toBeCalled(); + expect(consoleSpy).toHaveBeenCalled(); }); describe('should handle unexpected interactions', () => { @@ -246,7 +246,7 @@ describe('ActionsManager', () => { detail: { action: interaction as unknown as InteractionName }, }), ); - expect(consoleSpy).not.toBeCalled(); + expect(consoleSpy).not.toHaveBeenCalled(); }, ); }); @@ -271,7 +271,7 @@ describe('ActionsManager', () => { const consoleSpy = vi.spyOn(global.console, 'info').mockReturnValue(undefined); await manager.handleCustomActionEvent(event); - expect(consoleSpy).toBeCalled(); + expect(consoleSpy).toHaveBeenCalled(); }); it('should not handle generic event', async () => { @@ -292,7 +292,7 @@ describe('ActionsManager', () => { await manager.handleCustomActionEvent(event); - expect(handler).not.toBeCalled(); + expect(handler).not.toHaveBeenCalled(); }); it('should not handle event without detail', async () => { @@ -300,7 +300,7 @@ describe('ActionsManager', () => { const consoleSpy = vi.spyOn(global.console, 'info').mockReturnValue(undefined); await manager.handleCustomActionEvent(new Event('ll-custom')); - expect(consoleSpy).not.toBeCalled(); + expect(consoleSpy).not.toHaveBeenCalled(); }); }); @@ -319,7 +319,7 @@ describe('ActionsManager', () => { detail: { actions: createLogAction('Hello, world!') }, }), ); - expect(consoleSpy).toBeCalled(); + expect(consoleSpy).toHaveBeenCalled(); }); }); @@ -334,7 +334,7 @@ describe('ActionsManager', () => { const consoleSpy = vi.spyOn(global.console, 'info').mockReturnValue(undefined); await manager.executeActions({ actions: createLogAction('Hello, world!') }); - expect(consoleSpy).toBeCalled(); + expect(consoleSpy).toHaveBeenCalled(); }); it('should execute actions', async () => { @@ -343,7 +343,7 @@ describe('ActionsManager', () => { const consoleSpy = vi.spyOn(global.console, 'info').mockReturnValue(undefined); await manager.executeActions({ actions: createLogAction('Hello, world!') }); - expect(consoleSpy).toBeCalled(); + expect(consoleSpy).toHaveBeenCalled(); }); it('should render templates', async () => { @@ -372,14 +372,12 @@ describe('ActionsManager', () => { await manager.executeActions({ actions: action, config, triggerData }); - expect(vi.mocked(api.getTemplateManager().renderRecursivelyAsType)).toBeCalledWith( - hass, - action, - { - conditionState, - triggerData, - }, - ); + expect( + vi.mocked(api.getTemplateManager().renderRecursivelyAsType), + ).toHaveBeenCalledWith(hass, action, { + conditionState, + triggerData, + }); }); it('should filter actions through the lock manager before rendering them', async () => { @@ -403,9 +401,9 @@ describe('ActionsManager', () => { // The lock manager sees the raw (unrendered) action; only the action it // returns is rendered and run. - expect(api.getLockManager().getAllowedActions).toBeCalledWith([rawAction]); - expect(allowedRan).toBeCalled(); - expect(rawRan).not.toBeCalled(); + expect(api.getLockManager().getAllowedActions).toHaveBeenCalledWith([rawAction]); + expect(allowedRan).toHaveBeenCalled(); + expect(rawRan).not.toHaveBeenCalled(); }); it('should render each action against the state at its turn', async () => { @@ -481,7 +479,7 @@ describe('ActionsManager', () => { expect(ran).toEqual(['one', 'two']); expect( vi.mocked(api.getTemplateManager().renderRecursivelyAsType), - ).toBeCalledTimes(1); + ).toHaveBeenCalledTimes(1); }); it('should abort the remaining actions when one fails to render', async () => { @@ -519,7 +517,7 @@ describe('ActionsManager', () => { // The first action ran; the second's render threw, aborting the rest. The // error was caught by executeActions(). expect(ran).toEqual(['first']); - expect(warnSpy).toBeCalled(); + expect(warnSpy).toHaveBeenCalled(); }); it('should hand an if-action branch to the executor unrendered, with the trigger data', async () => { @@ -549,7 +547,7 @@ describe('ActionsManager', () => { // The branch is left raw (template intact) and forwarded with the trigger // data, so the nested executor renders it per-step when it runs -- not // frozen against the state at the `if` step. - expect(api.getActionsManager().executeNestedActions).toBeCalledWith({ + expect(api.getActionsManager().executeNestedActions).toHaveBeenCalledWith({ actions: [thenAction], config: undefined, triggerData: { platform: 'state', entity_id: 'binary_sensor.door' }, @@ -557,7 +555,7 @@ describe('ActionsManager', () => { // The log action is handed to the (mocked) nested executor, not run here, // so it must not actually log. - expect(consoleSpy).not.toBeCalled(); + expect(consoleSpy).not.toHaveBeenCalled(); }); it('should render if-action branch actions per-step', async () => { @@ -633,7 +631,7 @@ describe('ActionsManager', () => { await manager.executeActions({ actions: createLogAction('Blocked') }); - expect(consoleSpy).not.toBeCalled(); + expect(consoleSpy).not.toHaveBeenCalled(); }); describe('should forward haptics', () => { @@ -651,7 +649,9 @@ describe('ActionsManager', () => { await manager.executeActions({ actions: { action: 'none' } }); - expect(handler).toBeCalledWith(expect.objectContaining({ detail: 'success' })); + expect(handler).toHaveBeenCalledWith( + expect.objectContaining({ detail: 'success' }), + ); }); it('should forward warning haptic', async () => { @@ -669,7 +669,9 @@ describe('ActionsManager', () => { actions: { action: 'none', confirmation: true }, }); - expect(handler).toBeCalledWith(expect.objectContaining({ detail: 'warning' })); + expect(handler).toHaveBeenCalledWith( + expect.objectContaining({ detail: 'warning' }), + ); }); }); }); @@ -712,7 +714,7 @@ describe('ActionsManager', () => { await promise; // Action set will not continue. - expect(consoleSpy).not.toBeCalled(); + expect(consoleSpy).not.toHaveBeenCalled(); }); }); }); diff --git a/tests/card-controller/actions/actions/base.test.ts b/tests/card-controller/actions/actions/base.test.ts index 5543bd89..4bb74500 100644 --- a/tests/card-controller/actions/actions/base.test.ts +++ b/tests/card-controller/actions/actions/base.test.ts @@ -44,7 +44,7 @@ describe('should handle base action', () => { await action.execute(api); - expect(confirm).not.toBeCalled(); + expect(confirm).not.toHaveBeenCalled(); }); it('should continue execution when confirmed', async () => { @@ -61,7 +61,7 @@ describe('should handle base action', () => { await action.execute(api); - expect(confirm).toBeCalled(); + expect(confirm).toHaveBeenCalled(); }); it('should abort execution when not confirmed', async () => { @@ -76,7 +76,7 @@ describe('should handle base action', () => { vi.mocked(confirm).mockReturnValue(false); - expect(async () => await action.execute(api)).rejects.toThrowError(/Aborted action/); + expect(async () => await action.execute(api)).rejects.toThrow(/Aborted action/); }); it('should not confirm when exempted', async () => { @@ -100,7 +100,7 @@ describe('should handle base action', () => { await action.execute(api); - expect(confirm).not.toBeCalled(); + expect(confirm).not.toHaveBeenCalled(); }); describe('should show correct confirmation text', () => { @@ -121,7 +121,7 @@ describe('should handle base action', () => { await action.execute(api); - expect(confirm).toBeCalledWith( + expect(confirm).toHaveBeenCalledWith( 'Are you sure you want to perform this action: more-info', ); }); @@ -143,7 +143,7 @@ describe('should handle base action', () => { await action.execute(api); - expect(confirm).toBeCalledWith( + expect(confirm).toHaveBeenCalledWith( 'Are you sure you want to perform this action: clips', ); }); @@ -167,7 +167,7 @@ describe('should handle base action', () => { await action.execute(api); - expect(confirm).toBeCalledWith('Test text'); + expect(confirm).toHaveBeenCalledWith('Test text'); }); }); }); diff --git a/tests/card-controller/actions/actions/call-answer.test.ts b/tests/card-controller/actions/actions/call-answer.test.ts index a337c6f9..ed4f926f 100644 --- a/tests/card-controller/actions/actions/call-answer.test.ts +++ b/tests/card-controller/actions/actions/call-answer.test.ts @@ -15,5 +15,5 @@ it('should handle call_answer action', async () => { await action.execute(api); - expect(api.getCallManager().answer).toBeCalled(); + expect(api.getCallManager().answer).toHaveBeenCalled(); }); diff --git a/tests/card-controller/actions/actions/call-end.test.ts b/tests/card-controller/actions/actions/call-end.test.ts index 6335e3fc..499a541c 100644 --- a/tests/card-controller/actions/actions/call-end.test.ts +++ b/tests/card-controller/actions/actions/call-end.test.ts @@ -15,5 +15,5 @@ it('should handle call_end action', async () => { await action.execute(api); - expect(api.getCallManager().end).toBeCalled(); + expect(api.getCallManager().end).toHaveBeenCalled(); }); diff --git a/tests/card-controller/actions/actions/call-service.test.ts b/tests/card-controller/actions/actions/call-service.test.ts index 30585f69..d05d14f4 100644 --- a/tests/card-controller/actions/actions/call-service.test.ts +++ b/tests/card-controller/actions/actions/call-service.test.ts @@ -20,7 +20,7 @@ describe('CallServiceAction', () => { ); await action.execute(api); - expect(hass.callService).toBeCalledWith( + expect(hass.callService).toHaveBeenCalledWith( 'light', 'turn_on', { diff --git a/tests/card-controller/actions/actions/call-start.test.ts b/tests/card-controller/actions/actions/call-start.test.ts index 42edb9af..73639810 100644 --- a/tests/card-controller/actions/actions/call-start.test.ts +++ b/tests/card-controller/actions/actions/call-start.test.ts @@ -15,7 +15,7 @@ it('should handle call_start action without a camera or stream', async () => { await action.execute(api); - expect(api.getCallManager().start).toBeCalledWith({ + expect(api.getCallManager().start).toHaveBeenCalledWith({ cameraID: undefined, streamID: undefined, }); @@ -35,7 +35,7 @@ it('should handle call_start action with a camera and stream', async () => { await action.execute(api); - expect(api.getCallManager().start).toBeCalledWith({ + expect(api.getCallManager().start).toHaveBeenCalledWith({ cameraID: 'camera.front', streamID: 'camera.front_doorbell', }); diff --git a/tests/card-controller/actions/actions/camera-select.test.ts b/tests/card-controller/actions/actions/camera-select.test.ts index 093b4ca4..c86e18ac 100644 --- a/tests/card-controller/actions/actions/camera-select.test.ts +++ b/tests/card-controller/actions/actions/camera-select.test.ts @@ -1,7 +1,9 @@ import { describe, expect, it, vi } from 'vitest'; import { CameraSelectAction } from '../../../../src/card-controller/actions/actions/camera-select'; -import { createCardAPI, createConfig, createView } from '../../../test-utils'; +import { createConfig } from '../../../config/test-utils'; +import { createCardAPI } from '../../../test-utils'; +import { createView } from '../../../view/test-utils'; describe('should handle camera_select action', () => { it('with valid camera and view', async () => { @@ -19,7 +21,7 @@ describe('should handle camera_select action', () => { await action.execute(api); - expect(api.getViewManager().setViewByParametersWithNewQuery).toBeCalledWith( + expect(api.getViewManager().setViewByParametersWithNewQuery).toHaveBeenCalledWith( expect.objectContaining({ params: { view: 'live', @@ -49,7 +51,7 @@ describe('should handle camera_select action', () => { await action.execute(api); - expect(api.getViewManager().setViewByParametersWithNewQuery).not.toBeCalled(); + expect(api.getViewManager().setViewByParametersWithNewQuery).not.toHaveBeenCalled(); }); it('without config', async () => { @@ -72,7 +74,7 @@ describe('should handle camera_select action', () => { await action.execute(api); - expect(api.getViewManager().setViewByParametersWithNewQuery).toBeCalledWith( + expect(api.getViewManager().setViewByParametersWithNewQuery).toHaveBeenCalledWith( expect.objectContaining({ params: { view: 'timeline', @@ -110,7 +112,7 @@ describe('should handle camera_select action', () => { await action.execute(api); - expect(api.getViewManager().setViewByParametersWithNewQuery).toBeCalledWith( + expect(api.getViewManager().setViewByParametersWithNewQuery).toHaveBeenCalledWith( expect.objectContaining({ params: { view: 'clips', @@ -139,7 +141,7 @@ describe('should handle camera_select action', () => { await action.execute(api); - expect(api.getViewManager().setViewByParametersWithNewQuery).toBeCalledWith( + expect(api.getViewManager().setViewByParametersWithNewQuery).toHaveBeenCalledWith( expect.objectContaining({ params: { view: 'live', @@ -167,7 +169,7 @@ describe('should handle camera_select action', () => { await action.execute(api); - expect(api.getViewManager().setViewByParametersWithNewQuery).not.toBeCalled(); + expect(api.getViewManager().setViewByParametersWithNewQuery).not.toHaveBeenCalled(); }); it('without a current view', async () => { @@ -184,6 +186,6 @@ describe('should handle camera_select action', () => { await action.execute(api); - expect(api.getViewManager().setViewByParametersWithNewQuery).not.toBeCalled(); + expect(api.getViewManager().setViewByParametersWithNewQuery).not.toHaveBeenCalled(); }); }); diff --git a/tests/card-controller/actions/actions/camera-ui.test.ts b/tests/card-controller/actions/actions/camera-ui.test.ts index 9483d227..a2ef18ab 100644 --- a/tests/card-controller/actions/actions/camera-ui.test.ts +++ b/tests/card-controller/actions/actions/camera-ui.test.ts @@ -15,5 +15,5 @@ it('should handle camera_ui action', async () => { await action.execute(api); - expect(api.getCameraURLManager().openURL).toBeCalled(); + expect(api.getCameraURLManager().openURL).toHaveBeenCalled(); }); diff --git a/tests/card-controller/actions/actions/custom.test.ts b/tests/card-controller/actions/actions/custom.test.ts index 81f7981e..4b34237a 100644 --- a/tests/card-controller/actions/actions/custom.test.ts +++ b/tests/card-controller/actions/actions/custom.test.ts @@ -29,7 +29,7 @@ describe('CustomAction', () => { await action.execute(api); - expect(handler).toBeCalledWith( + expect(handler).toHaveBeenCalledWith( expect.objectContaining({ detail: { action: 'fire-dom-event', foo: 'bar', 1: 2 }, }), diff --git a/tests/card-controller/actions/actions/default.test.ts b/tests/card-controller/actions/actions/default.test.ts index c6cabc30..652133d7 100644 --- a/tests/card-controller/actions/actions/default.test.ts +++ b/tests/card-controller/actions/actions/default.test.ts @@ -15,5 +15,5 @@ it('should handle default action', async () => { await action.execute(api); - expect(api.getViewManager().setViewDefaultWithNewQuery).toBeCalled(); + expect(api.getViewManager().setViewDefaultWithNewQuery).toHaveBeenCalled(); }); diff --git a/tests/card-controller/actions/actions/display-mode-select.test.ts b/tests/card-controller/actions/actions/display-mode-select.test.ts index 61db376c..5f1968fe 100644 --- a/tests/card-controller/actions/actions/display-mode-select.test.ts +++ b/tests/card-controller/actions/actions/display-mode-select.test.ts @@ -16,7 +16,7 @@ it('should handle default action', async () => { await action.execute(api); - expect(api.getViewManager().setViewByParametersWithNewQuery).toBeCalledWith({ + expect(api.getViewManager().setViewByParametersWithNewQuery).toHaveBeenCalledWith({ params: { displayMode: 'grid', }, diff --git a/tests/card-controller/actions/actions/download.test.ts b/tests/card-controller/actions/actions/download.test.ts index 3f09a746..068d664e 100644 --- a/tests/card-controller/actions/actions/download.test.ts +++ b/tests/card-controller/actions/actions/download.test.ts @@ -2,7 +2,8 @@ import { expect, it, vi } from 'vitest'; import { DownloadAction } from '../../../../src/card-controller/actions/actions/download'; import { QueryResults } from '../../../../src/view/query-results'; -import { createCardAPI, createView, TestViewMedia } from '../../../test-utils'; +import { createCardAPI } from '../../../test-utils'; +import { createView, TestViewMedia } from '../../../view/test-utils'; it('should handle download action with selected media', async () => { const api = createCardAPI(); @@ -23,7 +24,7 @@ it('should handle download action with selected media', async () => { await action.execute(api); - expect(api.getViewItemManager().download).toBeCalledWith(selectedMedia); + expect(api.getViewItemManager().download).toHaveBeenCalledWith(selectedMedia); }); it('should handle download action without selected media', async () => { @@ -40,5 +41,5 @@ it('should handle download action without selected media', async () => { await action.execute(api); - expect(api.getViewItemManager().download).not.toBeCalled(); + expect(api.getViewItemManager().download).not.toHaveBeenCalled(); }); diff --git a/tests/card-controller/actions/actions/expand.test.ts b/tests/card-controller/actions/actions/expand.test.ts index a4a5960a..47520344 100644 --- a/tests/card-controller/actions/actions/expand.test.ts +++ b/tests/card-controller/actions/actions/expand.test.ts @@ -15,5 +15,5 @@ it('should handle expand action', async () => { await action.execute(api); - expect(api.getExpandManager().toggleExpanded).toBeCalled(); + expect(api.getExpandManager().toggleExpanded).toHaveBeenCalled(); }); diff --git a/tests/card-controller/actions/actions/fullscreen.test.ts b/tests/card-controller/actions/actions/fullscreen.test.ts index 549c216a..1b3e8db5 100644 --- a/tests/card-controller/actions/actions/fullscreen.test.ts +++ b/tests/card-controller/actions/actions/fullscreen.test.ts @@ -15,5 +15,5 @@ it('should handle fullscreen action', async () => { await action.execute(api); - expect(api.getFullscreenManager().toggleFullscreen).toBeCalled(); + expect(api.getFullscreenManager().toggleFullscreen).toHaveBeenCalled(); }); diff --git a/tests/card-controller/actions/actions/generated-action.test.ts b/tests/card-controller/actions/actions/generated-action.test.ts index 5e7bca02..eb2f5efa 100644 --- a/tests/card-controller/actions/actions/generated-action.test.ts +++ b/tests/card-controller/actions/actions/generated-action.test.ts @@ -17,7 +17,7 @@ describe('GeneratedAction', () => { await action.execute(api); - expect(api.getActionsManager().executeNestedActions).toBeCalledWith({ + expect(api.getActionsManager().executeNestedActions).toHaveBeenCalledWith({ actions: generated, config: undefined, triggerData: undefined, @@ -37,7 +37,7 @@ describe('GeneratedAction', () => { await action.execute(api); - expect(api.getActionsManager().executeNestedActions).toBeCalledWith({ + expect(api.getActionsManager().executeNestedActions).toHaveBeenCalledWith({ actions: generated, config: undefined, triggerData: undefined, @@ -53,7 +53,7 @@ describe('GeneratedAction', () => { await action.execute(api); - expect(api.getActionsManager().executeNestedActions).not.toBeCalled(); + expect(api.getActionsManager().executeNestedActions).not.toHaveBeenCalled(); }); it('should pass the api and trigger data to the generator', async () => { @@ -72,6 +72,6 @@ describe('GeneratedAction', () => { await action.execute(api); - expect(generator).toBeCalledWith({ api, triggerData }); + expect(generator).toHaveBeenCalledWith({ api, triggerData }); }); }); diff --git a/tests/card-controller/actions/actions/if.test.ts b/tests/card-controller/actions/actions/if.test.ts index 06a89034..06e88cc7 100644 --- a/tests/card-controller/actions/actions/if.test.ts +++ b/tests/card-controller/actions/actions/if.test.ts @@ -35,7 +35,7 @@ describe('IfAction', () => { await action.execute(api); - expect(api.getActionsManager().executeNestedActions).toBeCalledWith({ + expect(api.getActionsManager().executeNestedActions).toHaveBeenCalledWith({ actions: thenActions, config: undefined, triggerData: undefined, @@ -59,7 +59,7 @@ describe('IfAction', () => { await action.execute(api); - expect(api.getActionsManager().executeNestedActions).toBeCalledWith({ + expect(api.getActionsManager().executeNestedActions).toHaveBeenCalledWith({ actions: elseActions, config: undefined, triggerData: undefined, @@ -82,6 +82,6 @@ describe('IfAction', () => { await action.execute(api); - expect(api.getActionsManager().executeNestedActions).not.toBeCalled(); + expect(api.getActionsManager().executeNestedActions).not.toHaveBeenCalled(); }); }); diff --git a/tests/card-controller/actions/actions/info.test.ts b/tests/card-controller/actions/actions/info.test.ts index 4943db4a..47b59a7d 100644 --- a/tests/card-controller/actions/actions/info.test.ts +++ b/tests/card-controller/actions/actions/info.test.ts @@ -3,7 +3,8 @@ import { describe, expect, it, vi } from 'vitest'; import { InfoAction } from '../../../../src/card-controller/actions/actions/info'; import { QueryResults } from '../../../../src/view/query-results'; import { View } from '../../../../src/view/view'; -import { createCardAPI, TestViewMedia } from '../../../test-utils'; +import { createCardAPI } from '../../../test-utils'; +import { TestViewMedia } from '../../../view/test-utils'; describe('InfoAction', () => { it('should handle info action with media', async () => { @@ -29,7 +30,7 @@ describe('InfoAction', () => { await action.execute(api); - expect(api.getNotificationManager().setNotification).toBeCalled(); + expect(api.getNotificationManager().setNotification).toHaveBeenCalled(); }); it('should not handle info action without media', async () => { @@ -46,6 +47,6 @@ describe('InfoAction', () => { await action.execute(api); - expect(api.getNotificationManager().setNotification).not.toBeCalled(); + expect(api.getNotificationManager().setNotification).not.toHaveBeenCalled(); }); }); diff --git a/tests/card-controller/actions/actions/internal-callback.test.ts b/tests/card-controller/actions/actions/internal-callback.test.ts index a01238f5..f1d4f01b 100644 --- a/tests/card-controller/actions/actions/internal-callback.test.ts +++ b/tests/card-controller/actions/actions/internal-callback.test.ts @@ -18,5 +18,5 @@ it('should handle internal callback action', async () => { await action.execute(api); - expect(callback).toBeCalledWith(api); + expect(callback).toHaveBeenCalledWith(api); }); diff --git a/tests/card-controller/actions/actions/log.test.ts b/tests/card-controller/actions/actions/log.test.ts index 6bec09a1..d2b245b0 100644 --- a/tests/card-controller/actions/actions/log.test.ts +++ b/tests/card-controller/actions/actions/log.test.ts @@ -21,5 +21,5 @@ it('should handle log action', async () => { const spy = vi.spyOn(global.console, 'warn').mockImplementation(() => true); await action.execute(api); - expect(spy).toBeCalledWith('Hello, world!'); + expect(spy).toHaveBeenCalledWith('Hello, world!'); }); diff --git a/tests/card-controller/actions/actions/media-player.test.ts b/tests/card-controller/actions/actions/media-player.test.ts index e2a5e4fa..732d97ff 100644 --- a/tests/card-controller/actions/actions/media-player.test.ts +++ b/tests/card-controller/actions/actions/media-player.test.ts @@ -1,7 +1,8 @@ import { afterEach, describe, expect, it, vi } from 'vitest'; import { MediaPlayerAction } from '../../../../src/card-controller/actions/actions/media-player'; -import { createCardAPI, createView, createViewWithMedia } from '../../../test-utils'; +import { createCardAPI } from '../../../test-utils'; +import { createView, createViewWithMedia } from '../../../view/test-utils'; afterEach(() => { vi.resetAllMocks(); @@ -23,7 +24,9 @@ describe('should handle media_player action', () => { await action.execute(api); - expect(api.getMediaPlayerManager().stop).toBeCalledWith('this_is_a_media_player'); + expect(api.getMediaPlayerManager().stop).toHaveBeenCalledWith( + 'this_is_a_media_player', + ); }); it('to play live', async () => { @@ -47,7 +50,7 @@ describe('should handle media_player action', () => { await action.execute(api); - expect(api.getMediaPlayerManager().playLive).toBeCalledWith( + expect(api.getMediaPlayerManager().playLive).toHaveBeenCalledWith( 'this_is_a_media_player', 'camera', ); @@ -74,7 +77,7 @@ describe('should handle media_player action', () => { await action.execute(api); - expect(api.getMediaPlayerManager().playMedia).toBeCalledWith( + expect(api.getMediaPlayerManager().playMedia).toHaveBeenCalledWith( 'this_is_a_media_player', view.queryResults?.getSelectedResult(), ); @@ -100,7 +103,7 @@ describe('should handle media_player action', () => { await action.execute(api); - expect(api.getMediaPlayerManager().playMedia).not.toBeCalled(); + expect(api.getMediaPlayerManager().playMedia).not.toHaveBeenCalled(); }); it('to not play live without camera', async () => { @@ -124,6 +127,6 @@ describe('should handle media_player action', () => { await action.execute(api); - expect(api.getMediaPlayerManager().playLive).not.toBeCalled(); + expect(api.getMediaPlayerManager().playLive).not.toHaveBeenCalled(); }); }); diff --git a/tests/card-controller/actions/actions/menu-toggle.test.ts b/tests/card-controller/actions/actions/menu-toggle.test.ts index 9204e5cf..3b93277d 100644 --- a/tests/card-controller/actions/actions/menu-toggle.test.ts +++ b/tests/card-controller/actions/actions/menu-toggle.test.ts @@ -15,5 +15,5 @@ it('should handle menu toggle action', async () => { await action.execute(api); - expect(api.getCardElementManager().toggleMenu).toBeCalled(); + expect(api.getCardElementManager().toggleMenu).toHaveBeenCalled(); }); diff --git a/tests/card-controller/actions/actions/microphone-connect.test.ts b/tests/card-controller/actions/actions/microphone-connect.test.ts index a79465bb..c0f8e1e2 100644 --- a/tests/card-controller/actions/actions/microphone-connect.test.ts +++ b/tests/card-controller/actions/actions/microphone-connect.test.ts @@ -15,5 +15,5 @@ it('should handle microphone_connect action', async () => { await action.execute(api); - expect(api.getMicrophoneManager().connect).toBeCalled(); + expect(api.getMicrophoneManager().connect).toHaveBeenCalled(); }); diff --git a/tests/card-controller/actions/actions/microphone-disconnect.test.ts b/tests/card-controller/actions/actions/microphone-disconnect.test.ts index 15313961..46012598 100644 --- a/tests/card-controller/actions/actions/microphone-disconnect.test.ts +++ b/tests/card-controller/actions/actions/microphone-disconnect.test.ts @@ -15,5 +15,5 @@ it('should handle microphone_disconnect action', async () => { await action.execute(api); - expect(api.getMicrophoneManager().disconnect).toBeCalled(); + expect(api.getMicrophoneManager().disconnect).toHaveBeenCalled(); }); diff --git a/tests/card-controller/actions/actions/microphone-mute.test.ts b/tests/card-controller/actions/actions/microphone-mute.test.ts index f1afed2a..75308f78 100644 --- a/tests/card-controller/actions/actions/microphone-mute.test.ts +++ b/tests/card-controller/actions/actions/microphone-mute.test.ts @@ -15,5 +15,5 @@ it('should handle microphone_mute action', async () => { await action.execute(api); - expect(api.getMicrophoneManager().mute).toBeCalled(); + expect(api.getMicrophoneManager().mute).toHaveBeenCalled(); }); diff --git a/tests/card-controller/actions/actions/microphone-unmute.test.ts b/tests/card-controller/actions/actions/microphone-unmute.test.ts index 7afeb4c0..73bb42bd 100644 --- a/tests/card-controller/actions/actions/microphone-unmute.test.ts +++ b/tests/card-controller/actions/actions/microphone-unmute.test.ts @@ -15,5 +15,5 @@ it('should handle microphone_unmute action', async () => { await action.execute(api); - expect(api.getMicrophoneManager().unmute).toBeCalled(); + expect(api.getMicrophoneManager().unmute).toHaveBeenCalled(); }); diff --git a/tests/card-controller/actions/actions/more-info.test.ts b/tests/card-controller/actions/actions/more-info.test.ts index de9ff02a..102583c2 100644 --- a/tests/card-controller/actions/actions/more-info.test.ts +++ b/tests/card-controller/actions/actions/more-info.test.ts @@ -24,7 +24,7 @@ describe('should handle more-info action', () => { await action.execute(api); - expect(handler).toBeCalledWith( + expect(handler).toHaveBeenCalledWith( expect.objectContaining({ detail: { entityId: 'light.office' }, }), @@ -51,7 +51,7 @@ describe('should handle more-info action', () => { await action.execute(api); - expect(handler).toBeCalledWith( + expect(handler).toHaveBeenCalledWith( expect.objectContaining({ detail: { entityId: 'light.office' }, }), @@ -76,6 +76,6 @@ describe('should handle more-info action', () => { await action.execute(api); - expect(handler).not.toBeCalled(); + expect(handler).not.toHaveBeenCalled(); }); }); diff --git a/tests/card-controller/actions/actions/mute.test.ts b/tests/card-controller/actions/actions/mute.test.ts index fe022565..130baeb4 100644 --- a/tests/card-controller/actions/actions/mute.test.ts +++ b/tests/card-controller/actions/actions/mute.test.ts @@ -23,5 +23,5 @@ it('should handle mute action', async () => { await action.execute(api); - expect(mediaPlayerController.mute).toBeCalled(); + expect(mediaPlayerController.mute).toHaveBeenCalled(); }); diff --git a/tests/card-controller/actions/actions/navigate.test.ts b/tests/card-controller/actions/actions/navigate.test.ts index fa2a4262..9880fbb5 100644 --- a/tests/card-controller/actions/actions/navigate.test.ts +++ b/tests/card-controller/actions/actions/navigate.test.ts @@ -23,7 +23,7 @@ describe('should handle navigate action', () => { await action.execute(createCardAPI()); expect(history.length).toBe(historyLength + 1); - expect(handler).toBeCalledWith( + expect(handler).toHaveBeenCalledWith( expect.objectContaining({ detail: { replace: false }, }), @@ -49,7 +49,7 @@ describe('should handle navigate action', () => { await action.execute(createCardAPI()); expect(history.length).toBe(historyLength); - expect(handler).toBeCalledWith( + expect(handler).toHaveBeenCalledWith( expect.objectContaining({ detail: { replace: true }, }), diff --git a/tests/card-controller/actions/actions/pause.test.ts b/tests/card-controller/actions/actions/pause.test.ts index 67b8ab69..386e647b 100644 --- a/tests/card-controller/actions/actions/pause.test.ts +++ b/tests/card-controller/actions/actions/pause.test.ts @@ -25,5 +25,5 @@ it('should handle pause action', async () => { await action.execute(api); - expect(mediaPlayerController.playback?.pause).toBeCalled(); + expect(mediaPlayerController.playback?.pause).toHaveBeenCalled(); }); diff --git a/tests/card-controller/actions/actions/perform-action.test.ts b/tests/card-controller/actions/actions/perform-action.test.ts index fb5bda3a..714868df 100644 --- a/tests/card-controller/actions/actions/perform-action.test.ts +++ b/tests/card-controller/actions/actions/perform-action.test.ts @@ -20,7 +20,7 @@ describe('PerformActionAction', () => { ); await action.execute(api); - expect(hass.callService).toBeCalledWith( + expect(hass.callService).toHaveBeenCalledWith( 'light', 'turn_on', { diff --git a/tests/card-controller/actions/actions/pip.test.ts b/tests/card-controller/actions/actions/pip.test.ts index 15df0e86..d365ed3e 100644 --- a/tests/card-controller/actions/actions/pip.test.ts +++ b/tests/card-controller/actions/actions/pip.test.ts @@ -15,5 +15,5 @@ it('should toggle PIP', async () => { await action.execute(api); - expect(api.getPIPManager().togglePIP).toBeCalled(); + expect(api.getPIPManager().togglePIP).toHaveBeenCalled(); }); diff --git a/tests/card-controller/actions/actions/play.test.ts b/tests/card-controller/actions/actions/play.test.ts index c15a6b76..95216e04 100644 --- a/tests/card-controller/actions/actions/play.test.ts +++ b/tests/card-controller/actions/actions/play.test.ts @@ -25,5 +25,5 @@ it('should handle play action', async () => { await action.execute(api); - expect(mediaPlayerController.playback?.play).toBeCalled(); + expect(mediaPlayerController.playback?.play).toHaveBeenCalled(); }); diff --git a/tests/card-controller/actions/actions/ptz-controls.test.ts b/tests/card-controller/actions/actions/ptz-controls.test.ts index 915aa7be..c3432487 100644 --- a/tests/card-controller/actions/actions/ptz-controls.test.ts +++ b/tests/card-controller/actions/actions/ptz-controls.test.ts @@ -20,7 +20,7 @@ describe('PTZControlsAction', () => { await action.execute(api); - expect(api.getViewManager().setViewWithMergedContext).toBeCalledWith({ + expect(api.getViewManager().setViewWithMergedContext).toHaveBeenCalledWith({ ptzControls: { enabled: true, type: 'buttons' }, }); }); @@ -41,7 +41,7 @@ describe('PTZControlsAction', () => { await action.execute(api); - expect(api.getViewManager().setViewWithMergedContext).toBeCalledWith({ + expect(api.getViewManager().setViewWithMergedContext).toHaveBeenCalledWith({ ptzControls: { enabled: false }, }); }); @@ -59,7 +59,7 @@ describe('PTZControlsAction', () => { await action.execute(api); - expect(api.getViewManager().setViewWithMergedContext).toBeCalledWith({ + expect(api.getViewManager().setViewWithMergedContext).toHaveBeenCalledWith({ ptzControls: {}, }); }); @@ -78,7 +78,7 @@ describe('PTZControlsAction', () => { await action.execute(api); - expect(api.getViewManager().setViewWithMergedContext).toBeCalledWith({ + expect(api.getViewManager().setViewWithMergedContext).toHaveBeenCalledWith({ ptzControls: { type: 'gestures' }, }); }); @@ -100,7 +100,7 @@ describe('PTZControlsAction', () => { await action.execute(api); - expect(api.getViewManager().setViewWithMergedContext).toBeCalledWith({ + expect(api.getViewManager().setViewWithMergedContext).toHaveBeenCalledWith({ ptzControls: { type: 'buttons' }, }); }); diff --git a/tests/card-controller/actions/actions/ptz-digital.test.ts b/tests/card-controller/actions/actions/ptz-digital.test.ts index 2b8e3d3e..edc23bd1 100644 --- a/tests/card-controller/actions/actions/ptz-digital.test.ts +++ b/tests/card-controller/actions/actions/ptz-digital.test.ts @@ -6,7 +6,8 @@ import type { ZoomSettingsObserved, } from '../../../../src/components-lib/zoom/types'; import type { PTZAction } from '../../../../src/config/schema/actions/custom/ptz'; -import { createCardAPI, createView } from '../../../test-utils'; +import { createCardAPI } from '../../../test-utils'; +import { createView } from '../../../view/test-utils'; describe('should handle ptz digital action', () => { const defaultSettings = { @@ -47,7 +48,7 @@ describe('should handle ptz digital action', () => { await action.execute(api); - expect(api.getViewManager().setViewWithMergedContext).toBeCalledWith({ + expect(api.getViewManager().setViewWithMergedContext).toHaveBeenCalledWith({ zoom: { camera: { observed: undefined, @@ -77,7 +78,7 @@ describe('should handle ptz digital action', () => { await action.execute(api); - expect(api.getViewManager().setViewWithMergedContext).toBeCalledWith({ + expect(api.getViewManager().setViewWithMergedContext).toHaveBeenCalledWith({ zoom: { camera: { observed: undefined, @@ -101,7 +102,7 @@ describe('should handle ptz digital action', () => { await action.execute(api); - expect(api.getViewManager().setViewWithMergedContext).not.toBeCalledWith(); + expect(api.getViewManager().setViewWithMergedContext).not.toHaveBeenCalledWith(); }); it('should do nothing without a camera', async () => { @@ -125,7 +126,7 @@ describe('should handle ptz digital action', () => { await action.execute(api); - expect(api.getViewManager().setViewWithMergedContext).not.toBeCalled(); + expect(api.getViewManager().setViewWithMergedContext).not.toHaveBeenCalled(); }); describe('should honor ptz_action', () => { @@ -338,7 +339,7 @@ describe('should handle ptz digital action', () => { await action.execute(api); - expect(api.getViewManager().setViewWithMergedContext).toBeCalledWith({ + expect(api.getViewManager().setViewWithMergedContext).toHaveBeenCalledWith({ zoom: { camera: { observed: undefined, @@ -428,12 +429,12 @@ describe('should handle ptz digital action', () => { }, }, }); - expect(api.getViewManager().setViewWithMergedContext).toBeCalledTimes(2); + expect(api.getViewManager().setViewWithMergedContext).toHaveBeenCalledTimes(2); action.stop(); vi.runOnlyPendingTimers(); - expect(api.getViewManager().setViewWithMergedContext).toBeCalledTimes(2); + expect(api.getViewManager().setViewWithMergedContext).toHaveBeenCalledTimes(2); }); it('stop', async () => { @@ -463,7 +464,7 @@ describe('should handle ptz digital action', () => { }, }, }); - expect(api.getViewManager().setViewWithMergedContext).toBeCalledTimes(1); + expect(api.getViewManager().setViewWithMergedContext).toHaveBeenCalledTimes(1); const stopAction = new PTZDigitalAction(context, { action: 'fire-dom-event', @@ -474,7 +475,7 @@ describe('should handle ptz digital action', () => { vi.runOnlyPendingTimers(); - expect(api.getViewManager().setViewWithMergedContext).toBeCalledTimes(1); + expect(api.getViewManager().setViewWithMergedContext).toHaveBeenCalledTimes(1); }); }); }); diff --git a/tests/card-controller/actions/actions/ptz-multi.test.ts b/tests/card-controller/actions/actions/ptz-multi.test.ts index fdbb5abb..bd1e5b78 100644 --- a/tests/card-controller/actions/actions/ptz-multi.test.ts +++ b/tests/card-controller/actions/actions/ptz-multi.test.ts @@ -3,12 +3,9 @@ import { describe, expect, it, vi } from 'vitest'; import { Capabilities } from '../../../../src/camera-manager/capabilities'; import { PTZMultiAction } from '../../../../src/card-controller/actions/actions/ptz-multi'; import { PTZMovementType } from '../../../../src/types'; -import { - createCameraManager, - createCardAPI, - createStore, - createView, -} from '../../../test-utils'; +import { createCameraManager, createStore } from '../../../camera-manager/test-utils'; +import { createCardAPI } from '../../../test-utils'; +import { createView } from '../../../view/test-utils'; describe('should handle ptz multi action', () => { describe.each([ @@ -42,7 +39,7 @@ describe('should handle ptz multi action', () => { await action.execute(api); - expect(api.getCameraManager().executePTZAction).toBeCalledWith( + expect(api.getCameraManager().executePTZAction).toHaveBeenCalledWith( 'camera.office', 'left', { @@ -50,7 +47,7 @@ describe('should handle ptz multi action', () => { preset: undefined, }, ); - expect(api.getViewManager().setViewWithMergedContext).not.toBeCalled(); + expect(api.getViewManager().setViewWithMergedContext).not.toHaveBeenCalled(); }); it('should use digital ptz when camera does not have ptz support', async () => { @@ -79,7 +76,7 @@ describe('should handle ptz multi action', () => { await action.execute(api); - expect(api.getCameraManager().executePTZAction).not.toBeCalled(); + expect(api.getCameraManager().executePTZAction).not.toHaveBeenCalled(); expect(api.getViewManager().setViewWithMergedContext).toHaveBeenLastCalledWith({ zoom: { 'camera.office': { @@ -117,8 +114,8 @@ describe('should handle ptz multi action', () => { await action.execute(api); - expect(api.getCameraManager().executePTZAction).not.toBeCalled(); - expect(api.getViewManager().setViewWithMergedContext).not.toBeCalled(); + expect(api.getCameraManager().executePTZAction).not.toHaveBeenCalled(); + expect(api.getViewManager().setViewWithMergedContext).not.toHaveBeenCalled(); }); it('should do nothing with a media-less view without an explicit target_id', async () => { @@ -146,7 +143,7 @@ describe('should handle ptz multi action', () => { await action.execute(api); - expect(api.getCameraManager().executePTZAction).not.toBeCalled(); - expect(api.getViewManager().setViewWithMergedContext).not.toBeCalled(); + expect(api.getCameraManager().executePTZAction).not.toHaveBeenCalled(); + expect(api.getViewManager().setViewWithMergedContext).not.toHaveBeenCalled(); }); }); diff --git a/tests/card-controller/actions/actions/ptz.test.ts b/tests/card-controller/actions/actions/ptz.test.ts index 2da616e9..a54dee05 100644 --- a/tests/card-controller/actions/actions/ptz.test.ts +++ b/tests/card-controller/actions/actions/ptz.test.ts @@ -3,13 +3,10 @@ import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest'; import { Capabilities } from '../../../../src/camera-manager/capabilities'; import { PTZAction } from '../../../../src/card-controller/actions/actions/ptz'; import { PTZMovementType } from '../../../../src/types'; -import { - createCameraConfig, - createCameraManager, - createCardAPI, - createStore, - createView, -} from '../../../test-utils'; +import { createCameraManager, createStore } from '../../../camera-manager/test-utils'; +import { createCameraConfig } from '../../../config/test-utils'; +import { createCardAPI } from '../../../test-utils'; +import { createView } from '../../../view/test-utils'; describe('should handle ptz action', () => { it('should execute simple action', async () => { @@ -39,7 +36,7 @@ describe('should handle ptz action', () => { await action.execute(api); - expect(api.getCameraManager().executePTZAction).toBeCalledWith( + expect(api.getCameraManager().executePTZAction).toHaveBeenCalledWith( 'camera.office', 'left', { @@ -74,7 +71,7 @@ describe('should handle ptz action', () => { await action.execute(api); - expect(api.getCameraManager().executePTZAction).toBeCalledWith( + expect(api.getCameraManager().executePTZAction).toHaveBeenCalledWith( 'camera.office', 'left', { @@ -121,7 +118,7 @@ describe('should handle ptz action', () => { await action.execute(api); - expect(api.getCameraManager().executePTZAction).toBeCalledWith( + expect(api.getCameraManager().executePTZAction).toHaveBeenCalledWith( 'camera.office_hd', 'left', { @@ -149,7 +146,7 @@ describe('should handle ptz action', () => { await action.execute(api); - expect(api.getCameraManager().executePTZAction).not.toBeCalled(); + expect(api.getCameraManager().executePTZAction).not.toHaveBeenCalled(); }); }); @@ -168,7 +165,7 @@ describe('should handle ptz action', () => { await action.execute(api); - expect(api.getCameraManager().executePTZAction).not.toBeCalled(); + expect(api.getCameraManager().executePTZAction).not.toHaveBeenCalled(); }); describe('when there is no action', () => { @@ -195,7 +192,7 @@ describe('should handle ptz action', () => { await action.execute(api); - expect(api.getCameraManager().executePTZAction).toBeCalledWith( + expect(api.getCameraManager().executePTZAction).toHaveBeenCalledWith( 'camera.office', 'preset', { @@ -244,7 +241,7 @@ describe('should handle ptz action', () => { await action.execute(api); - expect(api.getCameraManager().executePTZAction).toBeCalledWith( + expect(api.getCameraManager().executePTZAction).toHaveBeenCalledWith( 'camera.office', 'preset', { @@ -290,7 +287,7 @@ describe('should handle ptz action', () => { await action.execute(api); - expect(api.getCameraManager().executePTZAction).toBeCalledWith( + expect(api.getCameraManager().executePTZAction).toHaveBeenCalledWith( 'camera.office', 'preset', { @@ -328,7 +325,7 @@ describe('should handle ptz action', () => { await action.execute(api); - expect(api.getCameraManager().executePTZAction).not.toBeCalled(); + expect(api.getCameraManager().executePTZAction).not.toHaveBeenCalled(); }); }); @@ -361,7 +358,7 @@ describe('should handle ptz action', () => { await action.execute(api); - expect(api.getCameraManager().executePTZAction).toBeCalledWith( + expect(api.getCameraManager().executePTZAction).toHaveBeenCalledWith( 'camera.office', 'preset', { @@ -400,7 +397,7 @@ describe('should handle ptz action', () => { await action.execute(api); - expect(api.getCameraManager().executePTZAction).toBeCalledWith( + expect(api.getCameraManager().executePTZAction).toHaveBeenCalledWith( 'camera.office', 'left', { @@ -447,7 +444,7 @@ describe('should handle ptz action', () => { await action.execute(api); - expect(api.getCameraManager().executePTZAction).toBeCalledWith( + expect(api.getCameraManager().executePTZAction).toHaveBeenCalledWith( 'camera.office', 'left', { @@ -456,7 +453,7 @@ describe('should handle ptz action', () => { ); vi.runOnlyPendingTimers(); - expect(api.getCameraManager().executePTZAction).toBeCalledWith( + expect(api.getCameraManager().executePTZAction).toHaveBeenCalledWith( 'camera.office', 'left', { @@ -491,12 +488,12 @@ describe('should handle ptz action', () => { await action.execute(api); - expect(api.getCameraManager().executePTZAction).toBeCalledTimes(1); + expect(api.getCameraManager().executePTZAction).toHaveBeenCalledTimes(1); action.stop(); vi.runOnlyPendingTimers(); - expect(api.getCameraManager().executePTZAction).toBeCalledTimes(1); + expect(api.getCameraManager().executePTZAction).toHaveBeenCalledTimes(1); }); }); @@ -535,20 +532,20 @@ describe('should handle ptz action', () => { }); await startAction.execute(api); - expect(api.getCameraManager().executePTZAction).toBeCalledWith( + expect(api.getCameraManager().executePTZAction).toHaveBeenCalledWith( 'camera.office', 'left', { phase: undefined, }, ); - expect(api.getCameraManager().executePTZAction).toBeCalledTimes(1); + expect(api.getCameraManager().executePTZAction).toHaveBeenCalledTimes(1); await vi.runOnlyPendingTimersAsync(); - expect(api.getCameraManager().executePTZAction).toBeCalledTimes(2); + expect(api.getCameraManager().executePTZAction).toHaveBeenCalledTimes(2); await vi.runOnlyPendingTimersAsync(); - expect(api.getCameraManager().executePTZAction).toBeCalledTimes(3); + expect(api.getCameraManager().executePTZAction).toHaveBeenCalledTimes(3); const stopAction = new PTZAction(context, { action: 'fire-dom-event', @@ -560,7 +557,7 @@ describe('should handle ptz action', () => { // There should be no additional calls. await vi.runOnlyPendingTimersAsync(); - expect(api.getCameraManager().executePTZAction).toBeCalledTimes(3); + expect(api.getCameraManager().executePTZAction).toHaveBeenCalledTimes(3); }); it('should honor stop', async () => { @@ -590,10 +587,10 @@ describe('should handle ptz action', () => { }); await action.execute(api); - expect(api.getCameraManager().executePTZAction).toBeCalledTimes(1); + expect(api.getCameraManager().executePTZAction).toHaveBeenCalledTimes(1); await vi.runOnlyPendingTimersAsync(); - expect(api.getCameraManager().executePTZAction).toBeCalledTimes(2); + expect(api.getCameraManager().executePTZAction).toHaveBeenCalledTimes(2); // Emulate the stop being called while the action is running, but before // the *next* timer is scheduled. @@ -604,7 +601,7 @@ describe('should handle ptz action', () => { vi.mocked(cameraManager.executePTZAction).mockReturnValueOnce(promise); await vi.runOnlyPendingTimersAsync(); - expect(api.getCameraManager().executePTZAction).toBeCalledTimes(3); + expect(api.getCameraManager().executePTZAction).toHaveBeenCalledTimes(3); action.stop(); @@ -612,7 +609,7 @@ describe('should handle ptz action', () => { await vi.runOnlyPendingTimersAsync(); // There should be no additional calls. - expect(api.getCameraManager().executePTZAction).toBeCalledTimes(3); + expect(api.getCameraManager().executePTZAction).toHaveBeenCalledTimes(3); }); }); }); diff --git a/tests/card-controller/actions/actions/reload.test.ts b/tests/card-controller/actions/actions/reload.test.ts index 9749528a..8c2a0b91 100644 --- a/tests/card-controller/actions/actions/reload.test.ts +++ b/tests/card-controller/actions/actions/reload.test.ts @@ -25,6 +25,6 @@ describe('should handle reload action', async () => { await action.execute(api); - expect(location.reload).toBeCalledTimes(1); + expect(location.reload).toHaveBeenCalledTimes(1); }); }); diff --git a/tests/card-controller/actions/actions/screenshot.test.ts b/tests/card-controller/actions/actions/screenshot.test.ts index be3d84f2..2f0446f0 100644 --- a/tests/card-controller/actions/actions/screenshot.test.ts +++ b/tests/card-controller/actions/actions/screenshot.test.ts @@ -34,7 +34,7 @@ describe('should handle screenshot action', async () => { await action.execute(api); - expect(downloadURL).toBeCalledWith('screenshot-url', 'screenshot.jpg'); + expect(downloadURL).toHaveBeenCalledWith('screenshot-url', 'screenshot.jpg'); }); it('should handle screenshot action without screenshot URL', async () => { @@ -58,6 +58,6 @@ describe('should handle screenshot action', async () => { await action.execute(api); - expect(downloadURL).not.toBeCalled(); + expect(downloadURL).not.toHaveBeenCalled(); }); }); diff --git a/tests/card-controller/actions/actions/set-review.test.ts b/tests/card-controller/actions/actions/set-review.test.ts index ac7d3f88..f944f0d3 100644 --- a/tests/card-controller/actions/actions/set-review.test.ts +++ b/tests/card-controller/actions/actions/set-review.test.ts @@ -3,7 +3,8 @@ import { describe, expect, it, vi } from 'vitest'; import { SetReviewAction } from '../../../../src/card-controller/actions/actions/set-review'; import { ViewMediaType } from '../../../../src/view/item'; import { QueryResults } from '../../../../src/view/query-results'; -import { createCardAPI, createView, TestViewMedia } from '../../../test-utils'; +import { createCardAPI } from '../../../test-utils'; +import { createView, TestViewMedia } from '../../../view/test-utils'; describe('SetReviewAction', () => { it('should toggle item reviewed state', async () => { @@ -27,13 +28,13 @@ describe('SetReviewAction', () => { ); await action.execute(api); - expect(api.getViewItemManager().reviewMedia).toBeCalledWith(item, true); + expect(api.getViewItemManager().reviewMedia).toHaveBeenCalledWith(item, true); // toggleReviewed mutates the item in-place expect(item.isReviewed()).toBe(true); // Verify UI update is triggered to refresh menu icon - expect(api.getCardElementManager().update).toBeCalled(); + expect(api.getCardElementManager().update).toHaveBeenCalled(); }); it('should set reviewed to true when requested and currently false', async () => { @@ -58,7 +59,7 @@ describe('SetReviewAction', () => { ); await action.execute(api); - expect(api.getViewItemManager().reviewMedia).toBeCalledWith(item, true); + expect(api.getViewItemManager().reviewMedia).toHaveBeenCalledWith(item, true); expect(item.isReviewed()).toBe(true); }); @@ -84,7 +85,7 @@ describe('SetReviewAction', () => { ); await action.execute(api); - expect(api.getViewItemManager().reviewMedia).not.toBeCalled(); + expect(api.getViewItemManager().reviewMedia).not.toHaveBeenCalled(); }); it('should not act on non-review media', async () => { @@ -108,7 +109,7 @@ describe('SetReviewAction', () => { ); await action.execute(api); - expect(api.getViewItemManager().reviewMedia).not.toBeCalled(); + expect(api.getViewItemManager().reviewMedia).not.toHaveBeenCalled(); }); it('should not act without a view', async () => { @@ -125,7 +126,7 @@ describe('SetReviewAction', () => { ); await action.execute(api); - expect(api.getViewItemManager().reviewMedia).not.toBeCalled(); + expect(api.getViewItemManager().reviewMedia).not.toHaveBeenCalled(); }); it('should not act without query results', async () => { @@ -143,7 +144,7 @@ describe('SetReviewAction', () => { ); await action.execute(api); - expect(api.getViewItemManager().reviewMedia).not.toBeCalled(); + expect(api.getViewItemManager().reviewMedia).not.toHaveBeenCalled(); }); it('should not update UI if review action fails', async () => { @@ -170,7 +171,7 @@ describe('SetReviewAction', () => { ); await action.execute(api); - expect(api.getViewItemManager().reviewMedia).toBeCalledWith(item, true); - expect(api.getCardElementManager().update).not.toBeCalled(); + expect(api.getViewItemManager().reviewMedia).toHaveBeenCalledWith(item, true); + expect(api.getCardElementManager().update).not.toHaveBeenCalled(); }); }); diff --git a/tests/card-controller/actions/actions/set.test.ts b/tests/card-controller/actions/actions/set.test.ts index 91204923..c341c8f9 100644 --- a/tests/card-controller/actions/actions/set.test.ts +++ b/tests/card-controller/actions/actions/set.test.ts @@ -25,7 +25,7 @@ describe('ActionSet', () => { const consoleSpy = vi.spyOn(global.console, 'info').mockReturnValue(undefined); await set.execute(api); - expect(consoleSpy).toBeCalled(); + expect(consoleSpy).toHaveBeenCalled(); }); it('should not execute invalid action', async () => { @@ -39,7 +39,7 @@ describe('ActionSet', () => { const consoleSpy = vi.spyOn(global.console, 'info').mockReturnValue(undefined); await set.execute(api); - expect(consoleSpy).not.toBeCalled(); + expect(consoleSpy).not.toHaveBeenCalled(); }); it('should stop execution', async () => { @@ -50,6 +50,6 @@ describe('ActionSet', () => { const consoleSpy = vi.spyOn(global.console, 'info').mockReturnValue(undefined); await set.stop(); await set.execute(api); - expect(consoleSpy).not.toBeCalled(); + expect(consoleSpy).not.toHaveBeenCalled(); }); }); diff --git a/tests/card-controller/actions/actions/sleep.test.ts b/tests/card-controller/actions/actions/sleep.test.ts index fe40d544..a8b8598a 100644 --- a/tests/card-controller/actions/actions/sleep.test.ts +++ b/tests/card-controller/actions/actions/sleep.test.ts @@ -22,5 +22,5 @@ it('should handle sleep action', async () => { await action.execute(api); - expect(sleep).toBeCalledWith(5.2); + expect(sleep).toHaveBeenCalledWith(5.2); }); diff --git a/tests/card-controller/actions/actions/status-bar.test.ts b/tests/card-controller/actions/actions/status-bar.test.ts index fd0a2e41..9ccdb55e 100644 --- a/tests/card-controller/actions/actions/status-bar.test.ts +++ b/tests/card-controller/actions/actions/status-bar.test.ts @@ -17,7 +17,9 @@ describe('should handle status bar action', () => { await action.execute(api); - expect(api.getStatusBarItemManager().removeAllDynamicStatusBarItems).toBeCalled(); + expect( + api.getStatusBarItemManager().removeAllDynamicStatusBarItems, + ).toHaveBeenCalled(); }); it('add', async () => { @@ -39,7 +41,9 @@ describe('should handle status bar action', () => { await action.execute(api); - expect(api.getStatusBarItemManager().addDynamicStatusBarItem).toBeCalledWith(item); + expect(api.getStatusBarItemManager().addDynamicStatusBarItem).toHaveBeenCalledWith( + item, + ); }); it('remove', async () => { @@ -61,8 +65,8 @@ describe('should handle status bar action', () => { await action.execute(api); - expect(api.getStatusBarItemManager().removeDynamicStatusBarItem).toBeCalledWith( - item, - ); + expect( + api.getStatusBarItemManager().removeDynamicStatusBarItem, + ).toHaveBeenCalledWith(item); }); }); diff --git a/tests/card-controller/actions/actions/substream-off.test.ts b/tests/card-controller/actions/actions/substream-off.test.ts index c3d88e58..b06cd942 100644 --- a/tests/card-controller/actions/actions/substream-off.test.ts +++ b/tests/card-controller/actions/actions/substream-off.test.ts @@ -4,7 +4,8 @@ import { SubstreamOffAction } from '../../../../src/card-controller/actions/acti import { applyViewModifiers } from '../../../../src/card-controller/view/modifiers'; import { createSubstreamOffAction } from '../../../../src/utils/action'; import type { View } from '../../../../src/view/view'; -import { createCardAPI, createView } from '../../../test-utils'; +import { createCardAPI } from '../../../test-utils'; +import { createView } from '../../../view/test-utils'; // Runs the off-action for `view` and applies the modifier it produces. const applySubstreamOff = async ( diff --git a/tests/card-controller/actions/actions/substream-on.test.ts b/tests/card-controller/actions/actions/substream-on.test.ts index 56ab2c73..ff414492 100644 --- a/tests/card-controller/actions/actions/substream-on.test.ts +++ b/tests/card-controller/actions/actions/substream-on.test.ts @@ -7,13 +7,13 @@ import { createSubstreamOnAction } from '../../../../src/utils/action'; import { getStreamCameraID } from '../../../../src/view/substream'; import type { View } from '../../../../src/view/view'; import { - createCameraConfig, createCameraManager, createCapabilities, - createCardAPI, createStore, - createView, -} from '../../../test-utils'; +} from '../../../camera-manager/test-utils'; +import { createCameraConfig } from '../../../config/test-utils'; +import { createCardAPI } from '../../../test-utils'; +import { createView } from '../../../view/test-utils'; // A store where `camera.office` has one substream dependency, `camera.kitchen`. const createStoreWithSubstreams = (): CameraManagerStore => @@ -181,6 +181,6 @@ describe('SubstreamOnAction', () => { await new SubstreamOnAction({}, createSubstreamOnAction()).execute(api); - expect(api.getViewManager().setViewByParameters).not.toBeCalled(); + expect(api.getViewManager().setViewByParameters).not.toHaveBeenCalled(); }); }); diff --git a/tests/card-controller/actions/actions/toggle.test.ts b/tests/card-controller/actions/actions/toggle.test.ts index ca058ccc..fdb92c9d 100644 --- a/tests/card-controller/actions/actions/toggle.test.ts +++ b/tests/card-controller/actions/actions/toggle.test.ts @@ -46,9 +46,13 @@ describe('ToggleAction', () => { const action = new ToggleAction({}, { action: 'toggle' }, { entity: entityID }); await action.execute(api); - expect(hass.callService).toBeCalledWith(expectedServiceDomain, expectedService, { - entity_id: entityID, - }); + expect(hass.callService).toHaveBeenCalledWith( + expectedServiceDomain, + expectedService, + { + entity_id: entityID, + }, + ); }, ); }); @@ -62,7 +66,7 @@ describe('ToggleAction', () => { await action.execute(api); - expect(hass.callService).not.toBeCalled(); + expect(hass.callService).not.toHaveBeenCalled(); }); it('should do nothing without an entity state', async () => { @@ -77,6 +81,6 @@ describe('ToggleAction', () => { ); await action.execute(api); - expect(hass.callService).not.toBeCalled(); + expect(hass.callService).not.toHaveBeenCalled(); }); }); diff --git a/tests/card-controller/actions/actions/unmute.test.ts b/tests/card-controller/actions/actions/unmute.test.ts index 0ab6de01..d3a6c70e 100644 --- a/tests/card-controller/actions/actions/unmute.test.ts +++ b/tests/card-controller/actions/actions/unmute.test.ts @@ -23,5 +23,5 @@ it('should handle unmute action', async () => { await action.execute(api); - expect(mediaPlayerController.unmute).toBeCalled(); + expect(mediaPlayerController.unmute).toHaveBeenCalled(); }); diff --git a/tests/card-controller/actions/actions/view.test.ts b/tests/card-controller/actions/actions/view.test.ts index fbbc4fa4..1c3f27f2 100644 --- a/tests/card-controller/actions/actions/view.test.ts +++ b/tests/card-controller/actions/actions/view.test.ts @@ -28,7 +28,7 @@ describe('should handle view action', () => { await action.execute(api); - expect(api.getViewManager().setViewByParametersWithNewQuery).toBeCalledWith( + expect(api.getViewManager().setViewByParametersWithNewQuery).toHaveBeenCalledWith( expect.objectContaining({ params: { view: viewName, @@ -53,7 +53,7 @@ describe('should handle folder view action', () => { await action.execute(api); - expect(api.getViewManager().setViewByParametersWithNewQuery).toBeCalledWith( + expect(api.getViewManager().setViewByParametersWithNewQuery).toHaveBeenCalledWith( expect.objectContaining({ params: { view: viewName, diff --git a/tests/card-controller/automations-manager.test.ts b/tests/card-controller/automations-manager.test.ts index 402d653a..f6efad4f 100644 --- a/tests/card-controller/automations-manager.test.ts +++ b/tests/card-controller/automations-manager.test.ts @@ -40,7 +40,7 @@ describe('AutomationsManager', () => { stateManager.setState({ fullscreen: true }); - expect(api.getActionsManager().executeActions).not.toBeCalled(); + expect(api.getActionsManager().executeActions).not.toHaveBeenCalled(); }); it('should do nothing without being initialized', () => { @@ -57,7 +57,7 @@ describe('AutomationsManager', () => { stateManager.setState({ fullscreen: true }); - expect(api.getActionsManager().executeActions).not.toBeCalled(); + expect(api.getActionsManager().executeActions).not.toHaveBeenCalled(); }); it('should do nothing when an issue is present', () => { @@ -78,7 +78,7 @@ describe('AutomationsManager', () => { stateManager.setState({ fullscreen: true }); - expect(api.getActionsManager().executeActions).not.toBeCalled(); + expect(api.getActionsManager().executeActions).not.toHaveBeenCalled(); }); }); @@ -96,17 +96,17 @@ describe('AutomationsManager', () => { stateManager.setState({ fullscreen: true }); - expect(api.getActionsManager().executeActions).toBeCalledTimes(1); + expect(api.getActionsManager().executeActions).toHaveBeenCalledTimes(1); // It does not re-trigger while its source stays in the same state. stateManager.setState({ fullscreen: true }); - expect(api.getActionsManager().executeActions).toBeCalledTimes(1); + expect(api.getActionsManager().executeActions).toHaveBeenCalledTimes(1); stateManager.setState({ fullscreen: false }); - expect(api.getActionsManager().executeActions).toBeCalledTimes(1); + expect(api.getActionsManager().executeActions).toHaveBeenCalledTimes(1); stateManager.setState({ fullscreen: true }); - expect(api.getActionsManager().executeActions).toBeCalledTimes(2); + expect(api.getActionsManager().executeActions).toHaveBeenCalledTimes(2); }); it('should subscribe automations registered before initialization', () => { @@ -125,7 +125,7 @@ describe('AutomationsManager', () => { // Registered before initialization: the triggers are not yet subscribed, so // a matching change does nothing. stateManager.setState({ fullscreen: true }); - expect(api.getActionsManager().executeActions).not.toBeCalled(); + expect(api.getActionsManager().executeActions).not.toHaveBeenCalled(); // Initialization completes and subscribes the dormant automations. isInitialized.mockReturnValue(true); @@ -133,7 +133,7 @@ describe('AutomationsManager', () => { stateManager.setState({ fullscreen: false }); stateManager.setState({ fullscreen: true }); - expect(api.getActionsManager().executeActions).toBeCalledTimes(1); + expect(api.getActionsManager().executeActions).toHaveBeenCalledTimes(1); }); it('should run actions when the ongoing conditions hold', () => { @@ -158,7 +158,7 @@ describe('AutomationsManager', () => { stateManager.setState({ expand: true }); stateManager.setState({ fullscreen: true }); - expect(api.getActionsManager().executeActions).toBeCalledWith({ + expect(api.getActionsManager().executeActions).toHaveBeenCalledWith({ actions: actions, triggerData: { platform: 'acc', type: 'fullscreen' }, }); @@ -186,7 +186,7 @@ describe('AutomationsManager', () => { // nothing runs. stateManager.setState({ fullscreen: true }); - expect(api.getActionsManager().executeActions).not.toBeCalled(); + expect(api.getActionsManager().executeActions).not.toHaveBeenCalled(); }); it('should do nothing when the actions are empty', () => { @@ -208,7 +208,7 @@ describe('AutomationsManager', () => { stateManager.setState({ fullscreen: true }); - expect(api.getActionsManager().executeActions).not.toBeCalled(); + expect(api.getActionsManager().executeActions).not.toHaveBeenCalled(); }); it('should prevent automation loops', () => { @@ -245,7 +245,7 @@ describe('AutomationsManager', () => { stateManager.setState({ camera: camera }); - expect(api.getNotificationManager().setNotification).toBeCalledWith({ + expect(api.getNotificationManager().setNotification).toHaveBeenCalledWith({ heading: { text: 'Too many nested automation calls, please check your configuration for loops', icon: 'mdi:alert', @@ -253,7 +253,7 @@ describe('AutomationsManager', () => { }, }); - expect(api.getActionsManager().executeActions).toBeCalledTimes(10); + expect(api.getActionsManager().executeActions).toHaveBeenCalledTimes(10); }); it('should reset the nested-execution counter after an overflow', async () => { @@ -284,7 +284,7 @@ describe('AutomationsManager', () => { ); stateManager.setState({ camera: camera }); - expect(api.getActionsManager().executeActions).toBeCalledTimes(10); + expect(api.getActionsManager().executeActions).toHaveBeenCalledTimes(10); // The counter is decremented on the microtasks that resume after each // awaited execution, so let them drain before the next batch. @@ -296,7 +296,7 @@ describe('AutomationsManager', () => { // again -- only possible if the counter returned to zero. A leaked counter // (overflow returning without decrementing) would cut this batch short. stateManager.setState({ camera: 'three' }); - expect(api.getActionsManager().executeActions).toBeCalledTimes(10); + expect(api.getActionsManager().executeActions).toHaveBeenCalledTimes(10); }); it('should execute actions on a matching HA bus event trigger', () => { @@ -321,13 +321,13 @@ describe('AutomationsManager', () => { }, ]); - expect(eventWatcher.subscribe).toBeCalledTimes(1); + expect(eventWatcher.subscribe).toHaveBeenCalledTimes(1); // Simulate an event arrival. const event = createHASSEvent('zha_event', { command: 'press' }); vi.mocked(eventWatcher.subscribe).mock.calls[0][0].callback(event); - expect(api.getActionsManager().executeActions).toBeCalledTimes(1); + expect(api.getActionsManager().executeActions).toHaveBeenCalledTimes(1); expect( vi.mocked(api.getActionsManager().executeActions).mock.calls[0][0].triggerData, ).toEqual({ platform: 'event', event }); @@ -356,23 +356,23 @@ describe('AutomationsManager', () => { ]); stateManager.setState({ fullscreen: true }); - expect(api.getActionsManager().executeActions).toBeCalledTimes(1); + expect(api.getActionsManager().executeActions).toHaveBeenCalledTimes(1); // Delete the fullscreen automation. automationsManager.deleteAutomations('fullscreen'); stateManager.setState({ fullscreen: false }); stateManager.setState({ fullscreen: true }); - expect(api.getActionsManager().executeActions).toBeCalledTimes(1); + expect(api.getActionsManager().executeActions).toHaveBeenCalledTimes(1); stateManager.setState({ expand: true }); - expect(api.getActionsManager().executeActions).toBeCalledTimes(2); + expect(api.getActionsManager().executeActions).toHaveBeenCalledTimes(2); // Delete all automations. automationsManager.deleteAutomations(); stateManager.setState({ expand: false }); stateManager.setState({ expand: true }); - expect(api.getActionsManager().executeActions).toBeCalledTimes(2); + expect(api.getActionsManager().executeActions).toHaveBeenCalledTimes(2); }); }); diff --git a/tests/card-controller/call/manager.test.ts b/tests/card-controller/call/manager.test.ts index 1d440a26..3e8087f6 100644 --- a/tests/card-controller/call/manager.test.ts +++ b/tests/card-controller/call/manager.test.ts @@ -16,16 +16,15 @@ import type { TriggerOfType } from '../../../src/condition-trigger/triggers/trig import type { RingtoneConfig } from '../../../src/config/schema/live'; import type { AdvancedCameraCardConfig } from '../../../src/config/schema/types'; import { View } from '../../../src/view/view'; -import { createTriggerEvaluatorContext } from '../../condition-trigger/triggers/triggers/test-utils'; import { - createCameraConfig, createCameraManager, createCapabilities, - createCardAPI, - createConfig, createStore, - createView, -} from '../../test-utils'; +} from '../../camera-manager/test-utils'; +import { createTriggerEvaluatorContext } from '../../condition-trigger/triggers/triggers/test-utils'; +import { createCameraConfig, createConfig } from '../../config/test-utils'; +import { createCardAPI } from '../../test-utils'; +import { createView } from '../../view/test-utils'; // Replace Ringtone with a fresh `mock()` per construction so each // CallManager gets an isolated, type-safe ringtone we can assert on. The @@ -130,7 +129,7 @@ describe('start', () => { expect(await new CallManager(api).start()).toBe(false); - expect(api.getViewManager().setViewByParameters).not.toBeCalled(); + expect(api.getViewManager().setViewByParameters).not.toHaveBeenCalled(); }); it('should do nothing when already active for the camera', async () => { @@ -141,7 +140,7 @@ describe('start', () => { vi.mocked(api.getViewManager().setViewByParameters).mockClear(); expect(await manager.start()).toBe(true); - expect(api.getViewManager().setViewByParameters).not.toBeCalled(); + expect(api.getViewManager().setViewByParameters).not.toHaveBeenCalled(); }); it('should start a call on the selected camera', async () => { @@ -149,7 +148,7 @@ describe('start', () => { expect(await new CallManager(api).start()).toBe(true); - expect(api.getViewManager().setViewByParameters).toBeCalledWith({ + expect(api.getViewManager().setViewByParameters).toHaveBeenCalledWith({ modifiers: [expect.any(SubstreamViewModifier)], force: true, }); @@ -162,7 +161,7 @@ describe('start', () => { expect(await new CallManager(api).start()).toBe(true); - expect(api.getViewManager().setViewByParameters).toBeCalledWith({ + expect(api.getViewManager().setViewByParameters).toHaveBeenCalledWith({ params: { view: 'live', camera: 'camera.office' }, modifiers: [expect.any(SubstreamViewModifier)], force: true, @@ -176,11 +175,11 @@ describe('start', () => { expect(await new CallManager(api).start()).toBe(true); - expect(api.getViewManager().setViewByParameters).toBeCalledWith({ + expect(api.getViewManager().setViewByParameters).toHaveBeenCalledWith({ modifiers: [expect.any(SubstreamViewModifier)], force: true, }); - expect(api.getViewManager().setViewByParameters).not.toBeCalledWith( + expect(api.getViewManager().setViewByParameters).not.toHaveBeenCalledWith( expect.objectContaining({ params: expect.anything() }), ); }); @@ -251,7 +250,7 @@ describe('start', () => { expect(call?.cameraID).toBe('camera.office'); expect(call?.previousView?.view).toBe('folder'); expect(call?.previousView?.camera).toBeNull(); - expect(api.getViewManager().setViewByParameters).toBeCalledWith({ + expect(api.getViewManager().setViewByParameters).toHaveBeenCalledWith({ params: { view: 'live', camera: 'camera.office' }, modifiers: [expect.any(SubstreamViewModifier)], force: true, @@ -280,7 +279,7 @@ describe('start', () => { expect(call?.cameraID).toBe('camera.garage'); expect(call?.previousView?.view).toBe('live'); expect(call?.previousView?.camera).toBe('camera.office'); - expect(api.getViewManager().setViewByParameters).toBeCalledWith({ + expect(api.getViewManager().setViewByParameters).toHaveBeenCalledWith({ params: { view: 'live', camera: 'camera.garage' }, modifiers: [expect.any(SubstreamViewModifier)], force: true, @@ -321,8 +320,8 @@ describe('start', () => { expect(await new CallManager(api).start({ cameraID: 'camera.unknown' })).toBe(false); - expect(api.getViewManager().setViewByParameters).not.toBeCalled(); - expect(api.getNotificationManager().setNotification).toBeCalled(); + expect(api.getViewManager().setViewByParameters).not.toHaveBeenCalled(); + expect(api.getNotificationManager().setNotification).toHaveBeenCalled(); }); it('should abort when the requested stream is not 2-way audio of the parent camera', async () => { @@ -347,8 +346,8 @@ describe('start', () => { }), ).toBe(false); - expect(api.getViewManager().setViewByParameters).not.toBeCalled(); - expect(api.getNotificationManager().setNotification).toBeCalled(); + expect(api.getViewManager().setViewByParameters).not.toHaveBeenCalled(); + expect(api.getNotificationManager().setNotification).toHaveBeenCalled(); }); it('should supersede an active call on a different camera', async () => { @@ -439,8 +438,8 @@ describe('start', () => { expect(await new CallManager(api).start()).toBe(false); - expect(api.getViewManager().setViewByParameters).not.toBeCalled(); - expect(api.getNotificationManager().setNotification).toBeCalled(); + expect(api.getViewManager().setViewByParameters).not.toHaveBeenCalled(); + expect(api.getNotificationManager().setNotification).toHaveBeenCalled(); }); it('should engage the active substream when it is call-capable', async () => { @@ -464,7 +463,7 @@ describe('start', () => { expect(await manager.start()).toBe(true); - expect(api.getViewManager().setViewByParameters).toBeCalledWith({ + expect(api.getViewManager().setViewByParameters).toHaveBeenCalledWith({ modifiers: [expect.any(SubstreamViewModifier)], force: true, }); @@ -493,7 +492,7 @@ describe('start', () => { expect(await new CallManager(api).start()).toBe(true); - expect(api.getViewManager().setViewByParameters).toBeCalledWith({ + expect(api.getViewManager().setViewByParameters).toHaveBeenCalledWith({ modifiers: [expect.any(SubstreamViewModifier)], force: true, }); @@ -507,8 +506,8 @@ describe('start', () => { expect(await new CallManager(api).start()).toBe(false); - expect(api.getViewManager().setViewByParameters).not.toBeCalled(); - expect(api.getNotificationManager().setNotification).toBeCalled(); + expect(api.getViewManager().setViewByParameters).not.toHaveBeenCalled(); + expect(api.getNotificationManager().setNotification).toHaveBeenCalled(); }); it('should abort when the microphone is forbidden', async () => { @@ -519,8 +518,8 @@ describe('start', () => { expect(await new CallManager(api).start()).toBe(false); - expect(api.getViewManager().setViewByParameters).not.toBeCalled(); - expect(api.getNotificationManager().setNotification).toBeCalled(); + expect(api.getViewManager().setViewByParameters).not.toHaveBeenCalled(); + expect(api.getNotificationManager().setNotification).toHaveBeenCalled(); }); it('should connect the microphone when not already connected', async () => { @@ -532,8 +531,8 @@ describe('start', () => { expect(await new CallManager(api).start()).toBe(true); - expect(api.getMicrophoneManager().connect).toBeCalled(); - expect(api.getViewManager().setViewByParameters).toBeCalled(); + expect(api.getMicrophoneManager().connect).toHaveBeenCalled(); + expect(api.getViewManager().setViewByParameters).toHaveBeenCalled(); }); it('should abort when connecting the microphone fails', async () => { @@ -545,8 +544,8 @@ describe('start', () => { expect(await new CallManager(api).start()).toBe(false); - expect(api.getViewManager().setViewByParameters).not.toBeCalled(); - expect(api.getNotificationManager().setNotification).toBeCalled(); + expect(api.getViewManager().setViewByParameters).not.toHaveBeenCalled(); + expect(api.getNotificationManager().setNotification).toHaveBeenCalled(); }); it('should ring an inbound call without connecting the microphone', async () => { @@ -562,8 +561,8 @@ describe('start', () => { // that would fail must not stop the call from ringing. expect(await manager.start({ inbound: true })).toBe(true); - expect(api.getMicrophoneManager().connect).not.toBeCalled(); - expect(getRingtone().start).toBeCalled(); + expect(api.getMicrophoneManager().connect).not.toHaveBeenCalled(); + expect(getRingtone().start).toHaveBeenCalled(); }); it('should ring an inbound call when the microphone is forbidden', async () => { @@ -578,7 +577,7 @@ describe('start', () => { // microphone, and answering retries the connect. expect(await manager.start({ inbound: true })).toBe(true); - expect(getRingtone().start).toBeCalled(); + expect(getRingtone().start).toHaveBeenCalled(); }); it('should abort an inbound call when the microphone is unsupported', async () => { @@ -593,7 +592,7 @@ describe('start', () => { // supporting it while the page is loaded, so the call can never be taken. expect(await manager.start({ inbound: true })).toBe(false); - expect(getRingtone().start).not.toBeCalled(); + expect(getRingtone().start).not.toHaveBeenCalled(); }); }); @@ -692,7 +691,7 @@ describe('end', () => { expect(new CallManager(api).end()).toBe(false); - expect(api.getViewManager().setViewByParameters).not.toBeCalled(); + expect(api.getViewManager().setViewByParameters).not.toHaveBeenCalled(); }); it('should end an active call', async () => { @@ -704,7 +703,7 @@ describe('end', () => { expect(manager.end()).toBe(true); expect(manager.isActive()).toBe(false); - expect(api.getViewManager().setViewByParameters).toBeCalledWith({ + expect(api.getViewManager().setViewByParameters).toHaveBeenCalledWith({ modifiers: [expect.any(SubstreamViewModifier)], force: true, }); @@ -734,7 +733,7 @@ describe('end', () => { expect(manager.end()).toBe(true); // The recorded pre-call substream (`camera.sub`) is reinstated. - expect(api.getViewManager().setViewByParameters).toBeCalledWith({ + expect(api.getViewManager().setViewByParameters).toHaveBeenCalledWith({ modifiers: [expect.any(SubstreamViewModifier)], force: true, }); @@ -749,7 +748,9 @@ describe('end', () => { expect(manager.end()).toBe(true); - expect(api.getViewManager().setViewByParametersWithExistingQuery).toBeCalledWith({ + expect( + api.getViewManager().setViewByParametersWithExistingQuery, + ).toHaveBeenCalledWith({ baseView: expect.any(View), force: true, }); @@ -770,11 +771,13 @@ describe('end', () => { expect(manager.end()).toBe(true); // No navigation: only the substream is undone. - expect(api.getViewManager().setViewByParameters).toBeCalledWith({ + expect(api.getViewManager().setViewByParameters).toHaveBeenCalledWith({ modifiers: [expect.any(SubstreamViewModifier)], force: true, }); - expect(api.getViewManager().setViewByParametersWithExistingQuery).not.toBeCalled(); + expect( + api.getViewManager().setViewByParametersWithExistingQuery, + ).not.toHaveBeenCalled(); }); it('should return to a camera-less pre-call view on an explicit end', async () => { @@ -957,7 +960,7 @@ describe('condition state changes', () => { }); expect(manager.isActive()).toBe(false); - expect(api.getViewManager().setViewByParameters).toBeCalledWith({ + expect(api.getViewManager().setViewByParameters).toHaveBeenCalledWith({ modifiers: [expect.any(SubstreamViewModifier)], force: true, }); @@ -979,7 +982,9 @@ describe('condition state changes', () => { }); expect(manager.isActive()).toBe(false); - expect(api.getViewManager().setViewByParametersWithExistingQuery).not.toBeCalled(); + expect( + api.getViewManager().setViewByParametersWithExistingQuery, + ).not.toHaveBeenCalled(); }); it('should end the call when the view leaves live', async () => { @@ -996,7 +1001,7 @@ describe('condition state changes', () => { }); expect(manager.isActive()).toBe(false); - expect(api.getViewManager().setViewByParameters).toBeCalledWith({ + expect(api.getViewManager().setViewByParameters).toHaveBeenCalledWith({ modifiers: [expect.any(SubstreamViewModifier)], force: true, }); @@ -1027,7 +1032,7 @@ describe('condition state changes', () => { new: { camera: 'camera.other' }, }); - expect(api.getViewManager().setViewByParameters).not.toBeCalled(); + expect(api.getViewManager().setViewByParameters).not.toHaveBeenCalled(); }); it('should end the call when the substream changes away', async () => { @@ -1103,7 +1108,7 @@ describe('initialize / uninitialize', () => { const api = createAPI(); new CallManager(api); - expect(api.getConditionStateManager().addListener).not.toBeCalled(); + expect(api.getConditionStateManager().addListener).not.toHaveBeenCalled(); }); it('should register the condition state listener on initialize', () => { @@ -1112,7 +1117,7 @@ describe('initialize / uninitialize', () => { manager.initialize(); - expect(api.getConditionStateManager().addListener).toBeCalled(); + expect(api.getConditionStateManager().addListener).toHaveBeenCalled(); }); it('should remove the condition state listener on uninitialize', () => { @@ -1123,7 +1128,7 @@ describe('initialize / uninitialize', () => { manager.uninitialize(); - expect(api.getConditionStateManager().removeListener).toBeCalledWith(listener); + expect(api.getConditionStateManager().removeListener).toHaveBeenCalledWith(listener); }); it('should tear down any active call session on uninitialize', async () => { @@ -1156,7 +1161,7 @@ describe('initialize / uninitialize', () => { new: { camera: 'camera.other', view: 'live' }, }); - expect(api.getViewManager().setViewByParameters).not.toBeCalled(); + expect(api.getViewManager().setViewByParameters).not.toHaveBeenCalled(); }); }); @@ -1179,7 +1184,7 @@ describe('inbound option', () => { expect(await new CallManager(api).start({ inbound: true })).toBe(false); - expect(api.getNotificationManager().setNotification).not.toBeCalled(); + expect(api.getNotificationManager().setNotification).not.toHaveBeenCalled(); }); it('should suppress notification when the microphone is unsupported', async () => { @@ -1190,7 +1195,7 @@ describe('inbound option', () => { expect(await new CallManager(api).start({ inbound: true })).toBe(false); - expect(api.getNotificationManager().setNotification).not.toBeCalled(); + expect(api.getNotificationManager().setNotification).not.toHaveBeenCalled(); }); it('should suppress notification when an explicit stream is not 2-way audio', async () => { @@ -1203,7 +1208,7 @@ describe('inbound option', () => { }), ).toBe(false); - expect(api.getNotificationManager().setNotification).not.toBeCalled(); + expect(api.getNotificationManager().setNotification).not.toHaveBeenCalled(); }); it('should suppress notification when no stream supports 2-way audio', async () => { @@ -1219,7 +1224,7 @@ describe('inbound option', () => { expect(await new CallManager(api).start({ inbound: true })).toBe(false); - expect(api.getNotificationManager().setNotification).not.toBeCalled(); + expect(api.getNotificationManager().setNotification).not.toHaveBeenCalled(); }); it('should record the call as inbound on the session', async () => { @@ -1274,7 +1279,7 @@ describe('answer', () => { expect(await manager.start({ inbound: true })).toBe(true); expect(manager.getCall()?.answered).toBe(false); - expect(getRingtone().start).toBeCalled(); + expect(getRingtone().start).toHaveBeenCalled(); }); it('should no-op when no call is active', async () => { @@ -1298,8 +1303,8 @@ describe('answer', () => { expect(await manager.answer()).toBe(false); - expect(getRingtone().stop).not.toBeCalled(); - expect(api.getCardElementManager().update).not.toBeCalled(); + expect(getRingtone().stop).not.toHaveBeenCalled(); + expect(api.getCardElementManager().update).not.toHaveBeenCalled(); }); it('should mark answered and replace the session immutably', async () => { @@ -1345,7 +1350,7 @@ describe('answer', () => { expect(await manager.answer()).toBe(true); - expect(getRingtone().stop).toBeCalled(); + expect(getRingtone().stop).toHaveBeenCalled(); // Timer was armed and should now be cancelled: advancing past the // timeout must not end the (now-answered) call. @@ -1369,7 +1374,7 @@ describe('answer', () => { expect(await manager.answer()).toBe(true); - expect(api.getMicrophoneManager().connect).toBeCalled(); + expect(api.getMicrophoneManager().connect).toHaveBeenCalled(); }); it('should silence the ringtone before the microphone connect', async () => { @@ -1393,7 +1398,7 @@ describe('answer', () => { // The user has acknowledged the ring, so it must stop without waiting for a // microphone permission prompt to be dealt with. - expect(getRingtone().stop).toBeCalled(); + expect(getRingtone().stop).toHaveBeenCalled(); resolveConnect(); expect(await answerPromise).toBe(true); @@ -1414,7 +1419,7 @@ describe('answer', () => { // Answering is an explicit user gesture, so the failure is surfaced and the // call remains answerable. - expect(api.getNotificationManager().setNotification).toBeCalled(); + expect(api.getNotificationManager().setNotification).toHaveBeenCalled(); expect(manager.getCall()?.answered).toBe(false); }); @@ -1433,7 +1438,7 @@ describe('answer', () => { // The card subtree depends on `getCall().answered`, which the manager // mutates outside the view-manager epoch -- so `update()` is what drives // the re-render through to the call-controls overlay. - expect(api.getCardElementManager().update).toBeCalled(); + expect(api.getCardElementManager().update).toHaveBeenCalled(); }); it('should not mark non-inbound (outbound) calls via answer (already answered)', async () => { @@ -1483,12 +1488,12 @@ describe('microphone usage', () => { expect(await manager.start()).toBe(true); - expect(api.getMicrophoneManager().startUsing).toBeCalledTimes(1); - expect(api.getMicrophoneManager().stopUsing).not.toBeCalled(); + expect(api.getMicrophoneManager().startUsing).toHaveBeenCalledTimes(1); + expect(api.getMicrophoneManager().stopUsing).not.toHaveBeenCalled(); manager.end(); - expect(api.getMicrophoneManager().stopUsing).toBeCalledTimes(1); + expect(api.getMicrophoneManager().stopUsing).toHaveBeenCalledTimes(1); }); it('should not mark the microphone in use when the start is aborted', async () => { @@ -1500,7 +1505,7 @@ describe('microphone usage', () => { expect(await new CallManager(api).start()).toBe(false); - expect(api.getMicrophoneManager().startUsing).not.toBeCalled(); + expect(api.getMicrophoneManager().startUsing).not.toHaveBeenCalled(); }); it('should keep the microphone in use when a call supersedes another', async () => { @@ -1522,8 +1527,8 @@ describe('microphone usage', () => { expect(await manager.start()).toBe(true); expect(await manager.start({ cameraID: 'camera.garage' })).toBe(true); - expect(api.getMicrophoneManager().startUsing).toBeCalledTimes(2); - expect(api.getMicrophoneManager().stopUsing).toBeCalledTimes(1); + expect(api.getMicrophoneManager().startUsing).toHaveBeenCalledTimes(2); + expect(api.getMicrophoneManager().stopUsing).toHaveBeenCalledTimes(1); }); it('should mark the microphone unused on uninitialization', async () => { @@ -1533,7 +1538,7 @@ describe('microphone usage', () => { expect(await manager.start()).toBe(true); manager.uninitialize(); - expect(api.getMicrophoneManager().stopUsing).toBeCalledTimes(1); + expect(api.getMicrophoneManager().stopUsing).toHaveBeenCalledTimes(1); }); it('should not mark the microphone unused when uninitializing without a call', () => { @@ -1541,7 +1546,7 @@ describe('microphone usage', () => { new CallManager(api).uninitialize(); - expect(api.getMicrophoneManager().stopUsing).not.toBeCalled(); + expect(api.getMicrophoneManager().stopUsing).not.toHaveBeenCalled(); }); }); @@ -1559,7 +1564,7 @@ describe('ringtone', () => { expect(await manager.start({ inbound: true })).toBe(true); - expect(getRingtone().start).toBeCalledWith(expect.objectContaining(ringtone)); + expect(getRingtone().start).toHaveBeenCalledWith(expect.objectContaining(ringtone)); }); it('should not start the ringtone for a non-inbound call', async () => { @@ -1572,7 +1577,7 @@ describe('ringtone', () => { expect(await manager.start()).toBe(true); - expect(getRingtone().start).not.toBeCalled(); + expect(getRingtone().start).not.toHaveBeenCalled(); }); it("should not start the ringtone when type is 'none'", async () => { @@ -1585,7 +1590,7 @@ describe('ringtone', () => { expect(await manager.start({ inbound: true })).toBe(true); - expect(getRingtone().start).not.toBeCalled(); + expect(getRingtone().start).not.toHaveBeenCalled(); }); it('should stop the ringtone when the call ends', async () => { @@ -1600,7 +1605,7 @@ describe('ringtone', () => { expect(manager.end()).toBe(true); - expect(getRingtone().stop).toBeCalled(); + expect(getRingtone().stop).toHaveBeenCalled(); }); it('should stop the ringtone on uninitialize', async () => { @@ -1615,7 +1620,7 @@ describe('ringtone', () => { manager.uninitialize(); - expect(getRingtone().stop).toBeCalled(); + expect(getRingtone().stop).toHaveBeenCalled(); }); }); @@ -1764,7 +1769,7 @@ describe('session end during setState', () => { expect(await manager.start({ inbound: true })).toBe(false); - expect(getRingtone().start).not.toBeCalled(); + expect(getRingtone().start).not.toHaveBeenCalled(); expect(manager.isActive()).toBe(false); }); }); @@ -1790,7 +1795,7 @@ describe('state changes during in-flight start', () => { expect(await startPromise).toBe(false); expect(manager.isActive()).toBe(false); - expect(api.getViewManager().setViewByParameters).not.toBeCalled(); + expect(api.getViewManager().setViewByParameters).not.toHaveBeenCalled(); }); it('should not install a session when uninitialized and re-initialized mid-await', async () => { @@ -1814,7 +1819,7 @@ describe('state changes during in-flight start', () => { expect(await startPromise).toBe(false); expect(manager.isActive()).toBe(false); - expect(api.getViewManager().setViewByParameters).not.toBeCalled(); + expect(api.getViewManager().setViewByParameters).not.toHaveBeenCalled(); }); it('should supersede a session installed by another start mid-await', async () => { @@ -1853,8 +1858,8 @@ describe('state changes during in-flight start', () => { // overwrite it, leaving exactly one live session and no stranded microphone // marking. expect(manager.getCall()?.cameraID).toBe('camera.garage'); - expect(api.getMicrophoneManager().startUsing).toBeCalledTimes(2); - expect(api.getMicrophoneManager().stopUsing).toBeCalledTimes(1); + expect(api.getMicrophoneManager().startUsing).toHaveBeenCalledTimes(2); + expect(api.getMicrophoneManager().stopUsing).toHaveBeenCalledTimes(1); }); it('should suppress the microphone-failure notification when uninitialized mid-await', async () => { @@ -1876,7 +1881,7 @@ describe('state changes during in-flight start', () => { rejectConnect(new Error('denied')); expect(await startPromise).toBe(false); - expect(api.getNotificationManager().setNotification).not.toBeCalled(); + expect(api.getNotificationManager().setNotification).not.toHaveBeenCalled(); }); }); @@ -1939,7 +1944,7 @@ describe('state changes during in-flight answer', () => { rejectConnect(new Error('denied')); expect(await answerPromise).toBe(false); - expect(api.getNotificationManager().setNotification).not.toBeCalled(); + expect(api.getNotificationManager().setNotification).not.toHaveBeenCalled(); }); it('should not answer a session that ended mid-await', async () => { diff --git a/tests/card-controller/call/ringtone.test.ts b/tests/card-controller/call/ringtone.test.ts index 1edd151d..4429f853 100644 --- a/tests/card-controller/call/ringtone.test.ts +++ b/tests/card-controller/call/ringtone.test.ts @@ -59,25 +59,25 @@ describe('factory dispatch', () => { it('should construct a ChimeTone for type "chime"', () => { new Ringtone(new Set()).start({ type: 'chime', repeat: 3 }); - expect(ChimeTone).toBeCalledWith(3); + expect(ChimeTone).toHaveBeenCalledWith(3); }); it('should construct a WestminsterTone for type "westminster"', () => { new Ringtone(new Set()).start({ type: 'westminster', repeat: 2 }); - expect(WestminsterTone).toBeCalledWith(2); + expect(WestminsterTone).toHaveBeenCalledWith(2); }); it('should construct an ArpeggioTone for type "arpeggio"', () => { new Ringtone(new Set()).start({ type: 'arpeggio', repeat: 1 }); - expect(ArpeggioTone).toBeCalledWith(1); + expect(ArpeggioTone).toHaveBeenCalledWith(1); }); it('should construct a MelodyTone for type "melody"', () => { new Ringtone(new Set()).start({ type: 'melody', repeat: 5 }); - expect(MelodyTone).toBeCalledWith(5); + expect(MelodyTone).toHaveBeenCalledWith(5); }); it('should construct a CustomTone for type "custom" with a URL', () => { @@ -87,7 +87,7 @@ describe('factory dispatch', () => { repeat: 0, }); - expect(CustomTone).toBeCalledWith('http://localhost/ring.mp3', 0); + expect(CustomTone).toHaveBeenCalledWith('http://localhost/ring.mp3', 0); }); it('should construct no tone for type "custom" without a URL', () => { @@ -95,7 +95,7 @@ describe('factory dispatch', () => { ringtone.start({ type: 'custom', repeat: 0 }); - expect(CustomTone).not.toBeCalled(); + expect(CustomTone).not.toHaveBeenCalled(); expect(ringtone.isPlaying()).toBe(false); }); @@ -104,7 +104,7 @@ describe('factory dispatch', () => { ringtone.start({ type: 'none', repeat: 0 }); - expect(ChimeTone).not.toBeCalled(); + expect(ChimeTone).not.toHaveBeenCalled(); expect(ringtone.isPlaying()).toBe(false); }); }); @@ -115,7 +115,7 @@ describe('start', () => { ringtone.start(chimeConfig); - expect(lastInstance(vi.mocked(ChimeTone)).start).toBeCalled(); + expect(lastInstance(vi.mocked(ChimeTone)).start).toHaveBeenCalled(); expect(ringtone.isPlaying()).toBe(true); }); @@ -125,8 +125,8 @@ describe('start', () => { ringtone.start(chimeConfig); ringtone.start(chimeConfig); - expect(ChimeTone).toBeCalledTimes(1); - expect(lastInstance(vi.mocked(ChimeTone)).start).toBeCalledTimes(1); + expect(ChimeTone).toHaveBeenCalledTimes(1); + expect(lastInstance(vi.mocked(ChimeTone)).start).toHaveBeenCalledTimes(1); }); it('should claim the lock when a tone starts', () => { @@ -158,7 +158,7 @@ describe('lock', () => { vi.mocked(ChimeTone).mockClear(); second.start(chimeConfig); - expect(ChimeTone).not.toBeCalled(); + expect(ChimeTone).not.toHaveBeenCalled(); expect(second.isPlaying()).toBe(false); expect(first.isPlaying()).toBe(true); }); @@ -171,11 +171,11 @@ describe('lock', () => { first.start(chimeConfig); const firstTone = lastInstance(vi.mocked(ChimeTone)); first.stop(); - expect(firstTone.stop).toBeCalled(); + expect(firstTone.stop).toHaveBeenCalled(); second.start(chimeConfig); - expect(lastInstance(vi.mocked(ChimeTone)).start).toBeCalled(); + expect(lastInstance(vi.mocked(ChimeTone)).start).toHaveBeenCalled(); expect(second.isPlaying()).toBe(true); }); @@ -227,7 +227,7 @@ describe('stop', () => { ringtone.start(chimeConfig); ringtone.stop(); - expect(lastInstance(vi.mocked(ChimeTone)).stop).toBeCalled(); + expect(lastInstance(vi.mocked(ChimeTone)).stop).toHaveBeenCalled(); expect(lock.has(ringtone)).toBe(false); expect(ringtone.isPlaying()).toBe(false); }); @@ -250,7 +250,7 @@ describe('default lock', () => { vi.mocked(ChimeTone).mockClear(); second.start(chimeConfig); - expect(ChimeTone).not.toBeCalled(); + expect(ChimeTone).not.toHaveBeenCalled(); first.stop(); }); }); diff --git a/tests/card-controller/call/tones/arpeggio.test.ts b/tests/card-controller/call/tones/arpeggio.test.ts index 6613e39a..1042f820 100644 --- a/tests/card-controller/call/tones/arpeggio.test.ts +++ b/tests/card-controller/call/tones/arpeggio.test.ts @@ -19,15 +19,15 @@ describe('ArpeggioTone', () => { // G5 (783.99) at t=0. expect(audio.oscillators[1].frequency.value).toBe(783.99); - expect(audio.oscillators[1].start).toBeCalledWith(0); + expect(audio.oscillators[1].start).toHaveBeenCalledWith(0); // E5 (659.25) at t=0.25. expect(audio.oscillators[4].frequency.value).toBe(659.25); - expect(audio.oscillators[4].start).toBeCalledWith(0.25); + expect(audio.oscillators[4].start).toHaveBeenCalledWith(0.25); // C5 (523.25) at t=0.5. expect(audio.oscillators[7].frequency.value).toBe(523.25); - expect(audio.oscillators[7].start).toBeCalledWith(0.5); + expect(audio.oscillators[7].start).toHaveBeenCalledWith(0.5); }); it('should use the lighter PLUCK envelope for every strike', () => { @@ -36,15 +36,15 @@ describe('ArpeggioTone', () => { // Sparkle / fundamental / hum peaks for every strike. for (let strike = 0; strike < 3; strike++) { const i = strike * 3; - expect(audio.gainParams[i].linearRampToValueAtTime).toBeCalledWith( + expect(audio.gainParams[i].linearRampToValueAtTime).toHaveBeenCalledWith( 0.05, expect.any(Number), ); - expect(audio.gainParams[i + 1].linearRampToValueAtTime).toBeCalledWith( + expect(audio.gainParams[i + 1].linearRampToValueAtTime).toHaveBeenCalledWith( 0.13, expect.any(Number), ); - expect(audio.gainParams[i + 2].linearRampToValueAtTime).toBeCalledWith( + expect(audio.gainParams[i + 2].linearRampToValueAtTime).toHaveBeenCalledWith( 0.04, expect.any(Number), ); diff --git a/tests/card-controller/call/tones/base.test.ts b/tests/card-controller/call/tones/base.test.ts index cf8ab57a..e8ac6b31 100644 --- a/tests/card-controller/call/tones/base.test.ts +++ b/tests/card-controller/call/tones/base.test.ts @@ -18,7 +18,7 @@ describe('start', () => { it('should construct an AudioContext and play one iteration', () => { new ChimeTone(0).start(); - expect(audio.audioContextCtor).toBeCalledTimes(1); + expect(audio.audioContextCtor).toHaveBeenCalledTimes(1); expect(audio.oscillators).toHaveLength(ITERATION_OSCILLATORS); }); @@ -28,7 +28,7 @@ describe('start', () => { tone.start(); tone.start(); - expect(audio.audioContextCtor).toBeCalledTimes(1); + expect(audio.audioContextCtor).toHaveBeenCalledTimes(1); expect(audio.oscillators).toHaveLength(ITERATION_OSCILLATORS); }); @@ -40,7 +40,7 @@ describe('start', () => { new ChimeTone(0).start(onFinished); - expect(onFinished).toBeCalled(); + expect(onFinished).toHaveBeenCalled(); expect(audio.oscillators).toHaveLength(0); }); }); @@ -52,7 +52,7 @@ describe('stop', () => { tone.stop(); - expect(audio.audioContext.close).toBeCalled(); + expect(audio.audioContext.close).toHaveBeenCalled(); }); it('should not fire finishedHandler on external stop', () => { @@ -62,7 +62,7 @@ describe('stop', () => { tone.stop(); - expect(onFinished).not.toBeCalled(); + expect(onFinished).not.toHaveBeenCalled(); }); it('should swallow AudioContext.close rejections silently', () => { @@ -91,7 +91,7 @@ describe('repeat counter', () => { vi.advanceTimersByTime(ITERATION_INTERVAL_MS); expect(audio.oscillators).toHaveLength(ITERATION_OSCILLATORS * i); } - expect(audio.audioContext.close).not.toBeCalled(); + expect(audio.audioContext.close).not.toHaveBeenCalled(); }); it('should play exactly `repeat` iterations and then finish', () => { @@ -110,8 +110,8 @@ describe('repeat counter', () => { // decay tail, then fires the finished handler and stops. vi.advanceTimersByTime(ITERATION_INTERVAL_MS); expect(audio.oscillators).toHaveLength(ITERATION_OSCILLATORS * 3); - expect(onFinished).toBeCalledTimes(1); - expect(audio.audioContext.close).toBeCalled(); + expect(onFinished).toHaveBeenCalledTimes(1); + expect(audio.audioContext.close).toHaveBeenCalled(); }); it('should not fire finishedHandler when stopped mid-sequence', () => { @@ -124,6 +124,6 @@ describe('repeat counter', () => { // Even if any stale scheduled work fires, finishedHandler stays silent. vi.advanceTimersByTime(ITERATION_INTERVAL_MS * 10); - expect(onFinished).not.toBeCalled(); + expect(onFinished).not.toHaveBeenCalled(); }); }); diff --git a/tests/card-controller/call/tones/chime.test.ts b/tests/card-controller/call/tones/chime.test.ts index 6ce93aee..a2f5481d 100644 --- a/tests/card-controller/call/tones/chime.test.ts +++ b/tests/card-controller/call/tones/chime.test.ts @@ -21,41 +21,53 @@ describe('ChimeTone', () => { expect(audio.oscillators[0].frequency.value).toBe(622.25 * 2); expect(audio.oscillators[1].frequency.value).toBe(622.25); expect(audio.oscillators[2].frequency.value).toBe(622.25 / 2); - expect(audio.oscillators[0].start).toBeCalledWith(0); - expect(audio.oscillators[1].start).toBeCalledWith(0); - expect(audio.oscillators[2].start).toBeCalledWith(0); + expect(audio.oscillators[0].start).toHaveBeenCalledWith(0); + expect(audio.oscillators[1].start).toHaveBeenCalledWith(0); + expect(audio.oscillators[2].start).toHaveBeenCalledWith(0); // DOOOOONG -- B4 (493.88Hz) at t=0.5. expect(audio.oscillators[3].frequency.value).toBe(493.88 * 2); expect(audio.oscillators[4].frequency.value).toBe(493.88); expect(audio.oscillators[5].frequency.value).toBe(493.88 / 2); - expect(audio.oscillators[3].start).toBeCalledWith(0.5); - expect(audio.oscillators[4].start).toBeCalledWith(0.5); - expect(audio.oscillators[5].start).toBeCalledWith(0.5); + expect(audio.oscillators[3].start).toHaveBeenCalledWith(0.5); + expect(audio.oscillators[4].start).toHaveBeenCalledWith(0.5); + expect(audio.oscillators[5].start).toHaveBeenCalledWith(0.5); }); it('should give DING a brighter, shorter bell envelope', () => { new ChimeTone(0).start(); // Sparkle / fundamental / hum peaks for DING. - expect(audio.gainParams[0].linearRampToValueAtTime).toBeCalledWith(0.1, 0.005); - expect(audio.gainParams[1].linearRampToValueAtTime).toBeCalledWith(0.22, 0.005); - expect(audio.gainParams[2].linearRampToValueAtTime).toBeCalledWith(0.08, 0.005); + expect(audio.gainParams[0].linearRampToValueAtTime).toHaveBeenCalledWith(0.1, 0.005); + expect(audio.gainParams[1].linearRampToValueAtTime).toHaveBeenCalledWith( + 0.22, + 0.005, + ); + expect(audio.gainParams[2].linearRampToValueAtTime).toHaveBeenCalledWith( + 0.08, + 0.005, + ); // Decay constants (sparkle fades fastest, hum lingers). - expect(audio.gainParams[0].setTargetAtTime).toBeCalledWith(0, 0.005, 0.3); - expect(audio.gainParams[1].setTargetAtTime).toBeCalledWith(0, 0.005, 0.8); - expect(audio.gainParams[2].setTargetAtTime).toBeCalledWith(0, 0.005, 1.2); + expect(audio.gainParams[0].setTargetAtTime).toHaveBeenCalledWith(0, 0.005, 0.3); + expect(audio.gainParams[1].setTargetAtTime).toHaveBeenCalledWith(0, 0.005, 0.8); + expect(audio.gainParams[2].setTargetAtTime).toHaveBeenCalledWith(0, 0.005, 1.2); }); it('should give DOOOOONG a fuller, longer bell envelope', () => { new ChimeTone(0).start(); - expect(audio.gainParams[3].linearRampToValueAtTime).toBeCalledWith(0.11, 0.505); - expect(audio.gainParams[4].linearRampToValueAtTime).toBeCalledWith(0.28, 0.505); - expect(audio.gainParams[5].linearRampToValueAtTime).toBeCalledWith(0.1, 0.505); - expect(audio.gainParams[3].setTargetAtTime).toBeCalledWith(0, 0.505, 0.5); - expect(audio.gainParams[4].setTargetAtTime).toBeCalledWith(0, 0.505, 1.3); - expect(audio.gainParams[5].setTargetAtTime).toBeCalledWith(0, 0.505, 1.8); + expect(audio.gainParams[3].linearRampToValueAtTime).toHaveBeenCalledWith( + 0.11, + 0.505, + ); + expect(audio.gainParams[4].linearRampToValueAtTime).toHaveBeenCalledWith( + 0.28, + 0.505, + ); + expect(audio.gainParams[5].linearRampToValueAtTime).toHaveBeenCalledWith(0.1, 0.505); + expect(audio.gainParams[3].setTargetAtTime).toHaveBeenCalledWith(0, 0.505, 0.5); + expect(audio.gainParams[4].setTargetAtTime).toHaveBeenCalledWith(0, 0.505, 1.3); + expect(audio.gainParams[5].setTargetAtTime).toHaveBeenCalledWith(0, 0.505, 1.8); }); describe('with fake timers', () => { diff --git a/tests/card-controller/call/tones/custom.test.ts b/tests/card-controller/call/tones/custom.test.ts index 44b9a9b0..75668589 100644 --- a/tests/card-controller/call/tones/custom.test.ts +++ b/tests/card-controller/call/tones/custom.test.ts @@ -59,14 +59,14 @@ describe('start', () => { it('should construct an Audio element with the configured URL', () => { new CustomTone('http://example/ring.mp3', 0).start(); - expect(audio.ctor).toBeCalledWith('http://example/ring.mp3'); + expect(audio.ctor).toHaveBeenCalledWith('http://example/ring.mp3'); }); it('should loop indefinitely when repeat is 0', () => { new CustomTone('http://example/ring.mp3', 0).start(); expect(audio.instances[0].loop).toBe(true); - expect(audio.instances[0].play).toBeCalled(); + expect(audio.instances[0].play).toHaveBeenCalled(); }); it('should re-play (not loop natively) when repeat is finite', () => { @@ -76,14 +76,14 @@ describe('start', () => { // Observable proof the 'ended' listener was registered: dispatching the // event triggers a second play(). audio.instances[0].dispatchEvent(new Event('ended')); - expect(audio.instances[0].play).toBeCalledTimes(2); + expect(audio.instances[0].play).toHaveBeenCalledTimes(2); }); it('should reset currentTime before play', () => { new CustomTone('http://example/ring.mp3', 0).start(); expect(audio.instances[0].currentTime).toBe(0); - expect(audio.instances[0].play).toBeCalled(); + expect(audio.instances[0].play).toHaveBeenCalled(); }); it('should no-op when called twice without stop', () => { @@ -92,7 +92,7 @@ describe('start', () => { tone.start(); tone.start(); - expect(audio.ctor).toBeCalledTimes(1); + expect(audio.ctor).toHaveBeenCalledTimes(1); }); it('should fire finishedHandler when Audio construction throws', () => { @@ -103,7 +103,7 @@ describe('start', () => { new CustomTone('http://example/ring.mp3', 0).start(onFinished); - expect(onFinished).toBeCalled(); + expect(onFinished).toHaveBeenCalled(); }); it('should fire finishedHandler when play() rejects (e.g. autoplay block)', async () => { @@ -116,7 +116,7 @@ describe('start', () => { await flushPromises(); - expect(onFinished).toBeCalled(); + expect(onFinished).toHaveBeenCalled(); }); }); @@ -128,15 +128,15 @@ describe('repeat counter', () => { // Iteration 1 already started by `start()`. Two more 'ended' events // should re-play, and the third 'ended' should finish. - expect(audio.instances[0].play).toBeCalledTimes(1); + expect(audio.instances[0].play).toHaveBeenCalledTimes(1); audio.instances[0].dispatchEvent(new Event('ended')); - expect(audio.instances[0].play).toBeCalledTimes(2); + expect(audio.instances[0].play).toHaveBeenCalledTimes(2); audio.instances[0].dispatchEvent(new Event('ended')); - expect(audio.instances[0].play).toBeCalledTimes(3); - expect(onFinished).not.toBeCalled(); + expect(audio.instances[0].play).toHaveBeenCalledTimes(3); + expect(onFinished).not.toHaveBeenCalled(); audio.instances[0].dispatchEvent(new Event('ended')); - expect(onFinished).toBeCalledTimes(1); + expect(onFinished).toHaveBeenCalledTimes(1); }); it('should ignore ended events after stop', () => { @@ -150,7 +150,7 @@ describe('repeat counter', () => { // no-op as far as the source is concerned. element.dispatchEvent(new Event('ended')); - expect(onFinished).not.toBeCalled(); + expect(onFinished).not.toHaveBeenCalled(); }); }); @@ -162,11 +162,11 @@ describe('stop', () => { tone.stop(); - expect(element.pause).toBeCalled(); + expect(element.pause).toHaveBeenCalled(); // Confirm the 'ended' listener is gone: dispatching it must not re-play. vi.mocked(HTMLMediaElement.prototype.play).mockClear(); element.dispatchEvent(new Event('ended')); - expect(element.play).not.toBeCalled(); + expect(element.play).not.toHaveBeenCalled(); }); it('should not fire finishedHandler on external stop', () => { @@ -176,7 +176,7 @@ describe('stop', () => { tone.stop(); - expect(onFinished).not.toBeCalled(); + expect(onFinished).not.toHaveBeenCalled(); }); it('should be safe to call before start', () => { diff --git a/tests/card-controller/call/tones/melody.test.ts b/tests/card-controller/call/tones/melody.test.ts index 93ede8e2..c111f92a 100644 --- a/tests/card-controller/call/tones/melody.test.ts +++ b/tests/card-controller/call/tones/melody.test.ts @@ -22,8 +22,8 @@ describe('MelodyTone', () => { expect(audio.oscillators[2].frequency.value).toBe(659.25); expect(audio.oscillators[3].frequency.value).toBe(783.99); expect(audio.oscillators[4].frequency.value).toBe(261.63); - expect(audio.oscillators[0].start).toBeCalledWith(0); - expect(audio.oscillators[4].start).toBeCalledWith(0); + expect(audio.oscillators[0].start).toHaveBeenCalledWith(0); + expect(audio.oscillators[4].start).toHaveBeenCalledWith(0); // --- V chord (G major) at t=1: sparkle D6, G4 + B4 + D5, hum G3. --- expect(audio.oscillators[5].frequency.value).toBe(1174.66); @@ -31,8 +31,8 @@ describe('MelodyTone', () => { expect(audio.oscillators[7].frequency.value).toBe(493.88); expect(audio.oscillators[8].frequency.value).toBe(587.33); expect(audio.oscillators[9].frequency.value).toBe(196.0); - expect(audio.oscillators[5].start).toBeCalledWith(1); - expect(audio.oscillators[9].start).toBeCalledWith(1); + expect(audio.oscillators[5].start).toHaveBeenCalledWith(1); + expect(audio.oscillators[9].start).toHaveBeenCalledWith(1); // --- I chord (resolution, an octave higher) at t=2. --- expect(audio.oscillators[10].frequency.value).toBe(2093.0); @@ -40,21 +40,21 @@ describe('MelodyTone', () => { expect(audio.oscillators[12].frequency.value).toBe(783.99); expect(audio.oscillators[13].frequency.value).toBe(1046.5); expect(audio.oscillators[14].frequency.value).toBe(329.63); - expect(audio.oscillators[10].start).toBeCalledWith(2); - expect(audio.oscillators[14].start).toBeCalledWith(2); + expect(audio.oscillators[10].start).toHaveBeenCalledWith(2); + expect(audio.oscillators[14].start).toHaveBeenCalledWith(2); }); it('should give the resolving chord a longer tail than the I and V chords', () => { new MelodyTone(0).start(); // I and V chords use default fundDecay=0.6, humDecay=1.1. - expect(audio.gainParams[1].setTargetAtTime).toBeCalledWith(0, 0.005, 0.6); - expect(audio.gainParams[4].setTargetAtTime).toBeCalledWith(0, 0.005, 1.1); - expect(audio.gainParams[6].setTargetAtTime).toBeCalledWith(0, 1.005, 0.6); - expect(audio.gainParams[9].setTargetAtTime).toBeCalledWith(0, 1.005, 1.1); + expect(audio.gainParams[1].setTargetAtTime).toHaveBeenCalledWith(0, 0.005, 0.6); + expect(audio.gainParams[4].setTargetAtTime).toHaveBeenCalledWith(0, 0.005, 1.1); + expect(audio.gainParams[6].setTargetAtTime).toHaveBeenCalledWith(0, 1.005, 0.6); + expect(audio.gainParams[9].setTargetAtTime).toHaveBeenCalledWith(0, 1.005, 1.1); // Final I chord overrides to fundDecay=0.9, humDecay=1.4. - expect(audio.gainParams[11].setTargetAtTime).toBeCalledWith(0, 2.005, 0.9); - expect(audio.gainParams[14].setTargetAtTime).toBeCalledWith(0, 2.005, 1.4); + expect(audio.gainParams[11].setTargetAtTime).toHaveBeenCalledWith(0, 2.005, 0.9); + expect(audio.gainParams[14].setTargetAtTime).toHaveBeenCalledWith(0, 2.005, 1.4); }); }); diff --git a/tests/card-controller/call/tones/westminster.test.ts b/tests/card-controller/call/tones/westminster.test.ts index 5b8c278f..77e98701 100644 --- a/tests/card-controller/call/tones/westminster.test.ts +++ b/tests/card-controller/call/tones/westminster.test.ts @@ -19,30 +19,38 @@ describe('WestminsterTone', () => { // E5 (659.25) at t=0. expect(audio.oscillators[1].frequency.value).toBe(659.25); - expect(audio.oscillators[1].start).toBeCalledWith(0); + expect(audio.oscillators[1].start).toHaveBeenCalledWith(0); // D5 (587.33) at t=0.55. expect(audio.oscillators[4].frequency.value).toBe(587.33); - expect(audio.oscillators[4].start).toBeCalledWith(0.55); + expect(audio.oscillators[4].start).toHaveBeenCalledWith(0.55); // C5 (523.25) at t=1.1. expect(audio.oscillators[7].frequency.value).toBe(523.25); - expect(audio.oscillators[7].start).toBeCalledWith(1.1); + expect(audio.oscillators[7].start).toHaveBeenCalledWith(1.1); // G4 (392.0) at t=1.65 -- the resolution. expect(audio.oscillators[10].frequency.value).toBe(392.0); - expect(audio.oscillators[10].start).toBeCalledWith(1.65); + expect(audio.oscillators[10].start).toHaveBeenCalledWith(1.65); }); it('should give the resolving G4 a longer bell tail than the other strikes', () => { new WestminsterTone(0).start(); // First three strikes use default decay (fundDecay=0.6, humDecay=1.0). - expect(audio.gainParams[1].setTargetAtTime).toBeCalledWith(0, 0.005, 0.6); - expect(audio.gainParams[2].setTargetAtTime).toBeCalledWith(0, 0.005, 1.0); + expect(audio.gainParams[1].setTargetAtTime).toHaveBeenCalledWith(0, 0.005, 0.6); + expect(audio.gainParams[2].setTargetAtTime).toHaveBeenCalledWith(0, 0.005, 1.0); // G4 (final strike) overrides to fundDecay=0.9, humDecay=1.4. - expect(audio.gainParams[10].setTargetAtTime).toBeCalledWith(0, 1.65 + 0.005, 0.9); - expect(audio.gainParams[11].setTargetAtTime).toBeCalledWith(0, 1.65 + 0.005, 1.4); + expect(audio.gainParams[10].setTargetAtTime).toHaveBeenCalledWith( + 0, + 1.65 + 0.005, + 0.9, + ); + expect(audio.gainParams[11].setTargetAtTime).toHaveBeenCalledWith( + 0, + 1.65 + 0.005, + 1.4, + ); }); }); diff --git a/tests/card-controller/camera-triggers-manager.test.ts b/tests/card-controller/camera-triggers-manager.test.ts index d0ba1a74..3c633369 100644 --- a/tests/card-controller/camera-triggers-manager.test.ts +++ b/tests/card-controller/camera-triggers-manager.test.ts @@ -7,17 +7,18 @@ import type { CardController } from '../../src/card-controller/controller'; import type { AdvancedCameraCardView } from '../../src/config/schema/common/const'; import { triggersSchema, type TriggersOptions } from '../../src/config/schema/view'; import { - createCameraConfig, createCameraManager, createCapabilities, + createStore, +} from '../camera-manager/test-utils'; +import { createCameraConfig, createConfig } from '../config/test-utils'; +import { createCardAPI, - createConfig, createHASS, createStateEntity, - createStore, - createView, flushPromises, } from '../test-utils'; +import { createView } from '../view/test-utils'; vi.mock('lodash-es', async () => ({ ...(await vi.importActual('lodash-es')), @@ -117,7 +118,7 @@ describe('CameraTriggersManager', () => { }); expect(manager.isTriggered()).toBeTruthy(); - expect(api.getViewManager().setViewDefaultWithNewQuery).not.toBeCalled(); + expect(api.getViewManager().setViewDefaultWithNewQuery).not.toHaveBeenCalled(); }); it('should not trigger if there is no config', async () => { @@ -156,7 +157,7 @@ describe('CameraTriggersManager', () => { }); expect(manager.isTriggered()).toBeTruthy(); - expect(api.getViewManager().setViewByParametersWithNewQuery).toBeCalledWith({ + expect(api.getViewManager().setViewByParametersWithNewQuery).toHaveBeenCalledWith({ queryExecutorOptions: { useCache: false }, }); }); @@ -181,7 +182,7 @@ describe('CameraTriggersManager', () => { }); expect(manager.isTriggered()).toBeTruthy(); - expect(api.getViewManager().setViewDefaultWithNewQuery).toBeCalledWith({ + expect(api.getViewManager().setViewDefaultWithNewQuery).toHaveBeenCalledWith({ params: { camera: 'camera_1', }, @@ -208,7 +209,7 @@ describe('CameraTriggersManager', () => { }); expect(manager.isTriggered()).toBeTruthy(); - expect(api.getViewManager().setViewByParametersWithNewQuery).toBeCalledWith({ + expect(api.getViewManager().setViewByParametersWithNewQuery).toHaveBeenCalledWith({ params: { view: 'live', camera: 'camera_1', @@ -257,10 +258,12 @@ describe('CameraTriggersManager', () => { if (!viewName) { expect( api.getViewManager().setViewByParametersWithNewQuery, - ).not.toBeCalled(); + ).not.toHaveBeenCalled(); } else { expect(manager.isTriggered()).toBeTruthy(); - expect(api.getViewManager().setViewByParametersWithNewQuery).toBeCalledWith({ + expect( + api.getViewManager().setViewByParametersWithNewQuery, + ).toHaveBeenCalledWith({ params: { camera: 'camera_1', view: viewName, @@ -291,8 +294,10 @@ describe('CameraTriggersManager', () => { }); expect(manager.isTriggered()).toBeTruthy(); - expect(api.getViewManager().setViewDefaultWithNewQuery).not.toBeCalled(); - expect(api.getViewManager().setViewByParametersWithNewQuery).not.toBeCalled(); + expect(api.getViewManager().setViewDefaultWithNewQuery).not.toHaveBeenCalled(); + expect( + api.getViewManager().setViewByParametersWithNewQuery, + ).not.toHaveBeenCalled(); }); it('should handle trigger action set to call', async () => { @@ -313,11 +318,13 @@ describe('CameraTriggersManager', () => { expect(manager.isTriggered()).toBeTruthy(); // `start()` is called with the triggered camera and the inbound flag -- // view navigation is delegated to CallManager itself. - expect(api.getCallManager().start).toBeCalledWith({ + expect(api.getCallManager().start).toHaveBeenCalledWith({ cameraID: 'camera_1', inbound: true, }); - expect(api.getViewManager().setViewByParametersWithNewQuery).not.toBeCalled(); + expect( + api.getViewManager().setViewByParametersWithNewQuery, + ).not.toHaveBeenCalled(); }); it('should start a call on a high-fidelity event with no media', async () => { @@ -338,7 +345,7 @@ describe('CameraTriggersManager', () => { fidelity: 'high', }); - expect(api.getCallManager().start).toBeCalledWith({ + expect(api.getCallManager().start).toHaveBeenCalledWith({ cameraID: 'camera_1', inbound: true, }); @@ -376,8 +383,10 @@ describe('CameraTriggersManager', () => { expect(manager.isTriggered()).toBeFalsy(); - expect(api.getViewManager().setViewDefaultWithNewQuery).not.toBeCalled(); - expect(api.getViewManager().setViewByParametersWithNewQuery).not.toBeCalled(); + expect(api.getViewManager().setViewDefaultWithNewQuery).not.toHaveBeenCalled(); + expect( + api.getViewManager().setViewByParametersWithNewQuery, + ).not.toHaveBeenCalled(); }); it('should handle untrigger action set to default', async () => { @@ -410,7 +419,7 @@ describe('CameraTriggersManager', () => { expect(manager.isTriggered()).toBeFalsy(); - expect(api.getViewManager().setViewDefaultWithNewQuery).toBeCalled(); + expect(api.getViewManager().setViewDefaultWithNewQuery).toHaveBeenCalled(); }); it('should handle untrigger action set to call', async () => { @@ -437,12 +446,12 @@ describe('CameraTriggersManager', () => { await flushPromises(); expect(manager.isTriggered()).toBeFalsy(); - expect(api.getCallManager().endIf).toBeCalledWith({ + expect(api.getCallManager().endIf).toHaveBeenCalledWith({ cameraID: 'camera_1', inbound: true, answered: false, }); - expect(api.getViewManager().setViewDefaultWithNewQuery).not.toBeCalled(); + expect(api.getViewManager().setViewDefaultWithNewQuery).not.toHaveBeenCalled(); }); it('should handle untrigger call with no state', async () => { @@ -455,7 +464,7 @@ describe('CameraTriggersManager', () => { type: 'end', }); - expect(api.getViewManager().setViewDefaultWithNewQuery).not.toBeCalled(); + expect(api.getViewManager().setViewDefaultWithNewQuery).not.toHaveBeenCalled(); }); it('should not untrigger if other sources are still active', async () => { @@ -531,7 +540,7 @@ describe('CameraTriggersManager', () => { // Should still be triggered because the second 'new' event should have cancelled the first timer. expect(manager.isTriggered()).toBeTruthy(); - expect(api.getViewManager().setViewDefaultWithNewQuery).not.toBeCalled(); + expect(api.getViewManager().setViewDefaultWithNewQuery).not.toHaveBeenCalled(); }); it('should untrigger each camera independently', async () => { @@ -580,7 +589,7 @@ describe('CameraTriggersManager', () => { await flushPromises(); // Camera 1 untriggered: setViewDefaultWithNewQuery is called. - expect(api.getViewManager().setViewDefaultWithNewQuery).toBeCalledTimes(1); + expect(api.getViewManager().setViewDefaultWithNewQuery).toHaveBeenCalledTimes(1); // Camera 2 is still triggered. expect(manager.getTriggeredCameraIDs()).toEqual(new Set(['camera_2'])); @@ -596,7 +605,7 @@ describe('CameraTriggersManager', () => { await flushPromises(); // Camera 2 untriggered: setViewDefaultWithNewQuery is called again. - expect(api.getViewManager().setViewDefaultWithNewQuery).toBeCalledTimes(2); + expect(api.getViewManager().setViewDefaultWithNewQuery).toHaveBeenCalledTimes(2); expect(manager.getTriggeredCameraIDs()).toEqual(new Set()); }); @@ -696,7 +705,7 @@ describe('CameraTriggersManager', () => { await flushPromises(); expect(manager.isTriggered()).toBeFalsy(); - expect(api.getViewManager().setViewDefaultWithNewQuery).toBeCalled(); + expect(api.getViewManager().setViewDefaultWithNewQuery).toHaveBeenCalled(); }); it('should force untrigger when untrigger_force_seconds expires', async () => { @@ -718,7 +727,7 @@ describe('CameraTriggersManager', () => { await flushPromises(); expect(manager.isTriggered()).toBeFalsy(); - expect(api.getViewManager().setViewDefaultWithNewQuery).toBeCalledTimes(1); + expect(api.getViewManager().setViewDefaultWithNewQuery).toHaveBeenCalledTimes(1); }); it('should not reset force timer on source update', async () => { @@ -750,7 +759,7 @@ describe('CameraTriggersManager', () => { vi.advanceTimersByTime(100); await flushPromises(); expect(manager.isTriggered()).toBeFalsy(); - expect(api.getViewManager().setViewDefaultWithNewQuery).toBeCalledTimes(1); + expect(api.getViewManager().setViewDefaultWithNewQuery).toHaveBeenCalledTimes(1); }); it('should force untrigger all sources when one force timer expires', async () => { @@ -777,7 +786,7 @@ describe('CameraTriggersManager', () => { await flushPromises(); expect(manager.isTriggered()).toBeFalsy(); - expect(api.getViewManager().setViewDefaultWithNewQuery).toBeCalledTimes(1); + expect(api.getViewManager().setViewDefaultWithNewQuery).toHaveBeenCalledTimes(1); }); it('should not extend force timer when a second source triggers later', async () => { @@ -809,7 +818,7 @@ describe('CameraTriggersManager', () => { vi.advanceTimersByTime(100); await flushPromises(); expect(manager.isTriggered()).toBeFalsy(); - expect(api.getViewManager().setViewDefaultWithNewQuery).toBeCalledTimes(1); + expect(api.getViewManager().setViewDefaultWithNewQuery).toHaveBeenCalledTimes(1); }); it('should auto-untrigger after the delay on a momentary event', async () => { @@ -827,14 +836,14 @@ describe('CameraTriggersManager', () => { }); expect(manager.isTriggered()).toBeTruthy(); - expect(api.getViewManager().setViewDefaultWithNewQuery).not.toBeCalled(); + expect(api.getViewManager().setViewDefaultWithNewQuery).not.toHaveBeenCalled(); vi.setSystemTime(add(start, { seconds: 10 })); vi.runOnlyPendingTimers(); await flushPromises(); expect(manager.isTriggered()).toBeFalsy(); - expect(api.getViewManager().setViewDefaultWithNewQuery).toBeCalled(); + expect(api.getViewManager().setViewDefaultWithNewQuery).toHaveBeenCalled(); }); it('should auto-untrigger immediately on a momentary event when delay is 0', async () => { @@ -854,7 +863,7 @@ describe('CameraTriggersManager', () => { await flushPromises(); expect(manager.isTriggered()).toBeFalsy(); - expect(api.getViewManager().setViewDefaultWithNewQuery).toBeCalled(); + expect(api.getViewManager().setViewDefaultWithNewQuery).toHaveBeenCalled(); }); it('should not auto-untrigger from a momentary event while a continuous source remains active', async () => { @@ -885,7 +894,7 @@ describe('CameraTriggersManager', () => { // Continuous source keeps the trigger alive. expect(manager.isTriggered()).toBeTruthy(); - expect(api.getViewManager().setViewDefaultWithNewQuery).not.toBeCalled(); + expect(api.getViewManager().setViewDefaultWithNewQuery).not.toHaveBeenCalled(); }); it('should add event_hold_seconds on top of untrigger_delay_seconds for momentary events', async () => { @@ -915,7 +924,7 @@ describe('CameraTriggersManager', () => { vi.advanceTimersByTime(1_000); await flushPromises(); expect(manager.isTriggered()).toBeFalsy(); - expect(api.getViewManager().setViewDefaultWithNewQuery).toBeCalled(); + expect(api.getViewManager().setViewDefaultWithNewQuery).toHaveBeenCalled(); }); it('should not apply event_hold_seconds to non-momentary events', async () => { @@ -1018,8 +1027,10 @@ describe('CameraTriggersManager', () => { fidelity: 'high', }); - expect(api.getViewManager().setViewDefaultWithNewQuery).not.toBeCalled(); - expect(api.getViewManager().setViewByParametersWithNewQuery).not.toBeCalled(); + expect(api.getViewManager().setViewDefaultWithNewQuery).not.toHaveBeenCalled(); + expect( + api.getViewManager().setViewByParametersWithNewQuery, + ).not.toHaveBeenCalled(); }); it('should ignore high-fidelity events when default view is not live', async () => { @@ -1043,8 +1054,10 @@ describe('CameraTriggersManager', () => { fidelity: 'high', }); - expect(api.getViewManager().setViewDefaultWithNewQuery).not.toBeCalled(); - expect(api.getViewManager().setViewByParametersWithNewQuery).not.toBeCalled(); + expect(api.getViewManager().setViewDefaultWithNewQuery).not.toHaveBeenCalled(); + expect( + api.getViewManager().setViewByParametersWithNewQuery, + ).not.toHaveBeenCalled(); }); }); @@ -1063,8 +1076,8 @@ describe('CameraTriggersManager', () => { expect(manager.isTriggered()).toBeTruthy(); - expect(api.getViewManager().setViewDefaultWithNewQuery).not.toBeCalled(); - expect(api.getViewManager().setViewByParametersWithNewQuery).not.toBeCalled(); + expect(api.getViewManager().setViewDefaultWithNewQuery).not.toHaveBeenCalled(); + expect(api.getViewManager().setViewByParametersWithNewQuery).not.toHaveBeenCalled(); await manager.handleCameraEvent({ cameraID: 'camera_1', @@ -1077,8 +1090,8 @@ describe('CameraTriggersManager', () => { expect(manager.isTriggered()).toBeFalsy(); - expect(api.getViewManager().setViewDefaultWithNewQuery).not.toBeCalled(); - expect(api.getViewManager().setViewByParametersWithNewQuery).not.toBeCalled(); + expect(api.getViewManager().setViewDefaultWithNewQuery).not.toHaveBeenCalled(); + expect(api.getViewManager().setViewByParametersWithNewQuery).not.toHaveBeenCalled(); }); it('should take no actions when actions are set to none', async () => { @@ -1098,8 +1111,8 @@ describe('CameraTriggersManager', () => { type: 'new', }); expect(manager.isTriggered()).toBeTruthy(); - expect(api.getViewManager().setViewDefaultWithNewQuery).not.toBeCalled(); - expect(api.getViewManager().setViewByParametersWithNewQuery).not.toBeCalled(); + expect(api.getViewManager().setViewDefaultWithNewQuery).not.toHaveBeenCalled(); + expect(api.getViewManager().setViewByParametersWithNewQuery).not.toHaveBeenCalled(); await manager.handleCameraEvent({ cameraID: 'camera_1', @@ -1111,8 +1124,8 @@ describe('CameraTriggersManager', () => { vi.runOnlyPendingTimers(); expect(manager.isTriggered()).toBeFalsy(); - expect(api.getViewManager().setViewDefaultWithNewQuery).not.toBeCalled(); - expect(api.getViewManager().setViewByParametersWithNewQuery).not.toBeCalled(); + expect(api.getViewManager().setViewDefaultWithNewQuery).not.toHaveBeenCalled(); + expect(api.getViewManager().setViewByParametersWithNewQuery).not.toHaveBeenCalled(); }); it('should take actions with human interactions when interaction mode is active', async () => { @@ -1136,7 +1149,7 @@ describe('CameraTriggersManager', () => { }); expect(manager.isTriggered()).toBeTruthy(); - expect(api.getViewManager().setViewByParametersWithNewQuery).toBeCalledWith({ + expect(api.getViewManager().setViewByParametersWithNewQuery).toHaveBeenCalledWith({ params: { view: 'live' as const, camera: 'camera_1' as const, @@ -1155,7 +1168,7 @@ describe('CameraTriggersManager', () => { expect(manager.isTriggered()).toBeFalsy(); - expect(api.getViewManager().setViewDefaultWithNewQuery).toBeCalled(); + expect(api.getViewManager().setViewDefaultWithNewQuery).toHaveBeenCalled(); }); it('should report multiple triggered cameras', async () => { @@ -1354,7 +1367,7 @@ describe('CameraTriggersManager', () => { await flushPromises(); expect(manager.isTriggered()).toBeFalsy(); - expect(api.getViewManager().setViewDefaultWithNewQuery).toBeCalled(); + expect(api.getViewManager().setViewDefaultWithNewQuery).toHaveBeenCalled(); }); }); @@ -1475,7 +1488,9 @@ describe('CameraTriggersManager', () => { expect(result).toBeTruthy(); expect(manager.isTriggered()).toBeTruthy(); - expect(api.getViewManager().setViewByParametersWithNewQuery).toBeCalledTimes(1); + expect(api.getViewManager().setViewByParametersWithNewQuery).toHaveBeenCalledTimes( + 1, + ); }); it('should prioritize the first triggered camera action at startup', async () => { @@ -1522,7 +1537,9 @@ describe('CameraTriggersManager', () => { expect(result).toBeTruthy(); expect(manager.getTriggeredCameraIDs()).toEqual(new Set(['camera_1', 'camera_2'])); - expect(api.getViewManager().setViewByParametersWithNewQuery).toBeCalledTimes(1); + expect(api.getViewManager().setViewByParametersWithNewQuery).toHaveBeenCalledTimes( + 1, + ); expect(api.getViewManager().setViewByParametersWithNewQuery).toHaveBeenCalledWith({ params: { view: 'live', @@ -1564,8 +1581,10 @@ describe('CameraTriggersManager', () => { expect(result).toBeTruthy(); // ...but the camera was filtered out, so no trigger state/action was applied. expect(manager.isTriggered()).toBeFalsy(); - expect(api.getViewManager().setViewByParametersWithNewQuery).not.toBeCalled(); - expect(api.getViewManager().setViewDefaultWithNewQuery).not.toBeCalled(); + expect( + api.getViewManager().setViewByParametersWithNewQuery, + ).not.toHaveBeenCalled(); + expect(api.getViewManager().setViewDefaultWithNewQuery).not.toHaveBeenCalled(); }); it('should not trigger a camera without the trigger capability', async () => { @@ -1674,7 +1693,7 @@ describe('CameraTriggersManager', () => { }); expect(manager.isTriggered()).toBeTruthy(); - expect(api.getViewManager().setViewByParametersWithNewQuery).toBeCalledWith({ + expect(api.getViewManager().setViewByParametersWithNewQuery).toHaveBeenCalledWith({ params: { view: 'live' as const, camera: 'camera_1' as const, @@ -1693,7 +1712,7 @@ describe('CameraTriggersManager', () => { expect(manager.isTriggered()).toBeFalsy(); - expect(api.getViewManager().setViewDefaultWithNewQuery).toBeCalled(); + expect(api.getViewManager().setViewDefaultWithNewQuery).toHaveBeenCalled(); }); it('should ignore untrigger actions during non-allowable interaction but still untrigger camera', async () => { diff --git a/tests/card-controller/camera-url-manager.test.ts b/tests/card-controller/camera-url-manager.test.ts index 891ba269..e683da78 100644 --- a/tests/card-controller/camera-url-manager.test.ts +++ b/tests/card-controller/camera-url-manager.test.ts @@ -3,7 +3,8 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'; import { CameraURLManager } from '../../src/card-controller/camera-url-manager'; import type { CardCameraURLAPI } from '../../src/card-controller/types'; import type { Endpoint } from '../../src/types'; -import { createCardAPI, createViewWithMedia } from '../test-utils'; +import { createCardAPI } from '../test-utils'; +import { createViewWithMedia } from '../view/test-utils'; const createAPIWithMedia = (): CardCameraURLAPI => { const api = createCardAPI(); @@ -34,7 +35,7 @@ describe('CameraURLManager', () => { const windowSpy = vi.spyOn(window, 'open').mockReturnValue(null); manager.openURL(); - expect(windowSpy).toBeCalledWith('http://frigate'); + expect(windowSpy).toHaveBeenCalledWith('http://frigate'); }); it('should not get URL without view', () => { @@ -43,7 +44,7 @@ describe('CameraURLManager', () => { const windowSpy = vi.spyOn(window, 'open').mockReturnValue(null); manager.openURL(); - expect(windowSpy).not.toBeCalled(); + expect(windowSpy).not.toHaveBeenCalled(); }); it('should not get URL without cameraManager endpoints', () => { diff --git a/tests/card-controller/card-element-manager.test.ts b/tests/card-controller/card-element-manager.test.ts index 89a4fc57..21bd702f 100644 --- a/tests/card-controller/card-element-manager.test.ts +++ b/tests/card-controller/card-element-manager.test.ts @@ -5,15 +5,14 @@ import { CardElementManager } from '../../src/card-controller/card-element-manag import type { StateWatcher } from '../../src/card-controller/hass/state-watcher'; import { QueryResults } from '../../src/view/query-results'; import { View } from '../../src/view/view'; +import { createConfig } from '../config/test-utils'; import { callStateWatcherCallback, createCardAPI, createCardHTMLElement, - createConfig, createStateEntity, - createView, - TestViewMedia, } from '../test-utils'; +import { createView, TestViewMedia } from '../view/test-utils'; // @vitest-environment jsdom describe('CardElementManager', () => { @@ -62,7 +61,7 @@ describe('CardElementManager', () => { manager.scrollReset(); - expect(callback).toBeCalled(); + expect(callback).toHaveBeenCalled(); }); it('should toggle menu', () => { @@ -76,7 +75,7 @@ describe('CardElementManager', () => { manager.toggleMenu(); - expect(callback).toBeCalled(); + expect(callback).toHaveBeenCalled(); }); it('should update', () => { @@ -89,7 +88,7 @@ describe('CardElementManager', () => { ); manager.update(); - expect(element.requestUpdate).toBeCalled(); + expect(element.requestUpdate).toHaveBeenCalled(); }); it('should get hasUpdated', () => { @@ -124,45 +123,48 @@ describe('CardElementManager', () => { expect(element.getAttribute('panel')).toBeNull(); expect(element.getAttribute('casted')).toBeNull(); - expect(api.getFullscreenManager().connect).toBeCalled(); + expect(api.getFullscreenManager().connect).toHaveBeenCalled(); - expect(addEventListener).toBeCalledWith( + expect(addEventListener).toHaveBeenCalledWith( 'mousemove', api.getInteractionManager().reportInteraction, ); - expect(addEventListener).toBeCalledWith( + expect(addEventListener).toHaveBeenCalledWith( 'll-custom', api.getActionsManager().handleCustomActionEvent, ); - expect(addEventListener).toBeCalledWith( + expect(addEventListener).toHaveBeenCalledWith( 'action', api.getActionsManager().handleInteractionEvent, ); - expect(addEventListener).toBeCalledWith( + expect(addEventListener).toHaveBeenCalledWith( 'action', api.getInteractionManager().reportInteraction, ); - expect(addEventListener).toBeCalledWith( + expect(addEventListener).toHaveBeenCalledWith( 'touchstart', api.getInteractionManager().reportInteraction, ); - expect(addEventListener).toBeCalledWith( + expect(addEventListener).toHaveBeenCalledWith( 'touchmove', api.getInteractionManager().reportInteraction, ); - expect(windowAddEventListener).toBeCalledWith('location-changed', expect.anything()); - expect(windowAddEventListener).toBeCalledWith('popstate', expect.anything()); - expect(windowAddEventListener).toBeCalledWith( + expect(windowAddEventListener).toHaveBeenCalledWith( + 'location-changed', + expect.anything(), + ); + expect(windowAddEventListener).toHaveBeenCalledWith('popstate', expect.anything()); + expect(windowAddEventListener).toHaveBeenCalledWith( 'advanced-camera-card:editor:diagnostics', expect.anything(), ); - expect(api.getInteractionManager().initialize).toBeCalled(); - expect(api.getFullscreenManager().initialize).toBeCalled(); - expect(api.getExpandManager().initialize).toBeCalled(); - expect(api.getMediaLoadedInfoManager().initialize).toBeCalled(); - expect(api.getMicrophoneManager().initialize).toBeCalled(); - expect(api.getCallManager().initialize).toBeCalled(); + expect(api.getInteractionManager().initialize).toHaveBeenCalled(); + expect(api.getFullscreenManager().initialize).toHaveBeenCalled(); + expect(api.getExpandManager().initialize).toHaveBeenCalled(); + expect(api.getMediaLoadedInfoManager().initialize).toHaveBeenCalled(); + expect(api.getMicrophoneManager().initialize).toHaveBeenCalled(); + expect(api.getCallManager().initialize).toHaveBeenCalled(); }); it('should disconnect', () => { @@ -188,49 +190,52 @@ describe('CardElementManager', () => { expect(element.getAttribute('panel')).toBeNull(); expect(element.getAttribute('casted')).toBeNull(); - expect(api.getMediaLoadedInfoManager().clear).toBeCalled(); - expect(api.getFullscreenManager().disconnect).toBeCalled(); + expect(api.getMediaLoadedInfoManager().clear).toHaveBeenCalled(); + expect(api.getFullscreenManager().disconnect).toHaveBeenCalled(); - expect(removeEventListener).toBeCalledWith( + expect(removeEventListener).toHaveBeenCalledWith( 'mousemove', api.getInteractionManager().reportInteraction, ); - expect(removeEventListener).toBeCalledWith( + expect(removeEventListener).toHaveBeenCalledWith( 'll-custom', api.getActionsManager().handleCustomActionEvent, ); - expect(removeEventListener).toBeCalledWith( + expect(removeEventListener).toHaveBeenCalledWith( 'action', api.getActionsManager().handleInteractionEvent, ); - expect(removeEventListener).toBeCalledWith( + expect(removeEventListener).toHaveBeenCalledWith( 'action', api.getInteractionManager().reportInteraction, ); - expect(removeEventListener).toBeCalledWith( + expect(removeEventListener).toHaveBeenCalledWith( 'touchstart', api.getInteractionManager().reportInteraction, ); - expect(removeEventListener).toBeCalledWith( + expect(removeEventListener).toHaveBeenCalledWith( 'touchmove', api.getInteractionManager().reportInteraction, ); - expect(windowRemoveEventListener).toBeCalledWith( + expect(windowRemoveEventListener).toHaveBeenCalledWith( 'location-changed', expect.anything(), ); - expect(windowRemoveEventListener).toBeCalledWith('popstate', expect.anything()); - expect(windowRemoveEventListener).toBeCalledWith( + expect(windowRemoveEventListener).toHaveBeenCalledWith( + 'popstate', + expect.anything(), + ); + expect(windowRemoveEventListener).toHaveBeenCalledWith( 'advanced-camera-card:editor:diagnostics', expect.anything(), ); - expect(api.getMediaLoadedInfoManager().clear).toBeCalled(); - expect(api.getFullscreenManager().disconnect).toBeCalled(); - expect(api.getKeyboardStateManager().uninitialize).toBeCalled(); - expect(api.getActionsManager().uninitialize).toBeCalled(); - expect(api.getCallManager().uninitialize).toBeCalled(); - expect(api.getInitializationManager().uninitialize).toBeCalledWith('cameras'); + expect(api.getMediaLoadedInfoManager().clear).toHaveBeenCalled(); + expect(api.getFullscreenManager().disconnect).toHaveBeenCalled(); + expect(api.getKeyboardStateManager().uninitialize).toHaveBeenCalled(); + expect(api.getActionsManager().uninitialize).toHaveBeenCalled(); + expect(api.getCallManager().uninitialize).toHaveBeenCalled(); + expect(api.getInitializationManager().uninitialize).toHaveBeenCalledWith('cameras'); }); describe('should update card when', () => { @@ -263,7 +268,7 @@ describe('CardElementManager', () => { }; callStateWatcherCallback(stateWatcher, diff); - expect(element.requestUpdate).toBeCalled(); + expect(element.requestUpdate).toHaveBeenCalled(); }); it('media player entity changes', () => { @@ -291,7 +296,7 @@ describe('CardElementManager', () => { }; callStateWatcherCallback(stateWatcher, diff); - expect(element.requestUpdate).toBeCalled(); + expect(element.requestUpdate).toHaveBeenCalled(); }); it('selected media review status changes', () => { @@ -325,7 +330,7 @@ describe('CardElementManager', () => { }), ); - expect(element.requestUpdate).toBeCalled(); + expect(element.requestUpdate).toHaveBeenCalled(); }); it('non-selected media review status changes does not update', () => { @@ -361,7 +366,7 @@ describe('CardElementManager', () => { ); // Should NOT update because the reviewed item is not the selected item. - expect(element.requestUpdate).not.toBeCalled(); + expect(element.requestUpdate).not.toHaveBeenCalled(); }); }); @@ -406,7 +411,7 @@ describe('CardElementManager', () => { fireFromDialog(dialog); - expect(api.getViewManager().setViewByParameters).toBeCalledWith({ + expect(api.getViewManager().setViewByParameters).toHaveBeenCalledWith({ params: { view: 'diagnostics' }, }); }); @@ -431,7 +436,7 @@ describe('CardElementManager', () => { fireFromDialog(dialog); - expect(api.getViewManager().setViewDefault).toBeCalled(); + expect(api.getViewManager().setViewDefault).toHaveBeenCalled(); }); it('does not set view to diagnostics if card is not in editor', () => { @@ -452,7 +457,7 @@ describe('CardElementManager', () => { document.body.append(otherDialog); fireFromDialog(otherDialog); - expect(api.getViewManager().setViewByParameters).not.toBeCalled(); + expect(api.getViewManager().setViewByParameters).not.toHaveBeenCalled(); }); }); }); diff --git a/tests/card-controller/config/config-manager.test.ts b/tests/card-controller/config/config-manager.test.ts index 43904c50..937ec6ae 100644 --- a/tests/card-controller/config/config-manager.test.ts +++ b/tests/card-controller/config/config-manager.test.ts @@ -11,9 +11,9 @@ import type { Automation } from '../../../src/config/schema/automations'; import type { Trigger } from '../../../src/config/schema/condition-trigger/triggers/types'; import { advancedCameraCardConfigSchema } from '../../../src/config/schema/types'; import { createGeneralAction } from '../../../src/utils/action'; +import { createConfig } from '../../config/test-utils'; import { createCardAPI, - createConfig, createHASS, createStateEntity, flushPromises, @@ -122,7 +122,7 @@ describe('ConfigManager', () => { describe('should handle error when', () => { it('should handle no input', () => { const manager = new ConfigManager(createCardAPI()); - expect(() => manager.setConfig()).toThrowError(/Invalid configuration/); + expect(() => manager.setConfig()).toThrow(/Invalid configuration/); }); it('should handle invalid configuration', () => { @@ -132,7 +132,7 @@ describe('ConfigManager', () => { .mockReturnValue({ success: false, error: new ZodError([]) }); const manager = new ConfigManager(createCardAPI()); - expect(() => manager.setConfig({})).toThrowError( + expect(() => manager.setConfig({})).toThrow( 'Invalid configuration: No location hint available (bad or missing type?)', ); @@ -141,7 +141,7 @@ describe('ConfigManager', () => { it('should handle invalid configuration with hint', () => { const manager = new ConfigManager(createCardAPI()); - expect(() => manager.setConfig({})).toThrowError( + expect(() => manager.setConfig({})).toThrow( 'Invalid configuration: [\n "type"\n]', ); }); @@ -154,7 +154,7 @@ describe('ConfigManager', () => { type: 'custom:frigate-card', cameras: 'WILL_NOT_PARSE', }), - ).toThrowError( + ).toThrow( 'An automated card configuration upgrade is ' + 'available, please visit the visual card editor. ' + 'Invalid configuration: [\n "cameras"\n]', @@ -190,17 +190,17 @@ describe('ConfigManager', () => { expect(manager.getConfig()?.menu.alignment).toBe('left'); // Verify appropriate API calls are made. - expect(api.getConditionStateManager().setState).toBeCalledWith({ + expect(api.getConditionStateManager().setState).toHaveBeenCalledWith({ view: undefined, displayMode: undefined, camera: undefined, }); - expect(api.getIssueManager().reset).toBeCalledWith('config_error'); - expect(api.getMediaLoadedInfoManager().clear).toBeCalled(); - expect(api.getViewManager().reset).toBeCalled(); - expect(api.getAutomationsManager().addAutomations).toBeCalled(); - expect(api.getStyleManager().updateFromConfig).toBeCalled(); - expect(api.getCardElementManager().update).toBeCalled(); + expect(api.getIssueManager().reset).toHaveBeenCalledWith('config_error'); + expect(api.getMediaLoadedInfoManager().clear).toHaveBeenCalled(); + expect(api.getViewManager().reset).toHaveBeenCalled(); + expect(api.getAutomationsManager().addAutomations).toHaveBeenCalled(); + expect(api.getStyleManager().updateFromConfig).toHaveBeenCalled(); + expect(api.getCardElementManager().update).toHaveBeenCalled(); }); it('should apply profiles', () => { @@ -243,12 +243,12 @@ describe('ConfigManager', () => { }; manager.setConfig(config); - expect(api.getViewManager().reset).toBeCalled(); + expect(api.getViewManager().reset).toHaveBeenCalled(); vi.mocked(api.getViewManager().reset).mockClear(); manager.setConfig(config); - expect(api.getViewManager().reset).not.toBeCalled(); + expect(api.getViewManager().reset).not.toHaveBeenCalled(); }); it('should get card wide config', () => { @@ -321,7 +321,7 @@ describe('ConfigManager', () => { const configAfter = manager.getConfig(); expect(configAfter).toEqual(configBefore); - expect(api.getStyleManager().updateFromConfig).toBeCalledTimes(1); + expect(api.getStyleManager().updateFromConfig).toHaveBeenCalledTimes(1); }); it('should honor override', () => { @@ -372,7 +372,7 @@ describe('ConfigManager', () => { stateManager.setState({ fullscreen: true }); expect(manager.getConfig()).not.toBeNull(); - expect(api.getIssueManager().trigger).toBeCalledWith( + expect(api.getIssueManager().trigger).toHaveBeenCalledWith( 'config_error', expect.objectContaining({ error: expect.any(Error) }), ); @@ -502,9 +502,11 @@ describe('ConfigManager', () => { await flushPromises(); - expect(api.getDefaultManager().initializeIfNecessary).toBeCalledTimes(1); - expect(api.getMediaPlayerManager().initializeIfNecessary).toBeCalledTimes(1); - expect(listener).not.toBeCalledWith( + expect(api.getDefaultManager().initializeIfNecessary).toHaveBeenCalledTimes(1); + expect(api.getMediaPlayerManager().initializeIfNecessary).toHaveBeenCalledTimes( + 1, + ); + expect(listener).not.toHaveBeenCalledWith( expect.objectContaining({ change: { config: expect.anything() } }), ); @@ -515,11 +517,13 @@ describe('ConfigManager', () => { await flushPromises(); - expect(api.getDefaultManager().initializeIfNecessary).toBeCalledTimes(2); - expect(api.getMediaPlayerManager().initializeIfNecessary).toBeCalledTimes(2); + expect(api.getDefaultManager().initializeIfNecessary).toHaveBeenCalledTimes(2); + expect(api.getMediaPlayerManager().initializeIfNecessary).toHaveBeenCalledTimes( + 2, + ); // Should set the config condition state. - expect(listener).toBeCalledWith( + expect(listener).toHaveBeenCalledWith( expect.objectContaining({ change: { config: expect.anything() } }), ); }); @@ -616,7 +620,7 @@ describe('ConfigManager', () => { await flushPromises(); // Verify delete was called when override triggered - expect(api.getFoldersManager().deleteFolders).toBeCalled(); + expect(api.getFoldersManager().deleteFolders).toHaveBeenCalled(); // Verify folder 'f' is no longer present after override // Since the override removes folders, the folders passed should no diff --git a/tests/card-controller/config/load-automations.test.ts b/tests/card-controller/config/load-automations.test.ts index 2ab4b359..cdf3e89a 100644 --- a/tests/card-controller/config/load-automations.test.ts +++ b/tests/card-controller/config/load-automations.test.ts @@ -1,15 +1,16 @@ import { describe, expect, it, vi } from 'vitest'; import { setAutomationsFromConfig } from '../../../src/card-controller/config/load-automations'; -import { createCardAPI, createConfig } from '../../test-utils'; +import { createConfig } from '../../config/test-utils'; +import { createCardAPI } from '../../test-utils'; describe('setAutomationsFromConfig', () => { it('without config', () => { const api = createCardAPI(); setAutomationsFromConfig(api); - expect(api.getAutomationsManager().deleteAutomations).toBeCalled(); - expect(api.getAutomationsManager().addAutomations).toBeCalledWith([]); + expect(api.getAutomationsManager().deleteAutomations).toHaveBeenCalled(); + expect(api.getAutomationsManager().addAutomations).toHaveBeenCalledWith([]); }); it('with config', () => { @@ -33,7 +34,7 @@ describe('setAutomationsFromConfig', () => { setAutomationsFromConfig(api); - expect(api.getAutomationsManager().deleteAutomations).toBeCalled(); - expect(api.getAutomationsManager().addAutomations).toBeCalledWith(automations); + expect(api.getAutomationsManager().deleteAutomations).toHaveBeenCalled(); + expect(api.getAutomationsManager().addAutomations).toHaveBeenCalledWith(automations); }); }); diff --git a/tests/card-controller/config/load-control-entities.test.ts b/tests/card-controller/config/load-control-entities.test.ts index 83a06ec7..35029692 100644 --- a/tests/card-controller/config/load-control-entities.test.ts +++ b/tests/card-controller/config/load-control-entities.test.ts @@ -19,14 +19,10 @@ import { isAdvancedCameraCardCustomAction, } from '../../../src/utils/action'; import { arrayify } from '../../../src/utils/basic'; -import { - createCardAPI, - createConfig, - createHASS, - createStateEntity, - createStore, - createView, -} from '../../test-utils'; +import { createStore } from '../../camera-manager/test-utils'; +import { createConfig } from '../../config/test-utils'; +import { createCardAPI, createHASS, createStateEntity } from '../../test-utils'; +import { createView } from '../../view/test-utils'; const isGeneratedAction = (action: ActionConfig): action is GeneratedActionConfig => 'advanced_camera_card_action' in action && @@ -37,8 +33,8 @@ describe('setRemoteControlEntityFromConfig', () => { const api = createCardAPI(); setRemoteControlEntityFromConfig(api); - expect(api.getAutomationsManager().deleteAutomations).toBeCalled(); - expect(api.getAutomationsManager().addAutomations).not.toBeCalled(); + expect(api.getAutomationsManager().deleteAutomations).toHaveBeenCalled(); + expect(api.getAutomationsManager().addAutomations).not.toHaveBeenCalled(); }); it('with control entity and card priority', () => { @@ -56,8 +52,8 @@ describe('setRemoteControlEntityFromConfig', () => { setRemoteControlEntityFromConfig(api); - expect(api.getAutomationsManager().deleteAutomations).toBeCalled(); - expect(api.getAutomationsManager().addAutomations).toBeCalledWith([ + expect(api.getAutomationsManager().deleteAutomations).toHaveBeenCalled(); + expect(api.getAutomationsManager().addAutomations).toHaveBeenCalledWith([ { actions: [ { @@ -138,8 +134,8 @@ describe('setRemoteControlEntityFromConfig', () => { setRemoteControlEntityFromConfig(api); - expect(api.getAutomationsManager().deleteAutomations).toBeCalled(); - expect(api.getAutomationsManager().addAutomations).toBeCalledWith([ + expect(api.getAutomationsManager().deleteAutomations).toHaveBeenCalled(); + expect(api.getAutomationsManager().addAutomations).toHaveBeenCalledWith([ { actions: [ { @@ -240,7 +236,7 @@ describe('setRemoteControlEntityFromConfig', () => { ); addOptionsAction.callback(api); - expect(hass.callService).toBeCalledWith( + expect(hass.callService).toHaveBeenCalledWith( 'input_select', 'set_options', { @@ -290,7 +286,7 @@ describe('setRemoteControlEntityFromConfig', () => { ); addOptionsAction.callback(api); - expect(hass.callService).not.toBeCalled(); + expect(hass.callService).not.toHaveBeenCalled(); }); it('should not throw when hass is undefined setting options', () => { @@ -371,7 +367,7 @@ describe('setRemoteControlEntityFromConfig', () => { ); await cameraSyncAction.callback(api); - expect(hass.callService).toBeCalledWith( + expect(hass.callService).toHaveBeenCalledWith( 'input_select', 'select_option', { @@ -425,7 +421,7 @@ describe('setRemoteControlEntityFromConfig', () => { cameraSyncAction.callback(api); // Should NOT call select_option since entity already shows camera.one - expect(hass.callService).not.toBeCalled(); + expect(hass.callService).not.toHaveBeenCalled(); }); it('should not select option when camera is undefined', () => { @@ -462,7 +458,7 @@ describe('setRemoteControlEntityFromConfig', () => { cameraSyncAction.callback(api); // Should NOT call select_option since camera is undefined - expect(hass.callService).not.toBeCalled(); + expect(hass.callService).not.toHaveBeenCalled(); }); it('should not select option when view exists but has no camera', () => { @@ -496,13 +492,13 @@ describe('setRemoteControlEntityFromConfig', () => { const cameraSyncAction = vi.mocked(api.getAutomationsManager().addAutomations).mock .calls[0][0][1].actions?.[0] as InternalCallbackActionConfig; cameraSyncAction.callback(api); - expect(hass.callService).not.toBeCalled(); + expect(hass.callService).not.toHaveBeenCalled(); // Also test the 'initialized' condition callback (automation index 2) const initializedSyncAction = vi.mocked(api.getAutomationsManager().addAutomations) .mock.calls[0][0][2].actions?.[0] as InternalCallbackActionConfig; initializedSyncAction.callback(api); - expect(hass.callService).not.toBeCalled(); + expect(hass.callService).not.toHaveBeenCalled(); }); it('should not throw when hass is undefined', async () => { @@ -574,7 +570,7 @@ describe('setRemoteControlEntityFromConfig', () => { ); await cameraSyncAction.callback(api); - expect(hass.callService).toBeCalledWith( + expect(hass.callService).toHaveBeenCalledWith( 'input_select', 'select_option', { @@ -624,7 +620,7 @@ describe('setRemoteControlEntityFromConfig', () => { expect(initAction.advanced_camera_card_action).toBe(INTERNAL_CALLBACK_ACTION); await initAction.callback(api); - expect(hass.callService).toBeCalledWith( + expect(hass.callService).toHaveBeenCalledWith( 'input_select', 'select_option', { diff --git a/tests/card-controller/config/load-folders.test.ts b/tests/card-controller/config/load-folders.test.ts index 6d5ec312..7bfe4f7b 100644 --- a/tests/card-controller/config/load-folders.test.ts +++ b/tests/card-controller/config/load-folders.test.ts @@ -3,7 +3,8 @@ import { mock } from 'vitest-mock-extended'; import { setFoldersFromConfig } from '../../../src/card-controller/config/load-folders'; import type { FoldersManager } from '../../../src/card-controller/folders/manager'; -import { createCardAPI, createConfig, createFolder } from '../../test-utils'; +import { createConfig } from '../../config/test-utils'; +import { createCardAPI, createFolder } from '../../test-utils'; describe('setFoldersFromConfig', () => { it('should replace folders', () => { @@ -19,8 +20,8 @@ describe('setFoldersFromConfig', () => { setFoldersFromConfig(api); - expect(foldersManager.deleteFolders).toBeCalled(); - expect(foldersManager.addFolders).toBeCalledWith(folders); + expect(foldersManager.deleteFolders).toHaveBeenCalled(); + expect(foldersManager.addFolders).toHaveBeenCalledWith(folders); }); it('should handle exceptions', () => { @@ -40,7 +41,7 @@ describe('setFoldersFromConfig', () => { setFoldersFromConfig(api); - expect(api.getIssueManager().trigger).toBeCalledWith('config_error', { + expect(api.getIssueManager().trigger).toHaveBeenCalledWith('config_error', { error, }); }); diff --git a/tests/card-controller/config/load-keyboard-shortcuts.test.ts b/tests/card-controller/config/load-keyboard-shortcuts.test.ts index 0b0c409a..0e9a8c42 100644 --- a/tests/card-controller/config/load-keyboard-shortcuts.test.ts +++ b/tests/card-controller/config/load-keyboard-shortcuts.test.ts @@ -3,17 +3,18 @@ import { describe, expect, it, vi } from 'vitest'; import { setKeyboardShortcutsFromConfig } from '../../../src/card-controller/config/load-keyboard-shortcuts'; import type { PTZAction } from '../../../src/config/schema/actions/custom/ptz'; import type { PTZKeyboardShortcutName } from '../../../src/config/schema/view'; -import { createCardAPI, createConfig } from '../../test-utils'; +import { createConfig } from '../../config/test-utils'; +import { createCardAPI } from '../../test-utils'; describe('setKeyboardShortcutsFromConfig', () => { it('without shortcuts', () => { const api = createCardAPI(); setKeyboardShortcutsFromConfig(api); - expect(api.getAutomationsManager().deleteAutomations).toBeCalledWith( + expect(api.getAutomationsManager().deleteAutomations).toHaveBeenCalledWith( setKeyboardShortcutsFromConfig, ); - expect(api.getAutomationsManager().addAutomations).not.toBeCalled(); + expect(api.getAutomationsManager().addAutomations).not.toHaveBeenCalled(); }); it('with shortcuts disabled', () => { @@ -29,10 +30,10 @@ describe('setKeyboardShortcutsFromConfig', () => { ); setKeyboardShortcutsFromConfig(api); - expect(api.getAutomationsManager().deleteAutomations).toBeCalledWith( + expect(api.getAutomationsManager().deleteAutomations).toHaveBeenCalledWith( setKeyboardShortcutsFromConfig, ); - expect(api.getAutomationsManager().addAutomations).not.toBeCalled(); + expect(api.getAutomationsManager().addAutomations).not.toHaveBeenCalled(); }); describe('PTZ shortcuts', () => { @@ -66,10 +67,10 @@ describe('setKeyboardShortcutsFromConfig', () => { setKeyboardShortcutsFromConfig(api); - expect(api.getAutomationsManager().deleteAutomations).toBeCalledWith( + expect(api.getAutomationsManager().deleteAutomations).toHaveBeenCalledWith( setKeyboardShortcutsFromConfig, ); - expect(api.getAutomationsManager().addAutomations).toBeCalledWith([ + expect(api.getAutomationsManager().addAutomations).toHaveBeenCalledWith([ { actions: [ { @@ -119,10 +120,10 @@ describe('setKeyboardShortcutsFromConfig', () => { setKeyboardShortcutsFromConfig(api); - expect(api.getAutomationsManager().deleteAutomations).toBeCalledWith( + expect(api.getAutomationsManager().deleteAutomations).toHaveBeenCalledWith( setKeyboardShortcutsFromConfig, ); - expect(api.getAutomationsManager().addAutomations).toBeCalledWith( + expect(api.getAutomationsManager().addAutomations).toHaveBeenCalledWith( expect.arrayContaining([ { actions: [ diff --git a/tests/card-controller/config/overrides-manager.test.ts b/tests/card-controller/config/overrides-manager.test.ts index 5ae8bbd3..698eaf28 100644 --- a/tests/card-controller/config/overrides-manager.test.ts +++ b/tests/card-controller/config/overrides-manager.test.ts @@ -4,7 +4,8 @@ import { OverridesManager } from '../../../src/card-controller/config/overrides- import { ConditionStateManager } from '../../../src/condition-trigger/conditions/state-manager'; import type { AdvancedCameraCardConfig } from '../../../src/config/schema/types'; import { AdvancedCameraCardError } from '../../../src/types'; -import { createConfig, createMockTemplateRenderer } from '../../test-utils'; +import { createConfig } from '../../config/test-utils'; +import { createMockTemplateRenderer } from '../../test-utils'; describe('OverridesManager', () => { const templateManager = createMockTemplateRenderer(); @@ -110,11 +111,11 @@ describe('OverridesManager', () => { expect(manager.getConfig(config).menu?.style).toBe('hidden'); - expect(callback).not.toBeCalled(); + expect(callback).not.toHaveBeenCalled(); stateManager.setState({ fullscreen: true }); - expect(callback).toBeCalledTimes(1); + expect(callback).toHaveBeenCalledTimes(1); }); describe('should handle override merge', () => { diff --git a/tests/card-controller/controller.test.ts b/tests/card-controller/controller.test.ts index 39079ec0..cab150f6 100644 --- a/tests/card-controller/controller.test.ts +++ b/tests/card-controller/controller.test.ts @@ -113,7 +113,7 @@ describe('CardController', () => { const controller = new CardController(element, scrollCallback, menuToggleCallback); - expect(CardElementManager).toBeCalledWith( + expect(CardElementManager).toHaveBeenCalledWith( controller, element, scrollCallback, @@ -137,7 +137,9 @@ describe('CardController', () => { const csmListener = calls[0][0]; const hass = {} as Parameters[0]; csmListener(hass, null); - expect(vi.mocked(ConditionStateManager).mock.instances[0].setState).toBeCalledWith({ + expect( + vi.mocked(ConditionStateManager).mock.instances[0].setState, + ).toHaveBeenCalledWith({ hass, }); }); @@ -372,14 +374,14 @@ describe('CardController', () => { createController().hostConnected(); expect( vi.mocked(CardElementManager).mock.instances[0].elementConnected, - ).toBeCalled(); + ).toHaveBeenCalled(); }); it('should handle hostDisconnected', () => { createController().hostDisconnected(); expect( vi.mocked(CardElementManager).mock.instances[0].elementDisconnected, - ).toBeCalled(); + ).toHaveBeenCalled(); }); }); }); diff --git a/tests/card-controller/default-manager.test.ts b/tests/card-controller/default-manager.test.ts index 5e324c6c..792dfb09 100644 --- a/tests/card-controller/default-manager.test.ts +++ b/tests/card-controller/default-manager.test.ts @@ -4,10 +4,10 @@ import { mock } from 'vitest-mock-extended'; import type { CardController } from '../../src/card-controller/controller'; import { DefaultManager } from '../../src/card-controller/default-manager'; import type { StateWatcherSubscriptionInterface } from '../../src/card-controller/hass/state-watcher'; +import { createConfig } from '../config/test-utils'; import { callStateWatcherCallback, createCardAPI, - createConfig, createHASS, createStateEntity, } from '../test-utils'; @@ -46,21 +46,21 @@ describe('DefaultManager', () => { const manager = new DefaultManager(api); await manager.initialize(); - expect(api.getViewManager().setViewDefault).not.toBeCalled(); + expect(api.getViewManager().setViewDefault).not.toHaveBeenCalled(); vi.runOnlyPendingTimers(); - expect(api.getViewManager().setViewDefault).not.toBeCalled(); + expect(api.getViewManager().setViewDefault).not.toHaveBeenCalled(); vi.mocked(api.getInteractionManager().hasInteraction).mockReturnValue(false); vi.runOnlyPendingTimers(); - expect(api.getViewManager().setViewDefault).toBeCalledTimes(1); + expect(api.getViewManager().setViewDefault).toHaveBeenCalledTimes(1); manager.uninitialize(); vi.runOnlyPendingTimers(); - expect(api.getViewManager().setViewDefault).toBeCalledTimes(1); + expect(api.getViewManager().setViewDefault).toHaveBeenCalledTimes(1); }); it('should not set default view when not configured', () => { @@ -82,11 +82,11 @@ describe('DefaultManager', () => { const manager = new DefaultManager(api); manager.initialize(); - expect(api.getViewManager().setViewDefault).not.toBeCalled(); + expect(api.getViewManager().setViewDefault).not.toHaveBeenCalled(); vi.runOnlyPendingTimers(); - expect(api.getViewManager().setViewDefault).not.toBeCalled(); + expect(api.getViewManager().setViewDefault).not.toHaveBeenCalled(); }); it('should restart timer when reconfigured', async () => { @@ -108,14 +108,14 @@ describe('DefaultManager', () => { const manager = new DefaultManager(api); await manager.initialize(); - expect(api.getViewManager().setViewDefault).not.toBeCalled(); + expect(api.getViewManager().setViewDefault).not.toHaveBeenCalled(); await manager.initialize(); - expect(api.getViewManager().setViewDefault).not.toBeCalled(); + expect(api.getViewManager().setViewDefault).not.toHaveBeenCalled(); vi.runOnlyPendingTimers(); - expect(api.getViewManager().setViewDefault).toBeCalled(); + expect(api.getViewManager().setViewDefault).toHaveBeenCalled(); }); }); @@ -146,7 +146,7 @@ describe('DefaultManager', () => { newState: createStateEntity({ state: 'on' }), }); - expect(api.getViewManager().setViewDefault).toBeCalled(); + expect(api.getViewManager().setViewDefault).toHaveBeenCalled(); }); describe('interaction based', () => { @@ -166,7 +166,7 @@ describe('DefaultManager', () => { const manager = new DefaultManager(api); await manager.initialize(); - expect(api.getAutomationsManager().addAutomations).not.toBeCalled(); + expect(api.getAutomationsManager().addAutomations).not.toHaveBeenCalled(); }); it('should register automation on initialization', async () => { @@ -185,7 +185,7 @@ describe('DefaultManager', () => { const manager = new DefaultManager(api); await manager.initialize(); - expect(api.getAutomationsManager().addAutomations).toBeCalledWith([ + expect(api.getAutomationsManager().addAutomations).toHaveBeenCalledWith([ { actions: [ { @@ -209,7 +209,9 @@ describe('DefaultManager', () => { const manager = new DefaultManager(api); manager.uninitialize(); - expect(api.getAutomationsManager().deleteAutomations).toBeCalledWith(manager); + expect(api.getAutomationsManager().deleteAutomations).toHaveBeenCalledWith( + manager, + ); }); }); @@ -241,18 +243,18 @@ describe('DefaultManager', () => { await manager.initializeIfNecessary(null); vi.runOnlyPendingTimers(); - expect(api.getViewManager().setViewDefault).toBeCalledTimes(1); + expect(api.getViewManager().setViewDefault).toHaveBeenCalledTimes(1); vi.mocked(api.getConfigManager().getConfig).mockReturnValue(configOff); await manager.initializeIfNecessary(configOn); vi.runOnlyPendingTimers(); - expect(api.getViewManager().setViewDefault).toBeCalledTimes(1); + expect(api.getViewManager().setViewDefault).toHaveBeenCalledTimes(1); vi.mocked(api.getConfigManager().getConfig).mockReturnValue(configOff); await manager.initializeIfNecessary(configOff); vi.runOnlyPendingTimers(); - expect(api.getViewManager().setViewDefault).toBeCalledTimes(1); + expect(api.getViewManager().setViewDefault).toHaveBeenCalledTimes(1); }); }); diff --git a/tests/card-controller/expand-manager.test.ts b/tests/card-controller/expand-manager.test.ts index 9813d389..77c6b017 100644 --- a/tests/card-controller/expand-manager.test.ts +++ b/tests/card-controller/expand-manager.test.ts @@ -16,7 +16,9 @@ describe('ExpandManager', () => { const manager = new ExpandManager(api); manager.initialize(); - expect(api.getConditionStateManager().setState).toBeCalledWith({ expand: false }); + expect(api.getConditionStateManager().setState).toHaveBeenCalledWith({ + expand: false, + }); }); it('should set expanded', () => { @@ -29,9 +31,11 @@ describe('ExpandManager', () => { manager.setExpanded(true); expect(manager.isExpanded()).toBeTruthy(); - expect(api.getFullscreenManager().setFullscreen).toBeCalledWith(false); - expect(api.getConditionStateManager().setState).toBeCalledWith({ expand: true }); - expect(api.getCardElementManager().update).toBeCalled(); + expect(api.getFullscreenManager().setFullscreen).toHaveBeenCalledWith(false); + expect(api.getConditionStateManager().setState).toHaveBeenCalledWith({ + expand: true, + }); + expect(api.getCardElementManager().update).toHaveBeenCalled(); expect(element.hasAttribute('expanded')).toBeTruthy(); }); @@ -44,7 +48,7 @@ describe('ExpandManager', () => { manager.setExpanded(true); - expect(api.getFullscreenManager().setFullscreen).not.toBeCalled(); + expect(api.getFullscreenManager().setFullscreen).not.toHaveBeenCalled(); }); it('should toggle expanded', () => { diff --git a/tests/card-controller/folders/executor.test.ts b/tests/card-controller/folders/executor.test.ts index c14a5b11..7d435225 100644 --- a/tests/card-controller/folders/executor.test.ts +++ b/tests/card-controller/folders/executor.test.ts @@ -9,7 +9,8 @@ import type { FolderConfig } from '../../../src/config/schema/folders'; import { QuerySource } from '../../../src/query-source'; import type { Endpoint } from '../../../src/types'; import { ViewFolder } from '../../../src/view/item'; -import { createFolder, createHASS, TestViewMedia } from '../../test-utils'; +import { createFolder, createHASS } from '../../test-utils'; +import { TestViewMedia } from '../../view/test-utils'; vi.mock('../../../src/card-controller/folders/ha/engine'); vi.mock('../../../../src/utils/ha/download'); @@ -134,7 +135,7 @@ describe('FoldersExecutor', () => { await executor.favorite(hass, item, true); - expect(haFolderEngine.favorite).toBeCalledWith(hass, item, true); + expect(haFolderEngine.favorite).toHaveBeenCalledWith(hass, item, true); }); }); @@ -153,7 +154,10 @@ describe('FoldersExecutor', () => { const executor = new FoldersExecutor(templateManager, { ha: haFolderEngine }); expect(executor.generateChildFolderQuery(query, viewFolder)).toEqual(query); - expect(haFolderEngine.generateChildFolderQuery).toBeCalledWith(query, viewFolder); + expect(haFolderEngine.generateChildFolderQuery).toHaveBeenCalledWith( + query, + viewFolder, + ); }); it('should return null for non-existent folder engine', () => { diff --git a/tests/card-controller/folders/ha/engine.test.ts b/tests/card-controller/folders/ha/engine.test.ts index 1b4e15e8..231bada0 100644 --- a/tests/card-controller/folders/ha/engine.test.ts +++ b/tests/card-controller/folders/ha/engine.test.ts @@ -11,12 +11,8 @@ import { homeAssistantWSRequest } from '../../../../src/ha/ws-request'; import { QuerySource } from '../../../../src/query-source'; import type { Endpoint } from '../../../../src/types'; import { ViewFolder, ViewMedia } from '../../../../src/view/item'; -import { - createBrowseMedia, - createFolder, - createHASS, - TestViewMedia, -} from '../../../test-utils'; +import { createBrowseMedia, createFolder, createHASS } from '../../../test-utils'; +import { TestViewMedia } from '../../../view/test-utils'; vi.mock('../../../../src/ha/download'); vi.mock('../../../../src/ha/ws-request'); @@ -143,11 +139,11 @@ describe('HAFoldersEngine', () => { expect(results?.[0]).toBeInstanceOf(ViewMedia); expect(results?.[1]).toBeInstanceOf(ViewFolder); - expect(homeAssistantWSRequest).toBeCalledTimes(1); + expect(homeAssistantWSRequest).toHaveBeenCalledTimes(1); // Expanding the folder again should use the cache. await engine.expandFolder(createHASS(), query); - expect(homeAssistantWSRequest).toBeCalledTimes(1); + expect(homeAssistantWSRequest).toHaveBeenCalledTimes(1); }); it('should expand folder without cache when requested', async () => { @@ -190,11 +186,11 @@ describe('HAFoldersEngine', () => { expect(results?.[0]).toBeInstanceOf(ViewMedia); expect(results?.[1]).toBeInstanceOf(ViewFolder); - expect(homeAssistantWSRequest).toBeCalledTimes(1); + expect(homeAssistantWSRequest).toHaveBeenCalledTimes(1); // Expanding the folder again should use the cache. await engine.expandFolder(createHASS(), query); - expect(homeAssistantWSRequest).toBeCalledTimes(2); + expect(homeAssistantWSRequest).toHaveBeenCalledTimes(2); }); it('should use id from browsemedia in folder in query', async () => { @@ -225,7 +221,7 @@ describe('HAFoldersEngine', () => { const engine = new HAFoldersEngine(templateManager); await engine.expandFolder(hass, query); - expect(homeAssistantWSRequest).toBeCalledWith(hass, browseMediaSchema, { + expect(homeAssistantWSRequest).toHaveBeenCalledWith(hass, browseMediaSchema, { type: 'media_source/browse_media', media_content_id: 'media-source://id', }); diff --git a/tests/card-controller/folders/manager.test.ts b/tests/card-controller/folders/manager.test.ts index 99b0174c..21bdfd9d 100644 --- a/tests/card-controller/folders/manager.test.ts +++ b/tests/card-controller/folders/manager.test.ts @@ -13,12 +13,8 @@ import { QuerySource } from '../../../src/query-source'; import type { Endpoint } from '../../../src/types'; import { ViewFolder } from '../../../src/view/item'; import type { ViewItemCapabilities } from '../../../src/view/types'; -import { - createCardAPI, - createFolder, - createHASS, - TestViewMedia, -} from '../../test-utils'; +import { createCardAPI, createFolder, createHASS } from '../../test-utils'; +import { TestViewMedia } from '../../view/test-utils'; describe('FoldersManager', () => { it('should initialize with no folders', () => { @@ -74,7 +70,7 @@ describe('FoldersManager', () => { const folder_1 = createFolder({ id: 'DUP' }); const folder_2 = createFolder({ id: 'DUP' }); - expect(() => manager.addFolders([folder_1, folder_2])).toThrowError( + expect(() => manager.addFolders([folder_1, folder_2])).toThrow( /Duplicate folder id/, ); }); @@ -119,7 +115,7 @@ describe('FoldersManager', () => { executor.getDefaultQueryParameters.mockReturnValue(query); expect(manager.getDefaultQueryParameters(folder)).toEqual(query); - expect(executor.getDefaultQueryParameters).toBeCalledWith(folder); + expect(executor.getDefaultQueryParameters).toHaveBeenCalledWith(folder); }); it('should fallback to default folder if none provided', () => { @@ -133,7 +129,9 @@ describe('FoldersManager', () => { executor.getDefaultQueryParameters.mockReturnValue(query); expect(manager.getDefaultQueryParameters()).toEqual(query); - expect(executor.getDefaultQueryParameters).toBeCalledWith(manager.getFolder()); + expect(executor.getDefaultQueryParameters).toHaveBeenCalledWith( + manager.getFolder(), + ); }); }); @@ -186,7 +184,7 @@ describe('FoldersManager', () => { const manager = new FoldersManager(createCardAPI(), executor); expect(manager.generateChildFolderQuery(query, viewFolder)).toEqual(query); - expect(executor.generateChildFolderQuery).toBeCalledWith(query, viewFolder); + expect(executor.generateChildFolderQuery).toHaveBeenCalledWith(query, viewFolder); }); }); @@ -215,7 +213,7 @@ describe('FoldersManager', () => { media, ]); - expect(executor.expandFolder).toBeCalledWith( + expect(executor.expandFolder).toHaveBeenCalledWith( hass, query, conditionState, @@ -237,7 +235,7 @@ describe('FoldersManager', () => { }), ).toBeNull(); - expect(executor.expandFolder).not.toBeCalled(); + expect(executor.expandFolder).not.toHaveBeenCalled(); }); it('should return null for query with unsupported filters', async () => { @@ -257,7 +255,7 @@ describe('FoldersManager', () => { }; expect(await manager.expandFolder(query)).toBeNull(); - expect(executor.expandFolder).not.toBeCalled(); + expect(executor.expandFolder).not.toHaveBeenCalled(); }); }); @@ -275,7 +273,7 @@ describe('FoldersManager', () => { const manager = new FoldersManager(api, executor); expect(manager.getItemCapabilities(item)).toEqual(capabilities); - expect(executor.getItemCapabilities).toBeCalledWith(item); + expect(executor.getItemCapabilities).toHaveBeenCalledWith(item); }); }); @@ -294,7 +292,7 @@ describe('FoldersManager', () => { const manager = new FoldersManager(api, executor); expect(await manager.getDownloadPath(item)).toEqual(endpoint); - expect(executor.getDownloadPath).toBeCalledWith(hass, item, { + expect(executor.getDownloadPath).toHaveBeenCalledWith(hass, item, { resolvedMediaCache: cache, }); }); @@ -311,7 +309,7 @@ describe('FoldersManager', () => { const manager = new FoldersManager(api, executor); await manager.favorite(item, true); - expect(executor.favorite).toBeCalledWith(hass, item, true); + expect(executor.favorite).toHaveBeenCalledWith(hass, item, true); }); }); diff --git a/tests/card-controller/fullscreen/fullscreen-manager.test.ts b/tests/card-controller/fullscreen/fullscreen-manager.test.ts index 685bb536..430ad4a7 100644 --- a/tests/card-controller/fullscreen/fullscreen-manager.test.ts +++ b/tests/card-controller/fullscreen/fullscreen-manager.test.ts @@ -19,7 +19,7 @@ describe('FullscreenManager', () => { const manager = new FullscreenManager(api, mock()); manager.initialize(); - expect(api.getConditionStateManager().setState).toBeCalledWith({ + expect(api.getConditionStateManager().setState).toHaveBeenCalledWith({ fullscreen: false, }); }); @@ -49,7 +49,7 @@ describe('FullscreenManager', () => { manager.toggleFullscreen(); - expect(provider.setFullscreen).toBeCalledWith(expected); + expect(provider.setFullscreen).toHaveBeenCalledWith(expected); }); }); @@ -60,7 +60,7 @@ describe('FullscreenManager', () => { manager.setFullscreen(fullscreen); - expect(provider.setFullscreen).toBeCalledWith(fullscreen); + expect(provider.setFullscreen).toHaveBeenCalledWith(fullscreen); }); }); @@ -70,7 +70,7 @@ describe('FullscreenManager', () => { manager.connect(); - expect(provider.connect).toBeCalled(); + expect(provider.connect).toHaveBeenCalled(); }); it('should disconnect', () => { @@ -79,7 +79,7 @@ describe('FullscreenManager', () => { manager.disconnect(); - expect(provider.disconnect).toBeCalled(); + expect(provider.disconnect).toHaveBeenCalled(); }); describe('should confirm whether fullscreen is supported', () => { @@ -137,7 +137,7 @@ describe('FullscreenManager', () => { handler(); - expect(api.getConditionStateManager().setState).toBeCalledWith({ + expect(api.getConditionStateManager().setState).toHaveBeenCalledWith({ fullscreen: fullscreen, }); }); @@ -148,6 +148,9 @@ describe('FullscreenManager', () => { new FullscreenManager(api); - expect(FullscreenProviderFactory.create).toBeCalledWith(api, expect.anything()); + expect(FullscreenProviderFactory.create).toHaveBeenCalledWith( + api, + expect.anything(), + ); }); }); diff --git a/tests/card-controller/fullscreen/screenfull/index.test.ts b/tests/card-controller/fullscreen/screenfull/index.test.ts index 4ba857f4..3e0a1b06 100644 --- a/tests/card-controller/fullscreen/screenfull/index.test.ts +++ b/tests/card-controller/fullscreen/screenfull/index.test.ts @@ -41,7 +41,7 @@ describe('ScreenfullFullScreenProvider', () => { provider.connect(); - expect(on).toBeCalledWith('change', expect.anything()); + expect(on).toHaveBeenCalledWith('change', expect.anything()); }); it('should not connect if not enabled', () => { @@ -52,7 +52,7 @@ describe('ScreenfullFullScreenProvider', () => { provider.connect(); - expect(on).not.toBeCalled(); + expect(on).not.toHaveBeenCalled(); }); }); @@ -65,7 +65,7 @@ describe('ScreenfullFullScreenProvider', () => { provider.disconnect(); - expect(off).toBeCalledWith('change', expect.anything()); + expect(off).toHaveBeenCalledWith('change', expect.anything()); }); it('should not disconnect if not enabled', () => { @@ -76,7 +76,7 @@ describe('ScreenfullFullScreenProvider', () => { provider.disconnect(); - expect(off).not.toBeCalled(); + expect(off).not.toHaveBeenCalled(); }); }); @@ -138,7 +138,7 @@ describe('ScreenfullFullScreenProvider', () => { provider.setFullscreen(true); - expect(screenfull.request).toBeCalledWith(element); + expect(screenfull.request).toHaveBeenCalledWith(element); }); it('should exit fullscreen if fullscreen is false', () => { @@ -148,7 +148,7 @@ describe('ScreenfullFullScreenProvider', () => { provider.setFullscreen(false); - expect(screenfull.exit).toBeCalled(); + expect(screenfull.exit).toHaveBeenCalled(); }); it('should take no action if not supported', () => { @@ -162,8 +162,8 @@ describe('ScreenfullFullScreenProvider', () => { provider.setFullscreen(true); provider.setFullscreen(false); - expect(screenfull.request).not.toBeCalled(); - expect(screenfull.exit).not.toBeCalled(); + expect(screenfull.request).not.toHaveBeenCalled(); + expect(screenfull.exit).not.toHaveBeenCalled(); }); it('should swallow a rejected fullscreen request', async () => { @@ -177,7 +177,7 @@ describe('ScreenfullFullScreenProvider', () => { provider.setFullscreen(true); await flushPromises(); - expect(screenfull.request).toBeCalledWith(element); + expect(screenfull.request).toHaveBeenCalledWith(element); }); it('should swallow a rejected fullscreen exit', async () => { @@ -188,7 +188,7 @@ describe('ScreenfullFullScreenProvider', () => { provider.setFullscreen(false); await flushPromises(); - expect(screenfull.exit).toBeCalled(); + expect(screenfull.exit).toHaveBeenCalled(); }); }); }); diff --git a/tests/card-controller/fullscreen/webkit/index.test.ts b/tests/card-controller/fullscreen/webkit/index.test.ts index 41b36971..7b1bc942 100644 --- a/tests/card-controller/fullscreen/webkit/index.test.ts +++ b/tests/card-controller/fullscreen/webkit/index.test.ts @@ -40,7 +40,9 @@ describe('WebkitFullScreenProvider', () => { provider.connect(); - expect(api.getConditionStateManager().addListener).toBeCalledWith(expect.anything()); + expect(api.getConditionStateManager().addListener).toHaveBeenCalledWith( + expect.anything(), + ); }); it('should disconnect', () => { @@ -49,7 +51,7 @@ describe('WebkitFullScreenProvider', () => { provider.disconnect(); - expect(api.getConditionStateManager().removeListener).toBeCalledWith( + expect(api.getConditionStateManager().removeListener).toHaveBeenCalledWith( expect.anything(), ); }); @@ -110,7 +112,7 @@ describe('WebkitFullScreenProvider', () => { provider.setFullscreen(true); - expect(element.webkitEnterFullscreen).toBeCalled(); + expect(element.webkitEnterFullscreen).toHaveBeenCalled(); }); it('should exit fullscreen if fullscreen is true', () => { @@ -130,7 +132,7 @@ describe('WebkitFullScreenProvider', () => { provider.setFullscreen(false); - expect(element.webkitExitFullscreen).toBeCalled(); + expect(element.webkitExitFullscreen).toHaveBeenCalled(); }); it('should take no action if not supported', () => { @@ -152,8 +154,8 @@ describe('WebkitFullScreenProvider', () => { provider.setFullscreen(true); provider.setFullscreen(false); - expect(element.webkitEnterFullscreen).not.toBeCalled(); - expect(element.webkitExitFullscreen).not.toBeCalled(); + expect(element.webkitEnterFullscreen).not.toHaveBeenCalled(); + expect(element.webkitExitFullscreen).not.toHaveBeenCalled(); }); it('should take no action if element is not a video', () => { @@ -175,8 +177,8 @@ describe('WebkitFullScreenProvider', () => { provider.setFullscreen(true); provider.setFullscreen(false); - expect(element.webkitEnterFullscreen).not.toBeCalled(); - expect(element.webkitExitFullscreen).not.toBeCalled(); + expect(element.webkitEnterFullscreen).not.toHaveBeenCalled(); + expect(element.webkitExitFullscreen).not.toHaveBeenCalled(); }); }); @@ -205,7 +207,7 @@ describe('WebkitFullScreenProvider', () => { element_1.dispatchEvent(new Event(event)); - expect(handler).toBeCalledTimes(1); + expect(handler).toHaveBeenCalledTimes(1); const element_2 = createWebkitVideoElement(); const mediaPlayerController_2 = mock(); @@ -218,12 +220,12 @@ describe('WebkitFullScreenProvider', () => { element_2.dispatchEvent(new Event(event)); - expect(handler).toBeCalledTimes(2); + expect(handler).toHaveBeenCalledTimes(2); // Events on the old element should be ignored. element_1.dispatchEvent(new Event(event)); - expect(handler).toBeCalledTimes(2); + expect(handler).toHaveBeenCalledTimes(2); // Test the media loaded info changing, but the player not changing. stateManager.setState({ @@ -233,7 +235,7 @@ describe('WebkitFullScreenProvider', () => { // Events on the new element should still be handled. element_2.dispatchEvent(new Event(event)); - expect(handler).toBeCalledTimes(3); + expect(handler).toHaveBeenCalledTimes(3); }, ); }); @@ -265,11 +267,11 @@ describe('WebkitFullScreenProvider', () => { element.dispatchEvent(new Event('webkitendfullscreen')); - expect(element.play).not.toBeCalled(); + expect(element.play).not.toHaveBeenCalled(); vi.runOnlyPendingTimers(); - expect(element.play).toBeCalled(); + expect(element.play).toHaveBeenCalled(); }); it('should swallow a rejected video replay after fullscreen ends', async () => { @@ -295,6 +297,6 @@ describe('WebkitFullScreenProvider', () => { vi.runOnlyPendingTimers(); await flushPromises(); - expect(element.play).toBeCalled(); + expect(element.play).toHaveBeenCalled(); }); }); diff --git a/tests/card-controller/hass/event-watcher.test.ts b/tests/card-controller/hass/event-watcher.test.ts index dc2d58fd..b9864d9b 100644 --- a/tests/card-controller/hass/event-watcher.test.ts +++ b/tests/card-controller/hass/event-watcher.test.ts @@ -35,7 +35,7 @@ describe('EventWatcher', () => { watcher.subscribe({ event_type: 'zha_event', callback: vi.fn() }); await flushPromises(); - expect(hass.connection.subscribeEvents).toBeCalledTimes(1); + expect(hass.connection.subscribeEvents).toHaveBeenCalledTimes(1); expect(vi.mocked(hass.connection.subscribeEvents).mock.calls[0][1]).toBe( 'zha_event', ); @@ -50,7 +50,7 @@ describe('EventWatcher', () => { watcher.subscribe({ event_type: 'zha_event', callback: vi.fn() }); await flushPromises(); - expect(hass.connection.subscribeEvents).toBeCalledTimes(1); + expect(hass.connection.subscribeEvents).toHaveBeenCalledTimes(1); }); it('should open separate WS subscriptions for distinct event_types', async () => { @@ -62,7 +62,7 @@ describe('EventWatcher', () => { watcher.subscribe({ event_type: 'deconz_event', callback: vi.fn() }); await flushPromises(); - expect(hass.connection.subscribeEvents).toBeCalledTimes(2); + expect(hass.connection.subscribeEvents).toHaveBeenCalledTimes(2); }); it('should dispatch to every subscriber whose event_type matches', async () => { @@ -79,8 +79,8 @@ describe('EventWatcher', () => { fireEvent(hass, event); - expect(cb1).toBeCalledWith(event); - expect(cb2).toBeCalledWith(event); + expect(cb1).toHaveBeenCalledWith(event); + expect(cb2).toHaveBeenCalledWith(event); }); it('should gate dispatch on the request matcher when provided', async () => { @@ -98,9 +98,9 @@ describe('EventWatcher', () => { fireEvent(hass, matching); fireEvent(hass, nonMatching); - expect(matcher).toBeCalledTimes(2); - expect(cb).toBeCalledTimes(1); - expect(cb).toBeCalledWith(matching); + expect(matcher).toHaveBeenCalledTimes(2); + expect(cb).toHaveBeenCalledTimes(1); + expect(cb).toHaveBeenCalledWith(matching); }); it('should tear down the WS subscription only when the last subscriber unsubscribes', async () => { @@ -118,11 +118,11 @@ describe('EventWatcher', () => { watcher.unsubscribe(req1); await flushPromises(); - expect(unsub).not.toBeCalled(); + expect(unsub).not.toHaveBeenCalled(); watcher.unsubscribe(req2); await flushPromises(); - expect(unsub).toBeCalledTimes(1); + expect(unsub).toHaveBeenCalledTimes(1); }); it('should drop events from an old-connection subscription after a swap', async () => { @@ -147,7 +147,7 @@ describe('EventWatcher', () => { // Old dispatcher fires: guard.isConnected() is now false, callback must NOT // receive the event. oldDispatcher?.(createHASSEvent('zha_event', { command: 'press' })); - expect(cb).not.toBeCalled(); + expect(cb).not.toHaveBeenCalled(); }); it('should not dispatch to a subscriber that registers mid-dispatch', async () => { @@ -164,8 +164,8 @@ describe('EventWatcher', () => { fireEvent(hass, createHASSEvent('zha_event', { command: 'press' })); - expect(reentrantCallback).toBeCalledTimes(1); - expect(lateCallback).not.toBeCalled(); + expect(reentrantCallback).toHaveBeenCalledTimes(1); + expect(lateCallback).not.toHaveBeenCalled(); }); describe('subscription health monitoring', () => { diff --git a/tests/card-controller/hass/hass-manager.test.ts b/tests/card-controller/hass/hass-manager.test.ts index 1cbc7480..47e98c31 100644 --- a/tests/card-controller/hass/hass-manager.test.ts +++ b/tests/card-controller/hass/hass-manager.test.ts @@ -4,16 +4,10 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'; import { EventWatcher } from '../../../src/card-controller/hass/event-watcher'; import { HASSManager } from '../../../src/card-controller/hass/hass-manager'; import { StateWatcher } from '../../../src/card-controller/hass/state-watcher'; -import { - createCameraConfig, - createCameraManager, - createCardAPI, - createConfig, - createHASS, - createStateEntity, - createStore, - createView, -} from '../../test-utils'; +import { createCameraManager, createStore } from '../../camera-manager/test-utils'; +import { createCameraConfig, createConfig } from '../../config/test-utils'; +import { createCardAPI, createHASS, createStateEntity } from '../../test-utils'; +import { createView } from '../../view/test-utils'; describe('HASSManager', () => { beforeEach(() => { @@ -53,11 +47,11 @@ describe('HASSManager', () => { const hass1 = createHASS(); manager.setHASS(hass1); - expect(listener).toBeCalledWith(hass1, null); + expect(listener).toHaveBeenCalledWith(hass1, null); const hass2 = createHASS(); manager.setHASS(hass2); - expect(listener).toBeCalledWith(hass2, hass1); + expect(listener).toHaveBeenCalledWith(hass2, hass1); }); it('should call listeners in insertion order on every fan-out', () => { @@ -79,7 +73,7 @@ describe('HASSManager', () => { unlisten(); manager.setHASS(createHASS()); - expect(listener).not.toBeCalled(); + expect(listener).not.toHaveBeenCalled(); }); it('should not fan out on null/undefined hass', () => { @@ -90,7 +84,7 @@ describe('HASSManager', () => { manager.setHASS(null); manager.setHASS(); - expect(listener).not.toBeCalled(); + expect(listener).not.toHaveBeenCalled(); }); it('should expose current hass via getHASS for source consumers', () => { @@ -128,10 +122,12 @@ describe('HASSManager', () => { // Cameras and view should be uninitialized so they get re-subscribed // to event sources (e.g. Frigate WebSocket events) on the next // render cycle. - expect(api.getInitializationManager().uninitialize).toBeCalledWith('cameras'); - expect(api.getCameraManager().destroy).toBeCalled(); - expect(api.getInitializationManager().uninitialize).toBeCalledWith('view'); - expect(api.getInitializationManager().uninitialize).toBeCalledWith( + expect(api.getInitializationManager().uninitialize).toHaveBeenCalledWith( + 'cameras', + ); + expect(api.getCameraManager().destroy).toHaveBeenCalled(); + expect(api.getInitializationManager().uninitialize).toHaveBeenCalledWith('view'); + expect(api.getInitializationManager().uninitialize).toHaveBeenCalledWith( 'initial-trigger', ); }); @@ -147,8 +143,8 @@ describe('HASSManager', () => { manager.setHASS(startingHASS); // No reinit yet -- HA isn't fully ready. - expect(api.getInitializationManager().uninitialize).not.toBeCalled(); - expect(api.getCameraManager().destroy).not.toBeCalled(); + expect(api.getInitializationManager().uninitialize).not.toHaveBeenCalled(); + expect(api.getCameraManager().destroy).not.toHaveBeenCalled(); // HA finishes booting. const readyHASS = createHASS(); @@ -156,10 +152,12 @@ describe('HASSManager', () => { readyHASS.config.state = STATE_RUNNING; manager.setHASS(readyHASS); - expect(api.getInitializationManager().uninitialize).toBeCalledWith('cameras'); - expect(api.getCameraManager().destroy).toBeCalled(); - expect(api.getInitializationManager().uninitialize).toBeCalledWith('view'); - expect(api.getInitializationManager().uninitialize).toBeCalledWith( + expect(api.getInitializationManager().uninitialize).toHaveBeenCalledWith( + 'cameras', + ); + expect(api.getCameraManager().destroy).toHaveBeenCalled(); + expect(api.getInitializationManager().uninitialize).toHaveBeenCalledWith('view'); + expect(api.getInitializationManager().uninitialize).toHaveBeenCalledWith( 'initial-trigger', ); }); @@ -178,8 +176,8 @@ describe('HASSManager', () => { manager.setHASS(startingHASS); // WS came back but integrations still loading -- wait for RUNNING. - expect(api.getInitializationManager().uninitialize).not.toBeCalled(); - expect(api.getCameraManager().destroy).not.toBeCalled(); + expect(api.getInitializationManager().uninitialize).not.toHaveBeenCalled(); + expect(api.getCameraManager().destroy).not.toHaveBeenCalled(); }); it('should not reinitialize on first hass set (no previous hass)', () => { @@ -194,8 +192,8 @@ describe('HASSManager', () => { // First-ever hass set -- there's no "previous not-ready state" to // transition from, so the normal first-load init flow applies and we must // not blow away cameras. - expect(api.getInitializationManager().uninitialize).not.toBeCalled(); - expect(api.getCameraManager().destroy).not.toBeCalled(); + expect(api.getInitializationManager().uninitialize).not.toHaveBeenCalled(); + expect(api.getCameraManager().destroy).not.toHaveBeenCalled(); }); it('should not reinitialize on ready → ready (no transition)', () => { @@ -212,8 +210,8 @@ describe('HASSManager', () => { anotherReadyHASS.config.state = STATE_RUNNING; manager.setHASS(anotherReadyHASS); - expect(api.getInitializationManager().uninitialize).not.toBeCalled(); - expect(api.getCameraManager().destroy).not.toBeCalled(); + expect(api.getInitializationManager().uninitialize).not.toHaveBeenCalled(); + expect(api.getCameraManager().destroy).not.toHaveBeenCalled(); }); it('should not crash when hass is null', () => { @@ -257,7 +255,7 @@ describe('HASSManager', () => { manager.setHASS(hass); - expect(api.getViewManager().setViewDefault).not.toBeCalled(); + expect(api.getViewManager().setViewDefault).not.toHaveBeenCalled(); }); it('should not set default view when there is card interaction', () => { @@ -280,7 +278,7 @@ describe('HASSManager', () => { manager.setHASS(hass); - expect(api.getViewManager().setViewDefault).not.toBeCalled(); + expect(api.getViewManager().setViewDefault).not.toHaveBeenCalled(); }); }); }); diff --git a/tests/card-controller/hass/state-watcher.test.ts b/tests/card-controller/hass/state-watcher.test.ts index e3e0bc8a..341a237f 100644 --- a/tests/card-controller/hass/state-watcher.test.ts +++ b/tests/card-controller/hass/state-watcher.test.ts @@ -63,8 +63,8 @@ describe('StateWatcher', () => { }), ); - expect(callback).toBeCalledTimes(1); - expect(callback).toBeCalledWith( + expect(callback).toHaveBeenCalledTimes(1); + expect(callback).toHaveBeenCalledWith( expect.objectContaining({ entityID: 'binary_sensor.bar', oldState: createStateEntity({ state: 'off' }), @@ -86,7 +86,7 @@ describe('StateWatcher', () => { }), ); - expect(callback).not.toBeCalled(); + expect(callback).not.toHaveBeenCalled(); }); it('should not call back without state change', () => { @@ -105,7 +105,7 @@ describe('StateWatcher', () => { }), ); - expect(callback).not.toBeCalled(); + expect(callback).not.toHaveBeenCalled(); }); it('should not call back when unsubscribed', () => { @@ -125,6 +125,6 @@ describe('StateWatcher', () => { }), ); - expect(callback).not.toBeCalled(); + expect(callback).not.toHaveBeenCalled(); }); }); diff --git a/tests/card-controller/initialization-manager.test.ts b/tests/card-controller/initialization-manager.test.ts index 8b86d8a9..f0bcf221 100644 --- a/tests/card-controller/initialization-manager.test.ts +++ b/tests/card-controller/initialization-manager.test.ts @@ -10,7 +10,8 @@ import { ConditionStateManager } from '../../src/condition-trigger/conditions/st import { sideLoadHomeAssistantElements } from '../../src/ha/side-load-ha-elements.js'; import { loadLanguages } from '../../src/localize/localize'; import type { Initializer } from '../../src/utils/initializer/initializer'; -import { createCardAPI, createConfig, createHASS } from '../test-utils'; +import { createConfig } from '../config/test-utils'; +import { createCardAPI, createHASS } from '../test-utils'; vi.mock('../../src/localize/localize.js'); vi.mock('../../src/ha/side-load-ha-elements.js'); @@ -78,9 +79,9 @@ describe('InitializationManager', () => { await manager.initializeMandatory(); - expect(initializer.initializeMultipleIfNecessary).not.toBeCalled(); - expect(initializer.initializeIfNecessary).not.toBeCalled(); - expect(api.getIssueManager().trigger).not.toBeCalled(); + expect(initializer.initializeMultipleIfNecessary).not.toHaveBeenCalled(); + expect(initializer.initializeIfNecessary).not.toHaveBeenCalled(); + expect(api.getIssueManager().trigger).not.toHaveBeenCalled(); expect(manager.wasEverInitialized()).toBeFalsy(); }); @@ -110,16 +111,16 @@ describe('InitializationManager', () => { await manager.initializeMandatory(); - expect(loadLanguages).toBeCalled(); - expect(sideLoadHomeAssistantElements).toBeCalled(); - expect(api.getCameraManager().initializeCamerasFromConfig).toBeCalled(); - expect(api.getViewManager().initialize).toBeCalled(); - expect(api.getMicrophoneManager().connect).not.toBeCalled(); - expect(api.getCardElementManager().update).toBeCalled(); + expect(loadLanguages).toHaveBeenCalled(); + expect(sideLoadHomeAssistantElements).toHaveBeenCalled(); + expect(api.getCameraManager().initializeCamerasFromConfig).toHaveBeenCalled(); + expect(api.getViewManager().initialize).toHaveBeenCalled(); + expect(api.getMicrophoneManager().connect).not.toHaveBeenCalled(); + expect(api.getCardElementManager().update).toHaveBeenCalled(); expect(manager.wasEverInitialized()).toBeTruthy(); - expect(stateListener).toBeCalledWith( + expect(stateListener).toHaveBeenCalledWith( expect.objectContaining({ change: { initialized: true, @@ -151,7 +152,7 @@ describe('InitializationManager', () => { await manager.initializeMandatory(); - expect(loadRenderer).toBeCalled(); + expect(loadRenderer).toHaveBeenCalled(); expect(manager.isInitialized(InitializationAspect.TEMPLATE_RENDERER)).toBeTruthy(); expect(manager.isInitializedMandatory()).toBeTruthy(); }); @@ -167,7 +168,7 @@ describe('InitializationManager', () => { await manager.initializeMandatory(); - expect(loadRenderer).not.toBeCalled(); + expect(loadRenderer).not.toHaveBeenCalled(); expect(manager.isInitialized(InitializationAspect.TEMPLATE_RENDERER)).toBeFalsy(); expect(manager.isInitializedMandatory()).toBeTruthy(); }); @@ -184,7 +185,7 @@ describe('InitializationManager', () => { await manager.initializeMandatory(); - expect(api.getMicrophoneManager().connect).toBeCalled(); + expect(api.getMicrophoneManager().connect).toHaveBeenCalled(); }); it('should handle message set during initialization', async () => { @@ -202,7 +203,7 @@ describe('InitializationManager', () => { await manager.initializeMandatory(); - expect(api.getViewManager().initialize).not.toBeCalled(); + expect(api.getViewManager().initialize).not.toHaveBeenCalled(); }); it('should handle languages and side load elements in progress', async () => { @@ -235,7 +236,7 @@ describe('InitializationManager', () => { await manager.initializeMandatory(); expect(manager.wasEverInitialized()).toBeFalsy(); - expect(api.getIssueManager().trigger).toBeCalledWith( + expect(api.getIssueManager().trigger).toHaveBeenCalledWith( 'initialization', expect.objectContaining({ error: expect.any(Error) }), ); @@ -260,7 +261,7 @@ describe('InitializationManager', () => { await manager.initializeMandatory(); expect(manager.wasEverInitialized()).toBeFalsy(); - expect(api.getIssueManager().trigger).toBeCalledWith( + expect(api.getIssueManager().trigger).toHaveBeenCalledWith( 'initialization', expect.objectContaining({ error: expect.any(Error) }), ); @@ -281,7 +282,7 @@ describe('InitializationManager', () => { await manager.initializeMandatory(); expect(manager.wasEverInitialized()).toBeFalsy(); - expect(api.getIssueManager().trigger).toBeCalledWith( + expect(api.getIssueManager().trigger).toHaveBeenCalledWith( 'initialization', expect.objectContaining({ error: expect.any(Error) }), ); @@ -304,7 +305,7 @@ describe('InitializationManager', () => { await manager.initializeMandatory(); expect(manager.wasEverInitialized()).toBeFalsy(); - expect(api.getIssueManager().trigger).not.toBeCalledWith( + expect(api.getIssueManager().trigger).not.toHaveBeenCalledWith( 'initialization', expect.anything(), ); @@ -322,7 +323,7 @@ describe('InitializationManager', () => { await manager.initializeMandatory(); expect(manager.wasEverInitialized()).toBeFalsy(); - expect(api.getIssueManager().trigger).toBeCalledWith( + expect(api.getIssueManager().trigger).toHaveBeenCalledWith( 'initialization', expect.objectContaining({ error: 'string error' }), ); @@ -335,15 +336,15 @@ describe('InitializationManager', () => { manager.uninitializeMandatory(); - expect(initializer.uninitialize).toBeCalledWith(InitializationAspect.CAMERAS); - expect(initializer.uninitialize).toBeCalledWith( + expect(initializer.uninitialize).toHaveBeenCalledWith(InitializationAspect.CAMERAS); + expect(initializer.uninitialize).toHaveBeenCalledWith( InitializationAspect.MICROPHONE_CONNECT, ); - expect(initializer.uninitialize).toBeCalledWith( + expect(initializer.uninitialize).toHaveBeenCalledWith( InitializationAspect.TEMPLATE_RENDERER, ); - expect(initializer.uninitialize).toBeCalledWith(InitializationAspect.VIEW); - expect(initializer.uninitialize).toBeCalledWith( + expect(initializer.uninitialize).toHaveBeenCalledWith(InitializationAspect.VIEW); + expect(initializer.uninitialize).toHaveBeenCalledWith( InitializationAspect.INITIAL_TRIGGER, ); }); @@ -354,7 +355,7 @@ describe('InitializationManager', () => { manager.uninitialize(InitializationAspect.CAMERAS); - expect(initializer.uninitialize).toBeCalledWith(InitializationAspect.CAMERAS); + expect(initializer.uninitialize).toHaveBeenCalledWith(InitializationAspect.CAMERAS); }); describe('should decide whether to trigger initialization', () => { @@ -379,7 +380,7 @@ describe('InitializationManager', () => { manager.triggerInitialization(); - expect(initializer.initializeMultipleIfNecessary).toBeCalled(); + expect(initializer.initializeMultipleIfNecessary).toHaveBeenCalled(); }); it('should not initialize without config', () => { @@ -390,7 +391,7 @@ describe('InitializationManager', () => { manager.triggerInitialization(); - expect(initializer.initializeMultipleIfNecessary).not.toBeCalled(); + expect(initializer.initializeMultipleIfNecessary).not.toHaveBeenCalled(); }); it('should not initialize when the element is disconnected', () => { @@ -401,7 +402,7 @@ describe('InitializationManager', () => { manager.triggerInitialization(); - expect(initializer.initializeMultipleIfNecessary).not.toBeCalled(); + expect(initializer.initializeMultipleIfNecessary).not.toHaveBeenCalled(); }); it('should not initialize when hass is not ready', () => { @@ -415,7 +416,7 @@ describe('InitializationManager', () => { manager.triggerInitialization(); - expect(initializer.initializeMultipleIfNecessary).not.toBeCalled(); + expect(initializer.initializeMultipleIfNecessary).not.toHaveBeenCalled(); }); it('should not initialize when already initialized', () => { @@ -425,7 +426,7 @@ describe('InitializationManager', () => { manager.triggerInitialization(); - expect(initializer.initializeMultipleIfNecessary).not.toBeCalled(); + expect(initializer.initializeMultipleIfNecessary).not.toHaveBeenCalled(); }); it('should not initialize while a full-card issue is shown', () => { @@ -438,7 +439,7 @@ describe('InitializationManager', () => { manager.triggerInitialization(); - expect(initializer.initializeMultipleIfNecessary).not.toBeCalled(); + expect(initializer.initializeMultipleIfNecessary).not.toHaveBeenCalled(); }); }); }); diff --git a/tests/card-controller/interaction-manager.test.ts b/tests/card-controller/interaction-manager.test.ts index 620a69dc..c734170f 100644 --- a/tests/card-controller/interaction-manager.test.ts +++ b/tests/card-controller/interaction-manager.test.ts @@ -2,11 +2,8 @@ import { add } from 'date-fns'; import { afterEach, describe, expect, it, vi } from 'vitest'; import { InteractionManager } from '../../src/card-controller/interaction-manager'; -import { createCardAPI, createConfig, createLitElement } from '../test-utils'; - -vi.mock('lodash-es', () => ({ - throttle: vi.fn((fn) => Object.assign(fn, { cancel: vi.fn() })), -})); +import { createConfig } from '../config/test-utils'; +import { createCardAPI, createLitElement } from '../test-utils'; // @vitest-environment jsdom describe('InteractionManager', () => { @@ -24,7 +21,7 @@ describe('InteractionManager', () => { const manager = new InteractionManager(api); manager.initialize(); - expect(api.getConditionStateManager().setState).toBeCalledWith({ + expect(api.getConditionStateManager().setState).toHaveBeenCalledWith({ interaction: false, }); expect(element.getAttribute('interaction')).toBeNull(); @@ -65,7 +62,7 @@ describe('InteractionManager', () => { vi.useFakeTimers(); vi.setSystemTime(start); - expect(api.getConditionStateManager().setState).not.toBeCalled(); + expect(api.getConditionStateManager().setState).not.toHaveBeenCalled(); manager.reportInteraction(); diff --git a/tests/card-controller/issues/issue-manager.test.ts b/tests/card-controller/issues/issue-manager.test.ts index c78dbfe5..5fe3b112 100644 --- a/tests/card-controller/issues/issue-manager.test.ts +++ b/tests/card-controller/issues/issue-manager.test.ts @@ -17,9 +17,9 @@ import type { } from '../../../src/card-controller/issues/types'; import { ConditionStateManager } from '../../../src/condition-trigger/conditions/state-manager'; import type { InteractionMode } from '../../../src/config/schema/view'; +import { createConfig } from '../../config/test-utils'; import { createCardAPI, - createConfig, createHASS, createMediaLoadedInfo, flushPromises, @@ -109,7 +109,7 @@ describe('IssueManager', () => { stateManager.setState({ view: 'live' }); - expect(issue.detectDynamic).toBeCalled(); + expect(issue.detectDynamic).toHaveBeenCalled(); }); it('should keep a liveness-triggered error active while its media still reads as loaded', () => { @@ -170,7 +170,7 @@ describe('IssueManager', () => { conditionStateManager.setState({ initialized: true }); await flushPromises(); - expect(detectStatic).toBeCalledWith(hass); + expect(detectStatic).toHaveBeenCalledWith(hass); }); it('should not run static detection when hass is unset', () => { @@ -185,7 +185,7 @@ describe('IssueManager', () => { conditionStateManager.setState({ initialized: true }); - expect(detectStatic).not.toBeCalled(); + expect(detectStatic).not.toHaveBeenCalled(); }); it('should not run static detection on unrelated state changes', () => { @@ -202,7 +202,7 @@ describe('IssueManager', () => { conditionStateManager.setState({ hass }); conditionStateManager.setState({ view: 'live' }); - expect(detectStatic).not.toBeCalled(); + expect(detectStatic).not.toHaveBeenCalled(); }); }); @@ -219,7 +219,7 @@ describe('IssueManager', () => { manager.trigger('config_error', { error: new Error('cfg') }); - expect(issue.trigger).toBeCalledWith({ error: expect.any(Error) }); + expect(issue.trigger).toHaveBeenCalledWith({ error: expect.any(Error) }); }); it('should update the card even when state was mutated before detectDynamic', () => { @@ -242,7 +242,7 @@ describe('IssueManager', () => { manager.trigger('config_error', { error: new Error('cfg') }); - expect(api.getCardElementManager().update).toBeCalled(); + expect(api.getCardElementManager().update).toHaveBeenCalled(); }); it('should never auto-popup on trigger -- non-full-card issues surface via the status-bar icon; user clicks to open', () => { @@ -261,7 +261,7 @@ describe('IssueManager', () => { manager.trigger('view_incompatible', { error: new Error('mismatch') }); - expect(api.getNotificationManager().setNotification).not.toBeCalled(); + expect(api.getNotificationManager().setNotification).not.toHaveBeenCalled(); }); }); @@ -273,14 +273,14 @@ describe('IssueManager', () => { manager.evaluate(); manager.retry('media_unavailable'); - expect(issue.retry).toBeCalled(); + expect(issue.retry).toHaveBeenCalled(); // Timer should have been reset -- advancing less than retrySeconds should // not fire it again. assert(issue.retry); vi.mocked(issue.retry).mockClear(); vi.advanceTimersByTime(500); - expect(issue.retry).not.toBeCalled(); + expect(issue.retry).not.toHaveBeenCalled(); }); it('should force retry even when needsRetry is false', () => { @@ -293,7 +293,7 @@ describe('IssueManager', () => { manager.retry('media_unavailable', true); - expect(issue.retry).toBeCalled(); + expect(issue.retry).toHaveBeenCalled(); }); }); @@ -311,7 +311,7 @@ describe('IssueManager', () => { manager.evaluate(); - expect(api.getCardElementManager().update).toBeCalled(); + expect(api.getCardElementManager().update).toHaveBeenCalled(); }); it('should not write issue presence back into the condition state', () => { @@ -328,7 +328,7 @@ describe('IssueManager', () => { manager.evaluate(); - expect(api.getConditionStateManager().setState).not.toBeCalled(); + expect(api.getConditionStateManager().setState).not.toHaveBeenCalled(); }); it('should not update the card when there are no issues', () => { @@ -340,7 +340,7 @@ describe('IssueManager', () => { manager.evaluate(); - expect(api.getCardElementManager().update).not.toBeCalled(); + expect(api.getCardElementManager().update).not.toHaveBeenCalled(); }); it('should call update when an active issue swaps sub-states without changing the key set', () => { @@ -371,7 +371,7 @@ describe('IssueManager', () => { ); manager.evaluate(); - expect(api.getCardElementManager().update).toBeCalled(); + expect(api.getCardElementManager().update).toHaveBeenCalled(); }); it('should not call update when content is identical across evaluations', () => { @@ -390,7 +390,7 @@ describe('IssueManager', () => { // Re-evaluate without any change. manager.evaluate(); - expect(api.getCardElementManager().update).not.toBeCalled(); + expect(api.getCardElementManager().update).not.toHaveBeenCalled(); }); it('should not request repeated updates as retry callback closures churn', () => { @@ -418,7 +418,7 @@ describe('IssueManager', () => { manager.evaluate(); - expect(api.getCardElementManager().update).not.toBeCalled(); + expect(api.getCardElementManager().update).not.toHaveBeenCalled(); }); it('should trigger evaluate from listener on condition state manager', () => { @@ -435,7 +435,7 @@ describe('IssueManager', () => { stateManager.setState({ view: 'live' }); - expect(issue.detectDynamic).toBeCalled(); + expect(issue.detectDynamic).toHaveBeenCalled(); }); }); @@ -452,7 +452,9 @@ describe('IssueManager', () => { manager.showNotification('media_query'); - expect(api.getNotificationManager().setNotification).toBeCalledWith(notification); + expect(api.getNotificationManager().setNotification).toHaveBeenCalledWith( + notification, + ); }); it('should not call setNotification when no notification exists for key', () => { @@ -460,7 +462,9 @@ describe('IssueManager', () => { manager.showNotification('initialization'); - expect(createCardAPI().getNotificationManager().setNotification).not.toBeCalled(); + expect( + createCardAPI().getNotificationManager().setNotification, + ).not.toHaveBeenCalled(); }); }); @@ -476,7 +480,7 @@ describe('IssueManager', () => { manager.evaluate(); vi.runAllTimers(); - expect(api.getViewManager().setViewWithMergedContext).not.toBeCalled(); + expect(api.getViewManager().setViewWithMergedContext).not.toHaveBeenCalled(); }); it('should not schedule a retry when config is null', () => { @@ -495,7 +499,7 @@ describe('IssueManager', () => { manager.evaluate(); vi.runAllTimers(); - expect(issue.retry).not.toBeCalled(); + expect(issue.retry).not.toHaveBeenCalled(); }); it('should not schedule a retry when retry_seconds is 0', () => { @@ -504,7 +508,7 @@ describe('IssueManager', () => { manager.evaluate(); vi.runAllTimers(); - expect(issue.retry).not.toBeCalled(); + expect(issue.retry).not.toHaveBeenCalled(); }); it('should schedule a retry when an issue wants retry and retry_seconds > 0', () => { @@ -513,7 +517,7 @@ describe('IssueManager', () => { manager.evaluate(); vi.advanceTimersByTime(5000); - expect(issue.retry).toBeCalled(); + expect(issue.retry).toHaveBeenCalled(); }); it('should call retry on the issue when the timer fires', () => { @@ -522,7 +526,7 @@ describe('IssueManager', () => { manager.evaluate(); vi.advanceTimersByTime(DEFAULT_RETRY_SECONDS * 1000); - expect(issue.retry).toBeCalled(); + expect(issue.retry).toHaveBeenCalled(); }); it('should not schedule a second timer if one is already running', () => { @@ -533,7 +537,7 @@ describe('IssueManager', () => { vi.advanceTimersByTime(10000); - expect(issue.retry).toBeCalledTimes(1); + expect(issue.retry).toHaveBeenCalledTimes(1); }); it('should stop repeated timer when needsRetry becomes false', () => { @@ -541,16 +545,16 @@ describe('IssueManager', () => { manager.evaluate(); vi.advanceTimersByTime(DEFAULT_RETRY_SECONDS * 1000); - expect(issue.retry).toBeCalledTimes(1); + expect(issue.retry).toHaveBeenCalledTimes(1); assert(issue.needsRetry); vi.mocked(issue.needsRetry).mockReturnValue(false); vi.advanceTimersByTime(DEFAULT_RETRY_SECONDS * 1000); - expect(issue.retry).toBeCalledTimes(1); + expect(issue.retry).toHaveBeenCalledTimes(1); vi.advanceTimersByTime(5000); - expect(issue.retry).toBeCalledTimes(1); + expect(issue.retry).toHaveBeenCalledTimes(1); }); it('should skip scheduled retry when user is interacting and mode is inactive', () => { @@ -561,7 +565,7 @@ describe('IssueManager', () => { manager.evaluate(); vi.advanceTimersByTime(DEFAULT_RETRY_SECONDS * 1000); - expect(issue.retry).not.toBeCalled(); + expect(issue.retry).not.toHaveBeenCalled(); }); it('should allow scheduled retry when user is not interacting and mode is inactive', () => { @@ -572,7 +576,7 @@ describe('IssueManager', () => { manager.evaluate(); vi.advanceTimersByTime(DEFAULT_RETRY_SECONDS * 1000); - expect(issue.retry).toBeCalled(); + expect(issue.retry).toHaveBeenCalled(); }); it('should allow scheduled retry when mode is all regardless of interaction', () => { @@ -584,7 +588,7 @@ describe('IssueManager', () => { manager.evaluate(); vi.advanceTimersByTime(DEFAULT_RETRY_SECONDS * 1000); - expect(issue.retry).toBeCalled(); + expect(issue.retry).toHaveBeenCalled(); }); it('should retry on next interval after interaction ends', () => { @@ -594,11 +598,11 @@ describe('IssueManager', () => { manager.evaluate(); vi.advanceTimersByTime(DEFAULT_RETRY_SECONDS * 1000); - expect(issue.retry).not.toBeCalled(); + expect(issue.retry).not.toHaveBeenCalled(); vi.mocked(api.getInteractionManager().hasInteraction).mockReturnValue(false); vi.advanceTimersByTime(DEFAULT_RETRY_SECONDS * 1000); - expect(issue.retry).toBeCalled(); + expect(issue.retry).toHaveBeenCalled(); }); }); @@ -610,10 +614,10 @@ describe('IssueManager', () => { manager.evaluate(); vi.advanceTimersByTime(RETRY_EXPONENTIAL_BASE_SECONDS * 0.5 * 1000 - 1); - expect(issue.retry).not.toBeCalled(); + expect(issue.retry).not.toHaveBeenCalled(); vi.advanceTimersByTime(1); - expect(issue.retry).toBeCalledTimes(1); + expect(issue.retry).toHaveBeenCalledTimes(1); }); it('should schedule the first retry at the upper bound when jitter is max', () => { @@ -623,10 +627,10 @@ describe('IssueManager', () => { manager.evaluate(); vi.advanceTimersByTime(RETRY_EXPONENTIAL_BASE_SECONDS * 1.0 * 1000 - 1); - expect(issue.retry).not.toBeCalled(); + expect(issue.retry).not.toHaveBeenCalled(); vi.advanceTimersByTime(1); - expect(issue.retry).toBeCalledTimes(1); + expect(issue.retry).toHaveBeenCalledTimes(1); }); it('should double the base delay on each successive attempt', () => { @@ -636,13 +640,13 @@ describe('IssueManager', () => { manager.evaluate(); vi.advanceTimersByTime(RETRY_EXPONENTIAL_BASE_SECONDS * 0.75 * 1000); - expect(issue.retry).toBeCalledTimes(1); + expect(issue.retry).toHaveBeenCalledTimes(1); vi.advanceTimersByTime(RETRY_EXPONENTIAL_BASE_SECONDS * 2 * 0.75 * 1000); - expect(issue.retry).toBeCalledTimes(2); + expect(issue.retry).toHaveBeenCalledTimes(2); vi.advanceTimersByTime(RETRY_EXPONENTIAL_BASE_SECONDS * 4 * 0.75 * 1000); - expect(issue.retry).toBeCalledTimes(3); + expect(issue.retry).toHaveBeenCalledTimes(3); }); it('should cap the backoff at the max delay', () => { @@ -662,15 +666,15 @@ describe('IssueManager', () => { vi.advanceTimersByTime(delaySeconds * 1000); attempts++; } - expect(issue.retry).toBeCalledTimes(attempts); + expect(issue.retry).toHaveBeenCalledTimes(attempts); // The next attempt clamps to MAX instead of the would-be larger delay. vi.advanceTimersByTime(RETRY_EXPONENTIAL_MAX_SECONDS * 1000); - expect(issue.retry).toBeCalledTimes(attempts + 1); + expect(issue.retry).toHaveBeenCalledTimes(attempts + 1); // And it stays capped at MAX rather than growing further. vi.advanceTimersByTime(RETRY_EXPONENTIAL_MAX_SECONDS * 1000); - expect(issue.retry).toBeCalledTimes(attempts + 2); + expect(issue.retry).toHaveBeenCalledTimes(attempts + 2); }); it('should reset the attempt counter when the issue clears', () => { @@ -681,14 +685,14 @@ describe('IssueManager', () => { // Run two retries -- second delay should be 2x the first. vi.advanceTimersByTime(RETRY_EXPONENTIAL_BASE_SECONDS * 0.75 * 1000); vi.advanceTimersByTime(RETRY_EXPONENTIAL_BASE_SECONDS * 2 * 0.75 * 1000); - expect(issue.retry).toBeCalledTimes(2); + expect(issue.retry).toHaveBeenCalledTimes(2); // Clear the issue: needsRetry returns false. The next timer fire sees // it cleared and resets the attempt counter. assert(issue.needsRetry); vi.mocked(issue.needsRetry).mockReturnValue(false); vi.advanceTimersByTime(RETRY_EXPONENTIAL_BASE_SECONDS * 4 * 0.75 * 1000); - expect(issue.retry).toBeCalledTimes(2); + expect(issue.retry).toHaveBeenCalledTimes(2); // Re-arm: needsRetry returns true again, evaluate to re-schedule. vi.mocked(issue.needsRetry).mockReturnValue(true); @@ -697,7 +701,7 @@ describe('IssueManager', () => { // Next delay should be back at the base (attempt 0), not continuing // from where we left off. vi.advanceTimersByTime(RETRY_EXPONENTIAL_BASE_SECONDS * 0.75 * 1000); - expect(issue.retry).toBeCalledTimes(3); + expect(issue.retry).toHaveBeenCalledTimes(3); }); it('should not grow the delay while retries are gated by user interaction', () => { @@ -717,13 +721,13 @@ describe('IssueManager', () => { vi.advanceTimersByTime(RETRY_EXPONENTIAL_BASE_SECONDS * 0.75 * 1000); vi.advanceTimersByTime(RETRY_EXPONENTIAL_BASE_SECONDS * 0.75 * 1000); vi.advanceTimersByTime(RETRY_EXPONENTIAL_BASE_SECONDS * 0.75 * 1000); - expect(issue.retry).not.toBeCalled(); + expect(issue.retry).not.toHaveBeenCalled(); // Clear the interaction. The next firing -- still at the base delay -- is // now allowed and the retry runs. vi.mocked(api.getInteractionManager().hasInteraction).mockReturnValue(false); vi.advanceTimersByTime(RETRY_EXPONENTIAL_BASE_SECONDS * 0.75 * 1000); - expect(issue.retry).toBeCalledTimes(1); + expect(issue.retry).toHaveBeenCalledTimes(1); }); it('should reset the attempt counter when retries are disabled and re-enabled', () => { @@ -736,7 +740,7 @@ describe('IssueManager', () => { vi.advanceTimersByTime(RETRY_EXPONENTIAL_BASE_SECONDS * 0.75 * 1000); vi.advanceTimersByTime(RETRY_EXPONENTIAL_BASE_SECONDS * 2 * 0.75 * 1000); - expect(issue.retry).toBeCalledTimes(2); + expect(issue.retry).toHaveBeenCalledTimes(2); // Disable retries via config. const config = createConfig(); @@ -751,7 +755,7 @@ describe('IssueManager', () => { // Let the pending timer fire. The retry runs (#3), then evaluate sees // retry_seconds=0 and resets _retryAttempt. vi.advanceTimersByTime(RETRY_EXPONENTIAL_BASE_SECONDS * 4 * 0.75 * 1000); - expect(issue.retry).toBeCalledTimes(3); + expect(issue.retry).toHaveBeenCalledTimes(3); // Re-enable. vi.mocked(api.getConfigManager().getConfig).mockReturnValue({ @@ -767,7 +771,7 @@ describe('IssueManager', () => { // delay BASE * 8 * 0.75 = 180s. With the reset, it's BASE * 0.75 = 22.5s, // so advancing only the base interval triggers the next retry. vi.advanceTimersByTime(RETRY_EXPONENTIAL_BASE_SECONDS * 0.75 * 1000); - expect(issue.retry).toBeCalledTimes(4); + expect(issue.retry).toHaveBeenCalledTimes(4); }); }); @@ -799,7 +803,7 @@ describe('IssueManager', () => { // First attempt fires at the base delay, advancing the backoff to // attempt 1. vi.advanceTimersByTime(RETRY_EXPONENTIAL_BASE_SECONDS * 0.75 * 1000); - expect(issue.retry).toBeCalledTimes(1); + expect(issue.retry).toHaveBeenCalledTimes(1); // The attempt is now in flight: the problem is still unresolved // (needsRetry) but cannot be retried right now (canRetryNow). The running @@ -807,7 +811,7 @@ describe('IssueManager', () => { canRetryNow.mockReturnValue(false); manager.evaluate(); vi.advanceTimersByTime(RETRY_EXPONENTIAL_MAX_SECONDS * 1000); - expect(issue.retry).toBeCalledTimes(1); + expect(issue.retry).toHaveBeenCalledTimes(1); // The attempt fails and becomes retryable again. Because the backoff was // preserved, the next delay is the attempt-1 step (base*2), not base. @@ -815,10 +819,10 @@ describe('IssueManager', () => { manager.evaluate(); vi.advanceTimersByTime(RETRY_EXPONENTIAL_BASE_SECONDS * 0.75 * 1000); - expect(issue.retry).toBeCalledTimes(1); + expect(issue.retry).toHaveBeenCalledTimes(1); vi.advanceTimersByTime(RETRY_EXPONENTIAL_BASE_SECONDS * 2 * 0.75 * 1000); - expect(issue.retry).toBeCalledTimes(2); + expect(issue.retry).toHaveBeenCalledTimes(2); }); }); @@ -837,7 +841,7 @@ describe('IssueManager', () => { manager.reset('config_error'); - expect(issue.reset).toBeCalled(); + expect(issue.reset).toHaveBeenCalled(); }); it('should skip reset when targeted key has no active issue', () => { @@ -854,8 +858,8 @@ describe('IssueManager', () => { manager.reset('config_error'); - expect(issue.reset).not.toBeCalled(); - expect(issue.detectDynamic).not.toBeCalled(); + expect(issue.reset).not.toHaveBeenCalled(); + expect(issue.detectDynamic).not.toHaveBeenCalled(); }); }); @@ -868,7 +872,7 @@ describe('IssueManager', () => { vi.advanceTimersByTime(5000); - expect(issue.retry).not.toBeCalled(); + expect(issue.retry).not.toHaveBeenCalled(); }); it('should gate evaluate while suspended', () => { @@ -884,7 +888,7 @@ describe('IssueManager', () => { manager.suspend(); manager.evaluate(); - expect(issue.detectDynamic).not.toBeCalled(); + expect(issue.detectDynamic).not.toHaveBeenCalled(); }); it('should preserve issue state across suspend', () => { @@ -918,8 +922,8 @@ describe('IssueManager', () => { manager.suspend(); manager.resume(); - expect(issue.detectDynamic).toBeCalled(); - expect(api.getCardElementManager().update).toBeCalled(); + expect(issue.detectDynamic).toHaveBeenCalled(); + expect(api.getCardElementManager().update).toHaveBeenCalled(); }); it('should invoke Issue.suspend on timer-backed issues when suspended', () => { @@ -931,7 +935,7 @@ describe('IssueManager', () => { manager.suspend(); - expect(issue.suspend).toBeCalled(); + expect(issue.suspend).toHaveBeenCalled(); }); it('should tolerate issues without a suspend hook', () => { @@ -961,8 +965,8 @@ describe('IssueManager', () => { vi.advanceTimersByTime(5000); - expect(issue.retry).not.toBeCalled(); - expect(issue.reset).toBeCalled(); + expect(issue.retry).not.toHaveBeenCalled(); + expect(issue.reset).toHaveBeenCalled(); }); }); }); diff --git a/tests/card-controller/issues/issues/config-upgrade-failure.test.ts b/tests/card-controller/issues/issues/config-upgrade-failure.test.ts index b7937ab3..a198d782 100644 --- a/tests/card-controller/issues/issues/config-upgrade-failure.test.ts +++ b/tests/card-controller/issues/issues/config-upgrade-failure.test.ts @@ -31,7 +31,7 @@ describe('ConfigUpgradeFailureIssue', () => { await issue.detectStatic(); expect(issue.hasIssue()).toBe(true); - expect(hasConfigUpgradeFailures).toBeCalledWith(rawConfig); + expect(hasConfigUpgradeFailures).toHaveBeenCalledWith(rawConfig); expect(issue.getIssue()).toEqual( expect.objectContaining({ icon: 'mdi:update', diff --git a/tests/card-controller/issues/issues/config-upgrade.test.ts b/tests/card-controller/issues/issues/config-upgrade.test.ts index 87033a62..c8f29e02 100644 --- a/tests/card-controller/issues/issues/config-upgrade.test.ts +++ b/tests/card-controller/issues/issues/config-upgrade.test.ts @@ -27,7 +27,7 @@ describe('ConfigUpgradeIssue', () => { await issue.detectStatic(); expect(issue.hasIssue()).toBe(true); - expect(isConfigUpgradeable).toBeCalledWith(rawConfig); + expect(isConfigUpgradeable).toHaveBeenCalledWith(rawConfig); }); it('should detect non-upgradeable config', async () => { diff --git a/tests/card-controller/issues/issues/event-subscription.test.ts b/tests/card-controller/issues/issues/event-subscription.test.ts index 9f2f02cd..efe0277d 100644 --- a/tests/card-controller/issues/issues/event-subscription.test.ts +++ b/tests/card-controller/issues/issues/event-subscription.test.ts @@ -19,7 +19,7 @@ describe('EventSubscriptionIssue', () => { new EventSubscriptionIssue(health, changeCallback); - expect(health.addListener).toBeCalledWith(changeCallback); + expect(health.addListener).toHaveBeenCalledWith(changeCallback); }); it('should have no issue when there are no failures', () => { @@ -65,7 +65,7 @@ describe('EventSubscriptionIssue', () => { const issue = new EventSubscriptionIssue(health, vi.fn()); expect(issue.retry()).toBe(true); - expect(health.retry).toBeCalledTimes(1); + expect(health.retry).toHaveBeenCalledTimes(1); }); it('should not opt into IssueManager-scheduled retries', () => { @@ -84,6 +84,6 @@ describe('EventSubscriptionIssue', () => { issue.destroy(); - expect(unsubscribe).toBeCalledTimes(1); + expect(unsubscribe).toHaveBeenCalledTimes(1); }); }); diff --git a/tests/card-controller/issues/issues/initialization.test.ts b/tests/card-controller/issues/issues/initialization.test.ts index 59c406dc..e78e7fc9 100644 --- a/tests/card-controller/issues/issues/initialization.test.ts +++ b/tests/card-controller/issues/issues/initialization.test.ts @@ -77,7 +77,7 @@ describe('InitializationIssue', () => { const tapAction = control.actions?.tap_action as InternalCallbackActionConfig; await tapAction.callback(api); - expect(api.getIssueManager().retry).toBeCalledWith('initialization', true); + expect(api.getIssueManager().retry).toHaveBeenCalledWith('initialization', true); }); describe('detectDynamic', () => { @@ -190,8 +190,8 @@ describe('InitializationIssue', () => { expect(result).toBe(false); expect(issue.hasIssue()).toBe(false); - expect(api.getInitializationManager().uninitializeMandatory).toBeCalled(); - expect(api.getCameraManager().destroy).toBeCalled(); + expect(api.getInitializationManager().uninitializeMandatory).toHaveBeenCalled(); + expect(api.getCameraManager().destroy).toHaveBeenCalled(); }); it('should be a no-op while a retry is already in flight', () => { @@ -205,8 +205,10 @@ describe('InitializationIssue', () => { const result = issue.retry(); expect(result).toBe(false); - expect(api.getInitializationManager().uninitializeMandatory).not.toBeCalled(); - expect(api.getCameraManager().destroy).not.toBeCalled(); + expect( + api.getInitializationManager().uninitializeMandatory, + ).not.toHaveBeenCalled(); + expect(api.getCameraManager().destroy).not.toHaveBeenCalled(); }); }); diff --git a/tests/card-controller/issues/issues/legacy-resource.test.ts b/tests/card-controller/issues/issues/legacy-resource.test.ts index 56de1571..e7257a17 100644 --- a/tests/card-controller/issues/issues/legacy-resource.test.ts +++ b/tests/card-controller/issues/issues/legacy-resource.test.ts @@ -179,14 +179,14 @@ describe('LegacyResourceIssue', () => { const result = await issue.fix(hass); expect(result).toBe(true); - expect(hass.callWS).toBeCalledWith( + expect(hass.callWS).toHaveBeenCalledWith( expect.objectContaining({ type: 'lovelace/resources/delete', resource_id: '1', }), ); expect(issue.hasIssue()).toBe(false); - expect(onChange).toBeCalled(); + expect(onChange).toHaveBeenCalled(); }); it('should not fix when only legacy resource exists', async () => { @@ -238,7 +238,7 @@ describe('LegacyResourceIssue', () => { const result = await issue.fix(hass); expect(result).toBe(false); - expect(onChange).not.toBeCalled(); + expect(onChange).not.toHaveBeenCalled(); }); it('should return false when the verification fetch silently fails after a successful delete', async () => { @@ -272,7 +272,7 @@ describe('LegacyResourceIssue', () => { const result = await issue.fix(hass); expect(result).toBe(false); - expect(onChange).not.toBeCalled(); + expect(onChange).not.toHaveBeenCalled(); }); it('should return false when re-detection still finds legacy resource', async () => { @@ -314,7 +314,7 @@ describe('LegacyResourceIssue', () => { const result = await issue.fix(hass); expect(result).toBe(false); - expect(onChange).not.toBeCalled(); + expect(onChange).not.toHaveBeenCalled(); }); it('should fix multiple legacy resources', async () => { @@ -437,7 +437,7 @@ describe('LegacyResourceIssue', () => { await callback?.(api); - expect(hass.callWS).toBeCalledWith( + expect(hass.callWS).toHaveBeenCalledWith( expect.objectContaining({ type: 'lovelace/resources/delete', }), diff --git a/tests/card-controller/issues/issues/media-query.test.ts b/tests/card-controller/issues/issues/media-query.test.ts index 92e19d7a..e1b5fbb3 100644 --- a/tests/card-controller/issues/issues/media-query.test.ts +++ b/tests/card-controller/issues/issues/media-query.test.ts @@ -87,7 +87,7 @@ describe('MediaQueryIssue', () => { const tapAction = control?.actions?.tap_action as InternalCallbackActionConfig; await tapAction.callback(api); - expect(api.getIssueManager().retry).toBeCalledWith('media_query', true); + expect(api.getIssueManager().retry).toHaveBeenCalledWith('media_query', true); }); }); @@ -133,7 +133,7 @@ describe('MediaQueryIssue', () => { const result = issue.retry(); expect(result).toEqual(true); - expect(api.getViewManager().setViewByParametersWithNewQuery).toBeCalledWith({ + expect(api.getViewManager().setViewByParametersWithNewQuery).toHaveBeenCalledWith({ intent: 'retry', }); @@ -189,7 +189,9 @@ describe('MediaQueryIssue', () => { const result = issue.retry(); expect(result).toBe(true); - expect(api.getViewManager().setViewByParametersWithNewQuery).toBeCalledTimes(1); + expect(api.getViewManager().setViewByParametersWithNewQuery).toHaveBeenCalledTimes( + 1, + ); }); it('should stop treating the retry as in flight once the query settles without an outcome', async () => { diff --git a/tests/card-controller/issues/issues/media-unavailable.test.ts b/tests/card-controller/issues/issues/media-unavailable.test.ts index b0ed0f4d..03a34980 100644 --- a/tests/card-controller/issues/issues/media-unavailable.test.ts +++ b/tests/card-controller/issues/issues/media-unavailable.test.ts @@ -69,7 +69,7 @@ describe('MediaUnavailableIssue', () => { vi.advanceTimersByTime(10000); expect(issue.hasIssue()).toBe(true); - expect(onChange).toBeCalled(); + expect(onChange).toHaveBeenCalled(); }); it('should not start timer when targetID is null (no provider rendering)', () => { @@ -229,7 +229,7 @@ describe('MediaUnavailableIssue', () => { // Full 10s from camera-2's timer start. vi.advanceTimersByTime(5000); expect(issue.hasIssue()).toBe(true); - expect(onChange).toBeCalledTimes(1); + expect(onChange).toHaveBeenCalledTimes(1); }); it('should not restart timer for same target while running', () => { @@ -251,7 +251,7 @@ describe('MediaUnavailableIssue', () => { // 5 more seconds completes the original 10s timer. vi.advanceTimersByTime(5000); expect(issue.hasIssue()).toBe(true); - expect(onChange).toBeCalledTimes(1); + expect(onChange).toHaveBeenCalledTimes(1); }); it('should not restart timer when targetID is undefined and matches', () => { @@ -266,7 +266,7 @@ describe('MediaUnavailableIssue', () => { vi.advanceTimersByTime(5000); expect(issue.hasIssue()).toBe(true); - expect(onChange).toBeCalledTimes(1); + expect(onChange).toHaveBeenCalledTimes(1); }); it('should not restart timer if already timed out', () => { @@ -275,12 +275,12 @@ describe('MediaUnavailableIssue', () => { issue.detectDynamic({ targetID: 'camera-1', view: 'live' }); vi.advanceTimersByTime(10000); - expect(onChange).toBeCalledTimes(1); + expect(onChange).toHaveBeenCalledTimes(1); // Calling detectDynamic again should not restart timer. issue.detectDynamic({ targetID: 'camera-1', view: 'live' }); vi.advanceTimersByTime(10000); - expect(onChange).toBeCalledTimes(1); + expect(onChange).toHaveBeenCalledTimes(1); }); }); @@ -535,7 +535,10 @@ describe('MediaUnavailableIssue', () => { const tapAction = control?.actions?.tap_action as InternalCallbackActionConfig; await tapAction.callback(api); - expect(api.getIssueManager().retry).toBeCalledWith('media_unavailable', true); + expect(api.getIssueManager().retry).toHaveBeenCalledWith( + 'media_unavailable', + true, + ); }); }); @@ -618,7 +621,7 @@ describe('MediaUnavailableIssue', () => { const result = issue.retry(); expect(result).toEqual(false); - expect(api.getViewManager().setViewWithMergedContext).toBeCalledWith({ + expect(api.getViewManager().setViewWithMergedContext).toHaveBeenCalledWith({ mediaEpoch: { 'camera-1': 1, 'media-1': 1 }, }); }); @@ -633,7 +636,7 @@ describe('MediaUnavailableIssue', () => { const result = issue.retry(); expect(result).toEqual(false); - expect(api.getViewManager().setViewWithMergedContext).toBeCalledWith({ + expect(api.getViewManager().setViewWithMergedContext).toHaveBeenCalledWith({ mediaEpoch: { [IMAGE_VIEW_TARGET_ID_SENTINEL]: 1 }, }); }); @@ -650,7 +653,7 @@ describe('MediaUnavailableIssue', () => { const result = issue.retry(); expect(result).toEqual(false); - expect(api.getViewManager().setViewWithMergedContext).toBeCalledWith({ + expect(api.getViewManager().setViewWithMergedContext).toHaveBeenCalledWith({ mediaEpoch: { 'camera-1': 6, 'camera-2': 3 }, }); }); @@ -665,7 +668,7 @@ describe('MediaUnavailableIssue', () => { issue.retry(); - expect(api.getViewManager().setViewWithMergedContext).toBeCalledWith({ + expect(api.getViewManager().setViewWithMergedContext).toHaveBeenCalledWith({ mediaEpoch: { 'camera-1': 1 }, }); }); @@ -701,7 +704,7 @@ describe('MediaUnavailableIssue', () => { vi.advanceTimersByTime(10000); expect(issue.hasIssue()).toBe(false); - expect(onChange).not.toBeCalled(); + expect(onChange).not.toHaveBeenCalled(); }); }); @@ -714,7 +717,7 @@ describe('MediaUnavailableIssue', () => { issue.trigger({ targetID: 'camera-1', reason: 'stalled' }); fireMediaLoad(api, 'camera-1'); - expect(onChange).toBeCalled(); + expect(onChange).toHaveBeenCalled(); }); it('should unsubscribe from media loads on destroy', () => { @@ -725,7 +728,7 @@ describe('MediaUnavailableIssue', () => { issue.destroy(); - expect(unsubscribe).toBeCalled(); + expect(unsubscribe).toHaveBeenCalled(); }); }); @@ -746,7 +749,7 @@ describe('MediaUnavailableIssue', () => { // offscreen and that time does not count against them. vi.advanceTimersByTime(20000); expect(issue.hasIssue()).toBe(false); - expect(onChange).not.toBeCalled(); + expect(onChange).not.toHaveBeenCalled(); }); it('should preserve an already-active issue across suspend', () => { @@ -780,7 +783,7 @@ describe('MediaUnavailableIssue', () => { expect(issue.hasIssue()).toBe(false); vi.advanceTimersByTime(1); expect(issue.hasIssue()).toBe(true); - expect(onChange).toBeCalled(); + expect(onChange).toHaveBeenCalled(); }); }); }); diff --git a/tests/card-controller/issues/retry-control.test.ts b/tests/card-controller/issues/retry-control.test.ts index b0732479..5c4c21f6 100644 --- a/tests/card-controller/issues/retry-control.test.ts +++ b/tests/card-controller/issues/retry-control.test.ts @@ -20,6 +20,6 @@ describe('createRetryControl', () => { const tapAction = control.actions?.tap_action as InternalCallbackActionConfig; await tapAction.callback(api); - expect(api.getIssueManager().retry).toBeCalledWith('media_query', true); + expect(api.getIssueManager().retry).toHaveBeenCalledWith('media_query', true); }); }); diff --git a/tests/card-controller/issues/state-manager.test.ts b/tests/card-controller/issues/state-manager.test.ts index 57d33069..8ab903e7 100644 --- a/tests/card-controller/issues/state-manager.test.ts +++ b/tests/card-controller/issues/state-manager.test.ts @@ -65,9 +65,9 @@ describe('IssueStateManager', () => { assert(mockConfigUpgrade.detectStatic); assert(mockLegacyResource.detectStatic); assert(mockMediaLoad.detectStatic); - expect(mockConfigUpgrade.detectStatic).toBeCalledWith(hass); - expect(mockLegacyResource.detectStatic).toBeCalledWith(hass); - expect(mockMediaLoad.detectStatic).toBeCalledWith(hass); + expect(mockConfigUpgrade.detectStatic).toHaveBeenCalledWith(hass); + expect(mockLegacyResource.detectStatic).toHaveBeenCalledWith(hass); + expect(mockMediaLoad.detectStatic).toHaveBeenCalledWith(hass); }); it('should isolate a failing issue and continue detecting the rest', async () => { @@ -87,8 +87,8 @@ describe('IssueStateManager', () => { await expect(manager.detectStatic(hass)).resolves.toBeUndefined(); // Detection continued to the final issue despite the two earlier failures. - expect(mockMediaLoad.detectStatic).toBeCalledWith(hass); - expect(spy).toBeCalledTimes(2); + expect(mockMediaLoad.detectStatic).toHaveBeenCalledWith(hass); + expect(spy).toHaveBeenCalledTimes(2); spy.mockRestore(); }); }); @@ -100,7 +100,7 @@ describe('IssueStateManager', () => { manager.trigger('media_unavailable', { targetID: 'cam1', reason: 'stalled' }); assert(mockMediaLoad.trigger); - expect(mockMediaLoad.trigger).toBeCalledWith({ + expect(mockMediaLoad.trigger).toHaveBeenCalledWith({ targetID: 'cam1', reason: 'stalled', }); @@ -112,7 +112,7 @@ describe('IssueStateManager', () => { manager.trigger('unknown' as never, {} as never); assert(mockMediaLoad.trigger); - expect(mockMediaLoad.trigger).not.toBeCalled(); + expect(mockMediaLoad.trigger).not.toHaveBeenCalled(); }); }); @@ -123,7 +123,7 @@ describe('IssueStateManager', () => { manager.detectDynamic({ view: 'live' }); assert(mockMediaLoad.detectDynamic); - expect(mockMediaLoad.detectDynamic).toBeCalledWith({ view: 'live' }); + expect(mockMediaLoad.detectDynamic).toHaveBeenCalledWith({ view: 'live' }); }); }); @@ -255,7 +255,7 @@ describe('IssueStateManager', () => { const manager = createManager(); manager.retry(); - expect(mockMediaLoad.retry).toBeCalled(); + expect(mockMediaLoad.retry).toHaveBeenCalled(); }); it('should call retry on issues that want retry with exclusive result', () => { @@ -266,7 +266,7 @@ describe('IssueStateManager', () => { createManager().retry(); - expect(mockMediaLoad.retry).toBeCalled(); + expect(mockMediaLoad.retry).toHaveBeenCalled(); }); it('should not call retry on issues that do not want retry', () => { @@ -277,7 +277,7 @@ describe('IssueStateManager', () => { manager.retry(); assert(mockMediaLoad.retry); - expect(mockMediaLoad.retry).not.toBeCalled(); + expect(mockMediaLoad.retry).not.toHaveBeenCalled(); }); it('should stop after exclusive result and not call retry on subsequent issues', () => { @@ -293,9 +293,9 @@ describe('IssueStateManager', () => { const manager = createManager(); manager.retry(); - expect(mockConfigUpgrade.retry).toBeCalled(); + expect(mockConfigUpgrade.retry).toHaveBeenCalled(); assert(mockMediaLoad.retry); - expect(mockMediaLoad.retry).not.toBeCalled(); + expect(mockMediaLoad.retry).not.toHaveBeenCalled(); }); it('should continue after non-exclusive result and call retry on subsequent issues', () => { @@ -312,8 +312,8 @@ describe('IssueStateManager', () => { const manager = createManager(); manager.retry(); - expect(mockConfigUpgrade.retry).toBeCalled(); - expect(mockMediaLoad.retry).toBeCalled(); + expect(mockConfigUpgrade.retry).toHaveBeenCalled(); + expect(mockMediaLoad.retry).toHaveBeenCalled(); }); }); @@ -326,7 +326,7 @@ describe('IssueStateManager', () => { createManager().retry('media_unavailable'); - expect(mockMediaLoad.retry).toBeCalled(); + expect(mockMediaLoad.retry).toHaveBeenCalled(); }); it('should not call retry on the matching issue when needsRetry is false', () => { @@ -336,7 +336,7 @@ describe('IssueStateManager', () => { createManager().retry('media_unavailable'); assert(mockMediaLoad.retry); - expect(mockMediaLoad.retry).not.toBeCalled(); + expect(mockMediaLoad.retry).not.toHaveBeenCalled(); }); it('should call retry when force is true even if needsRetry is false', () => { @@ -347,14 +347,14 @@ describe('IssueStateManager', () => { createManager().retry('media_unavailable', true); - expect(mockMediaLoad.retry).toBeCalled(); + expect(mockMediaLoad.retry).toHaveBeenCalled(); }); it('should do nothing for unknown key', () => { createManager().retry('unknown' as never); assert(mockMediaLoad.retry); - expect(mockMediaLoad.retry).not.toBeCalled(); + expect(mockMediaLoad.retry).not.toHaveBeenCalled(); }); }); @@ -383,7 +383,7 @@ describe('IssueStateManager', () => { const manager = createManager(); await manager.detectStatic(createHASS()); - expect(spy).toBeCalledWith( + expect(spy).toHaveBeenCalledWith( 'Advanced Camera Card [issue=legacy_resource]: Legacy issue', ); spy.mockRestore(); @@ -400,7 +400,7 @@ describe('IssueStateManager', () => { const manager = createManager(); manager.detectDynamic({ view: 'live' }); - expect(spy).toBeCalledWith( + expect(spy).toHaveBeenCalledWith( 'Advanced Camera Card [issue=media_unavailable]: Stream issue', ); spy.mockRestore(); @@ -416,7 +416,7 @@ describe('IssueStateManager', () => { const manager = createManager(); manager.trigger('media_unavailable', { targetID: 'cam1', reason: 'stalled' }); - expect(spy).toBeCalledWith( + expect(spy).toHaveBeenCalledWith( 'Advanced Camera Card [issue=media_unavailable]: Triggered', ); spy.mockRestore(); @@ -429,7 +429,7 @@ describe('IssueStateManager', () => { const manager = createManager(); manager.trigger('media_unavailable', { targetID: 'cam1', reason: 'stalled' }); - expect(spy).not.toBeCalled(); + expect(spy).not.toHaveBeenCalled(); spy.mockRestore(); }); @@ -439,7 +439,7 @@ describe('IssueStateManager', () => { const manager = createManager(); manager.trigger('unknown' as never, {} as never); - expect(spy).not.toBeCalled(); + expect(spy).not.toHaveBeenCalled(); spy.mockRestore(); }); @@ -455,7 +455,7 @@ describe('IssueStateManager', () => { await manager.detectStatic(createHASS()); await manager.detectStatic(createHASS()); - expect(spy).toBeCalledTimes(1); + expect(spy).toHaveBeenCalledTimes(1); spy.mockRestore(); }); @@ -466,7 +466,7 @@ describe('IssueStateManager', () => { const manager = createManager(); await manager.detectStatic(createHASS()); - expect(spy).not.toBeCalled(); + expect(spy).not.toHaveBeenCalled(); spy.mockRestore(); }); @@ -482,7 +482,7 @@ describe('IssueStateManager', () => { const manager = createManager(); await manager.detectStatic(createHASS()); - expect(spy).not.toBeCalled(); + expect(spy).not.toHaveBeenCalled(); spy.mockRestore(); }); @@ -507,7 +507,7 @@ describe('IssueStateManager', () => { // Re-activate with a different payload → log Second. await manager.detectStatic(createHASS()); - expect(spy).toBeCalledTimes(2); + expect(spy).toHaveBeenCalledTimes(2); expect(spy).toHaveBeenNthCalledWith( 1, 'Advanced Camera Card [issue=legacy_resource]: First', @@ -539,7 +539,7 @@ describe('IssueStateManager', () => { // Then it re-activates (e.g. new trigger arrives). manager.detectDynamic({ view: 'live' }); - expect(spy).toBeCalledTimes(2); + expect(spy).toHaveBeenCalledTimes(2); spy.mockRestore(); }); }); @@ -550,9 +550,9 @@ describe('IssueStateManager', () => { manager.reset('media_unavailable'); assert(mockMediaLoad.reset); - expect(mockMediaLoad.reset).toBeCalled(); + expect(mockMediaLoad.reset).toHaveBeenCalled(); assert(mockConfigUpgrade.reset); - expect(mockConfigUpgrade.reset).not.toBeCalled(); + expect(mockConfigUpgrade.reset).not.toHaveBeenCalled(); }); it('should reset all issues when no key is given', () => { @@ -562,9 +562,9 @@ describe('IssueStateManager', () => { assert(mockConfigUpgrade.reset); assert(mockLegacyResource.reset); assert(mockMediaLoad.reset); - expect(mockConfigUpgrade.reset).toBeCalled(); - expect(mockLegacyResource.reset).toBeCalled(); - expect(mockMediaLoad.reset).toBeCalled(); + expect(mockConfigUpgrade.reset).toHaveBeenCalled(); + expect(mockLegacyResource.reset).toHaveBeenCalled(); + expect(mockMediaLoad.reset).toHaveBeenCalled(); }); it('should do nothing for unknown key', () => { @@ -572,7 +572,7 @@ describe('IssueStateManager', () => { manager.reset('unknown' as never); assert(mockMediaLoad.reset); - expect(mockMediaLoad.reset).not.toBeCalled(); + expect(mockMediaLoad.reset).not.toHaveBeenCalled(); }); }); @@ -584,9 +584,9 @@ describe('IssueStateManager', () => { assert(mockConfigUpgrade.suspend); assert(mockLegacyResource.suspend); assert(mockMediaLoad.suspend); - expect(mockConfigUpgrade.suspend).toBeCalled(); - expect(mockLegacyResource.suspend).toBeCalled(); - expect(mockMediaLoad.suspend).toBeCalled(); + expect(mockConfigUpgrade.suspend).toHaveBeenCalled(); + expect(mockLegacyResource.suspend).toHaveBeenCalled(); + expect(mockMediaLoad.suspend).toHaveBeenCalled(); }); }); @@ -598,16 +598,16 @@ describe('IssueStateManager', () => { assert(mockConfigUpgrade.reset); assert(mockLegacyResource.reset); assert(mockMediaLoad.reset); - expect(mockConfigUpgrade.reset).toBeCalled(); - expect(mockLegacyResource.reset).toBeCalled(); - expect(mockMediaLoad.reset).toBeCalled(); + expect(mockConfigUpgrade.reset).toHaveBeenCalled(); + expect(mockLegacyResource.reset).toHaveBeenCalled(); + expect(mockMediaLoad.reset).toHaveBeenCalled(); assert(mockConfigUpgrade.destroy); assert(mockLegacyResource.destroy); assert(mockMediaLoad.destroy); - expect(mockConfigUpgrade.destroy).toBeCalled(); - expect(mockLegacyResource.destroy).toBeCalled(); - expect(mockMediaLoad.destroy).toBeCalled(); + expect(mockConfigUpgrade.destroy).toHaveBeenCalled(); + expect(mockLegacyResource.destroy).toHaveBeenCalled(); + expect(mockMediaLoad.destroy).toHaveBeenCalled(); expect(manager.getIssuePresence().size).toBe(0); }); diff --git a/tests/card-controller/keyboard-state-manager.test.ts b/tests/card-controller/keyboard-state-manager.test.ts index cd38c492..5e4400ae 100644 --- a/tests/card-controller/keyboard-state-manager.test.ts +++ b/tests/card-controller/keyboard-state-manager.test.ts @@ -27,7 +27,7 @@ describe('KeyboardStateManager', () => { element.dispatchEvent(new KeyboardEvent('keydown', { key: 'a' })); // Duplicate keydown should not re-set the state. - expect(api.getConditionStateManager().setState).toBeCalledTimes(1); + expect(api.getConditionStateManager().setState).toHaveBeenCalledTimes(1); }); it('should set state on keyup', () => { @@ -40,12 +40,12 @@ describe('KeyboardStateManager', () => { element.dispatchEvent(new KeyboardEvent('keyup', { key: 'a' })); // Key not held down in the first place should not update the state. - expect(api.getConditionStateManager().setState).not.toBeCalled(); + expect(api.getConditionStateManager().setState).not.toHaveBeenCalled(); element.dispatchEvent(new KeyboardEvent('keydown', { key: 'a' })); element.dispatchEvent(new KeyboardEvent('keyup', { key: 'a' })); - expect(api.getConditionStateManager().setState).toBeCalledTimes(2); + expect(api.getConditionStateManager().setState).toHaveBeenCalledTimes(2); expect(api.getConditionStateManager().setState).toHaveBeenLastCalledWith({ keys: { a: { state: 'up', ctrl: false, alt: false, meta: false, shift: false }, @@ -61,12 +61,12 @@ describe('KeyboardStateManager', () => { manager.initialize(); element.dispatchEvent(new FocusEvent('blur')); - expect(api.getConditionStateManager().setState).not.toBeCalled(); + expect(api.getConditionStateManager().setState).not.toHaveBeenCalled(); element.dispatchEvent(new KeyboardEvent('keydown', { key: 'a' })); element.dispatchEvent(new FocusEvent('blur')); - expect(api.getConditionStateManager().setState).toBeCalledTimes(2); + expect(api.getConditionStateManager().setState).toHaveBeenCalledTimes(2); expect(api.getConditionStateManager().setState).toHaveBeenLastCalledWith({ keys: {}, }); @@ -82,7 +82,7 @@ describe('KeyboardStateManager', () => { element.dispatchEvent(new KeyboardEvent('keydown', { key: 'a' })); - expect(api.getConditionStateManager().setState).not.toBeCalled(); + expect(api.getConditionStateManager().setState).not.toHaveBeenCalled(); }); it('should clear held keys on uninitialize', () => { @@ -97,7 +97,7 @@ describe('KeyboardStateManager', () => { manager.uninitialize(); - expect(api.getConditionStateManager().setState).toBeCalledWith({ keys: {} }); + expect(api.getConditionStateManager().setState).toHaveBeenCalledWith({ keys: {} }); }); it('should not set state on uninitialize when no keys held', () => { @@ -108,6 +108,6 @@ describe('KeyboardStateManager', () => { manager.initialize(); manager.uninitialize(); - expect(api.getConditionStateManager().setState).not.toBeCalled(); + expect(api.getConditionStateManager().setState).not.toHaveBeenCalled(); }); }); diff --git a/tests/card-controller/lock/manager.test.ts b/tests/card-controller/lock/manager.test.ts index 0ef3cadd..059dc424 100644 --- a/tests/card-controller/lock/manager.test.ts +++ b/tests/card-controller/lock/manager.test.ts @@ -12,7 +12,8 @@ import { createSubstreamOnAction, createViewAction, } from '../../../src/utils/action'; -import { createCardAPI, createConfig } from '../../test-utils'; +import { createConfig } from '../../config/test-utils'; +import { createCardAPI } from '../../test-utils'; const setCallLock = (api: CardController, lock: boolean): void => { vi.mocked(api.getConfigManager().getConfig).mockReturnValue( diff --git a/tests/card-controller/media-info-manager.test.ts b/tests/card-controller/media-info-manager.test.ts index a284d3fc..f3dc2812 100644 --- a/tests/card-controller/media-info-manager.test.ts +++ b/tests/card-controller/media-info-manager.test.ts @@ -22,7 +22,7 @@ describe('MediaLoadedInfoManager', () => { manager.initialize(); - expect(api.getConditionStateManager().setState).toBeCalledWith({ + expect(api.getConditionStateManager().setState).toHaveBeenCalledWith({ mediaLoadedInfo: null, }); }); @@ -40,11 +40,11 @@ describe('MediaLoadedInfoManager', () => { expect(manager.has()).toBeTruthy(); expect(manager.get()).toBe(info); - expect(api.getConditionStateManager().setState).toBeCalledWith({ + expect(api.getConditionStateManager().setState).toHaveBeenCalledWith({ mediaLoadedInfo: info, }); - expect(api.getStyleManager().setExpandedMode).toBeCalled(); - expect(api.getCardElementManager().update).toBeCalled(); + expect(api.getStyleManager().setExpandedMode).toHaveBeenCalled(); + expect(api.getCardElementManager().update).toHaveBeenCalled(); }); it('should cache info for non-selected targets without side effects', () => { @@ -57,15 +57,15 @@ describe('MediaLoadedInfoManager', () => { expect(manager.has()).toBeFalsy(); expect(manager.get()).toBeNull(); - expect(api.getConditionStateManager().setState).not.toBeCalled(); - expect(api.getStyleManager().setExpandedMode).not.toBeCalled(); - expect(api.getCardElementManager().update).not.toBeCalled(); + expect(api.getConditionStateManager().setState).not.toHaveBeenCalled(); + expect(api.getStyleManager().setExpandedMode).not.toHaveBeenCalled(); + expect(api.getCardElementManager().update).not.toHaveBeenCalled(); manager.setSelected('target-1'); expect(manager.has()).toBeTruthy(); expect(manager.get()).toBe(info); - expect(api.getConditionStateManager().setState).toBeCalledWith({ + expect(api.getConditionStateManager().setState).toHaveBeenCalledWith({ mediaLoadedInfo: info, }); }); @@ -86,9 +86,9 @@ describe('MediaLoadedInfoManager', () => { expect(manager.has()).toBeFalsy(); expect(manager.get()).toBeNull(); - expect(api.getConditionStateManager().setState).not.toBeCalled(); - expect(api.getStyleManager().setExpandedMode).not.toBeCalled(); - expect(api.getCardElementManager().update).not.toBeCalled(); + expect(api.getConditionStateManager().setState).not.toHaveBeenCalled(); + expect(api.getStyleManager().setExpandedMode).not.toHaveBeenCalled(); + expect(api.getCardElementManager().update).not.toHaveBeenCalled(); }); it('should reject info without a targetID', () => { @@ -134,7 +134,7 @@ describe('MediaLoadedInfoManager', () => { vi.clearAllMocks(); manager.setSelected('target-1'); - expect(api.getConditionStateManager().setState).not.toBeCalled(); + expect(api.getConditionStateManager().setState).not.toHaveBeenCalled(); }); it('should emit null condition state when selecting a target with no info', () => { @@ -143,7 +143,7 @@ describe('MediaLoadedInfoManager', () => { manager.setSelected('target-1'); - expect(api.getConditionStateManager().setState).toBeCalledWith({ + expect(api.getConditionStateManager().setState).toHaveBeenCalledWith({ mediaLoadedInfo: null, }); }); @@ -199,7 +199,7 @@ describe('MediaLoadedInfoManager', () => { ); expect(manager.get()).toBe(info); - expect(api.getConditionStateManager().setState).toBeCalledWith({ + expect(api.getConditionStateManager().setState).toHaveBeenCalledWith({ mediaLoadedInfo: info, }); }); @@ -299,7 +299,7 @@ describe('MediaLoadedInfoManager', () => { ac.abort(); - expect(api.getConditionStateManager().setState).not.toBeCalled(); + expect(api.getConditionStateManager().setState).not.toHaveBeenCalled(); }); }); @@ -317,7 +317,7 @@ describe('MediaLoadedInfoManager', () => { manager.clear(); expect(manager.has()).toBeFalsy(); - expect(api.getConditionStateManager().setState).toBeCalledWith({ + expect(api.getConditionStateManager().setState).toHaveBeenCalledWith({ mediaLoadedInfo: null, }); }); @@ -328,7 +328,7 @@ describe('MediaLoadedInfoManager', () => { manager.clear(); - expect(api.getConditionStateManager().setState).not.toBeCalled(); + expect(api.getConditionStateManager().setState).not.toHaveBeenCalled(); }); it('should not fire condition state when the selected target has no info', () => { @@ -339,7 +339,7 @@ describe('MediaLoadedInfoManager', () => { vi.clearAllMocks(); manager.clear(); - expect(api.getConditionStateManager().setState).not.toBeCalled(); + expect(api.getConditionStateManager().setState).not.toHaveBeenCalled(); }); }); @@ -437,7 +437,7 @@ describe('MediaLoadedInfoManager', () => { manager.subscribe(listener); manager.set(info, owner); - expect(listener).toBeCalledWith({ + expect(listener).toHaveBeenCalledWith({ type: 'load', targetID: 'target-1', info, @@ -455,7 +455,7 @@ describe('MediaLoadedInfoManager', () => { manager.subscribe(listener); manager.set(info, owner, true); - expect(listener).toBeCalledWith({ + expect(listener).toHaveBeenCalledWith({ type: 'load', targetID: 'target-1', info, @@ -472,8 +472,8 @@ describe('MediaLoadedInfoManager', () => { manager.setSelected('target-1'); manager.setSelected(null); - expect(listener).toBeCalledWith({ type: 'select', targetID: 'target-1' }); - expect(listener).toBeCalledWith({ type: 'select', targetID: null }); + expect(listener).toHaveBeenCalledWith({ type: 'select', targetID: 'target-1' }); + expect(listener).toHaveBeenCalledWith({ type: 'select', targetID: null }); }); it('should notify of an unload when a target is retired', () => { @@ -490,7 +490,7 @@ describe('MediaLoadedInfoManager', () => { ); ac.abort(); - expect(listener).toBeCalledWith({ type: 'unload', targetID: 'target-1' }); + expect(listener).toHaveBeenCalledWith({ type: 'unload', targetID: 'target-1' }); }); it('should notify of an unload for each active target on clear', () => { @@ -504,8 +504,8 @@ describe('MediaLoadedInfoManager', () => { manager.subscribe(listener); manager.clear(); - expect(listener).toBeCalledWith({ type: 'unload', targetID: 'target-1' }); - expect(listener).toBeCalledWith({ type: 'unload', targetID: 'target-2' }); + expect(listener).toHaveBeenCalledWith({ type: 'unload', targetID: 'target-1' }); + expect(listener).toHaveBeenCalledWith({ type: 'unload', targetID: 'target-2' }); }); it('should notify of unloads and a deselect on initialize', () => { @@ -519,8 +519,8 @@ describe('MediaLoadedInfoManager', () => { manager.subscribe(listener); manager.initialize(); - expect(listener).toBeCalledWith({ type: 'unload', targetID: 'target-1' }); - expect(listener).toBeCalledWith({ type: 'select', targetID: null }); + expect(listener).toHaveBeenCalledWith({ type: 'unload', targetID: 'target-1' }); + expect(listener).toHaveBeenCalledWith({ type: 'select', targetID: null }); }); it('should stop notifying after unsubscribe', () => { @@ -533,7 +533,7 @@ describe('MediaLoadedInfoManager', () => { manager.subscribe(listener)(); manager.set(info, owner); - expect(listener).not.toBeCalled(); + expect(listener).not.toHaveBeenCalled(); }); }); }); diff --git a/tests/card-controller/media-player-manager.test.ts b/tests/card-controller/media-player-manager.test.ts index 6095d12d..3c127093 100644 --- a/tests/card-controller/media-player-manager.test.ts +++ b/tests/card-controller/media-player-manager.test.ts @@ -10,18 +10,16 @@ import { import type { EntityRegistryManager } from '../../src/ha/registry/entity/types.js'; import type { HomeAssistant } from '../../src/ha/types.js'; import { ViewMediaType } from '../../src/view/item.js'; +import { createCameraManager, createStore } from '../camera-manager/test-utils'; +import { createCameraConfig, createConfig } from '../config/test-utils'; import { EntityRegistryManagerMock } from '../ha/registry/entity/mock.js'; import { - createCameraConfig, - createCameraManager, createCardAPI, - createConfig, createHASS, createRegistryEntity, createStateEntity, - createStore, - TestViewMedia, } from '../test-utils.js'; +import { TestViewMedia } from '../view/test-utils'; const createHASSWithMediaPlayers = (): HomeAssistant => { const attributesSupported = { @@ -152,7 +150,7 @@ describe('MediaPlayerManager', () => { 'media_player.ok3', ]); expect(manager.hasMediaPlayers()).toBeTruthy(); - expect(spy).toBeCalled(); + expect(spy).toHaveBeenCalled(); }); it('should reinitialize when there is a config change', async () => { @@ -210,7 +208,7 @@ describe('MediaPlayerManager', () => { await manager.stop('media_player.foo'); - expect(api.getHASSManager().getHASS()?.callService).toBeCalledWith( + expect(api.getHASSManager().getHASS()?.callService).toHaveBeenCalledWith( 'media_player', 'media_stop', { @@ -236,7 +234,7 @@ describe('MediaPlayerManager', () => { await manager.stop('media_player.foo'); - expect(api.getHASSManager().getHASS()?.callService).toBeCalledWith( + expect(api.getHASSManager().getHASS()?.callService).toHaveBeenCalledWith( 'media_player', 'turn_off', { @@ -258,7 +256,7 @@ describe('MediaPlayerManager', () => { await manager.stop('media_player.foo'); - expect(api.getHASSManager().getHASS()?.callService).not.toBeCalled(); + expect(api.getHASSManager().getHASS()?.callService).not.toHaveBeenCalled(); }); it('should do nothing without hass state', async () => { @@ -268,7 +266,7 @@ describe('MediaPlayerManager', () => { await manager.stop('media_player.foo'); - expect(api.getHASSManager().getHASS()?.callService).not.toBeCalled(); + expect(api.getHASSManager().getHASS()?.callService).not.toHaveBeenCalled(); }); }); @@ -282,7 +280,7 @@ describe('MediaPlayerManager', () => { await manager.playLive('media_player.foo', 'camera'); - expect(api.getHASSManager().getHASS()?.callService).not.toBeCalled(); + expect(api.getHASSManager().getHASS()?.callService).not.toHaveBeenCalled(); }); describe('using standard method', () => { @@ -316,7 +314,7 @@ describe('MediaPlayerManager', () => { await manager.playLive('media_player.foo', 'camera.foo'); - expect(api.getHASSManager().getHASS()?.callService).toBeCalledWith( + expect(api.getHASSManager().getHASS()?.callService).toHaveBeenCalledWith( 'media_player', 'play_media', { @@ -348,7 +346,7 @@ describe('MediaPlayerManager', () => { await manager.playLive('media_player.foo', 'camera.foo'); - expect(api.getHASSManager().getHASS()?.callService).not.toBeCalled(); + expect(api.getHASSManager().getHASS()?.callService).not.toHaveBeenCalled(); }); it('should handle without title and thumbnail', async () => { @@ -369,7 +367,7 @@ describe('MediaPlayerManager', () => { await manager.playLive('media_player.foo', 'camera.foo'); - expect(api.getHASSManager().getHASS()?.callService).toBeCalledWith( + expect(api.getHASSManager().getHASS()?.callService).toHaveBeenCalledWith( 'media_player', 'play_media', { @@ -408,7 +406,7 @@ describe('MediaPlayerManager', () => { await manager.playLive('media_player.foo', 'camera.foo'); - expect(api.getHASSManager().getHASS()?.callService).toBeCalledWith( + expect(api.getHASSManager().getHASS()?.callService).toHaveBeenCalledWith( 'cast', 'show_lovelace_view', { @@ -476,7 +474,7 @@ describe('MediaPlayerManager', () => { await manager.playLive('media_player.foo', 'camera.foo'); - expect(api.getHASSManager().getHASS()?.callService).not.toBeCalled(); + expect(api.getHASSManager().getHASS()?.callService).not.toHaveBeenCalled(); }); }); }); @@ -499,7 +497,7 @@ describe('MediaPlayerManager', () => { await manager.playMedia('media_player.foo', media); - expect(api.getHASSManager().getHASS()?.callService).toBeCalledWith( + expect(api.getHASSManager().getHASS()?.callService).toHaveBeenCalledWith( 'media_player', 'play_media', { diff --git a/tests/card-controller/microphone-manager.test.ts b/tests/card-controller/microphone-manager.test.ts index 9d5a91b2..f0d7a2b2 100644 --- a/tests/card-controller/microphone-manager.test.ts +++ b/tests/card-controller/microphone-manager.test.ts @@ -6,7 +6,8 @@ import { MicrophoneNotSupportedError, } from '../../src/card-controller/microphone-manager'; import type { MicrophoneState } from '../../src/card-controller/types'; -import { createCardAPI, createConfig } from '../test-utils'; +import { createConfig } from '../config/test-utils'; +import { createCardAPI } from '../test-utils'; const navigatorMock: Navigator = { ...mock(), @@ -66,7 +67,7 @@ describe('MicrophoneManager', () => { expect(manager.isConnected()).toBeTruthy(); expect(manager.getStream()).toBe(stream); expect(manager.isMuted()).toBeTruthy(); - expect(api.getCardElementManager().update).toBeCalled(); + expect(api.getCardElementManager().update).toHaveBeenCalled(); }); it('should be unsupported without browser support', () => { @@ -100,7 +101,7 @@ describe('MicrophoneManager', () => { expect(manager.isConnected()).toBeFalsy(); expect(manager.isForbidden()).toBeTruthy(); - expect(api.getCardElementManager().update).toBeCalled(); + expect(api.getCardElementManager().update).toHaveBeenCalled(); }); it('should mute and unmute', async () => { @@ -112,15 +113,15 @@ describe('MicrophoneManager', () => { await manager.connect(); expect(manager.isMuted()).toBeTruthy(); - expect(api.getCardElementManager().update).toBeCalledTimes(1); + expect(api.getCardElementManager().update).toHaveBeenCalledTimes(1); manager.mute(); expect(manager.isMuted()).toBeTruthy(); - expect(api.getCardElementManager().update).toBeCalledTimes(2); + expect(api.getCardElementManager().update).toHaveBeenCalledTimes(2); await manager.unmute(); expect(manager.isMuted()).toBeFalsy(); - expect(api.getCardElementManager().update).toBeCalledTimes(3); + expect(api.getCardElementManager().update).toHaveBeenCalledTimes(3); }); it('should not unmute when microphone forbidden', async () => { @@ -131,11 +132,11 @@ describe('MicrophoneManager', () => { await expect(manager.connect()).rejects.toThrow(Error); expect(manager.isMuted()).toBeTruthy(); - expect(api.getCardElementManager().update).toBeCalledTimes(1); + expect(api.getCardElementManager().update).toHaveBeenCalledTimes(1); await manager.unmute(); expect(manager.isMuted()).toBeTruthy(); - expect(api.getCardElementManager().update).toBeCalledTimes(1); + expect(api.getCardElementManager().update).toHaveBeenCalledTimes(1); }); it('should not unmute when not supported', async () => { @@ -163,7 +164,7 @@ describe('MicrophoneManager', () => { expect(manager.isConnected()).toBeTruthy(); expect(manager.isMuted()).toBeFalsy(); - expect(api.getCardElementManager().update).toBeCalled(); + expect(api.getCardElementManager().update).toHaveBeenCalled(); }); it('should disconnect', async () => { @@ -176,11 +177,11 @@ describe('MicrophoneManager', () => { await manager.connect(); expect(manager.isConnected()).toBeTruthy(); - expect(api.getCardElementManager().update).toBeCalledTimes(1); + expect(api.getCardElementManager().update).toHaveBeenCalledTimes(1); manager.disconnect(); expect(manager.isConnected()).toBeFalsy(); - expect(api.getCardElementManager().update).toBeCalledTimes(2); + expect(api.getCardElementManager().update).toHaveBeenCalledTimes(2); }); it('should automatically disconnect', async () => { @@ -206,12 +207,12 @@ describe('MicrophoneManager', () => { await manager.connect(); expect(manager.isConnected()).toBeTruthy(); - expect(api.getCardElementManager().update).toBeCalledTimes(1); + expect(api.getCardElementManager().update).toHaveBeenCalledTimes(1); vi.advanceTimersByTime(disconnectSeconds * 1000); expect(manager.isConnected()).toBeFalsy(); - expect(api.getCardElementManager().update).toBeCalledTimes(2); + expect(api.getCardElementManager().update).toHaveBeenCalledTimes(2); }); it('should not automatically disconnect when always connected', async () => { @@ -237,12 +238,12 @@ describe('MicrophoneManager', () => { await manager.connect(); expect(manager.isConnected()).toBeTruthy(); - expect(api.getCardElementManager().update).toBeCalledTimes(1); + expect(api.getCardElementManager().update).toHaveBeenCalledTimes(1); vi.advanceTimersByTime(disconnectSeconds * 1000); expect(manager.isConnected()).toBeTruthy(); - expect(api.getCardElementManager().update).toBeCalledTimes(1); + expect(api.getCardElementManager().update).toHaveBeenCalledTimes(1); }); describe('should stay connected while in use', () => { @@ -455,7 +456,7 @@ describe('MicrophoneManager', () => { const manager = new MicrophoneManager(api); manager.initialize(); - expect(api.getConditionStateManager().setState).toBeCalledWith({ + expect(api.getConditionStateManager().setState).toHaveBeenCalledWith({ microphone: { connected: false, muted: true, forbidden: false, stream: undefined }, }); }); @@ -466,7 +467,7 @@ describe('MicrophoneManager', () => { const stream = createMockStream(); vi.mocked(navigatorMock.mediaDevices.getUserMedia).mockResolvedValue(stream); - expect(api.getConditionStateManager().setState).not.toBeCalled(); + expect(api.getConditionStateManager().setState).not.toHaveBeenCalled(); await manager.connect(); diff --git a/tests/card-controller/pip-manager.test.ts b/tests/card-controller/pip-manager.test.ts index 336176b5..453047da 100644 --- a/tests/card-controller/pip-manager.test.ts +++ b/tests/card-controller/pip-manager.test.ts @@ -92,7 +92,7 @@ describe('PIPManager', () => { manager.initialize(); - expect(api.getConditionStateManager().addListener).toBeCalledWith( + expect(api.getConditionStateManager().addListener).toHaveBeenCalledWith( expect.anything(), ); }); @@ -105,7 +105,7 @@ describe('PIPManager', () => { manager.uninitialize(); - expect(api.getConditionStateManager().removeListener).toBeCalledWith( + expect(api.getConditionStateManager().removeListener).toHaveBeenCalledWith( expect.anything(), ); }); @@ -130,8 +130,14 @@ describe('PIPManager', () => { manager.uninitialize(); - expect(removeSpy).toBeCalledWith('enterpictureinpicture', expect.any(Function)); - expect(removeSpy).toBeCalledWith('leavepictureinpicture', expect.any(Function)); + expect(removeSpy).toHaveBeenCalledWith( + 'enterpictureinpicture', + expect.any(Function), + ); + expect(removeSpy).toHaveBeenCalledWith( + 'leavepictureinpicture', + expect.any(Function), + ); }); }); @@ -187,7 +193,7 @@ describe('PIPManager', () => { const { manager } = setupWithVideo(api); expect(manager.isAvailable()).toBe(true); - expect(api.getCardElementManager().update).toBeCalled(); + expect(api.getCardElementManager().update).toHaveBeenCalled(); }); it('removes listeners when the media element changes', () => { @@ -203,8 +209,14 @@ describe('PIPManager', () => { }), }); - expect(removeSpy).toBeCalledWith('enterpictureinpicture', expect.any(Function)); - expect(removeSpy).toBeCalledWith('leavepictureinpicture', expect.any(Function)); + expect(removeSpy).toHaveBeenCalledWith( + 'enterpictureinpicture', + expect.any(Function), + ); + expect(removeSpy).toHaveBeenCalledWith( + 'leavepictureinpicture', + expect.any(Function), + ); }); it('does not re-track when element is unchanged', () => { @@ -216,7 +228,10 @@ describe('PIPManager', () => { stateManager.setState({ interaction: true }); - expect(addSpy).not.toBeCalledWith('enterpictureinpicture', expect.any(Function)); + expect(addSpy).not.toHaveBeenCalledWith( + 'enterpictureinpicture', + expect.any(Function), + ); }); it('clears video element when media is unloaded', () => { @@ -250,7 +265,7 @@ describe('PIPManager', () => { mediaLoadedInfo: createMediaLoadedInfo(), }); - expect(exitPIP).toBeCalled(); + expect(exitPIP).toHaveBeenCalled(); }); it('handles exitPictureInPicture rejection gracefully', async () => { @@ -270,7 +285,7 @@ describe('PIPManager', () => { mediaLoadedInfo: createMediaLoadedInfo(), }); - expect(exitPIP).toBeCalled(); + expect(exitPIP).toHaveBeenCalled(); // Ensure the rejection is caught and does not throw. await flushPromises(); @@ -296,7 +311,7 @@ describe('PIPManager', () => { }), }); - expect(exitPIP).not.toBeCalled(); + expect(exitPIP).not.toHaveBeenCalled(); }); }); @@ -310,7 +325,7 @@ describe('PIPManager', () => { video.dispatchEvent(new Event('enterpictureinpicture')); expect(manager.isInPIP()).toBe(true); - expect(api.getCardElementManager().update).toBeCalled(); + expect(api.getCardElementManager().update).toHaveBeenCalled(); }); it('updates card when PIP is exited via leavepictureinpicture', () => { @@ -337,7 +352,7 @@ describe('PIPManager', () => { await manager.togglePIP(); - expect(video.requestPictureInPicture).toBeCalled(); + expect(video.requestPictureInPicture).toHaveBeenCalled(); }); it('exits PIP when currently in PIP', async () => { @@ -354,8 +369,8 @@ describe('PIPManager', () => { await manager.togglePIP(); - expect(document.exitPictureInPicture).toBeCalled(); - expect(video.requestPictureInPicture).not.toBeCalled(); + expect(document.exitPictureInPicture).toHaveBeenCalled(); + expect(video.requestPictureInPicture).not.toHaveBeenCalled(); }); it('does not enter PIP when no video is available', async () => { @@ -364,7 +379,7 @@ describe('PIPManager', () => { await manager.togglePIP(); - expect(api.getCardElementManager().update).not.toBeCalled(); + expect(api.getCardElementManager().update).not.toHaveBeenCalled(); }); }); }); diff --git a/tests/card-controller/query-string-manager.test.ts b/tests/card-controller/query-string-manager.test.ts index 37cf54ed..9756f603 100644 --- a/tests/card-controller/query-string-manager.test.ts +++ b/tests/card-controller/query-string-manager.test.ts @@ -4,7 +4,8 @@ import { mock } from 'vitest-mock-extended'; import type { CardController } from '../../src/card-controller/controller'; import { QueryStringManager } from '../../src/card-controller/query-string-manager'; import { SubstreamViewModifier } from '../../src/card-controller/view/modifiers/substream'; -import { createCardAPI, createConfig } from '../test-utils'; +import { createConfig } from '../config/test-utils'; +import { createCardAPI } from '../test-utils'; const setQueryString = (qs: string): void => { const location: Location = mock(); @@ -38,8 +39,8 @@ describe('QueryStringManager', () => { expect(manager.hasViewRelatedActionsToRun()).toBeFalsy(); await manager.executeIfNecessary(); - expect(api.getActionsManager().executeActions).not.toBeCalled(); - expect(api.getViewManager().setViewByParameters).not.toBeCalled(); + expect(api.getActionsManager().executeActions).not.toHaveBeenCalled(); + expect(api.getViewManager().setViewByParameters).not.toHaveBeenCalled(); }); describe('should execute view name action from query string', () => { @@ -67,7 +68,7 @@ describe('QueryStringManager', () => { await manager.executeIfNecessary(); expect(manager.hasViewRelatedActionsToRun()).toBeFalsy(); - expect(api.getViewManager().setViewByParametersWithNewQuery).toBeCalledWith({ + expect(api.getViewManager().setViewByParametersWithNewQuery).toHaveBeenCalledWith({ params: { view: viewName, }, @@ -91,7 +92,7 @@ describe('QueryStringManager', () => { expect(manager.hasViewRelatedActionsToRun()).toBeFalsy(); await manager.executeIfNecessary(); - expect(api.getActionsManager().executeActions).toBeCalledWith({ + expect(api.getActionsManager().executeActions).toHaveBeenCalledWith({ actions: [ { action: 'fire-dom-event', @@ -116,9 +117,9 @@ describe('QueryStringManager', () => { await manager.executeIfNecessary(); expect(manager.hasViewRelatedActionsToRun()).toBeFalsy(); - expect(api.getViewManager().setViewDefaultWithNewQuery).toBeCalled(); - expect(api.getActionsManager().executeActions).not.toBeCalled(); - expect(api.getViewManager().setViewByParameters).not.toBeCalled(); + expect(api.getViewManager().setViewDefaultWithNewQuery).toHaveBeenCalled(); + expect(api.getActionsManager().executeActions).not.toHaveBeenCalled(); + expect(api.getViewManager().setViewByParameters).not.toHaveBeenCalled(); }); it('should execute camera_select action', async () => { @@ -132,13 +133,13 @@ describe('QueryStringManager', () => { await manager.executeIfNecessary(); expect(manager.hasViewRelatedActionsToRun()).toBeFalsy(); - expect(api.getViewManager().setViewByParametersWithNewQuery).toBeCalledWith({ + expect(api.getViewManager().setViewByParametersWithNewQuery).toHaveBeenCalledWith({ params: { camera: 'camera.office', }, }); - expect(api.getActionsManager().executeActions).not.toBeCalled(); - expect(api.getViewManager().setViewDefault).not.toBeCalled(); + expect(api.getActionsManager().executeActions).not.toHaveBeenCalled(); + expect(api.getViewManager().setViewDefault).not.toHaveBeenCalled(); }); it('should execute substream_on with a stream value as a view modifier', async () => { @@ -152,13 +153,13 @@ describe('QueryStringManager', () => { await manager.executeIfNecessary(); expect(manager.hasViewRelatedActionsToRun()).toBeFalsy(); - expect(api.getViewManager().setViewByParametersWithNewQuery).toBeCalledWith({ + expect(api.getViewManager().setViewByParametersWithNewQuery).toHaveBeenCalledWith({ modifiers: [new SubstreamViewModifier({ stream: 'camera.office_hd' })], params: {}, }); - expect(api.getActionsManager().executeActions).not.toBeCalled(); - expect(api.getViewManager().setViewDefault).not.toBeCalled(); + expect(api.getActionsManager().executeActions).not.toHaveBeenCalled(); + expect(api.getViewManager().setViewDefault).not.toHaveBeenCalled(); }); it('should dispatch substream_on without a value as a non-view action', async () => { @@ -171,7 +172,7 @@ describe('QueryStringManager', () => { expect(manager.hasViewRelatedActionsToRun()).toBeFalsy(); await manager.executeIfNecessary(); - expect(api.getActionsManager().executeActions).toBeCalledWith({ + expect(api.getActionsManager().executeActions).toHaveBeenCalledWith({ actions: [ { action: 'fire-dom-event', @@ -193,11 +194,11 @@ describe('QueryStringManager', () => { await manager.executeIfNecessary(); expect(manager.hasViewRelatedActionsToRun()).toBeFalsy(); - expect(api.getViewManager().setViewByParametersWithNewQuery).toBeCalledWith({ + expect(api.getViewManager().setViewByParametersWithNewQuery).toHaveBeenCalledWith({ modifiers: [new SubstreamViewModifier()], params: {}, }); - expect(api.getActionsManager().executeActions).not.toBeCalled(); + expect(api.getActionsManager().executeActions).not.toHaveBeenCalled(); }); it('should warn on the legacy live_substream_select URL form', async () => { @@ -214,9 +215,11 @@ describe('QueryStringManager', () => { expect(manager.hasViewRelatedActionsToRun()).toBeFalsy(); await manager.executeIfNecessary(); - expect(api.getActionsManager().executeActions).not.toBeCalled(); - expect(api.getViewManager().setViewByParametersWithNewQuery).not.toBeCalled(); - expect(consoleSpy).toBeCalledWith(expect.stringContaining('live_substream_select')); + expect(api.getActionsManager().executeActions).not.toHaveBeenCalled(); + expect(api.getViewManager().setViewByParametersWithNewQuery).not.toHaveBeenCalled(); + expect(consoleSpy).toHaveBeenCalledWith( + expect.stringContaining('live_substream_select'), + ); }); it('should ignore camera_select without a value', async () => { @@ -229,9 +232,9 @@ describe('QueryStringManager', () => { expect(manager.hasViewRelatedActionsToRun()).toBeFalsy(); await manager.executeIfNecessary(); - expect(api.getActionsManager().executeActions).not.toBeCalled(); - expect(api.getViewManager().setViewDefault).not.toBeCalled(); - expect(api.getViewManager().setViewByParameters).not.toBeCalled(); + expect(api.getActionsManager().executeActions).not.toHaveBeenCalled(); + expect(api.getViewManager().setViewDefault).not.toHaveBeenCalled(); + expect(api.getViewManager().setViewByParameters).not.toHaveBeenCalled(); }); it('should handle unknown action', async () => { @@ -246,10 +249,10 @@ describe('QueryStringManager', () => { expect(manager.hasViewRelatedActionsToRun()).toBeFalsy(); await manager.executeIfNecessary(); - expect(api.getActionsManager().executeActions).not.toBeCalled(); - expect(api.getViewManager().setViewDefault).not.toBeCalled(); - expect(api.getViewManager().setViewByParameters).not.toBeCalled(); - expect(consoleSpy).toBeCalled(); + expect(api.getActionsManager().executeActions).not.toHaveBeenCalled(); + expect(api.getViewManager().setViewDefault).not.toHaveBeenCalled(); + expect(api.getViewManager().setViewByParameters).not.toHaveBeenCalled(); + expect(consoleSpy).toHaveBeenCalled(); }); describe('should execute view name action from query string', () => { @@ -275,7 +278,7 @@ describe('QueryStringManager', () => { await manager.executeIfNecessary(); expect(manager.hasViewRelatedActionsToRun()).toBeFalsy(); - expect(api.getViewManager().setViewByParametersWithNewQuery).toBeCalledWith({ + expect(api.getViewManager().setViewByParametersWithNewQuery).toHaveBeenCalledWith({ params: { view: viewName, }, @@ -298,13 +301,15 @@ describe('QueryStringManager', () => { await manager.executeIfNecessary(); - expect(api.getViewManager().setViewDefaultWithNewQuery).toBeCalledWith({ + expect(api.getViewManager().setViewDefaultWithNewQuery).toHaveBeenCalledWith({ params: { camera: 'camera.kitchen', }, modifiers: [new SubstreamViewModifier({ stream: 'camera.kitchen_hd' })], }); - expect(api.getViewManager().setViewByParametersWithNewQuery).not.toBeCalled(); + expect( + api.getViewManager().setViewByParametersWithNewQuery, + ).not.toHaveBeenCalled(); }); it('should handle multiple cameras specified', async () => { @@ -319,7 +324,7 @@ describe('QueryStringManager', () => { await manager.executeIfNecessary(); - expect(api.getViewManager().setViewByParametersWithNewQuery).toBeCalledWith({ + expect(api.getViewManager().setViewByParametersWithNewQuery).toHaveBeenCalledWith({ params: { camera: 'camera.office', }, @@ -337,18 +342,24 @@ describe('QueryStringManager', () => { expect(manager.hasViewRelatedActionsToRun()).toBeTruthy(); await manager.executeIfNecessary(); expect(manager.hasViewRelatedActionsToRun()).toBeFalsy(); - expect(api.getViewManager().setViewByParametersWithNewQuery).toBeCalledTimes(1); + expect(api.getViewManager().setViewByParametersWithNewQuery).toHaveBeenCalledTimes( + 1, + ); await manager.executeIfNecessary(); expect(manager.hasViewRelatedActionsToRun()).toBeFalsy(); - expect(api.getViewManager().setViewByParametersWithNewQuery).toBeCalledTimes(1); + expect(api.getViewManager().setViewByParametersWithNewQuery).toHaveBeenCalledTimes( + 1, + ); manager.requestExecution(); expect(manager.hasViewRelatedActionsToRun()).toBeTruthy(); await manager.executeIfNecessary(); expect(manager.hasViewRelatedActionsToRun()).toBeFalsy(); - expect(api.getViewManager().setViewByParametersWithNewQuery).toBeCalledTimes(2); + expect(api.getViewManager().setViewByParametersWithNewQuery).toHaveBeenCalledTimes( + 2, + ); }); it('should execute actions with old frigate-card-action key', async () => { @@ -364,7 +375,7 @@ describe('QueryStringManager', () => { await manager.executeIfNecessary(); expect(manager.hasViewRelatedActionsToRun()).toBeFalsy(); - expect(api.getViewManager().setViewByParametersWithNewQuery).toBeCalledWith({ + expect(api.getViewManager().setViewByParametersWithNewQuery).toHaveBeenCalledWith({ params: { view: 'clips', }, @@ -382,7 +393,7 @@ describe('QueryStringManager', () => { expect(manager.hasViewRelatedActionsToRun()).toBeTruthy(); await manager.executeIfNecessary(); - expect(api.getViewManager().setViewByParametersWithNewQuery).toBeCalledWith({ + expect(api.getViewManager().setViewByParametersWithNewQuery).toHaveBeenCalledWith({ params: { view: 'clips', }, @@ -399,8 +410,10 @@ describe('QueryStringManager', () => { expect(manager.hasViewRelatedActionsToRun()).toBeFalsy(); await manager.executeIfNecessary(); - expect(api.getViewManager().setViewByParametersWithNewQuery).not.toBeCalled(); - expect(api.getActionsManager().executeActions).not.toBeCalled(); + expect( + api.getViewManager().setViewByParametersWithNewQuery, + ).not.toHaveBeenCalled(); + expect(api.getActionsManager().executeActions).not.toHaveBeenCalled(); }); it('should execute action without card_id on any card', async () => { @@ -413,7 +426,7 @@ describe('QueryStringManager', () => { expect(manager.hasViewRelatedActionsToRun()).toBeTruthy(); await manager.executeIfNecessary(); - expect(api.getViewManager().setViewByParametersWithNewQuery).toBeCalledWith({ + expect(api.getViewManager().setViewByParametersWithNewQuery).toHaveBeenCalledWith({ params: { view: 'clips', }, @@ -430,7 +443,7 @@ describe('QueryStringManager', () => { expect(manager.hasViewRelatedActionsToRun()).toBeFalsy(); await manager.executeIfNecessary(); - expect(api.getActionsManager().executeActions).not.toBeCalled(); + expect(api.getActionsManager().executeActions).not.toHaveBeenCalled(); }); it('should execute action when card has no card_id and URL has card_id', async () => { @@ -443,7 +456,9 @@ describe('QueryStringManager', () => { expect(manager.hasViewRelatedActionsToRun()).toBeFalsy(); await manager.executeIfNecessary(); - expect(api.getViewManager().setViewByParametersWithNewQuery).not.toBeCalled(); + expect( + api.getViewManager().setViewByParametersWithNewQuery, + ).not.toHaveBeenCalled(); }); }); }); diff --git a/tests/card-controller/status-bar-item-manager.test.ts b/tests/card-controller/status-bar-item-manager.test.ts index 95fd4d20..eaf80fc7 100644 --- a/tests/card-controller/status-bar-item-manager.test.ts +++ b/tests/card-controller/status-bar-item-manager.test.ts @@ -3,13 +3,9 @@ import { describe, expect, it, vi } from 'vitest'; import { StatusBarItemManager } from '../../src/card-controller/status-bar-item-manager'; import type { StatusBarString } from '../../src/config/schema/actions/types'; import { QueryResults } from '../../src/view/query-results'; -import { - createCameraManager, - createCardAPI, - createStore, - createView, - TestViewMedia, -} from '../test-utils'; +import { createCameraManager, createStore } from '../camera-manager/test-utils'; +import { createCardAPI } from '../test-utils'; +import { createView, TestViewMedia } from '../view/test-utils'; describe('StatusBarItemManager', () => { const testItem: StatusBarString = { diff --git a/tests/card-controller/style-manager.test.ts b/tests/card-controller/style-manager.test.ts index 0e5a0941..4c138c1b 100644 --- a/tests/card-controller/style-manager.test.ts +++ b/tests/card-controller/style-manager.test.ts @@ -3,7 +3,9 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'; import { StyleManager } from '../../src/card-controller/style-manager'; import type { AdvancedCameraCardView } from '../../src/config/schema/common/const'; import type { ThemeName } from '../../src/config/schema/view'; -import { createCardAPI, createConfig, createView } from '../test-utils'; +import { createConfig } from '../config/test-utils'; +import { createCardAPI } from '../test-utils'; +import { createView } from '../view/test-utils'; // @vitest-environment jsdom describe('StyleManager', () => { diff --git a/tests/card-controller/templates/index.test.ts b/tests/card-controller/templates/index.test.ts index 882904c0..11b34bc1 100644 --- a/tests/card-controller/templates/index.test.ts +++ b/tests/card-controller/templates/index.test.ts @@ -1,8 +1,8 @@ import { beforeAll, describe, expect, it, vi } from 'vitest'; import { TemplateManager } from '../../../src/card-controller/templates/index'; +import { createConfig } from '../../config/test-utils'; import { - createConfig, createHASS, createStateEntity, stubConnectedHomeAssistant, @@ -55,7 +55,7 @@ describe('TemplateManager', () => { manager.renderRecursively(createHASS(), '{{ acc.camera }}'); - expect(loadRenderer).toBeCalled(); + expect(loadRenderer).toHaveBeenCalled(); }); it('should swallow a load failure when priming on an unloaded render', () => { diff --git a/tests/card-controller/view/factory.test.ts b/tests/card-controller/view/factory.test.ts index a73c575a..b0cb0271 100644 --- a/tests/card-controller/view/factory.test.ts +++ b/tests/card-controller/view/factory.test.ts @@ -13,12 +13,11 @@ import { View } from '../../../src/view/view'; import { createCameraManager, createCapabilities, - createCardAPI, - createConfig, - createFolder, createStore, - createView, -} from '../../test-utils'; +} from '../../camera-manager/test-utils'; +import { createConfig } from '../../config/test-utils'; +import { createCardAPI, createFolder } from '../../test-utils'; +import { createView } from '../../view/test-utils'; import { createPopulatedAPI } from './test-utils'; describe('getViewDefault', () => { @@ -51,7 +50,7 @@ describe('getViewDefault', () => { ); const factory = new ViewFactory(api); - expect(() => factory.getViewDefault()).toThrowError(ViewIncompatible); + expect(() => factory.getViewDefault()).toThrow(ViewIncompatible); }); it('should use folders view as default when folders exist without cameras', () => { @@ -245,7 +244,7 @@ describe('getViewByParameters', () => { view: 'snapshots', }, }), - ).toThrowError(ViewIncompatible); + ).toThrow(ViewIncompatible); }); describe('should handle no camera for view with failsafe', () => { @@ -325,7 +324,7 @@ describe('getViewByParameters', () => { view: 'snapshots', }, }), - ).toThrowError(ViewIncompatible); + ).toThrow(ViewIncompatible); }); it('should choose live view with failsafe', () => { @@ -373,7 +372,7 @@ describe('getViewByParameters', () => { view: 'snapshots', }, }), - ).toThrowError(ViewIncompatible); + ).toThrow(ViewIncompatible); }); }); diff --git a/tests/card-controller/view/item-manager.test.ts b/tests/card-controller/view/item-manager.test.ts index 83aa585c..cdd9455c 100644 --- a/tests/card-controller/view/item-manager.test.ts +++ b/tests/card-controller/view/item-manager.test.ts @@ -5,12 +5,8 @@ import { ViewItemManager } from '../../../src/card-controller/view/item-manager' import { homeAssistantGetSignedURLIfNecessary } from '../../../src/ha/sign-path.js'; import { downloadURL } from '../../../src/utils/download'; import { ViewFolder, ViewMediaType } from '../../../src/view/item'; -import { - createCardAPI, - createFolder, - createHASS, - TestViewMedia, -} from '../../test-utils'; +import { createCardAPI, createFolder, createHASS } from '../../test-utils'; +import { TestViewMedia } from '../../view/test-utils'; vi.mock('../../../src/utils/download'); vi.mock('../../../src/ha/sign-path.js'); @@ -95,7 +91,7 @@ describe('ViewItemManager', () => { vi.mocked(homeAssistantGetSignedURLIfNecessary).mockRejectedValue(signError); expect(await manager.download(item)).toBe(false); - expect(api.getNotificationManager().setNotification).toBeCalledWith( + expect(api.getNotificationManager().setNotification).toHaveBeenCalledWith( expect.objectContaining({ heading: expect.objectContaining({ text: 'Download failed', @@ -126,7 +122,7 @@ describe('ViewItemManager', () => { ); expect(await manager.download(item)).toBe(true); - expect(downloadURL).toBeCalledWith( + expect(downloadURL).toHaveBeenCalledWith( 'http://foo/signed-url', 'camera-office_id.mp4', ); @@ -149,7 +145,7 @@ describe('ViewItemManager', () => { ); expect(await manager.download(item)).toBe(true); - expect(downloadURL).toBeCalledWith('http://foo/signed-url', 'media_id.mp4'); + expect(downloadURL).toHaveBeenCalledWith('http://foo/signed-url', 'media_id.mp4'); }); it('should download media without signing', async () => { @@ -165,7 +161,7 @@ describe('ViewItemManager', () => { }); expect(await manager.download(item)).toBe(true); - expect(downloadURL).toBeCalledWith('foo', 'camera-office_id.mp4'); + expect(downloadURL).toHaveBeenCalledWith('foo', 'camera-office_id.mp4'); }); it('should download media without camera or folder', async () => { @@ -176,7 +172,7 @@ describe('ViewItemManager', () => { const item = new TestViewMedia({ cameraID: null, folder: null }); expect(await manager.download(item)).toBe(false); - expect(api.getNotificationManager().setNotification).toBeCalledWith( + expect(api.getNotificationManager().setNotification).toHaveBeenCalledWith( expect.objectContaining({ heading: expect.objectContaining({ text: 'Download failed', @@ -203,7 +199,7 @@ describe('ViewItemManager', () => { }); expect(await manager.download(item)).toBe(true); - expect(downloadURL).toBeCalledWith('foo', 'camera.mp4'); + expect(downloadURL).toHaveBeenCalledWith('foo', 'camera.mp4'); }); it('should generate filename for media with start time', async () => { @@ -223,7 +219,7 @@ describe('ViewItemManager', () => { // Use format() to generate expected filename timestamp (formats in local time) const expectedFilename = `camera_id_${format(startTime, 'yyyy-MM-dd-HH-mm-ss')}.mp4`; - expect(downloadURL).toBeCalledWith('foo', expectedFilename); + expect(downloadURL).toHaveBeenCalledWith('foo', expectedFilename); }); it('should generate filename for snapshot', async () => { @@ -239,7 +235,7 @@ describe('ViewItemManager', () => { }); expect(await manager.download(item)).toBe(true); - expect(downloadURL).toBeCalledWith('foo', 'camera_id.jpg'); + expect(downloadURL).toHaveBeenCalledWith('foo', 'camera_id.jpg'); }); it('should generate filename for folder without title', async () => { @@ -255,7 +251,7 @@ describe('ViewItemManager', () => { }); expect(await manager.download(item)).toBe(true); - expect(downloadURL).toBeCalledWith('foo', 'media'); + expect(downloadURL).toHaveBeenCalledWith('foo', 'media'); }); it('should generate filename for folder with title', async () => { @@ -271,7 +267,7 @@ describe('ViewItemManager', () => { }); expect(await manager.download(item)).toBe(true); - expect(downloadURL).toBeCalledWith('foo', 'title'); + expect(downloadURL).toHaveBeenCalledWith('foo', 'title'); }); }); }); @@ -288,7 +284,7 @@ describe('ViewItemManager', () => { await manager.favorite(item, true); - expect(api.getCameraManager().favoriteMedia).toBeCalledWith(item, true); + expect(api.getCameraManager().favoriteMedia).toHaveBeenCalledWith(item, true); }); it('should favorite folder media', async () => { @@ -302,7 +298,7 @@ describe('ViewItemManager', () => { await manager.favorite(item, true); - expect(api.getFoldersManager().favorite).toBeCalledWith(item, true); + expect(api.getFoldersManager().favorite).toHaveBeenCalledWith(item, true); }); }); @@ -318,7 +314,7 @@ describe('ViewItemManager', () => { await manager.reviewMedia(item, true); - expect(api.getCameraManager().reviewMedia).toBeCalledWith(item, true); + expect(api.getCameraManager().reviewMedia).toHaveBeenCalledWith(item, true); }); it('should not review non-review media', async () => { @@ -332,7 +328,7 @@ describe('ViewItemManager', () => { await manager.reviewMedia(item, true); - expect(api.getCameraManager().reviewMedia).not.toBeCalled(); + expect(api.getCameraManager().reviewMedia).not.toHaveBeenCalled(); }); }); }); diff --git a/tests/card-controller/view/modifiers/index.test.ts b/tests/card-controller/view/modifiers/index.test.ts index 89d211d9..8c9b28b9 100644 --- a/tests/card-controller/view/modifiers/index.test.ts +++ b/tests/card-controller/view/modifiers/index.test.ts @@ -5,7 +5,7 @@ import { MergeContextViewModifier } from '../../../../src/card-controller/view/m import { SetQueryViewModifier } from '../../../../src/card-controller/view/modifiers/set-query'; import { QueryResults } from '../../../../src/view/query-results'; import { UnifiedQuery } from '../../../../src/view/unified-query'; -import { createView } from '../../../test-utils'; +import { createView } from '../../../view/test-utils'; it('should apply view modifiers', () => { const view = createView(); diff --git a/tests/card-controller/view/modifiers/merge-context.test.ts b/tests/card-controller/view/modifiers/merge-context.test.ts index 412c70e8..c59d1521 100644 --- a/tests/card-controller/view/modifiers/merge-context.test.ts +++ b/tests/card-controller/view/modifiers/merge-context.test.ts @@ -2,7 +2,7 @@ import type { ViewContext } from 'view'; import { expect, it } from 'vitest'; import { MergeContextViewModifier } from '../../../../src/card-controller/view/modifiers/merge-context'; -import { createView } from '../../../test-utils'; +import { createView } from '../../../view/test-utils'; it('should merge context', () => { const context: ViewContext = { diff --git a/tests/card-controller/view/modifiers/remove-context-property.test.ts b/tests/card-controller/view/modifiers/remove-context-property.test.ts index db7b9696..44984a08 100644 --- a/tests/card-controller/view/modifiers/remove-context-property.test.ts +++ b/tests/card-controller/view/modifiers/remove-context-property.test.ts @@ -1,7 +1,7 @@ import { expect, it } from 'vitest'; import { RemoveContextPropertyViewModifier } from '../../../../src/card-controller/view/modifiers/remove-context-property'; -import { createView } from '../../../test-utils'; +import { createView } from '../../../view/test-utils'; it('should remove context property', () => { const modifier = new RemoveContextPropertyViewModifier('timeline', 'window'); diff --git a/tests/card-controller/view/modifiers/remove-context.test.ts b/tests/card-controller/view/modifiers/remove-context.test.ts index bf53d55a..78c62d15 100644 --- a/tests/card-controller/view/modifiers/remove-context.test.ts +++ b/tests/card-controller/view/modifiers/remove-context.test.ts @@ -1,7 +1,7 @@ import { expect, it } from 'vitest'; import { RemoveContextViewModifier } from '../../../../src/card-controller/view/modifiers/remove-context'; -import { createView } from '../../../test-utils'; +import { createView } from '../../../view/test-utils'; it('should remove context property', () => { const modifier = new RemoveContextViewModifier(['timeline']); diff --git a/tests/card-controller/view/modifiers/set-query.test.ts b/tests/card-controller/view/modifiers/set-query.test.ts index 7624d51a..41fae2a0 100644 --- a/tests/card-controller/view/modifiers/set-query.test.ts +++ b/tests/card-controller/view/modifiers/set-query.test.ts @@ -3,7 +3,7 @@ import { expect, it } from 'vitest'; import { SetQueryViewModifier } from '../../../../src/card-controller/view/modifiers/set-query'; import { QueryResults } from '../../../../src/view/query-results'; import { UnifiedQuery } from '../../../../src/view/unified-query'; -import { createView } from '../../../test-utils'; +import { createView } from '../../../view/test-utils'; it('should do nothing without arguments', () => { const view = createView(); diff --git a/tests/card-controller/view/modifiers/substream.test.ts b/tests/card-controller/view/modifiers/substream.test.ts index 14e236d7..152843e7 100644 --- a/tests/card-controller/view/modifiers/substream.test.ts +++ b/tests/card-controller/view/modifiers/substream.test.ts @@ -1,7 +1,7 @@ import { describe, expect, it } from 'vitest'; import { SubstreamViewModifier } from '../../../../src/card-controller/view/modifiers/substream'; -import { createView } from '../../../test-utils'; +import { createView } from '../../../view/test-utils'; describe('SubstreamViewModifier', () => { it('should write the override for the selected camera', () => { diff --git a/tests/card-controller/view/sort.test.ts b/tests/card-controller/view/sort.test.ts index 8b924694..3953d735 100644 --- a/tests/card-controller/view/sort.test.ts +++ b/tests/card-controller/view/sort.test.ts @@ -2,7 +2,8 @@ import { describe, expect, it } from 'vitest'; import { sortItems } from '../../../src/card-controller/view/sort.js'; import { ViewFolder } from '../../../src/view/item.js'; -import { createFolder, TestViewMedia } from '../../test-utils.js'; +import { createFolder } from '../../test-utils.js'; +import { TestViewMedia } from '../../view/test-utils'; describe('sortMedia', () => { const media_1 = new TestViewMedia({ diff --git a/tests/card-controller/view/test-utils.ts b/tests/card-controller/view/test-utils.ts index 4dd30bfa..0027cdd1 100644 --- a/tests/card-controller/view/test-utils.ts +++ b/tests/card-controller/view/test-utils.ts @@ -5,10 +5,10 @@ import type { RawAdvancedCameraCardConfig } from '../../../src/config/types'; import { createCameraManager, createCapabilities, - createCardAPI, - createConfig, createStore, -} from '../../test-utils'; +} from '../../camera-manager/test-utils'; +import { createConfig } from '../../config/test-utils'; +import { createCardAPI } from '../../test-utils'; export const createPopulatedAPI = ( config?: RawAdvancedCameraCardConfig, diff --git a/tests/card-controller/view/view-manager.test.ts b/tests/card-controller/view/view-manager.test.ts index 696bddb2..d2b0e9a8 100644 --- a/tests/card-controller/view/view-manager.test.ts +++ b/tests/card-controller/view/view-manager.test.ts @@ -16,7 +16,8 @@ import { ViewMedia, ViewMediaType } from '../../../src/view/item'; import { QueryResults } from '../../../src/view/query-results'; import { UnifiedQuery } from '../../../src/view/unified-query'; import type { View } from '../../../src/view/view'; -import { createCardAPI, createEventQuery, createView } from '../../test-utils'; +import { createCardAPI } from '../../test-utils'; +import { createEventQuery, createView } from '../../view/test-utils'; const createInitializedCardAPI = (initialized?: boolean): CardController => { const api = createCardAPI(); @@ -44,17 +45,17 @@ describe('should act correctly when view is set', () => { expect(manager.getView()).toBe(view); expect(manager.hasView()).toBeTruthy(); - expect(api.getMediaLoadedInfoManager().setSelected).toBeCalledWith('camera'); - expect(api.getCardElementManager().scrollReset).toBeCalled(); - expect(api.getStyleManager().setExpandedMode).toBeCalled(); - expect(api.getConditionStateManager()?.setState).toBeCalledWith({ + expect(api.getMediaLoadedInfoManager().setSelected).toHaveBeenCalledWith('camera'); + expect(api.getCardElementManager().scrollReset).toHaveBeenCalled(); + expect(api.getStyleManager().setExpandedMode).toHaveBeenCalled(); + expect(api.getConditionStateManager()?.setState).toHaveBeenCalledWith({ view: 'live', camera: 'camera', displayMode: 'grid', targetID: 'camera', substreamID: undefined, }); - expect(api.getCardElementManager().update).toBeCalled(); + expect(api.getCardElementManager().update).toHaveBeenCalled(); }); it('should set the engaged substream in condition state', () => { @@ -70,7 +71,7 @@ describe('should act correctly when view is set', () => { const api = createInitializedCardAPI(); new ViewManager(api, { viewFactory: factory }).setViewDefault(); - expect(api.getConditionStateManager()?.setState).toBeCalledWith( + expect(api.getConditionStateManager()?.setState).toHaveBeenCalledWith( expect.objectContaining({ substreamID: 'substream' }), ); }); @@ -102,7 +103,7 @@ describe('should act correctly when view is set', () => { expect(manager.getView()).toBe(view_2); // Same view name, so scrolling should not happen. - expect(api.getCardElementManager().scrollReset).not.toBeCalled(); + expect(api.getCardElementManager().scrollReset).not.toHaveBeenCalled(); }); }); @@ -341,7 +342,7 @@ it('should set view by parameters with an explicitly provided existing query', a await manager.setViewByParametersWithExistingQuery({ params: { query } }); // An explicitly-passed query is used as-is rather than the base view's. - expect(viewFactory.getViewByParameters).toBeCalledWith( + expect(viewFactory.getViewByParameters).toHaveBeenCalledWith( expect.objectContaining({ params: expect.objectContaining({ query }) }), ); }); @@ -362,7 +363,7 @@ it('should clear the query when an explicit null query is passed', async () => { // An explicit `null` is respected (clears the query), not overridden by the // base view's query. - expect(viewFactory.getViewByParameters).toBeCalledWith( + expect(viewFactory.getViewByParameters).toHaveBeenCalledWith( expect.objectContaining({ params: expect.objectContaining({ query: null }) }), ); }); @@ -384,13 +385,13 @@ describe('should handle exceptions', () => { expect(manager.hasView()).toBeTruthy(); expect(manager.getView()).toBe(failSafeView); - expect(viewFactory.getViewDefault).toBeCalledWith( + expect(viewFactory.getViewDefault).toHaveBeenCalledWith( expect.objectContaining({ baseView: null, failSafe: true }), ); - expect(api.getIssueManager().trigger).toBeCalledWith('view_incompatible', { + expect(api.getIssueManager().trigger).toHaveBeenCalledWith('view_incompatible', { error, }); - expect(api.getNotificationManager().setNotification).not.toBeCalled(); + expect(api.getNotificationManager().setNotification).not.toHaveBeenCalled(); }); it('should not retry with failSafe when existing view in sync calls', () => { @@ -409,8 +410,8 @@ describe('should handle exceptions', () => { manager.setViewDefault(); expect(manager.getView()).toBe(existingView); - expect(viewFactory.getViewDefault).toBeCalledTimes(2); - expect(api.getIssueManager().trigger).toBeCalledWith('view_incompatible', { + expect(viewFactory.getViewDefault).toHaveBeenCalledTimes(2); + expect(api.getIssueManager().trigger).toHaveBeenCalledWith('view_incompatible', { error, }); }); @@ -430,13 +431,13 @@ describe('should handle exceptions', () => { await manager.setViewDefaultWithNewQuery(); expect(manager.hasView()).toBeTruthy(); - expect(viewFactory.getViewDefault).toBeCalledWith( + expect(viewFactory.getViewDefault).toHaveBeenCalledWith( expect.objectContaining({ baseView: null, failSafe: true }), ); - expect(api.getIssueManager().trigger).toBeCalledWith('view_incompatible', { + expect(api.getIssueManager().trigger).toHaveBeenCalledWith('view_incompatible', { error, }); - expect(api.getNotificationManager().setNotification).not.toBeCalled(); + expect(api.getNotificationManager().setNotification).not.toHaveBeenCalled(); }); it('should not retry with failSafe when existing view in async calls', async () => { @@ -455,8 +456,8 @@ describe('should handle exceptions', () => { await manager.setViewDefaultWithNewQuery(); expect(manager.getView()).not.toBeNull(); - expect(viewFactory.getViewDefault).toBeCalledTimes(2); - expect(api.getIssueManager().trigger).toBeCalledWith('view_incompatible', { + expect(viewFactory.getViewDefault).toHaveBeenCalledTimes(2); + expect(api.getIssueManager().trigger).toHaveBeenCalledWith('view_incompatible', { error, }); }); @@ -469,7 +470,7 @@ describe('should handle exceptions', () => { const manager = new ViewManager(api, { viewFactory }); manager.setViewDefault(); - expect(api.getIssueManager().reset).toBeCalledWith('view_incompatible'); + expect(api.getIssueManager().reset).toHaveBeenCalledWith('view_incompatible'); }); it('should return null when failSafe view factory also throws', () => { @@ -483,7 +484,7 @@ describe('should handle exceptions', () => { manager.setViewDefault(); expect(manager.hasView()).toBeFalsy(); - expect(viewFactory.getViewDefault).toBeCalledTimes(2); + expect(viewFactory.getViewDefault).toHaveBeenCalledTimes(2); }); it('should handle viewQueryExecutor exceptions in async calls', async () => { @@ -505,7 +506,7 @@ describe('should handle exceptions', () => { expect(manager.hasView()).toBeTruthy(); // But an error will also be generated. - expect(api.getIssueManager().trigger).toBeCalledWith( + expect(api.getIssueManager().trigger).toHaveBeenCalledWith( 'media_query', expect.objectContaining({ error }), ); @@ -524,7 +525,7 @@ describe('should handle exceptions', () => { const manager = new ViewManager(api, { viewFactory }); manager.setViewByParameters(); - expect(api.getIssueManager().reset).toBeCalledWith('media_query'); + expect(api.getIssueManager().reset).toHaveBeenCalledWith('media_query'); }); it('should tolerate the view being reset during a failing async query', async () => { @@ -550,7 +551,7 @@ describe('should handle exceptions', () => { await manager.setViewDefaultWithNewQuery(); expect(manager.getView()).toBeNull(); - expect(api.getIssueManager().trigger).toBeCalledWith( + expect(api.getIssueManager().trigger).toHaveBeenCalledWith( 'media_query', expect.objectContaining({ error }), ); @@ -572,7 +573,7 @@ describe('should handle exceptions', () => { // Reset is called twice: once at dispatch (supersedes any prior error) // and once after success (clears on confirmed success). - expect(api.getIssueManager().reset).toBeCalledWith('media_query'); + expect(api.getIssueManager().reset).toHaveBeenCalledWith('media_query'); expect( vi .mocked(api.getIssueManager().reset) @@ -598,8 +599,8 @@ describe('should handle exceptions', () => { // A retry re-runs the same failing query, so the existing error must not be // cleared up front (it would blink away and back); the failure just // re-triggers it. - expect(api.getIssueManager().reset).not.toBeCalledWith('media_query'); - expect(api.getIssueManager().trigger).toBeCalledWith( + expect(api.getIssueManager().reset).not.toHaveBeenCalledWith('media_query'); + expect(api.getIssueManager().trigger).toHaveBeenCalledWith( 'media_query', expect.objectContaining({ error }), ); @@ -819,7 +820,7 @@ describe('should apply async view modifications', () => { expect(manager.getView()?.query).toBe(query); expect(manager.getView()?.queryResults).toBe(queryResults); expect(manager.getView()?.context?.loading?.query).toBeUndefined(); - expect(api.getIssueManager().reset).toBeCalledWith('media_query'); + expect(api.getIssueManager().reset).toHaveBeenCalledWith('media_query'); }); it('should not apply modifications if there is a major media change', async () => { diff --git a/tests/card-controller/view/view-query-executor.test.ts b/tests/card-controller/view/view-query-executor.test.ts index 7d766fd8..5bcb816e 100644 --- a/tests/card-controller/view/view-query-executor.test.ts +++ b/tests/card-controller/view/view-query-executor.test.ts @@ -10,17 +10,17 @@ import { QuerySource } from '../../../src/query-source'; import { UnifiedQuery } from '../../../src/view/unified-query'; import { View } from '../../../src/view/view'; import { - createCameraConfig, createCameraManager, createCapabilities, - createCardAPI, + createStore, +} from '../../camera-manager/test-utils'; +import { + createCameraConfig, createConfig, createPerformanceConfig, - createStore, - createView, - isEventQuery, - TestViewMedia, -} from '../../test-utils'; +} from '../../config/test-utils'; +import { createCardAPI } from '../../test-utils'; +import { createView, isEventQuery, TestViewMedia } from '../../view/test-utils'; import { createPopulatedAPI } from './test-utils'; describe('ViewQueryExecutor', () => { diff --git a/tests/components-lib/cached-value-controller.test.ts b/tests/components-lib/cached-value-controller.test.ts index 3830bcf9..76f34bcd 100644 --- a/tests/components-lib/cached-value-controller.test.ts +++ b/tests/components-lib/cached-value-controller.test.ts @@ -41,7 +41,7 @@ describe('CachedValueController', () => { controller.hostUpdate(); // Should not restart since refreshSeconds hasn't changed - expect(startTimerSpy).not.toBeCalled(); + expect(startTimerSpy).not.toHaveBeenCalled(); }); it('should construct', () => { @@ -69,28 +69,28 @@ describe('CachedValueController', () => { ); controller.startTimer(); - expect(startCallback).toBeCalled(); + expect(startCallback).toHaveBeenCalled(); callback.mockReturnValue(3); vi.runOnlyPendingTimers(); - expect(callback).toBeCalled(); - expect(host.requestUpdate).toBeCalled(); + expect(callback).toHaveBeenCalled(); + expect(host.requestUpdate).toHaveBeenCalled(); expect(controller.getValue()).toBe(3); callback.mockReturnValue(4); vi.runOnlyPendingTimers(); - expect(callback).toBeCalled(); - expect(host.requestUpdate).toBeCalled(); + expect(callback).toHaveBeenCalled(); + expect(host.requestUpdate).toHaveBeenCalled(); expect(controller.getValue()).toBe(4); expect(controller.hasTimer()).toBeTruthy(); controller.stopTimer(); - expect(stopCallback).toBeCalled(); + expect(stopCallback).toHaveBeenCalled(); callback.mockReset(); vi.runOnlyPendingTimers(); - expect(callback).not.toBeCalled(); + expect(callback).not.toHaveBeenCalled(); }); it('should clear value', () => { @@ -125,12 +125,12 @@ describe('CachedValueController', () => { controller.hostConnected(); expect(controller.getValue()).equal(43); - expect(startCallback).toBeCalled(); - expect(host.requestUpdate).toBeCalled(); + expect(startCallback).toHaveBeenCalled(); + expect(host.requestUpdate).toHaveBeenCalled(); controller.hostDisconnected(); expect(controller.getValue()).toBeNull(); - expect(stopCallback).toBeCalled(); + expect(stopCallback).toHaveBeenCalled(); }); it('should call timer tick callback on each tick before updateValue', () => { @@ -199,10 +199,10 @@ describe('CachedValueController', () => { // it shouldn't fire at 15 seconds. callback.mockClear(); vi.advanceTimersByTime(15 * 1000); - expect(callback).not.toBeCalled(); + expect(callback).not.toHaveBeenCalled(); vi.advanceTimersByTime(5 * 1000); - expect(callback).toBeCalled(); + expect(callback).toHaveBeenCalled(); // Now set it to null -> stops timer refreshSeconds = null; diff --git a/tests/components-lib/gallery/gallery-core-controller.test.ts b/tests/components-lib/gallery/gallery-core-controller.test.ts index 35907f98..bc337c7c 100644 --- a/tests/components-lib/gallery/gallery-core-controller.test.ts +++ b/tests/components-lib/gallery/gallery-core-controller.test.ts @@ -21,13 +21,6 @@ import { ResizeObserverMock, } from '../../test-utils'; -vi.mock('lodash-es', async () => { - return { - ...(await vi.importActual('lodash-es')), - throttle: vi.fn((fn) => fn), - }; -}); - vi.mock('../../../src/utils/sleep'); vi.mock('../../../src/utils/scroll'); @@ -66,7 +59,7 @@ describe('GalleryCoreController', () => { const host = createLitElement(); const controller = createController({ host }); expect(controller).toBeInstanceOf(GalleryCoreController); - expect(host.addController).toBeCalledWith(controller); + expect(host.addController).toHaveBeenCalledWith(controller); }); it('should remove controller', () => { @@ -74,7 +67,7 @@ describe('GalleryCoreController', () => { const controller = createController({ host }); expect(controller).toBeInstanceOf(GalleryCoreController); controller.removeController(); - expect(host.removeController).toBeCalledWith(controller); + expect(host.removeController).toHaveBeenCalledWith(controller); }); it('should remove controller', () => { @@ -82,7 +75,7 @@ describe('GalleryCoreController', () => { const controller = createController({ host }); expect(controller).toBeInstanceOf(GalleryCoreController); controller.removeController(); - expect(host.removeController).toBeCalledWith(controller); + expect(host.removeController).toHaveBeenCalledWith(controller); }); describe('should observe sentintel when host updated', () => { @@ -100,10 +93,10 @@ describe('GalleryCoreController', () => { expect( vi.mocked(IntersectionObserver).mock.results[0].value.disconnect, - ).toBeCalled(); + ).toHaveBeenCalled(); expect( vi.mocked(IntersectionObserver).mock.results[0].value.observe, - ).toBeCalledWith(sentinel); + ).toHaveBeenCalledWith(sentinel); }); it('should disconnect when sentinel changes to null', () => { @@ -125,10 +118,10 @@ describe('GalleryCoreController', () => { expect( vi.mocked(IntersectionObserver).mock.results[0].value.disconnect, - ).toBeCalledTimes(2); + ).toHaveBeenCalledTimes(2); expect( vi.mocked(IntersectionObserver).mock.results[0].value.observe, - ).toBeCalledTimes(1); + ).toHaveBeenCalledTimes(1); }); it('should skip disconnect/observe when sentinel is unchanged', () => { @@ -147,10 +140,10 @@ describe('GalleryCoreController', () => { // Only called once despite two hostUpdated() calls. expect( vi.mocked(IntersectionObserver).mock.results[0].value.disconnect, - ).toBeCalledTimes(1); + ).toHaveBeenCalledTimes(1); expect( vi.mocked(IntersectionObserver).mock.results[0].value.observe, - ).toBeCalledTimes(1); + ).toHaveBeenCalledTimes(1); }); it('should not observe when sentinel is null from the start', () => { @@ -166,10 +159,10 @@ describe('GalleryCoreController', () => { expect( vi.mocked(IntersectionObserver).mock.results[0].value.disconnect, - ).not.toBeCalled(); + ).not.toHaveBeenCalled(); expect( vi.mocked(IntersectionObserver).mock.results[0].value.observe, - ).not.toBeCalled(); + ).not.toHaveBeenCalled(); }); }); @@ -180,15 +173,17 @@ describe('GalleryCoreController', () => { controller.hostConnected(); - expect(vi.mocked(ResizeObserver).mock.results[0].value.observe).toBeCalledWith(host); - expect(host.addEventListener).toBeCalledWith('wheel', expect.anything(), { + expect(vi.mocked(ResizeObserver).mock.results[0].value.observe).toHaveBeenCalledWith( + host, + ); + expect(host.addEventListener).toHaveBeenCalledWith('wheel', expect.anything(), { passive: true, }); - expect(host.addEventListener).toBeCalledWith('touchstart', expect.anything(), { + expect(host.addEventListener).toHaveBeenCalledWith('touchstart', expect.anything(), { passive: true, }); - expect(host.addEventListener).toBeCalledWith('touchend', expect.anything()); - expect(host.requestUpdate).toBeCalled(); + expect(host.addEventListener).toHaveBeenCalledWith('touchend', expect.anything()); + expect(host.requestUpdate).toHaveBeenCalled(); }); it('should detach listeners on disconnect', () => { @@ -198,13 +193,18 @@ describe('GalleryCoreController', () => { controller.hostDisconnected(); - expect(vi.mocked(ResizeObserver).mock.results[0].value.disconnect).toBeCalled(); + expect( + vi.mocked(ResizeObserver).mock.results[0].value.disconnect, + ).toHaveBeenCalled(); expect( vi.mocked(IntersectionObserver).mock.results[0].value.disconnect, - ).toBeCalled(); - expect(host.removeEventListener).toBeCalledWith('wheel', expect.anything()); - expect(host.removeEventListener).toBeCalledWith('touchstart', expect.anything()); - expect(host.removeEventListener).toBeCalledWith('touchend', expect.anything()); + ).toHaveBeenCalled(); + expect(host.removeEventListener).toHaveBeenCalledWith('wheel', expect.anything()); + expect(host.removeEventListener).toHaveBeenCalledWith( + 'touchstart', + expect.anything(), + ); + expect(host.removeEventListener).toHaveBeenCalledWith('touchend', expect.anything()); }); describe('should set the number of columns', () => { @@ -415,7 +415,7 @@ describe('GalleryCoreController', () => { ); await flushPromises(); - expect(showLoaderTop).not.toBeCalled(); + expect(showLoaderTop).not.toHaveBeenCalled(); }); it('should not extend up with touch when when not at top of component', async () => { @@ -442,7 +442,7 @@ describe('GalleryCoreController', () => { ); await flushPromises(); - expect(showLoaderTop).not.toBeCalled(); + expect(showLoaderTop).not.toHaveBeenCalled(); }); it('should not extend up with touch when touches moved downwards', async () => { @@ -468,7 +468,7 @@ describe('GalleryCoreController', () => { ); await flushPromises(); - expect(showLoaderTop).not.toBeCalled(); + expect(showLoaderTop).not.toHaveBeenCalled(); }); }); @@ -607,7 +607,7 @@ describe('GalleryCoreController', () => { }); controller.updateContents(); - expect(scrollIntoView).toBeCalledWith(selectedChild, { + expect(scrollIntoView).toHaveBeenCalledWith(selectedChild, { boundary: host, block: 'center', }); @@ -635,7 +635,7 @@ describe('GalleryCoreController', () => { controller.updateContents(); controller.updateContents(); - expect(scrollIntoView).toBeCalledTimes(1); + expect(scrollIntoView).toHaveBeenCalledTimes(1); }); it('should do nothing without a selected element', async () => { @@ -655,7 +655,7 @@ describe('GalleryCoreController', () => { }); controller.updateContents(); - expect(scrollIntoView).not.toBeCalled(); + expect(scrollIntoView).not.toHaveBeenCalled(); }); }); @@ -674,7 +674,7 @@ describe('GalleryCoreController', () => { controller.updateContents(); - expect(showSentinelBottom).toBeCalledWith(true); + expect(showSentinelBottom).toHaveBeenCalledWith(true); }); it('should do nothing without slot', async () => { @@ -691,8 +691,8 @@ describe('GalleryCoreController', () => { controller.updateContents(); - expect(showSentinelBottom).not.toBeCalled(); - expect(scrollIntoView).not.toBeCalled(); + expect(showSentinelBottom).not.toHaveBeenCalled(); + expect(scrollIntoView).not.toHaveBeenCalled(); }); }); }); diff --git a/tests/components-lib/key-assigner-controller.test.ts b/tests/components-lib/key-assigner-controller.test.ts index 64bbd697..17ffb92c 100644 --- a/tests/components-lib/key-assigner-controller.test.ts +++ b/tests/components-lib/key-assigner-controller.test.ts @@ -26,8 +26,8 @@ describe('KeyAssignerController', () => { expect(controller.hasValue()).toBeTruthy(); expect(controller.getValue()).toEqual({ key: 'ArrowLeft' }); - expect(element.requestUpdate).toBeCalled(); - expect(valueChangeHandler).toBeCalledWith( + expect(element.requestUpdate).toHaveBeenCalled(); + expect(valueChangeHandler).toHaveBeenCalledWith( expect.objectContaining({ detail: { value: { key: 'ArrowLeft' } }, }), @@ -35,8 +35,8 @@ describe('KeyAssignerController', () => { // Set again with the same value. controller.setValue({ key: 'ArrowLeft' }); - expect(element.requestUpdate).toBeCalledTimes(1); - expect(valueChangeHandler).toBeCalledTimes(1); + expect(element.requestUpdate).toHaveBeenCalledTimes(1); + expect(valueChangeHandler).toHaveBeenCalledTimes(1); }); }); @@ -81,7 +81,7 @@ describe('KeyAssignerController', () => { const element = createLitElement(); const controller = new KeyAssignerController(element); controller.toggleAssigning(); - expect(element.requestUpdate).toBeCalled(); + expect(element.requestUpdate).toHaveBeenCalled(); expect(controller.isAssigning()).toBeTruthy(); expect(element.getAttribute('assigning')).toBe(''); diff --git a/tests/components-lib/lazy-load-controller.test.ts b/tests/components-lib/lazy-load-controller.test.ts index a4c82564..5f1c06de 100644 --- a/tests/components-lib/lazy-load-controller.test.ts +++ b/tests/components-lib/lazy-load-controller.test.ts @@ -58,14 +58,14 @@ describe('LazyLoadController', () => { it('should add controller to host', () => { const host = createLitElement(); const controller = new LazyLoadController(host); - expect(host.addController).toBeCalledWith(controller); + expect(host.addController).toHaveBeenCalledWith(controller); }); it('should remove controller from host', () => { const host = createLitElement(); const controller = new LazyLoadController(host); controller.removeController(); - expect(host.removeController).toBeCalledWith(controller); + expect(host.removeController).toHaveBeenCalledWith(controller); }); it('should remove handlers and listeners on destroy', () => { @@ -81,8 +81,8 @@ describe('LazyLoadController', () => { controller.destroy(); - expect(getMockIntersectionObserver()?.disconnect).toBeCalled(); - expect(global.document.removeEventListener).toBeCalledWith( + expect(getMockIntersectionObserver()?.disconnect).toHaveBeenCalled(); + expect(global.document.removeEventListener).toHaveBeenCalledWith( 'visibilitychange', expect.anything(), ); @@ -90,7 +90,7 @@ describe('LazyLoadController', () => { callVisibilityHandler(true); callIntersectionHandler(true); - expect(listener).not.toBeCalled(); + expect(listener).not.toHaveBeenCalled(); }); describe('should set configuration', () => { @@ -100,12 +100,12 @@ describe('LazyLoadController', () => { controller.addListener(listener); expect(controller.isLoaded()).toBe(false); - expect(listener).not.toBeCalled(); + expect(listener).not.toHaveBeenCalled(); controller.setConfiguration({ lazyLoad: false }); expect(controller.isLoaded()).toBe(true); - expect(listener).toBeCalled(); + expect(listener).toHaveBeenCalled(); }); it('should re-evaluate unload when conditions change while loaded', () => { @@ -206,8 +206,8 @@ describe('LazyLoadController', () => { expect(controller.isLoaded()).toBe(false); // Should also stop observing. - expect(getMockIntersectionObserver()?.disconnect).toBeCalled(); - expect(global.document.removeEventListener).toBeCalledWith( + expect(getMockIntersectionObserver()?.disconnect).toHaveBeenCalled(); + expect(global.document.removeEventListener).toHaveBeenCalledWith( 'visibilitychange', expect.anything(), ); @@ -383,19 +383,19 @@ describe('LazyLoadController', () => { callIntersectionHandler(true); callVisibilityHandler(true); expect(listener).toHaveBeenLastCalledWith(true); - expect(listener).toBeCalledTimes(1); + expect(listener).toHaveBeenCalledTimes(1); callIntersectionHandler(false); expect(listener).toHaveBeenLastCalledWith(false); - expect(listener).toBeCalledTimes(2); + expect(listener).toHaveBeenCalledTimes(2); callIntersectionHandler(true); expect(listener).toHaveBeenLastCalledWith(true); - expect(listener).toBeCalledTimes(3); + expect(listener).toHaveBeenCalledTimes(3); controller.removeListener(listener); callIntersectionHandler(false); - expect(listener).toBeCalledTimes(3); + expect(listener).toHaveBeenCalledTimes(3); }); }); diff --git a/tests/components-lib/live/liveness/stream-liveness-controller.test.ts b/tests/components-lib/live/liveness/stream-liveness-controller.test.ts index 378539fb..6d1dba7c 100644 --- a/tests/components-lib/live/liveness/stream-liveness-controller.test.ts +++ b/tests/components-lib/live/liveness/stream-liveness-controller.test.ts @@ -10,10 +10,10 @@ import { type LiveError, } from '../../../../src/components-lib/live/utils/dispatch-live-error'; import type { LivenessCallback, MediaPlayerController } from '../../../../src/types'; +import { createCameraConfig } from '../../../config/test-utils'; import { callIntersectionHandler, callStateWatcherCallback, - createCameraConfig, createHASS, createLitElement, createMediaLoadedInfo, diff --git a/tests/components-lib/live/microphone-actions-controller.test.ts b/tests/components-lib/live/microphone-actions-controller.test.ts index ad751fd2..3acd5d6b 100644 --- a/tests/components-lib/live/microphone-actions-controller.test.ts +++ b/tests/components-lib/live/microphone-actions-controller.test.ts @@ -53,8 +53,8 @@ describe('MicrophoneActionsController', () => { await controller.setSelectedCamera('camera-1'); - expect(microphoneManager.unmute).toBeCalledTimes(1); - expect(microphoneManager.mute).not.toBeCalled(); + expect(microphoneManager.unmute).toHaveBeenCalledTimes(1); + expect(microphoneManager.mute).not.toHaveBeenCalled(); }); it('should swallow a rejected auto-unmute so a denied microphone does not surface', async () => { @@ -67,7 +67,7 @@ describe('MicrophoneActionsController', () => { }); await expect(controller.setSelectedCamera('camera-1')).resolves.toBeUndefined(); - expect(microphoneManager.unmute).toBeCalledTimes(1); + expect(microphoneManager.unmute).toHaveBeenCalledTimes(1); }); it('should mute on unselected when transitioning to a new camera', async () => { @@ -81,7 +81,7 @@ describe('MicrophoneActionsController', () => { await controller.setSelectedCamera('camera-1'); await controller.setSelectedCamera('camera-2'); - expect(microphoneManager.mute).toBeCalledTimes(1); + expect(microphoneManager.mute).toHaveBeenCalledTimes(1); }); it('should sequence mute-then-unmute deterministically on transition', async () => { @@ -128,7 +128,7 @@ describe('MicrophoneActionsController', () => { await controller.setSelectedCamera('camera-1'); await controller.setSelectedCamera('camera-1'); - expect(microphoneManager.unmute).toBeCalledTimes(1); + expect(microphoneManager.unmute).toHaveBeenCalledTimes(1); }); it('should fire unselected only when transitioning from a camera to none', async () => { @@ -147,8 +147,8 @@ describe('MicrophoneActionsController', () => { await controller.setSelectedCamera(null); - expect(microphoneManager.mute).toBeCalledTimes(1); - expect(microphoneManager.unmute).not.toBeCalled(); + expect(microphoneManager.mute).toHaveBeenCalledTimes(1); + expect(microphoneManager.unmute).not.toHaveBeenCalled(); }); it('should not fire unselected on the very first selection (no previous)', async () => { @@ -162,8 +162,8 @@ describe('MicrophoneActionsController', () => { await controller.setSelectedCamera('camera-1'); - expect(microphoneManager.mute).not.toBeCalled(); - expect(microphoneManager.unmute).toBeCalledTimes(1); + expect(microphoneManager.mute).not.toHaveBeenCalled(); + expect(microphoneManager.unmute).toHaveBeenCalledTimes(1); }); it('should not fire when condition arrays are empty', async () => { @@ -178,8 +178,8 @@ describe('MicrophoneActionsController', () => { await controller.setSelectedCamera('camera-1'); await controller.setSelectedCamera('camera-2'); - expect(microphoneManager.mute).not.toBeCalled(); - expect(microphoneManager.unmute).not.toBeCalled(); + expect(microphoneManager.mute).not.toHaveBeenCalled(); + expect(microphoneManager.unmute).not.toHaveBeenCalled(); }); it('should not crash when conditions configured but no microphone manager is passed', async () => { @@ -212,7 +212,7 @@ describe('MicrophoneActionsController', () => { await callVisibilityHandler(false); - expect(microphoneManager.mute).toBeCalledTimes(1); + expect(microphoneManager.mute).toHaveBeenCalledTimes(1); }); it('should unmute on visible when the live root is intersecting', async () => { @@ -233,7 +233,7 @@ describe('MicrophoneActionsController', () => { await callVisibilityHandler(true); - expect(microphoneManager.unmute).toBeCalledTimes(1); + expect(microphoneManager.unmute).toHaveBeenCalledTimes(1); }); it('should not unmute on tab visible when the live root is hidden', async () => { @@ -254,7 +254,7 @@ describe('MicrophoneActionsController', () => { await callVisibilityHandler(false); await callVisibilityHandler(true); - expect(microphoneManager.unmute).not.toBeCalled(); + expect(microphoneManager.unmute).not.toHaveBeenCalled(); }); }); @@ -273,7 +273,7 @@ describe('MicrophoneActionsController', () => { await callIntersectionHandler(true); await callIntersectionHandler(false); - expect(microphoneManager.mute).toBeCalledTimes(1); + expect(microphoneManager.mute).toHaveBeenCalledTimes(1); }); it('should unmute when the live root scrolls back into view', async () => { @@ -288,7 +288,7 @@ describe('MicrophoneActionsController', () => { await callIntersectionHandler(false); await callIntersectionHandler(true); - expect(microphoneManager.unmute).toBeCalledTimes(1); + expect(microphoneManager.unmute).toHaveBeenCalledTimes(1); }); it('should ignore the very first intersection callback (baseline)', async () => { @@ -303,8 +303,8 @@ describe('MicrophoneActionsController', () => { await callIntersectionHandler(false); - expect(microphoneManager.mute).not.toBeCalled(); - expect(microphoneManager.unmute).not.toBeCalled(); + expect(microphoneManager.mute).not.toHaveBeenCalled(); + expect(microphoneManager.unmute).not.toHaveBeenCalled(); }); }); @@ -320,7 +320,7 @@ describe('MicrophoneActionsController', () => { controller.setCallAnswered(false); controller.setCallAnswered(true); - expect(microphoneManager.unmute).toBeCalledTimes(1); + expect(microphoneManager.unmute).toHaveBeenCalledTimes(1); }); it('should unmute when the call is already answered on first notification', () => { @@ -336,7 +336,7 @@ describe('MicrophoneActionsController', () => { // already answered. The initial state must not be swallowed as a baseline. controller.setCallAnswered(true); - expect(microphoneManager.unmute).toBeCalledTimes(1); + expect(microphoneManager.unmute).toHaveBeenCalledTimes(1); }); it('should mute on call end when call is a configured mute condition', () => { @@ -350,7 +350,7 @@ describe('MicrophoneActionsController', () => { controller.setCallAnswered(true); controller.setCallAnswered(false); - expect(microphoneManager.mute).toBeCalledTimes(1); + expect(microphoneManager.mute).toHaveBeenCalledTimes(1); }); it('should not act on the initial call state', () => { @@ -364,8 +364,8 @@ describe('MicrophoneActionsController', () => { controller.setCallAnswered(false); - expect(microphoneManager.mute).not.toBeCalled(); - expect(microphoneManager.unmute).not.toBeCalled(); + expect(microphoneManager.mute).not.toHaveBeenCalled(); + expect(microphoneManager.unmute).not.toHaveBeenCalled(); }); it('should not act on call answer when call is not a configured condition', () => { @@ -379,7 +379,7 @@ describe('MicrophoneActionsController', () => { controller.setCallAnswered(false); controller.setCallAnswered(true); - expect(microphoneManager.unmute).not.toBeCalled(); + expect(microphoneManager.unmute).not.toHaveBeenCalled(); }); }); diff --git a/tests/components-lib/live/providers/go2rtc-experimental/adapters/media-source.test.ts b/tests/components-lib/live/providers/go2rtc-experimental/adapters/media-source.test.ts index fd692f31..6e1ea387 100644 --- a/tests/components-lib/live/providers/go2rtc-experimental/adapters/media-source.test.ts +++ b/tests/components-lib/live/providers/go2rtc-experimental/adapters/media-source.test.ts @@ -87,7 +87,7 @@ describe('media-source', () => { const instance = createBrowserMediaSource(); expect(instance?.isTypeSupported('video/mp4')).toBe(false); - expect(FakeManagedMediaSource.isTypeSupported).toBeCalledWith('video/mp4'); + expect(FakeManagedMediaSource.isTypeSupported).toHaveBeenCalledWith('video/mp4'); }); }); @@ -99,7 +99,7 @@ describe('media-source', () => { const video = document.createElement('video'); instance?.attach(video); - expect(createObjectURL).toBeCalledTimes(1); + expect(createObjectURL).toHaveBeenCalledTimes(1); expect(video.src).toContain('blob:fake-url'); expect(video.srcObject).toBeNull(); }); @@ -111,15 +111,15 @@ describe('media-source', () => { const video = document.createElement('video'); instance?.attach(video); - expect(revokeObjectURL).not.toBeCalled(); + expect(revokeObjectURL).not.toHaveBeenCalled(); FakeMediaSource.instances[0].dispatchEvent(new Event('sourceopen')); - expect(revokeObjectURL).toBeCalledWith('blob:fake-url'); + expect(revokeObjectURL).toHaveBeenCalledWith('blob:fake-url'); instance?.detach(video); - expect(revokeObjectURL).toBeCalledTimes(1); + expect(revokeObjectURL).toHaveBeenCalledTimes(1); }); it('should delegate isTypeSupported to MediaSource', () => { @@ -129,7 +129,7 @@ describe('media-source', () => { const instance = createBrowserMediaSource(); expect(instance?.isTypeSupported('video/mp4')).toBe(false); - expect(FakeMediaSource.isTypeSupported).toBeCalledWith('video/mp4'); + expect(FakeMediaSource.isTypeSupported).toHaveBeenCalledWith('video/mp4'); }); it('should detach by clearing src and revoking the object URL', () => { @@ -140,7 +140,7 @@ describe('media-source', () => { instance?.attach(video); instance?.detach(video); - expect(revokeObjectURL).toBeCalledWith('blob:fake-url'); + expect(revokeObjectURL).toHaveBeenCalledWith('blob:fake-url'); expect(video.getAttribute('src')).toBe(''); }); @@ -153,7 +153,7 @@ describe('media-source', () => { instance?.detach(video); instance?.detach(video); - expect(revokeObjectURL).toBeCalledTimes(1); + expect(revokeObjectURL).toHaveBeenCalledTimes(1); }); }); @@ -170,12 +170,12 @@ describe('media-source', () => { const mediaSource = video.srcObject; assert(mediaSource instanceof FakeManagedMediaSource); mediaSource.dispatchEvent(new Event('sourceopen')); - expect(callback).toBeCalledTimes(1); + expect(callback).toHaveBeenCalledTimes(1); unsubscribe?.(); mediaSource.dispatchEvent(new Event('sourceopen')); - expect(callback).toBeCalledTimes(1); + expect(callback).toHaveBeenCalledTimes(1); }); it('should delegate addSourceBuffer', () => { @@ -188,7 +188,7 @@ describe('media-source', () => { const mediaSource = video.srcObject; assert(mediaSource instanceof FakeManagedMediaSource); - expect(mediaSource.addSourceBuffer).toBeCalledWith( + expect(mediaSource.addSourceBuffer).toHaveBeenCalledWith( 'video/mp4; codecs="avc1.640029"', ); }); @@ -203,7 +203,7 @@ describe('media-source', () => { const mediaSource = video.srcObject; assert(mediaSource instanceof FakeManagedMediaSource); - expect(mediaSource.setLiveSeekableRange).toBeCalledWith(10, 20); + expect(mediaSource.setLiveSeekableRange).toHaveBeenCalledWith(10, 20); }); it('should report open only while the media source readyState is open', () => { diff --git a/tests/components-lib/live/providers/go2rtc-experimental/image-surface-controller.test.ts b/tests/components-lib/live/providers/go2rtc-experimental/image-surface-controller.test.ts index 00c0c017..f5019976 100644 --- a/tests/components-lib/live/providers/go2rtc-experimental/image-surface-controller.test.ts +++ b/tests/components-lib/live/providers/go2rtc-experimental/image-surface-controller.test.ts @@ -72,10 +72,10 @@ describe('ImageSurfaceController', () => { await controller.showFrame(createFrame()); - expect(decoder.decode).toBeCalledTimes(1); - expect(URL.createObjectURL).toBeCalledTimes(1); + expect(decoder.decode).toHaveBeenCalledTimes(1); + expect(URL.createObjectURL).toHaveBeenCalledTimes(1); expect(image.getAttribute('src')).toBe(createdURLs()[0]); - expect(URL.revokeObjectURL).not.toBeCalled(); + expect(URL.revokeObjectURL).not.toHaveBeenCalled(); }); it('should revoke the previous frame when showing the next', async () => { @@ -85,8 +85,8 @@ describe('ImageSurfaceController', () => { await controller.showFrame(createFrame()); expect(image.getAttribute('src')).toBe(createdURLs()[1]); - expect(URL.revokeObjectURL).toBeCalledTimes(1); - expect(URL.revokeObjectURL).toBeCalledWith(createdURLs()[0]); + expect(URL.revokeObjectURL).toHaveBeenCalledTimes(1); + expect(URL.revokeObjectURL).toHaveBeenCalledWith(createdURLs()[0]); }); it('should keep showing the previous frame until the next has decoded', async () => { @@ -110,13 +110,13 @@ describe('ImageSurfaceController', () => { // The next frame is still decoding off-DOM, so the visible is // untouched and the previous URL stays valid (revoking it would blank it). expect(image.getAttribute('src')).toBe(createdURLs()[0]); - expect(URL.revokeObjectURL).not.toBeCalled(); + expect(URL.revokeObjectURL).not.toHaveBeenCalled(); releaseSecond(); await flushPromises(); expect(image.getAttribute('src')).toBe(createdURLs()[1]); - expect(URL.revokeObjectURL).toBeCalledWith(createdURLs()[0]); + expect(URL.revokeObjectURL).toHaveBeenCalledWith(createdURLs()[0]); }); it('should present only the newest frame while a decode is in flight', async () => { @@ -130,13 +130,13 @@ describe('ImageSurfaceController', () => { await flushPromises(); // Only the first frame's decode has started; the rest wait behind it. - expect(URL.createObjectURL).toBeCalledTimes(1); + expect(URL.createObjectURL).toHaveBeenCalledTimes(1); releaseDecode(); await flushPromises(); // The middle frame was superseded, so only the newest is presented next. - expect(URL.createObjectURL).toBeCalledTimes(2); + expect(URL.createObjectURL).toHaveBeenCalledTimes(2); expect(image.getAttribute('src')).toBe(createdURLs()[1]); }); @@ -148,7 +148,7 @@ describe('ImageSurfaceController', () => { await controller.showFrame(createFrame()); expect(image.hasAttribute('src')).toBe(false); - expect(URL.revokeObjectURL).toBeCalledWith(createdURLs()[0]); + expect(URL.revokeObjectURL).toHaveBeenCalledWith(createdURLs()[0]); }); it('should not paint a frame that decoded after the surface detached', async () => { @@ -165,7 +165,7 @@ describe('ImageSurfaceController', () => { await flushPromises(); expect(image.hasAttribute('src')).toBe(false); - expect(URL.revokeObjectURL).toBeCalledWith(createdURLs()[0]); + expect(URL.revokeObjectURL).toHaveBeenCalledWith(createdURLs()[0]); }); it('should do nothing without an image element', async () => { @@ -173,7 +173,7 @@ describe('ImageSurfaceController', () => { await controller.showFrame(createFrame()); - expect(URL.createObjectURL).not.toBeCalled(); + expect(URL.createObjectURL).not.toHaveBeenCalled(); }); it('should not paint onto a detached element', async () => { @@ -181,7 +181,7 @@ describe('ImageSurfaceController', () => { await controller.showFrame(createFrame()); - expect(URL.createObjectURL).not.toBeCalled(); + expect(URL.createObjectURL).not.toHaveBeenCalled(); }); }); @@ -192,7 +192,7 @@ describe('ImageSurfaceController', () => { controller.reset(); - expect(URL.revokeObjectURL).toBeCalledWith(createdURLs()[0]); + expect(URL.revokeObjectURL).toHaveBeenCalledWith(createdURLs()[0]); expect(image.hasAttribute('src')).toBe(false); }); @@ -201,7 +201,7 @@ describe('ImageSurfaceController', () => { controller.reset(); - expect(URL.revokeObjectURL).not.toBeCalled(); + expect(URL.revokeObjectURL).not.toHaveBeenCalled(); expect(image.hasAttribute('src')).toBe(false); }); @@ -226,7 +226,7 @@ describe('ImageSurfaceController', () => { const controller = new ImageSurfaceController(createLitElement(), () => null); expect(() => controller.reset()).not.toThrow(); - expect(URL.revokeObjectURL).not.toBeCalled(); + expect(URL.revokeObjectURL).not.toHaveBeenCalled(); }); }); }); diff --git a/tests/components-lib/live/providers/go2rtc-experimental/offscreen-image.test.ts b/tests/components-lib/live/providers/go2rtc-experimental/offscreen-image.test.ts index 386fa992..51e93d3c 100644 --- a/tests/components-lib/live/providers/go2rtc-experimental/offscreen-image.test.ts +++ b/tests/components-lib/live/providers/go2rtc-experimental/offscreen-image.test.ts @@ -16,7 +16,7 @@ describe('OffscreenImage', () => { const offscreen = new OffscreenImage(create); expect(offscreen.get()).toBe(offscreen.get()); - expect(create).toBeCalledTimes(1); + expect(create).toHaveBeenCalledTimes(1); }); it('should create an image with the default factory when none is injected', () => { @@ -43,7 +43,7 @@ describe('OffscreenImage', () => { offscreen.clear(); offscreen.get(); - expect(create).toBeCalledTimes(2); + expect(create).toHaveBeenCalledTimes(2); }); it('should tolerate clear when no image is held', () => { diff --git a/tests/components-lib/live/providers/go2rtc-experimental/offscreen-video.test.ts b/tests/components-lib/live/providers/go2rtc-experimental/offscreen-video.test.ts index ecef6e33..ec8ca5bb 100644 --- a/tests/components-lib/live/providers/go2rtc-experimental/offscreen-video.test.ts +++ b/tests/components-lib/live/providers/go2rtc-experimental/offscreen-video.test.ts @@ -17,7 +17,7 @@ describe('OffscreenVideo', () => { const offscreen = new OffscreenVideo(create); expect(offscreen.get()).toBe(offscreen.get()); - expect(create).toBeCalledTimes(1); + expect(create).toHaveBeenCalledTimes(1); }); it('should create a video with the default factory when none is injected', () => { @@ -48,7 +48,7 @@ describe('OffscreenVideo', () => { offscreen.clear(); offscreen.get(); - expect(create).toBeCalledTimes(2); + expect(create).toHaveBeenCalledTimes(2); }); it('should tolerate clear when no video is held', () => { diff --git a/tests/components-lib/live/providers/go2rtc-experimental/session-controller.test.ts b/tests/components-lib/live/providers/go2rtc-experimental/session-controller.test.ts index b8155efe..16877107 100644 --- a/tests/components-lib/live/providers/go2rtc-experimental/session-controller.test.ts +++ b/tests/components-lib/live/providers/go2rtc-experimental/session-controller.test.ts @@ -238,7 +238,7 @@ describe('Go2RTCSessionController', () => { const { session, surfaces, createWebSocket } = setup(); session.connect('https://host/api/ws?src=camera', surfaces, ['mse']); - expect(createWebSocket).toBeCalledWith('wss://host/api/ws?src=camera'); + expect(createWebSocket).toHaveBeenCalledWith('wss://host/api/ws?src=camera'); }); it('should be idempotent for an unchanged target', () => { @@ -246,7 +246,7 @@ describe('Go2RTCSessionController', () => { session.connect('http://host/api/ws?src=camera', surfaces, ['mse']); session.connect('http://host/api/ws?src=camera', surfaces, ['mse']); - expect(createWebSocket).toBeCalledTimes(1); + expect(createWebSocket).toHaveBeenCalledTimes(1); }); it('should reconnect when the URL changes', () => { @@ -254,8 +254,8 @@ describe('Go2RTCSessionController', () => { session.connect('http://host/api/ws?src=camera', surfaces, ['mse']); session.connect('http://host/api/ws?src=other', surfaces, ['mse']); - expect(websockets[0].close).toBeCalled(); - expect(createWebSocket).toBeCalledTimes(2); + expect(websockets[0].close).toHaveBeenCalled(); + expect(createWebSocket).toHaveBeenCalledTimes(2); }); it('should reconnect when the modes change', () => { @@ -263,8 +263,8 @@ describe('Go2RTCSessionController', () => { session.connect('http://host/api/ws?src=camera', surfaces, ['mse']); session.connect('http://host/api/ws?src=camera', surfaces, ['webrtc']); - expect(websockets[0].close).toBeCalled(); - expect(createWebSocket).toBeCalledTimes(2); + expect(websockets[0].close).toHaveBeenCalled(); + expect(createWebSocket).toHaveBeenCalledTimes(2); }); it('should be idempotent when omitted modes match the default', () => { @@ -272,7 +272,7 @@ describe('Go2RTCSessionController', () => { session.connect('http://host/api/ws?src=camera', surfaces); session.connect('http://host/api/ws?src=camera', surfaces, [...GO2RTC_MODES]); - expect(createWebSocket).toBeCalledTimes(1); + expect(createWebSocket).toHaveBeenCalledTimes(1); }); it('should construct real collaborators by default', () => { @@ -293,8 +293,8 @@ describe('Go2RTCSessionController', () => { session.connect('http://host/api/ws?src=camera', surfaces); websockets[0].fireOpen(); - expect(createBinarySource).toBeCalledTimes(1); - expect(createWebRTCSource).toBeCalledTimes(1); + expect(createBinarySource).toHaveBeenCalledTimes(1); + expect(createWebRTCSource).toHaveBeenCalledTimes(1); }); it('should keep the channel open when the binary lane drains synchronously while WebRTC is configured', () => { @@ -309,11 +309,11 @@ describe('Go2RTCSessionController', () => { session.connect('http://host/api/ws?src=camera', surfaces, ['mse', 'webrtc']); websockets[0].fireOpen(); - expect(createWebRTCSource).toBeCalledTimes(1); - expect(websockets[0].close).not.toBeCalled(); + expect(createWebRTCSource).toHaveBeenCalledTimes(1); + expect(websockets[0].close).not.toHaveBeenCalled(); vi.advanceTimersByTime(2 * 1000); - expect(createWebSocket).toBeCalledTimes(1); + expect(createWebSocket).toHaveBeenCalledTimes(1); }); }); @@ -324,9 +324,9 @@ describe('Go2RTCSessionController', () => { session.connect('http://host/api/ws?src=camera', surfaces, ['mse']); websockets[0].fireOpen(); - expect(createBinarySource).toBeCalledTimes(1); + expect(createBinarySource).toHaveBeenCalledTimes(1); expect(createBinarySource.mock.calls[0][0]).toBe('mse'); - expect(binarySources[0].start).toBeCalled(); + expect(binarySources[0].start).toHaveBeenCalled(); }); it('should report loaded media from the binary source', () => { @@ -336,7 +336,7 @@ describe('Go2RTCSessionController', () => { websockets[0].fireOpen(); binaryContexts[0].callbacks.loadedCallback(); - expect(mediaLoadedCallback).toBeCalledWith( + expect(mediaLoadedCallback).toHaveBeenCalledWith( expect.objectContaining({ technology: ['mse'] }), ); }); @@ -354,11 +354,11 @@ describe('Go2RTCSessionController', () => { websockets[0].fireOpen(); binaryContexts[0].callbacks.failedCallback('media_error'); - expect(binarySources[0].stop).toBeCalled(); - expect(websockets[0].close).toBeCalled(); + expect(binarySources[0].stop).toHaveBeenCalled(); + expect(websockets[0].close).toHaveBeenCalled(); vi.advanceTimersByTime(2 * 1000); - expect(createWebSocket).toBeCalledTimes(2); + expect(createWebSocket).toHaveBeenCalledTimes(2); }); it('should fall back through the binary modes in order', () => { @@ -385,9 +385,9 @@ describe('Go2RTCSessionController', () => { // The last binary mode failing reconnects. binaryContexts[2].callbacks.failedCallback('media_error'); - expect(websockets[0].close).toBeCalled(); + expect(websockets[0].close).toHaveBeenCalled(); vi.advanceTimersByTime(2 * 1000); - expect(createWebSocket).toBeCalledTimes(2); + expect(createWebSocket).toHaveBeenCalledTimes(2); }); it('should reconnect when the factory declines the mode', () => { @@ -397,9 +397,9 @@ describe('Go2RTCSessionController', () => { session.connect('http://host/api/ws?src=camera', surfaces, ['mse']); websockets[0].fireOpen(); - expect(websockets[0].close).toBeCalled(); + expect(websockets[0].close).toHaveBeenCalled(); vi.advanceTimersByTime(2 * 1000); - expect(createWebSocket).toBeCalledTimes(2); + expect(createWebSocket).toHaveBeenCalledTimes(2); }); it('should report loaded media with the video surface controller for MSE', () => { @@ -415,7 +415,7 @@ describe('Go2RTCSessionController', () => { expect( setupResult.mediaLoadedCallback.mock.calls[0][0].mediaPlayerController, ).toBe(setupResult.videoController); - expect(setupResult.surfaceCommittedCallback).toBeCalledWith('video'); + expect(setupResult.surfaceCommittedCallback).toHaveBeenCalledWith('video'); }); it('should report loaded media with the image surface controller for MJPEG', () => { @@ -431,7 +431,7 @@ describe('Go2RTCSessionController', () => { expect( setupResult.mediaLoadedCallback.mock.calls[0][0].mediaPlayerController, ).toBe(setupResult.imageController); - expect(setupResult.surfaceCommittedCallback).toBeCalledWith('image'); + expect(setupResult.surfaceCommittedCallback).toHaveBeenCalledWith('image'); }); it('should hide controls temporarily on load', () => { @@ -471,7 +471,7 @@ describe('Go2RTCSessionController', () => { binaryContexts[0].callbacks.loadedCallback(); // Committed once, but the reload refreshed the reported dimensions. - expect(surfaceCommittedCallback).toBeCalledTimes(1); + expect(surfaceCommittedCallback).toHaveBeenCalledTimes(1); expect(mediaLoadedCallback.mock.calls[0][0]).toEqual( expect.objectContaining({ width: 640, height: 480 }), ); @@ -494,7 +494,7 @@ describe('Go2RTCSessionController', () => { session.connect('http://host/api/ws?src=camera', surfaces, ['webrtc']); websockets[0].fireOpen(); - expect(createVideoElement).not.toBeCalled(); + expect(createVideoElement).not.toHaveBeenCalled(); expect(webRTCContexts[0].target.video).toBe(video); }); @@ -505,10 +505,10 @@ describe('Go2RTCSessionController', () => { websockets[0].fireOpen(); webRTCContexts[0].callbacks.loadedCallback(); - expect(mediaLoadedCallback).toBeCalledWith( + expect(mediaLoadedCallback).toHaveBeenCalledWith( expect.objectContaining({ technology: ['webrtc'] }), ); - expect(websockets[0].close).toBeCalled(); + expect(websockets[0].close).toHaveBeenCalled(); }); it('should reconnect when the committed WebRTC stream fails', () => { @@ -521,7 +521,7 @@ describe('Go2RTCSessionController', () => { expect(video.srcObject).toBeNull(); vi.advanceTimersByTime(2 * 1000); - expect(createWebSocket).toBeCalledTimes(2); + expect(createWebSocket).toHaveBeenCalledTimes(2); }); it('should pre-arm the WebRTC source with the current microphone stream', () => { @@ -552,7 +552,7 @@ describe('Go2RTCSessionController', () => { audioTransceiver.receiver.track.setMuted(true); - expect(mediaLoadedCallback).toBeCalledTimes(1); + expect(mediaLoadedCallback).toHaveBeenCalledTimes(1); }); }); @@ -561,8 +561,8 @@ describe('Go2RTCSessionController', () => { const setupResult = setup(); startSourceRace(setupResult); - expect(setupResult.createBinarySource).toBeCalledTimes(1); - expect(setupResult.createVideoElement).toBeCalledTimes(1); + expect(setupResult.createBinarySource).toHaveBeenCalledTimes(1); + expect(setupResult.createVideoElement).toHaveBeenCalledTimes(1); expect(setupResult.webRTCContexts[0].target.video).toBe( setupResult.offscreenVideos[0], ); @@ -579,9 +579,9 @@ describe('Go2RTCSessionController', () => { setupResult.webRTCContexts[0].callbacks.loadedCallback(); expect(setupResult.video.srcObject).toBe(setupResult.webRTCStream.asMediaStream()); - expect(setupResult.binarySources[0].stop).toBeCalled(); - expect(setupResult.websockets[0].close).toBeCalled(); - expect(setupResult.mediaLoadedCallback).toBeCalledWith( + expect(setupResult.binarySources[0].stop).toHaveBeenCalled(); + expect(setupResult.websockets[0].close).toHaveBeenCalled(); + expect(setupResult.mediaLoadedCallback).toHaveBeenCalledWith( expect.objectContaining({ technology: ['webrtc'] }), ); }); @@ -595,9 +595,9 @@ describe('Go2RTCSessionController', () => { setupResult.binaryContexts[0].callbacks.loadedCallback(); setupResult.webRTCContexts[0].callbacks.loadedCallback(); - expect(setupResult.webRTCSources[0].stop).toBeCalled(); - expect(setupResult.binarySources[0].stop).not.toBeCalled(); - expect(setupResult.websockets[0].close).not.toBeCalled(); + expect(setupResult.webRTCSources[0].stop).toHaveBeenCalled(); + expect(setupResult.binarySources[0].stop).not.toHaveBeenCalled(); + expect(setupResult.websockets[0].close).not.toHaveBeenCalled(); }); it('should adopt WebRTC that wins before the binary source loads', () => { @@ -606,7 +606,7 @@ describe('Go2RTCSessionController', () => { setupResult.webRTCContexts[0].callbacks.loadedCallback(); expect(setupResult.video.srcObject).toBe(setupResult.webRTCStream.asMediaStream()); - expect(setupResult.binarySources[0].stop).toBeCalled(); + expect(setupResult.binarySources[0].stop).toHaveBeenCalled(); }); it('should not reconnect when a racing binary fails while WebRTC continues', () => { @@ -614,7 +614,7 @@ describe('Go2RTCSessionController', () => { startSourceRace(setupResult); setupResult.binaryContexts[0].callbacks.failedCallback('media_error'); - expect(setupResult.websockets[0].close).not.toBeCalled(); + expect(setupResult.websockets[0].close).not.toHaveBeenCalled(); }); it('should not reconnect when a racing WebRTC fails while binary continues', () => { @@ -622,8 +622,8 @@ describe('Go2RTCSessionController', () => { startSourceRace(setupResult); setupResult.webRTCContexts[0].callbacks.failedCallback('connect_timeout'); - expect(setupResult.webRTCSources[0].stop).toBeCalled(); - expect(setupResult.websockets[0].close).not.toBeCalled(); + expect(setupResult.webRTCSources[0].stop).toHaveBeenCalled(); + expect(setupResult.websockets[0].close).not.toHaveBeenCalled(); }); it('should reconnect when both racing lanes fail', () => { @@ -632,9 +632,9 @@ describe('Go2RTCSessionController', () => { setupResult.binaryContexts[0].callbacks.failedCallback('media_error'); setupResult.webRTCContexts[0].callbacks.failedCallback('connect_timeout'); - expect(setupResult.websockets[0].close).toBeCalled(); + expect(setupResult.websockets[0].close).toHaveBeenCalled(); vi.advanceTimersByTime(2 * 1000); - expect(setupResult.createWebSocket).toBeCalledTimes(2); + expect(setupResult.createWebSocket).toHaveBeenCalledTimes(2); }); it('should ignore a duplicate loaded callback from a lost WebRTC lane', () => { @@ -648,7 +648,7 @@ describe('Go2RTCSessionController', () => { // WebRTC lost and stopped; a late duplicate callback is ignored. setupResult.webRTCContexts[0].callbacks.loadedCallback(); - expect(setupResult.webRTCSources[0].stop).toBeCalledTimes(1); + expect(setupResult.webRTCSources[0].stop).toHaveBeenCalledTimes(1); }); }); @@ -665,7 +665,7 @@ describe('Go2RTCSessionController', () => { setupResult.binaryContexts[0].targets.image.showFrame(frame); - expect(setupResult.showFrame).toBeCalledWith(frame); + expect(setupResult.showFrame).toHaveBeenCalledWith(frame); }); it('should reset the outgoing video surface when falling back to an image mode', () => { @@ -682,7 +682,7 @@ describe('Go2RTCSessionController', () => { expect(video.srcObject).toBeNull(); // The image surface, being committed to, is not reset. - expect(reset).not.toBeCalled(); + expect(reset).not.toHaveBeenCalled(); }); it('should reset the outgoing image surface when WebRTC wins over an image mode', () => { @@ -700,7 +700,7 @@ describe('Go2RTCSessionController', () => { setupResult.binaryContexts[0].callbacks.loadedCallback(); setupResult.webRTCContexts[0].callbacks.loadedCallback(); - expect(setupResult.reset).toBeCalled(); + expect(setupResult.reset).toHaveBeenCalled(); expect(setupResult.surfaceCommittedCallback).toHaveBeenLastCalledWith('video'); }); @@ -715,7 +715,7 @@ describe('Go2RTCSessionController', () => { websockets[0].fireOpen(); binaryContexts[0].callbacks.loadedCallback(); - expect(mediaLoadedCallback).not.toBeCalled(); + expect(mediaLoadedCallback).not.toHaveBeenCalled(); }); it('should reconnect when handed a new surfaces object for the same target', () => { @@ -728,8 +728,8 @@ describe('Go2RTCSessionController', () => { 'mse', ]); - expect(websockets[0].close).toBeCalled(); - expect(createWebSocket).toBeCalledTimes(2); + expect(websockets[0].close).toHaveBeenCalled(); + expect(createWebSocket).toHaveBeenCalledTimes(2); }); it('should abandon the binary lane when the video element is detached at open', () => { @@ -739,7 +739,7 @@ describe('Go2RTCSessionController', () => { setVideoElement(null); websockets[0].fireOpen(); - expect(createBinarySource).not.toBeCalled(); + expect(createBinarySource).not.toHaveBeenCalled(); }); it('should abandon a WebRTC-only lane when the video element is detached at open', () => { @@ -749,7 +749,7 @@ describe('Go2RTCSessionController', () => { setVideoElement(null); websockets[0].fireOpen(); - expect(createWebRTCSource).not.toBeCalled(); + expect(createWebRTCSource).not.toHaveBeenCalled(); }); it('should still commit a WebRTC win when the video element is detached', () => { @@ -762,8 +762,8 @@ describe('Go2RTCSessionController', () => { // No element to attach the stream to, but the win still tears down the // binary lane and reports loaded media (dimensions come from the // off-screen element). - expect(setupResult.binarySources[0].stop).toBeCalled(); - expect(setupResult.mediaLoadedCallback).toBeCalledWith( + expect(setupResult.binarySources[0].stop).toHaveBeenCalled(); + expect(setupResult.mediaLoadedCallback).toHaveBeenCalledWith( expect.objectContaining({ technology: ['webrtc'] }), ); }); @@ -782,7 +782,7 @@ describe('Go2RTCSessionController', () => { setVideoElement(null); expect(() => binaryContexts[0].callbacks.loadedCallback()).not.toThrow(); - expect(mediaLoadedCallback).not.toBeCalled(); + expect(mediaLoadedCallback).not.toHaveBeenCalled(); }); it('should skip resetting a detached video surface on a switch to image', () => { @@ -826,7 +826,7 @@ describe('Go2RTCSessionController', () => { // Re-attached in time for the retry, which then reconnects normally. setVideoElement(video); vi.advanceTimersByTime(2 * 1000); - expect(createWebSocket).toBeCalledTimes(2); + expect(createWebSocket).toHaveBeenCalledTimes(2); }); }); @@ -840,7 +840,7 @@ describe('Go2RTCSessionController', () => { ]).asMediaStream(); session.setMicrophoneStream(micStream); - expect(webRTCSources[0].setMicrophoneStream).toBeCalledWith(micStream); + expect(webRTCSources[0].setMicrophoneStream).toHaveBeenCalledWith(micStream); }); it('should tolerate a microphone change with no WebRTC source', () => { @@ -857,9 +857,9 @@ describe('Go2RTCSessionController', () => { websockets[0].fireOpen(); websockets[0].fireClose(); - expect(binarySources[0].stop).toBeCalled(); + expect(binarySources[0].stop).toHaveBeenCalled(); vi.advanceTimersByTime(2 * 1000); - expect(createWebSocket).toBeCalledTimes(2); + expect(createWebSocket).toHaveBeenCalledTimes(2); }); it('should escalate via the error callback after exhausting reconnect attempts', () => { @@ -877,13 +877,13 @@ describe('Go2RTCSessionController', () => { websockets[3].fireOpen(); websockets[3].fireClose(); - expect(createWebSocket).toBeCalledTimes(4); - expect(errorCallback).toBeCalledTimes(1); + expect(createWebSocket).toHaveBeenCalledTimes(4); + expect(errorCallback).toHaveBeenCalledTimes(1); // The socket dropped with no source reporting a cause. - expect(errorCallback).toBeCalledWith(null); + expect(errorCallback).toHaveBeenCalledWith(null); vi.advanceTimersByTime(2 * 1000); - expect(createWebSocket).toBeCalledTimes(4); + expect(createWebSocket).toHaveBeenCalledTimes(4); }); it('should escalate with the most recent source failure reason', () => { @@ -901,7 +901,7 @@ describe('Go2RTCSessionController', () => { websockets[3].fireOpen(); binaryContexts[3].callbacks.failedCallback('unsupported'); - expect(errorCallback).toBeCalledWith('unsupported'); + expect(errorCallback).toHaveBeenCalledWith('unsupported'); }); it('should reset the reconnect budget after a successful media load', () => { @@ -933,8 +933,8 @@ describe('Go2RTCSessionController', () => { websockets[attempt + 1].fireOpen(); } - expect(errorCallback).not.toBeCalled(); - expect(createWebSocket).toBeCalledTimes(6); + expect(errorCallback).not.toHaveBeenCalled(); + expect(createWebSocket).toHaveBeenCalledTimes(6); }); it('should tear down all lanes and clear the video on reset', () => { @@ -942,9 +942,9 @@ describe('Go2RTCSessionController', () => { startSourceRace(setupResult); setupResult.session.reset(); - expect(setupResult.binarySources[0].stop).toBeCalled(); - expect(setupResult.webRTCSources[0].stop).toBeCalled(); - expect(setupResult.websockets[0].close).toBeCalled(); + expect(setupResult.binarySources[0].stop).toHaveBeenCalled(); + expect(setupResult.webRTCSources[0].stop).toHaveBeenCalled(); + expect(setupResult.websockets[0].close).toHaveBeenCalled(); expect(setupResult.video.srcObject).toBeNull(); }); @@ -956,7 +956,7 @@ describe('Go2RTCSessionController', () => { session.reset(); vi.advanceTimersByTime(2 * 1000); - expect(createWebSocket).toBeCalledTimes(1); + expect(createWebSocket).toHaveBeenCalledTimes(1); }); it('should allow connecting to the same target after reset', () => { @@ -965,7 +965,7 @@ describe('Go2RTCSessionController', () => { session.reset(); session.connect('http://host/api/ws?src=camera', surfaces, ['mse']); - expect(createWebSocket).toBeCalledTimes(2); + expect(createWebSocket).toHaveBeenCalledTimes(2); }); }); @@ -978,7 +978,7 @@ describe('Go2RTCSessionController', () => { binaryContexts[0].callbacks.failedCallback('media_error'); vi.advanceTimersByTime(2 * 1000); - expect(createWebSocket).toBeCalledTimes(2); + expect(createWebSocket).toHaveBeenCalledTimes(2); }); it('should ignore a loaded callback from a retired binary source', () => { @@ -990,7 +990,7 @@ describe('Go2RTCSessionController', () => { setupResult.mediaLoadedCallback.mockClear(); setupResult.binaryContexts[0].callbacks.loadedCallback(); - expect(setupResult.mediaLoadedCallback).not.toBeCalled(); + expect(setupResult.mediaLoadedCallback).not.toHaveBeenCalled(); }); it('should ignore a failed callback from a retired binary source', () => { @@ -1000,7 +1000,7 @@ describe('Go2RTCSessionController', () => { setupResult.binarySources[0].stop.mockClear(); setupResult.binaryContexts[0].callbacks.failedCallback('media_error'); - expect(setupResult.binarySources[0].stop).not.toBeCalled(); + expect(setupResult.binarySources[0].stop).not.toHaveBeenCalled(); }); it('should ignore a failed callback from a retired WebRTC source', () => { @@ -1012,7 +1012,7 @@ describe('Go2RTCSessionController', () => { webRTCSources[0].stop.mockClear(); webRTCContexts[0].callbacks.failedCallback('media_error'); - expect(webRTCSources[0].stop).not.toBeCalled(); + expect(webRTCSources[0].stop).not.toHaveBeenCalled(); }); it('should adopt WebRTC when the racing binary already failed', () => { @@ -1031,7 +1031,7 @@ describe('Go2RTCSessionController', () => { setupResult.webRTCContexts[0].callbacks.loadedCallback(); expect(setupResult.video.srcObject).toBeFalsy(); - expect(setupResult.binarySources[0].stop).toBeCalled(); + expect(setupResult.binarySources[0].stop).toHaveBeenCalled(); }); it('should not report media that cannot be described', () => { @@ -1045,7 +1045,7 @@ describe('Go2RTCSessionController', () => { websockets[0].fireOpen(); binaryContexts[0].callbacks.loadedCallback(); - expect(mediaLoadedCallback).not.toBeCalled(); + expect(mediaLoadedCallback).not.toHaveBeenCalled(); }); it('should swallow a rejected microphone update', async () => { @@ -1090,7 +1090,7 @@ describe('Go2RTCSessionController', () => { ]); websockets[0].fireOpen(); - expect(mediaLoadedCallback).not.toBeCalled(); + expect(mediaLoadedCallback).not.toHaveBeenCalled(); }); it('should ignore callbacks fired while a WebRTC source is constructed', () => { @@ -1124,7 +1124,7 @@ describe('Go2RTCSessionController', () => { ]); websockets[0].fireOpen(); - expect(mediaLoadedCallback).not.toBeCalled(); + expect(mediaLoadedCallback).not.toHaveBeenCalled(); }); it('should use the default binary source factory when none is injected', () => { @@ -1153,7 +1153,7 @@ describe('Go2RTCSessionController', () => { // closes and retries; the point is that the default factory was used. websockets[0].fireOpen(); - expect(websockets[0].close).toBeCalled(); + expect(websockets[0].close).toHaveBeenCalled(); session.reset(); }); @@ -1236,7 +1236,7 @@ describe('Go2RTCSessionController', () => { binaryContexts[0].callbacks.failedCallback('media_error'); - expect(consoleSpy).toBeCalledWith('go2rtc-experimental source failed', { + expect(consoleSpy).toHaveBeenCalledWith('go2rtc-experimental source failed', { lane: 'binary', mode: 'mse', reason: 'media_error', @@ -1253,7 +1253,7 @@ describe('Go2RTCSessionController', () => { webRTCContexts[0].callbacks.failedCallback('connect_timeout'); - expect(consoleSpy).toBeCalledWith('go2rtc-experimental source failed', { + expect(consoleSpy).toHaveBeenCalledWith('go2rtc-experimental source failed', { lane: 'webrtc', reason: 'connect_timeout', }); @@ -1267,7 +1267,7 @@ describe('Go2RTCSessionController', () => { binaryContexts[0].callbacks.failedCallback('media_error'); - expect(consoleSpy).not.toBeCalled(); + expect(consoleSpy).not.toHaveBeenCalled(); }); }); }); diff --git a/tests/components-lib/live/providers/go2rtc-experimental/signaling.test.ts b/tests/components-lib/live/providers/go2rtc-experimental/signaling.test.ts index c5341062..5cc9e35f 100644 --- a/tests/components-lib/live/providers/go2rtc-experimental/signaling.test.ts +++ b/tests/components-lib/live/providers/go2rtc-experimental/signaling.test.ts @@ -29,7 +29,7 @@ describe('SignalingChannel', () => { const { channel, createWebSocket, websockets } = setup(); channel.connect(); - expect(createWebSocket).toBeCalledWith('ws://host/api/ws?src=camera'); + expect(createWebSocket).toHaveBeenCalledWith('ws://host/api/ws?src=camera'); expect(websockets[0].binaryType).toBe('arraybuffer'); }); @@ -38,7 +38,7 @@ describe('SignalingChannel', () => { channel.connect(); channel.connect(); - expect(createWebSocket).toBeCalledTimes(1); + expect(createWebSocket).toHaveBeenCalledTimes(1); }); it('should report open state and call the open callback', () => { @@ -51,7 +51,7 @@ describe('SignalingChannel', () => { websockets[0].fireOpen(); expect(channel.isOpen()).toBe(true); - expect(openCallback).toBeCalled(); + expect(openCallback).toHaveBeenCalled(); }); it('should tolerate an absent open callback', () => { @@ -66,7 +66,7 @@ describe('SignalingChannel', () => { channel.connect(); channel.send({ type: 'mse', value: 'codecs' }); - expect(websockets[0].send).not.toBeCalled(); + expect(websockets[0].send).not.toHaveBeenCalled(); }); it('should send JSON once open', () => { @@ -86,7 +86,7 @@ describe('SignalingChannel', () => { channel.connect(); websockets[0].fireMessage('{"type":"mse","value":"video/mp4"}'); - expect(callback).toBeCalledWith({ type: 'mse', value: 'video/mp4' }); + expect(callback).toHaveBeenCalledWith({ type: 'mse', value: 'video/mp4' }); }); it('should stop dispatching after unsubscribe', () => { @@ -97,7 +97,7 @@ describe('SignalingChannel', () => { unsubscribe(); websockets[0].fireMessage('{"type":"mse"}'); - expect(callback).not.toBeCalled(); + expect(callback).not.toHaveBeenCalled(); }); it('should dispatch to remaining subscribers when one unsubscribes during dispatch', () => { @@ -111,8 +111,8 @@ describe('SignalingChannel', () => { channel.connect(); websockets[0].fireMessage('{"type":"mse"}'); - expect(unsubscribeDuringDispatch).toBeCalledTimes(1); - expect(secondCallback).toBeCalledTimes(1); + expect(unsubscribeDuringDispatch).toHaveBeenCalledTimes(1); + expect(secondCallback).toHaveBeenCalledTimes(1); }); it('should ignore invalid JSON', () => { @@ -122,7 +122,7 @@ describe('SignalingChannel', () => { channel.connect(); websockets[0].fireMessage('NOT JSON'); - expect(callback).not.toBeCalled(); + expect(callback).not.toHaveBeenCalled(); }); it('should ignore malformed messages', () => { @@ -132,7 +132,7 @@ describe('SignalingChannel', () => { channel.connect(); websockets[0].fireMessage('{"type":6}'); - expect(callback).not.toBeCalled(); + expect(callback).not.toHaveBeenCalled(); }); it('should ignore unexpected data types', () => { @@ -142,7 +142,7 @@ describe('SignalingChannel', () => { channel.connect(); websockets[0].fireMessage(42); - expect(callback).not.toBeCalled(); + expect(callback).not.toHaveBeenCalled(); }); it('should route binary data to the binary callback', () => { @@ -153,7 +153,7 @@ describe('SignalingChannel', () => { const data = new ArrayBuffer(8); websockets[0].fireMessage(data); - expect(binaryCallback).toBeCalledWith(data); + expect(binaryCallback).toHaveBeenCalledWith(data); }); it('should drop binary data without a binary callback', () => { @@ -171,7 +171,7 @@ describe('SignalingChannel', () => { channel.connect(); websockets[0].fireMessage(new ArrayBuffer(8)); - expect(binaryCallback).not.toBeCalled(); + expect(binaryCallback).not.toHaveBeenCalled(); }); it('should close the underlying websocket without firing the disconnect callback', () => { @@ -181,9 +181,9 @@ describe('SignalingChannel', () => { websockets[0].fireOpen(); channel.close(); - expect(websockets[0].close).toBeCalled(); + expect(websockets[0].close).toHaveBeenCalled(); expect(channel.isOpen()).toBe(false); - expect(disconnectCallback).not.toBeCalled(); + expect(disconnectCallback).not.toHaveBeenCalled(); }); it('should tolerate closing when never connected', () => { @@ -205,9 +205,9 @@ describe('SignalingChannel', () => { websockets[0].fireMessage('{"type":"mse"}'); websockets[0].fireClose(); - expect(openCallback).not.toBeCalled(); - expect(messageCallback).not.toBeCalled(); - expect(disconnectCallback).not.toBeCalled(); + expect(openCallback).not.toHaveBeenCalled(); + expect(messageCallback).not.toHaveBeenCalled(); + expect(disconnectCallback).not.toHaveBeenCalled(); }); it('should fire the disconnect callback on unexpected closure', () => { @@ -217,7 +217,7 @@ describe('SignalingChannel', () => { websockets[0].fireOpen(); websockets[0].fireClose(); - expect(disconnectCallback).toBeCalledTimes(1); + expect(disconnectCallback).toHaveBeenCalledTimes(1); expect(channel.isOpen()).toBe(false); }); @@ -234,7 +234,7 @@ describe('SignalingChannel', () => { websockets[0].fireClose(); channel.connect(); - expect(createWebSocket).toBeCalledTimes(2); + expect(createWebSocket).toHaveBeenCalledTimes(2); }); it('should construct a real websocket by default', () => { diff --git a/tests/components-lib/live/providers/go2rtc-experimental/sources/mjpeg.test.ts b/tests/components-lib/live/providers/go2rtc-experimental/sources/mjpeg.test.ts index 81f6e5e4..fe8d22dc 100644 --- a/tests/components-lib/live/providers/go2rtc-experimental/sources/mjpeg.test.ts +++ b/tests/components-lib/live/providers/go2rtc-experimental/sources/mjpeg.test.ts @@ -41,7 +41,7 @@ describe('MJPEGStreamSource', () => { source.start(); channel.binaryCallback?.(frame()); - expect(showFrame).toBeCalledTimes(1); + expect(showFrame).toHaveBeenCalledTimes(1); const shown = showFrame.mock.calls[0][0] as Blob; expect(shown).toBeInstanceOf(Blob); expect(shown.type).toBe('image/jpeg'); @@ -55,7 +55,7 @@ describe('MJPEGStreamSource', () => { channel.binaryCallback?.(frame()); await flushPromises(); - expect(loadedCallback).toBeCalledTimes(1); + expect(loadedCallback).toHaveBeenCalledTimes(1); }); it('should not report loaded when stopped before the first frame decodes', async () => { @@ -75,7 +75,7 @@ describe('MJPEGStreamSource', () => { resolveDecode(); await flushPromises(); - expect(loadedCallback).not.toBeCalled(); + expect(loadedCallback).not.toHaveBeenCalled(); }); it('should fail on a server error for mjpeg', () => { @@ -83,7 +83,7 @@ describe('MJPEGStreamSource', () => { source.start(); channel.receiveMessage({ type: 'error', value: 'mjpeg: stream not found' }); - expect(failedCallback).toBeCalledWith('server_error'); + expect(failedCallback).toHaveBeenCalledWith('server_error'); }); it('should ignore a server error for another mode', () => { @@ -91,7 +91,7 @@ describe('MJPEGStreamSource', () => { source.start(); channel.receiveMessage({ type: 'error', value: 'mse: stream not found' }); - expect(failedCallback).not.toBeCalled(); + expect(failedCallback).not.toHaveBeenCalled(); }); it('should stop cleanly', () => { @@ -146,7 +146,7 @@ describe('MJPEGStreamSource', () => { source.start(); vi.advanceTimersByTime(5 * 1000); - expect(failedCallback).toBeCalledWith('connect_timeout'); + expect(failedCallback).toHaveBeenCalledWith('connect_timeout'); }); it('should not fail once a frame has arrived', () => { @@ -155,7 +155,7 @@ describe('MJPEGStreamSource', () => { channel.binaryCallback?.(frame()); vi.advanceTimersByTime(5 * 1000); - expect(failedCallback).not.toBeCalled(); + expect(failedCallback).not.toHaveBeenCalled(); }); it('should not fail after stop', () => { @@ -164,7 +164,7 @@ describe('MJPEGStreamSource', () => { source.stop(); vi.advanceTimersByTime(5 * 1000); - expect(failedCallback).not.toBeCalled(); + expect(failedCallback).not.toHaveBeenCalled(); }); }); }); diff --git a/tests/components-lib/live/providers/go2rtc-experimental/sources/mp4.test.ts b/tests/components-lib/live/providers/go2rtc-experimental/sources/mp4.test.ts index 2df988d8..92c37deb 100644 --- a/tests/components-lib/live/providers/go2rtc-experimental/sources/mp4.test.ts +++ b/tests/components-lib/live/providers/go2rtc-experimental/sources/mp4.test.ts @@ -93,7 +93,7 @@ describe('MP4StreamSource', () => { channel.binaryCallback?.(frame()); // Second frame reuses the same decoder rather than creating another. - expect(createVideoElement).toBeCalledTimes(1); + expect(createVideoElement).toHaveBeenCalledTimes(1); }); it('should draw a decoded frame and show it as an image', async () => { @@ -103,12 +103,12 @@ describe('MP4StreamSource', () => { decoderVideo.dispatchEvent(new Event('loadeddata')); await flushPromises(); - expect(canvas.context?.drawImage).toBeCalled(); - expect(showFrame).toBeCalledTimes(1); + expect(canvas.context?.drawImage).toHaveBeenCalled(); + expect(showFrame).toHaveBeenCalledTimes(1); const shown = showFrame.mock.calls[0][0] as Blob; expect(shown).toBeInstanceOf(Blob); expect(shown.type).toBe('image/jpeg'); - expect(loadedCallback).toBeCalledTimes(1); + expect(loadedCallback).toHaveBeenCalledTimes(1); }); it('should report loaded only on the first drawn frame', async () => { @@ -121,7 +121,7 @@ describe('MP4StreamSource', () => { decoderVideo.dispatchEvent(new Event('loadeddata')); await flushPromises(); - expect(loadedCallback).toBeCalledTimes(1); + expect(loadedCallback).toHaveBeenCalledTimes(1); }); it('should not show a frame when the canvas produces no blob', () => { @@ -131,7 +131,7 @@ describe('MP4StreamSource', () => { channel.binaryCallback?.(frame()); decoderVideo.dispatchEvent(new Event('loadeddata')); - expect(showFrame).not.toBeCalled(); + expect(showFrame).not.toHaveBeenCalled(); }); it('should do nothing when the canvas has no 2d context', () => { @@ -141,7 +141,7 @@ describe('MP4StreamSource', () => { channel.binaryCallback?.(frame()); decoderVideo.dispatchEvent(new Event('loadeddata')); - expect(showFrame).not.toBeCalled(); + expect(showFrame).not.toHaveBeenCalled(); }); it('should fail on a server error for mp4', () => { @@ -149,7 +149,7 @@ describe('MP4StreamSource', () => { source.start(); channel.receiveMessage({ type: 'error', value: 'mp4: stream not found' }); - expect(failedCallback).toBeCalledWith('server_error'); + expect(failedCallback).toHaveBeenCalledWith('server_error'); }); it('should clear the decoder on stop', () => { @@ -172,7 +172,7 @@ describe('MP4StreamSource', () => { // surface. decoderVideo.dispatchEvent(new Event('loadeddata')); - expect(showFrame).not.toBeCalled(); + expect(showFrame).not.toHaveBeenCalled(); }); describe('first-frame timeout', () => { @@ -181,7 +181,7 @@ describe('MP4StreamSource', () => { source.start(); vi.advanceTimersByTime(5 * 1000); - expect(failedCallback).toBeCalledWith('connect_timeout'); + expect(failedCallback).toHaveBeenCalledWith('connect_timeout'); }); it('should not fail once a frame has been drawn', () => { @@ -191,7 +191,7 @@ describe('MP4StreamSource', () => { decoderVideo.dispatchEvent(new Event('loadeddata')); vi.advanceTimersByTime(5 * 1000); - expect(failedCallback).not.toBeCalled(); + expect(failedCallback).not.toHaveBeenCalled(); }); it('should not fail after stop', () => { @@ -200,7 +200,7 @@ describe('MP4StreamSource', () => { source.stop(); vi.advanceTimersByTime(5 * 1000); - expect(failedCallback).not.toBeCalled(); + expect(failedCallback).not.toHaveBeenCalled(); }); }); diff --git a/tests/components-lib/live/providers/go2rtc-experimental/sources/mse.test.ts b/tests/components-lib/live/providers/go2rtc-experimental/sources/mse.test.ts index 11931b20..6ce8de6b 100644 --- a/tests/components-lib/live/providers/go2rtc-experimental/sources/mse.test.ts +++ b/tests/components-lib/live/providers/go2rtc-experimental/sources/mse.test.ts @@ -70,14 +70,14 @@ describe('MSEStreamSource', () => { const { source, failedCallback } = setup({ unsupported: true }); source.start(); - expect(failedCallback).toBeCalledWith('unsupported'); + expect(failedCallback).toHaveBeenCalledWith('unsupported'); }); it('should attach the media source to the video on start', () => { const { source, instance, video } = setup(); source.start(); - expect(instance.attach).toBeCalledWith(video); + expect(instance.attach).toHaveBeenCalledWith(video); }); }); @@ -121,7 +121,7 @@ describe('MSEStreamSource', () => { instance.fireSourceOpen(); vi.advanceTimersByTime(5 * 1000); - expect(failedCallback).toBeCalledWith('negotiation_timeout'); + expect(failedCallback).toHaveBeenCalledWith('negotiation_timeout'); }); it('should not time out after a successful negotiation', () => { @@ -129,14 +129,14 @@ describe('MSEStreamSource', () => { negotiate(setupResult); vi.advanceTimersByTime(5 * 1000); - expect(setupResult.failedCallback).not.toBeCalled(); + expect(setupResult.failedCallback).not.toHaveBeenCalled(); }); it('should create a source buffer in segments mode on negotiation', () => { const setupResult = setup(); negotiate(setupResult); - expect(setupResult.instance.addSourceBuffer).toBeCalledWith( + expect(setupResult.instance.addSourceBuffer).toHaveBeenCalledWith( 'video/mp4; codecs="avc1.640029,mp4a.40.2"', ); expect(setupResult.instance.sourceBuffer.mode).toBe('segments'); @@ -148,7 +148,7 @@ describe('MSEStreamSource', () => { negotiate(setupResult); setupResult.channel.receiveMessage({ type: 'mse', value: 'video/mp4' }); - expect(setupResult.instance.addSourceBuffer).toBeCalledTimes(1); + expect(setupResult.instance.addSourceBuffer).toHaveBeenCalledTimes(1); }); it('should ignore negotiation responses without a string value', () => { @@ -156,7 +156,7 @@ describe('MSEStreamSource', () => { setupResult.source.start(); setupResult.channel.receiveMessage({ type: 'mse', value: 42 }); - expect(setupResult.instance.addSourceBuffer).not.toBeCalled(); + expect(setupResult.instance.addSourceBuffer).not.toHaveBeenCalled(); }); it('should ignore unrelated messages', () => { @@ -164,8 +164,8 @@ describe('MSEStreamSource', () => { setupResult.source.start(); setupResult.channel.receiveMessage({ type: 'webrtc/answer', value: 'sdp' }); - expect(setupResult.instance.addSourceBuffer).not.toBeCalled(); - expect(setupResult.failedCallback).not.toBeCalled(); + expect(setupResult.instance.addSourceBuffer).not.toHaveBeenCalled(); + expect(setupResult.failedCallback).not.toHaveBeenCalled(); }); }); @@ -176,12 +176,12 @@ describe('MSEStreamSource', () => { instance.fireSourceOpen(); channel.receiveMessage({ type: 'error', value: 'mse: stream not found' }); - expect(failedCallback).toBeCalledWith('server_error'); + expect(failedCallback).toHaveBeenCalledWith('server_error'); // The negotiation timer must have stopped. failedCallback.mockClear(); vi.advanceTimersByTime(5 * 1000); - expect(failedCallback).not.toBeCalled(); + expect(failedCallback).not.toHaveBeenCalled(); }); it('should ignore server errors for other modes', () => { @@ -189,7 +189,7 @@ describe('MSEStreamSource', () => { source.start(); channel.receiveMessage({ type: 'error', value: 'webrtc/offer: failed' }); - expect(failedCallback).not.toBeCalled(); + expect(failedCallback).not.toHaveBeenCalled(); }); it('should ignore server errors without a string value', () => { @@ -197,7 +197,7 @@ describe('MSEStreamSource', () => { source.start(); channel.receiveMessage({ type: 'error' }); - expect(failedCallback).not.toBeCalled(); + expect(failedCallback).not.toHaveBeenCalled(); }); }); @@ -211,7 +211,7 @@ describe('MSEStreamSource', () => { instance.fireSourceOpen(); channel.receiveMessage({ type: 'mse', value: 'video/mp4' }); - expect(failedCallback).toBeCalledWith('media_error'); + expect(failedCallback).toHaveBeenCalledWith('media_error'); }); it('should append binary data directly when idle', () => { @@ -220,7 +220,7 @@ describe('MSEStreamSource', () => { const data = new ArrayBuffer(8); setupResult.channel.binaryCallback?.(data); - expect(setupResult.instance.sourceBuffer.appendBuffer).toBeCalledWith(data); + expect(setupResult.instance.sourceBuffer.appendBuffer).toHaveBeenCalledWith(data); }); it('should swallow direct append failures', () => { @@ -233,7 +233,7 @@ describe('MSEStreamSource', () => { expect(() => setupResult.channel.binaryCallback?.(new ArrayBuffer(8)), ).not.toThrow(); - expect(setupResult.failedCallback).not.toBeCalled(); + expect(setupResult.failedCallback).not.toHaveBeenCalled(); }); it('should stage binary data while the source buffer updates', () => { @@ -243,12 +243,14 @@ describe('MSEStreamSource', () => { const staged = new ArrayBuffer(8); setupResult.channel.binaryCallback?.(staged); - expect(setupResult.instance.sourceBuffer.appendBuffer).not.toBeCalled(); + expect(setupResult.instance.sourceBuffer.appendBuffer).not.toHaveBeenCalled(); setupResult.instance.sourceBuffer.updating = false; setupResult.instance.sourceBuffer.fireUpdateEnd(); - expect(setupResult.instance.sourceBuffer.appendBuffer).toBeCalledWith(staged); + expect(setupResult.instance.sourceBuffer.appendBuffer).toHaveBeenCalledWith( + staged, + ); }); it('should stage binary data behind earlier staged data', () => { @@ -263,7 +265,7 @@ describe('MSEStreamSource', () => { sourceBuffer.updating = false; setupResult.channel.binaryCallback?.(second); - expect(sourceBuffer.appendBuffer).not.toBeCalled(); + expect(sourceBuffer.appendBuffer).not.toHaveBeenCalled(); sourceBuffer.fireUpdateEnd(); expect(sourceBuffer.appendBuffer).toHaveBeenNthCalledWith(1, first); @@ -279,10 +281,10 @@ describe('MSEStreamSource', () => { sourceBuffer.updating = true; setupResult.channel.binaryCallback?.(new ArrayBuffer(2 * 1024 * 1024)); - expect(setupResult.failedCallback).not.toBeCalled(); + expect(setupResult.failedCallback).not.toHaveBeenCalled(); setupResult.channel.binaryCallback?.(new ArrayBuffer(1)); - expect(setupResult.failedCallback).toBeCalledWith('buffer_overflow'); + expect(setupResult.failedCallback).toHaveBeenCalledWith('buffer_overflow'); }); }); @@ -295,7 +297,7 @@ describe('MSEStreamSource', () => { sourceBuffer.updating = true; sourceBuffer.fireUpdateEnd(); - expect(sourceBuffer.remove).not.toBeCalled(); + expect(sourceBuffer.remove).not.toHaveBeenCalled(); }); it('should do nothing on updateend without buffered content', () => { @@ -303,8 +305,8 @@ describe('MSEStreamSource', () => { negotiate(setupResult); setupResult.instance.sourceBuffer.fireUpdateEnd(); - expect(setupResult.instance.sourceBuffer.remove).not.toBeCalled(); - expect(setupResult.instance.setLiveSeekableRange).not.toBeCalled(); + expect(setupResult.instance.sourceBuffer.remove).not.toHaveBeenCalled(); + expect(setupResult.instance.setLiveSeekableRange).not.toHaveBeenCalled(); }); it('should not trim after the media source has closed', () => { @@ -319,8 +321,8 @@ describe('MSEStreamSource', () => { setupResult.instance.isOpen.mockReturnValue(false); sourceBuffer.fireUpdateEnd(); - expect(sourceBuffer.remove).not.toBeCalled(); - expect(setupResult.instance.setLiveSeekableRange).not.toBeCalled(); + expect(sourceBuffer.remove).not.toHaveBeenCalled(); + expect(setupResult.instance.setLiveSeekableRange).not.toHaveBeenCalled(); }); it('should trim media behind the retained window', () => { @@ -332,8 +334,8 @@ describe('MSEStreamSource', () => { sourceBuffer.fireUpdateEnd(); // Retains the last 15s (end 20 -> retainedStart 5). - expect(sourceBuffer.remove).toBeCalledWith(0, 5); - expect(setupResult.instance.setLiveSeekableRange).toBeCalledWith(5, 20); + expect(sourceBuffer.remove).toHaveBeenCalledWith(0, 5); + expect(setupResult.instance.setLiveSeekableRange).toHaveBeenCalledWith(5, 20); }); it('should not trim when all media is within the retained window', () => { @@ -344,7 +346,7 @@ describe('MSEStreamSource', () => { setupResult.video.currentTime = 19; sourceBuffer.fireUpdateEnd(); - expect(sourceBuffer.remove).not.toBeCalled(); + expect(sourceBuffer.remove).not.toHaveBeenCalled(); }); it('should not move the playhead when it falls behind the window', () => { @@ -464,7 +466,7 @@ describe('MSEStreamSource', () => { sourceBuffer.fireUpdateEnd(); // Trim still bounds memory, but the playhead and rate are left untouched. - expect(sourceBuffer.remove).toBeCalled(); + expect(sourceBuffer.remove).toHaveBeenCalled(); expect(setupResult.video.currentTime).toBe(2); expect(setupResult.video.playbackRate).toBe(1); }); @@ -507,7 +509,7 @@ describe('MSEStreamSource', () => { negotiate(setupResult); setupResult.video.dispatchEvent(new Event('loadeddata')); - expect(setupResult.loadedCallback).toBeCalledTimes(1); + expect(setupResult.loadedCallback).toHaveBeenCalledTimes(1); }); it('should fail on video element errors', () => { @@ -515,7 +517,7 @@ describe('MSEStreamSource', () => { setupResult.source.start(); setupResult.video.dispatchEvent(new Event('error')); - expect(setupResult.failedCallback).toBeCalledWith('media_error'); + expect(setupResult.failedCallback).toHaveBeenCalledWith('media_error'); }); }); @@ -525,19 +527,19 @@ describe('MSEStreamSource', () => { negotiate(setupResult); setupResult.source.stop(); - expect(setupResult.instance.detach).toBeCalledWith(setupResult.video); + expect(setupResult.instance.detach).toHaveBeenCalledWith(setupResult.video); expect(setupResult.channel.binaryCallback).toBeNull(); expect(setupResult.channel.getMessageCallbackCount()).toBe(0); expect(setupResult.instance.getSourceOpenCallbackCount()).toBe(0); setupResult.video.dispatchEvent(new Event('loadeddata')); setupResult.video.dispatchEvent(new Event('error')); - expect(setupResult.loadedCallback).not.toBeCalled(); - expect(setupResult.failedCallback).not.toBeCalled(); + expect(setupResult.loadedCallback).not.toHaveBeenCalled(); + expect(setupResult.failedCallback).not.toHaveBeenCalled(); setupResult.instance.sourceBuffer.buffered = createTimeRanges([[0, 20]]); setupResult.instance.sourceBuffer.fireUpdateEnd(); - expect(setupResult.instance.sourceBuffer.remove).not.toBeCalled(); + expect(setupResult.instance.sourceBuffer.remove).not.toHaveBeenCalled(); }); it('should stop the negotiation timer on stop', () => { @@ -547,7 +549,7 @@ describe('MSEStreamSource', () => { setupResult.source.stop(); vi.advanceTimersByTime(5 * 1000); - expect(setupResult.failedCallback).not.toBeCalled(); + expect(setupResult.failedCallback).not.toHaveBeenCalled(); }); it('should tolerate stopping before starting', () => { diff --git a/tests/components-lib/media-actions-controller.test.ts b/tests/components-lib/media-actions-controller.test.ts index cc196d10..8d968fb3 100644 --- a/tests/components-lib/media-actions-controller.test.ts +++ b/tests/components-lib/media-actions-controller.test.ts @@ -104,7 +104,7 @@ describe('MediaActionsController', () => { expect( (await getPlayer(children[0], 'video')?.getMediaPlayerController())?.playback ?.play, - ).not.toBeCalled(); + ).not.toHaveBeenCalled(); }); it('should do nothing on resetting same root', () => { @@ -138,7 +138,7 @@ describe('MediaActionsController', () => { await controller.setTarget(1, true); - expect(mediaPlayerController.playback?.play).toBeCalled(); + expect(mediaPlayerController.playback?.play).toHaveBeenCalled(); }); }); @@ -161,7 +161,7 @@ describe('MediaActionsController', () => { expect( (await getPlayer(children[0], 'video')?.getMediaPlayerController())?.playback ?.play, - ).not.toBeCalled(); + ).not.toHaveBeenCalled(); }); }); @@ -195,7 +195,7 @@ describe('MediaActionsController', () => { await getPlayer(children[0], 'video')?.getMediaPlayerController(), func, ), - ).toBeCalledTimes(called ? 1 : 0); + ).toHaveBeenCalledTimes(called ? 1 : 0); }, ); @@ -214,14 +214,14 @@ describe('MediaActionsController', () => { expect( (await getPlayer(children[0], 'video')?.getMediaPlayerController())?.playback ?.play, - ).toBeCalledTimes(1); + ).toHaveBeenCalledTimes(1); await controller.setTarget(0, true); expect( (await getPlayer(children[0], 'video')?.getMediaPlayerController())?.playback ?.play, - ).toBeCalledTimes(1); + ).toHaveBeenCalledTimes(1); }); it('should unselect before selecting a new target', async () => { @@ -241,10 +241,10 @@ describe('MediaActionsController', () => { expect( (await getPlayer(children[0], 'video')?.getMediaPlayerController())?.playback ?.pause, - ).toBeCalled(); + ).toHaveBeenCalled(); expect( (await getPlayer(children[0], 'video')?.getMediaPlayerController())?.mute, - ).toBeCalled(); + ).toHaveBeenCalled(); }); it('should select after target was previously visible', async () => { @@ -263,20 +263,20 @@ describe('MediaActionsController', () => { expect( (await getPlayer(children[0], 'video')?.getMediaPlayerController())?.playback ?.play, - ).not.toBeCalled(); + ).not.toHaveBeenCalled(); expect( (await getPlayer(children[0], 'video')?.getMediaPlayerController())?.unmute, - ).not.toBeCalled(); + ).not.toHaveBeenCalled(); await controller.setTarget(0, true); expect( (await getPlayer(children[0], 'video')?.getMediaPlayerController())?.playback ?.play, - ).toBeCalled(); + ).toHaveBeenCalled(); expect( (await getPlayer(children[0], 'video')?.getMediaPlayerController())?.unmute, - ).toBeCalled(); + ).toHaveBeenCalled(); }); }); @@ -296,10 +296,10 @@ describe('MediaActionsController', () => { expect( (await getPlayer(children[0], 'video')?.getMediaPlayerController())?.playback ?.play, - ).toBeCalledTimes(1); + ).toHaveBeenCalledTimes(1); expect( (await getPlayer(children[0], 'video')?.getMediaPlayerController())?.unmute, - ).toBeCalledTimes(1); + ).toHaveBeenCalledTimes(1); controller.unsetTarget(); @@ -312,10 +312,10 @@ describe('MediaActionsController', () => { expect( (await getPlayer(children[0], 'video')?.getMediaPlayerController())?.playback ?.play, - ).toBeCalledTimes(1); + ).toHaveBeenCalledTimes(1); expect( (await getPlayer(children[0], 'video')?.getMediaPlayerController())?.unmute, - ).toBeCalledTimes(1); + ).toHaveBeenCalledTimes(1); }); describe('should respond to media loaded', () => { @@ -334,7 +334,7 @@ describe('MediaActionsController', () => { expect( (await getPlayer(children[0], 'video')?.getMediaPlayerController())?.playback ?.play, - ).toBeCalledTimes(1); + ).toHaveBeenCalledTimes(1); getPlayer(children[0], 'video')?.dispatchEvent( new Event('advanced-camera-card:media:loaded'), @@ -345,7 +345,7 @@ describe('MediaActionsController', () => { expect( (await getPlayer(children[0], 'video')?.getMediaPlayerController())?.playback ?.play, - ).toBeCalledTimes(2); + ).toHaveBeenCalledTimes(2); }); it('should unmute after media load', async () => { @@ -361,7 +361,7 @@ describe('MediaActionsController', () => { await controller.setTarget(0, true); expect( (await getPlayer(children[0], 'video')?.getMediaPlayerController())?.unmute, - ).toBeCalledTimes(1); + ).toHaveBeenCalledTimes(1); getPlayer(children[0], 'video')?.dispatchEvent( new Event('advanced-camera-card:media:loaded'), @@ -371,7 +371,7 @@ describe('MediaActionsController', () => { expect( (await getPlayer(children[0], 'video')?.getMediaPlayerController())?.unmute, - ).toBeCalledTimes(2); + ).toHaveBeenCalledTimes(2); }); it('should take no action on unrelated media load', async () => { @@ -396,10 +396,10 @@ describe('MediaActionsController', () => { expect( (await getPlayer(children[9], 'video')?.getMediaPlayerController())?.playback ?.play, - ).not.toBeCalled(); + ).not.toHaveBeenCalled(); expect( (await getPlayer(children[9], 'video')?.getMediaPlayerController())?.unmute, - ).not.toBeCalled(); + ).not.toHaveBeenCalled(); }); it('should play and unmute on unselected but targeted media load', async () => { @@ -418,10 +418,10 @@ describe('MediaActionsController', () => { expect( (await getPlayer(children[0], 'video')?.getMediaPlayerController())?.playback ?.play, - ).toBeCalledTimes(1); + ).toHaveBeenCalledTimes(1); expect( (await getPlayer(children[0], 'video')?.getMediaPlayerController())?.unmute, - ).toBeCalledTimes(1); + ).toHaveBeenCalledTimes(1); getPlayer(children[0], 'video')?.dispatchEvent( new Event('advanced-camera-card:media:loaded'), @@ -432,10 +432,10 @@ describe('MediaActionsController', () => { expect( (await getPlayer(children[0], 'video')?.getMediaPlayerController())?.playback ?.play, - ).toBeCalledTimes(2); + ).toHaveBeenCalledTimes(2); expect( (await getPlayer(children[0], 'video')?.getMediaPlayerController())?.unmute, - ).toBeCalledTimes(2); + ).toHaveBeenCalledTimes(2); }); }); @@ -470,7 +470,7 @@ describe('MediaActionsController', () => { await getPlayer(children[0], 'video')?.getMediaPlayerController(), func, ), - ).toBeCalledTimes(called ? 1 : 0); + ).toHaveBeenCalledTimes(called ? 1 : 0); }, ); }); @@ -516,7 +516,7 @@ describe('MediaActionsController', () => { await getPlayer(children[0], 'video')?.getMediaPlayerController(), func, ), - ).toBeCalledTimes(called ? 1 : 0); + ).toHaveBeenCalledTimes(called ? 1 : 0); }, ); }); @@ -561,7 +561,7 @@ describe('MediaActionsController', () => { await getPlayer(children[0], 'video')?.getMediaPlayerController(), func, ), - ).toBeCalledTimes(called ? 1 : 0); + ).toHaveBeenCalledTimes(called ? 1 : 0); }, ); }); @@ -596,7 +596,7 @@ describe('MediaActionsController', () => { await getPlayer(children[0], 'video')?.getMediaPlayerController(), func, ), - ).not.toBeCalled(); + ).not.toHaveBeenCalled(); // There's always a first call to an intersection observer handler. In // this case the MediaActionsController ignores it. @@ -610,7 +610,7 @@ describe('MediaActionsController', () => { await getPlayer(children[0], 'video')?.getMediaPlayerController(), func, ), - ).toBeCalledTimes(called ? 1 : 0); + ).toHaveBeenCalledTimes(called ? 1 : 0); }, ); }); @@ -645,7 +645,7 @@ describe('MediaActionsController', () => { await getPlayer(children[0], 'video')?.getMediaPlayerController(), func, ), - ).not.toBeCalled(); + ).not.toHaveBeenCalled(); // There's always a first call to an intersection observer handler. In // this case the MediaActionsController ignores it. @@ -659,7 +659,7 @@ describe('MediaActionsController', () => { await getPlayer(children[0], 'video')?.getMediaPlayerController(), func, ), - ).toBeCalledTimes(called ? 1 : 0); + ).toHaveBeenCalledTimes(called ? 1 : 0); }, ); }); @@ -702,7 +702,7 @@ describe('MediaActionsController', () => { expect( (await getPlayer(children[0], 'video')?.getMediaPlayerController())?.unmute, - ).toBeCalled(); + ).toHaveBeenCalled(); }); it('should mute after delay after microphone muted', async () => { @@ -725,7 +725,7 @@ describe('MediaActionsController', () => { expect( (await getPlayer(children[0], 'video')?.getMediaPlayerController())?.mute, - ).toBeCalled(); + ).toHaveBeenCalled(); }); it('should not mute after delay after microphone muted', async () => { @@ -748,7 +748,7 @@ describe('MediaActionsController', () => { expect( (await getPlayer(children[0], 'video')?.getMediaPlayerController())?.mute, - ).not.toBeCalled(); + ).not.toHaveBeenCalled(); }); it('should not act on the initial microphone state', async () => { @@ -767,7 +767,7 @@ describe('MediaActionsController', () => { expect( (await getPlayer(children[0], 'video')?.getMediaPlayerController())?.unmute, - ).not.toBeCalled(); + ).not.toHaveBeenCalled(); }); }); @@ -790,7 +790,7 @@ describe('MediaActionsController', () => { expect( (await getPlayer(children[0], 'video')?.getMediaPlayerController())?.unmute, - ).toBeCalled(); + ).toHaveBeenCalled(); }); it('should mute the target on call end', async () => { @@ -811,7 +811,7 @@ describe('MediaActionsController', () => { expect( (await getPlayer(children[0], 'video')?.getMediaPlayerController())?.mute, - ).toBeCalled(); + ).toHaveBeenCalled(); }); it('should not act on the initial call state', async () => { @@ -831,7 +831,7 @@ describe('MediaActionsController', () => { expect( (await getPlayer(children[0], 'video')?.getMediaPlayerController())?.mute, - ).not.toBeCalled(); + ).not.toHaveBeenCalled(); }); it('should not act when call is not a configured condition', async () => { @@ -851,7 +851,7 @@ describe('MediaActionsController', () => { expect( (await getPlayer(children[0], 'video')?.getMediaPlayerController())?.unmute, - ).not.toBeCalled(); + ).not.toHaveBeenCalled(); }); it('should apply the call-answer unmute when the target arrives after the call', async () => { @@ -871,13 +871,13 @@ describe('MediaActionsController', () => { await flushPromises(); expect( (await getPlayer(children[0], 'video')?.getMediaPlayerController())?.unmute, - ).not.toBeCalled(); + ).not.toHaveBeenCalled(); await controller.setTarget(0, true); expect( (await getPlayer(children[0], 'video')?.getMediaPlayerController())?.unmute, - ).toBeCalled(); + ).toHaveBeenCalled(); }); it('should unmute when the call is already answered on the first call-state signal', async () => { @@ -900,7 +900,7 @@ describe('MediaActionsController', () => { expect( (await getPlayer(children[0], 'video')?.getMediaPlayerController())?.unmute, - ).toBeCalled(); + ).toHaveBeenCalled(); }); it('should defer the call-answer unmute until the media player is ready', async () => { @@ -928,7 +928,7 @@ describe('MediaActionsController', () => { // The call is answered while the player is still not ready: no unmute yet. controller.setCallAnswered(true); await flushPromises(); - expect(mediaPlayerController.unmute).not.toBeCalled(); + expect(mediaPlayerController.unmute).not.toHaveBeenCalled(); // Once the media loads the deferred unmute is applied -- exactly once, // so a later reload cannot clobber a manual mute made during the call. @@ -936,7 +936,7 @@ describe('MediaActionsController', () => { await flushPromises(); player.dispatchEvent(new Event('advanced-camera-card:media:loaded')); await flushPromises(); - expect(mediaPlayerController.unmute).toBeCalledTimes(1); + expect(mediaPlayerController.unmute).toHaveBeenCalledTimes(1); }); }); }); diff --git a/tests/components-lib/media-dimensions-container-controller.test.ts b/tests/components-lib/media-dimensions-container-controller.test.ts index 8b0e1958..a565ed5f 100644 --- a/tests/components-lib/media-dimensions-container-controller.test.ts +++ b/tests/components-lib/media-dimensions-container-controller.test.ts @@ -1,6 +1,18 @@ -import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'; +import { + afterAll, + afterEach, + beforeAll, + beforeEach, + describe, + expect, + it, + vi, +} from 'vitest'; -import { MediaDimensionsContainerController } from '../../src/components-lib/media-dimensions-container-controller'; +import { + MediaDimensionsContainerController, + RESIZE_DEBOUNCE_SECONDS, +} from '../../src/components-lib/media-dimensions-container-controller'; import type { CameraDimensionsConfig, Rotation } from '../../src/config/schema/cameras'; import type { MediaLoadedInfo } from '../../src/types'; import { @@ -11,10 +23,6 @@ import { ResizeObserverMock, } from '../test-utils'; -vi.mock('lodash-es', () => ({ - debounce: vi.fn((fn) => fn), -})); - // @vitest-environment jsdom describe('MediaDimensionsContainerController', () => { beforeAll(() => { @@ -44,8 +52,8 @@ describe('MediaDimensionsContainerController', () => { const observer = getResizeObserver(); // No resize observer should be created. - expect(observer?.observe).not.toBeCalled(); - expect(eventListener).not.toBeCalled(); + expect(observer?.observe).not.toHaveBeenCalled(); + expect(eventListener).not.toHaveBeenCalled(); }); describe('should connect and disconnect', () => { @@ -54,14 +62,14 @@ describe('MediaDimensionsContainerController', () => { const controller = new MediaDimensionsContainerController(host); const observer = getResizeObserver(); - expect(observer?.observe).toBeCalledTimes(0); + expect(observer?.observe).toHaveBeenCalledTimes(0); controller.hostConnected(); - expect(observer?.observe).toBeCalledWith(host); - expect(observer?.observe).toBeCalledTimes(1); + expect(observer?.observe).toHaveBeenCalledWith(host); + expect(observer?.observe).toHaveBeenCalledTimes(1); controller.hostDisconnected(); - expect(observer?.disconnect).toBeCalled(); + expect(observer?.disconnect).toHaveBeenCalled(); }); it('should connect and disconnect with a container when host is connected', () => { @@ -77,13 +85,13 @@ describe('MediaDimensionsContainerController', () => { const container = createLitElement(); controller.setContainers(container); - expect(observer?.observe).not.toBeCalled(); + expect(observer?.observe).not.toHaveBeenCalled(); controller.hostDisconnected(); - expect(observer?.disconnect).toBeCalled(); + expect(observer?.disconnect).toHaveBeenCalled(); controller.hostConnected(); - expect(observer?.observe).toBeCalledWith(host); + expect(observer?.observe).toHaveBeenCalledWith(host); }); }); @@ -614,6 +622,13 @@ describe('MediaDimensionsContainerController', () => { }); describe('should respond to slot changes', () => { + beforeEach(() => { + vi.useFakeTimers(); + }); + afterEach(() => { + vi.useRealTimers(); + }); + it('should resize container on slotchange event', () => { const host = createLitElement(); host.getBoundingClientRect = vi.fn().mockReturnValue({ @@ -641,12 +656,20 @@ describe('MediaDimensionsContainerController', () => { host.removeAttribute('rotated'); innerContainer.dispatchEvent(new Event('slotchange')); + vi.advanceTimersByTime(RESIZE_DEBOUNCE_SECONDS * 1000); expect(host.hasAttribute('rotated')).toBeTruthy(); }); }); describe('should respond to media load', () => { + beforeEach(() => { + vi.useFakeTimers(); + }); + afterEach(() => { + vi.useRealTimers(); + }); + it('should resize container on media load', () => { const host = createLitElement(); host.getBoundingClientRect = vi.fn().mockReturnValue({ @@ -680,6 +703,7 @@ describe('MediaDimensionsContainerController', () => { innerContainer.dispatchEvent( createMediaLoadedInfoEvent({ info: mediaLoadedInfo }), ); + vi.advanceTimersByTime(RESIZE_DEBOUNCE_SECONDS * 1000); expect(host.hasAttribute('rotated')).toBeTruthy(); }); diff --git a/tests/components-lib/media-filter-controller.test.ts b/tests/components-lib/media-filter-controller.test.ts index 93150f5a..9a70e359 100644 --- a/tests/components-lib/media-filter-controller.test.ts +++ b/tests/components-lib/media-filter-controller.test.ts @@ -32,14 +32,10 @@ import { import { QuerySource } from '../../src/query-source'; import type { Severity } from '../../src/severity'; import { UnifiedQuery } from '../../src/view/unified-query'; -import { - createCameraConfig, - createCameraManager, - createLitElement, - createPerformanceConfig, - createStore, - createView, -} from '../test-utils'; +import { createCameraManager, createStore } from '../camera-manager/test-utils'; +import { createCameraConfig, createPerformanceConfig } from '../config/test-utils'; +import { createLitElement } from '../test-utils'; +import { createView } from '../view/test-utils'; const createCameraStore = (options?: { capabilities: Capabilities }) => { return createStore([ @@ -467,7 +463,7 @@ describe('MediaFilterController', () => { const host = createLitElement(); const controller = new MediaFilterController(host); await controller.computeMetadataOptions(cameraManager); - expect(host.requestUpdate).not.toBeCalled(); + expect(host.requestUpdate).not.toHaveBeenCalled(); }); it('with metadata for what', async () => { @@ -483,7 +479,7 @@ describe('MediaFilterController', () => { { value: 'car', label: 'Car' }, { value: 'person', label: 'Person' }, ]); - expect(host.requestUpdate).toBeCalled(); + expect(host.requestUpdate).toHaveBeenCalled(); }); it('with metadata for where', async () => { @@ -499,7 +495,7 @@ describe('MediaFilterController', () => { { value: 'back_yard', label: 'Back Yard' }, { value: 'front_door', label: 'Front Door' }, ]); - expect(host.requestUpdate).toBeCalled(); + expect(host.requestUpdate).toHaveBeenCalled(); }); it('with metadata for tags', async () => { @@ -515,7 +511,7 @@ describe('MediaFilterController', () => { { value: 'tag-1', label: 'Tag-1' }, { value: 'tag-2', label: 'Tag-2' }, ]); - expect(host.requestUpdate).toBeCalled(); + expect(host.requestUpdate).toHaveBeenCalled(); }); it('with metadata for days', async () => { @@ -536,7 +532,7 @@ describe('MediaFilterController', () => { }), ]), ); - expect(host.requestUpdate).toBeCalled(); + expect(host.requestUpdate).toHaveBeenCalled(); }); }); }); @@ -555,7 +551,7 @@ describe('MediaFilterController', () => { { when: {} }, ); - expect(viewManager.setViewByParametersWithExistingQuery).not.toBeCalled(); + expect(viewManager.setViewByParametersWithExistingQuery).not.toHaveBeenCalled(); }); describe('with events media type', () => { @@ -593,7 +589,7 @@ describe('MediaFilterController', () => { }, ); - expect(viewManager.setViewByParametersWithExistingQuery).toBeCalledWith({ + expect(viewManager.setViewByParametersWithExistingQuery).toHaveBeenCalledWith({ params: expect.objectContaining({ camera: 'camera.kitchen', }), @@ -615,7 +611,7 @@ describe('MediaFilterController', () => { limit: 11, }); - expect(host.requestUpdate).toBeCalled(); + expect(host.requestUpdate).toHaveBeenCalled(); }, ); }); @@ -647,7 +643,7 @@ describe('MediaFilterController', () => { }, ); - expect(viewManager.setViewByParametersWithExistingQuery).toBeCalledWith({ + expect(viewManager.setViewByParametersWithExistingQuery).toHaveBeenCalledWith({ params: expect.objectContaining({ camera: 'camera.kitchen', }), @@ -664,7 +660,7 @@ describe('MediaFilterController', () => { limit: 11, }); - expect(host.requestUpdate).toBeCalled(); + expect(host.requestUpdate).toHaveBeenCalled(); }); it('with reviews media type', async () => { @@ -687,7 +683,7 @@ describe('MediaFilterController', () => { }, ); - expect(viewManager.setViewByParametersWithExistingQuery).toBeCalled(); + expect(viewManager.setViewByParametersWithExistingQuery).toHaveBeenCalled(); const nodes = getQueryNodes(viewManager); expect(nodes).toHaveLength(1); @@ -717,7 +713,7 @@ describe('MediaFilterController', () => { }, ); - expect(viewManager.setViewByParametersWithExistingQuery).toBeCalled(); + expect(viewManager.setViewByParametersWithExistingQuery).toHaveBeenCalled(); const nodes = getQueryNodes(viewManager); expect(nodes).toHaveLength(1); @@ -746,7 +742,7 @@ describe('MediaFilterController', () => { }, ); - expect(viewManager.setViewByParametersWithExistingQuery).toBeCalled(); + expect(viewManager.setViewByParametersWithExistingQuery).toHaveBeenCalled(); const nodes = getQueryNodes(viewManager); expect(nodes).toHaveLength(2); @@ -772,7 +768,7 @@ describe('MediaFilterController', () => { }, ); - expect(viewManager.setViewByParametersWithExistingQuery).toBeCalledWith({ + expect(viewManager.setViewByParametersWithExistingQuery).toHaveBeenCalledWith({ params: expect.objectContaining({ camera: 'camera.kitchen', }), @@ -805,7 +801,7 @@ describe('MediaFilterController', () => { }, ); - expect(viewManager.setViewByParametersWithExistingQuery).toBeCalledWith({ + expect(viewManager.setViewByParametersWithExistingQuery).toHaveBeenCalledWith({ params: expect.objectContaining({ camera: 'camera.kitchen', }), @@ -837,7 +833,7 @@ describe('MediaFilterController', () => { }, ); - expect(viewManager.setViewByParametersWithExistingQuery).toBeCalled(); + expect(viewManager.setViewByParametersWithExistingQuery).toHaveBeenCalled(); const nodes = getQueryNodes(viewManager); // All 4 types selected for the single camera @@ -869,7 +865,7 @@ describe('MediaFilterController', () => { }, ); - expect(viewManager.setViewByParametersWithExistingQuery).toBeCalledWith({ + expect(viewManager.setViewByParametersWithExistingQuery).toHaveBeenCalledWith({ params: { query: expect.any(UnifiedQuery), }, diff --git a/tests/components-lib/media-grid-controller.test.ts b/tests/components-lib/media-grid-controller.test.ts index 00dd8680..5ead44ca 100644 --- a/tests/components-lib/media-grid-controller.test.ts +++ b/tests/components-lib/media-grid-controller.test.ts @@ -100,7 +100,7 @@ describe('MediaGridController', () => { it('should be constructable', () => { const controller = createController(createParent()); expect(controller).toBeTruthy(); - expect(masonry.layout).toBeCalled(); + expect(masonry.layout).toHaveBeenCalled(); }); it('should set grid contents correctly from regular elements', () => { @@ -115,7 +115,7 @@ describe('MediaGridController', () => { ]), ); expect(controller.getGridSize()).toBe(3); - expect(masonry.layout).toBeCalled(); + expect(masonry.layout).toHaveBeenCalled(); }); it('should set grid contents correctly from slotted elements', () => { @@ -205,12 +205,12 @@ describe('MediaGridController', () => { } // The grid signals its own state change via media-grid:unselected. - expect(unselectedHandler).toBeCalledTimes(1); + expect(unselectedHandler).toHaveBeenCalledTimes(1); // Unselecting a second time should do nothing. controller.unselectAll(); - expect(unselectedHandler).toBeCalledTimes(1); + expect(unselectedHandler).toHaveBeenCalledTimes(1); }); it('should select in constructor', () => { @@ -337,7 +337,7 @@ describe('MediaGridController', () => { const children = createChildren(); const parent = createParent({ children: children }); createController(parent); - expect(Masonry).toBeCalledWith( + expect(Masonry).toHaveBeenCalledWith( parent, expect.objectContaining({ initLayout: false, @@ -350,7 +350,7 @@ describe('MediaGridController', () => { it('should set default column size correctly', () => { const parent = createParent({ children: createChildren() }); createController(parent); - expect(Masonry).toBeCalledWith( + expect(Masonry).toHaveBeenCalledWith( parent, expect.objectContaining({ columnWidth: 245, @@ -368,8 +368,8 @@ describe('MediaGridController', () => { // The cells are unchanged, so the new column width is applied to the // existing Masonry instance rather than by constructing a new one. - expect(Masonry).toBeCalledTimes(1); - expect(masonry.option).toBeCalledWith( + expect(Masonry).toHaveBeenCalledTimes(1); + expect(masonry.option).toHaveBeenCalledWith( expect.objectContaining({ columnWidth: 1499, }), @@ -384,13 +384,13 @@ describe('MediaGridController', () => { createSlotHost({ slot: slot, children: createChildren() }); createController(slot); - expect(Masonry).toBeCalledTimes(1); - expect(masonry.destroy).not.toBeCalled(); + expect(Masonry).toHaveBeenCalledTimes(1); + expect(masonry.destroy).not.toHaveBeenCalled(); slot.dispatchEvent(new Event('slotchange')); - expect(Masonry).toBeCalledTimes(1); - expect(masonry.destroy).not.toBeCalled(); + expect(Masonry).toHaveBeenCalledTimes(1); + expect(masonry.destroy).not.toHaveBeenCalled(); }); it('should rebuild the grid and lay it out when the cells change', () => { @@ -398,17 +398,17 @@ describe('MediaGridController', () => { const host = createSlotHost({ slot: slot, children: createChildren() }); createController(slot); - expect(Masonry).toBeCalledTimes(1); + expect(Masonry).toHaveBeenCalledTimes(1); host.replaceChildren(...createChildren()); slot.dispatchEvent(new Event('slotchange')); - expect(Masonry).toBeCalledTimes(2); - expect(masonry.destroy).toBeCalledTimes(1); + expect(Masonry).toHaveBeenCalledTimes(2); + expect(masonry.destroy).toHaveBeenCalledTimes(1); // A rebuild leaves the cells unpositioned, so the layout must not be left // to the throttle. - expect(masonry.layout).toBeCalled(); + expect(masonry.layout).toHaveBeenCalled(); }); it('should rebuild the grid when the number of cells changes', () => { @@ -416,13 +416,13 @@ describe('MediaGridController', () => { const host = createSlotHost({ slot: slot, children: createChildren() }); createController(slot); - expect(Masonry).toBeCalledTimes(1); + expect(Masonry).toHaveBeenCalledTimes(1); host.append(...createChildren(['new-cell'])); slot.dispatchEvent(new Event('slotchange')); - expect(Masonry).toBeCalledTimes(2); - expect(masonry.destroy).toBeCalledTimes(1); + expect(Masonry).toHaveBeenCalledTimes(2); + expect(masonry.destroy).toHaveBeenCalledTimes(1); }); it('should not use more columns than the items ask for', () => { @@ -431,7 +431,7 @@ describe('MediaGridController', () => { // The lone item takes the whole grid. Sizing from the width alone would // give it 1 of 5 columns, with the other 4 left empty. - expect(Masonry).toBeCalledWith( + expect(Masonry).toHaveBeenCalledWith( parent, expect.objectContaining({ columnWidth: 3000, @@ -448,7 +448,7 @@ describe('MediaGridController', () => { // Sizing from the width alone would give the lone item half of a default // width card. - expect(Masonry).toBeCalledWith( + expect(Masonry).toHaveBeenCalledWith( parent, expect.objectContaining({ columnWidth: 492, @@ -463,7 +463,7 @@ describe('MediaGridController', () => { const parent = createParent({ width: 3000 }); createController(parent); - expect(Masonry).toBeCalledWith( + expect(Masonry).toHaveBeenCalledWith( parent, expect.objectContaining({ columnWidth: 3000, @@ -479,7 +479,7 @@ describe('MediaGridController', () => { const controller = createController(parent); controller.setDisplayConfig({ mode: 'grid', grid_columns: 4 }); - expect(masonry.option).toBeCalledWith( + expect(masonry.option).toHaveBeenCalledWith( expect.objectContaining({ columnWidth: 749, }), @@ -498,7 +498,7 @@ describe('MediaGridController', () => { // The items ask for 4 columns: 2 for the selection (the default // `grid_selected_width_factor`) and 1 for each of its siblings. - expect(Masonry).toBeCalledWith( + expect(Masonry).toHaveBeenCalledWith( parent, expect.objectContaining({ columnWidth: 749, @@ -515,7 +515,7 @@ describe('MediaGridController', () => { // A selection is normally reserved extra columns, but a lone item cannot be // wider than the grid and so cannot use them. - expect(Masonry).toBeCalledWith( + expect(Masonry).toHaveBeenCalledWith( parent, expect.objectContaining({ columnWidth: 3000, @@ -533,7 +533,7 @@ describe('MediaGridController', () => { // 3 columns for the selection and 1 for each sibling exhausts the 5 // columns the width allows. - expect(masonry.option).toBeCalledWith( + expect(masonry.option).toHaveBeenCalledWith( expect.objectContaining({ columnWidth: 599, }), @@ -551,7 +551,7 @@ describe('MediaGridController', () => { // The items span 4 columns, and the widest needs 2 more when selected. // Ignoring the width factor would give 4 columns of 1049px. - expect(Masonry).toBeCalledWith( + expect(Masonry).toHaveBeenCalledWith( parent, expect.objectContaining({ columnWidth: 699, @@ -569,7 +569,7 @@ describe('MediaGridController', () => { // Each item asks for one column: the selection fills exactly one at 0.5 x // 2, and a half-width sibling still occupies a whole one. - expect(Masonry).toBeCalledWith( + expect(Masonry).toHaveBeenCalledWith( parent, expect.objectContaining({ columnWidth: 599, @@ -584,7 +584,7 @@ describe('MediaGridController', () => { // Room for a selection is reserved whether or not there is one, so the // three items ask for 4 columns either way. - expect(Masonry).toBeCalledWith( + expect(Masonry).toHaveBeenCalledWith( parent, expect.objectContaining({ columnWidth: 749, @@ -596,14 +596,14 @@ describe('MediaGridController', () => { // Selecting an item would otherwise resize the items the user did not // interact with. - expect(masonry.option).not.toBeCalled(); + expect(masonry.option).not.toHaveBeenCalled(); expect( parent.style.getPropertyValue('--advanced-camera-card-grid-column-size'), ).toBe('749px'); controller.unselectAll(); - expect(masonry.option).not.toBeCalled(); + expect(masonry.option).not.toHaveBeenCalled(); expect( parent.style.getPropertyValue('--advanced-camera-card-grid-column-size'), ).toBe('749px'); @@ -642,8 +642,8 @@ describe('MediaGridController', () => { // Click is consumed; the controller dispatches the selection request but // does NOT mutate local state. The authoritative selection is applied by // the parent via `selectCell` once it propagates back. - expect(clickHandler).not.toBeCalled(); - expect(selectedHandler).toBeCalledTimes(1); + expect(clickHandler).not.toHaveBeenCalled(); + expect(selectedHandler).toHaveBeenCalledTimes(1); expect(selectedHandler.mock.calls[0][0].detail).toEqual({ selected: '1' }); expect(controller.getSelected()).toBeNull(); }); @@ -659,7 +659,7 @@ describe('MediaGridController', () => { children[1].click(); // Click will be allowed through. - expect(clickHandler).toBeCalled(); + expect(clickHandler).toHaveBeenCalled(); expect(controller.getSelected()).toBe('1'); }); @@ -668,14 +668,14 @@ describe('MediaGridController', () => { vi.mocked(masonry.layout)?.mockClear(); triggerResizeObserver('cell'); - expect(masonry.layout).toBeCalled(); + expect(masonry.layout).toHaveBeenCalled(); }); it('should update masonry column width when host size changes', () => { const children = createChildren(); const parent = createParent({ children: children }); createController(parent); - expect(Masonry).toBeCalledWith( + expect(Masonry).toHaveBeenCalledWith( parent, expect.objectContaining({ columnWidth: 245, @@ -696,12 +696,12 @@ describe('MediaGridController', () => { // Masonry should not be recreated, but column width should be updated // via option() and layout should be called. - expect(Masonry).not.toBeCalled(); - expect(masonry.option).toBeCalledWith({ columnWidth: 749 }); + expect(Masonry).not.toHaveBeenCalled(); + expect(masonry.option).toHaveBeenCalledWith({ columnWidth: 749 }); expect( parent.style.getPropertyValue('--advanced-camera-card-grid-column-size'), ).toBe('749px'); - expect(masonry.layout).toBeCalled(); + expect(masonry.layout).toHaveBeenCalled(); // Clear mock state. vi.mocked(Masonry).mockClear(); @@ -710,9 +710,9 @@ describe('MediaGridController', () => { // Trigger with the same sizes. triggerResizeObserver('host'); - expect(Masonry).not.toBeCalled(); - expect(masonry.option).not.toBeCalled(); - expect(masonry.layout).not.toBeCalled(); + expect(Masonry).not.toHaveBeenCalled(); + expect(masonry.option).not.toHaveBeenCalled(); + expect(masonry.layout).not.toHaveBeenCalled(); }); describe('describe should sort grid elements correctly', () => { diff --git a/tests/components-lib/media-height-controller.test.ts b/tests/components-lib/media-height-controller.test.ts index b8d1d335..5666618a 100644 --- a/tests/components-lib/media-height-controller.test.ts +++ b/tests/components-lib/media-height-controller.test.ts @@ -1,6 +1,18 @@ -import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'; +import { + afterAll, + afterEach, + beforeAll, + beforeEach, + describe, + expect, + it, + vi, +} from 'vitest'; -import { MediaHeightController } from '../../src/components-lib/media-height-controller'; +import { + MediaHeightController, + SET_HEIGHT_DEBOUNCE_SECONDS, +} from '../../src/components-lib/media-height-controller'; import { callMutationHandler, callResizeHandler, @@ -8,11 +20,6 @@ import { ResizeObserverMock, } from '../test-utils'; -vi.mock('lodash-es', async () => ({ - ...(await vi.importActual('lodash-es')), - debounce: vi.fn((fn) => fn), -})); - // @vitest-environment jsdom describe('MediaHeightController', () => { beforeAll(() => { @@ -26,6 +33,11 @@ describe('MediaHeightController', () => { beforeEach(() => { vi.clearAllMocks(); + vi.useFakeTimers(); + }); + + afterEach(() => { + vi.useRealTimers(); }); describe('should set height', () => { @@ -47,6 +59,8 @@ describe('MediaHeightController', () => { controller.setSelected(0); + vi.advanceTimersByTime(SET_HEIGHT_DEBOUNCE_SECONDS * 1000); + expect(host.style.maxHeight).toBe(`600px`); }); @@ -60,6 +74,8 @@ describe('MediaHeightController', () => { controller.setSelected(10); + vi.advanceTimersByTime(SET_HEIGHT_DEBOUNCE_SECONDS * 1000); + expect(host.style.maxHeight).toBe(''); }); @@ -86,6 +102,8 @@ describe('MediaHeightController', () => { }, ]); + vi.advanceTimersByTime(SET_HEIGHT_DEBOUNCE_SECONDS * 1000); + expect(host.style.maxHeight).toBe('800px'); }); @@ -111,6 +129,8 @@ describe('MediaHeightController', () => { }, ]); + vi.advanceTimersByTime(SET_HEIGHT_DEBOUNCE_SECONDS * 1000); + expect(host.style.maxHeight).toBe(''); }); @@ -137,6 +157,8 @@ describe('MediaHeightController', () => { controller.setSelected(1); + vi.advanceTimersByTime(SET_HEIGHT_DEBOUNCE_SECONDS * 1000); + expect(host.style.maxHeight).toBe('200px'); }); @@ -154,6 +176,8 @@ describe('MediaHeightController', () => { controller.setRoot(root); controller.setSelected(0); + vi.advanceTimersByTime(SET_HEIGHT_DEBOUNCE_SECONDS * 1000); + expect(host.style.maxHeight).toBe('700px'); child.getBoundingClientRect = vi.fn().mockReturnValue({ @@ -162,9 +186,30 @@ describe('MediaHeightController', () => { controller.recalculate(); + vi.advanceTimersByTime(SET_HEIGHT_DEBOUNCE_SECONDS * 1000); + expect(host.style.maxHeight).toBe('900px'); }); + it('should not set height when the selected child has no height', () => { + const host = document.createElement('div'); + const controller = new MediaHeightController(host, 'div'); + + const root = document.createElement('div'); + const child = document.createElement('div'); + child.getBoundingClientRect = vi.fn().mockReturnValue({ + height: 0, + }); + root.appendChild(child); + + controller.setRoot(root); + controller.setSelected(0); + + vi.advanceTimersByTime(SET_HEIGHT_DEBOUNCE_SECONDS * 1000); + + expect(host.style.maxHeight).toBe(''); + }); + it('should allow height to shrink when selected child is shorter', () => { const host = document.createElement('div'); const controller = new MediaHeightController(host, 'div'); @@ -184,10 +229,14 @@ describe('MediaHeightController', () => { controller.setRoot(root); controller.setSelected(0); + vi.advanceTimersByTime(SET_HEIGHT_DEBOUNCE_SECONDS * 1000); + expect(host.style.maxHeight).toBe('750px'); controller.setSelected(1); + vi.advanceTimersByTime(SET_HEIGHT_DEBOUNCE_SECONDS * 1000); + expect(host.style.maxHeight).toBe('562px'); }); }); diff --git a/tests/components-lib/media-loaded-info-sink-controller.test.ts b/tests/components-lib/media-loaded-info-sink-controller.test.ts index 7e760a91..e707ffa2 100644 --- a/tests/components-lib/media-loaded-info-sink-controller.test.ts +++ b/tests/components-lib/media-loaded-info-sink-controller.test.ts @@ -19,7 +19,7 @@ describe('MediaLoadedInfoSinkController', () => { getTargetID: () => 'target-1', }); - expect(host.addController).toBeCalledWith(controller); + expect(host.addController).toHaveBeenCalledWith(controller); }); it('should default to an empty info', () => { @@ -89,8 +89,8 @@ describe('MediaLoadedInfoSinkController', () => { selected = 'target-B'; controller.hostUpdated(); - expect(callback).toBeCalledWith(infoB); - expect(host.requestUpdate).toBeCalled(); + expect(callback).toHaveBeenCalledWith(infoB); + expect(host.requestUpdate).toHaveBeenCalled(); }); it('should not fire callback for non-selected target loads', () => { @@ -110,8 +110,8 @@ describe('MediaLoadedInfoSinkController', () => { }), ); - expect(callback).not.toBeCalled(); - expect(host.requestUpdate).not.toBeCalled(); + expect(callback).not.toHaveBeenCalled(); + expect(host.requestUpdate).not.toHaveBeenCalled(); }); it('should fire callback for selected target loads', () => { @@ -127,8 +127,8 @@ describe('MediaLoadedInfoSinkController', () => { const info = createMediaLoadedInfo({ targetID: 'target-A' }); host.dispatchEvent(createMediaLoadedInfoEvent({ info })); - expect(callback).toBeCalledWith(info); - expect(host.requestUpdate).toBeCalled(); + expect(callback).toHaveBeenCalledWith(info); + expect(host.requestUpdate).toHaveBeenCalled(); }); it('should not re-fire callback when hostUpdated runs without a targetID change', () => { @@ -148,7 +148,7 @@ describe('MediaLoadedInfoSinkController', () => { controller.hostUpdated(); controller.hostUpdated(); - expect(callback).not.toBeCalled(); + expect(callback).not.toHaveBeenCalled(); }); it('should not fire callback when selection switches between empty targets', () => { @@ -167,7 +167,7 @@ describe('MediaLoadedInfoSinkController', () => { selected = 'target-B'; controller.hostUpdated(); - expect(callback).not.toBeCalled(); + expect(callback).not.toHaveBeenCalled(); }); it('should ignore events whose info has no targetID', () => { @@ -186,7 +186,7 @@ describe('MediaLoadedInfoSinkController', () => { }), ); - expect(callback).not.toBeCalled(); + expect(callback).not.toHaveBeenCalled(); expect(controller.get()).toBeNull(); }); }); @@ -231,8 +231,8 @@ describe('MediaLoadedInfoSinkController', () => { controller.hostDisconnected(); - expect(callback).not.toBeCalled(); - expect(host.requestUpdate).not.toBeCalled(); + expect(callback).not.toHaveBeenCalled(); + expect(host.requestUpdate).not.toHaveBeenCalled(); expect(controller.get()).toBeNull(); }); }); @@ -259,8 +259,8 @@ describe('MediaLoadedInfoSinkController', () => { ac.abort(); expect(controller.get()).toBeNull(); - expect(callback).toBeCalledWith(null); - expect(host.requestUpdate).toBeCalled(); + expect(callback).toHaveBeenCalledWith(null); + expect(host.requestUpdate).toHaveBeenCalled(); }); it('should not fire callback when an unselected target aborts', () => { @@ -290,7 +290,7 @@ describe('MediaLoadedInfoSinkController', () => { acB.abort(); - expect(callback).not.toBeCalled(); + expect(callback).not.toHaveBeenCalled(); }); it('should not clobber a newer entry when an older signal aborts', () => { diff --git a/tests/components-lib/media-loaded-info-source-controller.test.ts b/tests/components-lib/media-loaded-info-source-controller.test.ts index dcb5c435..469a8218 100644 --- a/tests/components-lib/media-loaded-info-source-controller.test.ts +++ b/tests/components-lib/media-loaded-info-source-controller.test.ts @@ -17,7 +17,7 @@ describe('MediaLoadedInfoSourceController', () => { getTargetID: () => 'target-1', }); - expect(host.addController).toBeCalledWith(controller); + expect(host.addController).toHaveBeenCalledWith(controller); }); describe('set', () => { @@ -32,7 +32,7 @@ describe('MediaLoadedInfoSourceController', () => { controller.set(createMediaLoadedInfo()); - expect(handler).not.toBeCalled(); + expect(handler).not.toHaveBeenCalled(); }); it('should dispatch a bubbling, composed event with info+targetID and signal', () => { @@ -46,7 +46,7 @@ describe('MediaLoadedInfoSourceController', () => { controller.set(createMediaLoadedInfo({ width: 320, height: 240 })); - expect(handler).toBeCalledTimes(1); + expect(handler).toHaveBeenCalledTimes(1); const ev = handler.mock.calls[0][0] as CustomEvent; expect(ev.bubbles).toBe(true); expect(ev.composed).toBe(true); @@ -85,7 +85,7 @@ describe('MediaLoadedInfoSourceController', () => { createMediaLoadedInfo({ mediaPlayerController: player, technology: ['hls'] }), ); - expect(handler).toBeCalledTimes(1); + expect(handler).toHaveBeenCalledTimes(1); }); it('should redispatch when mediaPlayerController reference differs', () => { @@ -103,7 +103,7 @@ describe('MediaLoadedInfoSourceController', () => { controller.set(createMediaLoadedInfo({ mediaPlayerController: player1 })); controller.set(createMediaLoadedInfo({ mediaPlayerController: player2 })); - expect(handler).toBeCalledTimes(2); + expect(handler).toHaveBeenCalledTimes(2); }); it('should redispatch when getTargetID changes between calls', () => { @@ -120,7 +120,7 @@ describe('MediaLoadedInfoSourceController', () => { targetID = 'target-2'; controller.set(createMediaLoadedInfo()); - expect(handler).toBeCalledTimes(2); + expect(handler).toHaveBeenCalledTimes(2); expect((handler.mock.calls[0][0] as CustomEvent).detail.info.targetID).toBe( 'target-1', ); @@ -172,7 +172,7 @@ describe('MediaLoadedInfoSourceController', () => { controller.hostDisconnected(); controller.hostConnected(); - expect(handler).toBeCalledTimes(2); + expect(handler).toHaveBeenCalledTimes(2); const secondSignal = (handler.mock.calls[1][0] as CustomEvent).detail.signal; // The original signal aborted on disconnect, the new one is fresh. @@ -196,7 +196,7 @@ describe('MediaLoadedInfoSourceController', () => { controller.hostConnected(); - expect(handler).not.toBeCalled(); + expect(handler).not.toHaveBeenCalled(); }); it('should not redispatch if a registration is already active', () => { @@ -212,7 +212,7 @@ describe('MediaLoadedInfoSourceController', () => { // Active registration, no disconnect -- connect should be a no-op. controller.hostConnected(); - expect(handler).toBeCalledTimes(1); + expect(handler).toHaveBeenCalledTimes(1); }); it('should not replay stale info after targetID flips during disconnect', () => { @@ -228,7 +228,7 @@ describe('MediaLoadedInfoSourceController', () => { host.addEventListener('advanced-camera-card:media:loaded', handler); controller.set(createMediaLoadedInfo()); - expect(handler).toBeCalledTimes(1); + expect(handler).toHaveBeenCalledTimes(1); controller.hostDisconnected(); @@ -237,11 +237,11 @@ describe('MediaLoadedInfoSourceController', () => { controller.hostConnected(); // No re-dispatch -- the stale cache was discarded. - expect(handler).toBeCalledTimes(1); + expect(handler).toHaveBeenCalledTimes(1); // A subsequent set() under the new target dispatches fresh. controller.set(createMediaLoadedInfo({ width: 320, height: 240 })); - expect(handler).toBeCalledTimes(2); + expect(handler).toHaveBeenCalledTimes(2); expect((handler.mock.calls[1][0] as CustomEvent).detail.info.targetID).toBe( 'target-2', ); @@ -265,7 +265,7 @@ describe('MediaLoadedInfoSourceController', () => { controller.hostDisconnected(); - expect(cleanup).toBeCalled(); + expect(cleanup).toHaveBeenCalled(); expect(signal.aborted).toBe(true); }); diff --git a/tests/components-lib/media-player/image.test.ts b/tests/components-lib/media-player/image.test.ts index 8edd291f..518f421b 100644 --- a/tests/components-lib/media-player/image.test.ts +++ b/tests/components-lib/media-player/image.test.ts @@ -90,7 +90,7 @@ describe('ImageMediaPlayerController', () => { ); expect(await controller.getScreenshotURL()).toBe(url); - expect(screenshotImage).not.toBeCalled(); + expect(screenshotImage).not.toHaveBeenCalled(); }); }); @@ -135,7 +135,7 @@ describe('ImageMediaPlayerController', () => { await controller.playback?.play(); - expect(updateControl.start).toBeCalled(); + expect(updateControl.start).toHaveBeenCalled(); }); it('should stop the update loop on pause', async () => { @@ -148,7 +148,7 @@ describe('ImageMediaPlayerController', () => { await controller.playback?.pause(); - expect(updateControl.stop).toBeCalled(); + expect(updateControl.stop).toHaveBeenCalled(); }); it('should report paused when the update loop is not running', () => { diff --git a/tests/components-lib/media-player/jsmpeg.test.ts b/tests/components-lib/media-player/jsmpeg.test.ts index 933909c8..dff590c8 100644 --- a/tests/components-lib/media-player/jsmpeg.test.ts +++ b/tests/components-lib/media-player/jsmpeg.test.ts @@ -23,7 +23,7 @@ describe('JSMPEGMediaPlayerController', () => { await controller.playback.play(); - expect(videoElement.play).toBeCalled(); + expect(videoElement.play).toHaveBeenCalled(); }); it('should pause', async () => { @@ -37,7 +37,7 @@ describe('JSMPEGMediaPlayerController', () => { await controller.playback.pause(); - expect(videoElement.stop).toBeCalled(); + expect(videoElement.stop).toHaveBeenCalled(); }); describe('should mute', async () => { diff --git a/tests/components-lib/media-player/video.test.ts b/tests/components-lib/media-player/video.test.ts index 0942df99..6c253e60 100644 --- a/tests/components-lib/media-player/video.test.ts +++ b/tests/components-lib/media-player/video.test.ts @@ -80,7 +80,7 @@ describe('VideoMediaPlayerController', () => { await controller.playback.play(); - expect(video.play).toBeCalled(); + expect(video.play).toHaveBeenCalled(); }); it('should mute if not allowed to play and unmuted', async () => { @@ -92,7 +92,7 @@ describe('VideoMediaPlayerController', () => { await controller.playback.play(); - expect(video.play).toBeCalledTimes(2); + expect(video.play).toHaveBeenCalledTimes(2); expect(video.muted).toBeTruthy(); }); @@ -105,7 +105,7 @@ describe('VideoMediaPlayerController', () => { await controller.playback.play(); - expect(video.play).toBeCalledTimes(1); + expect(video.play).toHaveBeenCalledTimes(1); expect(video.muted).toBeTruthy(); }); @@ -118,7 +118,7 @@ describe('VideoMediaPlayerController', () => { await controller.playback.play(); - expect(video.play).toBeCalledTimes(2); + expect(video.play).toHaveBeenCalledTimes(2); expect(video.muted).toBeTruthy(); }); @@ -137,7 +137,7 @@ describe('VideoMediaPlayerController', () => { await controller.playback.pause(); - expect(video.pause).toBeCalled(); + expect(video.pause).toHaveBeenCalled(); }); describe('should mute', async () => { @@ -211,7 +211,7 @@ describe('VideoMediaPlayerController', () => { await controller.seek(10); - expect(hideMediaControlsTemporarily).toBeCalled(); + expect(hideMediaControlsTemporarily).toHaveBeenCalled(); expect(video.currentTime).toBe(10); }); @@ -231,7 +231,7 @@ describe('VideoMediaPlayerController', () => { await controller.setControls(true); - expect(setControlsOnVideo).toBeCalledWith(video, true); + expect(setControlsOnVideo).toHaveBeenCalledWith(video, true); }); it('should set controls to default', async () => { @@ -244,7 +244,7 @@ describe('VideoMediaPlayerController', () => { await controller.setControls(); - expect(setControlsOnVideo).toBeCalledWith(video, true); + expect(setControlsOnVideo).toHaveBeenCalledWith(video, true); }); it('should ignore calls without a default or value', async () => { @@ -252,7 +252,7 @@ describe('VideoMediaPlayerController', () => { await controller.setControls(true); - expect(setControlsOnVideo).not.toBeCalled(); + expect(setControlsOnVideo).not.toHaveBeenCalled(); }); }); diff --git a/tests/components-lib/media/notification-controller.test.ts b/tests/components-lib/media/notification-controller.test.ts index 3c68da9c..675366a2 100644 --- a/tests/components-lib/media/notification-controller.test.ts +++ b/tests/components-lib/media/notification-controller.test.ts @@ -15,7 +15,8 @@ import type { NotificationControl } from '../../../src/config/schema/actions/typ import { formatDateAndTime } from '../../../src/utils/basic'; import { downloadMedia, navigateToTimeline } from '../../../src/utils/media-actions'; import { ViewFolder, ViewMediaType } from '../../../src/view/item'; -import { createCardAPI, createFolder, TestViewMedia } from '../../test-utils'; +import { createCardAPI, createFolder } from '../../test-utils'; +import { TestViewMedia } from '../../view/test-utils'; vi.mock('../../../src/utils/media-actions', async (importOriginal) => ({ ...((await importOriginal()) as object), diff --git a/tests/components-lib/menu-button-controller.test.ts b/tests/components-lib/menu-button-controller.test.ts index f82938c1..a3284a94 100644 --- a/tests/components-lib/menu-button-controller.test.ts +++ b/tests/components-lib/menu-button-controller.test.ts @@ -38,19 +38,19 @@ import { } from '../../src/view/view-support.js'; import type { View } from '../../src/view/view.js'; import { - createCameraConfig, createCameraManager, createCapabilities, - createConfig, + createStore, +} from '../camera-manager/test-utils'; +import { createCameraConfig, createConfig } from '../config/test-utils'; +import { createFolder, createHASS, createMediaCapabilities, createMediaLoadedInfo, createStateEntity, - createStore, - createView, - TestViewMedia, } from '../test-utils.js'; +import { createView, TestViewMedia } from '../view/test-utils'; vi.mock('../../src/view/view-support.js'); vi.mock('../../src/utils/media-player-controller.js'); diff --git a/tests/components-lib/menu-controller.test.ts b/tests/components-lib/menu-controller.test.ts index 08e59e3a..cc8011b4 100644 --- a/tests/components-lib/menu-controller.test.ts +++ b/tests/components-lib/menu-controller.test.ts @@ -88,7 +88,7 @@ describe('MenuController', () => { vi.mocked(host.requestUpdate).mockClear(); controller.setLockManagerEpoch(createLock(true, true)); - expect(host.requestUpdate).toBeCalledTimes(1); + expect(host.requestUpdate).toHaveBeenCalledTimes(1); }); it('should not trigger update when lock epoch is unchanged', () => { @@ -99,7 +99,7 @@ describe('MenuController', () => { vi.mocked(host.requestUpdate).mockClear(); controller.setLockManagerEpoch({ manager: lock.manager, locked: lock.locked }); - expect(host.requestUpdate).not.toBeCalled(); + expect(host.requestUpdate).not.toHaveBeenCalled(); }); it('should reflect lock state in shouldButtonBeInert', () => { @@ -406,7 +406,7 @@ describe('MenuController', () => { ]; controller.setButtons(buttons); - expect(host.requestUpdate).toBeCalledTimes(1); + expect(host.requestUpdate).toHaveBeenCalledTimes(1); controller.setButtons([ { @@ -414,7 +414,7 @@ describe('MenuController', () => { icon: 'mdi:cow', }, ]); - expect(host.requestUpdate).toBeCalledTimes(1); + expect(host.requestUpdate).toHaveBeenCalledTimes(1); }); }); @@ -535,7 +535,7 @@ describe('MenuController', () => { const controller = new MenuController(host); controller.handleAction(createInteractionActionEvent('tap')); - expect(handler).not.toBeCalled(); + expect(handler).not.toHaveBeenCalled(); }); it('should execute simple action in non-hidden menu', () => { @@ -546,7 +546,7 @@ describe('MenuController', () => { const controller = new MenuController(host); controller.handleAction(createInteractionActionEvent('tap'), tapActionConfig); - expect(handler).toBeCalledWith( + expect(handler).toHaveBeenCalledWith( expect.objectContaining({ detail: { actions: [action], config: tapActionConfig }, }), @@ -564,7 +564,7 @@ describe('MenuController', () => { controller.handleAction( createSubmenuInteractionActionEvent('tap', tapActionConfig as SubmenuItem), ); - expect(handler).toBeCalledWith( + expect(handler).toHaveBeenCalledWith( expect.objectContaining({ detail: { actions: [action], config: tapActionConfig }, }), @@ -580,7 +580,7 @@ describe('MenuController', () => { controller.handleAction(createInteractionActionEvent('tap'), tapActionConfigMulti); - expect(handler).toBeCalledWith( + expect(handler).toHaveBeenCalledWith( expect.objectContaining({ detail: { actions: [action, action, action], config: tapActionConfigMulti }, }), diff --git a/tests/components-lib/navigation.test.ts b/tests/components-lib/navigation.test.ts index a3c18de4..af7b4761 100644 --- a/tests/components-lib/navigation.test.ts +++ b/tests/components-lib/navigation.test.ts @@ -22,12 +22,8 @@ import { QuerySource } from '../../src/query-source'; import { ViewFolder, ViewMedia } from '../../src/view/item'; import { UnifiedQuery } from '../../src/view/unified-query'; import { UnifiedQueryBuilder } from '../../src/view/unified-query-builder'; -import { - createCardAPI, - createFolder, - createView, - createViewWithMedia, -} from '../test-utils'; +import { createCardAPI, createFolder } from '../test-utils'; +import { createView, createViewWithMedia } from '../view/test-utils'; const createFolderQuery = ( folder: ReturnType, @@ -83,7 +79,9 @@ describe('navigateUp', () => { navigateUp(options); - expect(api.getViewManager().setViewByParametersWithExistingQuery).not.toBeCalled(); + expect( + api.getViewManager().setViewByParametersWithExistingQuery, + ).not.toHaveBeenCalled(); }); it('should ignore folder query without parent to go up to', () => { @@ -109,7 +107,9 @@ describe('navigateUp', () => { navigateUp(options); - expect(api.getViewManager().setViewByParametersWithExistingQuery).not.toBeCalled(); + expect( + api.getViewManager().setViewByParametersWithExistingQuery, + ).not.toHaveBeenCalled(); }); it('should go up in the folder hierarchy', () => { @@ -139,7 +139,9 @@ describe('navigateUp', () => { navigateUp(options); - expect(api.getViewManager().setViewByParametersWithExistingQuery).toBeCalledWith({ + expect( + api.getViewManager().setViewByParametersWithExistingQuery, + ).toHaveBeenCalledWith({ params: { query: expect.any(UnifiedQuery), }, @@ -220,7 +222,9 @@ describe('navigateToFolder', () => { const item = new ViewFolder(folder, [{ ha: { id: 'root' } }]); navigateToFolder(item, options); - expect(api.getViewManager().setViewByParametersWithExistingQuery).toBeCalledWith({ + expect( + api.getViewManager().setViewByParametersWithExistingQuery, + ).toHaveBeenCalledWith({ params: { query: expect.any(UnifiedQuery), }, @@ -317,7 +321,7 @@ describe('navigateToMedia', () => { navigateToMedia(media, options); - expect(api.getViewManager().setViewByParameters).toBeCalledWith( + expect(api.getViewManager().setViewByParameters).toHaveBeenCalledWith( expect.objectContaining({ params: expect.objectContaining({ view: 'media', @@ -364,7 +368,7 @@ describe('navigateToMedia', () => { navigateToMedia(media, options); - expect(api.getViewManager().setViewByParameters).toBeCalledWith( + expect(api.getViewManager().setViewByParameters).toHaveBeenCalledWith( expect.objectContaining({ params: expect.objectContaining({ view: 'media', @@ -391,7 +395,7 @@ describe('navigateToMedia', () => { navigateToMedia(media, options); - expect(api.getViewManager().setViewByParameters).toBeCalledWith( + expect(api.getViewManager().setViewByParameters).toHaveBeenCalledWith( expect.objectContaining({ modifiers: [modifier], }), @@ -412,7 +416,7 @@ describe('navigateToMedia', () => { navigateToMedia(media, options); - expect(api.getViewManager().setViewByParameters).not.toBeCalled(); + expect(api.getViewManager().setViewByParameters).not.toHaveBeenCalled(); }); it('should do nothing if view is missing', () => { @@ -428,6 +432,6 @@ describe('navigateToMedia', () => { navigateToMedia(media, options); - expect(api.getViewManager().setViewByParameters).not.toBeCalled(); + expect(api.getViewManager().setViewByParameters).not.toHaveBeenCalled(); }); }); diff --git a/tests/components-lib/notification/action.test.ts b/tests/components-lib/notification/action.test.ts index 1049c1ff..afbe791e 100644 --- a/tests/components-lib/notification/action.test.ts +++ b/tests/components-lib/notification/action.test.ts @@ -29,7 +29,7 @@ describe('handleControlAction', () => { const host = document.createElement('div'); handleControlAction(ev, createControl(), host); - expect(stopEventFromActivatingCardWideActions).toBeCalledWith(ev); + expect(stopEventFromActivatingCardWideActions).toHaveBeenCalledWith(ev); }); it('should dispatch action when getActionConfigGivenAction returns an action', () => { @@ -42,7 +42,7 @@ describe('handleControlAction', () => { handleControlAction(ev, control, host); - expect(dispatchActionExecutionRequest).toBeCalledWith(host, { + expect(dispatchActionExecutionRequest).toHaveBeenCalledWith(host, { actions: [action], }); }); @@ -55,7 +55,7 @@ describe('handleControlAction', () => { handleControlAction(ev, createControl(), host); - expect(dispatchActionExecutionRequest).not.toBeCalled(); + expect(dispatchActionExecutionRequest).not.toHaveBeenCalled(); }); it('should call onDismiss when dismiss is not false', () => { @@ -67,7 +67,7 @@ describe('handleControlAction', () => { handleControlAction(ev, createControl({ dismiss: true }), host, onDismiss); - expect(onDismiss).toBeCalled(); + expect(onDismiss).toHaveBeenCalled(); }); it('should not call onDismiss when dismiss is false', () => { @@ -79,7 +79,7 @@ describe('handleControlAction', () => { handleControlAction(ev, createControl({ dismiss: false }), host, onDismiss); - expect(onDismiss).not.toBeCalled(); + expect(onDismiss).not.toHaveBeenCalled(); }); it('should not call onDismiss when no onDismiss is provided', () => { diff --git a/tests/components-lib/ptz/drag-controller.test.ts b/tests/components-lib/ptz/drag-controller.test.ts index 7b0f8fa5..acdf07ef 100644 --- a/tests/components-lib/ptz/drag-controller.test.ts +++ b/tests/components-lib/ptz/drag-controller.test.ts @@ -45,7 +45,7 @@ describe('PTZDragController', () => { it('should register as a controller on the host', () => { const host = createLitElement(); new PTZDragController(host); - expect(host.addController).toBeCalled(); + expect(host.addController).toHaveBeenCalled(); }); describe('activation', () => { @@ -56,7 +56,7 @@ describe('PTZDragController', () => { const element = document.createElement('div'); controller.activateIfNecessary(element); - expect(host.requestUpdate).toBeCalled(); + expect(host.requestUpdate).toHaveBeenCalled(); }); it('should set cursor and touch-action styles on the element', () => { @@ -93,7 +93,7 @@ describe('PTZDragController', () => { controller.activateIfNecessary(element); controller.activateIfNecessary(element); - expect(createGesture).toBeCalledTimes(1); + expect(createGesture).toHaveBeenCalledTimes(1); }); it('should create gesture with drag, pinch, and wheel actions', () => { @@ -102,7 +102,7 @@ describe('PTZDragController', () => { controller.activateIfNecessary(document.createElement('div')); - expect(createGesture).toBeCalled(); + expect(createGesture).toHaveBeenCalled(); }); }); @@ -117,7 +117,7 @@ describe('PTZDragController', () => { controller.deactivateIfNecessary(); - expect(host.requestUpdate).toBeCalled(); + expect(host.requestUpdate).toHaveBeenCalled(); }); it('should destroy the gesture recognizer', () => { @@ -127,7 +127,7 @@ describe('PTZDragController', () => { controller.activateIfNecessary(document.createElement('div')); controller.deactivateIfNecessary(); - expect(destroy).toBeCalled(); + expect(destroy).toHaveBeenCalled(); }); it('should not deactivate when not active', () => { @@ -136,7 +136,7 @@ describe('PTZDragController', () => { controller.deactivateIfNecessary(); - expect(host.requestUpdate).not.toBeCalled(); + expect(host.requestUpdate).not.toHaveBeenCalled(); }); it('should stop active directions on deactivation', () => { @@ -154,11 +154,11 @@ describe('PTZDragController', () => { controller.deactivateIfNecessary(); - expect(dispatchActionExecutionRequest).toBeCalledWith( + expect(dispatchActionExecutionRequest).toHaveBeenCalledWith( host, ptzAction('left', 'stop'), ); - expect(dispatchActionExecutionRequest).toBeCalledWith( + expect(dispatchActionExecutionRequest).toHaveBeenCalledWith( host, ptzAction('down', 'stop'), ); @@ -175,7 +175,7 @@ describe('PTZDragController', () => { controller.deactivateIfNecessary(); - expect(dispatchActionExecutionRequest).toBeCalledWith( + expect(dispatchActionExecutionRequest).toHaveBeenCalledWith( host, ptzAction('zoom_in', 'stop'), ); @@ -190,7 +190,7 @@ describe('PTZDragController', () => { controller.activateIfNecessary(document.createElement('div')); controller.hostDisconnected(); - expect(destroy).toBeCalled(); + expect(destroy).toHaveBeenCalled(); }); }); @@ -208,7 +208,7 @@ describe('PTZDragController', () => { movement: [110, 0], }); - expect(dispatchActionExecutionRequest).toBeCalledWith( + expect(dispatchActionExecutionRequest).toHaveBeenCalledWith( host, ptzAction('left', 'start'), ); @@ -226,7 +226,7 @@ describe('PTZDragController', () => { movement: [-110, 0], }); - expect(dispatchActionExecutionRequest).toBeCalledWith( + expect(dispatchActionExecutionRequest).toHaveBeenCalledWith( host, ptzAction('right', 'start'), ); @@ -244,7 +244,7 @@ describe('PTZDragController', () => { movement: [0, 110], }); - expect(dispatchActionExecutionRequest).toBeCalledWith( + expect(dispatchActionExecutionRequest).toHaveBeenCalledWith( host, ptzAction('up', 'start'), ); @@ -262,7 +262,7 @@ describe('PTZDragController', () => { movement: [0, -110], }); - expect(dispatchActionExecutionRequest).toBeCalledWith( + expect(dispatchActionExecutionRequest).toHaveBeenCalledWith( host, ptzAction('down', 'start'), ); @@ -280,11 +280,11 @@ describe('PTZDragController', () => { movement: [110, -110], }); - expect(dispatchActionExecutionRequest).toBeCalledWith( + expect(dispatchActionExecutionRequest).toHaveBeenCalledWith( host, ptzAction('left', 'start'), ); - expect(dispatchActionExecutionRequest).toBeCalledWith( + expect(dispatchActionExecutionRequest).toHaveBeenCalledWith( host, ptzAction('down', 'start'), ); @@ -309,7 +309,7 @@ describe('PTZDragController', () => { movement: [120, 0], }); - expect(dispatchActionExecutionRequest).not.toBeCalled(); + expect(dispatchActionExecutionRequest).not.toHaveBeenCalled(); }); it('should stop old and start new on X direction reversal', () => { @@ -331,11 +331,11 @@ describe('PTZDragController', () => { movement: [-110, 0], }); - expect(dispatchActionExecutionRequest).toBeCalledWith( + expect(dispatchActionExecutionRequest).toHaveBeenCalledWith( host, ptzAction('left', 'stop'), ); - expect(dispatchActionExecutionRequest).toBeCalledWith( + expect(dispatchActionExecutionRequest).toHaveBeenCalledWith( host, ptzAction('right', 'start'), ); @@ -360,11 +360,11 @@ describe('PTZDragController', () => { movement: [0, -110], }); - expect(dispatchActionExecutionRequest).toBeCalledWith( + expect(dispatchActionExecutionRequest).toHaveBeenCalledWith( host, ptzAction('up', 'stop'), ); - expect(dispatchActionExecutionRequest).toBeCalledWith( + expect(dispatchActionExecutionRequest).toHaveBeenCalledWith( host, ptzAction('down', 'start'), ); @@ -390,15 +390,15 @@ describe('PTZDragController', () => { movement: [0, 0], }); - expect(dispatchActionExecutionRequest).toBeCalledWith( + expect(dispatchActionExecutionRequest).toHaveBeenCalledWith( host, ptzAction('left', 'stop'), ); - expect(dispatchActionExecutionRequest).toBeCalledWith( + expect(dispatchActionExecutionRequest).toHaveBeenCalledWith( host, ptzAction('up', 'stop'), ); - expect(dispatchActionExecutionRequest).toBeCalledTimes(2); + expect(dispatchActionExecutionRequest).toHaveBeenCalledTimes(2); }); it('should stop active directions on drag end', () => { @@ -420,11 +420,11 @@ describe('PTZDragController', () => { movement: [110, -110], }); - expect(dispatchActionExecutionRequest).toBeCalledWith( + expect(dispatchActionExecutionRequest).toHaveBeenCalledWith( host, ptzAction('left', 'stop'), ); - expect(dispatchActionExecutionRequest).toBeCalledWith( + expect(dispatchActionExecutionRequest).toHaveBeenCalledWith( host, ptzAction('down', 'stop'), ); @@ -450,8 +450,8 @@ describe('PTZDragController', () => { }); // Only the stop is dispatched, not a relative action. - expect(dispatchActionExecutionRequest).toBeCalledTimes(1); - expect(dispatchActionExecutionRequest).toBeCalledWith( + expect(dispatchActionExecutionRequest).toHaveBeenCalledTimes(1); + expect(dispatchActionExecutionRequest).toHaveBeenCalledWith( host, ptzAction('left', 'stop'), ); @@ -471,7 +471,7 @@ describe('PTZDragController', () => { movement: [30, -20], }); - expect(dispatchActionExecutionRequest).not.toBeCalled(); + expect(dispatchActionExecutionRequest).not.toHaveBeenCalled(); }); it('should dispatch relative left and down on small drag end', () => { @@ -491,8 +491,14 @@ describe('PTZDragController', () => { movement: [30, -20], }); - expect(dispatchActionExecutionRequest).toBeCalledWith(host, ptzAction('left')); - expect(dispatchActionExecutionRequest).toBeCalledWith(host, ptzAction('down')); + expect(dispatchActionExecutionRequest).toHaveBeenCalledWith( + host, + ptzAction('left'), + ); + expect(dispatchActionExecutionRequest).toHaveBeenCalledWith( + host, + ptzAction('down'), + ); }); it('should dispatch relative right and up on small drag end', () => { @@ -512,8 +518,14 @@ describe('PTZDragController', () => { movement: [-30, 20], }); - expect(dispatchActionExecutionRequest).toBeCalledWith(host, ptzAction('right')); - expect(dispatchActionExecutionRequest).toBeCalledWith(host, ptzAction('up')); + expect(dispatchActionExecutionRequest).toHaveBeenCalledWith( + host, + ptzAction('right'), + ); + expect(dispatchActionExecutionRequest).toHaveBeenCalledWith( + host, + ptzAction('up'), + ); }); it('should not dispatch relative on zero movement', () => { @@ -528,7 +540,7 @@ describe('PTZDragController', () => { movement: [0, 0], }); - expect(dispatchActionExecutionRequest).not.toBeCalled(); + expect(dispatchActionExecutionRequest).not.toHaveBeenCalled(); }); }); @@ -545,7 +557,7 @@ describe('PTZDragController', () => { movement: [80, 80], }); - expect(dispatchActionExecutionRequest).not.toBeCalled(); + expect(dispatchActionExecutionRequest).not.toHaveBeenCalled(); }); it('should stop active continuous directions when pinch starts', () => { @@ -567,7 +579,7 @@ describe('PTZDragController', () => { movement: [120, 0], }); - expect(dispatchActionExecutionRequest).toBeCalledWith( + expect(dispatchActionExecutionRequest).toHaveBeenCalledWith( host, ptzAction('left', 'stop'), ); @@ -593,7 +605,7 @@ describe('PTZDragController', () => { movement: [110, 0], }); - expect(dispatchActionExecutionRequest).not.toBeCalled(); + expect(dispatchActionExecutionRequest).not.toHaveBeenCalled(); }); it('should resume drag handling after poisoned gesture ends', () => { @@ -616,7 +628,7 @@ describe('PTZDragController', () => { movement: [80, 0], }); - expect(dispatchActionExecutionRequest).not.toBeCalled(); + expect(dispatchActionExecutionRequest).not.toHaveBeenCalled(); }); }); @@ -632,7 +644,7 @@ describe('PTZDragController', () => { movement: [0, 0], }); - expect(dispatchActionExecutionRequest).not.toBeCalled(); + expect(dispatchActionExecutionRequest).not.toHaveBeenCalled(); }); }); @@ -645,7 +657,7 @@ describe('PTZDragController', () => { getHandlers().onPinch({ direction: [1], last: false }); - expect(dispatchActionExecutionRequest).toBeCalledWith( + expect(dispatchActionExecutionRequest).toHaveBeenCalledWith( host, ptzAction('zoom_in', 'start'), ); @@ -659,7 +671,7 @@ describe('PTZDragController', () => { getHandlers().onPinch({ direction: [-1], last: false }); - expect(dispatchActionExecutionRequest).toBeCalledWith( + expect(dispatchActionExecutionRequest).toHaveBeenCalledWith( host, ptzAction('zoom_out', 'start'), ); @@ -676,11 +688,11 @@ describe('PTZDragController', () => { getHandlers().onPinch({ direction: [-1], last: false }); - expect(dispatchActionExecutionRequest).toBeCalledWith( + expect(dispatchActionExecutionRequest).toHaveBeenCalledWith( host, ptzAction('zoom_in', 'stop'), ); - expect(dispatchActionExecutionRequest).toBeCalledWith( + expect(dispatchActionExecutionRequest).toHaveBeenCalledWith( host, ptzAction('zoom_out', 'start'), ); @@ -697,7 +709,7 @@ describe('PTZDragController', () => { getHandlers().onPinch({ direction: [1], last: true }); - expect(dispatchActionExecutionRequest).toBeCalledWith( + expect(dispatchActionExecutionRequest).toHaveBeenCalledWith( host, ptzAction('zoom_in', 'stop'), ); @@ -711,7 +723,7 @@ describe('PTZDragController', () => { getHandlers().onPinch({ direction: [0], last: false }); - expect(dispatchActionExecutionRequest).not.toBeCalled(); + expect(dispatchActionExecutionRequest).not.toHaveBeenCalled(); }); it('should not re-dispatch when zoom direction is unchanged', () => { @@ -725,7 +737,7 @@ describe('PTZDragController', () => { getHandlers().onPinch({ direction: [1], last: false }); - expect(dispatchActionExecutionRequest).not.toBeCalled(); + expect(dispatchActionExecutionRequest).not.toHaveBeenCalled(); }); }); @@ -738,7 +750,10 @@ describe('PTZDragController', () => { getHandlers().onWheel({ delta: [0, 100] }); - expect(dispatchActionExecutionRequest).toBeCalledWith(host, ptzAction('zoom_out')); + expect(dispatchActionExecutionRequest).toHaveBeenCalledWith( + host, + ptzAction('zoom_out'), + ); }); it('should dispatch zoom_in on scroll up', () => { @@ -749,7 +764,10 @@ describe('PTZDragController', () => { getHandlers().onWheel({ delta: [0, -100] }); - expect(dispatchActionExecutionRequest).toBeCalledWith(host, ptzAction('zoom_in')); + expect(dispatchActionExecutionRequest).toHaveBeenCalledWith( + host, + ptzAction('zoom_in'), + ); }); it('should not dispatch on zero delta', () => { @@ -760,7 +778,7 @@ describe('PTZDragController', () => { getHandlers().onWheel({ delta: [0, 0] }); - expect(dispatchActionExecutionRequest).not.toBeCalled(); + expect(dispatchActionExecutionRequest).not.toHaveBeenCalled(); }); }); diff --git a/tests/components-lib/ptz/ptz-controller.test.ts b/tests/components-lib/ptz/ptz-controller.test.ts index 05770b6c..b610866b 100644 --- a/tests/components-lib/ptz/ptz-controller.test.ts +++ b/tests/components-lib/ptz/ptz-controller.test.ts @@ -8,7 +8,11 @@ import { type PTZControlsConfig, } from '../../../src/config/schema/common/controls/ptz'; import { PTZMovementType } from '../../../src/types'; -import { createCameraManager, createCapabilities, createStore } from '../../test-utils'; +import { + createCameraManager, + createCapabilities, + createStore, +} from '../../camera-manager/test-utils'; const createConfig = (config?: Partial): PTZControlsConfig => { return ptzControlsConfigSchema.parse({ @@ -405,8 +409,8 @@ describe('PTZController', () => { controller.toggleTypeHandler(ev, 'gestures'); - expect(ev.stopPropagation).toBeCalled(); - expect(handler).toBeCalledWith( + expect(ev.stopPropagation).toHaveBeenCalled(); + expect(handler).toHaveBeenCalledWith( expect.objectContaining({ detail: { actions: { @@ -429,7 +433,7 @@ describe('PTZController', () => { controller.toggleTypeHandler(ev, 'buttons'); - expect(handler).toBeCalledWith( + expect(handler).toHaveBeenCalledWith( expect.objectContaining({ detail: { actions: { @@ -452,7 +456,7 @@ describe('PTZController', () => { controller.toggleTypeHandler(ev); - expect(handler).toBeCalledWith( + expect(handler).toHaveBeenCalledWith( expect.objectContaining({ detail: { actions: { @@ -487,7 +491,7 @@ describe('PTZController', () => { config, ); - expect(handler).toBeCalledWith( + expect(handler).toHaveBeenCalledWith( expect.objectContaining({ detail: { actions: action, @@ -508,7 +512,7 @@ describe('PTZController', () => { new CustomEvent<{ action: string }>('@action', { detail: { action: 'tap' } }), ); - expect(handler).not.toBeCalled(); + expect(handler).not.toHaveBeenCalled(); }); it('should not call action without hass', () => { @@ -522,7 +526,7 @@ describe('PTZController', () => { new CustomEvent<{ action: string }>('@action', { detail: { action: 'tap' } }), ); - expect(handler).not.toBeCalled(); + expect(handler).not.toHaveBeenCalled(); }); }); }); diff --git a/tests/components-lib/signed-url-controller.test.ts b/tests/components-lib/signed-url-controller.test.ts index ce6cae0d..71499233 100644 --- a/tests/components-lib/signed-url-controller.test.ts +++ b/tests/components-lib/signed-url-controller.test.ts @@ -30,7 +30,7 @@ describe('SignedURLController', () => { const host = mock(); const controller = new SignedURLController(host, () => ({})); - expect(host.addController).toBeCalledWith(controller); + expect(host.addController).toHaveBeenCalledWith(controller); expect(controller.getValue()).toBeNull(); }); @@ -65,7 +65,7 @@ describe('SignedURLController', () => { expect(controller.getValue()).toBeNull(); - expect(createProxiedEndpointIfNecessary).toBeCalledWith( + expect(createProxiedEndpointIfNecessary).toHaveBeenCalledWith( hass, { endpoint: 'http://test-url.com/', sign: false }, proxyConfig, @@ -74,9 +74,9 @@ describe('SignedURLController', () => { await flushPromises(); - expect(homeAssistantGetSignedURLIfNecessary).toBeCalled(); + expect(homeAssistantGetSignedURLIfNecessary).toHaveBeenCalled(); expect(controller.getValue()).toBe('http://signed-proxied-url.com'); - expect(host.requestUpdate).toBeCalled(); + expect(host.requestUpdate).toHaveBeenCalled(); }); it('should not fetch if inputs are missing', async () => { @@ -86,7 +86,7 @@ describe('SignedURLController', () => { controller.hostUpdate(); await flushPromises(); - expect(createProxiedEndpointIfNecessary).not.toBeCalled(); + expect(createProxiedEndpointIfNecessary).not.toHaveBeenCalled(); expect(controller.getValue()).toBeNull(); }); @@ -102,7 +102,7 @@ describe('SignedURLController', () => { controller.hostUpdate(); await flushPromises(); - expect(createProxiedEndpointIfNecessary).not.toBeCalled(); + expect(createProxiedEndpointIfNecessary).not.toHaveBeenCalled(); expect(controller.getValue()).toBe('http://test-url.com'); }); @@ -208,7 +208,7 @@ describe('SignedURLController', () => { await flushPromises(); expect(controller.getValue()).toBeNull(); - expect(valueChangeCallback).not.toBeCalled(); + expect(valueChangeCallback).not.toHaveBeenCalled(); }); it('should not call valueChangeCallback on null signed URL', async () => { @@ -243,7 +243,7 @@ describe('SignedURLController', () => { await flushPromises(); expect(controller.getValue()).toBeNull(); - expect(valueChangeCallback).not.toBeCalled(); + expect(valueChangeCallback).not.toHaveBeenCalled(); }); it('should ignore successful fetch if inputs become invalid', async () => { @@ -281,7 +281,7 @@ describe('SignedURLController', () => { await flushPromises(); expect(controller.getValue()).toBeNull(); - expect(host.requestUpdate).not.toBeCalled(); + expect(host.requestUpdate).not.toHaveBeenCalled(); }); it('should invalidate cache if input changes', async () => { @@ -401,7 +401,7 @@ describe('SignedURLController', () => { expect(controller.getValue()).toBeNull(); expect(controller.getError()).toBe('proxy'); - expect(host.requestUpdate).toBeCalledTimes(1); + expect(host.requestUpdate).toHaveBeenCalledTimes(1); }); it('should not retry after sign error with same inputs', async () => { @@ -677,7 +677,7 @@ describe('SignedURLController', () => { rejectProxy?.(new Error('fail')); await flushPromises(); - expect(host.requestUpdate).not.toBeCalled(); + expect(host.requestUpdate).not.toHaveBeenCalled(); }); it('should clear value if proxy endpoint is null', async () => { @@ -704,7 +704,7 @@ describe('SignedURLController', () => { expect(controller.getValue()).toBeNull(); expect(controller.getError()).toBe('proxy'); - expect(host.requestUpdate).toBeCalled(); + expect(host.requestUpdate).toHaveBeenCalled(); }); it('should not retry after proxy error with same inputs', async () => { @@ -881,7 +881,7 @@ describe('SignedURLController', () => { await flushPromises(); expect(controller.getValue()).toBeNull(); - expect(host.requestUpdate).not.toBeCalled(); + expect(host.requestUpdate).not.toHaveBeenCalled(); }); it('should ignore stale null signed URL after request ID changed', async () => { @@ -925,7 +925,7 @@ describe('SignedURLController', () => { await flushPromises(); // Stale result should be discarded. - expect(host.requestUpdate).not.toBeCalled(); + expect(host.requestUpdate).not.toHaveBeenCalled(); }); it('should sign endpoint without proxying when sign is set', async () => { @@ -946,10 +946,10 @@ describe('SignedURLController', () => { controller.hostUpdate(); await flushPromises(); - expect(createProxiedEndpointIfNecessary).not.toBeCalled(); - expect(homeAssistantGetSignedURLIfNecessary).toBeCalled(); + expect(createProxiedEndpointIfNecessary).not.toHaveBeenCalled(); + expect(homeAssistantGetSignedURLIfNecessary).toHaveBeenCalled(); expect(controller.getValue()).toBe('http://ha.local/api/some/endpoint?authSig=abc'); - expect(host.requestUpdate).toBeCalled(); + expect(host.requestUpdate).toHaveBeenCalled(); }); it('should return url directly when sign is false and proxy is disabled', () => { @@ -998,7 +998,7 @@ describe('SignedURLController', () => { resolveProxy?.(null); await flushPromises(); - expect(host.requestUpdate).not.toBeCalled(); + expect(host.requestUpdate).not.toHaveBeenCalled(); }); }); diff --git a/tests/components-lib/status-bar-controller.test.ts b/tests/components-lib/status-bar-controller.test.ts index 149f5bf2..626e3757 100644 --- a/tests/components-lib/status-bar-controller.test.ts +++ b/tests/components-lib/status-bar-controller.test.ts @@ -403,7 +403,7 @@ describe('StatusBarController', () => { const controller = new StatusBarController(host); controller.actionHandler(createInteractionActionEvent('tap')); - expect(handler).not.toBeCalled(); + expect(handler).not.toHaveBeenCalled(); }); it('should request action execution', () => { @@ -422,7 +422,7 @@ describe('StatusBarController', () => { controller.actionHandler(createInteractionActionEvent('tap'), tapActionConfig); - expect(handler).toBeCalledWith( + expect(handler).toHaveBeenCalledWith( expect.objectContaining({ detail: { actions: [action], config: tapActionConfig }, }), diff --git a/tests/components-lib/thumbnail/feature/controller.test.ts b/tests/components-lib/thumbnail/feature/controller.test.ts index e9e8327c..94238c05 100644 --- a/tests/components-lib/thumbnail/feature/controller.test.ts +++ b/tests/components-lib/thumbnail/feature/controller.test.ts @@ -5,7 +5,8 @@ import { mock } from 'vitest-mock-extended'; import type { CameraManager } from '../../../../src/camera-manager/manager'; import { ThumbnailFeatureController } from '../../../../src/components-lib/thumbnail/feature/controller'; import { ViewFolder } from '../../../../src/view/item'; -import { createFolder, TestViewMedia } from '../../../test-utils'; +import { createFolder } from '../../../test-utils'; +import { TestViewMedia } from '../../../view/test-utils'; describe('ThumbnailFeatureController', () => { const itemWithTime = new TestViewMedia({ diff --git a/tests/components-lib/timeline/source.test.ts b/tests/components-lib/timeline/source.test.ts index e8ef18dd..aae38b69 100644 --- a/tests/components-lib/timeline/source.test.ts +++ b/tests/components-lib/timeline/source.test.ts @@ -38,12 +38,9 @@ import type { ConditionStateManagerReadonlyInterface } from '../../../src/condit import { QuerySource } from '../../../src/query-source'; import { ViewMediaType } from '../../../src/view/item'; import { UnifiedQuery, type QueryNode } from '../../../src/view/unified-query'; -import { - createCameraManager, - createFolder, - createStore, - TestViewMedia, -} from '../../test-utils'; +import { createCameraManager, createStore } from '../../camera-manager/test-utils'; +import { createFolder } from '../../test-utils'; +import { TestViewMedia } from '../../view/test-utils'; const CAMERA_ID = 'camera-1'; const TEST_MEDIA_ID = 'TEST_MEDIA_ID'; diff --git a/tests/components-lib/zoom/zoom-controller.test.ts b/tests/components-lib/zoom/zoom-controller.test.ts index 99bc5302..21fcbe09 100644 --- a/tests/components-lib/zoom/zoom-controller.test.ts +++ b/tests/components-lib/zoom/zoom-controller.test.ts @@ -83,7 +83,7 @@ describe('ZoomController', () => { // Won't zoom without control key. const ev_1 = new WheelEvent('wheel', { bubbles: false, deltaY: -120 }); element.dispatchEvent(ev_1); - expect(panzoom.zoomWithWheel).not.toBeCalled(); + expect(panzoom.zoomWithWheel).not.toHaveBeenCalled(); const ev_2 = new WheelEvent('wheel', { bubbles: false, @@ -91,21 +91,21 @@ describe('ZoomController', () => { ctrlKey: true, }); element.dispatchEvent(ev_2); - expect(panzoom.zoomWithWheel).toBeCalledWith(ev_2); + expect(panzoom.zoomWithWheel).toHaveBeenCalledWith(ev_2); panzoom.getScale = vi.fn().mockReturnValue(1.2); const ev_3 = new PointerEvent('pointerdown'); element.dispatchEvent(ev_3); - expect(panzoom.handleDown).toBeCalledWith(ev_3); + expect(panzoom.handleDown).toHaveBeenCalledWith(ev_3); const ev_4 = new PointerEvent('pointermove'); element.dispatchEvent(ev_4); - expect(panzoom.handleMove).toBeCalledWith(ev_4); + expect(panzoom.handleMove).toHaveBeenCalledWith(ev_4); const ev_5 = new PointerEvent('pointerup'); element.dispatchEvent(ev_5); - expect(panzoom.handleUp).toBeCalledWith(ev_5); + expect(panzoom.handleUp).toHaveBeenCalledWith(ev_5); }); it('should not respond to pointer when not zoomed', () => { @@ -118,15 +118,15 @@ describe('ZoomController', () => { const ev_1 = new PointerEvent('pointerdown'); element.dispatchEvent(ev_1); - expect(panzoom.handleDown).not.toBeCalledWith(ev_1); + expect(panzoom.handleDown).not.toHaveBeenCalledWith(ev_1); const ev_2 = new PointerEvent('pointermove'); element.dispatchEvent(ev_2); - expect(panzoom.handleDown).not.toBeCalledWith(ev_2); + expect(panzoom.handleDown).not.toHaveBeenCalledWith(ev_2); const ev_3 = new PointerEvent('pointerup'); element.dispatchEvent(ev_3); - expect(panzoom.handleDown).not.toBeCalledWith(ev_3); + expect(panzoom.handleDown).not.toHaveBeenCalledWith(ev_3); }); it('should respond with touch', () => { @@ -143,21 +143,21 @@ describe('ZoomController', () => { touches: [createTouch({ target: element }), createTouch({ target: element })], }); element.dispatchEvent(ev_1); - expect(panzoom.handleDown).toBeCalledWith(ev_1); + expect(panzoom.handleDown).toHaveBeenCalledWith(ev_1); panzoom.getScale = vi.fn().mockReturnValue(1.2); const ev_3 = createTouchEvent('touchstart'); element.dispatchEvent(ev_3); - expect(panzoom.handleDown).toBeCalledWith(ev_3); + expect(panzoom.handleDown).toHaveBeenCalledWith(ev_3); const ev_4 = createTouchEvent('touchmove'); element.dispatchEvent(ev_4); - expect(panzoom.handleMove).toBeCalledWith(ev_4); + expect(panzoom.handleMove).toHaveBeenCalledWith(ev_4); const ev_5 = createTouchEvent('touchend'); element.dispatchEvent(ev_5); - expect(panzoom.handleUp).toBeCalledWith(ev_5); + expect(panzoom.handleUp).toHaveBeenCalledWith(ev_5); }); }); @@ -179,7 +179,7 @@ describe('ZoomController', () => { // A click on its own will be fine. const click_1 = new MouseEvent('click', { bubbles: true }); inner.dispatchEvent(click_1); - expect(clickHandler).toBeCalledTimes(1); + expect(clickHandler).toHaveBeenCalledTimes(1); // A click after a pointerdown will be ignored. const pointerdown_1 = new PointerEvent('pointerdown'); @@ -189,7 +189,7 @@ describe('ZoomController', () => { inner.dispatchEvent(click_2); // Click will have been ignored. - //expect(clickHandler).toBeCalledTimes(1); + //expect(clickHandler).toHaveBeenCalledTimes(1); // Simulate being zoomed out. panzoom.getScale = vi.fn().mockReturnValue(1.0); @@ -200,7 +200,7 @@ describe('ZoomController', () => { inner.dispatchEvent(click_3); // Click will have been processed. - expect(clickHandler).toBeCalledTimes(2); + expect(clickHandler).toHaveBeenCalledTimes(2); }); it('deactivate should remove event handlers', () => { @@ -217,7 +217,7 @@ describe('ZoomController', () => { ctrlKey: true, }); element.dispatchEvent(ev_1); - expect(panzoom.zoomWithWheel).not.toBeCalled(); + expect(panzoom.zoomWithWheel).not.toHaveBeenCalled(); }); describe('should fire events', () => { @@ -241,8 +241,8 @@ describe('ZoomController', () => { }, }); element.dispatchEvent(ev_1); - expect(zoomedFunc).toBeCalled(); - expect(unzoomedFunc).not.toBeCalled(); + expect(zoomedFunc).toHaveBeenCalled(); + expect(unzoomedFunc).not.toHaveBeenCalled(); const ev_2 = new CustomEvent('panzoomchange', { detail: { @@ -254,7 +254,7 @@ describe('ZoomController', () => { }, }); element.dispatchEvent(ev_2); - expect(unzoomedFunc).toBeCalled(); + expect(unzoomedFunc).toHaveBeenCalled(); }); it('when state has not changed or spurious events received', () => { @@ -279,8 +279,8 @@ describe('ZoomController', () => { element.dispatchEvent(ev_1); // Unzoomed event with scale === 1, this._zoomed will already be false. - expect(unzoomedFunc).not.toBeCalled(); - expect(zoomedFunc).not.toBeCalled(); + expect(unzoomedFunc).not.toHaveBeenCalled(); + expect(zoomedFunc).not.toHaveBeenCalled(); const ev_2 = new CustomEvent('panzoomchange', { detail: { @@ -292,12 +292,12 @@ describe('ZoomController', () => { }, }); element.dispatchEvent(ev_2); - expect(zoomedFunc).toBeCalledTimes(1); - expect(unzoomedFunc).not.toBeCalled(); + expect(zoomedFunc).toHaveBeenCalledTimes(1); + expect(unzoomedFunc).not.toHaveBeenCalled(); // Another call when already zoomed will be ignored. element.dispatchEvent(ev_2); - expect(zoomedFunc).toBeCalledTimes(1); + expect(zoomedFunc).toHaveBeenCalledTimes(1); }); describe('on default/non-default', () => { @@ -453,11 +453,11 @@ describe('ZoomController', () => { controller.setDefaultSettings({ zoom: 2, pan: { x: 3, y: 4 } }); // Controller was not activated, config setting will not update pan/zoom. - expect(panzoom.zoom).not.toBeCalled(); - expect(panzoom.pan).not.toBeCalled(); + expect(panzoom.zoom).not.toHaveBeenCalled(); + expect(panzoom.pan).not.toHaveBeenCalled(); controller.activate(); - expect(Panzoom).toBeCalledWith( + expect(Panzoom).toHaveBeenCalledWith( expect.anything(), expect.objectContaining({ contain: 'outside', @@ -485,8 +485,8 @@ describe('ZoomController', () => { triggerResizeObserver(); - expect(panzoom.zoom).toBeCalledWith(2, { animate: false }); - expect(panzoom.pan).toBeCalledWith(115.62, 63.6525, { + expect(panzoom.zoom).toHaveBeenCalledWith(2, { animate: false }); + expect(panzoom.pan).toHaveBeenCalledWith(115.62, 63.6525, { animate: true, duration: 100, }); @@ -504,8 +504,8 @@ describe('ZoomController', () => { // This call will do nothing since this is what zoom/pan already are. controller.setDefaultSettings({ zoom: 1, pan: { x: 0, y: 0 } }); - expect(panzoom.zoom).not.toBeCalled(); - expect(panzoom.pan).not.toBeCalled(); + expect(panzoom.zoom).not.toHaveBeenCalled(); + expect(panzoom.pan).not.toHaveBeenCalled(); controller.setDefaultSettings({ zoom: 2, pan: { x: 3, y: 4 } }); @@ -547,8 +547,8 @@ describe('ZoomController', () => { controller.setSettings({ zoom: 2 }); - expect(panzoom.zoom).toBeCalledTimes(1); - expect(panzoom.pan).toBeCalledTimes(1); + expect(panzoom.zoom).toHaveBeenCalledTimes(1); + expect(panzoom.pan).toHaveBeenCalledTimes(1); expect(panzoom.zoom).toHaveBeenNthCalledWith(1, 2, { animate: false }); expect(panzoom.pan).toHaveBeenNthCalledWith(1, 0, 0, { animate: true, @@ -559,8 +559,8 @@ describe('ZoomController', () => { vi.mocked(panzoom.getPan).mockReturnValue({ x: 0, y: 0 }); controller.setSettings({ zoom: 2 }); - expect(panzoom.zoom).toBeCalledTimes(1); - expect(panzoom.pan).toBeCalledTimes(1); + expect(panzoom.zoom).toHaveBeenCalledTimes(1); + expect(panzoom.pan).toHaveBeenCalledTimes(1); }); it('when config is set to empty', () => { @@ -577,8 +577,8 @@ describe('ZoomController', () => { controller.setSettings({}); // Should fall back to default. - expect(panzoom.zoom).toBeCalledWith(2, { animate: false }); - expect(panzoom.pan).toBeCalledWith(115.62, 63.6525, { + expect(panzoom.zoom).toHaveBeenCalledWith(2, { animate: false }); + expect(panzoom.pan).toHaveBeenCalledWith(115.62, 63.6525, { animate: true, duration: 100, }); @@ -624,8 +624,8 @@ describe('ZoomController', () => { triggerResizeObserver(); - expect(panzoom.zoom).not.toBeCalled(); - expect(panzoom.pan).not.toBeCalled(); + expect(panzoom.zoom).not.toHaveBeenCalled(); + expect(panzoom.pan).not.toHaveBeenCalled(); }); it('when element has no size', () => { @@ -641,8 +641,8 @@ describe('ZoomController', () => { triggerResizeObserver(); - expect(panzoom.zoom).not.toBeCalled(); - expect(panzoom.pan).not.toBeCalled(); + expect(panzoom.zoom).not.toHaveBeenCalled(); + expect(panzoom.pan).not.toHaveBeenCalled(); }); }); @@ -673,7 +673,7 @@ describe('ZoomController', () => { const ev = new PointerEvent('pointerdown'); element.dispatchEvent(ev); - expect(panzoom.handleDown).not.toBeCalled(); + expect(panzoom.handleDown).not.toHaveBeenCalled(); }); it('should set touch action on zoom/unzoom', () => { diff --git a/tests/components-lib/zoom/zoom-view-context.test.ts b/tests/components-lib/zoom/zoom-view-context.test.ts index da3ddf68..5d0e25f2 100644 --- a/tests/components-lib/zoom/zoom-view-context.test.ts +++ b/tests/components-lib/zoom/zoom-view-context.test.ts @@ -88,7 +88,7 @@ describe('handleZoomSettingsObservedEvent', () => { viewManager, ); - expect(viewManager.setViewByParameters).not.toBeCalled(); + expect(viewManager.setViewByParameters).not.toHaveBeenCalled(); }); it('should handle observed zoom settings ', () => { @@ -106,13 +106,13 @@ describe('handleZoomSettingsObservedEvent', () => { viewManager, 'target', ); - expect(viewManager.setViewByParameters).toBeCalledWith( + expect(viewManager.setViewByParameters).toHaveBeenCalledWith( expect.objectContaining({ modifiers: [expect.any(MergeContextViewModifier)], }), ); - expect(MergeContextViewModifier).toBeCalledWith({ + expect(MergeContextViewModifier).toHaveBeenCalledWith({ zoom: { target: { observed: { pan: { x: 1, y: 2 }, zoom: 3, isDefault: true, unzoomed: true }, diff --git a/tests/components/notification/media.test.ts b/tests/components/notification/media.test.ts index 5d13a350..abc239ba 100644 --- a/tests/components/notification/media.test.ts +++ b/tests/components/notification/media.test.ts @@ -1,7 +1,7 @@ import { describe, expect, it, vi } from 'vitest'; import { renderNoMediaNotification } from '../../../src/components/notification/media'; -import { createCameraManager, createStore } from '../../test-utils'; +import { createCameraManager, createStore } from '../../camera-manager/test-utils'; // @vitest-environment jsdom describe('renderNoMediaNotification', () => { @@ -23,7 +23,7 @@ describe('renderNoMediaNotification', () => { expect( renderNoMediaNotification({ cameraID: 'camera.office' }, cameraManager), ).toBeTruthy(); - expect(cameraManager.getCameraMetadata).toBeCalledWith('camera.office'); + expect(cameraManager.getCameraMetadata).toHaveBeenCalledWith('camera.office'); }); it('should fall back to the raw camera ID when metadata has no title', () => { @@ -42,13 +42,13 @@ describe('renderNoMediaNotification', () => { vi.mocked(cameraManager.getCameraMetadata).mockReturnValue(null); expect(renderNoMediaNotification({ cameraID: null }, cameraManager)).toBeTruthy(); - expect(cameraManager.getCameraMetadata).toBeCalledWith('camera.default'); + expect(cameraManager.getCameraMetadata).toHaveBeenCalledWith('camera.default'); }); it('should not resolve a title when no camera is resolvable', () => { const cameraManager = createCameraManager(createStore()); expect(renderNoMediaNotification({ cameraID: null }, cameraManager)).toBeTruthy(); - expect(cameraManager.getCameraMetadata).not.toBeCalled(); + expect(cameraManager.getCameraMetadata).not.toHaveBeenCalled(); }); }); diff --git a/tests/condition-trigger/conditions/conditions-manager.test.ts b/tests/condition-trigger/conditions/conditions-manager.test.ts index 490dfbc9..b549561d 100644 --- a/tests/condition-trigger/conditions/conditions-manager.test.ts +++ b/tests/condition-trigger/conditions/conditions-manager.test.ts @@ -45,7 +45,7 @@ describe('ConditionsManager', () => { hass: createHASS({ 'sensor.foo': createStateEntity({ state: '11' }) }), }); - expect(listener).not.toBeCalled(); + expect(listener).not.toHaveBeenCalled(); }); it('should forward the triggering state change to listeners', () => { @@ -94,11 +94,11 @@ describe('ConditionsManager', () => { // Fire the media-query change; the manager re-evaluates and notifies. addEventListener.mock.calls[0][1](); - expect(listener).toBeCalledWith({ result: true }, undefined); + expect(listener).toHaveBeenCalledWith({ result: true }, undefined); // Destroy tears the subscription down. manager.destroy(); - expect(removeEventListener).toBeCalled(); + expect(removeEventListener).toHaveBeenCalled(); }); describe('should handle listeners correctly', () => { @@ -115,20 +115,20 @@ describe('ConditionsManager', () => { stateManager.setState({ fullscreen: true }); - expect(listener).toBeCalledWith({ result: true }, expect.anything()); - expect(listener).toBeCalledTimes(1); + expect(listener).toHaveBeenCalledWith({ result: true }, expect.anything()); + expect(listener).toHaveBeenCalledTimes(1); stateManager.setState({ fullscreen: false }); - expect(listener).toBeCalledWith({ result: false }, expect.anything()); - expect(listener).toBeCalledTimes(2); + expect(listener).toHaveBeenCalledWith({ result: false }, expect.anything()); + expect(listener).toHaveBeenCalledTimes(2); // Re-add the same listener (will still only be called once). manager.addListener(listener); stateManager.setState({ fullscreen: true }); - expect(listener).toBeCalledWith({ result: true }, expect.anything()); - expect(listener).toBeCalledTimes(3); + expect(listener).toHaveBeenCalledWith({ result: true }, expect.anything()); + expect(listener).toHaveBeenCalledTimes(3); }); it('should remove listener', () => { @@ -145,7 +145,7 @@ describe('ConditionsManager', () => { stateManager.setState({ fullscreen: true }); - expect(listener).not.toBeCalled(); + expect(listener).not.toHaveBeenCalled(); }); it('should remove listener on destroy', () => { @@ -162,7 +162,7 @@ describe('ConditionsManager', () => { stateManager.setState({ fullscreen: true }); - expect(listener).not.toBeCalled(); + expect(listener).not.toHaveBeenCalled(); }); it('should not call listeners when the condition result does not change', () => { @@ -177,19 +177,19 @@ describe('ConditionsManager', () => { manager.addListener(listener); stateManager.setState({ view: 'live' }); - expect(listener).toBeCalledTimes(1); + expect(listener).toHaveBeenCalledTimes(1); stateManager.setState({ view: 'clip' }); - expect(listener).toBeCalledTimes(2); + expect(listener).toHaveBeenCalledTimes(2); stateManager.setState({ view: 'clip' }); - expect(listener).toBeCalledTimes(2); + expect(listener).toHaveBeenCalledTimes(2); stateManager.setState({ view: 'live' }); - expect(listener).toBeCalledTimes(3); + expect(listener).toHaveBeenCalledTimes(3); stateManager.setState({ view: 'live' }); - expect(listener).toBeCalledTimes(3); + expect(listener).toHaveBeenCalledTimes(3); }); }); diff --git a/tests/condition-trigger/conditions/state-manager.test.ts b/tests/condition-trigger/conditions/state-manager.test.ts index e89d5126..8007d4fd 100644 --- a/tests/condition-trigger/conditions/state-manager.test.ts +++ b/tests/condition-trigger/conditions/state-manager.test.ts @@ -42,13 +42,13 @@ describe('ConditionStateManager', () => { }; expect(manager.setState(state)).toBe(true); - expect(listener).toBeCalledTimes(1); + expect(listener).toHaveBeenCalledTimes(1); expect(manager.setState(state)).toBe(false); - expect(listener).toBeCalledTimes(1); + expect(listener).toHaveBeenCalledTimes(1); expect(manager.setState({ ...state })).toBe(false); - expect(listener).toBeCalledTimes(1); + expect(listener).toHaveBeenCalledTimes(1); expect( manager.setState({ @@ -57,10 +57,10 @@ describe('ConditionStateManager', () => { }), }), ).toBe(true); - expect(listener).toBeCalledTimes(2); + expect(listener).toHaveBeenCalledTimes(2); expect(manager.setState({ fullscreen: true })).toBe(false); - expect(listener).toBeCalledTimes(2); + expect(listener).toHaveBeenCalledTimes(2); expect( manager.setState({ @@ -69,13 +69,13 @@ describe('ConditionStateManager', () => { }), }), ).toBe(true); - expect(listener).toBeCalledTimes(3); + expect(listener).toHaveBeenCalledTimes(3); expect(manager.setState({ fullscreen: false })).toBe(true); - expect(listener).toBeCalledTimes(4); + expect(listener).toHaveBeenCalledTimes(4); expect(manager.setState({ fullscreen: false })).toBe(false); - expect(listener).toBeCalledTimes(4); + expect(listener).toHaveBeenCalledTimes(4); expect( manager.setState({ @@ -84,7 +84,7 @@ describe('ConditionStateManager', () => { }), }), ).toBe(true); - expect(listener).toBeCalledTimes(5); + expect(listener).toHaveBeenCalledTimes(5); }); }); @@ -138,7 +138,7 @@ describe('ConditionStateManager', () => { manager.setState({ expand: true }); - expect(listener).toBeCalledWith({ + expect(listener).toHaveBeenCalledWith({ old: { fullscreen: true }, change: { expand: true }, new: { fullscreen: true, expand: true }, @@ -155,6 +155,6 @@ describe('ConditionStateManager', () => { const state = { fullscreen: true }; manager.setState(state); - expect(listener).not.toBeCalled(); + expect(listener).not.toHaveBeenCalled(); }); }); diff --git a/tests/condition-trigger/triggers/triggers/camera.test.ts b/tests/condition-trigger/triggers/triggers/camera.test.ts index 41662bef..afe07dad 100644 --- a/tests/condition-trigger/triggers/triggers/camera.test.ts +++ b/tests/condition-trigger/triggers/triggers/camera.test.ts @@ -3,7 +3,7 @@ import { describe, expect, it, vi, type Mock } from 'vitest'; import { ConditionStateManager } from '../../../../src/condition-trigger/conditions/state-manager'; import { CameraTrigger } from '../../../../src/condition-trigger/triggers/triggers/camera'; import type { TriggerOfType } from '../../../../src/condition-trigger/triggers/triggers/types'; -import { createConfig } from '../../../test-utils'; +import { createConfig } from '../../../config/test-utils'; import { createTriggerEvaluatorContext } from './test-utils'; describe('CameraTrigger', () => { diff --git a/tests/condition-trigger/triggers/triggers/config.test.ts b/tests/condition-trigger/triggers/triggers/config.test.ts index b9b3f911..995e3433 100644 --- a/tests/condition-trigger/triggers/triggers/config.test.ts +++ b/tests/condition-trigger/triggers/triggers/config.test.ts @@ -3,7 +3,7 @@ import { describe, expect, it, vi, type Mock } from 'vitest'; import { ConditionStateManager } from '../../../../src/condition-trigger/conditions/state-manager'; import { ConfigTrigger } from '../../../../src/condition-trigger/triggers/triggers/config'; import type { TriggerOfType } from '../../../../src/condition-trigger/triggers/triggers/types'; -import { createConfig } from '../../../test-utils'; +import { createConfig } from '../../../config/test-utils'; import { createTriggerEvaluatorContext } from './test-utils'; describe('ConfigTrigger', () => { diff --git a/tests/condition-trigger/triggers/triggers/event.test.ts b/tests/condition-trigger/triggers/triggers/event.test.ts index 215e6aec..e24bccd4 100644 --- a/tests/condition-trigger/triggers/triggers/event.test.ts +++ b/tests/condition-trigger/triggers/triggers/event.test.ts @@ -49,7 +49,7 @@ describe('EventTrigger', () => { event_type: 'zha_event', }); trigger.subscribe(callback); - expect(eventWatcher.subscribe).toBeCalledTimes(1); + expect(eventWatcher.subscribe).toHaveBeenCalledTimes(1); expect(vi.mocked(eventWatcher.subscribe).mock.calls[0][0].event_type).toBe( 'zha_event', ); @@ -63,7 +63,7 @@ describe('EventTrigger', () => { event_type: ['zha_event', 'zha_event'], }); trigger.subscribe(callback); - expect(eventWatcher.subscribe).toBeCalledTimes(1); + expect(eventWatcher.subscribe).toHaveBeenCalledTimes(1); }); it('should expand list-form event_type into one request per type', () => { @@ -72,7 +72,7 @@ describe('EventTrigger', () => { event_type: ['zha_event', 'deconz_event'], }); trigger.subscribe(callback); - expect(eventWatcher.subscribe).toBeCalledTimes(2); + expect(eventWatcher.subscribe).toHaveBeenCalledTimes(2); expect(vi.mocked(eventWatcher.subscribe).mock.calls[0][0].event_type).toBe( 'zha_event', ); @@ -89,7 +89,7 @@ describe('EventTrigger', () => { trigger.subscribe(callback); const event = createHASSEvent('zha_event', { command: 'press' }); callEventCallback(eventWatcher, event); - expect(callback).toBeCalledWith({ platform: 'event', event }); + expect(callback).toHaveBeenCalledWith({ platform: 'event', event }); }); it('should omit the matcher when neither event_data nor context is set', () => { @@ -164,7 +164,7 @@ describe('EventTrigger', () => { }); trigger.subscribe(callback); trigger.destroy(); - expect(eventWatcher.unsubscribe).toBeCalledTimes(2); + expect(eventWatcher.unsubscribe).toHaveBeenCalledTimes(2); }); it('should be a no-op when destroyed without subscribing', () => { @@ -173,6 +173,6 @@ describe('EventTrigger', () => { event_type: 'zha_event', }); trigger.destroy(); - expect(eventWatcher.unsubscribe).not.toBeCalled(); + expect(eventWatcher.unsubscribe).not.toHaveBeenCalled(); }); }); diff --git a/tests/config/profiles/casting.test.ts b/tests/config/profiles/casting.test.ts index c76aa388..0f99fbb9 100644 --- a/tests/config/profiles/casting.test.ts +++ b/tests/config/profiles/casting.test.ts @@ -4,7 +4,7 @@ import { copyConfig } from '../../../src/config/management'; import { CASTING_PROFILE } from '../../../src/config/profiles/casting'; import { setProfiles } from '../../../src/config/profiles/set-profiles'; import { advancedCameraCardConfigSchema } from '../../../src/config/schema/types'; -import { createRawConfig } from '../../test-utils'; +import { createRawConfig } from '../test-utils'; it('should contain expected defaults', () => { expect(CASTING_PROFILE).toEqual({ diff --git a/tests/config/profiles/doorbell.test.ts b/tests/config/profiles/doorbell.test.ts index ca2b7371..35f276b7 100644 --- a/tests/config/profiles/doorbell.test.ts +++ b/tests/config/profiles/doorbell.test.ts @@ -4,7 +4,7 @@ import { copyConfig } from '../../../src/config/management'; import { DOORBELL_PROFILE } from '../../../src/config/profiles/doorbell'; import { setProfiles } from '../../../src/config/profiles/set-profiles'; import { advancedCameraCardConfigSchema } from '../../../src/config/schema/types'; -import { createRawConfig } from '../../test-utils'; +import { createRawConfig } from '../test-utils'; it('should contain expected defaults', () => { expect(DOORBELL_PROFILE).toEqual({ diff --git a/tests/config/profiles/index.test.ts b/tests/config/profiles/index.test.ts index 96e97bd1..2a86ea70 100644 --- a/tests/config/profiles/index.test.ts +++ b/tests/config/profiles/index.test.ts @@ -2,7 +2,7 @@ import { describe, expect, it } from 'vitest'; import { setProfiles } from '../../../src/config/profiles/set-profiles'; import type { ProfileType } from '../../../src/config/schema/profiles'; -import { createConfig } from '../../test-utils'; +import { createConfig } from '../test-utils'; describe('setProfiles', () => { it('should handle failed parse', () => { diff --git a/tests/config/profiles/low-performance.test.ts b/tests/config/profiles/low-performance.test.ts index 95f981eb..6ef56a74 100644 --- a/tests/config/profiles/low-performance.test.ts +++ b/tests/config/profiles/low-performance.test.ts @@ -4,7 +4,7 @@ import { copyConfig } from '../../../src/config/management'; import { LOW_PERFORMANCE_PROFILE } from '../../../src/config/profiles/low-performance'; import { setProfiles } from '../../../src/config/profiles/set-profiles'; import { advancedCameraCardConfigSchema } from '../../../src/config/schema/types'; -import { createRawConfig } from '../../test-utils'; +import { createRawConfig } from '../test-utils'; it('should contain expected defaults', () => { expect(LOW_PERFORMANCE_PROFILE).toEqual({ diff --git a/tests/config/profiles/scrubbing.test.ts b/tests/config/profiles/scrubbing.test.ts index e49b1b1c..a1ffa1ed 100644 --- a/tests/config/profiles/scrubbing.test.ts +++ b/tests/config/profiles/scrubbing.test.ts @@ -4,7 +4,7 @@ import { copyConfig } from '../../../src/config/management'; import { SCRUBBING_PROFILE } from '../../../src/config/profiles/scrubbing'; import { setProfiles } from '../../../src/config/profiles/set-profiles'; import { advancedCameraCardConfigSchema } from '../../../src/config/schema/types'; -import { createRawConfig } from '../../test-utils'; +import { createRawConfig } from '../test-utils'; it('should contain expected defaults', () => { expect(SCRUBBING_PROFILE).toEqual({ diff --git a/tests/config/schema/folders.test.ts b/tests/config/schema/folders.test.ts index f3038d99..40c51478 100644 --- a/tests/config/schema/folders.test.ts +++ b/tests/config/schema/folders.test.ts @@ -65,7 +65,7 @@ describe('transformPathURLToPathArray', () => { describe('should throw error for non-media source path component', () => { it.each(prefixes)('with prefix %s', (urlPrefix: string) => { const url = `${urlPrefix}media-browser/browser,does-not-start-with-media-source`; - expect(() => transformPathURLToPathArray(url)).toThrowError( + expect(() => transformPathURLToPathArray(url)).toThrow( /Could not parse media source URL/, ); }); diff --git a/tests/config/test-utils.ts b/tests/config/test-utils.ts new file mode 100644 index 00000000..62c4f90f --- /dev/null +++ b/tests/config/test-utils.ts @@ -0,0 +1,34 @@ +import { cameraConfigSchema, type CameraConfig } from '../../src/config/schema/cameras'; +import { + performanceConfigSchema, + type PerformanceConfig, +} from '../../src/config/schema/performance'; +import { + advancedCameraCardConfigSchema, + type AdvancedCameraCardConfig, +} from '../../src/config/schema/types'; +import type { RawAdvancedCameraCardConfig } from '../../src/config/types'; + +export const createRawConfig = ( + config?: Partial, +): RawAdvancedCameraCardConfig => { + return { + type: 'advanced-camera-card', + cameras: [{}], + ...config, + }; +}; + +export const createConfig = ( + config?: RawAdvancedCameraCardConfig, +): AdvancedCameraCardConfig => { + return advancedCameraCardConfigSchema.parse(createRawConfig(config)); +}; + +export const createPerformanceConfig = (config: unknown): PerformanceConfig => { + return performanceConfigSchema.parse(config); +}; + +export const createCameraConfig = (config?: unknown): CameraConfig => { + return cameraConfigSchema.parse(config ?? {}); +}; diff --git a/tests/config/types.test.ts b/tests/config/types.test.ts index ed39ae46..7df5f63a 100644 --- a/tests/config/types.test.ts +++ b/tests/config/types.test.ts @@ -11,7 +11,7 @@ import { conditionSchema } from '../../src/config/schema/condition-trigger/condi import { dimensionsConfigSchema } from '../../src/config/schema/dimensions'; import { customSchema } from '../../src/config/schema/elements/stock/custom'; import { conditionalSchema } from '../../src/config/schema/elements/types'; -import { createConfig } from '../test-utils'; +import { createConfig } from './test-utils'; describe('config defaults', () => { it('should be as expected', () => { @@ -1342,7 +1342,7 @@ describe('should refine user_agent_re conditions', () => { condition: 'user_agent', user_agent_re: '[', }), - ).toThrowError(/Invalid regular expression/); + ).toThrow(/Invalid regular expression/); }); }); @@ -1885,7 +1885,7 @@ it('media viewer should not support microphone based conditions', () => { auto_unmute: 'microphone' as const, }, }), - ).toThrowError(); + ).toThrow(); }); describe('automations should require actions', () => { @@ -1895,7 +1895,7 @@ describe('automations should require actions', () => { cameras: [{}], automations: [{ triggers: [{ trigger: 'initialized' }], conditions: [] }], }), - ).toThrowError(); + ).toThrow(); }); }); diff --git a/tests/ha/browse-media/walker.test.ts b/tests/ha/browse-media/walker.test.ts index 2a427742..b76d1f25 100644 --- a/tests/ha/browse-media/walker.test.ts +++ b/tests/ha/browse-media/walker.test.ts @@ -44,7 +44,7 @@ describe('BrowseMediaWalker', () => { }, ]); - expect(homeAssistantWSRequest).toBeCalledWith(hass, browseMediaSchema, { + expect(homeAssistantWSRequest).toHaveBeenCalledWith(hass, browseMediaSchema, { type: 'media_source/browse_media', media_content_id: 'media/parent', }); @@ -80,7 +80,7 @@ describe('BrowseMediaWalker', () => { const result = await walker.walk(hass, steps); - expect(homeAssistantWSRequest).toBeCalledWith(hass, browseMediaSchema, { + expect(homeAssistantWSRequest).toHaveBeenCalledWith(hass, browseMediaSchema, { type: 'media_source/browse_media', media_content_id: 'media/parent', }); @@ -330,8 +330,8 @@ describe('BrowseMediaWalker', () => { const result = await walker.walk(hass, steps); expect(result).toEqual([child_1]); - expect(homeAssistantWSRequest).toBeCalledTimes(1); - expect(homeAssistantWSRequest).toBeCalledWith(hass, browseMediaSchema, { + expect(homeAssistantWSRequest).toHaveBeenCalledTimes(1); + expect(homeAssistantWSRequest).toHaveBeenCalledWith(hass, browseMediaSchema, { type: 'media_source/browse_media', media_content_id: 'media/parent-1', }); @@ -418,14 +418,14 @@ describe('BrowseMediaWalker', () => { child, ]); - expect(homeAssistantWSRequest).toBeCalledTimes(1); + expect(homeAssistantWSRequest).toHaveBeenCalledTimes(1); expect(cache.has('media/parent')).toBe(true); expect(cache.get('media/parent')).toEqual(parent); expect(await walker.walk(hass, [{ targets: ['media/parent'] }], { cache })).toEqual([ child, ]); - expect(homeAssistantWSRequest).toBeCalledTimes(1); + expect(homeAssistantWSRequest).toHaveBeenCalledTimes(1); }); it('should process multiple targets and combine their children', async () => { diff --git a/tests/ha/connection/subscription-health-monitor.test.ts b/tests/ha/connection/subscription-health-monitor.test.ts index 3a4d8a07..ad27d6cb 100644 --- a/tests/ha/connection/subscription-health-monitor.test.ts +++ b/tests/ha/connection/subscription-health-monitor.test.ts @@ -82,15 +82,15 @@ describe('SubscriptionHealthMonitor', () => { // Healthy -> failing: one notification. monitor.update(status('failing', request, 'zha_event', { failureCount: 1 })); - expect(listener).toBeCalledTimes(1); + expect(listener).toHaveBeenCalledTimes(1); // Still failing (next attempt, same key): no membership change, no notify. monitor.update(status('failing', request, 'zha_event', { failureCount: 2 })); - expect(listener).toBeCalledTimes(1); + expect(listener).toHaveBeenCalledTimes(1); // Failing -> healthy: one more notification. monitor.update(status('subscribed', request, 'zha_event')); - expect(listener).toBeCalledTimes(2); + expect(listener).toHaveBeenCalledTimes(2); }); it('should stop notifying after the returned unsubscribe is called', () => { @@ -101,7 +101,7 @@ describe('SubscriptionHealthMonitor', () => { remove(); monitor.update(status('failing', { id: 'a' }, 'zha_event', { failureCount: 1 })); - expect(listener).not.toBeCalled(); + expect(listener).not.toHaveBeenCalled(); }); it('should retry one request per failing key and leave healthy keys alone', () => { @@ -120,7 +120,7 @@ describe('SubscriptionHealthMonitor', () => { // Exactly one retry, for one of the failing key's requests; never the // healthy key. - expect(retry).toBeCalledTimes(1); + expect(retry).toHaveBeenCalledTimes(1); expect([failingA, failingB]).toContainEqual(retry.mock.calls[0][0]); }); @@ -136,8 +136,8 @@ describe('SubscriptionHealthMonitor', () => { monitor.retry(); - expect(retry).toBeCalledTimes(1); - expect(retry).toBeCalledWith(failing); + expect(retry).toHaveBeenCalledTimes(1); + expect(retry).toHaveBeenCalledWith(failing); }); it('should retry one request for each distinct failing key', () => { @@ -151,7 +151,7 @@ describe('SubscriptionHealthMonitor', () => { monitor.retry(); - expect(retry).toBeCalledTimes(2); + expect(retry).toHaveBeenCalledTimes(2); expect(retry.mock.calls.map((c) => c[0])).toEqual(expect.arrayContaining([a, b])); }); @@ -162,7 +162,7 @@ describe('SubscriptionHealthMonitor', () => { monitor.update(status('unsubscribed', { id: 'a' }, 'zha_event')); - expect(listener).not.toBeCalled(); + expect(listener).not.toHaveBeenCalled(); expect(monitor.getFailures()).toEqual([]); }); }); diff --git a/tests/ha/connection/subscription-manager.test.ts b/tests/ha/connection/subscription-manager.test.ts index 4c37d8db..7cf3726f 100644 --- a/tests/ha/connection/subscription-manager.test.ts +++ b/tests/ha/connection/subscription-manager.test.ts @@ -189,7 +189,7 @@ describe('HASSConnectionSubscriptionManager', () => { await flushPromises(); // The old era's subscription is closed, not abandoned. - expect(calls[0].unsub).toBeCalledTimes(1); + expect(calls[0].unsub).toHaveBeenCalledTimes(1); }); it('should close old-era subscriptions when HA goes not-ready', async () => { @@ -206,7 +206,7 @@ describe('HASSConnectionSubscriptionManager', () => { push(notReady); await flushPromises(); - expect(calls[0].unsub).toBeCalledTimes(1); + expect(calls[0].unsub).toHaveBeenCalledTimes(1); }); it('should mint a fresh era when reanimating from a dead not-ready era, even with the same Connection', async () => { @@ -330,14 +330,14 @@ describe('HASSConnectionSubscriptionManager', () => { manager.subscribe({ key: 'a' }, failing); await vi.advanceTimersByTimeAsync(0); - expect(failing).toBeCalledTimes(1); + expect(failing).toHaveBeenCalledTimes(1); // HASS pushes do not retry while the retry-timer is scheduled. for (let i = 0; i < 10; i++) { push(hass); await vi.advanceTimersByTimeAsync(0); } - expect(failing).toBeCalledTimes(1); + expect(failing).toHaveBeenCalledTimes(1); }); it('should retry after the backoff delay elapses', async () => { @@ -347,19 +347,19 @@ describe('HASSConnectionSubscriptionManager', () => { manager.subscribe({ key: 'a' }, failing); await vi.advanceTimersByTimeAsync(0); - expect(failing).toBeCalledTimes(1); + expect(failing).toHaveBeenCalledTimes(1); // 1st retry: ~1s. await vi.advanceTimersByTimeAsync(1000); - expect(failing).toBeCalledTimes(2); + expect(failing).toHaveBeenCalledTimes(2); // 2nd retry: ~2s. await vi.advanceTimersByTimeAsync(2000); - expect(failing).toBeCalledTimes(3); + expect(failing).toHaveBeenCalledTimes(3); // 3rd retry: ~4s. await vi.advanceTimersByTimeAsync(4000); - expect(failing).toBeCalledTimes(4); + expect(failing).toHaveBeenCalledTimes(4); }); it('should reset the backoff on a connection swap', async () => { @@ -434,13 +434,13 @@ describe('HASSConnectionSubscriptionManager', () => { manager.subscribe({ key: 'a' }, openCallback); await flushPromises(); - expect(openCallback).toBeCalledTimes(1); + expect(openCallback).toHaveBeenCalledTimes(1); // Swap to a new connection BEFORE the first call settles. const hass2 = createSwappedHASS(); push(hass2); await flushPromises(); - expect(openCallback).toBeCalledTimes(2); + expect(openCallback).toHaveBeenCalledTimes(2); // Now reject the stale first call. The catch must NOT wipe the marker for // the in-flight second submission, so the next same-connection push must @@ -450,7 +450,7 @@ describe('HASSConnectionSubscriptionManager', () => { push(hass2); await flushPromises(); - expect(openCallback).toBeCalledTimes(2); + expect(openCallback).toHaveBeenCalledTimes(2); }); }); @@ -474,7 +474,7 @@ describe('HASSConnectionSubscriptionManager', () => { manager.destroy(); await flushPromises(); - expect(failingUnsub).toBeCalled(); + expect(failingUnsub).toHaveBeenCalled(); }); it('should detach source listener, drain KSM, clear state, and flip guards dead', async () => { @@ -511,7 +511,7 @@ describe('HASSConnectionSubscriptionManager', () => { await flushPromises(); expect(calls).toHaveLength(1); - expect(calls[0].unsub).toBeCalledTimes(1); + expect(calls[0].unsub).toHaveBeenCalledTimes(1); }); }); @@ -556,7 +556,7 @@ describe('HASSConnectionSubscriptionManager', () => { manager.unsubscribe(req); await flushPromises(); - expect(failingUnsub).toBeCalled(); + expect(failingUnsub).toHaveBeenCalled(); }); }); @@ -730,17 +730,17 @@ describe('HASSConnectionSubscriptionManager', () => { manager.subscribe(req, openCallback); await vi.advanceTimersByTimeAsync(0); - expect(openCallback).toBeCalledTimes(1); + expect(openCallback).toHaveBeenCalledTimes(1); // Pending timer would fire ~1s from now. retry runs the second attempt // synchronously instead of waiting. manager.retry(req); await flushPromises(); - expect(openCallback).toBeCalledTimes(2); + expect(openCallback).toHaveBeenCalledTimes(2); // Advance well past the original 1s schedule: nothing further fires. await vi.advanceTimersByTimeAsync(10_000); - expect(openCallback).toBeCalledTimes(2); + expect(openCallback).toHaveBeenCalledTimes(2); }); it('should reset the backoff so the next failure schedules at the base delay', async () => { @@ -755,17 +755,17 @@ describe('HASSConnectionSubscriptionManager', () => { await vi.advanceTimersByTimeAsync(0); await vi.advanceTimersByTimeAsync(1000); await vi.advanceTimersByTimeAsync(2000); - expect(failing).toBeCalledTimes(3); + expect(failing).toHaveBeenCalledTimes(3); // User clicks retry: counter resets. Next failure should schedule at ~1s // again, not ~8s. manager.retry(req); await flushPromises(); - expect(failing).toBeCalledTimes(4); + expect(failing).toHaveBeenCalledTimes(4); await vi.advanceTimersByTimeAsync(999); - expect(failing).toBeCalledTimes(4); + expect(failing).toHaveBeenCalledTimes(4); await vi.advanceTimersByTimeAsync(1); - expect(failing).toBeCalledTimes(5); + expect(failing).toHaveBeenCalledTimes(5); }); it('should be a no-op for an unknown request', () => { @@ -785,18 +785,18 @@ describe('HASSConnectionSubscriptionManager', () => { manager.subscribe(req, openCallback); await vi.advanceTimersByTimeAsync(0); - expect(openCallback).not.toBeCalled(); + expect(openCallback).not.toHaveBeenCalled(); manager.retry(req); await flushPromises(); - expect(openCallback).not.toBeCalled(); + expect(openCallback).not.toHaveBeenCalled(); // Era starts; the request submits. const ready = createHASS(); ready.config.state = STATE_RUNNING; push(ready); await flushPromises(); - expect(openCallback).toBeCalledTimes(1); + expect(openCallback).toHaveBeenCalledTimes(1); }); }); }); diff --git a/tests/ha/download.test.ts b/tests/ha/download.test.ts index f580b7c3..0dc91db0 100644 --- a/tests/ha/download.test.ts +++ b/tests/ha/download.test.ts @@ -45,6 +45,6 @@ describe('getMediaDownloadPath', () => { expect(await getMediaDownloadPath(hass, 'id-1', resolvedMediaCache)).toEqual({ endpoint: 'canonicalized:/media/path.mp4', }); - expect(resolveMedia).toBeCalledWith(hass, 'id-1', resolvedMediaCache); + expect(resolveMedia).toHaveBeenCalledWith(hass, 'id-1', resolvedMediaCache); }); }); diff --git a/tests/ha/fire-hass-event.test.ts b/tests/ha/fire-hass-event.test.ts index ed6d5d53..e417e794 100644 --- a/tests/ha/fire-hass-event.test.ts +++ b/tests/ha/fire-hass-event.test.ts @@ -15,7 +15,7 @@ describe('fireHASSEvent', () => { fireHASSEvent(target, type, detail); - expect(handler).toBeCalledWith( + expect(handler).toHaveBeenCalledWith( expect.objectContaining({ detail, }), diff --git a/tests/ha/haptic.test.ts b/tests/ha/haptic.test.ts index 917f081c..233a2a5c 100644 --- a/tests/ha/haptic.test.ts +++ b/tests/ha/haptic.test.ts @@ -20,6 +20,6 @@ describe('forwardHaptic', () => { ])('should call fireHASSEvent with %s', (hapticType: HapticType) => { forwardHaptic(hapticType); - expect(fireHASSEvent).toBeCalledWith(window, 'haptic', hapticType); + expect(fireHASSEvent).toHaveBeenCalledWith(window, 'haptic', hapticType); }); }); diff --git a/tests/ha/integration/index.test.ts b/tests/ha/integration/index.test.ts index cbe2f0be..a09a6431 100644 --- a/tests/ha/integration/index.test.ts +++ b/tests/ha/integration/index.test.ts @@ -1,20 +1,23 @@ import { describe, expect, it, vi } from 'vitest'; import { getIntegrationManifest } from '../../../src/ha/integration'; -import { integrationManifestSchema } from '../../../src/ha/integration/types'; -import { homeAssistantWSRequest } from '../../../src/ha/ws-request.js'; import { createHASS } from '../../test-utils'; -vi.mock('../../../src/ha/ws-request.js'); - describe('getIntegrationManifest', () => { it('should get integration manifest', async () => { const hass = createHASS(); - await getIntegrationManifest(hass, 'INTEGRATION'); - expect(homeAssistantWSRequest).toHaveBeenCalledWith( - hass, - integrationManifestSchema, - { type: 'manifest/get', integration: 'INTEGRATION' }, - ); + vi.mocked(hass.callWS).mockResolvedValue({ + domain: 'INTEGRATION', + version: '1.0', + }); + + expect(await getIntegrationManifest(hass, 'INTEGRATION')).toEqual({ + domain: 'INTEGRATION', + version: '1.0', + }); + expect(hass.callWS).toHaveBeenCalledWith({ + type: 'manifest/get', + integration: 'INTEGRATION', + }); }); }); diff --git a/tests/ha/is-hass-different.test.ts b/tests/ha/is-hass-different.test.ts index 704618f5..dbdb2f1b 100644 --- a/tests/ha/is-hass-different.test.ts +++ b/tests/ha/is-hass-different.test.ts @@ -21,7 +21,7 @@ describe('isHassDifferent', () => { const entities = ['light.office']; expect(isHassDifferent(newHass, oldHass, entities)).toBe(true); - expect(getHassDifferences).toBeCalledWith(newHass, oldHass, entities, { + expect(getHassDifferences).toHaveBeenCalledWith(newHass, oldHass, entities, { firstOnly: true, }); }); diff --git a/tests/ha/registry/device/index.test.ts b/tests/ha/registry/device/index.test.ts index 2a829e4e..5db1280d 100644 --- a/tests/ha/registry/device/index.test.ts +++ b/tests/ha/registry/device/index.test.ts @@ -2,10 +2,9 @@ import { afterEach, describe, expect, it, vi } from 'vitest'; import { DeviceRegistryManager } from '../../../../src/ha/registry/device'; import { DeviceCache } from '../../../../src/ha/registry/device/types'; -import { homeAssistantWSRequest } from '../../../../src/ha/ws-request'; +import { AdvancedCameraCardError } from '../../../../src/types'; import { createHASS, createRegistryDevice } from '../../../test-utils.js'; -vi.mock('../../../../src/ha/ws-request'); vi.spyOn(global.console, 'warn').mockImplementation(() => true); describe('DeviceRegistryManager', () => { @@ -20,37 +19,43 @@ describe('DeviceRegistryManager', () => { cache.set('test', testDevice); + const hass = createHASS(); const manager = new DeviceRegistryManager(cache); - expect(await manager.getDevice(createHASS(), 'test')).toEqual(testDevice); + expect(await manager.getDevice(hass, 'test')).toEqual(testDevice); - expect(homeAssistantWSRequest).not.toHaveBeenCalled(); + expect(hass.callWS).not.toHaveBeenCalled(); }); it('should fetch and cache when not cached', async () => { const testDevice = createRegistryDevice({ id: 'test' }); + const hass = createHASS(); const manager = new DeviceRegistryManager(new DeviceCache()); - vi.mocked(homeAssistantWSRequest).mockResolvedValueOnce([testDevice]); + vi.mocked(hass.callWS).mockResolvedValueOnce([testDevice]); - expect(await manager.getDevice(createHASS(), 'test')).toEqual(testDevice); - expect(homeAssistantWSRequest).toBeCalledTimes(1); + expect(await manager.getDevice(hass, 'test')).toEqual(testDevice); + expect(hass.callWS).toHaveBeenCalledTimes(1); - expect(await manager.getDevice(createHASS(), 'test')).toEqual(testDevice); - expect(homeAssistantWSRequest).toBeCalledTimes(1); + expect(await manager.getDevice(hass, 'test')).toEqual(testDevice); + expect(hass.callWS).toHaveBeenCalledTimes(1); - expect(await manager.getDevice(createHASS(), 'missing')).toBeNull(); + expect(await manager.getDevice(hass, 'missing')).toBeNull(); // The fetch call is called exactly once. - expect(homeAssistantWSRequest).toBeCalledTimes(1); + expect(hass.callWS).toHaveBeenCalledTimes(1); }); it('should return null when fetch fails', async () => { - vi.mocked(homeAssistantWSRequest).mockRejectedValueOnce(new Error('Fetch error')); + const hass = createHASS(); + vi.mocked(hass.callWS).mockRejectedValueOnce(new Error('Fetch error')); const manager = new DeviceRegistryManager(new DeviceCache()); - expect(await manager.getDevice(createHASS(), 'test')).toBeNull(); + expect(await manager.getDevice(hass, 'test')).toBeNull(); - vi.mocked(expect(console.warn)).toBeCalledWith('Fetch error'); + expect(console.warn).toHaveBeenCalledWith( + expect.any(AdvancedCameraCardError), + expect.anything(), + ); }); }); @@ -59,10 +64,7 @@ describe('DeviceRegistryManager', () => { const notMatchingDevice = createRegistryDevice({ id: 'not-matching' }); const hass = createHASS(); - vi.mocked(homeAssistantWSRequest).mockResolvedValueOnce([ - matchingDevice, - notMatchingDevice, - ]); + vi.mocked(hass.callWS).mockResolvedValueOnce([matchingDevice, notMatchingDevice]); const manager = new DeviceRegistryManager(new DeviceCache()); expect( diff --git a/tests/ha/registry/entity/index.test.ts b/tests/ha/registry/entity/index.test.ts index 4aba6c9d..9732f394 100644 --- a/tests/ha/registry/entity/index.test.ts +++ b/tests/ha/registry/entity/index.test.ts @@ -2,10 +2,9 @@ import { afterEach, describe, expect, it, vi } from 'vitest'; import { EntityRegistryManagerLive } from '../../../../src/ha/registry/entity'; import { EntityCache } from '../../../../src/ha/registry/entity/types'; -import { homeAssistantWSRequest } from '../../../../src/ha/ws-request'; +import { AdvancedCameraCardError } from '../../../../src/types'; import { createHASS, createRegistryEntity } from '../../../test-utils.js'; -vi.mock('../../../../src/ha/ws-request'); vi.spyOn(global.console, 'warn').mockImplementation(() => true); describe('EntityRegistryManager', () => { @@ -20,32 +19,38 @@ describe('EntityRegistryManager', () => { cache.set('test', testEntity); + const hass = createHASS(); const manager = new EntityRegistryManagerLive(cache); - expect(await manager.getEntity(createHASS(), 'test')).toEqual(testEntity); + expect(await manager.getEntity(hass, 'test')).toEqual(testEntity); - expect(homeAssistantWSRequest).not.toHaveBeenCalled(); + expect(hass.callWS).not.toHaveBeenCalled(); }); it('should fetch and cache when not cached', async () => { const testEntity = createRegistryEntity({ entity_id: 'test' }); + const hass = createHASS(); const manager = new EntityRegistryManagerLive(new EntityCache()); - vi.mocked(homeAssistantWSRequest).mockResolvedValueOnce(testEntity); + vi.mocked(hass.callWS).mockResolvedValueOnce(testEntity); - expect(await manager.getEntity(createHASS(), 'test')).toEqual(testEntity); - expect(homeAssistantWSRequest).toBeCalledTimes(1); + expect(await manager.getEntity(hass, 'test')).toEqual(testEntity); + expect(hass.callWS).toHaveBeenCalledTimes(1); - expect(await manager.getEntity(createHASS(), 'test')).toEqual(testEntity); - expect(homeAssistantWSRequest).toBeCalledTimes(1); + expect(await manager.getEntity(hass, 'test')).toEqual(testEntity); + expect(hass.callWS).toHaveBeenCalledTimes(1); }); it('should return null when entity does not exist', async () => { - vi.mocked(homeAssistantWSRequest).mockRejectedValueOnce(new Error('Not found')); + const hass = createHASS(); + vi.mocked(hass.callWS).mockRejectedValueOnce(new Error('Not found')); const manager = new EntityRegistryManagerLive(new EntityCache()); - expect(await manager.getEntity(createHASS(), 'missing')).toBeNull(); + expect(await manager.getEntity(hass, 'missing')).toBeNull(); - vi.mocked(expect(console.warn)).toBeCalledWith('Not found'); + expect(console.warn).toHaveBeenCalledWith( + expect.any(AdvancedCameraCardError), + expect.anything(), + ); }); }); @@ -56,57 +61,58 @@ describe('EntityRegistryManager', () => { const cache = new EntityCache(); cache.set('cached', cachedEntity); + const hass = createHASS(); const manager = new EntityRegistryManagerLive(cache); - vi.mocked(homeAssistantWSRequest).mockResolvedValueOnce(notCachedEntity); - vi.mocked(homeAssistantWSRequest).mockRejectedValueOnce(new Error('Not found')); + vi.mocked(hass.callWS).mockResolvedValueOnce(notCachedEntity); + vi.mocked(hass.callWS).mockRejectedValueOnce(new Error('Not found')); - expect( - await manager.getEntities(createHASS(), ['cached', 'not-cached', 'missing']), - ).toEqual( + expect(await manager.getEntities(hass, ['cached', 'not-cached', 'missing'])).toEqual( new Map([ ['cached', cachedEntity], ['not-cached', notCachedEntity], ]), ); - vi.mocked(expect(console.warn)).toBeCalledWith('Not found'); + expect(console.warn).toHaveBeenCalledWith( + expect.any(AdvancedCameraCardError), + expect.anything(), + ); }); describe('fetchEntityList', async () => { it('should fetch entire entity list once', async () => { const hass = createHASS(); const entity = createRegistryEntity({ entity_id: 'cached' }); - vi.mocked(homeAssistantWSRequest).mockResolvedValueOnce([entity]); + vi.mocked(hass.callWS).mockResolvedValueOnce([entity]); const manager = new EntityRegistryManagerLive(new EntityCache()); await manager.fetchEntityList(hass); - expect(homeAssistantWSRequest).toBeCalledTimes(1); - expect(homeAssistantWSRequest).toBeCalledWith( - expect.anything(), - expect.anything(), - { - type: 'config/entity_registry/list', - }, - ); + expect(hass.callWS).toHaveBeenCalledTimes(1); + expect(hass.callWS).toHaveBeenCalledWith({ + type: 'config/entity_registry/list', + }); expect(await manager.getEntity(hass, 'cached')).toEqual(entity); - expect(homeAssistantWSRequest).toBeCalledTimes(1); + expect(hass.callWS).toHaveBeenCalledTimes(1); await manager.fetchEntityList(hass); - expect(homeAssistantWSRequest).toBeCalledTimes(1); + expect(hass.callWS).toHaveBeenCalledTimes(1); }); it('should log to console on error', async () => { const hass = createHASS(); - vi.mocked(homeAssistantWSRequest).mockRejectedValueOnce(new Error('Fetch error')); + vi.mocked(hass.callWS).mockRejectedValueOnce(new Error('Fetch error')); const manager = new EntityRegistryManagerLive(new EntityCache()); await manager.fetchEntityList(hass); - vi.mocked(expect(console.warn)).toBeCalledWith('Fetch error'); + expect(console.warn).toHaveBeenCalledWith( + expect.any(AdvancedCameraCardError), + expect.anything(), + ); }); }); @@ -115,10 +121,7 @@ describe('EntityRegistryManager', () => { const notMatchingEntity = createRegistryEntity({ entity_id: 'not-matching' }); const hass = createHASS(); - vi.mocked(homeAssistantWSRequest).mockResolvedValueOnce([ - matchingEntity, - notMatchingEntity, - ]); + vi.mocked(hass.callWS).mockResolvedValueOnce([matchingEntity, notMatchingEntity]); const manager = new EntityRegistryManagerLive(new EntityCache()); expect( diff --git a/tests/ha/resolved-media.test.ts b/tests/ha/resolved-media.test.ts index e16164b0..26919280 100644 --- a/tests/ha/resolved-media.test.ts +++ b/tests/ha/resolved-media.test.ts @@ -58,7 +58,7 @@ describe('resolveMedia', () => { const cache = new ResolvedMediaCache(); const result = await resolveMedia(hass, mediaContentID, cache); - expect(homeAssistantWSRequest).toBeCalledWith( + expect(homeAssistantWSRequest).toHaveBeenCalledWith( hass, resolvedMediaSchema, expect.objectContaining({ @@ -77,7 +77,7 @@ describe('resolveMedia', () => { const cache = new ResolvedMediaCache(); const result = await resolveMedia(createHASS(), mediaContentID, cache); expect(result).toBeNull(); - expect(errorToConsole).toBeCalledWith(error); + expect(errorToConsole).toHaveBeenCalledWith(error); }); it('does not cache null results', async () => { diff --git a/tests/ha/sign-path.test.ts b/tests/ha/sign-path.test.ts index 7a2f9cbf..ac0cb94f 100644 --- a/tests/ha/sign-path.test.ts +++ b/tests/ha/sign-path.test.ts @@ -4,12 +4,9 @@ import { homeAssistantGetSignedURLIfNecessary, homeAssistantSignPath, } from '../../src/ha/sign-path'; -import { homeAssistantWSRequest } from '../../src/ha/ws-request.js'; -import { signedPathSchema } from '../../src/types'; +import { AdvancedCameraCardError } from '../../src/types'; import { createHASS } from '../test-utils'; -vi.mock('../../src/ha/ws-request.js'); - describe('homeAssistantSignPath', () => { afterEach(() => { vi.clearAllMocks(); @@ -20,7 +17,7 @@ describe('homeAssistantSignPath', () => { const unsignedPath = 'unsigned/path'; const expires = 42; - vi.mocked(homeAssistantWSRequest).mockResolvedValue({ + vi.mocked(hass.callWS).mockResolvedValue({ path: 'signed/path', }); vi.mocked(hass.hassUrl).mockImplementation((url) => 'hass:' + url); @@ -28,16 +25,20 @@ describe('homeAssistantSignPath', () => { expect(await homeAssistantSignPath(hass, unsignedPath, expires)).toEqual( 'hass:signed/path', ); - expect(homeAssistantWSRequest).toBeCalledWith(hass, signedPathSchema, { + expect(hass.callWS).toHaveBeenCalledWith({ type: 'auth/sign_path', path: unsignedPath, expires, }); }); - it('should return null for null response', async () => { - vi.mocked(homeAssistantWSRequest).mockResolvedValue(null); - expect(await homeAssistantSignPath(createHASS(), 'unsigned/path', 42)).toBeNull(); + it('should throw for empty response', async () => { + const hass = createHASS(); + vi.mocked(hass.callWS).mockResolvedValue(null); + + await expect(homeAssistantSignPath(hass, 'unsigned/path', 42)).rejects.toThrow( + AdvancedCameraCardError, + ); }); }); @@ -47,24 +48,26 @@ describe('homeAssistantSignEndpoint', () => { }); it('should return endpoint URL without signing when sign is false', async () => { + const hass = createHASS(); const endpoint = { endpoint: 'http://example.com', sign: false }; - expect(await homeAssistantGetSignedURLIfNecessary(createHASS(), endpoint)).toBe( + expect(await homeAssistantGetSignedURLIfNecessary(hass, endpoint)).toBe( 'http://example.com', ); - expect(homeAssistantWSRequest).not.toHaveBeenCalled(); + expect(hass.callWS).not.toHaveBeenCalled(); }); it('should return endpoint URL without signing when sign is undefined', async () => { + const hass = createHASS(); const endpoint = { endpoint: 'http://example.com' }; - expect(await homeAssistantGetSignedURLIfNecessary(createHASS(), endpoint)).toBe( + expect(await homeAssistantGetSignedURLIfNecessary(hass, endpoint)).toBe( 'http://example.com', ); - expect(homeAssistantWSRequest).not.toHaveBeenCalled(); + expect(hass.callWS).not.toHaveBeenCalled(); }); it('should sign endpoint when sign is true', async () => { const hass = createHASS(); - vi.mocked(homeAssistantWSRequest).mockResolvedValue({ + vi.mocked(hass.callWS).mockResolvedValue({ path: 'signed/path', }); vi.mocked(hass.hassUrl).mockImplementation((url) => 'hass:' + url); @@ -73,19 +76,20 @@ describe('homeAssistantSignEndpoint', () => { expect(await homeAssistantGetSignedURLIfNecessary(hass, endpoint, 60)).toBe( 'hass:signed/path', ); - expect(homeAssistantWSRequest).toHaveBeenCalledWith(hass, signedPathSchema, { + expect(hass.callWS).toHaveBeenCalledWith({ type: 'auth/sign_path', path: 'http://example.com', expires: 60, }); }); - it('should return null when signing fails', async () => { - vi.mocked(homeAssistantWSRequest).mockResolvedValue(null); + it('should throw when signing fails', async () => { + const hass = createHASS(); + vi.mocked(hass.callWS).mockRejectedValue(new Error('connection lost')); const endpoint = { endpoint: 'http://example.com', sign: true }; - expect( - await homeAssistantGetSignedURLIfNecessary(createHASS(), endpoint), - ).toBeNull(); + await expect(homeAssistantGetSignedURLIfNecessary(hass, endpoint)).rejects.toThrow( + AdvancedCameraCardError, + ); }); }); diff --git a/tests/ha/ws-request.test.ts b/tests/ha/ws-request.test.ts index 52d63e71..3f873c6c 100644 --- a/tests/ha/ws-request.test.ts +++ b/tests/ha/ws-request.test.ts @@ -38,7 +38,7 @@ describe('homeAssistantWSRequest', () => { await expect( homeAssistantWSRequest(hass, resolvedMediaSchema, request), - ).rejects.toThrowError(/Failed to receive response/); + ).rejects.toThrow(/Failed to receive response/); }); it('should throw on empty response', async () => { @@ -47,7 +47,7 @@ describe('homeAssistantWSRequest', () => { await expect( homeAssistantWSRequest(hass, resolvedMediaSchema, request), - ).rejects.toThrowError(/Received empty response/); + ).rejects.toThrow(/Received empty response/); }); it('should throw error on parse failure', async () => { @@ -56,7 +56,7 @@ describe('homeAssistantWSRequest', () => { await expect( homeAssistantWSRequest(hass, resolvedMediaSchema, request), - ).rejects.toThrowError(/Received invalid response/); + ).rejects.toThrow(/Received invalid response/); }); it('should throw on JSON parse failure', async () => { @@ -67,6 +67,6 @@ describe('homeAssistantWSRequest', () => { await expect( homeAssistantWSRequest(hass, resolvedMediaSchema, request, true), - ).rejects.toThrowError(/Received invalid response/); + ).rejects.toThrow(/Received invalid response/); }); }); diff --git a/tests/test-utils.ts b/tests/test-utils.ts index 80497ad1..790f3b9a 100644 --- a/tests/test-utils.ts +++ b/tests/test-utils.ts @@ -9,24 +9,12 @@ import screenfull from 'screenfull'; import { expect, onTestFinished, vi, type Mock } from 'vitest'; import { mock } from 'vitest-mock-extended'; -import { Camera } from '../src/camera-manager/camera'; -import { Capabilities } from '../src/camera-manager/capabilities'; -import type { CameraManagerEngine } from '../src/camera-manager/engine'; import type { FrigateEvent, FrigateRecording, FrigateReview, } from '../src/camera-manager/frigate/types'; -import { GenericCameraManagerEngine } from '../src/camera-manager/generic/engine-generic'; import type { CameraManager } from '../src/camera-manager/manager'; -import { CameraManagerStore } from '../src/camera-manager/store'; -import { - QueryType, - type CameraEventCallback, - type EventQuery, - type RecordingQuery, - type ReviewQuery, -} from '../src/camera-manager/types'; import type { ActionsManager } from '../src/card-controller/actions/actions-manager'; import type { AutomationsManager } from '../src/card-controller/automations-manager'; import type { CallManager } from '../src/card-controller/call/manager'; @@ -42,7 +30,6 @@ import type { DefaultManager } from '../src/card-controller/default-manager'; import type { EffectsManager } from '../src/card-controller/effects/effects-manager'; import type { ExpandManager } from '../src/card-controller/expand-manager'; import type { FoldersManager } from '../src/card-controller/folders/manager'; -import type { FolderQuery } from '../src/card-controller/folders/types'; import type { FullscreenManager } from '../src/card-controller/fullscreen/fullscreen-manager'; import type { EventWatcherSubscriptionInterface } from '../src/card-controller/hass/event-watcher'; import type { HASSManager } from '../src/card-controller/hass/hass-manager'; @@ -67,17 +54,7 @@ import type { ViewItemManager } from '../src/card-controller/view/item-manager'; import type { ViewManager } from '../src/card-controller/view/view-manager'; import type { SubmenuInteraction, SubmenuItem } from '../src/components/submenu/types'; import type { ConditionStateManager } from '../src/condition-trigger/conditions/state-manager'; -import { cameraConfigSchema, type CameraConfig } from '../src/config/schema/cameras'; import type { FolderConfig } from '../src/config/schema/folders'; -import { - performanceConfigSchema, - type PerformanceConfig, -} from '../src/config/schema/performance'; -import { - advancedCameraCardConfigSchema, - type AdvancedCameraCardConfig, -} from '../src/config/schema/types'; -import type { RawAdvancedCameraCardConfig } from '../src/config/types'; import type { BrowseMedia, BrowseMediaMetadata, @@ -87,57 +64,12 @@ import type { Device } from '../src/ha/registry/device/types'; import type { Entity, EntityRegistryManager } from '../src/ha/registry/entity/types'; import type { HASSListener, HASSSource } from '../src/ha/source'; import type { CurrentUser, HassStateDifference, HomeAssistant } from '../src/ha/types'; -import { QuerySource } from '../src/query-source'; -import type { Severity } from '../src/severity'; import type { - CapabilitiesRaw, Interaction, MediaLoadedInfo, MediaLoadedInfoEventDetail, } from '../src/types'; -import { - ViewMedia, - ViewMediaType, - type EventViewMedia, - type ReviewViewMedia, -} from '../src/view/item'; -import { QueryResults } from '../src/view/query-results'; import type { ViewItemCapabilities } from '../src/view/types'; -import { View, type ViewParameters } from '../src/view/view'; - -export const createCameraConfig = (config?: unknown): CameraConfig => { - return cameraConfigSchema.parse(config ?? {}); -}; - -export const createRawConfig = ( - config?: Partial, -): RawAdvancedCameraCardConfig => { - return { - type: 'advanced-camera-card', - cameras: [{}], - ...config, - }; -}; - -export const createConfig = ( - config?: RawAdvancedCameraCardConfig, -): AdvancedCameraCardConfig => { - return advancedCameraCardConfigSchema.parse(createRawConfig(config)); -}; - -export const createInitializedCamera = async ( - config: CameraConfig, - engine: CameraManagerEngine, - capabilities?: Capabilities, - stateWatcher?: StateWatcherSubscriptionInterface, -): Promise => { - const camera = new Camera(config, engine); - await camera.initialize({ - hassManager: createHASSManager({ stateWatcher }), - ...(capabilities ? { capabilityOptions: { capabilities } } : {}), - }); - return camera; -}; export const createHASS = (states?: HassEntities, user?: CurrentUser): HomeAssistant => { const hass = mock(); @@ -307,85 +239,6 @@ export const createFrigateReview = (review?: Partial) => { }; }; -export const createView = (options?: Partial): View => { - return new View({ - view: 'live', - camera: 'camera', - ...options, - }); -}; - -export const createViewWithMedia = (options?: Partial): View => { - const media = generateViewMediaArray({ count: 5 }); - return createView({ - queryResults: new QueryResults({ - results: media, - selectedIndex: 0, - }), - ...options, - }); -}; - -export const createStore = ( - cameras?: { - cameraID: string; - engine?: CameraManagerEngine; - config?: CameraConfig; - capabilities?: Capabilities | null; - eventCallback?: CameraEventCallback; - }[], -): CameraManagerStore => { - const store = new CameraManagerStore(); - for (const cameraProps of cameras ?? []) { - const eventCallback = cameraProps.eventCallback ?? vi.fn(); - const capabilities = - cameraProps.capabilities === undefined - ? createCapabilities() - : cameraProps.capabilities ?? undefined; - const camera = new Camera( - cameraProps.config ?? createCameraConfig(), - cameraProps.engine ?? - new GenericCameraManagerEngine( - createHASSManager(), - mock(), - eventCallback, - ), - { eventCallback, capabilities }, - ); - camera.setID(cameraProps.cameraID); - store.addCamera(camera); - } - return store; -}; - -export const createCameraManager = (store?: CameraManagerStore): CameraManager => { - const cameraStore = store ?? createStore(); - const cameraManager = mock(); - vi.mocked(cameraManager.getStore).mockReturnValue(cameraStore); - vi.mocked(cameraManager.getCameraCapabilities).mockImplementation( - (cameraID: string): Capabilities | null => { - return cameraStore.getCamera(cameraID)?.getCapabilities() ?? null; - }, - ); - - return cameraManager; -}; - -export const createCapabilities = (capabilities?: CapabilitiesRaw): Capabilities => { - return new Capabilities({ - 'favorite-events': false, - 'favorite-recordings': false, - 'remote-control-entity': true, - clips: false, - live: false, - recordings: false, - seek: false, - snapshots: false, - trigger: true, - ...capabilities, - }); -}; - export const createMediaCapabilities = ( options?: Partial, ): ViewItemCapabilities => { @@ -433,154 +286,6 @@ export const createMediaLoadedInfoEvent = (options?: { return ev; }; -export const createPerformanceConfig = (config: unknown): PerformanceConfig => { - return performanceConfigSchema.parse(config); -}; - -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 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 stubMatchMedia = (): Mock => { const matchMedia = vi.fn(); vi.stubGlobal('matchMedia', matchMedia); @@ -982,60 +687,3 @@ export const createRichBrowseMedia = ( }, }; }; - -export const createEventQuery = ( - cameraID: string, - options?: Partial, -): EventQuery => ({ - source: QuerySource.Camera, - type: QueryType.Event, - cameraIDs: new Set([cameraID]), - ...options, -}); - -export const createReviewQuery = ( - cameraID: string, - options?: Partial, -): ReviewQuery => ({ - source: QuerySource.Camera, - type: QueryType.Review, - cameraIDs: new Set([cameraID]), - ...options, -}); - -export const createRecordingQuery = ( - cameraID: string, - options?: Partial, -): RecordingQuery => ({ - source: QuerySource.Camera, - type: QueryType.Recording, - 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; diff --git a/tests/utils/abort-signal.test.ts b/tests/utils/abort-signal.test.ts index 2356188e..23710cb3 100644 --- a/tests/utils/abort-signal.test.ts +++ b/tests/utils/abort-signal.test.ts @@ -8,10 +8,10 @@ describe('onAbort', () => { const cb = vi.fn(); onAbort(ac.signal, cb); - expect(cb).not.toBeCalled(); + expect(cb).not.toHaveBeenCalled(); ac.abort(); - expect(cb).toBeCalledTimes(1); + expect(cb).toHaveBeenCalledTimes(1); }); it('should call the callback synchronously if the signal is already aborted', () => { @@ -21,7 +21,7 @@ describe('onAbort', () => { const cb = vi.fn(); onAbort(ac.signal, cb); - expect(cb).toBeCalledTimes(1); + expect(cb).toHaveBeenCalledTimes(1); }); it('should fire only once even if the signal aborts repeatedly', () => { @@ -35,6 +35,6 @@ describe('onAbort', () => { // no-op too. ac.signal.dispatchEvent(new Event('abort')); - expect(cb).toBeCalledTimes(1); + expect(cb).toHaveBeenCalledTimes(1); }); }); diff --git a/tests/utils/action.test.ts b/tests/utils/action.test.ts index e591b540..893bf02b 100644 --- a/tests/utils/action.test.ts +++ b/tests/utils/action.test.ts @@ -600,6 +600,6 @@ describe('stopEventFromActivatingCardWideActions', () => { it('should stop event from propogating', () => { const event = mock(); stopEventFromActivatingCardWideActions(event); - expect(event.stopPropagation).toBeCalled(); + expect(event.stopPropagation).toHaveBeenCalled(); }); }); diff --git a/tests/utils/basic.test.ts b/tests/utils/basic.test.ts index 21473641..dadc7086 100644 --- a/tests/utils/basic.test.ts +++ b/tests/utils/basic.test.ts @@ -193,7 +193,7 @@ describe('runWhenIdleIfSupported', () => { window.requestIdleCallback = requestIdle; const func = vi.fn(); runWhenIdleIfSupported(func); - expect(requestIdle).toBeCalledWith(func, {}); + expect(requestIdle).toHaveBeenCalledWith(func, {}); }); it('should run idle with timeout when supported', () => { @@ -201,7 +201,7 @@ describe('runWhenIdleIfSupported', () => { window.requestIdleCallback = requestIdle; const func = vi.fn(); runWhenIdleIfSupported(func, 10); - expect(requestIdle).toBeCalledWith(func, { timeout: 10 }); + expect(requestIdle).toHaveBeenCalledWith(func, { timeout: 10 }); }); }); diff --git a/tests/utils/camera.test.ts b/tests/utils/camera.test.ts index c2238802..35574309 100644 --- a/tests/utils/camera.test.ts +++ b/tests/utils/camera.test.ts @@ -1,7 +1,7 @@ import { describe, expect, it } from 'vitest'; import { getCameraID } from '../../src/utils/camera.js'; -import { createCameraConfig } from '../test-utils.js'; +import { createCameraConfig } from '../config/test-utils'; describe('getCameraID', () => { it('should get camera id with id', () => { diff --git a/tests/utils/concurrency/keyed-subscription-manager.test.ts b/tests/utils/concurrency/keyed-subscription-manager.test.ts index 5d044e8d..31542a7f 100644 --- a/tests/utils/concurrency/keyed-subscription-manager.test.ts +++ b/tests/utils/concurrency/keyed-subscription-manager.test.ts @@ -18,7 +18,7 @@ describe('KeyedSubscriptionManager', () => { await manager.subscribe({ key: 'a', callback: vi.fn() }, subscribeFn); await manager.subscribe({ key: 'a', callback: vi.fn() }, subscribeFn); - expect(subscribeFn).toBeCalledTimes(1); + expect(subscribeFn).toHaveBeenCalledTimes(1); }); it('should open a separate subscription for each distinct key', async () => { @@ -28,7 +28,7 @@ describe('KeyedSubscriptionManager', () => { await manager.subscribe({ key: 'a', callback: vi.fn() }, subscribeFn); await manager.subscribe({ key: 'b', callback: vi.fn() }, subscribeFn); - expect(subscribeFn).toBeCalledTimes(2); + expect(subscribeFn).toHaveBeenCalledTimes(2); }); it('should tear down the subscription only when the last subscriber for a key unsubscribes', async () => { @@ -42,10 +42,10 @@ describe('KeyedSubscriptionManager', () => { await manager.subscribe(req2, subscribeFn); await manager.unsubscribe(req1); - expect(unsub).not.toBeCalled(); + expect(unsub).not.toHaveBeenCalled(); await manager.unsubscribe(req2); - expect(unsub).toBeCalledTimes(1); + expect(unsub).toHaveBeenCalledTimes(1); }); it('should await a pending subscribe before tearing down when unsubscribed mid-flight', async () => { @@ -66,7 +66,7 @@ describe('KeyedSubscriptionManager', () => { await subscribePromise; await unsubscribePromise; - expect(unsub).toBeCalledTimes(1); + expect(unsub).toHaveBeenCalledTimes(1); }); it('should expose the requests matching a given key', async () => { @@ -101,7 +101,7 @@ describe('KeyedSubscriptionManager', () => { // A subsequent successful subscribe should re-attempt the underlying call. const successFn = vi.fn().mockResolvedValue(vi.fn()); await manager.subscribe(req, successFn); - expect(successFn).toBeCalledTimes(1); + expect(successFn).toHaveBeenCalledTimes(1); expect(manager.getRequestsForKey('a')).toEqual([req]); }); @@ -115,7 +115,7 @@ describe('KeyedSubscriptionManager', () => { await manager.unsubscribe({ key: 'a', callback: vi.fn() }); - expect(unsub).not.toBeCalled(); + expect(unsub).not.toHaveBeenCalled(); expect(manager.getRequestsForKey('a')).toEqual([subscribed]); }); }); diff --git a/tests/utils/debug.test.ts b/tests/utils/debug.test.ts index 7680fc1a..b3bae251 100644 --- a/tests/utils/debug.test.ts +++ b/tests/utils/debug.test.ts @@ -9,10 +9,10 @@ describe('log', () => { }); it('should do nothing without debug logging set', () => { log({}, 'foo'); - expect(spy).not.toBeCalled(); + expect(spy).not.toHaveBeenCalled(); }); it('should log debug when appropriately configured', () => { log({ debug: { logging: true } }, 'foo'); - expect(spy).toBeCalledWith('foo'); + expect(spy).toHaveBeenCalledWith('foo'); }); }); diff --git a/tests/utils/download.test.ts b/tests/utils/download.test.ts index 4e9547e1..d9f9073a 100644 --- a/tests/utils/download.test.ts +++ b/tests/utils/download.test.ts @@ -23,8 +23,8 @@ describe('downloadURL', () => { downloadURL('http://foo/url.mp4'); expect(link.href).toBe('http://foo/url.mp4'); - expect(link.setAttribute).toBeCalledWith('download', 'download'); - expect(link.click).toBeCalled(); + expect(link.setAttribute).toHaveBeenCalledWith('download', 'download'); + expect(link.click).toHaveBeenCalled(); }); it('should download data URL via link', () => { @@ -36,8 +36,8 @@ describe('downloadURL', () => { downloadURL('data:text/plain;charset=utf-8;base64,VEhJUyBJUyBEQVRB'); expect(link.href).toBe('data:text/plain;charset=utf-8;base64,VEhJUyBJUyBEQVRB'); - expect(link.setAttribute).toBeCalledWith('download', 'download'); - expect(link.click).toBeCalled(); + expect(link.setAttribute).toHaveBeenCalledWith('download', 'download'); + expect(link.click).toHaveBeenCalled(); }); it('should download different origin via window.open', () => { @@ -50,6 +50,6 @@ describe('downloadURL', () => { const windowSpy = vi.spyOn(window, 'open').mockReturnValue(null); downloadURL('http://bar/url.mp4'); - expect(windowSpy).toBeCalledWith('http://bar/url.mp4', '_blank'); + expect(windowSpy).toHaveBeenCalledWith('http://bar/url.mp4', '_blank'); }); }); diff --git a/tests/utils/embla/carousel-controller.test.ts b/tests/utils/embla/carousel-controller.test.ts index 5bb712db..30c5b2c1 100644 --- a/tests/utils/embla/carousel-controller.test.ts +++ b/tests/utils/embla/carousel-controller.test.ts @@ -74,7 +74,7 @@ describe('CarouselController', () => { carousel.destroy(); - expect(getEmblaApi()?.destroy).toBeCalled(); + expect(getEmblaApi()?.destroy).toHaveBeenCalled(); }); it('should destroy with slot', () => { @@ -84,7 +84,7 @@ describe('CarouselController', () => { carousel.destroy(); - expect(getEmblaApi()?.destroy).toBeCalled(); + expect(getEmblaApi()?.destroy).toHaveBeenCalled(); }); it('should get slide by index', () => { @@ -123,7 +123,7 @@ describe('CarouselController', () => { carousel.selectSlide(4); - expect(getEmblaApi()?.scrollTo).toBeCalledWith(4, false); + expect(getEmblaApi()?.scrollTo).toHaveBeenCalledWith(4, false); }); it('should not select non-existent slide', () => { @@ -135,7 +135,7 @@ describe('CarouselController', () => { carousel.selectSlide(11); // Should not call scrollTo because index is out of bounds. - expect(getEmblaApi()?.scrollTo).not.toBeCalled(); + expect(getEmblaApi()?.scrollTo).not.toHaveBeenCalled(); }); it('should dispatch select event', () => { @@ -150,7 +150,7 @@ describe('CarouselController', () => { getEmblaApi()?.slideNodes.mockReturnValue(children); callEmblaHandler(getEmblaApi(), 'select'); - expect(selectHandler).toBeCalledWith( + expect(selectHandler).toHaveBeenCalledWith( expect.objectContaining({ detail: { index: 6, @@ -174,7 +174,7 @@ describe('CarouselController', () => { callEmblaHandler(getEmblaApi(), 'select'); callEmblaHandler(getEmblaApi(), 'settle'); - expect(selectHandler).not.toBeCalled(); + expect(selectHandler).not.toHaveBeenCalled(); }); it('should honor creation options', () => { @@ -192,7 +192,7 @@ describe('CarouselController', () => { textDirection: 'rtl', }); - expect(EmblaCarousel).toBeCalledWith( + expect(EmblaCarousel).toHaveBeenCalledWith( root, { slides: children, @@ -241,8 +241,8 @@ describe('CarouselController', () => { expect(emblaApi).toBeTruthy(); carousel.setDragEnabled(false); - expect(emblaApi?.reInit).not.toBeCalled(); - expect(emblaApi?.destroy).not.toBeCalled(); + expect(emblaApi?.reInit).not.toHaveBeenCalled(); + expect(emblaApi?.destroy).not.toHaveBeenCalled(); }); it('should include wheel plugin when slides > 1', () => { @@ -251,7 +251,7 @@ describe('CarouselController', () => { const parent = createParent({ children: children }); new CarouselController(root, parent); - expect(EmblaCarousel).toBeCalledWith( + expect(EmblaCarousel).toHaveBeenCalledWith( root, expect.anything(), expect.arrayContaining([ @@ -269,7 +269,7 @@ describe('CarouselController', () => { new CarouselController(root, parent, { wheelScrolling: false }); // Verify WheelGesturesPlugin is NOT present - expect(EmblaCarousel).toBeCalledWith( + expect(EmblaCarousel).toHaveBeenCalledWith( root, expect.anything(), expect.not.arrayContaining([ @@ -286,7 +286,7 @@ describe('CarouselController', () => { const parent = createParent({ children: children }); new CarouselController(root, parent); - expect(EmblaCarousel).toBeCalledTimes(1); + expect(EmblaCarousel).toHaveBeenCalledTimes(1); const originalEmblaApi = getEmblaApi(); expect(originalEmblaApi).toBeTruthy(); @@ -297,12 +297,12 @@ describe('CarouselController', () => { callMutationHandler(); // Should call reInit instead of destroy/recreate - expect(originalEmblaApi?.reInit).toBeCalledWith({ + expect(originalEmblaApi?.reInit).toHaveBeenCalledWith({ slides: [...children, newChild], }); // Should still be same carousel instance (no new creation) - expect(EmblaCarousel).toBeCalledTimes(1); + expect(EmblaCarousel).toHaveBeenCalledTimes(1); }); it('should not recreate carousel when children have not changed', () => { @@ -311,7 +311,7 @@ describe('CarouselController', () => { const parent = createParent({ children: children }); new CarouselController(root, parent); - expect(EmblaCarousel).toBeCalledTimes(1); + expect(EmblaCarousel).toHaveBeenCalledTimes(1); const originalEmblaApi = getEmblaApi(); expect(originalEmblaApi).toBeTruthy(); @@ -319,10 +319,10 @@ describe('CarouselController', () => { originalEmblaApi?.slideNodes.mockReturnValue(children); callMutationHandler(); - expect(originalEmblaApi?.destroy).not.toBeCalled(); + expect(originalEmblaApi?.destroy).not.toHaveBeenCalled(); expect(getEmblaApi()).toBe(originalEmblaApi); - expect(EmblaCarousel).toBeCalledTimes(1); + expect(EmblaCarousel).toHaveBeenCalledTimes(1); }); it('should reinit carousel when children are added to slot', () => { @@ -332,7 +332,7 @@ describe('CarouselController', () => { new CarouselController(host, slot); - expect(EmblaCarousel).toBeCalledTimes(1); + expect(EmblaCarousel).toHaveBeenCalledTimes(1); const originalEmblaApi = getEmblaApi(); expect(originalEmblaApi).toBeTruthy(); @@ -344,11 +344,11 @@ describe('CarouselController', () => { slot.dispatchEvent(new Event('slotchange')); // Should call reInit instead of destroy/recreate - expect(originalEmblaApi?.reInit).toBeCalledWith({ + expect(originalEmblaApi?.reInit).toHaveBeenCalledWith({ slides: [...children, newChild], }); // Should still be same carousel instance (no new creation) - expect(EmblaCarousel).toBeCalledTimes(1); + expect(EmblaCarousel).toHaveBeenCalledTimes(1); }); }); diff --git a/tests/utils/find-best-media-time-index.test.ts b/tests/utils/find-best-media-time-index.test.ts index fcd45ccb..aa7fe092 100644 --- a/tests/utils/find-best-media-time-index.test.ts +++ b/tests/utils/find-best-media-time-index.test.ts @@ -2,7 +2,7 @@ import { describe, expect, it, vi } from 'vitest'; import { findBestMediaTimeIndex } from '../../src/utils/find-best-media-time-index'; import { ViewFolder } from '../../src/view/item'; -import { TestViewMedia } from '../test-utils'; +import { TestViewMedia } from '../view/test-utils'; describe('findBestMediaTimeIndex', () => { it('should handle non-media items', () => { diff --git a/tests/utils/fire-advanced-camera-card-event.test.ts b/tests/utils/fire-advanced-camera-card-event.test.ts index 73a8e404..56676402 100644 --- a/tests/utils/fire-advanced-camera-card-event.test.ts +++ b/tests/utils/fire-advanced-camera-card-event.test.ts @@ -10,7 +10,7 @@ describe('fireAdvancedCameraCardEvent', () => { element.addEventListener('advanced-camera-card:foo', handler); fireAdvancedCameraCardEvent(element, 'foo'); - expect(handler).toBeCalled(); + expect(handler).toHaveBeenCalled(); }); it('should fire event with data', () => { @@ -22,6 +22,6 @@ describe('fireAdvancedCameraCardEvent', () => { element.addEventListener('advanced-camera-card:foo', handler); fireAdvancedCameraCardEvent(element, 'foo', data); - expect(handler).toBeCalled(); + expect(handler).toHaveBeenCalled(); }); }); diff --git a/tests/utils/live-provider.test.ts b/tests/utils/live-provider.test.ts index 274ef7b1..ae938b05 100644 --- a/tests/utils/live-provider.test.ts +++ b/tests/utils/live-provider.test.ts @@ -6,7 +6,8 @@ import { isGo2RTCLiveProvider, liveProviderSupports2WayAudio, } from '../../src/utils/live-provider'; -import { createCameraConfig, createHASS } from '../test-utils'; +import { createCameraConfig } from '../config/test-utils'; +import { createHASS } from '../test-utils'; vi.mock('../../src/camera-manager/utils/go2rtc/audio'); diff --git a/tests/utils/media-actions.test.ts b/tests/utils/media-actions.test.ts index 3613e68d..d79ac8b2 100644 --- a/tests/utils/media-actions.test.ts +++ b/tests/utils/media-actions.test.ts @@ -14,7 +14,7 @@ import { import { ViewMediaType, type ViewItem } from '../../src/view/item'; import type { QueryResults } from '../../src/view/query-results'; import type { View } from '../../src/view/view'; -import { TestViewMedia } from '../test-utils'; +import { TestViewMedia } from '../view/test-utils'; describe('MediaActions', () => { describe('toggleReviewed', () => { diff --git a/tests/utils/media-info.test.ts b/tests/utils/media-info.test.ts index 10750b0a..dc9b2f31 100644 --- a/tests/utils/media-info.test.ts +++ b/tests/utils/media-info.test.ts @@ -89,7 +89,7 @@ describe('dispatchMediaVolumeChangeEvent', () => { div.addEventListener('advanced-camera-card:media:volumechange', handler); dispatchMediaVolumeChangeEvent(div); - expect(handler).toBeCalled(); + expect(handler).toHaveBeenCalled(); }); }); @@ -101,7 +101,7 @@ describe('dispatchMediaPlayEvent', () => { div.addEventListener('advanced-camera-card:media:play', handler); dispatchMediaPlayEvent(div); - expect(handler).toBeCalled(); + expect(handler).toHaveBeenCalled(); }); }); @@ -113,7 +113,7 @@ describe('dispatchMediaPauseEvent', () => { div.addEventListener('advanced-camera-card:media:pause', handler); dispatchMediaPauseEvent(div); - expect(handler).toBeCalled(); + expect(handler).toHaveBeenCalled(); }); }); diff --git a/tests/utils/ptz.test.ts b/tests/utils/ptz.test.ts index c0d3607c..a4da3721 100644 --- a/tests/utils/ptz.test.ts +++ b/tests/utils/ptz.test.ts @@ -11,12 +11,8 @@ import { import { QueryResults } from '../../src/view/query-results'; import * as targetId from '../../src/view/target-id'; import { IMAGE_VIEW_TARGET_ID_SENTINEL } from '../../src/view/target-id'; -import { - createCameraManager, - createStore, - createView, - TestViewMedia, -} from '../test-utils'; +import { createCameraManager, createStore } from '../camera-manager/test-utils'; +import { createView, TestViewMedia } from '../view/test-utils'; describe('getPTZTarget', () => { describe('in a viewer view', () => { diff --git a/tests/utils/retry-timer.test.ts b/tests/utils/retry-timer.test.ts index 1f4d036a..09dc5614 100644 --- a/tests/utils/retry-timer.test.ts +++ b/tests/utils/retry-timer.test.ts @@ -17,9 +17,9 @@ describe('RetryTimer', () => { timer.schedule(cb); vi.advanceTimersByTime(999); - expect(cb).not.toBeCalled(); + expect(cb).not.toHaveBeenCalled(); vi.advanceTimersByTime(1); - expect(cb).toBeCalledTimes(1); + expect(cb).toHaveBeenCalledTimes(1); }); it('should advance the counter on schedule by default', () => { @@ -57,9 +57,9 @@ describe('RetryTimer', () => { // Counter is 1, delay should be base * 2^1 = 2 seconds. vi.advanceTimersByTime(1999); - expect(cb).not.toBeCalled(); + expect(cb).not.toHaveBeenCalled(); vi.advanceTimersByTime(1); - expect(cb).toBeCalledTimes(1); + expect(cb).toHaveBeenCalledTimes(1); }); it('should cancel a pending callback', () => { @@ -71,7 +71,7 @@ describe('RetryTimer', () => { timer.schedule(cb); timer.cancel(); vi.advanceTimersByTime(10_000); - expect(cb).not.toBeCalled(); + expect(cb).not.toHaveBeenCalled(); }); it('should reset both the timer and the counter', () => { @@ -91,7 +91,7 @@ describe('RetryTimer', () => { expect(timer.isRunning()).toBe(false); vi.advanceTimersByTime(10_000); - expect(cb).not.toBeCalled(); + expect(cb).not.toHaveBeenCalled(); }); it('should report running state', () => { @@ -133,9 +133,9 @@ describe('RetryTimer', () => { timer.advance(); timer.schedule(cb); vi.advanceTimersByTime(29_999); - expect(cb).not.toBeCalled(); + expect(cb).not.toHaveBeenCalled(); vi.advanceTimersByTime(1); - expect(cb).toBeCalledTimes(1); + expect(cb).toHaveBeenCalledTimes(1); }); it('should accept a plain number as shorthand for a fixed delay', () => { @@ -146,9 +146,9 @@ describe('RetryTimer', () => { timer.advance(); timer.schedule(cb); vi.advanceTimersByTime(29_999); - expect(cb).not.toBeCalled(); + expect(cb).not.toHaveBeenCalled(); vi.advanceTimersByTime(1); - expect(cb).toBeCalledTimes(1); + expect(cb).toHaveBeenCalledTimes(1); }); describe('setOptions', () => { @@ -171,9 +171,9 @@ describe('RetryTimer', () => { }); timer.schedule(cb); vi.advanceTimersByTime(9_999); - expect(cb).not.toBeCalled(); + expect(cb).not.toHaveBeenCalled(); vi.advanceTimersByTime(1); - expect(cb).toBeCalledTimes(1); + expect(cb).toHaveBeenCalledTimes(1); }); it('should preserve the attempt counter and any pending callback', () => { diff --git a/tests/utils/screenshot.test.ts b/tests/utils/screenshot.test.ts index a6e158b2..e115be71 100644 --- a/tests/utils/screenshot.test.ts +++ b/tests/utils/screenshot.test.ts @@ -8,7 +8,7 @@ import { } from '../../src/utils/screenshot'; import { QueryResults } from '../../src/view/query-results'; import { View } from '../../src/view/view'; -import { createView, TestViewMedia } from '../test-utils'; +import { createView, TestViewMedia } from '../view/test-utils'; // @vitest-environment jsdom describe('screenshotVideo', () => { diff --git a/tests/utils/scroll.test.ts b/tests/utils/scroll.test.ts index 59ddc1b9..c226746c 100644 --- a/tests/utils/scroll.test.ts +++ b/tests/utils/scroll.test.ts @@ -27,7 +27,7 @@ describe('scrollIntoView', () => { scrollIntoView(element, options); - expect(computeScroll).toBeCalledWith(element, options); + expect(computeScroll).toHaveBeenCalledWith(element, options); expect(element.scrollTop).toBe(42); expect(element.scrollLeft).toBe(142); }); diff --git a/tests/utils/timer.test.ts b/tests/utils/timer.test.ts index 52356014..af5d4f6c 100644 --- a/tests/utils/timer.test.ts +++ b/tests/utils/timer.test.ts @@ -23,12 +23,12 @@ describe('Timer', () => { timer.start(10, handler); expect(timer.isRunning()).toBeTruthy(); - expect(handler).not.toBeCalled(); + expect(handler).not.toHaveBeenCalled(); vi.runOnlyPendingTimers(); expect(timer.isRunning()).toBeFalsy(); - expect(handler).toBeCalled(); + expect(handler).toHaveBeenCalled(); }); it('should not fire when stopped', () => { @@ -37,14 +37,14 @@ describe('Timer', () => { timer.start(10, handler); expect(timer.isRunning()).toBeTruthy(); - expect(handler).not.toBeCalled(); + expect(handler).not.toHaveBeenCalled(); timer.stop(); vi.runOnlyPendingTimers(); expect(timer.isRunning()).toBeFalsy(); - expect(handler).not.toBeCalled(); + expect(handler).not.toHaveBeenCalled(); }); it('should fire repeatedly when started', () => { @@ -53,17 +53,17 @@ describe('Timer', () => { timer.startRepeated(10, handler); expect(timer.isRunning()).toBeTruthy(); - expect(handler).not.toBeCalled(); + expect(handler).not.toHaveBeenCalled(); vi.runOnlyPendingTimers(); expect(timer.isRunning()).toBeTruthy(); - expect(handler).toBeCalledTimes(1); + expect(handler).toHaveBeenCalledTimes(1); vi.runOnlyPendingTimers(); expect(timer.isRunning()).toBeTruthy(); - expect(handler).toBeCalledTimes(2); + expect(handler).toHaveBeenCalledTimes(2); }); it('should not fire repeatedly when stopped', () => { @@ -72,13 +72,13 @@ describe('Timer', () => { timer.startRepeated(10, handler); expect(timer.isRunning()).toBeTruthy(); - expect(handler).not.toBeCalled(); + expect(handler).not.toHaveBeenCalled(); timer.stop(); vi.runOnlyPendingTimers(); expect(timer.isRunning()).toBeFalsy(); - expect(handler).not.toBeCalled(); + expect(handler).not.toHaveBeenCalled(); }); }); diff --git a/tests/utils/zod/deep-remove-defaults.test.ts b/tests/utils/zod/deep-remove-defaults.test.ts index a86cbe14..88807d76 100644 --- a/tests/utils/zod/deep-remove-defaults.test.ts +++ b/tests/utils/zod/deep-remove-defaults.test.ts @@ -326,7 +326,7 @@ describe('deepNoDefaults', () => { value: () => ({}) as unknown as z.core.$ZodType, }); - expect(() => deepRemoveDefaults(schema)).toThrowError( + expect(() => deepRemoveDefaults(schema)).toThrow( 'deepRemoveDefaults supports full zod schemas only', ); }); diff --git a/tests/view/item-classifier.test.ts b/tests/view/item-classifier.test.ts index 5b8e0151..cedd9e5f 100644 --- a/tests/view/item-classifier.test.ts +++ b/tests/view/item-classifier.test.ts @@ -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', () => { diff --git a/tests/view/item.test.ts b/tests/view/item.test.ts index da3bdb98..5861550b 100644 --- a/tests/view/item.test.ts +++ b/tests/view/item.test.ts @@ -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(() => { diff --git a/tests/view/query-results.test.ts b/tests/view/query-results.test.ts index 02edba66..f9cb1c67 100644 --- a/tests/view/query-results.test.ts +++ b/tests/view/query-results.test.ts @@ -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(() => { diff --git a/tests/view/substream.test.ts b/tests/view/substream.test.ts index b9d42037..023c45a6 100644 --- a/tests/view/substream.test.ts +++ b/tests/view/substream.test.ts @@ -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', () => { diff --git a/tests/view/target-id.test.ts b/tests/view/target-id.test.ts index be622bec..46d02a2d 100644 --- a/tests/view/target-id.test.ts +++ b/tests/view/target-id.test.ts @@ -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', () => { diff --git a/tests/view/test-utils.ts b/tests/view/test-utils.ts new file mode 100644 index 00000000..188bb8e6 --- /dev/null +++ b/tests/view/test-utils.ts @@ -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): View => { + return new View({ + view: 'live', + camera: 'camera', + ...options, + }); +}; + +export const createViewWithMedia = (options?: Partial): View => { + const media = generateViewMediaArray({ count: 5 }); + return createView({ + queryResults: new QueryResults({ + results: media, + selectedIndex: 0, + }), + ...options, + }); +}; + +export const createEventQuery = ( + cameraID: string, + options?: Partial, +): EventQuery => ({ + source: QuerySource.Camera, + type: QueryType.Event, + cameraIDs: new Set([cameraID]), + ...options, +}); + +export const createRecordingQuery = ( + cameraID: string, + options?: Partial, +): RecordingQuery => ({ + source: QuerySource.Camera, + type: QueryType.Recording, + cameraIDs: new Set([cameraID]), + ...options, +}); + +export const createReviewQuery = ( + cameraID: string, + options?: Partial, +): 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; diff --git a/tests/view/unified-query-builder.test.ts b/tests/view/unified-query-builder.test.ts index 739f5dde..40370dbb 100644 --- a/tests/view/unified-query-builder.test.ts +++ b/tests/view/unified-query-builder.test.ts @@ -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 = ( diff --git a/tests/view/unified-query-runner.test.ts b/tests/view/unified-query-runner.test.ts index 886fe556..122d307a 100644 --- a/tests/view/unified-query-runner.test.ts +++ b/tests/view/unified-query-runner.test.ts @@ -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', () => { diff --git a/tests/view/unified-query-transformer.test.ts b/tests/view/unified-query-transformer.test.ts index 09589878..1847f71a 100644 --- a/tests/view/unified-query-transformer.test.ts +++ b/tests/view/unified-query-transformer.test.ts @@ -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(); diff --git a/tests/view/unified-query.test.ts b/tests/view/unified-query.test.ts index b66093cd..01fc1c72 100644 --- a/tests/view/unified-query.test.ts +++ b/tests/view/unified-query.test.ts @@ -10,7 +10,7 @@ import { createReviewQuery, isEventQuery, isFolderQuery, -} from '../test-utils'; +} from './test-utils'; describe('UnifiedQuery', () => { describe('Node Management', () => { diff --git a/tests/view/utils/query-filter.test.ts b/tests/view/utils/query-filter.test.ts index 951349af..1a1c6bec 100644 --- a/tests/view/utils/query-filter.test.ts +++ b/tests/view/utils/query-filter.test.ts @@ -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', () => { diff --git a/tests/view/utils/resolve-default.test.ts b/tests/view/utils/resolve-default.test.ts index 1a26515c..3a7911b0 100644 --- a/tests/view/utils/resolve-default.test.ts +++ b/tests/view/utils/resolve-default.test.ts @@ -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', () => { diff --git a/tests/view/view-support.test.ts b/tests/view/view-support.test.ts index d027aaaf..6a12c831 100644 --- a/tests/view/view-support.test.ts +++ b/tests/view/view-support.test.ts @@ -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', () => { diff --git a/tests/view/view.test.ts b/tests/view/view.test.ts index ddd6adee..5886139a 100644 --- a/tests/view/view.test.ts +++ b/tests/view/view.test.ts @@ -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', () => {