Ampel / ampel (push) Successful in 1m33s
Der Commander: "irgendwie fehlt in rippy selbst der Button 'Auf updates pruefen' oder auch direkt beim start ne meldung 'ey jo update verfuegbar' - waere noch cool wenn das kommt inkl. changelog". Zu Recht. Der Kanal arbeitete korrekt, war aber UNSICHTBAR: Der Update-Stand lag im Haupt und wurde nur beim Laden des Fensters EINMAL mitgeschickt. Fand die Pruefung etwas, aenderte sich am Bildschirm nichts; der einzige Hinweis war ein Satz unter "Update-Adresse", den man kennen musste, um ihn zu finden. Was jetzt da ist: * Eine goldene Karte OBEN in der Uebersicht, sobald etwas gefunden wird — mit neuer Versionsnummer, der laufenden Version, Ladefortschritt und den Aenderungsnotizen. Dazu ein Windows-Toast beim Fund. * Knopf "Jetzt auf Updates pruefen" in den Einstellungen. Waehrend eines Rips ist er AUS, mit Begruendung im Tooltip — Rippy sucht dann bewusst nicht (Paragraph 6.8). * Knopf "Jetzt neu starten und installieren" auf der Karte, sobald das Paket geladen ist. Sagt NEIN mit Grund statt still nichts zu tun (R4). * Der Stand meldet JEDE Aenderung sofort ans Fenster (setzen() ruft immer beiAenderung) — kein Zustand mehr, den niemand erfaehrt. Der Changelog kommt aus bau/release-notes.md. An der installierten Bibliothek geprueft statt aus dem Kopf (Regel D): app-builder-lib out/publish/updateInfoBuilder.js getReleaseInfo() liest genau diesen Dateinamen und legt den Inhalt als releaseNotes in latest.yml; builder-util-runtime deklariert UpdateInfo.releaseNotes als "string | Array<ReleaseNoteInfo> | null". neuerungenText() vertraegt beide Formen, wirft HTML-Marken raus und deckelt die Laenge. UpdateStand ist jetzt EINE Definition in gemeinsam/nachrichten.ts, die sich Haupt und Fenster teilen. 230 Tests gruen (vorher 225), Typpruefung sauber. Version 5.1.2. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
71 lines
2.7 KiB
TypeScript
71 lines
2.7 KiB
TypeScript
// Tests der Update-Regeln (§ 6.8) — die HTTPS-Pflicht ist keine Kür:
|
|
// Ohne Code-Signing (Entscheid 5) ist der Transportweg die EINZIGE
|
|
// Absicherung des Selbst-Updates.
|
|
import { describe, expect, it } from 'vitest'
|
|
import {
|
|
neuerungenText,
|
|
updateUrlPruefen,
|
|
NEUERUNGEN_HOECHSTENS,
|
|
STANDARD_UPDATE_URL,
|
|
} from '../src/haupt/update-regeln'
|
|
|
|
describe('updateUrlPruefen', () => {
|
|
it('leer heißt Standard (der ist https)', () => {
|
|
expect(updateUrlPruefen('').ok).toBe(true)
|
|
expect(STANDARD_UPDATE_URL.startsWith('https://')).toBe(true)
|
|
})
|
|
it('https wird angenommen', () => {
|
|
expect(updateUrlPruefen('https://mein-server.example/rippy').ok).toBe(true)
|
|
})
|
|
it('http wird ABGELEHNT, nicht stillschweigend akzeptiert', () => {
|
|
const urteil = updateUrlPruefen('http://mein-server.example/rippy')
|
|
expect(urteil.ok).toBe(false)
|
|
expect(urteil.grund).toContain('https://')
|
|
})
|
|
it('anderes Zeug wird abgelehnt', () => {
|
|
expect(updateUrlPruefen('ftp://x').ok).toBe(false)
|
|
expect(updateUrlPruefen('file://x').ok).toBe(false)
|
|
})
|
|
})
|
|
|
|
// Die Änderungsnotizen kommen aus latest.yml und werden im Fenster
|
|
// angezeigt (Commander-Wunsch 01.09.2026: „inkl. changelog").
|
|
// builder-util-runtime deklariert sie als
|
|
// `string | Array<ReleaseNoteInfo> | null` — beide Formen müssen hier
|
|
// ankommen, sonst steht in der Karte „[object Object]".
|
|
describe('neuerungenText', () => {
|
|
it('nimmt den Text aus bau/release-notes.md, wie er kommt', () => {
|
|
expect(neuerungenText('Version 5.1.2\n\nNeu: ein Knopf.\n')).toBe('Version 5.1.2\n\nNeu: ein Knopf.')
|
|
})
|
|
|
|
it('nimmt auch die Listenform (mehrere übersprungene Versionen)', () => {
|
|
const roh = [
|
|
{ version: '5.1.1', note: 'Serien-Discs richtig erkannt.' },
|
|
{ version: '5.1.2', note: 'Update-Meldung im Fenster.' },
|
|
]
|
|
const text = neuerungenText(roh)
|
|
expect(text).toContain('5.1.1')
|
|
expect(text).toContain('Serien-Discs richtig erkannt.')
|
|
expect(text).toContain('Update-Meldung im Fenster.')
|
|
})
|
|
|
|
it('HTML-Marken fliegen raus — der Text kommt von einem Server', () => {
|
|
const text = neuerungenText('<b>fett</b> und <script>boese()</script> Rest')
|
|
expect(text).not.toContain('<')
|
|
expect(text).not.toContain('script>')
|
|
expect(text).toContain('fett')
|
|
})
|
|
|
|
it('nichts, Unsinn und Leeres ergeben einen leeren Text — nie [object Object]', () => {
|
|
expect(neuerungenText(null)).toBe('')
|
|
expect(neuerungenText(undefined)).toBe('')
|
|
expect(neuerungenText(42)).toBe('')
|
|
expect(neuerungenText([{ version: '1' }])).toBe('')
|
|
expect(neuerungenText(' \n ')).toBe('')
|
|
})
|
|
|
|
it('eine ausufernde Datei sprengt die Karte nicht', () => {
|
|
expect(neuerungenText('x'.repeat(NEUERUNGEN_HOECHSTENS * 2)).length).toBe(NEUERUNGEN_HOECHSTENS)
|
|
})
|
|
})
|