mirror of
https://github.com/immich-app/immich.git
synced 2024-12-29 15:11:58 +00:00
refactor(e2e): use better dummy assets (#7536)
This commit is contained in:
parent
af0de1a768
commit
100363c7be
7 changed files with 88 additions and 54 deletions
20
e2e/package-lock.json
generated
20
e2e/package-lock.json
generated
|
@ -15,6 +15,7 @@
|
||||||
"@types/luxon": "^3.4.2",
|
"@types/luxon": "^3.4.2",
|
||||||
"@types/node": "^20.11.17",
|
"@types/node": "^20.11.17",
|
||||||
"@types/pg": "^8.11.0",
|
"@types/pg": "^8.11.0",
|
||||||
|
"@types/pngjs": "^6.0.4",
|
||||||
"@types/supertest": "^6.0.2",
|
"@types/supertest": "^6.0.2",
|
||||||
"@typescript-eslint/eslint-plugin": "^7.1.0",
|
"@typescript-eslint/eslint-plugin": "^7.1.0",
|
||||||
"@typescript-eslint/parser": "^7.1.0",
|
"@typescript-eslint/parser": "^7.1.0",
|
||||||
|
@ -26,6 +27,7 @@
|
||||||
"exiftool-vendored": "^24.5.0",
|
"exiftool-vendored": "^24.5.0",
|
||||||
"luxon": "^3.4.4",
|
"luxon": "^3.4.4",
|
||||||
"pg": "^8.11.3",
|
"pg": "^8.11.3",
|
||||||
|
"pngjs": "^7.0.0",
|
||||||
"prettier": "^3.2.5",
|
"prettier": "^3.2.5",
|
||||||
"prettier-plugin-organize-imports": "^3.2.4",
|
"prettier-plugin-organize-imports": "^3.2.4",
|
||||||
"socket.io-client": "^4.7.4",
|
"socket.io-client": "^4.7.4",
|
||||||
|
@ -1236,6 +1238,15 @@
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/pngjs": {
|
||||||
|
"version": "6.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/pngjs/-/pngjs-6.0.4.tgz",
|
||||||
|
"integrity": "sha512-atAK9xLKOnxiuArxcHovmnOUUGBZOQ3f0vCf43FnoKs6XnqiambT1kkJWmdo71IR+BoXSh+CueeFR0GfH3dTlQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@types/node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/semver": {
|
"node_modules/@types/semver": {
|
||||||
"version": "7.5.8",
|
"version": "7.5.8",
|
||||||
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz",
|
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz",
|
||||||
|
@ -3897,6 +3908,15 @@
|
||||||
"node": ">=4"
|
"node": ">=4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/pngjs": {
|
||||||
|
"version": "7.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/pngjs/-/pngjs-7.0.0.tgz",
|
||||||
|
"integrity": "sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.19.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/postcss": {
|
"node_modules/postcss": {
|
||||||
"version": "8.4.35",
|
"version": "8.4.35",
|
||||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz",
|
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz",
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
"@types/luxon": "^3.4.2",
|
"@types/luxon": "^3.4.2",
|
||||||
"@types/node": "^20.11.17",
|
"@types/node": "^20.11.17",
|
||||||
"@types/pg": "^8.11.0",
|
"@types/pg": "^8.11.0",
|
||||||
|
"@types/pngjs": "^6.0.4",
|
||||||
"@types/supertest": "^6.0.2",
|
"@types/supertest": "^6.0.2",
|
||||||
"@typescript-eslint/eslint-plugin": "^7.1.0",
|
"@typescript-eslint/eslint-plugin": "^7.1.0",
|
||||||
"@typescript-eslint/parser": "^7.1.0",
|
"@typescript-eslint/parser": "^7.1.0",
|
||||||
|
@ -34,6 +35,7 @@
|
||||||
"exiftool-vendored": "^24.5.0",
|
"exiftool-vendored": "^24.5.0",
|
||||||
"luxon": "^3.4.4",
|
"luxon": "^3.4.4",
|
||||||
"pg": "^8.11.3",
|
"pg": "^8.11.3",
|
||||||
|
"pngjs": "^7.0.0",
|
||||||
"prettier": "^3.2.5",
|
"prettier": "^3.2.5",
|
||||||
"prettier-plugin-organize-imports": "^3.2.4",
|
"prettier-plugin-organize-imports": "^3.2.4",
|
||||||
"socket.io-client": "^4.7.4",
|
"socket.io-client": "^4.7.4",
|
||||||
|
|
|
@ -256,7 +256,7 @@ describe('/album', () => {
|
||||||
expect(status).toBe(200);
|
expect(status).toBe(200);
|
||||||
expect(body).toEqual({
|
expect(body).toEqual({
|
||||||
...user1Albums[0],
|
...user1Albums[0],
|
||||||
assets: [expect.objectContaining(user1Albums[0].assets[0])],
|
assets: [expect.objectContaining({ id: user1Albums[0].assets[0].id })],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -268,7 +268,7 @@ describe('/album', () => {
|
||||||
expect(status).toBe(200);
|
expect(status).toBe(200);
|
||||||
expect(body).toEqual({
|
expect(body).toEqual({
|
||||||
...user2Albums[0],
|
...user2Albums[0],
|
||||||
assets: [expect.objectContaining(user2Albums[0].assets[0])],
|
assets: [expect.objectContaining({ id: user2Albums[0].assets[0].id })],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -280,7 +280,7 @@ describe('/album', () => {
|
||||||
expect(status).toBe(200);
|
expect(status).toBe(200);
|
||||||
expect(body).toEqual({
|
expect(body).toEqual({
|
||||||
...user1Albums[0],
|
...user1Albums[0],
|
||||||
assets: [expect.objectContaining(user1Albums[0].assets[0])],
|
assets: [expect.objectContaining({ id: user1Albums[0].assets[0].id })],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -59,30 +59,25 @@ describe('/asset', () => {
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// asset location
|
// asset location
|
||||||
assetLocation = await apiUtils.createAsset(
|
assetLocation = await apiUtils.createAsset(admin.accessToken, {
|
||||||
admin.accessToken,
|
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 wsUtils.waitForEvent({ event: 'upload', assetId: assetLocation.id });
|
||||||
|
|
||||||
user1Assets = await Promise.all([
|
user1Assets = await Promise.all([
|
||||||
apiUtils.createAsset(user1.accessToken),
|
apiUtils.createAsset(user1.accessToken),
|
||||||
apiUtils.createAsset(user1.accessToken),
|
apiUtils.createAsset(user1.accessToken),
|
||||||
apiUtils.createAsset(
|
apiUtils.createAsset(user1.accessToken, {
|
||||||
user1.accessToken,
|
isFavorite: true,
|
||||||
{
|
isReadOnly: true,
|
||||||
isFavorite: true,
|
fileCreatedAt: yesterday.toISO(),
|
||||||
isReadOnly: true,
|
fileModifiedAt: yesterday.toISO(),
|
||||||
fileCreatedAt: yesterday.toISO(),
|
assetData: { filename: 'example.mp4' },
|
||||||
fileModifiedAt: yesterday.toISO(),
|
}),
|
||||||
},
|
|
||||||
{ filename: 'example.mp4' },
|
|
||||||
),
|
|
||||||
apiUtils.createAsset(user1.accessToken),
|
apiUtils.createAsset(user1.accessToken),
|
||||||
apiUtils.createAsset(user1.accessToken),
|
apiUtils.createAsset(user1.accessToken),
|
||||||
]);
|
]);
|
||||||
|
@ -98,14 +93,11 @@ describe('/asset', () => {
|
||||||
apiUtils.createAsset(userStats.accessToken),
|
apiUtils.createAsset(userStats.accessToken),
|
||||||
apiUtils.createAsset(userStats.accessToken, { isFavorite: true }),
|
apiUtils.createAsset(userStats.accessToken, { isFavorite: true }),
|
||||||
apiUtils.createAsset(userStats.accessToken, { isArchived: true }),
|
apiUtils.createAsset(userStats.accessToken, { isArchived: true }),
|
||||||
apiUtils.createAsset(
|
apiUtils.createAsset(userStats.accessToken, {
|
||||||
userStats.accessToken,
|
isArchived: true,
|
||||||
{
|
isFavorite: true,
|
||||||
isArchived: true,
|
assetData: { filename: 'example.mp4' },
|
||||||
isFavorite: true,
|
}),
|
||||||
},
|
|
||||||
{ filename: 'example.mp4' },
|
|
||||||
),
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const person1 = await apiUtils.createPerson(user1.accessToken, {
|
const person1 = await apiUtils.createPerson(user1.accessToken, {
|
||||||
|
@ -615,11 +607,9 @@ 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(
|
const { id, duplicate } = await apiUtils.createAsset(admin.accessToken, {
|
||||||
admin.accessToken,
|
assetData: { bytes: await readFile(filepath), filename: basename(filepath) },
|
||||||
{},
|
});
|
||||||
{ bytes: await readFile(filepath), filename: basename(filepath) },
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(duplicate).toBe(false);
|
expect(duplicate).toBe(false);
|
||||||
|
|
||||||
|
@ -635,14 +625,12 @@ 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(
|
const { duplicate } = await apiUtils.createAsset(admin.accessToken, {
|
||||||
admin.accessToken,
|
assetData: {
|
||||||
{},
|
|
||||||
{
|
|
||||||
bytes: await readFile(join(testAssetDir, filepath)),
|
bytes: await readFile(join(testAssetDir, filepath)),
|
||||||
filename: basename(filepath),
|
filename: basename(filepath),
|
||||||
},
|
},
|
||||||
);
|
});
|
||||||
|
|
||||||
expect(duplicate).toBe(true);
|
expect(duplicate).toBe(true);
|
||||||
});
|
});
|
||||||
|
@ -669,14 +657,12 @@ 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(
|
const response = await apiUtils.createAsset(admin.accessToken, {
|
||||||
admin.accessToken,
|
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 wsUtils.waitForEvent({ event: 'upload', assetId: response.id });
|
||||||
|
|
||||||
|
|
|
@ -54,7 +54,7 @@ describe('/download', () => {
|
||||||
.set('Authorization', `Bearer ${admin.accessToken}`);
|
.set('Authorization', `Bearer ${admin.accessToken}`);
|
||||||
|
|
||||||
expect(response.status).toBe(200);
|
expect(response.status).toBe(200);
|
||||||
expect(response.headers['content-type']).toEqual('image/jpeg');
|
expect(response.headers['content-type']).toEqual('image/png');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
31
e2e/src/generators.ts
Normal file
31
e2e/src/generators.ts
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
import { PNG } from 'pngjs';
|
||||||
|
|
||||||
|
const createPNG = (r: number, g: number, b: number) => {
|
||||||
|
const image = new PNG({ width: 1, height: 1 });
|
||||||
|
image.data[0] = r;
|
||||||
|
image.data[1] = g;
|
||||||
|
image.data[2] = b;
|
||||||
|
image.data[3] = 255;
|
||||||
|
return PNG.sync.write(image);
|
||||||
|
};
|
||||||
|
|
||||||
|
function* newPngFactory() {
|
||||||
|
for (let r = 0; r < 255; r++) {
|
||||||
|
for (let g = 0; g < 255; g++) {
|
||||||
|
for (let b = 0; b < 255; b++) {
|
||||||
|
yield createPNG(r, g, b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const pngFactory = newPngFactory();
|
||||||
|
|
||||||
|
export const makeRandomImage = () => {
|
||||||
|
const { value } = pngFactory.next();
|
||||||
|
if (!value) {
|
||||||
|
throw new Error('Ran out of random asset data');
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
};
|
|
@ -21,7 +21,6 @@ import {
|
||||||
} from '@immich/sdk';
|
} from '@immich/sdk';
|
||||||
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 { randomBytes } from 'node:crypto';
|
|
||||||
import { access } from 'node:fs/promises';
|
import { access } from 'node:fs/promises';
|
||||||
import { tmpdir } from 'node:os';
|
import { tmpdir } from 'node:os';
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
|
@ -29,6 +28,7 @@ import { promisify } from 'node:util';
|
||||||
import pg from 'pg';
|
import pg from 'pg';
|
||||||
import { io, type Socket } from 'socket.io-client';
|
import { io, type Socket } from 'socket.io-client';
|
||||||
import { loginDto, signupDto } from 'src/fixtures';
|
import { loginDto, signupDto } from 'src/fixtures';
|
||||||
|
import { makeRandomImage } from 'src/generators';
|
||||||
import request from 'supertest';
|
import request from 'supertest';
|
||||||
|
|
||||||
const execPromise = promisify(exec);
|
const execPromise = promisify(exec);
|
||||||
|
@ -241,6 +241,8 @@ export const wsUtils = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type AssetData = { bytes?: Buffer; filename: string };
|
||||||
|
|
||||||
export const apiUtils = {
|
export const apiUtils = {
|
||||||
setup: () => {
|
setup: () => {
|
||||||
defaults.baseUrl = app;
|
defaults.baseUrl = app;
|
||||||
|
@ -269,11 +271,7 @@ export const apiUtils = {
|
||||||
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'>>,
|
dto?: Partial<Omit<CreateAssetDto, 'assetData'>> & { assetData?: AssetData },
|
||||||
data?: {
|
|
||||||
bytes?: Buffer;
|
|
||||||
filename: string;
|
|
||||||
},
|
|
||||||
) => {
|
) => {
|
||||||
const _dto = {
|
const _dto = {
|
||||||
deviceAssetId: 'test-1',
|
deviceAssetId: 'test-1',
|
||||||
|
@ -283,15 +281,12 @@ export const apiUtils = {
|
||||||
...dto,
|
...dto,
|
||||||
};
|
};
|
||||||
|
|
||||||
const _assetData = {
|
const assetData = dto?.assetData?.bytes || makeRandomImage();
|
||||||
bytes: randomBytes(32),
|
const filename = dto?.assetData?.filename || 'example.png';
|
||||||
filename: 'example.jpg',
|
|
||||||
...data,
|
|
||||||
};
|
|
||||||
|
|
||||||
const builder = request(app)
|
const builder = request(app)
|
||||||
.post(`/asset/upload`)
|
.post(`/asset/upload`)
|
||||||
.attach('assetData', _assetData.bytes, _assetData.filename)
|
.attach('assetData', assetData, filename)
|
||||||
.set('Authorization', `Bearer ${accessToken}`);
|
.set('Authorization', `Bearer ${accessToken}`);
|
||||||
|
|
||||||
for (const [key, value] of Object.entries(_dto)) {
|
for (const [key, value] of Object.entries(_dto)) {
|
||||||
|
|
Loading…
Reference in a new issue