168 lines
5.6 KiB
TypeScript
168 lines
5.6 KiB
TypeScript
import { expect, test } from './fixtures';
|
|
import { UNKNOWN_UUID } from './helpers/constants';
|
|
import { createWebsite, deleteWebsite, uniqueName } from './helpers/entities';
|
|
|
|
test.describe('Annotations', () => {
|
|
test.describe.configure({ mode: 'serial' });
|
|
|
|
const date = new Date('2026-01-15T12:00:00.000Z');
|
|
|
|
let websiteId = '';
|
|
let annotationId = '';
|
|
let base = '';
|
|
|
|
test.beforeAll(async ({ admin }) => {
|
|
websiteId = (await createWebsite(admin)).id;
|
|
base = `/api/websites/${websiteId}/annotations`;
|
|
});
|
|
|
|
test.afterAll(async ({ admin }) => {
|
|
if (websiteId) {
|
|
await deleteWebsite(admin, websiteId);
|
|
}
|
|
});
|
|
|
|
test('POST /api/websites/{websiteId}/annotations creates an annotation', async ({
|
|
admin,
|
|
seed,
|
|
}) => {
|
|
const note = uniqueName('note');
|
|
const response = await admin.post(base, { date: date.toISOString(), note });
|
|
|
|
expect(response.status).toBe(200);
|
|
expect(response.body).toMatchObject({
|
|
id: expect.any(String),
|
|
websiteId,
|
|
userId: seed.admin.id,
|
|
date: date.toISOString(),
|
|
allDay: true,
|
|
note,
|
|
createdAt: expect.any(String),
|
|
});
|
|
|
|
annotationId = response.body.id;
|
|
});
|
|
|
|
test('POST /api/websites/{websiteId}/annotations validates and requires permission', async ({
|
|
admin,
|
|
user,
|
|
viewer,
|
|
api,
|
|
}) => {
|
|
const missingNote = await admin.post(base, { date: date.toISOString() });
|
|
const emptyNote = await admin.post(base, { date: date.toISOString(), note: '' });
|
|
const badDate = await admin.post(base, { date: 'not-a-date', note: 'x' });
|
|
const tooLong = await admin.post(base, { date: date.toISOString(), note: 'x'.repeat(501) });
|
|
const notOwner = await user.post(base, { date: date.toISOString(), note: 'x' });
|
|
const denied = await viewer.post(base, { date: date.toISOString(), note: 'x' });
|
|
const anonymous = await api.post(base, { date: date.toISOString(), note: 'x' });
|
|
|
|
expect(missingNote.status).toBe(400);
|
|
expect(emptyNote.status).toBe(400);
|
|
expect(badDate.status).toBe(400);
|
|
expect(tooLong.status).toBe(400);
|
|
expect(notOwner.status).toBe(401);
|
|
expect(denied.status).toBe(401);
|
|
expect(anonymous.status).toBe(401);
|
|
});
|
|
|
|
test('GET /api/websites/{websiteId}/annotations lists annotations', async ({
|
|
admin,
|
|
viewer,
|
|
share,
|
|
seed,
|
|
}) => {
|
|
const response = await admin.get(base);
|
|
const inRange = await admin.get(base, {
|
|
params: { startAt: date.getTime() - 1000, endAt: date.getTime() + 1000 },
|
|
});
|
|
const outOfRange = await admin.get(base, {
|
|
params: { startAt: date.getTime() + 86_400_000, endAt: date.getTime() + 2 * 86_400_000 },
|
|
});
|
|
const search = await admin.get(base, { params: { search: 'zzz-no-such-note' } });
|
|
const paged = await admin.get(base, { params: { page: 1, pageSize: 1 } });
|
|
const denied = await viewer.get(base);
|
|
const shared = await (await share()).get(`/api/websites/${seed.website.id}/annotations`);
|
|
|
|
expect(response.status).toBe(200);
|
|
expect(response.body).toMatchObject({
|
|
data: expect.any(Array),
|
|
count: 1,
|
|
page: 1,
|
|
pageSize: expect.any(Number),
|
|
});
|
|
expect(response.body.data[0]).toMatchObject({ id: annotationId, websiteId });
|
|
expect(inRange.body.data.map((a: any) => a.id)).toEqual([annotationId]);
|
|
expect(outOfRange.body.data).toHaveLength(0);
|
|
expect(search.body.data).toHaveLength(0);
|
|
expect(paged.body.pageSize).toBe(1);
|
|
expect(denied.status).toBe(401);
|
|
expect(shared.status).toBe(200);
|
|
});
|
|
|
|
test('GET /api/websites/{websiteId}/annotations/{annotationId} returns an annotation', async ({
|
|
admin,
|
|
viewer,
|
|
}) => {
|
|
const response = await admin.get(`${base}/${annotationId}`);
|
|
const denied = await viewer.get(`${base}/${annotationId}`);
|
|
const unknown = await admin.get(`${base}/${UNKNOWN_UUID}`);
|
|
|
|
expect(response.status).toBe(200);
|
|
expect(response.body).toMatchObject({ id: annotationId, websiteId, allDay: true });
|
|
expect(denied.status).toBe(401);
|
|
expect(unknown.status).toBe(404);
|
|
});
|
|
|
|
test('POST /api/websites/{websiteId}/annotations/{annotationId} updates an annotation', async ({
|
|
admin,
|
|
user,
|
|
}) => {
|
|
const note = uniqueName('updated');
|
|
const newDate = new Date('2026-02-01T08:30:00.000Z');
|
|
const response = await admin.post(`${base}/${annotationId}`, {
|
|
date: newDate.toISOString(),
|
|
note,
|
|
allDay: false,
|
|
});
|
|
const invalid = await admin.post(`${base}/${annotationId}`, { date: newDate.toISOString() });
|
|
const unknown = await admin.post(`${base}/${UNKNOWN_UUID}`, {
|
|
date: newDate.toISOString(),
|
|
note,
|
|
});
|
|
const denied = await user.post(`${base}/${annotationId}`, {
|
|
date: newDate.toISOString(),
|
|
note,
|
|
});
|
|
|
|
expect(response.status).toBe(200);
|
|
expect(response.body).toMatchObject({
|
|
id: annotationId,
|
|
date: newDate.toISOString(),
|
|
note,
|
|
allDay: false,
|
|
});
|
|
expect(invalid.status).toBe(400);
|
|
expect(unknown.status).toBe(404);
|
|
expect(denied.status).toBe(401);
|
|
});
|
|
|
|
test('DELETE /api/websites/{websiteId}/annotations/{annotationId} deletes an annotation', async ({
|
|
admin,
|
|
user,
|
|
}) => {
|
|
const denied = await user.del(`${base}/${annotationId}`);
|
|
const response = await admin.del(`${base}/${annotationId}`);
|
|
const gone = await admin.get(`${base}/${annotationId}`);
|
|
const again = await admin.del(`${base}/${annotationId}`);
|
|
|
|
expect(denied.status).toBe(401);
|
|
expect(response.status).toBe(200);
|
|
expect(response.body).toEqual({ ok: true });
|
|
expect(gone.status).toBe(404);
|
|
expect(again.status).toBe(404);
|
|
|
|
annotationId = '';
|
|
});
|
|
});
|