This commit is contained in:
@@ -97,6 +97,7 @@ npm audit --omit=dev
|
|||||||
|
|
||||||
## Zuletzt verifiziert
|
## Zuletzt verifiziert
|
||||||
|
|
||||||
|
- Update-Dialog nach `2.1.49`: Versionsmeldung von 12 auf 14 px erhöht. Updater reicht `published_at` als `publishedAt` weiter; Anzeige ergänzt lokale Veröffentlichungszeit als `TT.MM.JJJJ - HH:mm`, fehlende/ungültige Werte bleiben ausgeblendet. Deutsche und englische Übersetzung mit Datum geprüft. 40 Übersetzungs-/Updater-Tests, zwei gezielte Renderer-Tests und Lint ohne Warnungen erfolgreich. Laufende Entwicklung zeigt im lokalen Test `Update v2.1.50 verfügbar (22.09.2026 - 19:30)`; kein Release veröffentlicht.
|
||||||
- Update-Button nach `2.1.49`: feste Breite für Button und sichtbaren Platzhalter von 146 auf 166 px erhöht; Beschriftung ohne Umbruch und ohne Schrumpfen. Gezielter Header-Regressionstest und Lint erfolgreich. In der laufenden Entwicklung für Deutsch und Englisch jeweils eine Textzeile und ausreichender Innenabstand per DOM-Messung bestätigt. Lokaler Anzeigetest für `2.1.50` im ignorierten Entwicklungslauncher aktiv; Download und Installation dort gesperrt, kein tatsächliches Release. Entwicklerversion automatisch neu gestartet.
|
- Update-Button nach `2.1.49`: feste Breite für Button und sichtbaren Platzhalter von 146 auf 166 px erhöht; Beschriftung ohne Umbruch und ohne Schrumpfen. Gezielter Header-Regressionstest und Lint erfolgreich. In der laufenden Entwicklung für Deutsch und Englisch jeweils eine Textzeile und ausreichender Innenabstand per DOM-Messung bestätigt. Lokaler Anzeigetest für `2.1.50` im ignorierten Entwicklungslauncher aktiv; Download und Installation dort gesperrt, kein tatsächliches Release. Entwicklerversion automatisch neu gestartet.
|
||||||
- Nachbesserung gegen abgeschnittenes letztes Zeichen: gezielter Electron-Layouttest für beide Sprachen und sechs Breiten sowie Lint erfolgreich. Screenshot mit vollständigem Text geprüft; automatischer Entwicklungsneustart bestätigt. Kein Release.
|
- Nachbesserung gegen abgeschnittenes letztes Zeichen: gezielter Electron-Layouttest für beide Sprachen und sechs Breiten sowie Lint erfolgreich. Screenshot mit vollständigem Text geprüft; automatischer Entwicklungsneustart bestätigt. Kein Release.
|
||||||
- Einstellungssuche: auf Nutzerwunsch Lupe rechts (12 px Innenabstand), Suchtext mit symmetrischem 32-px-Padding zentriert. Gezielter Electron-Layouttest prüft Zentrierung, Symbolposition und vollständige Platzhalter in beiden Sprachen bei sechs Breiten; Lint erfolgreich, Screenshot visuell geprüft. Automatischer Entwicklungsneustart bestätigt; noch nicht veröffentlicht.
|
- Einstellungssuche: auf Nutzerwunsch Lupe rechts (12 px Innenabstand), Suchtext mit symmetrischem 32-px-Padding zentriert. Gezielter Electron-Layouttest prüft Zentrierung, Symbolposition und vollständige Platzhalter in beiden Sprachen bei sechs Breiten; Lint erfolgreich, Screenshot visuell geprüft. Automatischer Entwicklungsneustart bestätigt; noch nicht veröffentlicht.
|
||||||
|
|||||||
@@ -185,6 +185,7 @@ async function checkForUpdate(options = {}) {
|
|||||||
currentVersion,
|
currentVersion,
|
||||||
remoteVersion,
|
remoteVersion,
|
||||||
transportTag,
|
transportTag,
|
||||||
|
publishedAt: release.published_at || null,
|
||||||
releaseUrl: release.html_url,
|
releaseUrl: release.html_url,
|
||||||
assetUrl: setupAsset.browser_download_url,
|
assetUrl: setupAsset.browser_download_url,
|
||||||
assetSize: setupAsset.size,
|
assetSize: setupAsset.size,
|
||||||
|
|||||||
+10
-1
@@ -9558,6 +9558,14 @@ function _renderUpdateReleaseNotes(container, value) {
|
|||||||
return container.childElementCount > 0;
|
return container.childElementCount > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function formatUpdatePublicationDate(value) {
|
||||||
|
if (!value) return '';
|
||||||
|
const date = new Date(value);
|
||||||
|
if (!Number.isFinite(date.getTime())) return '';
|
||||||
|
const pad = number => String(number).padStart(2, '0');
|
||||||
|
return `${pad(date.getDate())}.${pad(date.getMonth() + 1)}.${date.getFullYear()} - ${pad(date.getHours())}:${pad(date.getMinutes())}`;
|
||||||
|
}
|
||||||
|
|
||||||
function showUpdateBanner(info) {
|
function showUpdateBanner(info) {
|
||||||
if (!info) return;
|
if (!info) return;
|
||||||
_knownUpdateInfo = { ...info, available: true };
|
_knownUpdateInfo = { ...info, available: true };
|
||||||
@@ -9575,7 +9583,8 @@ function showUpdateBanner(info) {
|
|||||||
const installButton = document.getElementById('installUpdateBtn');
|
const installButton = document.getElementById('installUpdateBtn');
|
||||||
if (title) title.textContent = 'Eine neue Version ist verfügbar!';
|
if (title) title.textContent = 'Eine neue Version ist verfügbar!';
|
||||||
if (message) {
|
if (message) {
|
||||||
message.textContent = `Update v${version} verfügbar`;
|
const publishedAt = formatUpdatePublicationDate(info.publishedAt);
|
||||||
|
message.textContent = `Update v${version} verfügbar${publishedAt ? ` (${publishedAt})` : ''}`;
|
||||||
message.hidden = false;
|
message.hidden = false;
|
||||||
}
|
}
|
||||||
if (notes && notesBody) {
|
if (notes && notesBody) {
|
||||||
|
|||||||
@@ -804,6 +804,7 @@
|
|||||||
if (exact) return `${leading}${exact}${trailing}`;
|
if (exact) return `${leading}${exact}${trailing}`;
|
||||||
const patterns = target === 'en'
|
const patterns = target === 'en'
|
||||||
? [
|
? [
|
||||||
|
[/^Update v([\d.]+) verfügbar \(([^)]+)\)$/, 'Update v$1 available ($2)'],
|
||||||
[/^Update v(.+) verfügbar$/, 'Update v$1 available'],
|
[/^Update v(.+) verfügbar$/, 'Update v$1 available'],
|
||||||
[/^Wiederhergestellte Warteschlange startet in (.+) s \((.+) Jobs\)\.$/, 'Restored queue starts in $1 s ($2 jobs).'],
|
[/^Wiederhergestellte Warteschlange startet in (.+) s \((.+) Jobs\)\.$/, 'Restored queue starts in $1 s ($2 jobs).'],
|
||||||
[/^Login ok, Upload-Form bereit \(Dateifeld: (.+)\)$/, 'Login successful, upload form ready (file field: $1)'],
|
[/^Login ok, Upload-Form bereit \(Dateifeld: (.+)\)$/, 'Login successful, upload form ready (file field: $1)'],
|
||||||
@@ -912,6 +913,7 @@
|
|||||||
[/^Update v(.+) verfügbar\. Klicken zum Installieren\.$/, 'Update v$1 available. Click to install.']
|
[/^Update v(.+) verfügbar\. Klicken zum Installieren\.$/, 'Update v$1 available. Click to install.']
|
||||||
]
|
]
|
||||||
: [
|
: [
|
||||||
|
[/^Update v([\d.]+) available \(([^)]+)\)$/, 'Update v$1 verfügbar ($2)'],
|
||||||
[/^Update v(.+) available$/, 'Update v$1 verfügbar'],
|
[/^Update v(.+) available$/, 'Update v$1 verfügbar'],
|
||||||
[/^Sleep in (\d+)s\.\.\.$/, 'Ruhezustand in $1s...'],
|
[/^Sleep in (\d+)s\.\.\.$/, 'Ruhezustand in $1s...'],
|
||||||
[/^Shut down in (\d+)s\.\.\.$/, 'Herunterfahren in $1s...'],
|
[/^Shut down in (\d+)s\.\.\.$/, 'Herunterfahren in $1s...'],
|
||||||
|
|||||||
+1
-1
@@ -2952,7 +2952,7 @@ select.hs-input { max-width: none; width: auto; min-width: 140px; }
|
|||||||
.update-dialog-copy p {
|
.update-dialog-copy p {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
color: var(--text-muted);
|
color: var(--text-muted);
|
||||||
font-size: 12px;
|
font-size: 14px;
|
||||||
line-height: 1.55;
|
line-height: 1.55;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -176,6 +176,8 @@ test('English is the fallback language and German remains selectable', () => {
|
|||||||
test('translations cover static labels and interpolated status text in both languages', () => {
|
test('translations cover static labels and interpolated status text in both languages', () => {
|
||||||
assert.equal(translateText('Einstellungen', 'en'), 'Settings');
|
assert.equal(translateText('Einstellungen', 'en'), 'Settings');
|
||||||
assert.equal(translateText('Update v2.1.0 verfügbar', 'en'), 'Update v2.1.0 available');
|
assert.equal(translateText('Update v2.1.0 verfügbar', 'en'), 'Update v2.1.0 available');
|
||||||
|
assert.equal(translateText('Update v2.1.50 verfügbar (22.09.2026 - 19:30)', 'en'), 'Update v2.1.50 available (22.09.2026 - 19:30)');
|
||||||
|
assert.equal(translateText('Update v2.1.50 available (22.09.2026 - 19:30)', 'de'), 'Update v2.1.50 verfügbar (22.09.2026 - 19:30)');
|
||||||
assert.equal(translateText('Settings', 'de'), 'Einstellungen');
|
assert.equal(translateText('Settings', 'de'), 'Einstellungen');
|
||||||
assert.equal(translateText('Update v2.1.0 available', 'de'), 'Update v2.1.0 verfügbar');
|
assert.equal(translateText('Update v2.1.0 available', 'de'), 'Update v2.1.0 verfügbar');
|
||||||
assert.equal(translateText('Alle Status', 'en'), 'Any status');
|
assert.equal(translateText('Alle Status', 'en'), 'Any status');
|
||||||
|
|||||||
@@ -5483,6 +5483,21 @@ test('upload sidebar renders and updates the remaining upload size', () => {
|
|||||||
assert.match(appSource, /_setUploadTelemetryText\(['"]uploadTelemetryRemainingSize['"],\s*formatBytes\(stats\.bytesRemaining\)\)/u);
|
assert.match(appSource, /_setUploadTelemetryText\(['"]uploadTelemetryRemainingSize['"],\s*formatBytes\(stats\.bytesRemaining\)\)/u);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('update publication dates use local time and omit missing or invalid metadata', () => {
|
||||||
|
const vm = require('node:vm');
|
||||||
|
const source = fs.readFileSync(path.join(__dirname, '../renderer/app.js'), 'utf8');
|
||||||
|
const formatter = source.slice(source.indexOf('function formatUpdatePublicationDate('), source.indexOf('function showUpdateBanner('));
|
||||||
|
const context = vm.createContext({});
|
||||||
|
vm.runInContext(formatter, context);
|
||||||
|
assert.equal(vm.runInContext("formatUpdatePublicationDate('2026-09-22T19:30:00')", context), '22.09.2026 - 19:30');
|
||||||
|
for (const value of [null, undefined, '', 'invalid']) {
|
||||||
|
context.value = value;
|
||||||
|
assert.equal(vm.runInContext('formatUpdatePublicationDate(value)', context), '');
|
||||||
|
}
|
||||||
|
const css = fs.readFileSync(path.join(__dirname, '../renderer/styles.css'), 'utf8');
|
||||||
|
assert.match(css, /\.update-dialog-copy p\s*\{[^}]*font-size:\s*14px;/su);
|
||||||
|
});
|
||||||
|
|
||||||
test('header occupies its final geometry before asynchronous initialization', () => {
|
test('header occupies its final geometry before asynchronous initialization', () => {
|
||||||
const projectRoot = path.join(__dirname, '..');
|
const projectRoot = path.join(__dirname, '..');
|
||||||
const html = fs.readFileSync(path.join(projectRoot, 'renderer', 'index.html'), 'utf8');
|
const html = fs.readFileSync(path.join(projectRoot, 'renderer', 'index.html'), 'utf8');
|
||||||
|
|||||||
@@ -231,6 +231,7 @@ test('update preparation refreshes a cached release before downloading the insta
|
|||||||
name: `Multi-Hoster-Upload v${version}`,
|
name: `Multi-Hoster-Upload v${version}`,
|
||||||
tag_name: `v${version}`,
|
tag_name: `v${version}`,
|
||||||
html_url: `https://update.invalid/releases/${version}`,
|
html_url: `https://update.invalid/releases/${version}`,
|
||||||
|
published_at: '2026-09-22T17:30:00Z',
|
||||||
body: `Release ${version}`,
|
body: `Release ${version}`,
|
||||||
assets: [
|
assets: [
|
||||||
{
|
{
|
||||||
@@ -288,6 +289,7 @@ test('update preparation refreshes a cached release before downloading the insta
|
|||||||
};
|
};
|
||||||
const oldCheck = await isolatedUpdater.checkForUpdate();
|
const oldCheck = await isolatedUpdater.checkForUpdate();
|
||||||
assert.equal(oldCheck.remoteVersion, oldVersion);
|
assert.equal(oldCheck.remoteVersion, oldVersion);
|
||||||
|
assert.equal(oldCheck.publishedAt, '2026-09-22T17:30:00Z');
|
||||||
|
|
||||||
global.fetch = async url => {
|
global.fetch = async url => {
|
||||||
const value = String(url);
|
const value = String(url);
|
||||||
|
|||||||
Reference in New Issue
Block a user