mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-21 22:46:32 +00:00
feat(notices): bring the release notes back on every update
The release modal takes the 4.3.0 design: the release and its headline cards on the left, the note and the support links on the right, at the design's own size. The cards carry small drawings of the features instead of an icon. The 4.0.0 entry is retired. In its place sits one entry, release-notes, that every big release rewrites and keeps. It recurs per version with no upper bound, so every user sees it once on each update, whatever they did with it before, and a reload of the same version keeps it closed. An integration test holds that promise through the real entry and the real dismiss route. All 23 locales carry the new copy and drop the 4.0.0 strings.
This commit is contained in:
@@ -97,7 +97,7 @@ describe('GET /api/system-notices/active', () => {
|
||||
// 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 release-4-0-0 at 4.0.0.
|
||||
// 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.
|
||||
testDb.prepare('UPDATE users SET login_count = 5, first_seen_version = ? WHERE id = ?').run('3.0.0', user.id);
|
||||
const alwaysOn = new Set(
|
||||
@@ -226,6 +226,47 @@ describe('GET /api/system-notices/active', () => {
|
||||
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);
|
||||
testDb.prepare('UPDATE users SET login_count = 5, first_seen_version = ? WHERE id = ?').run('3.0.0', user.id);
|
||||
|
||||
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 === '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...
|
||||
testDb.prepare(
|
||||
'UPDATE user_notice_dismissals SET dismissed_app_version = ? WHERE user_id = ? AND notice_id = ?'
|
||||
).run('4.0.0', user.id, 'release-notes');
|
||||
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);
|
||||
});
|
||||
});
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -2,7 +2,7 @@ import { describe, it, expect } from 'vitest';
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import semver from 'semver';
|
||||
import { SYSTEM_NOTICES } from '../../../src/systemNotices/registry.js';
|
||||
import { RETIRED_NOTICE_IDS, SYSTEM_NOTICES } from '../../../src/systemNotices/registry.js';
|
||||
import { isNoticeVersionActive } from '../../../src/systemNotices/service.js';
|
||||
|
||||
/** Collect all actionIds registered via registerNoticeAction() in client source files. */
|
||||
@@ -65,29 +65,28 @@ describe('registry integrity', () => {
|
||||
}
|
||||
});
|
||||
|
||||
it('the 4.0.0 release notice covers the whole 4.x line', () => {
|
||||
const release = SYSTEM_NOTICES.find(n => n.id === 'release-4-0-0');
|
||||
it('the release notes come back on every upgrade, with no upper bound', () => {
|
||||
const release = SYSTEM_NOTICES.find(n => n.id === 'release-notes');
|
||||
expect(release).toBeDefined();
|
||||
// It must not greet somebody still on 3.x...
|
||||
// Nothing on 3.x ships this copy, and the thank-you notice still covers it there.
|
||||
expect(isNoticeVersionActive(release!, '3.4.1')).toBe(false);
|
||||
expect(isNoticeVersionActive(release!, '4.0.0')).toBe(true);
|
||||
expect(isNoticeVersionActive(release!, '4.0.7')).toBe(true);
|
||||
expect(isNoticeVersionActive(release!, '4.0.12')).toBe(true);
|
||||
// ...and it stays up across the minors, so no 4.x install is left without a
|
||||
// notice the way 4.1.0 was under the old per-release window.
|
||||
expect(isNoticeVersionActive(release!, '4.1.0')).toBe(true);
|
||||
expect(isNoticeVersionActive(release!, '4.2.0')).toBe(true);
|
||||
expect(isNoticeVersionActive(release!, '4.9.9')).toBe(true);
|
||||
// The upper bound is exclusive: 5.0.0 gets its own notice.
|
||||
expect(isNoticeVersionActive(release!, '5.0.0')).toBe(false);
|
||||
// From there on every version carries it, a patch release included, so no
|
||||
// install is left with nothing to show because nobody wrote a new window.
|
||||
for (const version of ['4.0.0', '4.2.1', '4.3.0', '4.3.1', '4.9.9', '5.0.0', '12.4.0']) {
|
||||
expect(isNoticeVersionActive(release!, version), version).toBe(true);
|
||||
}
|
||||
expect(release!.maxVersion).toBeUndefined();
|
||||
// The window alone is not enough: a one-time dismissal would retire it for good
|
||||
// the first time somebody closed it.
|
||||
expect(release!.recurring).toBe('per-version');
|
||||
});
|
||||
|
||||
it('the 4.0.0 release notice comes back on each upgrade inside that line', () => {
|
||||
const release = SYSTEM_NOTICES.find(n => n.id === 'release-4-0-0')!;
|
||||
// The version window above is only half of "covers the whole 4.x line". Without
|
||||
// this, the default one-time dismissal retired the notice for everyone who
|
||||
// closed it on 4.0.0, so none of them saw it again on 4.1, 4.2 or later.
|
||||
expect(release.recurring).toBe('per-version');
|
||||
it('the 4.0.0 release notice is retired and its id stays reserved', () => {
|
||||
expect(SYSTEM_NOTICES.some(n => n.id === 'release-4-0-0')).toBe(false);
|
||||
expect(RETIRED_NOTICE_IDS).toContain('release-4-0-0');
|
||||
for (const id of RETIRED_NOTICE_IDS) {
|
||||
expect(SYSTEM_NOTICES.some(n => n.id === id), id).toBe(false);
|
||||
}
|
||||
});
|
||||
|
||||
it('the thank-you notice hands over to the release modal at 4.0.0', () => {
|
||||
@@ -98,10 +97,10 @@ describe('registry integrity', () => {
|
||||
expect(isNoticeVersionActive(thankYou!, '3.4.1')).toBe(true);
|
||||
expect(isNoticeVersionActive(thankYou!, '4.0.0')).toBe(false);
|
||||
|
||||
const release = SYSTEM_NOTICES.find(n => n.id === 'release-4-0-0')!;
|
||||
for (const version of ['3.4.1', '4.0.0', '4.0.7', '4.1.0', '5.0.0']) {
|
||||
const release = SYSTEM_NOTICES.find(n => n.id === 'release-notes')!;
|
||||
for (const version of ['3.4.1', '4.0.0', '4.0.7', '4.1.0', '4.3.0', '5.0.0']) {
|
||||
const active = [thankYou!, release].filter(n => isNoticeVersionActive(n, version));
|
||||
expect(active.length, `both thank-you notices active at ${version}`).toBeLessThanOrEqual(1);
|
||||
expect(active.length, `thank-you and release notes at ${version}`).toBe(1);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user