mirror of
https://github.com/immich-app/immich.git
synced 2026-07-10 14:14:00 -07:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3071a4ea21 | |||
| 19313e75fd | |||
| 352c8086f9 |
@@ -0,0 +1,50 @@
|
||||
name: Update F-Droid Repo
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
update-index:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ !github.event.release.prerelease }}
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Checkout pubspec for versionCode
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
ref: ${{ github.event.release.tag_name }}
|
||||
persist-credentials: false
|
||||
sparse-checkout: mobile/pubspec.yaml
|
||||
sparse-checkout-cone-mode: false
|
||||
|
||||
- name: Update F-Droid repo
|
||||
env:
|
||||
GITLAB_TOKEN: ${{ secrets.FDROID_REPO_TOKEN }}
|
||||
RELEASE: ${{ toJSON(github.event.release) }}
|
||||
TAG: ${{ github.event.release.tag_name }}
|
||||
run: |
|
||||
export VERSION_CODE=$(yq '.version' mobile/pubspec.yaml | cut -d+ -f2)
|
||||
|
||||
git clone --depth 1 "https://oauth2:${GITLAB_TOKEN}@gitlab.futo.org/fdroid/repo-v2.git" fdroid-repo
|
||||
cd fdroid-repo
|
||||
|
||||
yq -i '
|
||||
[env(RELEASE).assets[] | select(.name == "app-release.apk")] as $matches |
|
||||
with(select($matches | length == 0); error("app-release.apk not found in release assets")) |
|
||||
with(select([.apks[] | select(."version-code" == env(VERSION_CODE))] | length > 0); error("version-code already in index")) |
|
||||
.apks = [{
|
||||
"url": $matches[0].browser_download_url,
|
||||
"sha256sum": ($matches[0].digest | sub("^sha256:", "")),
|
||||
"date": (env(RELEASE).published_at | split("T") | .[0]),
|
||||
"version-code": env(VERSION_CODE)
|
||||
}] + .apks
|
||||
' apps/Immich/index.yml
|
||||
|
||||
git config user.name "Immich Release Bot"
|
||||
git config user.email "bot@immich.app"
|
||||
git commit -am "Immich: add version-code ${VERSION_CODE} (${TAG})"
|
||||
git push
|
||||
+2
-2
@@ -431,8 +431,8 @@
|
||||
"transcoding_realtime_description": "Permet au transcodage d'être réalisé en temps réel durant la diffusion du flux de la vidéo. Active la bascule automatique entre résolutions, mais peut entraîner une latence importante de lecture et des microcoupures en fonction des capacités du serveur.",
|
||||
"transcoding_realtime_enabled": "Activer la transcodification en temps réel",
|
||||
"transcoding_realtime_enabled_description": "Si désactivé, le serveur refusera de démarrer de nouvelles sessions de transcodifications en temps réel.",
|
||||
"transcoding_realtime_resolutions": "Définitions",
|
||||
"transcoding_realtime_resolutions_description": "Les définitions proposées pour le transcodage en temps réel. Des définitions supérieures peuvent causer des soucis pendant la lecture si le serveur ne peut pas les transcoder suffisamment rapidement.",
|
||||
"transcoding_realtime_resolutions": "Résolutions",
|
||||
"transcoding_realtime_resolutions_description": "Les résolutions proposées pour le transcodage en temps réel. Des résolutions supérieures peuvent causer des soucis pendant la lecture si le serveur ne peut pas les transcoder suffisamment rapidement.",
|
||||
"transcoding_realtime_video_codecs": "Codecs vidéo",
|
||||
"transcoding_realtime_video_codecs_description": "Les codecs vidéo proposés pour le transcodage en temps réel. Les clients choisissent la meilleure option qu'ils supportent durant la lecture. AV1 est plus efficace que HEVC, qui est plus efficace que H.264. En utilisant l'accélération matérielle, sélectionnez seulement les codecs que l'accélérateur peut encoder. En utilisant le transcodage logiciel, notez que H.264 est plus rapide que AV1, qui est plus rapide que HEVC.",
|
||||
"transcoding_reference_frames": "Trames de référence",
|
||||
|
||||
@@ -7,6 +7,16 @@ import { preferencesFactory } from '@test-data/factories/preferences-factory';
|
||||
import { userAdminFactory } from '@test-data/factories/user-factory';
|
||||
import AssetViewerNavBar from './AssetViewerNavBar.svelte';
|
||||
|
||||
vi.mock(import('$lib/managers/feature-flags-manager.svelte'), function () {
|
||||
return {
|
||||
featureFlagsManager: {
|
||||
init: vi.fn(),
|
||||
loadFeatureFlags: vi.fn(),
|
||||
value: { smartSearch: true, trash: true },
|
||||
} as never,
|
||||
};
|
||||
});
|
||||
|
||||
describe('AssetViewerNavBar component', () => {
|
||||
const additionalProps = {
|
||||
preAction: () => {},
|
||||
@@ -24,15 +34,6 @@ describe('AssetViewerNavBar component', () => {
|
||||
};
|
||||
});
|
||||
vi.stubGlobal('ResizeObserver', getResizeObserverMock());
|
||||
vi.mock(import('$lib/managers/feature-flags-manager.svelte'), function () {
|
||||
return {
|
||||
featureFlagsManager: {
|
||||
init: vi.fn(),
|
||||
loadFeatureFlags: vi.fn(),
|
||||
value: { smartSearch: true, trash: true },
|
||||
} as never,
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
|
||||
@@ -4,16 +4,14 @@ import { renderWithTooltips } from '$tests/helpers';
|
||||
import { assetFactory } from '@test-data/factories/asset-factory';
|
||||
import DeleteAction from './DeleteAction.svelte';
|
||||
|
||||
vi.mock(import('$lib/managers/feature-flags-manager.svelte'), () => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
return { featureFlagsManager: { init: vi.fn(), loadFeatureFlags: vi.fn(), value: { trash: true } } as any };
|
||||
});
|
||||
|
||||
let asset: AssetResponseDto;
|
||||
|
||||
describe('DeleteAction component', () => {
|
||||
beforeEach(() => {
|
||||
vi.mock(import('$lib/managers/feature-flags-manager.svelte'), () => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
return { featureFlagsManager: { init: vi.fn(), loadFeatureFlags: vi.fn(), value: { trash: true } } as any };
|
||||
});
|
||||
});
|
||||
|
||||
describe('given an asset which is not trashed yet', () => {
|
||||
beforeEach(() => {
|
||||
asset = assetFactory.build({ isTrashed: false });
|
||||
|
||||
@@ -4,6 +4,11 @@ import Thumbnail from '$lib/components/assets/thumbnail/Thumbnail.svelte';
|
||||
import { getTabbable } from '$lib/utils/focus-util';
|
||||
import { assetFactory } from '@test-data/factories/asset-factory';
|
||||
|
||||
vi.mock('$lib/utils/navigation', () => ({
|
||||
currentUrlReplaceAssetId: vi.fn(),
|
||||
isSharedLinkRoute: vi.fn().mockReturnValue(false),
|
||||
}));
|
||||
|
||||
vi.hoisted(() => {
|
||||
Object.defineProperty(globalThis, 'matchMedia', {
|
||||
writable: true,
|
||||
@@ -26,10 +31,6 @@ vi.hoisted(() => {
|
||||
describe('Thumbnail component', () => {
|
||||
beforeAll(() => {
|
||||
vi.stubGlobal('IntersectionObserver', getIntersectionObserverMock());
|
||||
vi.mock('$lib/utils/navigation', () => ({
|
||||
currentUrlReplaceAssetId: vi.fn(),
|
||||
isSharedLinkRoute: vi.fn().mockReturnValue(false),
|
||||
}));
|
||||
});
|
||||
|
||||
it('should only contain a single tabbable element (the container)', () => {
|
||||
|
||||
@@ -16,7 +16,7 @@ describe('RecentAlbums component', () => {
|
||||
sdkMock.getAllAlbums.mockResolvedValueOnce([...albums]);
|
||||
render(RecentAlbums);
|
||||
|
||||
expect(sdkMock.getAllAlbums).toBeCalledTimes(1);
|
||||
expect(sdkMock.getAllAlbums).toHaveBeenCalledOnce();
|
||||
|
||||
// wtf
|
||||
await tick();
|
||||
|
||||
@@ -35,7 +35,7 @@ describe('FormatMessage component', () => {
|
||||
|
||||
it('throws an error when locale is empty', async () => {
|
||||
await locale.set(undefined);
|
||||
expect(() => render(FormatMessage, { key: '' as Translations })).toThrowError();
|
||||
expect(() => render(FormatMessage, { key: '' as Translations })).toThrow();
|
||||
await locale.set('en');
|
||||
});
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ describe('TimelineManager', () => {
|
||||
});
|
||||
|
||||
it('should load months in viewport', () => {
|
||||
expect(sdkMock.getTimeBuckets).toBeCalledTimes(1);
|
||||
expect(sdkMock.getTimeBuckets).toHaveBeenCalledOnce();
|
||||
expect(sdkMock.getTimeBucket).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
@@ -133,13 +133,13 @@ describe('TimelineManager', () => {
|
||||
it('loads a month', async () => {
|
||||
expect(getTimelineMonthByDate(timelineManager, { year: 2024, month: 1 })?.getAssets().length).toEqual(0);
|
||||
await timelineManager.loadTimelineMonth({ year: 2024, month: 1 });
|
||||
expect(sdkMock.getTimeBucket).toBeCalledTimes(1);
|
||||
expect(sdkMock.getTimeBucket).toHaveBeenCalledOnce();
|
||||
expect(getTimelineMonthByDate(timelineManager, { year: 2024, month: 1 })?.getAssets().length).toEqual(3);
|
||||
});
|
||||
|
||||
it('ignores invalid months', async () => {
|
||||
await timelineManager.loadTimelineMonth({ year: 2023, month: 1 });
|
||||
expect(sdkMock.getTimeBucket).toBeCalledTimes(0);
|
||||
expect(sdkMock.getTimeBucket).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('cancels month loading', async () => {
|
||||
@@ -147,7 +147,7 @@ describe('TimelineManager', () => {
|
||||
void timelineManager.loadTimelineMonth({ year: 2024, month: 1 });
|
||||
const abortSpy = vi.spyOn(month!.loader!.cancelToken!, 'abort');
|
||||
month?.cancel();
|
||||
expect(abortSpy).toBeCalledTimes(1);
|
||||
expect(abortSpy).toHaveBeenCalledOnce();
|
||||
await timelineManager.loadTimelineMonth({ year: 2024, month: 1 });
|
||||
expect(getTimelineMonthByDate(timelineManager, { year: 2024, month: 1 })?.getAssets().length).toEqual(3);
|
||||
});
|
||||
@@ -157,10 +157,10 @@ describe('TimelineManager', () => {
|
||||
timelineManager.loadTimelineMonth({ year: 2024, month: 1 }),
|
||||
timelineManager.loadTimelineMonth({ year: 2024, month: 1 }),
|
||||
]);
|
||||
expect(sdkMock.getTimeBucket).toBeCalledTimes(1);
|
||||
expect(sdkMock.getTimeBucket).toHaveBeenCalledOnce();
|
||||
|
||||
await timelineManager.loadTimelineMonth({ year: 2024, month: 1 });
|
||||
expect(sdkMock.getTimeBucket).toBeCalledTimes(1);
|
||||
expect(sdkMock.getTimeBucket).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it('allows loading a canceled month', async () => {
|
||||
@@ -283,7 +283,7 @@ describe('TimelineManager', () => {
|
||||
const asset = deriveLocalDateTimeFromFileCreatedAt(timelineAssetFactory.build());
|
||||
timelineManager.upsertAssets([asset]);
|
||||
|
||||
expect(updateAssetsSpy).toBeCalledWith([asset]);
|
||||
expect(updateAssetsSpy).toHaveBeenCalledWith([asset]);
|
||||
expect(timelineManager.assetCount).toEqual(1);
|
||||
});
|
||||
|
||||
@@ -642,8 +642,8 @@ describe('TimelineManager', () => {
|
||||
const previousMonthSpy = vi.spyOn(previousMonth!.loader!, 'execute');
|
||||
const previous = await timelineManager.getLaterAsset(a);
|
||||
expect(previous).toEqual(b);
|
||||
expect(loadTimelineMonthSpy).toBeCalledTimes(0);
|
||||
expect(previousMonthSpy).toBeCalledTimes(0);
|
||||
expect(loadTimelineMonthSpy).not.toHaveBeenCalled();
|
||||
expect(previousMonthSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('skips removed assets', async () => {
|
||||
|
||||
@@ -2,17 +2,15 @@ import type { ServerConfigDto } from '@immich/sdk';
|
||||
import { asUrl } from '$lib/services/shared-link.service';
|
||||
import { sharedLinkFactory } from '@test-data/factories/shared-link-factory';
|
||||
|
||||
describe('SharedLinkService', () => {
|
||||
beforeAll(() => {
|
||||
vi.mock(import('$lib/managers/server-config-manager.svelte'), () => ({
|
||||
serverConfigManager: {
|
||||
value: { externalDomain: 'http://localhost:2283' } as ServerConfigDto,
|
||||
init: vi.fn(),
|
||||
loadServerConfig: vi.fn(),
|
||||
},
|
||||
}));
|
||||
});
|
||||
vi.mock(import('$lib/managers/server-config-manager.svelte'), () => ({
|
||||
serverConfigManager: {
|
||||
value: { externalDomain: 'http://localhost:2283' } as ServerConfigDto,
|
||||
init: vi.fn(),
|
||||
loadServerConfig: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
describe('SharedLinkService', () => {
|
||||
describe('asUrl', () => {
|
||||
it('should properly encode characters in slug', () => {
|
||||
expect(asUrl(sharedLinkFactory.build({ slug: 'foo/bar' }))).toBe('http://localhost:2283/s/foo%2Fbar');
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import { writable } from 'svelte/store';
|
||||
import { getAlbumDateRange, getShortDateRange } from './date-time';
|
||||
|
||||
vitest.mock('$lib/stores/preferences.store', () => ({
|
||||
locale: writable('en'),
|
||||
}));
|
||||
|
||||
describe('getShortDateRange', () => {
|
||||
beforeEach(() => {
|
||||
vi.stubEnv('TZ', 'UTC');
|
||||
@@ -41,10 +45,6 @@ describe('getShortDateRange', () => {
|
||||
describe('getAlbumDate', () => {
|
||||
beforeAll(() => {
|
||||
process.env.TZ = 'UTC';
|
||||
|
||||
vitest.mock('$lib/stores/preferences.store', () => ({
|
||||
locale: writable('en'),
|
||||
}));
|
||||
});
|
||||
|
||||
it('should work with only a start date', () => {
|
||||
|
||||
@@ -34,7 +34,7 @@ describe('Executor Queue test', function () {
|
||||
// The last task will be executed after 200ms and will finish at 400ms
|
||||
void eq.addTask(() => timeoutPromiseBuilder(200, 'T4'));
|
||||
|
||||
expect(finished).not.toBeCalled();
|
||||
expect(finished).not.toHaveBeenCalled();
|
||||
expect(started).toHaveBeenCalledTimes(3);
|
||||
|
||||
vi.advanceTimersByTime(100);
|
||||
|
||||
Reference in New Issue
Block a user