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

This commit is contained in:
Dermot Duffy
2026-07-26 16:29:53 -07:00
committed by GitHub
parent 8d44fcecf1
commit ad89aa9538
234 changed files with 2730 additions and 2516 deletions
+4 -4
View File
@@ -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);
});
});
+1 -1
View File
@@ -600,6 +600,6 @@ describe('stopEventFromActivatingCardWideActions', () => {
it('should stop event from propogating', () => {
const event = mock<Event>();
stopEventFromActivatingCardWideActions(event);
expect(event.stopPropagation).toBeCalled();
expect(event.stopPropagation).toHaveBeenCalled();
});
});
+2 -2
View File
@@ -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 });
});
});
+1 -1
View File
@@ -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', () => {
@@ -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]);
});
});
+2 -2
View File
@@ -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');
});
});
+5 -5
View File
@@ -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');
});
});
+20 -20
View File
@@ -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);
});
});
@@ -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', () => {
@@ -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();
});
});
+2 -1
View File
@@ -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');
+1 -1
View File
@@ -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', () => {
+3 -3
View File
@@ -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();
});
});
+2 -6
View File
@@ -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', () => {
+12 -12
View File
@@ -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', () => {
+1 -1
View File
@@ -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', () => {
+1 -1
View File
@@ -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);
});
+9 -9
View File
@@ -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();
});
});
+1 -1
View File
@@ -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',
);
});