feat: signed releases, release feed, notification (read-only)
This commit is contained in:
1 parent
a49eec909c
commit
834493a4f5
49 files changed
+4191
-18
No files matched your search
@@ -0,0 +1,66 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import {
|
||||
AUTO_CHECK_INTERVAL_MS,
|
||||
displayVersion,
|
||||
formatLastChecked,
|
||||
formatReleased,
|
||||
isCheckDue,
|
||||
} from '@/lib/updateFormat'
|
||||
|
||||
describe('displayVersion', () => {
|
||||
it.each([
|
||||
['v2.3.0-beta', '2.3.0-beta'],
|
||||
['2.0.0-beta', '2.0.0-beta'],
|
||||
['v2.5.0', '2.5.0'],
|
||||
])('shows %s as %s', (tag, expected) => {
|
||||
expect(displayVersion(tag)).toBe(expected)
|
||||
})
|
||||
})
|
||||
|
||||
describe('formatLastChecked', () => {
|
||||
const now = new Date(2026, 8, 30, 12, 0, 0)
|
||||
|
||||
it('reads today at the local time', () => {
|
||||
expect(formatLastChecked(new Date(2026, 8, 30, 9, 41).toISOString(), now)).toBe(
|
||||
'Last checked today at 09:41',
|
||||
)
|
||||
})
|
||||
|
||||
it('reads yesterday at the local time', () => {
|
||||
expect(formatLastChecked(new Date(2026, 8, 29, 18, 12).toISOString(), now)).toBe(
|
||||
'Last checked yesterday at 18:12',
|
||||
)
|
||||
})
|
||||
|
||||
it('reads an older day as an absolute date', () => {
|
||||
expect(formatLastChecked(new Date(2026, 8, 20, 7, 5).toISOString(), now)).toBe(
|
||||
'Last checked 20 Sep 2026 at 07:05',
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('isCheckDue', () => {
|
||||
const now = Date.parse('2026-09-30T12:00:00Z')
|
||||
|
||||
it('is due when never checked', () => {
|
||||
expect(isCheckDue(null, now)).toBe(true)
|
||||
})
|
||||
|
||||
it('is due once the last check is older than 24 hours', () => {
|
||||
expect(isCheckDue(new Date(now - AUTO_CHECK_INTERVAL_MS - 1000).toISOString(), now)).toBe(
|
||||
true,
|
||||
)
|
||||
})
|
||||
|
||||
it('is not due when the last check is under 24 hours old', () => {
|
||||
expect(isCheckDue(new Date(now - AUTO_CHECK_INTERVAL_MS + 1000).toISOString(), now)).toBe(
|
||||
false,
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('formatReleased', () => {
|
||||
it('reads the release date as an absolute date', () => {
|
||||
expect(formatReleased('2026-09-24T08:00:00Z')).toBe('Released 24 Sep 2026')
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,46 @@
|
||||
import { formatAbsoluteDateFromIso } from '@/lib/relativeDate'
|
||||
|
||||
export const ENABLE_BROWSER_UPDATES_COMMAND = 'renewcms enable-browser-updates'
|
||||
export const AUTO_CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000
|
||||
|
||||
export function displayVersion(tag: string) {
|
||||
return tag.replace(/^v/, '')
|
||||
}
|
||||
|
||||
function isSameLocalDay(a: Date, b: Date) {
|
||||
return (
|
||||
a.getFullYear() === b.getFullYear() &&
|
||||
a.getMonth() === b.getMonth() &&
|
||||
a.getDate() === b.getDate()
|
||||
)
|
||||
}
|
||||
|
||||
function formatClock(date: Date) {
|
||||
const hours = String(date.getHours()).padStart(2, '0')
|
||||
const minutes = String(date.getMinutes()).padStart(2, '0')
|
||||
return `${hours}:${minutes}`
|
||||
}
|
||||
|
||||
export function formatLastChecked(iso: string, now: Date = new Date()) {
|
||||
const date = new Date(iso)
|
||||
const yesterday = new Date(now)
|
||||
yesterday.setDate(now.getDate() - 1)
|
||||
if (isSameLocalDay(date, now)) {
|
||||
return `Last checked today at ${formatClock(date)}`
|
||||
}
|
||||
if (isSameLocalDay(date, yesterday)) {
|
||||
return `Last checked yesterday at ${formatClock(date)}`
|
||||
}
|
||||
return `Last checked ${formatAbsoluteDateFromIso(iso)} at ${formatClock(date)}`
|
||||
}
|
||||
|
||||
export function isCheckDue(checkedAt: string | null, now: number = Date.now()) {
|
||||
if (checkedAt === null) {
|
||||
return true
|
||||
}
|
||||
return now - new Date(checkedAt).getTime() > AUTO_CHECK_INTERVAL_MS
|
||||
}
|
||||
|
||||
export function formatReleased(iso: string) {
|
||||
return `Released ${formatAbsoluteDateFromIso(iso)}`
|
||||
}
|
||||
Reference in new issue
Block a user