mirror of
https://github.com/immich-app/immich.git
synced 2025-01-03 13:09:27 +02:00
refactor: e2e (#7703)
* refactor: e2e * fix: submodule check * chore: extend startup timeout
This commit is contained in:
parent
2dcd0e516f
commit
b733a29430
@ -5,7 +5,8 @@
|
|||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "vitest --config vitest.config.ts",
|
"test": "vitest --run",
|
||||||
|
"test:watch": "vitest",
|
||||||
"test:web": "npx playwright test",
|
"test:web": "npx playwright test",
|
||||||
"start:web": "npx playwright test --ui",
|
"start:web": "npx playwright test --ui",
|
||||||
"format": "prettier --check .",
|
"format": "prettier --check .",
|
||||||
|
@ -9,7 +9,7 @@ import {
|
|||||||
} from '@immich/sdk';
|
} from '@immich/sdk';
|
||||||
import { createUserDto, uuidDto } from 'src/fixtures';
|
import { createUserDto, uuidDto } from 'src/fixtures';
|
||||||
import { errorDto } from 'src/responses';
|
import { errorDto } from 'src/responses';
|
||||||
import { apiUtils, app, asBearerAuth, dbUtils } from 'src/utils';
|
import { app, asBearerAuth, utils } from 'src/utils';
|
||||||
import request from 'supertest';
|
import request from 'supertest';
|
||||||
import { beforeAll, beforeEach, describe, expect, it } from 'vitest';
|
import { beforeAll, beforeEach, describe, expect, it } from 'vitest';
|
||||||
|
|
||||||
@ -23,12 +23,11 @@ describe('/activity', () => {
|
|||||||
create({ activityCreateDto: dto }, { headers: asBearerAuth(accessToken || admin.accessToken) });
|
create({ activityCreateDto: dto }, { headers: asBearerAuth(accessToken || admin.accessToken) });
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
apiUtils.setup();
|
await utils.resetDatabase();
|
||||||
await dbUtils.reset();
|
|
||||||
|
|
||||||
admin = await apiUtils.adminSetup();
|
admin = await utils.adminSetup();
|
||||||
nonOwner = await apiUtils.userSetup(admin.accessToken, createUserDto.user1);
|
nonOwner = await utils.userSetup(admin.accessToken, createUserDto.user1);
|
||||||
asset = await apiUtils.createAsset(admin.accessToken);
|
asset = await utils.createAsset(admin.accessToken);
|
||||||
album = await createAlbum(
|
album = await createAlbum(
|
||||||
{
|
{
|
||||||
createAlbumDto: {
|
createAlbumDto: {
|
||||||
@ -42,7 +41,7 @@ describe('/activity', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await dbUtils.reset(['activity']);
|
await utils.resetDatabase(['activity']);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('GET /activity', () => {
|
describe('GET /activity', () => {
|
||||||
|
@ -7,7 +7,7 @@ import {
|
|||||||
} from '@immich/sdk';
|
} from '@immich/sdk';
|
||||||
import { createUserDto, uuidDto } from 'src/fixtures';
|
import { createUserDto, uuidDto } from 'src/fixtures';
|
||||||
import { errorDto } from 'src/responses';
|
import { errorDto } from 'src/responses';
|
||||||
import { apiUtils, app, asBearerAuth, dbUtils } from 'src/utils';
|
import { app, asBearerAuth, utils } from 'src/utils';
|
||||||
import request from 'supertest';
|
import request from 'supertest';
|
||||||
import { beforeAll, beforeEach, describe, expect, it } from 'vitest';
|
import { beforeAll, beforeEach, describe, expect, it } from 'vitest';
|
||||||
|
|
||||||
@ -29,49 +29,48 @@ describe('/album', () => {
|
|||||||
let user3: LoginResponseDto; // deleted
|
let user3: LoginResponseDto; // deleted
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
apiUtils.setup();
|
await utils.resetDatabase();
|
||||||
await dbUtils.reset();
|
|
||||||
|
|
||||||
admin = await apiUtils.adminSetup();
|
admin = await utils.adminSetup();
|
||||||
|
|
||||||
[user1, user2, user3] = await Promise.all([
|
[user1, user2, user3] = await Promise.all([
|
||||||
apiUtils.userSetup(admin.accessToken, createUserDto.user1),
|
utils.userSetup(admin.accessToken, createUserDto.user1),
|
||||||
apiUtils.userSetup(admin.accessToken, createUserDto.user2),
|
utils.userSetup(admin.accessToken, createUserDto.user2),
|
||||||
apiUtils.userSetup(admin.accessToken, createUserDto.user3),
|
utils.userSetup(admin.accessToken, createUserDto.user3),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
[user1Asset1, user1Asset2] = await Promise.all([
|
[user1Asset1, user1Asset2] = await Promise.all([
|
||||||
apiUtils.createAsset(user1.accessToken, { isFavorite: true }),
|
utils.createAsset(user1.accessToken, { isFavorite: true }),
|
||||||
apiUtils.createAsset(user1.accessToken),
|
utils.createAsset(user1.accessToken),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const albums = await Promise.all([
|
const albums = await Promise.all([
|
||||||
// user 1
|
// user 1
|
||||||
apiUtils.createAlbum(user1.accessToken, {
|
utils.createAlbum(user1.accessToken, {
|
||||||
albumName: user1SharedUser,
|
albumName: user1SharedUser,
|
||||||
sharedWithUserIds: [user2.userId],
|
sharedWithUserIds: [user2.userId],
|
||||||
assetIds: [user1Asset1.id],
|
assetIds: [user1Asset1.id],
|
||||||
}),
|
}),
|
||||||
apiUtils.createAlbum(user1.accessToken, {
|
utils.createAlbum(user1.accessToken, {
|
||||||
albumName: user1SharedLink,
|
albumName: user1SharedLink,
|
||||||
assetIds: [user1Asset1.id],
|
assetIds: [user1Asset1.id],
|
||||||
}),
|
}),
|
||||||
apiUtils.createAlbum(user1.accessToken, {
|
utils.createAlbum(user1.accessToken, {
|
||||||
albumName: user1NotShared,
|
albumName: user1NotShared,
|
||||||
assetIds: [user1Asset1.id, user1Asset2.id],
|
assetIds: [user1Asset1.id, user1Asset2.id],
|
||||||
}),
|
}),
|
||||||
|
|
||||||
// user 2
|
// user 2
|
||||||
apiUtils.createAlbum(user2.accessToken, {
|
utils.createAlbum(user2.accessToken, {
|
||||||
albumName: user2SharedUser,
|
albumName: user2SharedUser,
|
||||||
sharedWithUserIds: [user1.userId],
|
sharedWithUserIds: [user1.userId],
|
||||||
assetIds: [user1Asset1.id],
|
assetIds: [user1Asset1.id],
|
||||||
}),
|
}),
|
||||||
apiUtils.createAlbum(user2.accessToken, { albumName: user2SharedLink }),
|
utils.createAlbum(user2.accessToken, { albumName: user2SharedLink }),
|
||||||
apiUtils.createAlbum(user2.accessToken, { albumName: user2NotShared }),
|
utils.createAlbum(user2.accessToken, { albumName: user2NotShared }),
|
||||||
|
|
||||||
// user 3
|
// user 3
|
||||||
apiUtils.createAlbum(user3.accessToken, {
|
utils.createAlbum(user3.accessToken, {
|
||||||
albumName: 'Deleted',
|
albumName: 'Deleted',
|
||||||
sharedWithUserIds: [user1.userId],
|
sharedWithUserIds: [user1.userId],
|
||||||
}),
|
}),
|
||||||
@ -82,12 +81,12 @@ describe('/album', () => {
|
|||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
// add shared link to user1SharedLink album
|
// add shared link to user1SharedLink album
|
||||||
apiUtils.createSharedLink(user1.accessToken, {
|
utils.createSharedLink(user1.accessToken, {
|
||||||
type: SharedLinkType.Album,
|
type: SharedLinkType.Album,
|
||||||
albumId: user1Albums[1].id,
|
albumId: user1Albums[1].id,
|
||||||
}),
|
}),
|
||||||
// add shared link to user2SharedLink album
|
// add shared link to user2SharedLink album
|
||||||
apiUtils.createSharedLink(user2.accessToken, {
|
utils.createSharedLink(user2.accessToken, {
|
||||||
type: SharedLinkType.Album,
|
type: SharedLinkType.Album,
|
||||||
albumId: user2Albums[1].id,
|
albumId: user2Albums[1].id,
|
||||||
}),
|
}),
|
||||||
@ -366,7 +365,7 @@ describe('/album', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should be able to add own asset to own album', async () => {
|
it('should be able to add own asset to own album', async () => {
|
||||||
const asset = await apiUtils.createAsset(user1.accessToken);
|
const asset = await utils.createAsset(user1.accessToken);
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.put(`/album/${user1Albums[0].id}/assets`)
|
.put(`/album/${user1Albums[0].id}/assets`)
|
||||||
.set('Authorization', `Bearer ${user1.accessToken}`)
|
.set('Authorization', `Bearer ${user1.accessToken}`)
|
||||||
@ -377,7 +376,7 @@ describe('/album', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should be able to add own asset to shared album', async () => {
|
it('should be able to add own asset to shared album', async () => {
|
||||||
const asset = await apiUtils.createAsset(user1.accessToken);
|
const asset = await utils.createAsset(user1.accessToken);
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.put(`/album/${user2Albums[0].id}/assets`)
|
.put(`/album/${user2Albums[0].id}/assets`)
|
||||||
.set('Authorization', `Bearer ${user1.accessToken}`)
|
.set('Authorization', `Bearer ${user1.accessToken}`)
|
||||||
@ -398,7 +397,7 @@ describe('/album', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should update an album', async () => {
|
it('should update an album', async () => {
|
||||||
const album = await apiUtils.createAlbum(user1.accessToken, {
|
const album = await utils.createAlbum(user1.accessToken, {
|
||||||
albumName: 'New album',
|
albumName: 'New album',
|
||||||
});
|
});
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
@ -485,7 +484,7 @@ describe('/album', () => {
|
|||||||
let album: AlbumResponseDto;
|
let album: AlbumResponseDto;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
album = await apiUtils.createAlbum(user1.accessToken, {
|
album = await utils.createAlbum(user1.accessToken, {
|
||||||
albumName: 'testAlbum',
|
albumName: 'testAlbum',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -12,7 +12,7 @@ import { basename, join } from 'node:path';
|
|||||||
import { Socket } from 'socket.io-client';
|
import { Socket } from 'socket.io-client';
|
||||||
import { createUserDto, uuidDto } from 'src/fixtures';
|
import { createUserDto, uuidDto } from 'src/fixtures';
|
||||||
import { errorDto } from 'src/responses';
|
import { errorDto } from 'src/responses';
|
||||||
import { apiUtils, app, dbUtils, fileUtils, tempDir, testAssetDir, wsUtils } from 'src/utils';
|
import { app, tempDir, testAssetDir, utils } from 'src/utils';
|
||||||
import request from 'supertest';
|
import request from 'supertest';
|
||||||
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
|
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
|
||||||
|
|
||||||
@ -44,42 +44,41 @@ describe('/asset', () => {
|
|||||||
let ws: Socket;
|
let ws: Socket;
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
apiUtils.setup();
|
await utils.resetDatabase();
|
||||||
await dbUtils.reset();
|
admin = await utils.adminSetup({ onboarding: false });
|
||||||
admin = await apiUtils.adminSetup({ onboarding: false });
|
|
||||||
|
|
||||||
[ws, user1, user2, userStats] = await Promise.all([
|
[ws, user1, user2, userStats] = await Promise.all([
|
||||||
wsUtils.connect(admin.accessToken),
|
utils.connectWebsocket(admin.accessToken),
|
||||||
apiUtils.userSetup(admin.accessToken, createUserDto.user1),
|
utils.userSetup(admin.accessToken, createUserDto.user1),
|
||||||
apiUtils.userSetup(admin.accessToken, createUserDto.user2),
|
utils.userSetup(admin.accessToken, createUserDto.user2),
|
||||||
apiUtils.userSetup(admin.accessToken, createUserDto.user3),
|
utils.userSetup(admin.accessToken, createUserDto.user3),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// asset location
|
// asset location
|
||||||
assetLocation = await apiUtils.createAsset(admin.accessToken, {
|
assetLocation = await utils.createAsset(admin.accessToken, {
|
||||||
assetData: {
|
assetData: {
|
||||||
filename: 'thompson-springs.jpg',
|
filename: 'thompson-springs.jpg',
|
||||||
bytes: await readFile(locationAssetFilepath),
|
bytes: await readFile(locationAssetFilepath),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
await wsUtils.waitForEvent({ event: 'upload', assetId: assetLocation.id });
|
await utils.waitForWebsocketEvent({ event: 'upload', assetId: assetLocation.id });
|
||||||
|
|
||||||
user1Assets = await Promise.all([
|
user1Assets = await Promise.all([
|
||||||
apiUtils.createAsset(user1.accessToken),
|
utils.createAsset(user1.accessToken),
|
||||||
apiUtils.createAsset(user1.accessToken),
|
utils.createAsset(user1.accessToken),
|
||||||
apiUtils.createAsset(user1.accessToken, {
|
utils.createAsset(user1.accessToken, {
|
||||||
isFavorite: true,
|
isFavorite: true,
|
||||||
isReadOnly: true,
|
isReadOnly: true,
|
||||||
fileCreatedAt: yesterday.toISO(),
|
fileCreatedAt: yesterday.toISO(),
|
||||||
fileModifiedAt: yesterday.toISO(),
|
fileModifiedAt: yesterday.toISO(),
|
||||||
assetData: { filename: 'example.mp4' },
|
assetData: { filename: 'example.mp4' },
|
||||||
}),
|
}),
|
||||||
apiUtils.createAsset(user1.accessToken),
|
utils.createAsset(user1.accessToken),
|
||||||
apiUtils.createAsset(user1.accessToken),
|
utils.createAsset(user1.accessToken),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
user2Assets = await Promise.all([apiUtils.createAsset(user2.accessToken)]);
|
user2Assets = await Promise.all([utils.createAsset(user2.accessToken)]);
|
||||||
|
|
||||||
for (const asset of [...user1Assets, ...user2Assets]) {
|
for (const asset of [...user1Assets, ...user2Assets]) {
|
||||||
expect(asset.duplicate).toBe(false);
|
expect(asset.duplicate).toBe(false);
|
||||||
@ -87,27 +86,27 @@ describe('/asset', () => {
|
|||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
// stats
|
// stats
|
||||||
apiUtils.createAsset(userStats.accessToken),
|
utils.createAsset(userStats.accessToken),
|
||||||
apiUtils.createAsset(userStats.accessToken, { isFavorite: true }),
|
utils.createAsset(userStats.accessToken, { isFavorite: true }),
|
||||||
apiUtils.createAsset(userStats.accessToken, { isArchived: true }),
|
utils.createAsset(userStats.accessToken, { isArchived: true }),
|
||||||
apiUtils.createAsset(userStats.accessToken, {
|
utils.createAsset(userStats.accessToken, {
|
||||||
isArchived: true,
|
isArchived: true,
|
||||||
isFavorite: true,
|
isFavorite: true,
|
||||||
assetData: { filename: 'example.mp4' },
|
assetData: { filename: 'example.mp4' },
|
||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const person1 = await apiUtils.createPerson(user1.accessToken, {
|
const person1 = await utils.createPerson(user1.accessToken, {
|
||||||
name: 'Test Person',
|
name: 'Test Person',
|
||||||
});
|
});
|
||||||
await dbUtils.createFace({
|
await utils.createFace({
|
||||||
assetId: user1Assets[0].id,
|
assetId: user1Assets[0].id,
|
||||||
personId: person1.id,
|
personId: person1.id,
|
||||||
});
|
});
|
||||||
}, 30_000);
|
}, 30_000);
|
||||||
|
|
||||||
afterAll(() => {
|
afterAll(() => {
|
||||||
wsUtils.disconnect(ws);
|
utils.disconnectWebsocket(ws);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('GET /asset/:id', () => {
|
describe('GET /asset/:id', () => {
|
||||||
@ -142,7 +141,7 @@ describe('/asset', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should work with a shared link', async () => {
|
it('should work with a shared link', async () => {
|
||||||
const sharedLink = await apiUtils.createSharedLink(user1.accessToken, {
|
const sharedLink = await utils.createSharedLink(user1.accessToken, {
|
||||||
type: SharedLinkType.Individual,
|
type: SharedLinkType.Individual,
|
||||||
assetIds: [user1Assets[0].id],
|
assetIds: [user1Assets[0].id],
|
||||||
});
|
});
|
||||||
@ -172,7 +171,7 @@ describe('/asset', () => {
|
|||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
const sharedLink = await apiUtils.createSharedLink(user1.accessToken, {
|
const sharedLink = await utils.createSharedLink(user1.accessToken, {
|
||||||
type: SharedLinkType.Individual,
|
type: SharedLinkType.Individual,
|
||||||
assetIds: [user1Assets[0].id],
|
assetIds: [user1Assets[0].id],
|
||||||
});
|
});
|
||||||
@ -244,12 +243,12 @@ describe('/asset', () => {
|
|||||||
describe('GET /asset/random', () => {
|
describe('GET /asset/random', () => {
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
apiUtils.createAsset(user1.accessToken),
|
utils.createAsset(user1.accessToken),
|
||||||
apiUtils.createAsset(user1.accessToken),
|
utils.createAsset(user1.accessToken),
|
||||||
apiUtils.createAsset(user1.accessToken),
|
utils.createAsset(user1.accessToken),
|
||||||
apiUtils.createAsset(user1.accessToken),
|
utils.createAsset(user1.accessToken),
|
||||||
apiUtils.createAsset(user1.accessToken),
|
utils.createAsset(user1.accessToken),
|
||||||
apiUtils.createAsset(user1.accessToken),
|
utils.createAsset(user1.accessToken),
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -332,7 +331,7 @@ describe('/asset', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should favorite an asset', async () => {
|
it('should favorite an asset', async () => {
|
||||||
const before = await apiUtils.getAssetInfo(user1.accessToken, user1Assets[0].id);
|
const before = await utils.getAssetInfo(user1.accessToken, user1Assets[0].id);
|
||||||
expect(before.isFavorite).toBe(false);
|
expect(before.isFavorite).toBe(false);
|
||||||
|
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
@ -344,7 +343,7 @@ describe('/asset', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should archive an asset', async () => {
|
it('should archive an asset', async () => {
|
||||||
const before = await apiUtils.getAssetInfo(user1.accessToken, user1Assets[0].id);
|
const before = await utils.getAssetInfo(user1.accessToken, user1Assets[0].id);
|
||||||
expect(before.isArchived).toBe(false);
|
expect(before.isArchived).toBe(false);
|
||||||
|
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
@ -472,9 +471,9 @@ describe('/asset', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should move an asset to the trash', async () => {
|
it('should move an asset to the trash', async () => {
|
||||||
const { id: assetId } = await apiUtils.createAsset(admin.accessToken);
|
const { id: assetId } = await utils.createAsset(admin.accessToken);
|
||||||
|
|
||||||
const before = await apiUtils.getAssetInfo(admin.accessToken, assetId);
|
const before = await utils.getAssetInfo(admin.accessToken, assetId);
|
||||||
expect(before.isTrashed).toBe(false);
|
expect(before.isTrashed).toBe(false);
|
||||||
|
|
||||||
const { status } = await request(app)
|
const { status } = await request(app)
|
||||||
@ -483,7 +482,7 @@ describe('/asset', () => {
|
|||||||
.set('Authorization', `Bearer ${admin.accessToken}`);
|
.set('Authorization', `Bearer ${admin.accessToken}`);
|
||||||
expect(status).toBe(204);
|
expect(status).toBe(204);
|
||||||
|
|
||||||
const after = await apiUtils.getAssetInfo(admin.accessToken, assetId);
|
const after = await utils.getAssetInfo(admin.accessToken, assetId);
|
||||||
expect(after.isTrashed).toBe(true);
|
expect(after.isTrashed).toBe(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -604,15 +603,15 @@ describe('/asset', () => {
|
|||||||
for (const { input, expected } of tests) {
|
for (const { input, expected } of tests) {
|
||||||
it(`should generate a thumbnail for ${input}`, async () => {
|
it(`should generate a thumbnail for ${input}`, async () => {
|
||||||
const filepath = join(testAssetDir, input);
|
const filepath = join(testAssetDir, input);
|
||||||
const { id, duplicate } = await apiUtils.createAsset(admin.accessToken, {
|
const { id, duplicate } = await utils.createAsset(admin.accessToken, {
|
||||||
assetData: { bytes: await readFile(filepath), filename: basename(filepath) },
|
assetData: { bytes: await readFile(filepath), filename: basename(filepath) },
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(duplicate).toBe(false);
|
expect(duplicate).toBe(false);
|
||||||
|
|
||||||
await wsUtils.waitForEvent({ event: 'upload', assetId: id });
|
await utils.waitForWebsocketEvent({ event: 'upload', assetId: id });
|
||||||
|
|
||||||
const asset = await apiUtils.getAssetInfo(admin.accessToken, id);
|
const asset = await utils.getAssetInfo(admin.accessToken, id);
|
||||||
|
|
||||||
expect(asset.exifInfo).toBeDefined();
|
expect(asset.exifInfo).toBeDefined();
|
||||||
expect(asset.exifInfo).toMatchObject(expected.exifInfo);
|
expect(asset.exifInfo).toMatchObject(expected.exifInfo);
|
||||||
@ -622,7 +621,7 @@ describe('/asset', () => {
|
|||||||
|
|
||||||
it('should handle a duplicate', async () => {
|
it('should handle a duplicate', async () => {
|
||||||
const filepath = 'formats/jpeg/el_torcal_rocks.jpeg';
|
const filepath = 'formats/jpeg/el_torcal_rocks.jpeg';
|
||||||
const { duplicate } = await apiUtils.createAsset(admin.accessToken, {
|
const { duplicate } = await utils.createAsset(admin.accessToken, {
|
||||||
assetData: {
|
assetData: {
|
||||||
bytes: await readFile(join(testAssetDir, filepath)),
|
bytes: await readFile(join(testAssetDir, filepath)),
|
||||||
filename: basename(filepath),
|
filename: basename(filepath),
|
||||||
@ -654,21 +653,21 @@ describe('/asset', () => {
|
|||||||
|
|
||||||
for (const { filepath, checksum } of motionTests) {
|
for (const { filepath, checksum } of motionTests) {
|
||||||
it(`should extract motionphoto video from ${filepath}`, async () => {
|
it(`should extract motionphoto video from ${filepath}`, async () => {
|
||||||
const response = await apiUtils.createAsset(admin.accessToken, {
|
const response = await utils.createAsset(admin.accessToken, {
|
||||||
assetData: {
|
assetData: {
|
||||||
bytes: await readFile(join(testAssetDir, filepath)),
|
bytes: await readFile(join(testAssetDir, filepath)),
|
||||||
filename: basename(filepath),
|
filename: basename(filepath),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
await wsUtils.waitForEvent({ event: 'upload', assetId: response.id });
|
await utils.waitForWebsocketEvent({ event: 'upload', assetId: response.id });
|
||||||
|
|
||||||
expect(response.duplicate).toBe(false);
|
expect(response.duplicate).toBe(false);
|
||||||
|
|
||||||
const asset = await apiUtils.getAssetInfo(admin.accessToken, response.id);
|
const asset = await utils.getAssetInfo(admin.accessToken, response.id);
|
||||||
expect(asset.livePhotoVideoId).toBeDefined();
|
expect(asset.livePhotoVideoId).toBeDefined();
|
||||||
|
|
||||||
const video = await apiUtils.getAssetInfo(admin.accessToken, asset.livePhotoVideoId as string);
|
const video = await utils.getAssetInfo(admin.accessToken, asset.livePhotoVideoId as string);
|
||||||
expect(video.checksum).toStrictEqual(checksum);
|
expect(video.checksum).toStrictEqual(checksum);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -687,7 +686,7 @@ describe('/asset', () => {
|
|||||||
.get(`/asset/thumbnail/${assetLocation.id}?format=WEBP`)
|
.get(`/asset/thumbnail/${assetLocation.id}?format=WEBP`)
|
||||||
.set('Authorization', `Bearer ${admin.accessToken}`);
|
.set('Authorization', `Bearer ${admin.accessToken}`);
|
||||||
|
|
||||||
await wsUtils.waitForEvent({
|
await utils.waitForWebsocketEvent({
|
||||||
event: 'upload',
|
event: 'upload',
|
||||||
assetId: assetLocation.id,
|
assetId: assetLocation.id,
|
||||||
});
|
});
|
||||||
@ -733,11 +732,11 @@ describe('/asset', () => {
|
|||||||
expect(body).toBeDefined();
|
expect(body).toBeDefined();
|
||||||
expect(type).toBe('image/jpeg');
|
expect(type).toBe('image/jpeg');
|
||||||
|
|
||||||
const asset = await apiUtils.getAssetInfo(admin.accessToken, assetLocation.id);
|
const asset = await utils.getAssetInfo(admin.accessToken, assetLocation.id);
|
||||||
|
|
||||||
const original = await readFile(locationAssetFilepath);
|
const original = await readFile(locationAssetFilepath);
|
||||||
const originalChecksum = fileUtils.sha1(original);
|
const originalChecksum = utils.sha1(original);
|
||||||
const downloadChecksum = fileUtils.sha1(body);
|
const downloadChecksum = utils.sha1(body);
|
||||||
|
|
||||||
expect(originalChecksum).toBe(downloadChecksum);
|
expect(originalChecksum).toBe(downloadChecksum);
|
||||||
expect(downloadChecksum).toBe(asset.checksum);
|
expect(downloadChecksum).toBe(asset.checksum);
|
||||||
|
@ -1,24 +1,23 @@
|
|||||||
import { deleteAssets, getAuditFiles, updateAsset, type LoginResponseDto } from '@immich/sdk';
|
import { deleteAssets, getAuditFiles, updateAsset, type LoginResponseDto } from '@immich/sdk';
|
||||||
import { apiUtils, asBearerAuth, dbUtils, fileUtils } from 'src/utils';
|
import { asBearerAuth, utils } from 'src/utils';
|
||||||
import { beforeAll, describe, expect, it } from 'vitest';
|
import { beforeAll, describe, expect, it } from 'vitest';
|
||||||
|
|
||||||
describe('/audit', () => {
|
describe('/audit', () => {
|
||||||
let admin: LoginResponseDto;
|
let admin: LoginResponseDto;
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
apiUtils.setup();
|
await utils.resetDatabase();
|
||||||
await dbUtils.reset();
|
await utils.resetFilesystem();
|
||||||
await fileUtils.reset();
|
|
||||||
|
|
||||||
admin = await apiUtils.adminSetup();
|
admin = await utils.adminSetup();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('GET :/file-report', () => {
|
describe('GET :/file-report', () => {
|
||||||
it('excludes assets without issues from report', async () => {
|
it('excludes assets without issues from report', async () => {
|
||||||
const [trashedAsset, archivedAsset] = await Promise.all([
|
const [trashedAsset, archivedAsset] = await Promise.all([
|
||||||
apiUtils.createAsset(admin.accessToken),
|
utils.createAsset(admin.accessToken),
|
||||||
apiUtils.createAsset(admin.accessToken),
|
utils.createAsset(admin.accessToken),
|
||||||
apiUtils.createAsset(admin.accessToken),
|
utils.createAsset(admin.accessToken),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
|
@ -1,19 +1,15 @@
|
|||||||
import { LoginResponseDto, getAuthDevices, login, signUpAdmin } from '@immich/sdk';
|
import { LoginResponseDto, getAuthDevices, login, signUpAdmin } from '@immich/sdk';
|
||||||
import { loginDto, signupDto, uuidDto } from 'src/fixtures';
|
import { loginDto, signupDto, uuidDto } from 'src/fixtures';
|
||||||
import { deviceDto, errorDto, loginResponseDto, signupResponseDto } from 'src/responses';
|
import { deviceDto, errorDto, loginResponseDto, signupResponseDto } from 'src/responses';
|
||||||
import { apiUtils, app, asBearerAuth, dbUtils } from 'src/utils';
|
import { app, asBearerAuth, utils } from 'src/utils';
|
||||||
import request from 'supertest';
|
import request from 'supertest';
|
||||||
import { beforeAll, beforeEach, describe, expect, it } from 'vitest';
|
import { beforeEach, describe, expect, it } from 'vitest';
|
||||||
|
|
||||||
const { name, email, password } = signupDto.admin;
|
const { name, email, password } = signupDto.admin;
|
||||||
|
|
||||||
describe(`/auth/admin-sign-up`, () => {
|
describe(`/auth/admin-sign-up`, () => {
|
||||||
beforeAll(() => {
|
|
||||||
apiUtils.setup();
|
|
||||||
});
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await dbUtils.reset();
|
await utils.resetDatabase();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('POST /auth/admin-sign-up', () => {
|
describe('POST /auth/admin-sign-up', () => {
|
||||||
@ -84,7 +80,7 @@ describe('/auth/*', () => {
|
|||||||
let admin: LoginResponseDto;
|
let admin: LoginResponseDto;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await dbUtils.reset();
|
await utils.resetDatabase();
|
||||||
await signUpAdmin({ signUpDto: signupDto.admin });
|
await signUpAdmin({ signUpDto: signupDto.admin });
|
||||||
admin = await login({ loginCredentialDto: loginDto.admin });
|
admin = await login({ loginCredentialDto: loginDto.admin });
|
||||||
});
|
});
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { AssetFileUploadResponseDto, LoginResponseDto } from '@immich/sdk';
|
import { AssetFileUploadResponseDto, LoginResponseDto } from '@immich/sdk';
|
||||||
import { readFile, writeFile } from 'node:fs/promises';
|
import { readFile, writeFile } from 'node:fs/promises';
|
||||||
import { errorDto } from 'src/responses';
|
import { errorDto } from 'src/responses';
|
||||||
import { apiUtils, app, dbUtils, fileUtils, tempDir } from 'src/utils';
|
import { app, tempDir, utils } from 'src/utils';
|
||||||
import request from 'supertest';
|
import request from 'supertest';
|
||||||
import { beforeAll, describe, expect, it } from 'vitest';
|
import { beforeAll, describe, expect, it } from 'vitest';
|
||||||
|
|
||||||
@ -11,13 +11,9 @@ describe('/download', () => {
|
|||||||
let asset2: AssetFileUploadResponseDto;
|
let asset2: AssetFileUploadResponseDto;
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
apiUtils.setup();
|
await utils.resetDatabase();
|
||||||
await dbUtils.reset();
|
admin = await utils.adminSetup();
|
||||||
admin = await apiUtils.adminSetup();
|
[asset1, asset2] = await Promise.all([utils.createAsset(admin.accessToken), utils.createAsset(admin.accessToken)]);
|
||||||
[asset1, asset2] = await Promise.all([
|
|
||||||
apiUtils.createAsset(admin.accessToken),
|
|
||||||
apiUtils.createAsset(admin.accessToken),
|
|
||||||
]);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('POST /download/info', () => {
|
describe('POST /download/info', () => {
|
||||||
@ -65,15 +61,15 @@ describe('/download', () => {
|
|||||||
expect(body instanceof Buffer).toBe(true);
|
expect(body instanceof Buffer).toBe(true);
|
||||||
|
|
||||||
await writeFile(`${tempDir}/archive.zip`, body);
|
await writeFile(`${tempDir}/archive.zip`, body);
|
||||||
await fileUtils.unzip(`${tempDir}/archive.zip`, `${tempDir}/archive`);
|
await utils.unzip(`${tempDir}/archive.zip`, `${tempDir}/archive`);
|
||||||
const files = [
|
const files = [
|
||||||
{ filename: 'example.png', id: asset1.id },
|
{ filename: 'example.png', id: asset1.id },
|
||||||
{ filename: 'example+1.png', id: asset2.id },
|
{ filename: 'example+1.png', id: asset2.id },
|
||||||
];
|
];
|
||||||
for (const { id, filename } of files) {
|
for (const { id, filename } of files) {
|
||||||
const bytes = await readFile(`${tempDir}/archive/${filename}`);
|
const bytes = await readFile(`${tempDir}/archive/${filename}`);
|
||||||
const asset = await apiUtils.getAssetInfo(admin.accessToken, id);
|
const asset = await utils.getAssetInfo(admin.accessToken, id);
|
||||||
expect(fileUtils.sha1(bytes)).toBe(asset.checksum);
|
expect(utils.sha1(bytes)).toBe(asset.checksum);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { LibraryResponseDto, LibraryType, LoginResponseDto, getAllLibraries } from '@immich/sdk';
|
import { LibraryResponseDto, LibraryType, LoginResponseDto, getAllLibraries } from '@immich/sdk';
|
||||||
import { userDto, uuidDto } from 'src/fixtures';
|
import { userDto, uuidDto } from 'src/fixtures';
|
||||||
import { errorDto } from 'src/responses';
|
import { errorDto } from 'src/responses';
|
||||||
import { apiUtils, app, asBearerAuth, dbUtils, testAssetDirInternal } from 'src/utils';
|
import { app, asBearerAuth, testAssetDirInternal, utils } from 'src/utils';
|
||||||
import request from 'supertest';
|
import request from 'supertest';
|
||||||
import { beforeAll, describe, expect, it } from 'vitest';
|
import { beforeAll, describe, expect, it } from 'vitest';
|
||||||
|
|
||||||
@ -11,11 +11,10 @@ describe('/library', () => {
|
|||||||
let library: LibraryResponseDto;
|
let library: LibraryResponseDto;
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
apiUtils.setup();
|
await utils.resetDatabase();
|
||||||
await dbUtils.reset();
|
admin = await utils.adminSetup();
|
||||||
admin = await apiUtils.adminSetup();
|
user = await utils.userSetup(admin.accessToken, userDto.user1);
|
||||||
user = await apiUtils.userSetup(admin.accessToken, userDto.user1);
|
library = await utils.createLibrary(admin.accessToken, { type: LibraryType.External });
|
||||||
library = await apiUtils.createLibrary(admin.accessToken, { type: LibraryType.External });
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('GET /library', () => {
|
describe('GET /library', () => {
|
||||||
@ -303,7 +302,7 @@ describe('/library', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should get library by id', async () => {
|
it('should get library by id', async () => {
|
||||||
const library = await apiUtils.createLibrary(admin.accessToken, { type: LibraryType.External });
|
const library = await utils.createLibrary(admin.accessToken, { type: LibraryType.External });
|
||||||
|
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.get(`/library/${library.id}`)
|
.get(`/library/${library.id}`)
|
||||||
@ -359,7 +358,7 @@ describe('/library', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should delete an external library', async () => {
|
it('should delete an external library', async () => {
|
||||||
const library = await apiUtils.createLibrary(admin.accessToken, { type: LibraryType.External });
|
const library = await utils.createLibrary(admin.accessToken, { type: LibraryType.External });
|
||||||
|
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.delete(`/library/${library.id}`)
|
.delete(`/library/${library.id}`)
|
||||||
@ -415,14 +414,14 @@ describe('/library', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should pass with no import paths', async () => {
|
it('should pass with no import paths', async () => {
|
||||||
const response = await apiUtils.validateLibrary(admin.accessToken, library.id, { importPaths: [] });
|
const response = await utils.validateLibrary(admin.accessToken, library.id, { importPaths: [] });
|
||||||
expect(response.importPaths).toEqual([]);
|
expect(response.importPaths).toEqual([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fail if path does not exist', async () => {
|
it('should fail if path does not exist', async () => {
|
||||||
const pathToTest = `${testAssetDirInternal}/does/not/exist`;
|
const pathToTest = `${testAssetDirInternal}/does/not/exist`;
|
||||||
|
|
||||||
const response = await apiUtils.validateLibrary(admin.accessToken, library.id, {
|
const response = await utils.validateLibrary(admin.accessToken, library.id, {
|
||||||
importPaths: [pathToTest],
|
importPaths: [pathToTest],
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -439,7 +438,7 @@ describe('/library', () => {
|
|||||||
it('should fail if path is a file', async () => {
|
it('should fail if path is a file', async () => {
|
||||||
const pathToTest = `${testAssetDirInternal}/albums/nature/el_torcal_rocks.jpg`;
|
const pathToTest = `${testAssetDirInternal}/albums/nature/el_torcal_rocks.jpg`;
|
||||||
|
|
||||||
const response = await apiUtils.validateLibrary(admin.accessToken, library.id, {
|
const response = await utils.validateLibrary(admin.accessToken, library.id, {
|
||||||
importPaths: [pathToTest],
|
importPaths: [pathToTest],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,16 +1,12 @@
|
|||||||
import { errorDto } from 'src/responses';
|
import { errorDto } from 'src/responses';
|
||||||
import { apiUtils, app, dbUtils } from 'src/utils';
|
import { app, utils } from 'src/utils';
|
||||||
import request from 'supertest';
|
import request from 'supertest';
|
||||||
import { beforeAll, beforeEach, describe, expect, it } from 'vitest';
|
import { beforeAll, describe, expect, it } from 'vitest';
|
||||||
|
|
||||||
describe(`/oauth`, () => {
|
describe(`/oauth`, () => {
|
||||||
beforeAll(() => {
|
beforeAll(async () => {
|
||||||
apiUtils.setup();
|
await utils.resetDatabase();
|
||||||
});
|
await utils.adminSetup();
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
await dbUtils.reset();
|
|
||||||
await apiUtils.adminSetup();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('POST /oauth/authorize', () => {
|
describe('POST /oauth/authorize', () => {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { LoginResponseDto, createPartner } from '@immich/sdk';
|
import { LoginResponseDto, createPartner } from '@immich/sdk';
|
||||||
import { createUserDto } from 'src/fixtures';
|
import { createUserDto } from 'src/fixtures';
|
||||||
import { errorDto } from 'src/responses';
|
import { errorDto } from 'src/responses';
|
||||||
import { apiUtils, app, asBearerAuth, dbUtils } from 'src/utils';
|
import { app, asBearerAuth, utils } from 'src/utils';
|
||||||
import request from 'supertest';
|
import request from 'supertest';
|
||||||
import { beforeAll, describe, expect, it } from 'vitest';
|
import { beforeAll, describe, expect, it } from 'vitest';
|
||||||
|
|
||||||
@ -12,15 +12,14 @@ describe('/partner', () => {
|
|||||||
let user3: LoginResponseDto;
|
let user3: LoginResponseDto;
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
apiUtils.setup();
|
await utils.resetDatabase();
|
||||||
await dbUtils.reset();
|
|
||||||
|
|
||||||
admin = await apiUtils.adminSetup();
|
admin = await utils.adminSetup();
|
||||||
|
|
||||||
[user1, user2, user3] = await Promise.all([
|
[user1, user2, user3] = await Promise.all([
|
||||||
apiUtils.userSetup(admin.accessToken, createUserDto.user1),
|
utils.userSetup(admin.accessToken, createUserDto.user1),
|
||||||
apiUtils.userSetup(admin.accessToken, createUserDto.user2),
|
utils.userSetup(admin.accessToken, createUserDto.user2),
|
||||||
apiUtils.userSetup(admin.accessToken, createUserDto.user3),
|
utils.userSetup(admin.accessToken, createUserDto.user3),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { LoginResponseDto, PersonResponseDto } from '@immich/sdk';
|
import { LoginResponseDto, PersonResponseDto } from '@immich/sdk';
|
||||||
import { uuidDto } from 'src/fixtures';
|
import { uuidDto } from 'src/fixtures';
|
||||||
import { errorDto } from 'src/responses';
|
import { errorDto } from 'src/responses';
|
||||||
import { apiUtils, app, dbUtils } from 'src/utils';
|
import { app, utils } from 'src/utils';
|
||||||
import request from 'supertest';
|
import request from 'supertest';
|
||||||
import { beforeAll, beforeEach, describe, expect, it } from 'vitest';
|
import { beforeAll, beforeEach, describe, expect, it } from 'vitest';
|
||||||
|
|
||||||
@ -12,36 +12,35 @@ describe('/activity', () => {
|
|||||||
let multipleAssetsPerson: PersonResponseDto;
|
let multipleAssetsPerson: PersonResponseDto;
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
apiUtils.setup();
|
await utils.resetDatabase();
|
||||||
await dbUtils.reset();
|
admin = await utils.adminSetup();
|
||||||
admin = await apiUtils.adminSetup();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await dbUtils.reset(['person']);
|
await utils.resetDatabase(['person']);
|
||||||
|
|
||||||
[visiblePerson, hiddenPerson, multipleAssetsPerson] = await Promise.all([
|
[visiblePerson, hiddenPerson, multipleAssetsPerson] = await Promise.all([
|
||||||
apiUtils.createPerson(admin.accessToken, {
|
utils.createPerson(admin.accessToken, {
|
||||||
name: 'visible_person',
|
name: 'visible_person',
|
||||||
}),
|
}),
|
||||||
apiUtils.createPerson(admin.accessToken, {
|
utils.createPerson(admin.accessToken, {
|
||||||
name: 'hidden_person',
|
name: 'hidden_person',
|
||||||
isHidden: true,
|
isHidden: true,
|
||||||
}),
|
}),
|
||||||
apiUtils.createPerson(admin.accessToken, {
|
utils.createPerson(admin.accessToken, {
|
||||||
name: 'multiple_assets_person',
|
name: 'multiple_assets_person',
|
||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const asset1 = await apiUtils.createAsset(admin.accessToken);
|
const asset1 = await utils.createAsset(admin.accessToken);
|
||||||
const asset2 = await apiUtils.createAsset(admin.accessToken);
|
const asset2 = await utils.createAsset(admin.accessToken);
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
dbUtils.createFace({ assetId: asset1.id, personId: visiblePerson.id }),
|
utils.createFace({ assetId: asset1.id, personId: visiblePerson.id }),
|
||||||
dbUtils.createFace({ assetId: asset1.id, personId: hiddenPerson.id }),
|
utils.createFace({ assetId: asset1.id, personId: hiddenPerson.id }),
|
||||||
dbUtils.createFace({ assetId: asset1.id, personId: multipleAssetsPerson.id }),
|
utils.createFace({ assetId: asset1.id, personId: multipleAssetsPerson.id }),
|
||||||
dbUtils.createFace({ assetId: asset1.id, personId: multipleAssetsPerson.id }),
|
utils.createFace({ assetId: asset1.id, personId: multipleAssetsPerson.id }),
|
||||||
dbUtils.createFace({ assetId: asset2.id, personId: multipleAssetsPerson.id }),
|
utils.createFace({ assetId: asset2.id, personId: multipleAssetsPerson.id }),
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -194,7 +193,7 @@ describe('/activity', () => {
|
|||||||
|
|
||||||
it('should clear a date of birth', async () => {
|
it('should clear a date of birth', async () => {
|
||||||
// TODO ironically this uses the update endpoint to create the person
|
// TODO ironically this uses the update endpoint to create the person
|
||||||
const person = await apiUtils.createPerson(admin.accessToken, {
|
const person = await utils.createPerson(admin.accessToken, {
|
||||||
birthDate: new Date('1990-01-01').toISOString(),
|
birthDate: new Date('1990-01-01').toISOString(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { LoginResponseDto, getServerConfig } from '@immich/sdk';
|
import { LoginResponseDto, getServerConfig } from '@immich/sdk';
|
||||||
import { createUserDto } from 'src/fixtures';
|
import { createUserDto } from 'src/fixtures';
|
||||||
import { errorDto } from 'src/responses';
|
import { errorDto } from 'src/responses';
|
||||||
import { apiUtils, app, dbUtils } from 'src/utils';
|
import { app, utils } from 'src/utils';
|
||||||
import request from 'supertest';
|
import request from 'supertest';
|
||||||
import { beforeAll, describe, expect, it } from 'vitest';
|
import { beforeAll, describe, expect, it } from 'vitest';
|
||||||
|
|
||||||
@ -10,10 +10,9 @@ describe('/server-info', () => {
|
|||||||
let nonAdmin: LoginResponseDto;
|
let nonAdmin: LoginResponseDto;
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
apiUtils.setup();
|
await utils.resetDatabase();
|
||||||
await dbUtils.reset();
|
admin = await utils.adminSetup({ onboarding: false });
|
||||||
admin = await apiUtils.adminSetup({ onboarding: false });
|
nonAdmin = await utils.userSetup(admin.accessToken, createUserDto.user1);
|
||||||
nonAdmin = await apiUtils.userSetup(admin.accessToken, createUserDto.user1);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('GET /server-info', () => {
|
describe('GET /server-info', () => {
|
||||||
|
@ -9,7 +9,7 @@ import {
|
|||||||
} from '@immich/sdk';
|
} from '@immich/sdk';
|
||||||
import { createUserDto, uuidDto } from 'src/fixtures';
|
import { createUserDto, uuidDto } from 'src/fixtures';
|
||||||
import { errorDto } from 'src/responses';
|
import { errorDto } from 'src/responses';
|
||||||
import { apiUtils, app, asBearerAuth, dbUtils } from 'src/utils';
|
import { app, asBearerAuth, utils } from 'src/utils';
|
||||||
import request from 'supertest';
|
import request from 'supertest';
|
||||||
import { beforeAll, describe, expect, it } from 'vitest';
|
import { beforeAll, describe, expect, it } from 'vitest';
|
||||||
|
|
||||||
@ -30,20 +30,16 @@ describe('/shared-link', () => {
|
|||||||
let linkWithoutMetadata: SharedLinkResponseDto;
|
let linkWithoutMetadata: SharedLinkResponseDto;
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
apiUtils.setup();
|
await utils.resetDatabase();
|
||||||
await dbUtils.reset();
|
|
||||||
|
|
||||||
admin = await apiUtils.adminSetup();
|
admin = await utils.adminSetup();
|
||||||
|
|
||||||
[user1, user2] = await Promise.all([
|
[user1, user2] = await Promise.all([
|
||||||
apiUtils.userSetup(admin.accessToken, createUserDto.user1),
|
utils.userSetup(admin.accessToken, createUserDto.user1),
|
||||||
apiUtils.userSetup(admin.accessToken, createUserDto.user2),
|
utils.userSetup(admin.accessToken, createUserDto.user2),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
[asset1, asset2] = await Promise.all([
|
[asset1, asset2] = await Promise.all([utils.createAsset(user1.accessToken), utils.createAsset(user1.accessToken)]);
|
||||||
apiUtils.createAsset(user1.accessToken),
|
|
||||||
apiUtils.createAsset(user1.accessToken),
|
|
||||||
]);
|
|
||||||
|
|
||||||
[album, deletedAlbum, metadataAlbum] = await Promise.all([
|
[album, deletedAlbum, metadataAlbum] = await Promise.all([
|
||||||
createAlbum({ createAlbumDto: { albumName: 'album' } }, { headers: asBearerAuth(user1.accessToken) }),
|
createAlbum({ createAlbumDto: { albumName: 'album' } }, { headers: asBearerAuth(user1.accessToken) }),
|
||||||
@ -61,29 +57,29 @@ describe('/shared-link', () => {
|
|||||||
|
|
||||||
[linkWithDeletedAlbum, linkWithAlbum, linkWithAssets, linkWithPassword, linkWithMetadata, linkWithoutMetadata] =
|
[linkWithDeletedAlbum, linkWithAlbum, linkWithAssets, linkWithPassword, linkWithMetadata, linkWithoutMetadata] =
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
apiUtils.createSharedLink(user2.accessToken, {
|
utils.createSharedLink(user2.accessToken, {
|
||||||
type: SharedLinkType.Album,
|
type: SharedLinkType.Album,
|
||||||
albumId: deletedAlbum.id,
|
albumId: deletedAlbum.id,
|
||||||
}),
|
}),
|
||||||
apiUtils.createSharedLink(user1.accessToken, {
|
utils.createSharedLink(user1.accessToken, {
|
||||||
type: SharedLinkType.Album,
|
type: SharedLinkType.Album,
|
||||||
albumId: album.id,
|
albumId: album.id,
|
||||||
}),
|
}),
|
||||||
apiUtils.createSharedLink(user1.accessToken, {
|
utils.createSharedLink(user1.accessToken, {
|
||||||
type: SharedLinkType.Individual,
|
type: SharedLinkType.Individual,
|
||||||
assetIds: [asset1.id],
|
assetIds: [asset1.id],
|
||||||
}),
|
}),
|
||||||
apiUtils.createSharedLink(user1.accessToken, {
|
utils.createSharedLink(user1.accessToken, {
|
||||||
type: SharedLinkType.Album,
|
type: SharedLinkType.Album,
|
||||||
albumId: album.id,
|
albumId: album.id,
|
||||||
password: 'foo',
|
password: 'foo',
|
||||||
}),
|
}),
|
||||||
apiUtils.createSharedLink(user1.accessToken, {
|
utils.createSharedLink(user1.accessToken, {
|
||||||
type: SharedLinkType.Album,
|
type: SharedLinkType.Album,
|
||||||
albumId: metadataAlbum.id,
|
albumId: metadataAlbum.id,
|
||||||
showMetadata: true,
|
showMetadata: true,
|
||||||
}),
|
}),
|
||||||
apiUtils.createSharedLink(user1.accessToken, {
|
utils.createSharedLink(user1.accessToken, {
|
||||||
type: SharedLinkType.Album,
|
type: SharedLinkType.Album,
|
||||||
albumId: metadataAlbum.id,
|
albumId: metadataAlbum.id,
|
||||||
showMetadata: false,
|
showMetadata: false,
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { LoginResponseDto } from '@immich/sdk';
|
import { LoginResponseDto } from '@immich/sdk';
|
||||||
import { createUserDto } from 'src/fixtures';
|
import { createUserDto } from 'src/fixtures';
|
||||||
import { errorDto } from 'src/responses';
|
import { errorDto } from 'src/responses';
|
||||||
import { apiUtils, app, dbUtils } from 'src/utils';
|
import { app, utils } from 'src/utils';
|
||||||
import request from 'supertest';
|
import request from 'supertest';
|
||||||
import { beforeAll, describe, expect, it } from 'vitest';
|
import { beforeAll, describe, expect, it } from 'vitest';
|
||||||
|
|
||||||
@ -10,10 +10,9 @@ describe('/system-config', () => {
|
|||||||
let nonAdmin: LoginResponseDto;
|
let nonAdmin: LoginResponseDto;
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
apiUtils.setup();
|
await utils.resetDatabase();
|
||||||
await dbUtils.reset();
|
admin = await utils.adminSetup();
|
||||||
admin = await apiUtils.adminSetup();
|
nonAdmin = await utils.userSetup(admin.accessToken, createUserDto.user1);
|
||||||
nonAdmin = await apiUtils.userSetup(admin.accessToken, createUserDto.user1);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('GET /system-config/map/style.json', () => {
|
describe('GET /system-config/map/style.json', () => {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { LoginResponseDto, getAllAssets } from '@immich/sdk';
|
import { LoginResponseDto, getAllAssets } from '@immich/sdk';
|
||||||
import { Socket } from 'socket.io-client';
|
import { Socket } from 'socket.io-client';
|
||||||
import { errorDto } from 'src/responses';
|
import { errorDto } from 'src/responses';
|
||||||
import { apiUtils, app, asBearerAuth, dbUtils, wsUtils } from 'src/utils';
|
import { app, asBearerAuth, utils } from 'src/utils';
|
||||||
import request from 'supertest';
|
import request from 'supertest';
|
||||||
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
|
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
|
||||||
|
|
||||||
@ -10,14 +10,13 @@ describe('/trash', () => {
|
|||||||
let ws: Socket;
|
let ws: Socket;
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
apiUtils.setup();
|
await utils.resetDatabase();
|
||||||
await dbUtils.reset();
|
admin = await utils.adminSetup({ onboarding: false });
|
||||||
admin = await apiUtils.adminSetup({ onboarding: false });
|
ws = await utils.connectWebsocket(admin.accessToken);
|
||||||
ws = await wsUtils.connect(admin.accessToken);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
afterAll(() => {
|
afterAll(() => {
|
||||||
wsUtils.disconnect(ws);
|
utils.disconnectWebsocket(ws);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('POST /trash/empty', () => {
|
describe('POST /trash/empty', () => {
|
||||||
@ -29,8 +28,8 @@ describe('/trash', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should empty the trash', async () => {
|
it('should empty the trash', async () => {
|
||||||
const { id: assetId } = await apiUtils.createAsset(admin.accessToken);
|
const { id: assetId } = await utils.createAsset(admin.accessToken);
|
||||||
await apiUtils.deleteAssets(admin.accessToken, [assetId]);
|
await utils.deleteAssets(admin.accessToken, [assetId]);
|
||||||
|
|
||||||
const before = await getAllAssets({}, { headers: asBearerAuth(admin.accessToken) });
|
const before = await getAllAssets({}, { headers: asBearerAuth(admin.accessToken) });
|
||||||
|
|
||||||
@ -39,7 +38,7 @@ describe('/trash', () => {
|
|||||||
const { status } = await request(app).post('/trash/empty').set('Authorization', `Bearer ${admin.accessToken}`);
|
const { status } = await request(app).post('/trash/empty').set('Authorization', `Bearer ${admin.accessToken}`);
|
||||||
expect(status).toBe(204);
|
expect(status).toBe(204);
|
||||||
|
|
||||||
await wsUtils.waitForEvent({ event: 'delete', assetId });
|
await utils.waitForWebsocketEvent({ event: 'delete', assetId });
|
||||||
|
|
||||||
const after = await getAllAssets({}, { headers: asBearerAuth(admin.accessToken) });
|
const after = await getAllAssets({}, { headers: asBearerAuth(admin.accessToken) });
|
||||||
expect(after.length).toBe(0);
|
expect(after.length).toBe(0);
|
||||||
@ -55,16 +54,16 @@ describe('/trash', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should restore all trashed assets', async () => {
|
it('should restore all trashed assets', async () => {
|
||||||
const { id: assetId } = await apiUtils.createAsset(admin.accessToken);
|
const { id: assetId } = await utils.createAsset(admin.accessToken);
|
||||||
await apiUtils.deleteAssets(admin.accessToken, [assetId]);
|
await utils.deleteAssets(admin.accessToken, [assetId]);
|
||||||
|
|
||||||
const before = await apiUtils.getAssetInfo(admin.accessToken, assetId);
|
const before = await utils.getAssetInfo(admin.accessToken, assetId);
|
||||||
expect(before.isTrashed).toBe(true);
|
expect(before.isTrashed).toBe(true);
|
||||||
|
|
||||||
const { status } = await request(app).post('/trash/restore').set('Authorization', `Bearer ${admin.accessToken}`);
|
const { status } = await request(app).post('/trash/restore').set('Authorization', `Bearer ${admin.accessToken}`);
|
||||||
expect(status).toBe(204);
|
expect(status).toBe(204);
|
||||||
|
|
||||||
const after = await apiUtils.getAssetInfo(admin.accessToken, assetId);
|
const after = await utils.getAssetInfo(admin.accessToken, assetId);
|
||||||
expect(after.isTrashed).toBe(false);
|
expect(after.isTrashed).toBe(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -78,10 +77,10 @@ describe('/trash', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should restore a trashed asset by id', async () => {
|
it('should restore a trashed asset by id', async () => {
|
||||||
const { id: assetId } = await apiUtils.createAsset(admin.accessToken);
|
const { id: assetId } = await utils.createAsset(admin.accessToken);
|
||||||
await apiUtils.deleteAssets(admin.accessToken, [assetId]);
|
await utils.deleteAssets(admin.accessToken, [assetId]);
|
||||||
|
|
||||||
const before = await apiUtils.getAssetInfo(admin.accessToken, assetId);
|
const before = await utils.getAssetInfo(admin.accessToken, assetId);
|
||||||
expect(before.isTrashed).toBe(true);
|
expect(before.isTrashed).toBe(true);
|
||||||
|
|
||||||
const { status } = await request(app)
|
const { status } = await request(app)
|
||||||
@ -90,7 +89,7 @@ describe('/trash', () => {
|
|||||||
.send({ ids: [assetId] });
|
.send({ ids: [assetId] });
|
||||||
expect(status).toBe(204);
|
expect(status).toBe(204);
|
||||||
|
|
||||||
const after = await apiUtils.getAssetInfo(admin.accessToken, assetId);
|
const after = await utils.getAssetInfo(admin.accessToken, assetId);
|
||||||
expect(after.isTrashed).toBe(false);
|
expect(after.isTrashed).toBe(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { LoginResponseDto, deleteUser, getUserById } from '@immich/sdk';
|
import { LoginResponseDto, deleteUser, getUserById } from '@immich/sdk';
|
||||||
import { createUserDto, userDto } from 'src/fixtures';
|
import { createUserDto, userDto } from 'src/fixtures';
|
||||||
import { errorDto } from 'src/responses';
|
import { errorDto } from 'src/responses';
|
||||||
import { apiUtils, app, asBearerAuth, dbUtils } from 'src/utils';
|
import { app, asBearerAuth, utils } from 'src/utils';
|
||||||
import request from 'supertest';
|
import request from 'supertest';
|
||||||
import { beforeAll, describe, expect, it } from 'vitest';
|
import { beforeAll, describe, expect, it } from 'vitest';
|
||||||
|
|
||||||
@ -12,14 +12,13 @@ describe('/server-info', () => {
|
|||||||
let nonAdmin: LoginResponseDto;
|
let nonAdmin: LoginResponseDto;
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
apiUtils.setup();
|
await utils.resetDatabase();
|
||||||
await dbUtils.reset();
|
admin = await utils.adminSetup({ onboarding: false });
|
||||||
admin = await apiUtils.adminSetup({ onboarding: false });
|
|
||||||
|
|
||||||
[deletedUser, nonAdmin, userToDelete] = await Promise.all([
|
[deletedUser, nonAdmin, userToDelete] = await Promise.all([
|
||||||
apiUtils.userSetup(admin.accessToken, createUserDto.user1),
|
utils.userSetup(admin.accessToken, createUserDto.user1),
|
||||||
apiUtils.userSetup(admin.accessToken, createUserDto.user2),
|
utils.userSetup(admin.accessToken, createUserDto.user2),
|
||||||
apiUtils.userSetup(admin.accessToken, createUserDto.user3),
|
utils.userSetup(admin.accessToken, createUserDto.user3),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await deleteUser({ id: deletedUser.userId }, { headers: asBearerAuth(admin.accessToken) });
|
await deleteUser({ id: deletedUser.userId }, { headers: asBearerAuth(admin.accessToken) });
|
||||||
|
@ -1,14 +1,10 @@
|
|||||||
import { stat } from 'node:fs/promises';
|
import { stat } from 'node:fs/promises';
|
||||||
import { apiUtils, app, dbUtils, immichCli } from 'src/utils';
|
import { app, immichCli, utils } from 'src/utils';
|
||||||
import { beforeAll, beforeEach, describe, expect, it } from 'vitest';
|
import { beforeEach, describe, expect, it } from 'vitest';
|
||||||
|
|
||||||
describe(`immich login-key`, () => {
|
describe(`immich login-key`, () => {
|
||||||
beforeAll(() => {
|
|
||||||
apiUtils.setup();
|
|
||||||
});
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await dbUtils.reset();
|
await utils.resetDatabase();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should require a url', async () => {
|
it('should require a url', async () => {
|
||||||
@ -30,8 +26,8 @@ describe(`immich login-key`, () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should login and save auth.yml with 600', async () => {
|
it('should login and save auth.yml with 600', async () => {
|
||||||
const admin = await apiUtils.adminSetup();
|
const admin = await utils.adminSetup();
|
||||||
const key = await apiUtils.createApiKey(admin.accessToken);
|
const key = await utils.createApiKey(admin.accessToken);
|
||||||
const { stdout, stderr, exitCode } = await immichCli(['login-key', app, `${key.secret}`]);
|
const { stdout, stderr, exitCode } = await immichCli(['login-key', app, `${key.secret}`]);
|
||||||
expect(stdout.split('\n')).toEqual([
|
expect(stdout.split('\n')).toEqual([
|
||||||
'Logging in to http://127.0.0.1:2283/api',
|
'Logging in to http://127.0.0.1:2283/api',
|
||||||
@ -47,8 +43,8 @@ describe(`immich login-key`, () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should login without /api in the url', async () => {
|
it('should login without /api in the url', async () => {
|
||||||
const admin = await apiUtils.adminSetup();
|
const admin = await utils.adminSetup();
|
||||||
const key = await apiUtils.createApiKey(admin.accessToken);
|
const key = await utils.createApiKey(admin.accessToken);
|
||||||
const { stdout, stderr, exitCode } = await immichCli(['login-key', app.replaceAll('/api', ''), `${key.secret}`]);
|
const { stdout, stderr, exitCode } = await immichCli(['login-key', app.replaceAll('/api', ''), `${key.secret}`]);
|
||||||
expect(stdout.split('\n')).toEqual([
|
expect(stdout.split('\n')).toEqual([
|
||||||
'Logging in to http://127.0.0.1:2283',
|
'Logging in to http://127.0.0.1:2283',
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
import { apiUtils, cliUtils, dbUtils, immichCli } from 'src/utils';
|
import { immichCli, utils } from 'src/utils';
|
||||||
import { beforeAll, describe, expect, it } from 'vitest';
|
import { beforeAll, describe, expect, it } from 'vitest';
|
||||||
|
|
||||||
describe(`immich server-info`, () => {
|
describe(`immich server-info`, () => {
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
apiUtils.setup();
|
await utils.resetDatabase();
|
||||||
await dbUtils.reset();
|
await utils.cliLogin();
|
||||||
await cliUtils.login();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return the server info', async () => {
|
it('should return the server info', async () => {
|
||||||
|
@ -1,19 +1,18 @@
|
|||||||
import { getAllAlbums, getAllAssets } from '@immich/sdk';
|
import { getAllAlbums, getAllAssets } from '@immich/sdk';
|
||||||
import { mkdir, readdir, rm, symlink } from 'node:fs/promises';
|
import { mkdir, readdir, rm, symlink } from 'node:fs/promises';
|
||||||
import { apiUtils, asKeyAuth, cliUtils, dbUtils, immichCli, testAssetDir } from 'src/utils';
|
import { asKeyAuth, immichCli, testAssetDir, utils } from 'src/utils';
|
||||||
import { beforeAll, beforeEach, describe, expect, it } from 'vitest';
|
import { beforeAll, beforeEach, describe, expect, it } from 'vitest';
|
||||||
|
|
||||||
describe(`immich upload`, () => {
|
describe(`immich upload`, () => {
|
||||||
let key: string;
|
let key: string;
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
apiUtils.setup();
|
await utils.resetDatabase();
|
||||||
await dbUtils.reset();
|
key = await utils.cliLogin();
|
||||||
key = await cliUtils.login();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await dbUtils.reset(['assets', 'albums']);
|
await utils.resetDatabase(['assets', 'albums']);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('immich upload --recursive', () => {
|
describe('immich upload --recursive', () => {
|
||||||
|
@ -1,14 +1,10 @@
|
|||||||
import { readFileSync } from 'node:fs';
|
import { readFileSync } from 'node:fs';
|
||||||
import { apiUtils, immichCli } from 'src/utils';
|
import { immichCli } from 'src/utils';
|
||||||
import { beforeAll, describe, expect, it } from 'vitest';
|
import { describe, expect, it } from 'vitest';
|
||||||
|
|
||||||
const pkg = JSON.parse(readFileSync('../cli/package.json', 'utf8'));
|
const pkg = JSON.parse(readFileSync('../cli/package.json', 'utf8'));
|
||||||
|
|
||||||
describe(`immich --version`, () => {
|
describe(`immich --version`, () => {
|
||||||
beforeAll(() => {
|
|
||||||
apiUtils.setup();
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('immich --version', () => {
|
describe('immich --version', () => {
|
||||||
it('should print the cli version', async () => {
|
it('should print the cli version', async () => {
|
||||||
const { stdout, stderr, exitCode } = await immichCli(['--version']);
|
const { stdout, stderr, exitCode } = await immichCli(['--version']);
|
||||||
|
@ -1,8 +1,16 @@
|
|||||||
import { exec, spawn } from 'node:child_process';
|
import { exec, spawn } from 'node:child_process';
|
||||||
|
import { setTimeout } from 'node:timers';
|
||||||
|
|
||||||
export default async () => {
|
export default async () => {
|
||||||
let _resolve: () => unknown;
|
let _resolve: () => unknown;
|
||||||
const ready = new Promise<void>((resolve) => (_resolve = resolve));
|
let _reject: (error: Error) => unknown;
|
||||||
|
|
||||||
|
const ready = new Promise<void>((resolve, reject) => {
|
||||||
|
_resolve = resolve;
|
||||||
|
_reject = reject;
|
||||||
|
});
|
||||||
|
|
||||||
|
const timeout = setTimeout(() => _reject(new Error('Timeout starting e2e environment')), 60_000);
|
||||||
|
|
||||||
const child = spawn('docker', ['compose', 'up'], { stdio: 'pipe' });
|
const child = spawn('docker', ['compose', 'up'], { stdio: 'pipe' });
|
||||||
|
|
||||||
@ -17,6 +25,7 @@ export default async () => {
|
|||||||
child.stderr.on('data', (data) => console.log(data.toString()));
|
child.stderr.on('data', (data) => console.log(data.toString()));
|
||||||
|
|
||||||
await ready;
|
await ready;
|
||||||
|
clearTimeout(timeout);
|
||||||
|
|
||||||
return async () => {
|
return async () => {
|
||||||
await new Promise<void>((resolve) => exec('docker compose down', () => resolve()));
|
await new Promise<void>((resolve) => exec('docker compose down', () => resolve()));
|
||||||
|
271
e2e/src/utils.ts
271
e2e/src/utils.ts
@ -26,7 +26,7 @@ import {
|
|||||||
import { BrowserContext } from '@playwright/test';
|
import { BrowserContext } from '@playwright/test';
|
||||||
import { exec, spawn } from 'node:child_process';
|
import { exec, spawn } from 'node:child_process';
|
||||||
import { createHash } from 'node:crypto';
|
import { createHash } from 'node:crypto';
|
||||||
import { access } from 'node:fs/promises';
|
import { existsSync } from 'node:fs';
|
||||||
import { tmpdir } from 'node:os';
|
import { tmpdir } from 'node:os';
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
import { promisify } from 'node:util';
|
import { promisify } from 'node:util';
|
||||||
@ -36,79 +36,71 @@ import { loginDto, signupDto } from 'src/fixtures';
|
|||||||
import { makeRandomImage } from 'src/generators';
|
import { makeRandomImage } from 'src/generators';
|
||||||
import request from 'supertest';
|
import request from 'supertest';
|
||||||
|
|
||||||
const execPromise = promisify(exec);
|
type CliResponse = { stdout: string; stderr: string; exitCode: number | null };
|
||||||
|
type EventType = 'upload' | 'delete';
|
||||||
|
type WaitOptions = { event: EventType; assetId: string; timeout?: number };
|
||||||
|
type AdminSetupOptions = { onboarding?: boolean };
|
||||||
|
type AssetData = { bytes?: Buffer; filename: string };
|
||||||
|
|
||||||
export const app = 'http://127.0.0.1:2283/api';
|
const dbUrl = 'postgres://postgres:postgres@127.0.0.1:5433/immich';
|
||||||
|
const baseUrl = 'http://127.0.0.1:2283';
|
||||||
const directoryExists = (directory: string) =>
|
|
||||||
access(directory)
|
|
||||||
.then(() => true)
|
|
||||||
.catch(() => false);
|
|
||||||
|
|
||||||
|
export const app = `${baseUrl}/api`;
|
||||||
// TODO move test assets into e2e/assets
|
// TODO move test assets into e2e/assets
|
||||||
export const testAssetDir = path.resolve(`./../server/test/assets/`);
|
export const testAssetDir = path.resolve(`./../server/test/assets/`);
|
||||||
export const testAssetDirInternal = '/data/assets';
|
export const testAssetDirInternal = '/data/assets';
|
||||||
export const tempDir = tmpdir();
|
export const tempDir = tmpdir();
|
||||||
|
export const asBearerAuth = (accessToken: string) => ({ Authorization: `Bearer ${accessToken}` });
|
||||||
const serverContainerName = 'immich-e2e-server';
|
|
||||||
const mediaDir = '/usr/src/app/upload';
|
|
||||||
const dirs = [
|
|
||||||
`"${mediaDir}/thumbs"`,
|
|
||||||
`"${mediaDir}/upload"`,
|
|
||||||
`"${mediaDir}/library"`,
|
|
||||||
`"${mediaDir}/encoded-video"`,
|
|
||||||
].join(' ');
|
|
||||||
|
|
||||||
if (!(await directoryExists(`${testAssetDir}/albums`))) {
|
|
||||||
throw new Error(
|
|
||||||
`Test assets not found. Please checkout https://github.com/immich-app/test-assets into ${testAssetDir} before testing`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export const asBearerAuth = (accessToken: string) => ({
|
|
||||||
Authorization: `Bearer ${accessToken}`,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const asKeyAuth = (key: string) => ({ 'x-api-key': key });
|
export const asKeyAuth = (key: string) => ({ 'x-api-key': key });
|
||||||
|
export const immichCli = async (args: string[]) => {
|
||||||
|
let _resolve: (value: CliResponse) => void;
|
||||||
|
const deferred = new Promise<CliResponse>((resolve) => (_resolve = resolve));
|
||||||
|
const _args = ['node_modules/.bin/immich', '-d', `/${tempDir}/immich/`, ...args];
|
||||||
|
const child = spawn('node', _args, {
|
||||||
|
stdio: 'pipe',
|
||||||
|
});
|
||||||
|
|
||||||
|
let stdout = '';
|
||||||
|
let stderr = '';
|
||||||
|
|
||||||
|
child.stdout.on('data', (data) => (stdout += data.toString()));
|
||||||
|
child.stderr.on('data', (data) => (stderr += data.toString()));
|
||||||
|
child.on('exit', (exitCode) => {
|
||||||
|
_resolve({
|
||||||
|
stdout: stdout.trim(),
|
||||||
|
stderr: stderr.trim(),
|
||||||
|
exitCode,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return deferred;
|
||||||
|
};
|
||||||
|
|
||||||
let client: pg.Client | null = null;
|
let client: pg.Client | null = null;
|
||||||
|
|
||||||
export const fileUtils = {
|
const events: Record<EventType, Set<string>> = {
|
||||||
reset: async () => {
|
upload: new Set<string>(),
|
||||||
await execPromise(`docker exec -i "${serverContainerName}" /bin/bash -c "rm -rf ${dirs} && mkdir ${dirs}"`);
|
delete: new Set<string>(),
|
||||||
},
|
|
||||||
unzip: async (input: string, output: string) => {
|
|
||||||
await execPromise(`unzip -o -d "${output}" "${input}"`);
|
|
||||||
},
|
|
||||||
sha1: (bytes: Buffer) => createHash('sha1').update(bytes).digest('base64'),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const dbUtils = {
|
const callbacks: Record<string, () => void> = {};
|
||||||
createFace: async ({ assetId, personId }: { assetId: string; personId: string }) => {
|
|
||||||
if (!client) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const vector = Array.from({ length: 512 }, Math.random);
|
const execPromise = promisify(exec);
|
||||||
const embedding = `[${vector.join(',')}]`;
|
|
||||||
|
|
||||||
await client.query('INSERT INTO asset_faces ("assetId", "personId", "embedding") VALUES ($1, $2, $3)', [
|
const onEvent = ({ event, assetId }: { event: EventType; assetId: string }) => {
|
||||||
assetId,
|
events[event].add(assetId);
|
||||||
personId,
|
const callback = callbacks[assetId];
|
||||||
embedding,
|
if (callback) {
|
||||||
]);
|
callback();
|
||||||
},
|
delete callbacks[assetId];
|
||||||
setPersonThumbnail: async (personId: string) => {
|
}
|
||||||
if (!client) {
|
};
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await client.query(`UPDATE "person" set "thumbnailPath" = '/my/awesome/thumbnail.jpg' where "id" = $1`, [personId]);
|
export const utils = {
|
||||||
},
|
resetDatabase: async (tables?: string[]) => {
|
||||||
reset: async (tables?: string[]) => {
|
|
||||||
try {
|
try {
|
||||||
if (!client) {
|
if (!client) {
|
||||||
client = new pg.Client('postgres://postgres:postgres@127.0.0.1:5433/immich');
|
client = new pg.Client(dbUrl);
|
||||||
await client.connect();
|
await client.connect();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,83 +126,27 @@ export const dbUtils = {
|
|||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
teardown: async () => {
|
|
||||||
try {
|
resetFilesystem: async () => {
|
||||||
if (client) {
|
const mediaInternal = '/usr/src/app/upload';
|
||||||
await client.end();
|
const dirs = [
|
||||||
client = null;
|
`"${mediaInternal}/thumbs"`,
|
||||||
}
|
`"${mediaInternal}/upload"`,
|
||||||
} catch (error) {
|
`"${mediaInternal}/library"`,
|
||||||
console.error('Failed to teardown database', error);
|
`"${mediaInternal}/encoded-video"`,
|
||||||
throw error;
|
].join(' ');
|
||||||
}
|
|
||||||
|
await execPromise(`docker exec -i "immich-e2e-server" /bin/bash -c "rm -rf ${dirs} && mkdir ${dirs}"`);
|
||||||
},
|
},
|
||||||
};
|
|
||||||
export interface CliResponse {
|
|
||||||
stdout: string;
|
|
||||||
stderr: string;
|
|
||||||
exitCode: number | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const immichCli = async (args: string[]) => {
|
unzip: async (input: string, output: string) => {
|
||||||
let _resolve: (value: CliResponse) => void;
|
await execPromise(`unzip -o -d "${output}" "${input}"`);
|
||||||
const deferred = new Promise<CliResponse>((resolve) => (_resolve = resolve));
|
},
|
||||||
const _args = ['node_modules/.bin/immich', '-d', `/${tempDir}/immich/`, ...args];
|
|
||||||
const child = spawn('node', _args, {
|
|
||||||
stdio: 'pipe',
|
|
||||||
});
|
|
||||||
|
|
||||||
let stdout = '';
|
sha1: (bytes: Buffer) => createHash('sha1').update(bytes).digest('base64'),
|
||||||
let stderr = '';
|
|
||||||
|
|
||||||
child.stdout.on('data', (data) => (stdout += data.toString()));
|
connectWebsocket: async (accessToken: string) => {
|
||||||
child.stderr.on('data', (data) => (stderr += data.toString()));
|
const websocket = io(baseUrl, {
|
||||||
child.on('exit', (exitCode) => {
|
|
||||||
_resolve({
|
|
||||||
stdout: stdout.trim(),
|
|
||||||
stderr: stderr.trim(),
|
|
||||||
exitCode,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return deferred;
|
|
||||||
};
|
|
||||||
|
|
||||||
export interface AdminSetupOptions {
|
|
||||||
onboarding?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum SocketEvent {
|
|
||||||
UPLOAD = 'upload',
|
|
||||||
DELETE = 'delete',
|
|
||||||
}
|
|
||||||
|
|
||||||
export type EventType = 'upload' | 'delete';
|
|
||||||
export interface WaitOptions {
|
|
||||||
event: EventType;
|
|
||||||
assetId: string;
|
|
||||||
timeout?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
const events: Record<EventType, Set<string>> = {
|
|
||||||
upload: new Set<string>(),
|
|
||||||
delete: new Set<string>(),
|
|
||||||
};
|
|
||||||
|
|
||||||
const callbacks: Record<string, () => void> = {};
|
|
||||||
|
|
||||||
const onEvent = ({ event, assetId }: { event: EventType; assetId: string }) => {
|
|
||||||
events[event].add(assetId);
|
|
||||||
const callback = callbacks[assetId];
|
|
||||||
if (callback) {
|
|
||||||
callback();
|
|
||||||
delete callbacks[assetId];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const wsUtils = {
|
|
||||||
connect: async (accessToken: string) => {
|
|
||||||
const websocket = io('http://127.0.0.1:2283', {
|
|
||||||
path: '/api/socket.io',
|
path: '/api/socket.io',
|
||||||
transports: ['websocket'],
|
transports: ['websocket'],
|
||||||
extraHeaders: { Authorization: `Bearer ${accessToken}` },
|
extraHeaders: { Authorization: `Bearer ${accessToken}` },
|
||||||
@ -226,7 +162,8 @@ export const wsUtils = {
|
|||||||
.connect();
|
.connect();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
disconnect: (ws: Socket) => {
|
|
||||||
|
disconnectWebsocket: (ws: Socket) => {
|
||||||
if (ws?.connected) {
|
if (ws?.connected) {
|
||||||
ws.disconnect();
|
ws.disconnect();
|
||||||
}
|
}
|
||||||
@ -235,14 +172,15 @@ export const wsUtils = {
|
|||||||
set.clear();
|
set.clear();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
waitForEvent: async ({ event, assetId, timeout: ms }: WaitOptions): Promise<void> => {
|
|
||||||
|
waitForWebsocketEvent: async ({ event, assetId, timeout: ms }: WaitOptions): Promise<void> => {
|
||||||
const set = events[event];
|
const set = events[event];
|
||||||
if (set.has(assetId)) {
|
if (set.has(assetId)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Promise<void>((resolve, reject) => {
|
return new Promise<void>((resolve, reject) => {
|
||||||
const timeout = setTimeout(() => reject(new Error(`Timed out waiting for ${event} event`)), ms || 5000);
|
const timeout = setTimeout(() => reject(new Error(`Timed out waiting for ${event} event`)), ms || 10_000);
|
||||||
|
|
||||||
callbacks[assetId] = () => {
|
callbacks[assetId] = () => {
|
||||||
clearTimeout(timeout);
|
clearTimeout(timeout);
|
||||||
@ -250,12 +188,8 @@ export const wsUtils = {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
};
|
|
||||||
|
|
||||||
type AssetData = { bytes?: Buffer; filename: string };
|
setApiEndpoint: () => {
|
||||||
|
|
||||||
export const apiUtils = {
|
|
||||||
setup: () => {
|
|
||||||
defaults.baseUrl = app;
|
defaults.baseUrl = app;
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -269,17 +203,21 @@ export const apiUtils = {
|
|||||||
}
|
}
|
||||||
return response;
|
return response;
|
||||||
},
|
},
|
||||||
|
|
||||||
userSetup: async (accessToken: string, dto: CreateUserDto) => {
|
userSetup: async (accessToken: string, dto: CreateUserDto) => {
|
||||||
await createUser({ createUserDto: dto }, { headers: asBearerAuth(accessToken) });
|
await createUser({ createUserDto: dto }, { headers: asBearerAuth(accessToken) });
|
||||||
return login({
|
return login({
|
||||||
loginCredentialDto: { email: dto.email, password: dto.password },
|
loginCredentialDto: { email: dto.email, password: dto.password },
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
createApiKey: (accessToken: string) => {
|
createApiKey: (accessToken: string) => {
|
||||||
return createApiKey({ apiKeyCreateDto: { name: 'e2e' } }, { headers: asBearerAuth(accessToken) });
|
return createApiKey({ apiKeyCreateDto: { name: 'e2e' } }, { headers: asBearerAuth(accessToken) });
|
||||||
},
|
},
|
||||||
|
|
||||||
createAlbum: (accessToken: string, dto: CreateAlbumDto) =>
|
createAlbum: (accessToken: string, dto: CreateAlbumDto) =>
|
||||||
createAlbum({ createAlbumDto: dto }, { headers: asBearerAuth(accessToken) }),
|
createAlbum({ createAlbumDto: dto }, { headers: asBearerAuth(accessToken) }),
|
||||||
|
|
||||||
createAsset: async (
|
createAsset: async (
|
||||||
accessToken: string,
|
accessToken: string,
|
||||||
dto?: Partial<Omit<CreateAssetDto, 'assetData'>> & { assetData?: AssetData },
|
dto?: Partial<Omit<CreateAssetDto, 'assetData'>> & { assetData?: AssetData },
|
||||||
@ -308,13 +246,16 @@ export const apiUtils = {
|
|||||||
|
|
||||||
return body as AssetFileUploadResponseDto;
|
return body as AssetFileUploadResponseDto;
|
||||||
},
|
},
|
||||||
|
|
||||||
getAssetInfo: (accessToken: string, id: string) => getAssetInfo({ id }, { headers: asBearerAuth(accessToken) }),
|
getAssetInfo: (accessToken: string, id: string) => getAssetInfo({ id }, { headers: asBearerAuth(accessToken) }),
|
||||||
|
|
||||||
deleteAssets: (accessToken: string, ids: string[]) =>
|
deleteAssets: (accessToken: string, ids: string[]) =>
|
||||||
deleteAssets({ assetBulkDeleteDto: { ids } }, { headers: asBearerAuth(accessToken) }),
|
deleteAssets({ assetBulkDeleteDto: { ids } }, { headers: asBearerAuth(accessToken) }),
|
||||||
|
|
||||||
createPerson: async (accessToken: string, dto?: PersonUpdateDto) => {
|
createPerson: async (accessToken: string, dto?: PersonUpdateDto) => {
|
||||||
// TODO fix createPerson to accept a body
|
// TODO fix createPerson to accept a body
|
||||||
const person = await createPerson({ headers: asBearerAuth(accessToken) });
|
const person = await createPerson({ headers: asBearerAuth(accessToken) });
|
||||||
await dbUtils.setPersonThumbnail(person.id);
|
await utils.setPersonThumbnail(person.id);
|
||||||
|
|
||||||
if (!dto) {
|
if (!dto) {
|
||||||
return person;
|
return person;
|
||||||
@ -322,24 +263,39 @@ export const apiUtils = {
|
|||||||
|
|
||||||
return updatePerson({ id: person.id, personUpdateDto: dto }, { headers: asBearerAuth(accessToken) });
|
return updatePerson({ id: person.id, personUpdateDto: dto }, { headers: asBearerAuth(accessToken) });
|
||||||
},
|
},
|
||||||
|
|
||||||
|
createFace: async ({ assetId, personId }: { assetId: string; personId: string }) => {
|
||||||
|
if (!client) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const vector = Array.from({ length: 512 }, Math.random);
|
||||||
|
const embedding = `[${vector.join(',')}]`;
|
||||||
|
|
||||||
|
await client.query('INSERT INTO asset_faces ("assetId", "personId", "embedding") VALUES ($1, $2, $3)', [
|
||||||
|
assetId,
|
||||||
|
personId,
|
||||||
|
embedding,
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
|
||||||
|
setPersonThumbnail: async (personId: string) => {
|
||||||
|
if (!client) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await client.query(`UPDATE "person" set "thumbnailPath" = '/my/awesome/thumbnail.jpg' where "id" = $1`, [personId]);
|
||||||
|
},
|
||||||
|
|
||||||
createSharedLink: (accessToken: string, dto: SharedLinkCreateDto) =>
|
createSharedLink: (accessToken: string, dto: SharedLinkCreateDto) =>
|
||||||
createSharedLink({ sharedLinkCreateDto: dto }, { headers: asBearerAuth(accessToken) }),
|
createSharedLink({ sharedLinkCreateDto: dto }, { headers: asBearerAuth(accessToken) }),
|
||||||
|
|
||||||
createLibrary: (accessToken: string, dto: CreateLibraryDto) =>
|
createLibrary: (accessToken: string, dto: CreateLibraryDto) =>
|
||||||
createLibrary({ createLibraryDto: dto }, { headers: asBearerAuth(accessToken) }),
|
createLibrary({ createLibraryDto: dto }, { headers: asBearerAuth(accessToken) }),
|
||||||
|
|
||||||
validateLibrary: (accessToken: string, id: string, dto: ValidateLibraryDto) =>
|
validateLibrary: (accessToken: string, id: string, dto: ValidateLibraryDto) =>
|
||||||
validate({ id, validateLibraryDto: dto }, { headers: asBearerAuth(accessToken) }),
|
validate({ id, validateLibraryDto: dto }, { headers: asBearerAuth(accessToken) }),
|
||||||
};
|
|
||||||
|
|
||||||
export const cliUtils = {
|
|
||||||
login: async () => {
|
|
||||||
const admin = await apiUtils.adminSetup();
|
|
||||||
const key = await apiUtils.createApiKey(admin.accessToken);
|
|
||||||
await immichCli(['login-key', app, `${key.secret}`]);
|
|
||||||
return key.secret;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export const webUtils = {
|
|
||||||
setAuthCookies: async (context: BrowserContext, accessToken: string) =>
|
setAuthCookies: async (context: BrowserContext, accessToken: string) =>
|
||||||
await context.addCookies([
|
await context.addCookies([
|
||||||
{
|
{
|
||||||
@ -373,4 +329,19 @@ export const webUtils = {
|
|||||||
sameSite: 'Lax',
|
sameSite: 'Lax',
|
||||||
},
|
},
|
||||||
]),
|
]),
|
||||||
|
|
||||||
|
cliLogin: async () => {
|
||||||
|
const admin = await utils.adminSetup();
|
||||||
|
const key = await utils.createApiKey(admin.accessToken);
|
||||||
|
await immichCli(['login-key', app, `${key.secret}`]);
|
||||||
|
return key.secret;
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
utils.setApiEndpoint();
|
||||||
|
|
||||||
|
if (!existsSync(`${testAssetDir}/albums`)) {
|
||||||
|
throw new Error(
|
||||||
|
`Test assets not found. Please checkout https://github.com/immich-app/test-assets into ${testAssetDir} before testing`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
@ -1,17 +1,13 @@
|
|||||||
import { expect, test } from '@playwright/test';
|
import { expect, test } from '@playwright/test';
|
||||||
import { apiUtils, dbUtils, webUtils } from 'src/utils';
|
import { utils } from 'src/utils';
|
||||||
|
|
||||||
test.describe('Registration', () => {
|
test.describe('Registration', () => {
|
||||||
test.beforeAll(() => {
|
test.beforeAll(() => {
|
||||||
apiUtils.setup();
|
utils.setApiEndpoint();
|
||||||
});
|
});
|
||||||
|
|
||||||
test.beforeEach(async () => {
|
test.beforeEach(async () => {
|
||||||
await dbUtils.reset();
|
await utils.resetDatabase();
|
||||||
});
|
|
||||||
|
|
||||||
test.afterAll(async () => {
|
|
||||||
await dbUtils.teardown();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('admin registration', async ({ page }) => {
|
test('admin registration', async ({ page }) => {
|
||||||
@ -45,8 +41,8 @@ test.describe('Registration', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('user registration', async ({ context, page }) => {
|
test('user registration', async ({ context, page }) => {
|
||||||
const admin = await apiUtils.adminSetup();
|
const admin = await utils.adminSetup();
|
||||||
await webUtils.setAuthCookies(context, admin.accessToken);
|
await utils.setAuthCookies(context, admin.accessToken);
|
||||||
|
|
||||||
// create user
|
// create user
|
||||||
await page.goto('/admin/user-management');
|
await page.goto('/admin/user-management');
|
||||||
|
@ -7,7 +7,7 @@ import {
|
|||||||
createAlbum,
|
createAlbum,
|
||||||
} from '@immich/sdk';
|
} from '@immich/sdk';
|
||||||
import { test } from '@playwright/test';
|
import { test } from '@playwright/test';
|
||||||
import { apiUtils, asBearerAuth, dbUtils } from 'src/utils';
|
import { asBearerAuth, utils } from 'src/utils';
|
||||||
|
|
||||||
test.describe('Shared Links', () => {
|
test.describe('Shared Links', () => {
|
||||||
let admin: LoginResponseDto;
|
let admin: LoginResponseDto;
|
||||||
@ -17,10 +17,10 @@ test.describe('Shared Links', () => {
|
|||||||
let sharedLinkPassword: SharedLinkResponseDto;
|
let sharedLinkPassword: SharedLinkResponseDto;
|
||||||
|
|
||||||
test.beforeAll(async () => {
|
test.beforeAll(async () => {
|
||||||
apiUtils.setup();
|
utils.setApiEndpoint();
|
||||||
await dbUtils.reset();
|
await utils.resetDatabase();
|
||||||
admin = await apiUtils.adminSetup();
|
admin = await utils.adminSetup();
|
||||||
asset = await apiUtils.createAsset(admin.accessToken);
|
asset = await utils.createAsset(admin.accessToken);
|
||||||
album = await createAlbum(
|
album = await createAlbum(
|
||||||
{
|
{
|
||||||
createAlbumDto: {
|
createAlbumDto: {
|
||||||
@ -30,21 +30,17 @@ test.describe('Shared Links', () => {
|
|||||||
},
|
},
|
||||||
{ headers: asBearerAuth(admin.accessToken) },
|
{ headers: asBearerAuth(admin.accessToken) },
|
||||||
);
|
);
|
||||||
sharedLink = await apiUtils.createSharedLink(admin.accessToken, {
|
sharedLink = await utils.createSharedLink(admin.accessToken, {
|
||||||
type: SharedLinkType.Album,
|
type: SharedLinkType.Album,
|
||||||
albumId: album.id,
|
albumId: album.id,
|
||||||
});
|
});
|
||||||
sharedLinkPassword = await apiUtils.createSharedLink(admin.accessToken, {
|
sharedLinkPassword = await utils.createSharedLink(admin.accessToken, {
|
||||||
type: SharedLinkType.Album,
|
type: SharedLinkType.Album,
|
||||||
albumId: album.id,
|
albumId: album.id,
|
||||||
password: 'test-password',
|
password: 'test-password',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test.afterAll(async () => {
|
|
||||||
await dbUtils.teardown();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('download from a shared link', async ({ page }) => {
|
test('download from a shared link', async ({ page }) => {
|
||||||
await page.goto(`/share/${sharedLink.key}`);
|
await page.goto(`/share/${sharedLink.key}`);
|
||||||
await page.getByRole('heading', { name: 'Test Album' }).waitFor();
|
await page.getByRole('heading', { name: 'Test Album' }).waitFor();
|
||||||
|
@ -12,6 +12,7 @@ export default defineConfig({
|
|||||||
test: {
|
test: {
|
||||||
include: ['src/{api,cli}/specs/*.e2e-spec.ts'],
|
include: ['src/{api,cli}/specs/*.e2e-spec.ts'],
|
||||||
globalSetup,
|
globalSetup,
|
||||||
|
testTimeout: 10_000,
|
||||||
poolOptions: {
|
poolOptions: {
|
||||||
threads: {
|
threads: {
|
||||||
singleThread: true,
|
singleThread: true,
|
||||||
|
Loading…
Reference in New Issue
Block a user