ytptube/ui/tests/plugins/modal-opacity.client.test.ts
2026-04-15 18:13:04 +03:00

120 lines
3.3 KiB
TypeScript

import { afterEach, beforeAll, beforeEach, describe, expect, it, mock } from 'bun:test';
const disableOpacityMock = mock(() => {});
const enableOpacityMock = mock(() => {});
const syncOpacityMock = mock(() => {});
mock.module('~/utils', () => ({
disableOpacity: disableOpacityMock,
enableOpacity: enableOpacityMock,
syncOpacity: syncOpacityMock,
}));
(globalThis as typeof globalThis & { defineNuxtPlugin?: (setup: () => void) => () => void }).defineNuxtPlugin =
(setup) => setup;
let plugin: Awaited<typeof import('../../app/plugins/modal-opacity.client.ts')>['default'];
let started = false;
const flushMutations = async (): Promise<void> => {
await Promise.resolve();
await new Promise((resolve) => setTimeout(resolve, 0));
};
const createOverlay = (): HTMLDivElement => {
const overlay = document.createElement('div');
overlay.setAttribute('data-slot', 'overlay');
return overlay;
};
const startPlugin = (): void => {
if (started) {
return;
}
plugin();
document.dispatchEvent(new window.Event('DOMContentLoaded'));
started = true;
};
beforeAll(async () => {
plugin = (await import('../../app/plugins/modal-opacity.client.ts')).default;
});
beforeEach(() => {
globalThis.MutationObserver = window.MutationObserver;
document.body.innerHTML = '';
disableOpacityMock.mockClear();
enableOpacityMock.mockClear();
syncOpacityMock.mockClear();
});
afterEach(async () => {
document.body.innerHTML = '';
await flushMutations();
});
describe('modal opacity plugin', () => {
it('ignores a settings-only overlay', async () => {
startPlugin();
const settingsPanel = document.createElement('div');
settingsPanel.className = 'yt-settings-panel';
document.body.append(settingsPanel, createOverlay());
await flushMutations();
expect(disableOpacityMock).not.toHaveBeenCalled();
expect(enableOpacityMock).not.toHaveBeenCalled();
expect(syncOpacityMock).not.toHaveBeenCalled();
});
it('restores opacity when a normal overlay closes back to settings-only', async () => {
startPlugin();
const settingsPanel = document.createElement('div');
settingsPanel.className = 'yt-settings-panel';
const settingsOverlay = createOverlay();
const modalOverlay = createOverlay();
document.body.append(settingsPanel, settingsOverlay);
await flushMutations();
document.body.append(modalOverlay);
await flushMutations();
expect(disableOpacityMock).toHaveBeenCalledTimes(1);
modalOverlay.remove();
await flushMutations();
expect(enableOpacityMock).toHaveBeenCalledTimes(1);
});
it('resyncs opacity when overlays change while already locked', async () => {
startPlugin();
document.body.append(createOverlay());
await flushMutations();
document.body.append(createOverlay());
await flushMutations();
expect(disableOpacityMock).toHaveBeenCalledTimes(1);
expect(syncOpacityMock).toHaveBeenCalledTimes(1);
});
it('does not unlock opacity on beforeunload when reload is canceled', async () => {
startPlugin();
document.body.append(createOverlay());
await flushMutations();
expect(disableOpacityMock).toHaveBeenCalledTimes(1);
window.dispatchEvent(new window.Event('beforeunload'));
await flushMutations();
expect(enableOpacityMock).not.toHaveBeenCalled();
});
});