Complete carousel refactor.
Reduces one layer of DOM nesting for simplication, uses the latest Embla version, unittests for everything.
This commit is contained in:
@@ -0,0 +1,219 @@
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
import { AutoLazyLoad } from '../../../../../src/utils/embla/plugins/auto-lazy-load/auto-lazy-load';
|
||||
import {
|
||||
callEmblaHandler,
|
||||
callVisibilityHandler,
|
||||
createEmblaApiInstance,
|
||||
createTestEmblaOptionHandler,
|
||||
createTestSlideNodes,
|
||||
} from '../../test-utils';
|
||||
|
||||
// @vitest-environment jsdom
|
||||
describe('AutoLazyLoad', () => {
|
||||
it('should construct', () => {
|
||||
const plugin = AutoLazyLoad();
|
||||
expect(plugin.name).toBe('autoLazyLoad');
|
||||
});
|
||||
|
||||
it('should destroy', () => {
|
||||
const plugin = AutoLazyLoad({
|
||||
lazyLoadCallback: vi.fn(),
|
||||
lazyUnloadCallback: vi.fn(),
|
||||
});
|
||||
const emblaApi = createEmblaApiInstance();
|
||||
plugin.init(emblaApi, createTestEmblaOptionHandler());
|
||||
plugin.destroy();
|
||||
|
||||
expect(emblaApi.off).toBeCalledWith('init', expect.anything());
|
||||
expect(emblaApi.off).toBeCalledWith('select', expect.anything());
|
||||
});
|
||||
|
||||
it('should do nothing without callbacks', () => {
|
||||
const plugin = AutoLazyLoad({
|
||||
// No callbacks provided.
|
||||
});
|
||||
|
||||
const children = createTestSlideNodes();
|
||||
const emblaApi = createEmblaApiInstance({ slideNodes: children });
|
||||
|
||||
plugin.init(emblaApi, createTestEmblaOptionHandler());
|
||||
expect(emblaApi.on).not.toBeCalled();
|
||||
|
||||
plugin.destroy();
|
||||
expect(emblaApi.off).not.toBeCalled();
|
||||
});
|
||||
|
||||
it('should lazy load single slide on select', () => {
|
||||
const lazyLoadCallback = vi.fn();
|
||||
const plugin = AutoLazyLoad({
|
||||
lazyLoadCallback: lazyLoadCallback,
|
||||
});
|
||||
|
||||
const children = createTestSlideNodes();
|
||||
const emblaApi = createEmblaApiInstance({ slideNodes: children });
|
||||
plugin.init(emblaApi, createTestEmblaOptionHandler());
|
||||
|
||||
expect(emblaApi.on).toBeCalledWith('init', expect.anything());
|
||||
expect(emblaApi.on).toBeCalledWith('select', expect.anything());
|
||||
|
||||
callEmblaHandler(emblaApi, 'init');
|
||||
expect(lazyLoadCallback).toBeCalledWith(0, children[0]);
|
||||
|
||||
callEmblaHandler(emblaApi, 'select');
|
||||
|
||||
// The select call will not re-lazyload the same slide.
|
||||
expect(lazyLoadCallback).toBeCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should lazy load multiple slides on select', () => {
|
||||
const lazyLoadCallback = vi.fn();
|
||||
const plugin = AutoLazyLoad({
|
||||
lazyLoadCallback: lazyLoadCallback,
|
||||
lazyLoadCount: 3,
|
||||
});
|
||||
|
||||
const children = createTestSlideNodes();
|
||||
const emblaApi = createEmblaApiInstance({
|
||||
slideNodes: children,
|
||||
selectedScrollSnap: 5,
|
||||
});
|
||||
plugin.init(emblaApi, createTestEmblaOptionHandler());
|
||||
|
||||
callEmblaHandler(emblaApi, 'select');
|
||||
for (let i = 3; i <= 8; ++i) {
|
||||
expect(lazyLoadCallback).toBeCalledWith(i, children[i]);
|
||||
}
|
||||
});
|
||||
|
||||
it('should lazy unload on select', () => {
|
||||
const lazyUnloadCallback = vi.fn();
|
||||
const plugin = AutoLazyLoad({
|
||||
lazyLoadCallback: vi.fn(),
|
||||
lazyLoadCount: 3,
|
||||
lazyUnloadCallback: lazyUnloadCallback,
|
||||
lazyUnloadCondition: 'all',
|
||||
});
|
||||
|
||||
const children = createTestSlideNodes();
|
||||
const emblaApi = createEmblaApiInstance({
|
||||
selectedScrollSnap: 5,
|
||||
slideNodes: children,
|
||||
});
|
||||
plugin.init(emblaApi, createTestEmblaOptionHandler());
|
||||
|
||||
callEmblaHandler(emblaApi, 'select');
|
||||
|
||||
// First call will not unload anything, since it was not lazy loaded.
|
||||
expect(lazyUnloadCallback).not.toBeCalled();
|
||||
|
||||
vi.mocked(emblaApi.previousScrollSnap).mockReturnValue(5);
|
||||
callEmblaHandler(emblaApi, 'select');
|
||||
|
||||
// Second call should lazy unload the previous slide.
|
||||
expect(lazyUnloadCallback).toBeCalledWith(5, children[5]);
|
||||
});
|
||||
|
||||
it('should lazy load on visibility', () => {
|
||||
vi.spyOn(global.document, 'addEventListener');
|
||||
|
||||
const lazyLoadCallback = vi.fn();
|
||||
const plugin = AutoLazyLoad({
|
||||
lazyLoadCallback: lazyLoadCallback,
|
||||
});
|
||||
|
||||
const children = createTestSlideNodes();
|
||||
const emblaApi = createEmblaApiInstance({
|
||||
slideNodes: children,
|
||||
});
|
||||
plugin.init(emblaApi, createTestEmblaOptionHandler());
|
||||
|
||||
Object.defineProperty(document, 'visibilityState', {
|
||||
value: 'visible',
|
||||
writable: true,
|
||||
});
|
||||
callVisibilityHandler();
|
||||
expect(lazyLoadCallback).toBeCalledWith(0, children[0]);
|
||||
});
|
||||
|
||||
it('should lazy unload on visibility', () => {
|
||||
vi.spyOn(global.document, 'addEventListener');
|
||||
|
||||
const lazyUnloadCallback = vi.fn();
|
||||
|
||||
const plugin = AutoLazyLoad({
|
||||
lazyLoadCallback: vi.fn(),
|
||||
lazyUnloadCallback: lazyUnloadCallback,
|
||||
lazyUnloadCondition: 'all',
|
||||
});
|
||||
|
||||
const children = createTestSlideNodes();
|
||||
const emblaApi = createEmblaApiInstance({
|
||||
slideNodes: children,
|
||||
});
|
||||
plugin.init(emblaApi, createTestEmblaOptionHandler());
|
||||
|
||||
Object.defineProperty(document, 'visibilityState', {
|
||||
value: 'visible',
|
||||
writable: true,
|
||||
});
|
||||
callVisibilityHandler();
|
||||
|
||||
Object.defineProperty(document, 'visibilityState', {
|
||||
value: 'hidden',
|
||||
writable: true,
|
||||
});
|
||||
callVisibilityHandler();
|
||||
expect(lazyUnloadCallback).toBeCalledWith(0, children[0]);
|
||||
});
|
||||
|
||||
it('should not lazy unload on visibility without a callback', () => {
|
||||
vi.spyOn(global.document, 'addEventListener');
|
||||
|
||||
const lazyLoadCallback = vi.fn();
|
||||
const plugin = AutoLazyLoad({
|
||||
lazyLoadCallback: lazyLoadCallback,
|
||||
lazyUnloadCondition: 'all',
|
||||
// No lazy unload callback.
|
||||
});
|
||||
const children = createTestSlideNodes();
|
||||
const emblaApi = createEmblaApiInstance({
|
||||
slideNodes: children,
|
||||
});
|
||||
plugin.init(emblaApi, createTestEmblaOptionHandler());
|
||||
|
||||
Object.defineProperty(document, 'visibilityState', {
|
||||
value: 'visible',
|
||||
writable: true,
|
||||
});
|
||||
callVisibilityHandler();
|
||||
expect(lazyLoadCallback).toBeCalledTimes(1);
|
||||
|
||||
Object.defineProperty(document, 'visibilityState', {
|
||||
value: 'hidden',
|
||||
writable: true,
|
||||
});
|
||||
callVisibilityHandler();
|
||||
expect(lazyLoadCallback).toBeCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should not lazy load or unload on visibility when no callback provided', () => {
|
||||
vi.spyOn(global.document, 'addEventListener');
|
||||
|
||||
const plugin = AutoLazyLoad({
|
||||
lazyUnloadCondition: 'all',
|
||||
// No callbacks provided.
|
||||
});
|
||||
|
||||
const children = createTestSlideNodes();
|
||||
const emblaApi = createEmblaApiInstance({
|
||||
slideNodes: children,
|
||||
});
|
||||
plugin.init(emblaApi, createTestEmblaOptionHandler());
|
||||
|
||||
Object.defineProperty(document, 'visibilityState', {
|
||||
value: 'visible',
|
||||
writable: true,
|
||||
});
|
||||
callVisibilityHandler();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,304 @@
|
||||
import { beforeAll, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { FrigateCardMediaPlayer } from '../../../../../src/types';
|
||||
import {
|
||||
AutoMediaActions,
|
||||
AutoMediaActionsOptionsType,
|
||||
AutoMediaActionsType,
|
||||
} from '../../../../../src/utils/embla/plugins/auto-media-actions/auto-media-actions';
|
||||
import { dispatchExistingMediaLoadedInfoAsEvent } from '../../../../../src/utils/media-info';
|
||||
import {
|
||||
IntersectionObserverMock,
|
||||
createMediaLoadedInfo,
|
||||
createParent,
|
||||
} from '../../../../test-utils';
|
||||
import {
|
||||
callEmblaHandler,
|
||||
callIntersectionHandler,
|
||||
callVisibilityHandler,
|
||||
createEmblaApiInstance,
|
||||
createTestEmblaOptionHandler,
|
||||
createTestSlideNodes,
|
||||
} from '../../test-utils';
|
||||
|
||||
const getPlayer = (
|
||||
element: HTMLElement,
|
||||
selector: string,
|
||||
): (HTMLElement & FrigateCardMediaPlayer) | null => {
|
||||
return element.querySelector(selector);
|
||||
};
|
||||
|
||||
const createPlayerSlideNodes = (n = 10): HTMLElement[] => {
|
||||
const slides = createTestSlideNodes({ n: n });
|
||||
for (const slide of slides) {
|
||||
const player = document.createElement('video');
|
||||
|
||||
player['play'] = vi.fn();
|
||||
player['pause'] = vi.fn();
|
||||
player['mute'] = vi.fn();
|
||||
player['unmute'] = vi.fn();
|
||||
player['isMuted'] = vi.fn().mockReturnValue(true);
|
||||
player['seek'] = vi.fn();
|
||||
player['getScreenshotURL'] = vi.fn();
|
||||
player['setControls'] = vi.fn();
|
||||
player['isPaused'] = vi.fn();
|
||||
|
||||
slide.appendChild(player);
|
||||
}
|
||||
return slides;
|
||||
};
|
||||
|
||||
const createPlugin = (options?: AutoMediaActionsOptionsType): AutoMediaActionsType => {
|
||||
return AutoMediaActions({
|
||||
playerSelector: 'video',
|
||||
autoPlayCondition: 'all',
|
||||
autoUnmuteCondition: 'all',
|
||||
autoPauseCondition: 'all',
|
||||
autoMuteCondition: 'all',
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
// @vitest-environment jsdom
|
||||
describe('AutoMediaActions', () => {
|
||||
beforeAll(() => {
|
||||
vi.stubGlobal('IntersectionObserver', IntersectionObserverMock);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should construct', () => {
|
||||
const plugin = AutoMediaActions();
|
||||
expect(plugin.name).toBe('autoMediaActions');
|
||||
});
|
||||
|
||||
it('should init without any conditions', () => {
|
||||
const plugin = AutoMediaActions();
|
||||
|
||||
const parent = createParent();
|
||||
const addEventListener = vi.fn();
|
||||
parent.addEventListener = addEventListener;
|
||||
const emblaApi = createEmblaApiInstance({ containerNode: parent });
|
||||
|
||||
plugin.init(emblaApi, createTestEmblaOptionHandler());
|
||||
|
||||
expect(emblaApi.on).toBeCalledWith('destroy', expect.anything());
|
||||
expect(emblaApi.on).not.toBeCalledWith('select', expect.anything());
|
||||
expect(addEventListener).not.toBeCalled();
|
||||
});
|
||||
|
||||
it('should destroy', () => {
|
||||
const plugin = createPlugin();
|
||||
const parent = createParent();
|
||||
const removeEventListener = vi.fn();
|
||||
parent.removeEventListener = removeEventListener;
|
||||
const emblaApi = createEmblaApiInstance({ containerNode: parent });
|
||||
|
||||
plugin.init(emblaApi, createTestEmblaOptionHandler());
|
||||
|
||||
plugin.destroy();
|
||||
|
||||
expect(emblaApi.off).toBeCalledWith('destroy', expect.anything());
|
||||
expect(emblaApi.off).toBeCalledWith('select', expect.anything());
|
||||
expect(removeEventListener).toBeCalled();
|
||||
});
|
||||
|
||||
it('should destroy without any conditions', () => {
|
||||
const plugin = AutoMediaActions();
|
||||
const parent = createParent();
|
||||
const removeEventListener = vi.fn();
|
||||
parent.removeEventListener = removeEventListener;
|
||||
const emblaApi = createEmblaApiInstance({ containerNode: parent });
|
||||
|
||||
plugin.init(emblaApi, createTestEmblaOptionHandler());
|
||||
|
||||
plugin.destroy();
|
||||
|
||||
expect(emblaApi.off).toBeCalledWith('destroy', expect.anything());
|
||||
expect(emblaApi.off).not.toBeCalledWith('select', expect.anything());
|
||||
expect(removeEventListener).not.toBeCalled();
|
||||
});
|
||||
|
||||
it('should mute and pause on destroy', () => {
|
||||
const plugin = createPlugin();
|
||||
const children = createPlayerSlideNodes();
|
||||
const parent = createParent({ children: children });
|
||||
const emblaApi = createEmblaApiInstance({
|
||||
slideNodes: children,
|
||||
containerNode: parent,
|
||||
selectedScrollSnap: 5,
|
||||
});
|
||||
|
||||
plugin.init(emblaApi, createTestEmblaOptionHandler());
|
||||
callEmblaHandler(emblaApi, 'destroy');
|
||||
|
||||
expect(getPlayer(children[5], 'video')?.pause).toBeCalled();
|
||||
expect(getPlayer(children[5], 'video')?.mute).toBeCalled();
|
||||
});
|
||||
|
||||
it('should play and unmute on media load', () => {
|
||||
const plugin = createPlugin();
|
||||
const children = createPlayerSlideNodes();
|
||||
const parent = createParent({ children: children });
|
||||
const emblaApi = createEmblaApiInstance({
|
||||
slideNodes: children,
|
||||
containerNode: parent,
|
||||
selectedScrollSnap: 5,
|
||||
});
|
||||
|
||||
plugin.init(emblaApi, createTestEmblaOptionHandler());
|
||||
dispatchExistingMediaLoadedInfoAsEvent(parent, createMediaLoadedInfo());
|
||||
|
||||
expect(getPlayer(children[5], 'video')?.play).toBeCalled();
|
||||
expect(getPlayer(children[5], 'video')?.unmute).toBeCalled();
|
||||
});
|
||||
|
||||
it('should not play or unmute on media load when player selecter not provided', () => {
|
||||
const plugin = createPlugin({ playerSelector: undefined });
|
||||
const children = createPlayerSlideNodes();
|
||||
const parent = createParent({ children: children });
|
||||
const emblaApi = createEmblaApiInstance({
|
||||
slideNodes: children,
|
||||
containerNode: parent,
|
||||
selectedScrollSnap: 5,
|
||||
});
|
||||
|
||||
plugin.init(emblaApi, createTestEmblaOptionHandler());
|
||||
dispatchExistingMediaLoadedInfoAsEvent(parent, createMediaLoadedInfo());
|
||||
|
||||
expect(getPlayer(children[5], 'video')?.play).not.toBeCalled();
|
||||
expect(getPlayer(children[5], 'video')?.unmute).not.toBeCalled();
|
||||
});
|
||||
|
||||
it('should pause and mute previous on select', () => {
|
||||
const plugin = createPlugin();
|
||||
const children = createPlayerSlideNodes();
|
||||
const emblaApi = createEmblaApiInstance({
|
||||
slideNodes: children,
|
||||
previousScrollSnap: 4,
|
||||
});
|
||||
|
||||
plugin.init(emblaApi, createTestEmblaOptionHandler());
|
||||
callEmblaHandler(emblaApi, 'select');
|
||||
|
||||
expect(getPlayer(children[4], 'video')?.pause).toBeCalled();
|
||||
expect(getPlayer(children[4], 'video')?.mute).toBeCalled();
|
||||
});
|
||||
|
||||
it('should play and unmute on visibility change to visible', () => {
|
||||
vi.spyOn(global.document, 'addEventListener');
|
||||
|
||||
const plugin = createPlugin();
|
||||
const children = createPlayerSlideNodes();
|
||||
const emblaApi = createEmblaApiInstance({
|
||||
slideNodes: children,
|
||||
selectedScrollSnap: 5,
|
||||
});
|
||||
|
||||
plugin.init(emblaApi, createTestEmblaOptionHandler());
|
||||
|
||||
Object.defineProperty(document, 'visibilityState', {
|
||||
value: 'visible',
|
||||
writable: true,
|
||||
});
|
||||
callVisibilityHandler();
|
||||
|
||||
expect(getPlayer(children[5], 'video')?.play).toBeCalled();
|
||||
expect(getPlayer(children[5], 'video')?.unmute).toBeCalled();
|
||||
});
|
||||
|
||||
it('should pause and unmute on visibility change to hidden', () => {
|
||||
vi.spyOn(global.document, 'addEventListener');
|
||||
|
||||
const plugin = createPlugin();
|
||||
const children = createPlayerSlideNodes();
|
||||
const emblaApi = createEmblaApiInstance({
|
||||
slideNodes: children,
|
||||
});
|
||||
|
||||
plugin.init(emblaApi, createTestEmblaOptionHandler());
|
||||
|
||||
Object.defineProperty(document, 'visibilityState', {
|
||||
value: 'hidden',
|
||||
writable: true,
|
||||
});
|
||||
callVisibilityHandler();
|
||||
|
||||
for (const child of children) {
|
||||
expect(getPlayer(child, 'video')?.pause).toBeCalled();
|
||||
expect(getPlayer(child, 'video')?.mute).toBeCalled();
|
||||
}
|
||||
});
|
||||
|
||||
describe('should take no action on visibility change without callbacks', () => {
|
||||
it.each([['visible' as const], ['hidden' as const]])(
|
||||
'%s',
|
||||
(visibilityState: 'visible' | 'hidden') => {
|
||||
vi.spyOn(global.document, 'addEventListener');
|
||||
|
||||
const plugin = AutoMediaActions();
|
||||
const children = createPlayerSlideNodes();
|
||||
const emblaApi = createEmblaApiInstance({
|
||||
slideNodes: children,
|
||||
});
|
||||
|
||||
plugin.init(emblaApi, createTestEmblaOptionHandler());
|
||||
|
||||
Object.defineProperty(document, 'visibilityState', {
|
||||
value: visibilityState,
|
||||
writable: true,
|
||||
});
|
||||
callVisibilityHandler();
|
||||
|
||||
for (const child of children) {
|
||||
expect(getPlayer(child, 'video')?.play).not.toBeCalled();
|
||||
expect(getPlayer(child, 'video')?.pause).not.toBeCalled();
|
||||
expect(getPlayer(child, 'video')?.mute).not.toBeCalled();
|
||||
expect(getPlayer(child, 'video')?.unmute).not.toBeCalled();
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it('should play and unmute on intersection', () => {
|
||||
const plugin = createPlugin();
|
||||
const children = createPlayerSlideNodes();
|
||||
const emblaApi = createEmblaApiInstance({
|
||||
slideNodes: children,
|
||||
selectedScrollSnap: 5,
|
||||
});
|
||||
|
||||
plugin.init(emblaApi, createTestEmblaOptionHandler());
|
||||
|
||||
// Intersection observer always calls handler on creation (and we ignore
|
||||
// these first calls).
|
||||
callIntersectionHandler(true);
|
||||
callIntersectionHandler(true);
|
||||
|
||||
expect(getPlayer(children[5], 'video')?.play).toBeCalled();
|
||||
expect(getPlayer(children[5], 'video')?.unmute).toBeCalled();
|
||||
});
|
||||
|
||||
it('should pause and mute on intersection', () => {
|
||||
vi.spyOn(global.document, 'addEventListener');
|
||||
|
||||
const plugin = createPlugin();
|
||||
const children = createPlayerSlideNodes();
|
||||
const emblaApi = createEmblaApiInstance({
|
||||
slideNodes: children,
|
||||
});
|
||||
|
||||
plugin.init(emblaApi, createTestEmblaOptionHandler());
|
||||
|
||||
// Intersection observer always calls handler on creation (and we ignore
|
||||
// these first calls).
|
||||
callIntersectionHandler(true);
|
||||
callIntersectionHandler(false);
|
||||
|
||||
for (const child of children) {
|
||||
expect(getPlayer(child, 'video')?.pause).toBeCalled();
|
||||
expect(getPlayer(child, 'video')?.mute).toBeCalled();
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,92 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import AutoMediaLoadedInfo from '../../../../../src/utils/embla/plugins/auto-media-loaded-info/auto-media-loaded-info';
|
||||
import {
|
||||
dispatchExistingMediaLoadedInfoAsEvent,
|
||||
dispatchMediaUnloadedEvent,
|
||||
} from '../../../../../src/utils/media-info';
|
||||
import { createMediaLoadedInfo, createParent } from '../../../../test-utils';
|
||||
import {
|
||||
callEmblaHandler,
|
||||
createEmblaApiInstance,
|
||||
createTestEmblaOptionHandler,
|
||||
createTestSlideNodes,
|
||||
} from '../../test-utils';
|
||||
|
||||
// @vitest-environment jsdom
|
||||
describe('AutoMediaLoadedInfo', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should construct', () => {
|
||||
const plugin = AutoMediaLoadedInfo();
|
||||
expect(plugin.name).toBe('autoMediaLoadedInfo');
|
||||
});
|
||||
|
||||
it('should destroy', () => {
|
||||
const plugin = AutoMediaLoadedInfo();
|
||||
const emblaApi = createEmblaApiInstance();
|
||||
plugin.init(emblaApi, createTestEmblaOptionHandler());
|
||||
plugin.destroy();
|
||||
|
||||
expect(emblaApi.off).toBeCalledWith('init', expect.anything());
|
||||
expect(emblaApi.off).toBeCalledWith('select', expect.anything());
|
||||
});
|
||||
|
||||
describe('should correctly propogate media load/unload depending on whether media is currently selected', () => {
|
||||
it.each([
|
||||
['loaded' as const, true],
|
||||
['unloaded' as const, true],
|
||||
['loaded' as const, false],
|
||||
['unloaded' as const, false],
|
||||
])('%s', (type: string, selected: boolean) => {
|
||||
const plugin = AutoMediaLoadedInfo();
|
||||
const children = createTestSlideNodes();
|
||||
const parent = createParent({ children: children });
|
||||
|
||||
const emblaApi = createEmblaApiInstance({
|
||||
containerNode: parent,
|
||||
slideNodes: children,
|
||||
selectedScrollSnap: selected ? 5 : 4,
|
||||
});
|
||||
plugin.init(emblaApi, createTestEmblaOptionHandler());
|
||||
|
||||
const mediaLoadedHandler = vi.fn();
|
||||
parent.addEventListener('frigate-card:media:' + type, mediaLoadedHandler);
|
||||
if (type === 'loaded') {
|
||||
dispatchExistingMediaLoadedInfoAsEvent(children[5], createMediaLoadedInfo());
|
||||
} else if (type === 'unloaded') {
|
||||
dispatchMediaUnloadedEvent(children[5]);
|
||||
}
|
||||
|
||||
if (selected) {
|
||||
expect(mediaLoadedHandler).toBeCalled();
|
||||
} else {
|
||||
expect(mediaLoadedHandler).not.toBeCalled();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('selecting a slide should dispatch a previously saved media loaded info if present', () => {
|
||||
const plugin = AutoMediaLoadedInfo();
|
||||
const children = createTestSlideNodes();
|
||||
const parent = createParent({ children: children });
|
||||
const emblaApi = createEmblaApiInstance({
|
||||
containerNode: parent,
|
||||
slideNodes: children,
|
||||
});
|
||||
plugin.init(emblaApi, createTestEmblaOptionHandler());
|
||||
|
||||
const mediaLoadedHandler = vi.fn();
|
||||
parent.addEventListener('frigate-card:media:loaded', mediaLoadedHandler);
|
||||
dispatchExistingMediaLoadedInfoAsEvent(children[5], createMediaLoadedInfo());
|
||||
|
||||
vi.mocked(emblaApi.selectedScrollSnap).mockReturnValue(4);
|
||||
callEmblaHandler(emblaApi, 'select');
|
||||
expect(mediaLoadedHandler).not.toBeCalled();
|
||||
|
||||
vi.mocked(emblaApi.selectedScrollSnap).mockReturnValue(5);
|
||||
callEmblaHandler(emblaApi, 'select');
|
||||
expect(mediaLoadedHandler).toBeCalled();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,161 @@
|
||||
import { beforeAll, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import AutoSize from '../../../../../src/utils/embla/plugins/auto-size/auto-size';
|
||||
import {
|
||||
IntersectionObserverMock,
|
||||
ResizeObserverMock,
|
||||
createParent,
|
||||
requestAnimationFrameMock,
|
||||
} from '../../../../test-utils';
|
||||
import {
|
||||
callEmblaHandler,
|
||||
callIntersectionHandler,
|
||||
callResizeHandler,
|
||||
createEmblaApiInstance,
|
||||
createTestEmblaOptionHandler,
|
||||
createTestSlideNodes,
|
||||
} from '../../test-utils';
|
||||
|
||||
// Mock out debouncing (used in the reinit controller).
|
||||
vi.mock('lodash-es/debounce', () => ({
|
||||
default: vi.fn((fn) => fn),
|
||||
}));
|
||||
|
||||
// @vitest-environment jsdom
|
||||
describe('AutoSize', () => {
|
||||
beforeAll(() => {
|
||||
// Mock out requestAnimationFrame (used in the reinit controller).
|
||||
window.requestAnimationFrame = requestAnimationFrameMock;
|
||||
vi.stubGlobal('IntersectionObserver', IntersectionObserverMock);
|
||||
vi.stubGlobal('ResizeObserver', ResizeObserverMock);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should construct', () => {
|
||||
const plugin = AutoSize();
|
||||
expect(plugin.name).toBe('autoSize');
|
||||
});
|
||||
|
||||
it('should destroy', () => {
|
||||
const plugin = AutoSize();
|
||||
const emblaApi = createEmblaApiInstance();
|
||||
plugin.init(emblaApi, createTestEmblaOptionHandler());
|
||||
plugin.destroy();
|
||||
|
||||
expect(emblaApi.off).toBeCalledWith('settle', expect.anything());
|
||||
|
||||
expect(
|
||||
vi.mocked(IntersectionObserver).mock.results[0].value.disconnect,
|
||||
).toBeCalled();
|
||||
expect(vi.mocked(ResizeObserver).mock.results[0].value.disconnect).toBeCalled();
|
||||
});
|
||||
|
||||
it('should correctly handle intersection', () => {
|
||||
const plugin = AutoSize();
|
||||
const emblaApi = createEmblaApiInstance();
|
||||
plugin.init(emblaApi, createTestEmblaOptionHandler());
|
||||
|
||||
// First intersection handler call sets the state only.
|
||||
callIntersectionHandler(true);
|
||||
|
||||
callIntersectionHandler(false);
|
||||
callIntersectionHandler(false);
|
||||
callIntersectionHandler(false);
|
||||
|
||||
expect(emblaApi.reInit).toBeCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should correctly handle resize', () => {
|
||||
const plugin = AutoSize();
|
||||
const parent = createParent();
|
||||
const emblaApi = createEmblaApiInstance({ containerNode: parent });
|
||||
|
||||
plugin.init(emblaApi, createTestEmblaOptionHandler());
|
||||
|
||||
callResizeHandler([{ target: parent, width: 10, height: 20 }]);
|
||||
callResizeHandler([{ target: parent, width: 10, height: 20 }]);
|
||||
callResizeHandler([{ target: parent, width: 10, height: 20 }]);
|
||||
|
||||
expect(emblaApi.reInit).toBeCalledTimes(1);
|
||||
|
||||
callResizeHandler([{ target: parent, width: 20, height: 40 }]);
|
||||
|
||||
expect(emblaApi.reInit).toBeCalledTimes(2);
|
||||
});
|
||||
|
||||
it('should set container height on slide settle', () => {
|
||||
const plugin = AutoSize();
|
||||
const parent = createParent();
|
||||
const children = createTestSlideNodes();
|
||||
const emblaApi = createEmblaApiInstance({
|
||||
containerNode: parent,
|
||||
selectedScrollSnap: 0,
|
||||
slideNodes: children,
|
||||
// 0th scroll snap shows the 0th slide only.
|
||||
slideRegistry: [[0]],
|
||||
});
|
||||
plugin.init(emblaApi, createTestEmblaOptionHandler());
|
||||
|
||||
children[0].getBoundingClientRect = vi.fn().mockReturnValue({
|
||||
width: 200,
|
||||
height: 800,
|
||||
});
|
||||
|
||||
// select should not do anything, we wait for it to have settled for
|
||||
// smoothness.
|
||||
callEmblaHandler(emblaApi, 'select');
|
||||
expect(parent.style.maxHeight).toBeFalsy();
|
||||
|
||||
callEmblaHandler(emblaApi, 'settle');
|
||||
expect(parent.style.maxHeight).toBe('800px');
|
||||
});
|
||||
|
||||
it('should not set container height on horizontal carousel', () => {
|
||||
const plugin = AutoSize();
|
||||
const parent = createParent();
|
||||
const children = createTestSlideNodes();
|
||||
const emblaApi = createEmblaApiInstance({
|
||||
containerNode: parent,
|
||||
selectedScrollSnap: 0,
|
||||
slideNodes: children,
|
||||
axis: 'y',
|
||||
// 0th scroll snap shows the 0th slide only.
|
||||
slideRegistry: [[0]],
|
||||
});
|
||||
plugin.init(emblaApi, createTestEmblaOptionHandler());
|
||||
|
||||
children[0].getBoundingClientRect = vi.fn().mockReturnValue({
|
||||
width: 200,
|
||||
height: 800,
|
||||
});
|
||||
|
||||
callEmblaHandler(emblaApi, 'settle');
|
||||
|
||||
expect(parent.style.maxHeight).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should not set container height when slide dimensions are invalid', () => {
|
||||
const plugin = AutoSize();
|
||||
const parent = createParent();
|
||||
const children = createTestSlideNodes();
|
||||
const emblaApi = createEmblaApiInstance({
|
||||
containerNode: parent,
|
||||
selectedScrollSnap: 0,
|
||||
slideNodes: children,
|
||||
axis: 'x',
|
||||
// 0th scroll snap shows the 0th slide only.
|
||||
slideRegistry: [[0]],
|
||||
});
|
||||
plugin.init(emblaApi, createTestEmblaOptionHandler());
|
||||
|
||||
children[0].getBoundingClientRect = vi.fn().mockReturnValue(NaN);
|
||||
callEmblaHandler(emblaApi, 'settle');
|
||||
|
||||
children[0].getBoundingClientRect = vi.fn().mockReturnValue(0);
|
||||
callEmblaHandler(emblaApi, 'settle');
|
||||
|
||||
expect(parent.style.maxHeight).toBeFalsy();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user