diff --git a/server/src/systemNotices/registry.ts b/server/src/systemNotices/registry.ts index 56938c6b0..3dca1513f 100644 --- a/server/src/systemNotices/registry.ts +++ b/server/src/systemNotices/registry.ts @@ -106,7 +106,11 @@ export const SYSTEM_NOTICES: SystemNotice[] = [ conditions: [{ kind: 'managed', is: false }], publishedAt: '2026-08-22T00:00:00Z', priority: 110, + // The 4.0.x window. The copy is about this release, so somebody arriving on + // 4.1 — or jumping 3.x straight to 4.2 — should get that release's notice + // instead of this one, not a summary of a version they skipped. minVersion: '4.0.0', + maxVersion: '4.1.0', }, // ── Thank-you + support the project — shown once per install AND once per upgrade ── diff --git a/server/tests/unit/systemNotices/registry.test.ts b/server/tests/unit/systemNotices/registry.test.ts index ab72bceb5..1c4c2ce41 100644 --- a/server/tests/unit/systemNotices/registry.test.ts +++ b/server/tests/unit/systemNotices/registry.test.ts @@ -3,6 +3,7 @@ import fs from 'node:fs'; import path from 'node:path'; import semver from 'semver'; import { SYSTEM_NOTICES } from '../../../src/systemNotices/registry.js'; +import { isNoticeVersionActive } from '../../../src/systemNotices/service.js'; /** Collect all actionIds registered via registerNoticeAction() in client source files. */ function collectRegisteredActionIds(): Set { @@ -63,4 +64,34 @@ describe('registry integrity', () => { } } }); + + it('the 4.0.0 release notice is confined to the 4.0.x line', () => { + const release = SYSTEM_NOTICES.find(n => n.id === 'release-4-0-0'); + expect(release).toBeDefined(); + // Its copy is about this release, so it must not greet somebody who skipped + // straight past it, and it must not run alongside the next release's notice. + expect(isNoticeVersionActive(release!, '3.4.1')).toBe(false); + expect(isNoticeVersionActive(release!, '4.0.0')).toBe(true); + expect(isNoticeVersionActive(release!, '4.0.7')).toBe(true); + // The upper bound is exclusive, so every 4.0.x patch still gets it. + expect(isNoticeVersionActive(release!, '4.0.9')).toBe(true); + expect(isNoticeVersionActive(release!, '4.0.12')).toBe(true); + expect(isNoticeVersionActive(release!, '4.1.0')).toBe(false); + expect(isNoticeVersionActive(release!, '4.2.0')).toBe(false); + }); + + it('the thank-you notice hands over to the release modal at 4.0.0', () => { + const thankYou = SYSTEM_NOTICES.find(n => n.id === 'thank-you-support'); + expect(thankYou).toBeDefined(); + // Both carry the same thank-you and the same two support links, so exactly + // one of them may be active at any version. + 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 active = [thankYou!, release].filter(n => isNoticeVersionActive(n, version)); + expect(active.length, `both thank-you notices active at ${version}`).toBeLessThanOrEqual(1); + } + }); });