From e9090fa33c0d5b1d5ad2eb1ed456c540bf552f07 Mon Sep 17 00:00:00 2001 From: Maurice Date: Sat, 22 Aug 2026 11:05:05 +0200 Subject: [PATCH] fix(notices): confine the 4.0.0 release modal to the 4.0.x line Without an upper bound it would greet somebody who jumped 3.x straight to 4.2 with a summary of a release they skipped, and it would run alongside the next release's own notice. maxVersion is exclusive, so 4.1.0 covers every 4.0.x patch. Pinned in the registry test, together with the handover from the thank-you notice: at no version may both be active. --- server/src/systemNotices/registry.ts | 4 +++ .../tests/unit/systemNotices/registry.test.ts | 31 +++++++++++++++++++ 2 files changed, 35 insertions(+) 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); + } + }); });