From 867f6e64f94e70334e518dc819383eb05faf299b Mon Sep 17 00:00:00 2001 From: bo0tzz <git@bo0tzz.me> Date: Wed, 7 May 2025 07:42:48 +0200 Subject: [PATCH 1/2] chore: run all e2e tests on github runners (#17987) * chore: run all e2e tests on github runners * fix: use it.each for multi-case test --- .github/workflows/test.yml | 4 ++-- e2e/src/api/specs/asset.e2e-spec.ts | 36 +++++++++++------------------ 2 files changed, 15 insertions(+), 25 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 605993f5e9..64c084fa2e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -346,7 +346,7 @@ jobs: working-directory: ./e2e strategy: matrix: - runner: [mich, ubuntu-24.04-arm] + runner: [ubuntu-latest, ubuntu-24.04-arm] steps: - name: Checkout code @@ -394,7 +394,7 @@ jobs: working-directory: ./e2e strategy: matrix: - runner: [mich, ubuntu-24.04-arm] + runner: [ubuntu-latest, ubuntu-24.04-arm] steps: - name: Checkout code diff --git a/e2e/src/api/specs/asset.e2e-spec.ts b/e2e/src/api/specs/asset.e2e-spec.ts index 8196186059..8c203860df 100644 --- a/e2e/src/api/specs/asset.e2e-spec.ts +++ b/e2e/src/api/specs/asset.e2e-spec.ts @@ -1085,31 +1085,21 @@ describe('/asset', () => { }, ]; - it(`should upload and generate a thumbnail for different file types`, async () => { - // upload in parallel - const assets = await Promise.all( - tests.map(async ({ input }) => { - const filepath = join(testAssetDir, input); - return utils.createAsset(admin.accessToken, { - assetData: { bytes: await readFile(filepath), filename: basename(filepath) }, - }); - }), - ); + it.each(tests)(`should upload and generate a thumbnail for different file types`, async ({ input, expected }) => { + const filepath = join(testAssetDir, input); + const response = await utils.createAsset(admin.accessToken, { + assetData: { bytes: await readFile(filepath), filename: basename(filepath) }, + }); - for (const { id, status } of assets) { - expect(status).toBe(AssetMediaStatus.Created); - // longer timeout as the thumbnail generation from full-size raw files can take a while - await utils.waitForWebsocketEvent({ event: 'assetUpload', id }); - } + expect(response.status).toBe(AssetMediaStatus.Created); + const id = response.id; + // longer timeout as the thumbnail generation from full-size raw files can take a while + await utils.waitForWebsocketEvent({ event: 'assetUpload', id }); - for (const [i, { id }] of assets.entries()) { - const { expected } = tests[i]; - const asset = await utils.getAssetInfo(admin.accessToken, id); - - expect(asset.exifInfo).toBeDefined(); - expect(asset.exifInfo).toMatchObject(expected.exifInfo); - expect(asset).toMatchObject(expected); - } + const asset = await utils.getAssetInfo(admin.accessToken, id); + expect(asset.exifInfo).toBeDefined(); + expect(asset.exifInfo).toMatchObject(expected.exifInfo); + expect(asset).toMatchObject(expected); }); it('should handle a duplicate', async () => { From bbd8de177b3a330a18e8d1644f71bcdd30d519c6 Mon Sep 17 00:00:00 2001 From: Daniel Dietzler <36593685+danieldietzler@users.noreply.github.com> Date: Wed, 7 May 2025 16:01:51 +0200 Subject: [PATCH 2/2] refactor: side bar modals (#18134) --- web/src/app.html | 2 +- .../purchase-activation-success.svelte | 6 +++--- .../purchasing/purchase-content.svelte | 12 ++++++------ .../side-bar/purchase-info.svelte | 12 ++++-------- .../side-bar/server-status.svelte | 14 +++++++------- web/src/lib/managers/modal-manager.svelte.ts | 12 ++++++------ .../PurchaseModal.svelte} | 11 +++++------ .../ServerAboutModal.svelte} | 19 +++++++++---------- 8 files changed, 41 insertions(+), 47 deletions(-) rename web/src/lib/{components/shared-components/purchasing/purchase-modal.svelte => modals/PurchaseModal.svelte} (69%) rename web/src/lib/{components/shared-components/server-about-modal.svelte => modals/ServerAboutModal.svelte} (96%) diff --git a/web/src/app.html b/web/src/app.html index 18a873b525..832b3265ef 100644 --- a/web/src/app.html +++ b/web/src/app.html @@ -107,7 +107,7 @@ To use Immich, you must enable JavaScript or use a JavaScript compatible browser. </noscript> - <body class="bg-immich-bg dark:bg-immich-dark-bg"> + <body class="bg-light text-dark"> <div id="stencil"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 792 792"> <style type="text/css"> diff --git a/web/src/lib/components/shared-components/purchasing/purchase-activation-success.svelte b/web/src/lib/components/shared-components/purchasing/purchase-activation-success.svelte index 00800ab489..1b1a91d163 100644 --- a/web/src/lib/components/shared-components/purchasing/purchase-activation-success.svelte +++ b/web/src/lib/components/shared-components/purchasing/purchase-activation-success.svelte @@ -1,11 +1,11 @@ <script lang="ts"> import Button from '$lib/components/elements/buttons/button.svelte'; import Icon from '$lib/components/elements/icon.svelte'; - import { t } from 'svelte-i18n'; - import { mdiPartyPopper } from '@mdi/js'; import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte'; import { preferences } from '$lib/stores/user.store'; import { setSupportBadgeVisibility } from '$lib/utils/purchase-utils'; + import { mdiPartyPopper } from '@mdi/js'; + import { t } from 'svelte-i18n'; interface Props { onDone: () => void; @@ -14,7 +14,7 @@ let { onDone }: Props = $props(); </script> -<div class="m-auto w-3/4 text-center flex flex-col place-content-center place-items-center dark:text-white my-6"> +<div class="m-auto w-3/4 text-center flex flex-col place-content-center place-items-center my-6"> <Icon path={mdiPartyPopper} class="text-immich-primary dark:text-immich-dark-primary" size="96" /> <p class="text-4xl mt-8 font-bold">{$t('purchase_activated_title')}</p> <p class="text-lg mt-6">{$t('purchase_activated_subtitle')}</p> diff --git a/web/src/lib/components/shared-components/purchasing/purchase-content.svelte b/web/src/lib/components/shared-components/purchasing/purchase-content.svelte index 567fce9281..b46bdcb5e3 100644 --- a/web/src/lib/components/shared-components/purchasing/purchase-content.svelte +++ b/web/src/lib/components/shared-components/purchasing/purchase-content.svelte @@ -1,12 +1,12 @@ <script lang="ts"> - import { handleError } from '$lib/utils/handle-error'; - import ServerPurchaseOptionCard from './server-purchase-option-card.svelte'; - import UserPurchaseOptionCard from './individual-purchase-option-card.svelte'; - import { activateProduct, getActivationKey } from '$lib/utils/license-utils'; import Button from '$lib/components/elements/buttons/button.svelte'; import LoadingSpinner from '$lib/components/shared-components/loading-spinner.svelte'; import { purchaseStore } from '$lib/stores/purchase.store'; + import { handleError } from '$lib/utils/handle-error'; + import { activateProduct, getActivationKey } from '$lib/utils/license-utils'; import { t } from 'svelte-i18n'; + import UserPurchaseOptionCard from './individual-purchase-option-card.svelte'; + import ServerPurchaseOptionCard from './server-purchase-option-card.svelte'; interface Props { onActivate: () => void; @@ -39,13 +39,13 @@ <section class="p-4"> <div> {#if showTitle} - <h1 class="text-4xl font-bold text-immich-primary dark:text-immich-dark-primary tracking-wider"> + <h1 class="text-4xl font-bold tracking-wider"> {$t('purchase_option_title')} </h1> {/if} {#if showMessage} - <div class="mt-2 dark:text-immich-gray"> + <div class="mt-2"> <p> {$t('purchase_panel_info_1')} </p> diff --git a/web/src/lib/components/shared-components/side-bar/purchase-info.svelte b/web/src/lib/components/shared-components/side-bar/purchase-info.svelte index d94a0c169e..fe48a68009 100644 --- a/web/src/lib/components/shared-components/side-bar/purchase-info.svelte +++ b/web/src/lib/components/shared-components/side-bar/purchase-info.svelte @@ -4,9 +4,10 @@ import Icon from '$lib/components/elements/icon.svelte'; import ImmichLogo from '$lib/components/shared-components/immich-logo.svelte'; import Portal from '$lib/components/shared-components/portal/portal.svelte'; - import LicenseModal from '$lib/components/shared-components/purchasing/purchase-modal.svelte'; import SupporterBadge from '$lib/components/shared-components/side-bar/supporter-badge.svelte'; import { AppRoute } from '$lib/constants'; + import { modalManager } from '$lib/managers/modal-manager.svelte'; + import PurchaseModal from '$lib/modals/PurchaseModal.svelte'; import { purchaseStore } from '$lib/stores/purchase.store'; import { preferences } from '$lib/stores/user.store'; import { getAccountAge } from '$lib/utils/auth'; @@ -19,7 +20,6 @@ import { fade } from 'svelte/transition'; let showMessage = $state(false); - let isOpen = $state(false); let hoverMessage = $state(false); let hoverButton = $state(false); @@ -27,8 +27,8 @@ const { isPurchased } = purchaseStore; - const openPurchaseModal = () => { - isOpen = true; + const openPurchaseModal = async () => { + await modalManager.open(PurchaseModal); showMessage = false; }; @@ -74,10 +74,6 @@ }); </script> -{#if isOpen} - <LicenseModal onClose={() => (isOpen = false)} /> -{/if} - <div class="license-status ps-4 text-sm"> {#if $isPurchased && $preferences.purchase.showSupportBadge} <button diff --git a/web/src/lib/components/shared-components/side-bar/server-status.svelte b/web/src/lib/components/shared-components/side-bar/server-status.svelte index 49006dfe5a..665decc44f 100644 --- a/web/src/lib/components/shared-components/side-bar/server-status.svelte +++ b/web/src/lib/components/shared-components/side-bar/server-status.svelte @@ -1,6 +1,7 @@ <script lang="ts"> import Icon from '$lib/components/elements/icon.svelte'; - import ServerAboutModal from '$lib/components/shared-components/server-about-modal.svelte'; + import { modalManager } from '$lib/managers/modal-manager.svelte'; + import ServerAboutModal from '$lib/modals/ServerAboutModal.svelte'; import { userInteraction } from '$lib/stores/user.svelte'; import { websocketStore } from '$lib/stores/websocket'; import { requestServerInfo } from '$lib/utils/auth'; @@ -16,7 +17,6 @@ const { serverVersion, connected } = websocketStore; - let isOpen = $state(false); let info: ServerAboutResponseDto | undefined = $state(); let versions: ServerVersionHistoryResponseDto[] = $state([]); @@ -37,10 +37,6 @@ ); </script> -{#if isOpen && info} - <ServerAboutModal onClose={() => (isOpen = false)} {info} {versions} /> -{/if} - <div class="text-sm flex md:flex ps-5 pe-1 place-items-center place-content-center justify-between min-w-52 overflow-hidden dark:text-immich-dark-fg" > @@ -58,7 +54,11 @@ <div class="flex justify-between justify-items-center"> {#if $connected && version} - <button type="button" onclick={() => (isOpen = true)} class="dark:text-immich-gray flex gap-1"> + <button + type="button" + onclick={() => info && modalManager.open(ServerAboutModal, { versions, info })} + class="dark:text-immich-gray flex gap-1" + > {#if isMain} <Icon path={mdiAlert} size="1.5em" color="#ffcc4d" /> {info?.sourceRef} {:else} diff --git a/web/src/lib/managers/modal-manager.svelte.ts b/web/src/lib/managers/modal-manager.svelte.ts index 73a3351a7e..055df14502 100644 --- a/web/src/lib/managers/modal-manager.svelte.ts +++ b/web/src/lib/managers/modal-manager.svelte.ts @@ -1,15 +1,15 @@ import ConfirmDialog from '$lib/components/shared-components/dialog/confirm-dialog.svelte'; import { mount, unmount, type Component, type ComponentProps } from 'svelte'; -type OnCloseData<T> = T extends { onClose: (data: infer R) => void } ? R : never; -type OptionalIfEmpty<T extends object> = keyof T extends never ? undefined : T; +type OnCloseData<T> = T extends { onClose: (data: infer R) => void | Promise<void> } ? R : never; class ModalManager { - open<T extends object, K = OnCloseData<T>>( - Component: Component<T>, - props?: OptionalIfEmpty<Omit<T, 'onClose'>> | Record<string, never>, + open<T = { onClose: (data: unknown) => void }, K = OnCloseData<T>>( + Component: Component<{ onClose: T }>, + props?: Record<string, never>, ): Promise<K>; - open<T extends object, K = OnCloseData<T>>(Component: Component<T>, props: OptionalIfEmpty<Omit<T, 'onClose'>>) { + open<T extends object, K = OnCloseData<T>>(Component: Component<T>, props: Omit<T, 'onClose'>): Promise<K>; + open<T extends object, K = OnCloseData<T>>(Component: Component<T>, props?: Omit<T, 'onClose'>) { return new Promise<K>((resolve) => { let modal: object = {}; diff --git a/web/src/lib/components/shared-components/purchasing/purchase-modal.svelte b/web/src/lib/modals/PurchaseModal.svelte similarity index 69% rename from web/src/lib/components/shared-components/purchasing/purchase-modal.svelte rename to web/src/lib/modals/PurchaseModal.svelte index eaf6d14674..f771529ad2 100644 --- a/web/src/lib/components/shared-components/purchasing/purchase-modal.svelte +++ b/web/src/lib/modals/PurchaseModal.svelte @@ -1,9 +1,8 @@ <script lang="ts"> - import FullScreenModal from '$lib/components/shared-components/full-screen-modal.svelte'; import PurchaseActivationSuccess from '$lib/components/shared-components/purchasing/purchase-activation-success.svelte'; import PurchaseContent from '$lib/components/shared-components/purchasing/purchase-content.svelte'; - import Portal from '$lib/components/shared-components/portal/portal.svelte'; + import { Modal, ModalBody } from '@immich/ui'; interface Props { onClose: () => void; @@ -14,8 +13,8 @@ let showProductActivated = $state(false); </script> -<Portal> - <FullScreenModal showLogo title="" {onClose} width="wide"> +<Modal title="" {onClose} size="large"> + <ModalBody> {#if showProductActivated} <PurchaseActivationSuccess onDone={onClose} /> {:else} @@ -26,5 +25,5 @@ showMessage={false} /> {/if} - </FullScreenModal> -</Portal> + </ModalBody> +</Modal> diff --git a/web/src/lib/components/shared-components/server-about-modal.svelte b/web/src/lib/modals/ServerAboutModal.svelte similarity index 96% rename from web/src/lib/components/shared-components/server-about-modal.svelte rename to web/src/lib/modals/ServerAboutModal.svelte index 1284bb126d..f8f70387e6 100644 --- a/web/src/lib/components/shared-components/server-about-modal.svelte +++ b/web/src/lib/modals/ServerAboutModal.svelte @@ -1,12 +1,11 @@ <script lang="ts"> - import FullScreenModal from '$lib/components/shared-components/full-screen-modal.svelte'; - import Portal from '$lib/components/shared-components/portal/portal.svelte'; - import { type ServerAboutResponseDto, type ServerVersionHistoryResponseDto } from '@immich/sdk'; - import { DateTime } from 'luxon'; - import { t } from 'svelte-i18n'; - import { mdiAlert } from '@mdi/js'; import Icon from '$lib/components/elements/icon.svelte'; import { locale } from '$lib/stores/preferences.store'; + import { type ServerAboutResponseDto, type ServerVersionHistoryResponseDto } from '@immich/sdk'; + import { Modal, ModalBody } from '@immich/ui'; + import { mdiAlert } from '@mdi/js'; + import { DateTime } from 'luxon'; + import { t } from 'svelte-i18n'; interface Props { onClose: () => void; @@ -17,8 +16,8 @@ let { onClose, info, versions }: Props = $props(); </script> -<Portal> - <FullScreenModal title={$t('about')} {onClose}> +<Modal title={$t('about')} {onClose}> + <ModalBody> <div class="flex flex-col sm:grid sm:grid-cols-2 gap-1 text-immich-primary dark:text-immich-dark-primary"> <div> <label class="font-medium text-immich-primary dark:text-immich-dark-primary text-sm" for="version-desc" @@ -199,5 +198,5 @@ </ul> </div> </div> - </FullScreenModal> -</Portal> + </ModalBody> +</Modal>