mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-19 13:36:48 +00:00
724c88c201
A mechanical prettier --write over server/src, server/tests, server/scripts and shared/src with the workspaces' .prettierrc; nothing but layout and import order changes, and every file keeps the same set of imports. shared is formatted throughout and its format baseline is empty. The server baseline keeps the 35 files that Prettier would grow past their lint:size limit (they need a split first, which is not a formatting change), and the generated entity files stay as their generator writes them.
506 lines
22 KiB
TypeScript
506 lines
22 KiB
TypeScript
/**
|
|
* System Notices API integration tests.
|
|
* Covers GET /api/system-notices/active and POST /api/system-notices/:id/dismiss.
|
|
*/
|
|
import { ADDON_IDS } from '../../src/addons';
|
|
import { buildApp } from '../../src/bootstrap';
|
|
import { db as testDb } from '../../src/db/database';
|
|
import { Addons } from '../../src/db/entities/Addons.entity';
|
|
import { UserNoticeDismissals } from '../../src/db/entities/UserNoticeDismissals.entity';
|
|
import { Users } from '../../src/db/entities/Users.entity';
|
|
import { SYSTEM_NOTICES } from '../../src/systemNotices/registry';
|
|
import { getCurrentAppVersion } from '../../src/systemNotices/service';
|
|
import type { SystemNotice } from '../../src/systemNotices/types';
|
|
import { authCookie } from '../helpers/auth';
|
|
import { createUser, createAdmin } from '../helpers/factories';
|
|
import type { FactoryOrm } from '../helpers/factories/context';
|
|
import { insertRow, updateRows } from '../helpers/factories/rows';
|
|
import { setAppSetting } from '../helpers/factories/settings';
|
|
import { resetTestDb } from '../helpers/test-db';
|
|
import { MikroORM } from '@mikro-orm/core';
|
|
import type { INestApplication } from '@nestjs/common';
|
|
|
|
import type { Application } from 'express';
|
|
import request from 'supertest';
|
|
import { describe, it, expect, vi, beforeAll, beforeEach, afterAll, afterEach } from 'vitest';
|
|
|
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
// Bare in-memory DB — schema applied in beforeAll after mocks register
|
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
vi.mock('../../src/db/database', async () => {
|
|
const { createSnapshotTestDb, buildDbMock } = await import('../helpers/db-mock');
|
|
return buildDbMock(createSnapshotTestDb());
|
|
});
|
|
|
|
let nestApp: INestApplication;
|
|
let app: Application;
|
|
let orm: FactoryOrm;
|
|
|
|
// Test notice injected into the registry for notice-specific tests
|
|
const TEST_NOTICE: SystemNotice = {
|
|
id: 'test-first-login-notice',
|
|
display: 'modal',
|
|
severity: 'info',
|
|
titleKey: 'system_notice.test_first_login_notice.title',
|
|
bodyKey: 'system_notice.test_first_login_notice.body',
|
|
dismissible: true,
|
|
conditions: [{ kind: 'firstLogin' }],
|
|
publishedAt: '2026-01-01T00:00:00Z',
|
|
priority: 0,
|
|
};
|
|
|
|
// Test notice gated on an addon, so the addonFlags lookup in getActiveNoticesFor
|
|
// (built once per call from every addonEnabled condition in SYSTEM_NOTICES) is
|
|
// exercised for both the enabled and the disabled case.
|
|
const TEST_NOTICE_ADDON: SystemNotice = {
|
|
id: 'test-addon-notice',
|
|
display: 'modal',
|
|
severity: 'info',
|
|
titleKey: 'system_notice.test_addon_notice.title',
|
|
bodyKey: 'system_notice.test_addon_notice.body',
|
|
dismissible: true,
|
|
conditions: [{ kind: 'addonEnabled', addonId: ADDON_IDS.JOURNEY }],
|
|
publishedAt: '2026-01-01T00:00:00Z',
|
|
priority: 0,
|
|
};
|
|
|
|
beforeAll(async () => {
|
|
nestApp = await buildApp();
|
|
app = nestApp.getHttpAdapter().getInstance();
|
|
orm = nestApp.get(MikroORM);
|
|
});
|
|
|
|
beforeEach(() => {
|
|
resetTestDb(testDb);
|
|
});
|
|
|
|
afterAll(async () => {
|
|
await nestApp.close();
|
|
testDb.close();
|
|
});
|
|
|
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
// GET /api/system-notices/active
|
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
|
|
describe('GET /api/system-notices/active', () => {
|
|
it('returns 401 without auth', async () => {
|
|
const res = await request(app).get('/api/system-notices/active');
|
|
expect(res.status).toBe(401);
|
|
});
|
|
|
|
it('returns no login/version-gated notices for an established user', async () => {
|
|
const { user } = createUser(testDb);
|
|
// login_count > 1 means firstLogin does not match; first_seen_version >= 3.0.0 means
|
|
// existingUserBeforeVersion('3.0.0') does not match either. Notices that gate on
|
|
// nothing but the install being self-hosted still apply, and which ones those are
|
|
// changes every release (the thank-you modal handed over to the release notes at 4.0.0).
|
|
// Read the set out of the registry rather than naming them, or this ages out again.
|
|
await updateRows(orm, Users, { id: user.id }, { login_count: 5, first_seen_version: '3.0.0' });
|
|
const alwaysOn = new Set(
|
|
SYSTEM_NOTICES.filter((n) => (n.conditions ?? []).every((c) => c.kind === 'managed')).map((n) => n.id),
|
|
);
|
|
const res = await request(app).get('/api/system-notices/active').set('Cookie', authCookie(user.id));
|
|
expect(res.status).toBe(200);
|
|
expect(res.body.filter((n: { id: string }) => !alwaysOn.has(n.id))).toEqual([]);
|
|
});
|
|
|
|
it('returns firstLogin notice for user with login_count <= 1', async () => {
|
|
SYSTEM_NOTICES.push(TEST_NOTICE);
|
|
try {
|
|
const { user } = createUser(testDb);
|
|
// Set login_count to 1 (first login)
|
|
await updateRows(orm, Users, { id: user.id }, { login_count: 1 });
|
|
|
|
const res = await request(app).get('/api/system-notices/active').set('Cookie', authCookie(user.id));
|
|
expect(res.status).toBe(200);
|
|
// The always-on thank-you notice may also be present, so just assert TEST_NOTICE is there
|
|
const testNotice = res.body.find((n: { id: string }) => n.id === TEST_NOTICE.id);
|
|
expect(testNotice).toBeDefined();
|
|
// DTO should not expose conditions, publishedAt, minVersion, maxVersion, priority
|
|
expect(testNotice.conditions).toBeUndefined();
|
|
expect(testNotice.publishedAt).toBeUndefined();
|
|
expect(testNotice.minVersion).toBeUndefined();
|
|
expect(testNotice.maxVersion).toBeUndefined();
|
|
} finally {
|
|
const idx = SYSTEM_NOTICES.indexOf(TEST_NOTICE);
|
|
if (idx !== -1) SYSTEM_NOTICES.splice(idx, 1);
|
|
}
|
|
});
|
|
|
|
it('an addonEnabled-gated notice appears only while the addon is on', async () => {
|
|
SYSTEM_NOTICES.push(TEST_NOTICE_ADDON);
|
|
try {
|
|
const { user } = createUser(testDb);
|
|
await updateRows(orm, Addons, { id: ADDON_IDS.JOURNEY }, { enabled: false });
|
|
|
|
const off = await request(app).get('/api/system-notices/active').set('Cookie', authCookie(user.id));
|
|
expect(off.status).toBe(200);
|
|
expect(off.body.find((n: { id: string }) => n.id === TEST_NOTICE_ADDON.id)).toBeUndefined();
|
|
|
|
await updateRows(orm, Addons, { id: ADDON_IDS.JOURNEY }, { enabled: true });
|
|
|
|
const on = await request(app).get('/api/system-notices/active').set('Cookie', authCookie(user.id));
|
|
expect(on.status).toBe(200);
|
|
expect(on.body.find((n: { id: string }) => n.id === TEST_NOTICE_ADDON.id)).toBeDefined();
|
|
} finally {
|
|
const idx = SYSTEM_NOTICES.indexOf(TEST_NOTICE_ADDON);
|
|
if (idx !== -1) SYSTEM_NOTICES.splice(idx, 1);
|
|
}
|
|
});
|
|
|
|
it('does not return firstLogin notice for user with login_count > 1', async () => {
|
|
SYSTEM_NOTICES.push(TEST_NOTICE);
|
|
try {
|
|
const { user } = createUser(testDb);
|
|
await updateRows(orm, Users, { id: user.id }, { login_count: 5, first_seen_version: '3.0.0' });
|
|
|
|
const res = await request(app).get('/api/system-notices/active').set('Cookie', authCookie(user.id));
|
|
expect(res.status).toBe(200);
|
|
expect(res.body.find((n: { id: string }) => n.id === TEST_NOTICE.id)).toBeUndefined();
|
|
} finally {
|
|
const idx = SYSTEM_NOTICES.indexOf(TEST_NOTICE);
|
|
if (idx !== -1) SYSTEM_NOTICES.splice(idx, 1);
|
|
}
|
|
});
|
|
|
|
it('filters out dismissed notices', async () => {
|
|
SYSTEM_NOTICES.push(TEST_NOTICE);
|
|
try {
|
|
const { user } = createUser(testDb);
|
|
await updateRows(orm, Users, { id: user.id }, { login_count: 1 });
|
|
|
|
// Dismiss the notice directly in DB
|
|
await insertRow(orm, UserNoticeDismissals, {
|
|
user: user.id,
|
|
notice_id: TEST_NOTICE.id,
|
|
dismissed_at: Date.now(),
|
|
});
|
|
|
|
const res = await request(app).get('/api/system-notices/active').set('Cookie', authCookie(user.id));
|
|
expect(res.status).toBe(200);
|
|
// TEST_NOTICE should be filtered out; the thank-you notice may still appear
|
|
const found = res.body.find((n: { id: string }) => n.id === TEST_NOTICE.id);
|
|
expect(found).toBeUndefined();
|
|
} finally {
|
|
const idx = SYSTEM_NOTICES.indexOf(TEST_NOTICE);
|
|
if (idx !== -1) SYSTEM_NOTICES.splice(idx, 1);
|
|
}
|
|
});
|
|
|
|
// Drives the recurrence mechanism through a notice this test owns. It used to
|
|
// ride on thank-you-support, which stopped applying the moment that notice was
|
|
// capped at 4.0.0 — the mechanism was fine, the fixture had retired.
|
|
it('re-surfaces a per-version notice after an upgrade but hides it within the same version', async () => {
|
|
const RECURRING: SystemNotice = {
|
|
id: 'test-per-version-notice',
|
|
display: 'modal',
|
|
severity: 'info',
|
|
titleKey: 'system_notice.test_per_version_notice.title',
|
|
bodyKey: 'system_notice.test_per_version_notice.body',
|
|
dismissible: true,
|
|
conditions: [],
|
|
recurring: 'per-version',
|
|
publishedAt: '2026-01-01T00:00:00Z',
|
|
priority: 0,
|
|
};
|
|
SYSTEM_NOTICES.push(RECURRING);
|
|
try {
|
|
const { user } = createUser(testDb);
|
|
await updateRows(orm, Users, { id: user.id }, { login_count: 5, first_seen_version: '3.0.0' });
|
|
|
|
const shows = async () => {
|
|
const res = await request(app).get('/api/system-notices/active').set('Cookie', authCookie(user.id));
|
|
expect(res.status).toBe(200);
|
|
return res.body.some((n: { id: string }) => n.id === RECURRING.id);
|
|
};
|
|
|
|
// Fresh user with no dismissal: the recurring notice shows.
|
|
expect(await shows()).toBe(true);
|
|
|
|
// Dismissed at an old version → it returns once the running version is newer.
|
|
await insertRow(orm, UserNoticeDismissals, {
|
|
user: user.id,
|
|
notice_id: RECURRING.id,
|
|
dismissed_at: Date.now(),
|
|
dismissed_app_version: '0.0.1',
|
|
});
|
|
expect(await shows()).toBe(true);
|
|
|
|
// Dismissed at a version >= the running one → stays hidden until the next upgrade.
|
|
await updateRows(
|
|
orm,
|
|
UserNoticeDismissals,
|
|
{ user: user.id, notice_id: RECURRING.id },
|
|
{ dismissed_app_version: '99.0.0' },
|
|
);
|
|
expect(await shows()).toBe(false);
|
|
} finally {
|
|
const idx = SYSTEM_NOTICES.indexOf(RECURRING);
|
|
if (idx !== -1) SYSTEM_NOTICES.splice(idx, 1);
|
|
}
|
|
});
|
|
|
|
// The release notes carry the support links, so the promise is exact: once per
|
|
// update for every user, whatever they did with it before, and never again on a
|
|
// reload of the same version. Driven through the real registry entry and the real
|
|
// dismiss route, so neither half can drift away from the other unnoticed.
|
|
it('shows the release notes once per update, however often they were closed before', async () => {
|
|
const { user } = createUser(testDb);
|
|
await updateRows(orm, Users, { id: user.id }, { login_count: 5, first_seen_version: '3.0.0' });
|
|
|
|
// The client announces the release layout and the version it was built as; a
|
|
// bundle that predates that, or one built for another version, never gets the
|
|
// notice (pinned separately below).
|
|
const shows = async () => {
|
|
const res = await request(app)
|
|
.get('/api/system-notices/active')
|
|
.query({ supports: 'release', ui: getCurrentAppVersion() })
|
|
.set('Cookie', authCookie(user.id));
|
|
expect(res.status).toBe(200);
|
|
return res.body.some((n: { id: string }) => n.id === 'release-notes');
|
|
};
|
|
const dismiss = async () => {
|
|
const res = await request(app)
|
|
.post('/api/system-notices/release-notes/dismiss')
|
|
.set('Cookie', authCookie(user.id));
|
|
expect(res.status).toBe(204);
|
|
};
|
|
|
|
expect(await shows()).toBe(true);
|
|
|
|
// Closed on this version: gone for every reload after it.
|
|
await dismiss();
|
|
expect(await shows()).toBe(false);
|
|
expect(await shows()).toBe(false);
|
|
|
|
// The next update brings it back, although it was closed before...
|
|
await updateRows(
|
|
orm,
|
|
UserNoticeDismissals,
|
|
{ user: user.id, notice_id: 'release-notes' },
|
|
{ dismissed_app_version: '4.0.0' },
|
|
);
|
|
expect(await shows()).toBe(true);
|
|
expect(await shows()).toBe(true);
|
|
|
|
// ...and closing it again holds until the one after that.
|
|
await dismiss();
|
|
expect(await shows()).toBe(false);
|
|
});
|
|
|
|
// Right after an update the browser still runs the bundle the service worker cached,
|
|
// which has neither the copy for the new release notice nor a way to tell. It would
|
|
// draw the keys, or its own older texts, and on close spend the notice for this whole
|
|
// version. So the notice waits for a client that announces the release layout AND
|
|
// was built for the version now running; the reload brings one.
|
|
it('keeps the release notes from a client that does not announce the release layout for this version', async () => {
|
|
const { user } = createUser(testDb);
|
|
await updateRows(orm, Users, { id: user.id }, { login_count: 5, first_seen_version: '3.0.0' });
|
|
|
|
const fetchActive = (query: Record<string, string>) =>
|
|
request(app).get('/api/system-notices/active').query(query).set('Cookie', authCookie(user.id));
|
|
|
|
const without = await fetchActive({});
|
|
expect(without.status).toBe(200);
|
|
expect(without.body.some((n: { id: string }) => n.id === 'release-notes')).toBe(false);
|
|
expect(without.body.some((n: { release?: unknown }) => n.release !== undefined)).toBe(false);
|
|
|
|
// The layout alone is the shell of the previous version after an update.
|
|
const layoutOnly = await fetchActive({ supports: 'release' });
|
|
expect(layoutOnly.body.some((n: { id: string }) => n.id === 'release-notes')).toBe(false);
|
|
const olderBundle = await fetchActive({ supports: 'release', ui: '4.0.0' });
|
|
expect(olderBundle.body.some((n: { id: string }) => n.id === 'release-notes')).toBe(false);
|
|
|
|
const matching = await fetchActive({ supports: 'release', ui: getCurrentAppVersion() });
|
|
expect(matching.status).toBe(200);
|
|
expect(matching.body.some((n: { id: string }) => n.id === 'release-notes')).toBe(true);
|
|
});
|
|
});
|
|
|
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
// POST /api/system-notices/:id/dismiss
|
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
|
|
describe('POST /api/system-notices/:id/dismiss', () => {
|
|
it('returns 401 without auth', async () => {
|
|
const res = await request(app).post('/api/system-notices/test-id/dismiss');
|
|
expect(res.status).toBe(401);
|
|
});
|
|
|
|
it('returns 404 for unknown notice id', async () => {
|
|
const { user } = createUser(testDb);
|
|
const res = await request(app)
|
|
.post('/api/system-notices/nonexistent-id/dismiss')
|
|
.set('Cookie', authCookie(user.id));
|
|
expect(res.status).toBe(404);
|
|
expect(res.body.error).toBe('NOTICE_NOT_FOUND');
|
|
});
|
|
|
|
it('returns 204 for valid notice id', async () => {
|
|
SYSTEM_NOTICES.push(TEST_NOTICE);
|
|
try {
|
|
const { user } = createUser(testDb);
|
|
const res = await request(app)
|
|
.post(`/api/system-notices/${TEST_NOTICE.id}/dismiss`)
|
|
.set('Cookie', authCookie(user.id));
|
|
expect(res.status).toBe(204);
|
|
} finally {
|
|
const idx = SYSTEM_NOTICES.indexOf(TEST_NOTICE);
|
|
if (idx !== -1) SYSTEM_NOTICES.splice(idx, 1);
|
|
}
|
|
});
|
|
|
|
it('is idempotent — second dismiss also returns 204', async () => {
|
|
SYSTEM_NOTICES.push(TEST_NOTICE);
|
|
try {
|
|
const { user } = createUser(testDb);
|
|
const first = await request(app)
|
|
.post(`/api/system-notices/${TEST_NOTICE.id}/dismiss`)
|
|
.set('Cookie', authCookie(user.id));
|
|
expect(first.status).toBe(204);
|
|
|
|
const second = await request(app)
|
|
.post(`/api/system-notices/${TEST_NOTICE.id}/dismiss`)
|
|
.set('Cookie', authCookie(user.id));
|
|
expect(second.status).toBe(204);
|
|
} finally {
|
|
const idx = SYSTEM_NOTICES.indexOf(TEST_NOTICE);
|
|
if (idx !== -1) SYSTEM_NOTICES.splice(idx, 1);
|
|
}
|
|
});
|
|
|
|
it('dismiss appears in GET /active as filtered out', async () => {
|
|
SYSTEM_NOTICES.push(TEST_NOTICE);
|
|
try {
|
|
const { user } = createUser(testDb);
|
|
await updateRows(orm, Users, { id: user.id }, { login_count: 1 });
|
|
|
|
// Confirm TEST_NOTICE is visible before dismiss
|
|
const before = await request(app).get('/api/system-notices/active').set('Cookie', authCookie(user.id));
|
|
expect(before.body.find((n: { id: string }) => n.id === TEST_NOTICE.id)).toBeDefined();
|
|
|
|
// Dismiss it
|
|
await request(app).post(`/api/system-notices/${TEST_NOTICE.id}/dismiss`).set('Cookie', authCookie(user.id));
|
|
|
|
// Confirm TEST_NOTICE is gone; other notices (e.g. welcome-v1) may still appear
|
|
const after = await request(app).get('/api/system-notices/active').set('Cookie', authCookie(user.id));
|
|
expect(after.status).toBe(200);
|
|
expect(after.body.find((n: { id: string }) => n.id === TEST_NOTICE.id)).toBeUndefined();
|
|
} finally {
|
|
const idx = SYSTEM_NOTICES.indexOf(TEST_NOTICE);
|
|
if (idx !== -1) SYSTEM_NOTICES.splice(idx, 1);
|
|
}
|
|
});
|
|
});
|
|
|
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
// v3014-whitespace-collision notice
|
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
|
|
/**
|
|
* Helper: creates an admin user whose first_seen_version is before 3.0.14
|
|
* (so existingUserBeforeVersion('3.0.14') passes) and whose login_count is
|
|
* high enough to suppress the firstLogin and v3-upgrade notice conditions.
|
|
*/
|
|
async function setupCollisionAdmin() {
|
|
const { user } = createAdmin(testDb);
|
|
await updateRows(orm, Users, { id: user.id }, { login_count: 5, first_seen_version: '3.0.0' });
|
|
return user;
|
|
}
|
|
|
|
describe('v3014-whitespace-collision notice', () => {
|
|
const NOTICE_ID = 'v3014-whitespace-collision';
|
|
const originalAppVersion = process.env.APP_VERSION;
|
|
|
|
beforeEach(() => {
|
|
process.env.APP_VERSION = '3.0.14';
|
|
});
|
|
|
|
afterEach(() => {
|
|
if (originalAppVersion === undefined) {
|
|
delete process.env.APP_VERSION;
|
|
} else {
|
|
process.env.APP_VERSION = originalAppVersion;
|
|
}
|
|
});
|
|
|
|
it('SN-COLLISION-1 — shown to admin when collision flag is set and user predates 3.0.14', async () => {
|
|
const user = await setupCollisionAdmin();
|
|
await setAppSetting(orm, 'whitespace_migration_collision', 'true');
|
|
|
|
const res = await request(app).get('/api/system-notices/active').set('Cookie', authCookie(user.id));
|
|
|
|
expect(res.status).toBe(200);
|
|
expect(res.body.find((n: { id: string }) => n.id === NOTICE_ID)).toBeDefined();
|
|
});
|
|
|
|
it('SN-COLLISION-2 — hidden when collision flag is absent', async () => {
|
|
const user = await setupCollisionAdmin();
|
|
|
|
const res = await request(app).get('/api/system-notices/active').set('Cookie', authCookie(user.id));
|
|
|
|
expect(res.status).toBe(200);
|
|
expect(res.body.find((n: { id: string }) => n.id === NOTICE_ID)).toBeUndefined();
|
|
});
|
|
|
|
it('SN-COLLISION-3 — hidden when collision flag is explicitly false', async () => {
|
|
const user = await setupCollisionAdmin();
|
|
await setAppSetting(orm, 'whitespace_migration_collision', 'false');
|
|
|
|
const res = await request(app).get('/api/system-notices/active').set('Cookie', authCookie(user.id));
|
|
|
|
expect(res.status).toBe(200);
|
|
expect(res.body.find((n: { id: string }) => n.id === NOTICE_ID)).toBeUndefined();
|
|
});
|
|
|
|
it('SN-COLLISION-4 — hidden for non-admin user even when collision flag is set', async () => {
|
|
const { user } = createUser(testDb);
|
|
await updateRows(orm, Users, { id: user.id }, { login_count: 5, first_seen_version: '3.0.0' });
|
|
await setAppSetting(orm, 'whitespace_migration_collision', 'true');
|
|
|
|
const res = await request(app).get('/api/system-notices/active').set('Cookie', authCookie(user.id));
|
|
|
|
expect(res.status).toBe(200);
|
|
expect(res.body.find((n: { id: string }) => n.id === NOTICE_ID)).toBeUndefined();
|
|
});
|
|
|
|
it('SN-COLLISION-5 — hidden for user whose first_seen_version is >= 3.0.14 (new account)', async () => {
|
|
const { user } = createAdmin(testDb);
|
|
await updateRows(orm, Users, { id: user.id }, { login_count: 5, first_seen_version: '3.0.14' });
|
|
await setAppSetting(orm, 'whitespace_migration_collision', 'true');
|
|
|
|
const res = await request(app).get('/api/system-notices/active').set('Cookie', authCookie(user.id));
|
|
|
|
expect(res.status).toBe(200);
|
|
expect(res.body.find((n: { id: string }) => n.id === NOTICE_ID)).toBeUndefined();
|
|
});
|
|
|
|
it('SN-COLLISION-6 — hidden when app version is below 3.0.14', async () => {
|
|
process.env.APP_VERSION = '3.0.13';
|
|
const user = await setupCollisionAdmin();
|
|
await setAppSetting(orm, 'whitespace_migration_collision', 'true');
|
|
|
|
const res = await request(app).get('/api/system-notices/active').set('Cookie', authCookie(user.id));
|
|
|
|
expect(res.status).toBe(200);
|
|
expect(res.body.find((n: { id: string }) => n.id === NOTICE_ID)).toBeUndefined();
|
|
});
|
|
|
|
it('SN-COLLISION-7 — hidden after admin dismisses it', async () => {
|
|
const user = await setupCollisionAdmin();
|
|
await setAppSetting(orm, 'whitespace_migration_collision', 'true');
|
|
|
|
const before = await request(app).get('/api/system-notices/active').set('Cookie', authCookie(user.id));
|
|
expect(before.body.find((n: { id: string }) => n.id === NOTICE_ID)).toBeDefined();
|
|
|
|
const dismiss = await request(app)
|
|
.post(`/api/system-notices/${NOTICE_ID}/dismiss`)
|
|
.set('Cookie', authCookie(user.id));
|
|
expect(dismiss.status).toBe(204);
|
|
|
|
const after = await request(app).get('/api/system-notices/active').set('Cookie', authCookie(user.id));
|
|
expect(after.body.find((n: { id: string }) => n.id === NOTICE_ID)).toBeUndefined();
|
|
});
|
|
});
|