diff --git a/API.md b/API.md
index 8b625811..907dd2d3 100644
--- a/API.md
+++ b/API.md
@@ -1777,6 +1777,7 @@ Binary image data with appropriate headers
- `per_page` (optional): Items per page. Default: `config.default_pagination`.
- `sort` (optional): Comma-separated sort fields. Accepted values: `id`, `name`, `priority`, `default`, `created_at`, `updated_at`. Default: `priority,name`.
- `order` (optional): Comma-separated sort directions matching `sort`, or a single direction applied to every requested sort field. Accepted values: `asc`, `desc`. Default: `desc,asc`.
+- `exclude_defaults` (optional): When `true`, excludes system presets from the results. Default: `false`.
**Response**:
```json
@@ -1806,10 +1807,9 @@ Binary image data with appropriate headers
**Notes**:
- `default: true` indicates this is a system default preset (cannot be modified or deleted)
-- Default ordering remains `priority desc, name asc`
**Error Responses**:
-- `400 Bad Request` - Invalid pagination or sorting query parameters
+- `400 Bad Request` - Invalid data was provided.
---
diff --git a/app/features/presets/repository.py b/app/features/presets/repository.py
index 3f867677..94567d03 100644
--- a/app/features/presets/repository.py
+++ b/app/features/presets/repository.py
@@ -180,25 +180,32 @@ async def list_paginated(
per_page: int,
sort: str | None = None,
order: str | None = None,
+ exclude_defaults: bool = False,
) -> tuple[list[PresetModel], int, int, int]:
order_by = self._build_order_by(sort, order)
async with self.session() as session:
- total: int = await self.count()
+ total: int = await self.count(exclude_defaults=exclude_defaults)
total_pages: int = (total + per_page - 1) // per_page if total > 0 else 1
if page > total_pages and total > 0:
page = total_pages
- query: Select[tuple[PresetModel]] = (
- select(PresetModel).order_by(*order_by).limit(per_page).offset((page - 1) * per_page)
- )
+ query: Select[tuple[PresetModel]] = select(PresetModel)
+ if exclude_defaults:
+ query = query.where(PresetModel.default.is_(False))
+
+ query = query.order_by(*order_by).limit(per_page).offset((page - 1) * per_page)
result: Result[tuple[PresetModel]] = await session.execute(query)
return list(result.scalars().all()), total, page, total_pages
- async def count(self) -> int:
+ async def count(self, exclude_defaults: bool = False) -> int:
async with self.session() as session:
- result: Result[tuple[int]] = await session.execute(select(func.count()).select_from(PresetModel))
+ query = select(func.count()).select_from(PresetModel)
+ if exclude_defaults:
+ query = query.where(PresetModel.default.is_(False))
+
+ result: Result[tuple[int]] = await session.execute(query)
return int(result.scalar_one())
async def get(self, identifier: int | str) -> PresetModel | None:
diff --git a/app/features/presets/router.py b/app/features/presets/router.py
index 28105e13..fd30521e 100644
--- a/app/features/presets/router.py
+++ b/app/features/presets/router.py
@@ -26,10 +26,11 @@ async def presets_list(request: Request, encoder: Encoder, repo: PresetsReposito
try:
page, per_page = normalize_pagination(request)
items, total, current_page, total_pages = await repo.list_paginated(
- page,
- per_page,
+ page=page,
+ per_page=per_page,
sort=request.query.get("sort"),
order=request.query.get("order"),
+ exclude_defaults=bool(request.query.get("exclude_defaults", False)),
)
except ValueError as exc:
return web.json_response(data={"error": str(exc)}, status=web.HTTPBadRequest.status_code)
diff --git a/app/features/presets/tests/test_presets_repository.py b/app/features/presets/tests/test_presets_repository.py
index 11c83add..656ac446 100644
--- a/app/features/presets/tests/test_presets_repository.py
+++ b/app/features/presets/tests/test_presets_repository.py
@@ -88,6 +88,18 @@ async def test_list_paginated_sorts_by_name_desc(self, repo):
assert [item.name for item in items] == ["gamma", "beta", "alpha"], "Should sort by requested field"
+ @pytest.mark.asyncio
+ async def test_list_paginated_excludes_defaults(self, repo):
+ await repo.create({"name": "System Default", "default": True, "priority": 10})
+ await repo.create({"name": "Custom Preset", "priority": 1})
+
+ items, total, page, total_pages = await repo.list_paginated(page=1, per_page=10, exclude_defaults=True)
+
+ assert [item.name for item in items] == ["custom_preset"], "Should exclude default presets"
+ assert total == 1, "Should count only custom presets"
+ assert page == 1, "Should keep current page when filtered results exist"
+ assert total_pages == 1, "Should compute pages from the filtered total"
+
@pytest.mark.asyncio
async def test_list_paginated_supports_multiple_sort_fields(self, repo):
await repo.create({"name": "Charlie", "priority": 2})
@@ -153,3 +165,17 @@ async def test_list_route_rejects_invalid_sort_direction(self, repo):
assert response.status == web.HTTPBadRequest.status_code, "Should reject unsupported sort direction"
assert "order" in payload["error"], "Should explain invalid sort direction"
+
+ async def test_list_route_supports_excluding_defaults(self, repo):
+ await repo.create({"name": "System Default", "default": True, "priority": 10})
+ await repo.create({"name": "Custom Preset", "priority": 1})
+
+ request = MagicMock(spec=Request)
+ request.query = {"page": "1", "per_page": "10", "exclude_defaults": "true"}
+
+ response = await presets_list(request, Encoder(), repo)
+ payload = json.loads(response.text)
+
+ assert response.status == web.HTTPOk.status_code, "Should return 200 for valid default exclusion"
+ assert [item["name"] for item in payload["items"]] == ["custom_preset"], "Should exclude default presets"
+ assert payload["pagination"]["total"] == 1, "Should report filtered total"
diff --git a/ui/app/components/Dialog.vue b/ui/app/components/Dialog.vue
index 06dff7e0..06e0a135 100644
--- a/ui/app/components/Dialog.vue
+++ b/ui/app/components/Dialog.vue
@@ -5,7 +5,7 @@
:title="state.current?.opts.title ?? defaultTitle"
:dismissible="true"
:ui="{ content: 'max-w-lg', body: 'space-y-4', footer: 'justify-end gap-2' }"
- @update:open="(open) => !open && onCancel()"
+ @update:open="(open) => !open && cancel()"
@after:enter="focusInput"
>
@@ -63,7 +63,7 @@
{{ state.current?.opts.confirmText ?? 'OK' }}
-
+
{{ (state.current?.opts as PromptOptions | ConfirmOptions)?.cancelText ?? 'Cancel' }}
@@ -121,7 +121,6 @@ const focusInput = async () => {
requestAnimationFrame(focusPrimary);
};
-const onCancel = () => cancel();
const onEnter = () =>
confirm('confirm' === state.current?.type ? selected.value : localInput.value);
diff --git a/ui/app/components/NewDownload.vue b/ui/app/components/NewDownload.vue
index b8136148..a0f28f6e 100644
--- a/ui/app/components/NewDownload.vue
+++ b/ui/app/components/NewDownload.vue
@@ -170,7 +170,7 @@
@@ -1124,7 +1124,6 @@ const hasFormatInConfig = computed(
(): boolean => !!form.value.cli?.match(/(? findPreset(name);
const expand_description = (e: Event) =>
toggleClass(e.target as HTMLElement, ['is-ellipsis', 'is-pre-wrap']);
diff --git a/ui/app/components/PresetForm.vue b/ui/app/components/PresetForm.vue
index cb5240d6..bb6f6aa4 100644
--- a/ui/app/components/PresetForm.vue
+++ b/ui/app/components/PresetForm.vue
@@ -367,13 +367,12 @@ const props = defineProps<{
reference?: number | null;
preset: Partial;
addInProgress?: boolean;
- presets?: Preset[];
}>();
const config = useYtpConfig();
const toast = useNotification();
const dialog = useDialog();
-const { presets, findPreset, selectItems } = usePresetOptions(() => props.presets);
+const { presets, findPreset, selectItems } = usePresetOptions();
const form = reactive({
name: '',
diff --git a/ui/app/composables/usePresets.ts b/ui/app/composables/usePresets.ts
index e9021129..50872fd4 100644
--- a/ui/app/composables/usePresets.ts
+++ b/ui/app/composables/usePresets.ts
@@ -129,6 +129,7 @@ const removePreset = (id: number) => {
const loadPresets = async (
page: number = 1,
perPage: number | undefined = undefined,
+ options: { excludeDefaults?: boolean } = {},
): Promise => {
isLoading.value = true;
try {
@@ -136,6 +137,10 @@ const loadPresets = async (
if (perPage !== undefined) {
url += `&per_page=${perPage}`;
}
+ if (options.excludeDefaults) {
+ url += '&exclude_defaults=true';
+ }
+
const response = await request(url);
await ensureSuccess(response);
diff --git a/ui/app/pages/conditions.vue b/ui/app/pages/conditions.vue
index 68b3dd19..7c5b1b07 100644
--- a/ui/app/pages/conditions.vue
+++ b/ui/app/pages/conditions.vue
@@ -63,7 +63,7 @@
icon="i-lucide-refresh-cw"
:loading="isLoading"
:disabled="isLoading"
- @click="() => void reloadContent()"
+ @click="() => void loadContent(page)"
>
Reload
@@ -122,7 +122,7 @@
:disabled="isLoading"
show-edges
:sibling-count="0"
- @update:page="navigatePage"
+ @update:page="loadContent"
size="sm"
/>
@@ -447,7 +447,7 @@
:disabled="isLoading"
show-edges
:sibling-count="0"
- @update:page="navigatePage"
+ @update:page="loadContent"
size="sm"
/>
@@ -648,14 +648,6 @@ const loadContent = async (pageNumber = 1): Promise => {
await syncPageQuery(pageNumber);
};
-const reloadContent = async (): Promise => {
- await loadContent(page.value);
-};
-
-const navigatePage = async (newPage: number): Promise => {
- await loadContent(newPage);
-};
-
const resetEditor = (): void => {
item.value = {};
itemRef.value = null;
diff --git a/ui/app/pages/dl_fields.vue b/ui/app/pages/dl_fields.vue
index 10ceeea3..ebc618d7 100644
--- a/ui/app/pages/dl_fields.vue
+++ b/ui/app/pages/dl_fields.vue
@@ -63,7 +63,7 @@
icon="i-lucide-refresh-cw"
:loading="isLoading"
:disabled="isLoading"
- @click="() => void reloadContent()"
+ @click="() => void loadContent(page)"
>
Reload
@@ -122,7 +122,7 @@
:disabled="isLoading"
show-edges
:sibling-count="0"
- @update:page="navigatePage"
+ @update:page="loadContent"
size="sm"
/>
@@ -399,7 +399,7 @@
:disabled="isLoading"
show-edges
:sibling-count="0"
- @update:page="navigatePage"
+ @update:page="loadContent"
size="sm"
/>
@@ -567,14 +567,6 @@ const loadContent = async (pageNumber = 1): Promise => {
await syncPageQuery(pageNumber);
};
-const reloadContent = async (): Promise => {
- await loadContent(page.value);
-};
-
-const navigatePage = async (newPage: number): Promise => {
- await loadContent(newPage);
-};
-
const resetEditor = (): void => {
item.value = {};
itemRef.value = null;
diff --git a/ui/app/pages/notifications.vue b/ui/app/pages/notifications.vue
index 7ea3aabd..fe9ba388 100644
--- a/ui/app/pages/notifications.vue
+++ b/ui/app/pages/notifications.vue
@@ -76,7 +76,7 @@
icon="i-lucide-refresh-cw"
:loading="isLoading"
:disabled="isLoading"
- @click="() => void reloadContent()"
+ @click="() => void loadContent(page)"
>
Reload
@@ -135,7 +135,7 @@
:disabled="isLoading"
show-edges
:sibling-count="0"
- @update:page="navigatePage"
+ @update:page="loadContent"
size="sm"
/>
@@ -497,7 +497,7 @@
:disabled="isLoading"
show-edges
:sibling-count="0"
- @update:page="navigatePage"
+ @update:page="loadContent"
size="sm"
/>
@@ -706,14 +706,6 @@ const loadContent = async (pageNumber = page.value): Promise => {
await notificationsStore.loadNotifications(pageNumber);
};
-const reloadContent = async (): Promise => {
- await loadContent(page.value);
-};
-
-const navigatePage = async (newPage: number): Promise => {
- await loadContent(newPage);
-};
-
const resetEditor = (): void => {
target.value = defaultState();
targetRef.value = undefined;
diff --git a/ui/app/pages/presets.vue b/ui/app/pages/presets.vue
index 91da8fd7..16c784e8 100644
--- a/ui/app/pages/presets.vue
+++ b/ui/app/pages/presets.vue
@@ -23,7 +23,7 @@
void reloadContent()"
+ @click="() => void loadContent(page)"
>
Reload
+
+
+
+
+
+
@@ -443,7 +471,6 @@
:addInProgress="editor.addInProgress.value"
:reference="editor.reference.value"
:preset="editor.preset.value"
- :presets="presets"
@cancel="() => void editor.requestClose()"
@dirty-change="(dirty) => (editor.dirty.value = dirty)"
@submit="editor.submit"
@@ -470,6 +497,8 @@ const config = useYtpConfig();
const box = useConfirm();
const editor = usePresetEditor();
const pageShell = requirePageShell('presets');
+const route = useRoute();
+const router = useRouter();
const { confirmDialog } = useDialog();
const { toggleExpand, expandClass } = useExpandableMeta();
@@ -481,21 +510,19 @@ const showFilter = ref(false);
const filterInput = ref<{ inputRef?: { value?: HTMLInputElement | null } } | null>(null);
const selectedIds = ref([]);
const massDelete = ref(false);
+const page = ref(route.query.page ? parseInt(route.query.page as string, 10) : 1);
const presets = computed(() => presetsStore.presets.value as PresetWithUI[]);
+const paging = presetsStore.pagination;
const isLoading = presetsStore.isLoading;
-const presetsNoDefault = computed(() => presets.value.filter((item) => !item.default));
-
const filteredPresets = computed(() => {
const normalizedQuery = query.value?.toLowerCase();
if (!normalizedQuery) {
- return presetsNoDefault.value;
+ return presets.value;
}
- return presetsNoDefault.value.filter((item) =>
- deepIncludes(item, normalizedQuery, new WeakSet()),
- );
+ return presets.value.filter((item) => deepIncludes(item, normalizedQuery, new WeakSet()));
});
const selectablePresetIds = computed(() =>
@@ -525,6 +552,19 @@ const bulkActionGroups = computed(() => [
],
]);
+const syncPageQuery = async (pageNumber: number): Promise => {
+ const totalPages = paging.value.total_pages;
+ const nextQuery = { ...route.query };
+
+ if (totalPages > 1) {
+ nextQuery.page = String(pageNumber);
+ } else {
+ delete nextQuery.page;
+ }
+
+ await router.replace({ query: nextQuery });
+};
+
watch(showFilter, (value) => {
if (!value) {
query.value = '';
@@ -553,8 +593,11 @@ const toggleFilterPanel = async (): Promise => {
filterInput.value?.inputRef?.value?.focus?.({ preventScroll: true });
};
-const reloadContent = async (): Promise => {
- await presetsStore.loadPresets(1, 1000);
+const loadContent = async (pageNumber = 1): Promise => {
+ page.value = pageNumber;
+ await presetsStore.loadPresets(pageNumber, undefined, { excludeDefaults: true });
+ await nextTick();
+ await syncPageQuery(pageNumber);
};
const toggleMasterSelection = (): void => {
@@ -600,15 +643,20 @@ const deleteSelected = async (): Promise => {
massDelete.value = true;
- for (const item of itemsToDelete) {
- if (!item.id) {
- continue;
+ try {
+ for (const item of itemsToDelete) {
+ if (!item.id) {
+ continue;
+ }
+
+ await presetsStore.deletePreset(item.id);
}
- await presetsStore.deletePreset(item.id);
+ } finally {
+ selectedIds.value = [];
+ massDelete.value = false;
}
- selectedIds.value = [];
- massDelete.value = false;
+ await loadContent(page.value);
};
const deleteItem = async (item: Preset): Promise => {
@@ -644,5 +692,5 @@ const calcPath = (path?: string): string => {
return path ? location + '/' + sTrim(path, '/') : location;
};
-onMounted(async () => await reloadContent());
+onMounted(async () => await loadContent(page.value));
diff --git a/ui/app/pages/task_definitions.vue b/ui/app/pages/task_definitions.vue
index fa8a3dc1..370958c2 100644
--- a/ui/app/pages/task_definitions.vue
+++ b/ui/app/pages/task_definitions.vue
@@ -71,7 +71,7 @@
icon="i-lucide-refresh-cw"
:loading="isLoading"
:disabled="isLoading"
- @click="() => void reloadContent()"
+ @click="() => void loadDefinitions(1, 1000)"
>
Reload
@@ -642,10 +642,6 @@ const toggleFilterPanel = async (): Promise => {
filterInput.value?.inputRef?.value?.focus?.({ preventScroll: true });
};
-const reloadContent = async (): Promise => {
- await loadDefinitions(1, 1000);
-};
-
const toggleDisplayStyle = (): void => {
display_style.value = display_style.value === 'list' ? 'grid' : 'list';
};
@@ -837,7 +833,7 @@ const exportDefinition = async (summary: TaskDefinitionSummary): Promise =
onMounted(async () => {
if (!definitions.value.length) {
- await reloadContent();
+ await loadDefinitions(1, 1000);
}
});
diff --git a/ui/app/pages/tasks.vue b/ui/app/pages/tasks.vue
index e4ea0cad..3104d61f 100644
--- a/ui/app/pages/tasks.vue
+++ b/ui/app/pages/tasks.vue
@@ -62,7 +62,7 @@
icon="i-lucide-refresh-cw"
:loading="isLoading"
:disabled="isLoading"
- @click="() => void reloadContent()"
+ @click="() => void loadContent(page)"
>
Reload
@@ -113,7 +113,17 @@
- {{ filteredTasks.length }} displayed
+
+
+
+
+
@@ -722,6 +745,8 @@ const config = useYtpConfig();
const socket = useAppSocket();
const stateStore = useQueueState();
const pageShell = requirePageShell('tasks');
+const route = useRoute();
+const router = useRouter();
const { confirmDialog } = useDialog();
const sessionCache = useSessionCache();
const { toggleExpand, expandClass } = useExpandableMeta();
@@ -731,6 +756,7 @@ const isMobile = useMediaQuery({ maxWidth: 639 });
const tasksComposable = useTasks();
const {
tasks,
+ pagination: paging,
isLoading,
addInProgress,
isTaskInProgress,
@@ -762,6 +788,7 @@ const inspectTask = ref(null);
const query = ref('');
const showFilter = ref(false);
const filterInput = ref<{ inputRef?: { value?: HTMLInputElement | null } } | null>(null);
+const page = ref(route.query.page ? parseInt(route.query.page as string, 10) : 1);
const CACHE_KEY = 'tasks:handler_support';
const taskHandlerSupport = ref>(sessionCache.get(CACHE_KEY) || {});
@@ -861,6 +888,19 @@ watch(
{ immediate: true },
);
+const syncPageQuery = async (pageNumber: number): Promise => {
+ const totalPages = tasksComposable.pagination.value.total_pages;
+ const nextQuery = { ...route.query };
+
+ if (totalPages > 1) {
+ nextQuery.page = String(pageNumber);
+ } else {
+ delete nextQuery.page;
+ }
+
+ await router.replace({ query: nextQuery });
+};
+
const toggleFilterPanel = async (): Promise => {
showFilter.value = !showFilter.value;
if (!showFilter.value) {
@@ -941,9 +981,15 @@ const willTaskBeProcessed = (item: Task): boolean => {
return hasTimer || hasHandler;
};
-const reloadContent = async (fromMounted: boolean = false) => {
+const loadContent = async (pageNumber = page.value, fromMounted: boolean = false) => {
+ page.value = pageNumber;
+
try {
- await tasksComposable.loadTasks();
+ await tasksComposable.loadTasks(pageNumber);
+
+ page.value = tasksComposable.pagination.value.page;
+ await nextTick();
+ await syncPageQuery(page.value);
if (tasks.value.length > 0) {
cleanStaleCache(tasks.value);
@@ -1400,7 +1446,7 @@ const itemActionGroups = (item: Task): DropdownMenuItem[][] => [
];
onMounted(async () => {
- await reloadContent(true);
+ await loadContent(page.value, true);
});
onBeforeUnmount(() => socket.off('item_status', statusHandler));
diff --git a/ui/app/spa-loading-template.html b/ui/app/spa-loading-template.html
index c74f7b2d..492615d1 100644
--- a/ui/app/spa-loading-template.html
+++ b/ui/app/spa-loading-template.html
@@ -163,8 +163,14 @@
}
@media (max-width: 560px) {
+ body {
+ padding-top: calc(1rem + env(safe-area-inset-top));
+ padding-bottom: calc(1rem + env(safe-area-inset-bottom));
+ }
+
.loading-card {
padding: 1.3rem;
+ transform: translateY(clamp(-2.75rem, -7vh, -1.5rem));
}
.brand {
diff --git a/ui/tests/composables/usePresets.test.ts b/ui/tests/composables/usePresets.test.ts
index 82be3518..e6d1e95c 100644
--- a/ui/tests/composables/usePresets.test.ts
+++ b/ui/tests/composables/usePresets.test.ts
@@ -104,6 +104,26 @@ describe('usePresets', () => {
requestSpy.mockRestore()
})
+ it('requests custom presets without defaults when asked', async () => {
+ const requestSpy = spyOn(utils, 'request')
+ requestSpy.mockResolvedValueOnce(
+ createMockResponse({
+ ok: true,
+ status: 200,
+ jsonData: {
+ items: [mockPreset],
+ pagination: mockPagination,
+ },
+ }),
+ )
+
+ const presets = usePresets()
+ await presets.loadPresets(2, 25, { excludeDefaults: true })
+
+ expect(requestSpy).toHaveBeenCalledWith('/api/presets/?page=2&per_page=25&exclude_defaults=true')
+ requestSpy.mockRestore()
+ })
+
it('sorts presets by priority then name', async () => {
const items = [
{ ...mockPreset, id: 1, name: 'B', priority: 2 },
@@ -127,12 +147,12 @@ describe('usePresets', () => {
await presets.loadPresets()
const sorted = presets.presets.value
- expect(sorted[0].priority).toBe(2)
- expect(sorted[0].name).toBe('A')
- expect(sorted[1].priority).toBe(2)
- expect(sorted[1].name).toBe('B')
- expect(sorted[2].priority).toBe(1)
- expect(sorted[2].name).toBe('C')
+ expect(sorted[0]!.priority).toBe(2)
+ expect(sorted[0]!.name).toBe('A')
+ expect(sorted[1]!.priority).toBe(2)
+ expect(sorted[1]!.name).toBe('B')
+ expect(sorted[2]!.priority).toBe(1)
+ expect(sorted[2]!.name).toBe('C')
requestSpy.mockRestore()
})
@@ -219,7 +239,7 @@ describe('usePresets', () => {
const presets = usePresets()
await presets.updatePreset(1, { ...mockPreset, default: true })
- const requestBody = JSON.parse((requestSpy.mock.calls[0][1] as any).body)
+ const requestBody = JSON.parse((requestSpy.mock.calls[0]![1] as any).body)
expect(requestBody.id).toBeUndefined()
expect(requestBody.default).toBe(false)
requestSpy.mockRestore()
@@ -257,7 +277,7 @@ describe('usePresets', () => {
const presets = usePresets()
await presets.patchPreset(1, { id: 10, default: true })
- const requestBody = JSON.parse((requestSpy.mock.calls[0][1] as any).body)
+ const requestBody = JSON.parse((requestSpy.mock.calls[0]![1] as any).body)
expect(requestBody.id).toBeUndefined()
expect(requestBody.default).toBe(false)
requestSpy.mockRestore()
diff --git a/ui/tests/plugins/modal-opacity.client.test.ts b/ui/tests/plugins/modal-opacity.client.test.ts
index 8e5628a5..dc46b2b4 100644
--- a/ui/tests/plugins/modal-opacity.client.test.ts
+++ b/ui/tests/plugins/modal-opacity.client.test.ts
@@ -1,20 +1,18 @@
-import { afterEach, beforeAll, beforeEach, describe, expect, it, mock } from 'bun:test';
+import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, mock, spyOn } 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 utils: Awaited;
let plugin: Awaited['default'];
let started = false;
+let disableOpacitySpy: ReturnType;
+let enableOpacitySpy: ReturnType;
+let syncOpacitySpy: ReturnType;
const flushMutations = async (): Promise => {
await Promise.resolve();
@@ -38,9 +36,19 @@ const startPlugin = (): void => {
};
beforeAll(async () => {
+ utils = await import('~/utils/index');
+ disableOpacitySpy = spyOn(utils, 'disableOpacity').mockImplementation(disableOpacityMock);
+ enableOpacitySpy = spyOn(utils, 'enableOpacity').mockImplementation(enableOpacityMock);
+ syncOpacitySpy = spyOn(utils, 'syncOpacity').mockImplementation(syncOpacityMock);
plugin = (await import('../../app/plugins/modal-opacity.client.ts')).default;
});
+afterAll(() => {
+ disableOpacitySpy.mockRestore();
+ enableOpacitySpy.mockRestore();
+ syncOpacitySpy.mockRestore();
+});
+
beforeEach(() => {
globalThis.MutationObserver = window.MutationObserver;
document.body.innerHTML = '';