This commit is contained in:
@@ -6,6 +6,7 @@ Multi-Hoster-Upload ist eine Electron-Desktopanwendung für Windows, die große
|
|||||||
|
|
||||||
## Aktueller Zustand
|
## Aktueller Zustand
|
||||||
|
|
||||||
|
- Online-Backup-Gültigkeitsdauer nach `2.1.49`, noch nicht veröffentlicht: Auswahl und Erstellen-Button verwenden 40 px Höhe und 14 px Schrift; Label und Auswahl sind zur Importzeile ausgerichtet. Footer bricht bei Platzmangel um, auf schmalen Fenstern untereinander. Leere Statuszeile belegt keinen Platz; echte Meldungen bleiben sichtbar. Nativer Select und 14 px Pfeil-Innenabstand bleiben erhalten. Electron-Layoutregression bei 1000, 600 und 360 px; Screenshot der breiten Darstellung visuell geprüft.
|
||||||
- Nach `2.1.49`, noch nicht veröffentlicht: Backup-Untermenü erhält eine durchgängige Hover-Fläche über den sichtbaren Abstand und 250 ms abbrechbare Schließverzögerung. Hover plus Klick bleibt offen, erneutes Betreten startet keine neue Öffnungsanimation; Tastaturfokus hält das Untermenü offen. Pfeil und Pfeiltasten folgen der linken Öffnungsrichtung, Escape schließt nur die aktuelle Untermenüebene. Fremde `animationend`-Ereignisse schließen kein übergeordnetes Panel mehr. Electron-Regression prüft DOM-Treffertests im Abstand, verzögerte Ein-/Austritte, Klick, Fokus und Escape bei 850 und 600 Pixel Fensterbreite. Kein Release beauftragt.
|
- Nach `2.1.49`, noch nicht veröffentlicht: Backup-Untermenü erhält eine durchgängige Hover-Fläche über den sichtbaren Abstand und 250 ms abbrechbare Schließverzögerung. Hover plus Klick bleibt offen, erneutes Betreten startet keine neue Öffnungsanimation; Tastaturfokus hält das Untermenü offen. Pfeil und Pfeiltasten folgen der linken Öffnungsrichtung, Escape schließt nur die aktuelle Untermenüebene. Fremde `animationend`-Ereignisse schließen kein übergeordnetes Panel mehr. Electron-Regression prüft DOM-Treffertests im Abstand, verzögerte Ein-/Austritte, Klick, Fokus und Escape bei 850 und 600 Pixel Fensterbreite. Kein Release beauftragt.
|
||||||
- Release `2.1.49` auf beiden Plattformen veröffentlicht und geprüft: Vidmoly-Sitzungsprüfung, getrennte Upload-Sperrmeldungen, Checkbox-Fokusfix und GB-Dateigrößenlimit. Live-Diagnose am 22.09.2026 bestätigt zuerst gültige Anmeldung mit `UPLOAD_DISABLED` (Upload-Konfiguration HTTP 403), danach erfolgreiche Anmeldung und vollständige Upload-Konfiguration (HTTP 200). Nutzer bestätigt funktionierende Anmeldung und gibt Release frei. Ein tatsächlicher Dateitransfer wurde dadurch nicht nachgewiesen. Ältere Angaben zu unveröffentlichten Änderungen darunter dokumentieren die Vorbereitung; diese Änderungen sind jetzt in `2.1.49` enthalten.
|
- Release `2.1.49` auf beiden Plattformen veröffentlicht und geprüft: Vidmoly-Sitzungsprüfung, getrennte Upload-Sperrmeldungen, Checkbox-Fokusfix und GB-Dateigrößenlimit. Live-Diagnose am 22.09.2026 bestätigt zuerst gültige Anmeldung mit `UPLOAD_DISABLED` (Upload-Konfiguration HTTP 403), danach erfolgreiche Anmeldung und vollständige Upload-Konfiguration (HTTP 200). Nutzer bestätigt funktionierende Anmeldung und gibt Release frei. Ein tatsächlicher Dateitransfer wurde dadurch nicht nachgewiesen. Ältere Angaben zu unveröffentlichten Änderungen darunter dokumentieren die Vorbereitung; diese Änderungen sind jetzt in `2.1.49` enthalten.
|
||||||
- Vidmoly nach `2.1.48`, noch nicht veröffentlicht: Anmeldung wird über `/api/auth/me` statt anhand der Upload-Konfiguration verifiziert. Öffentliches Website-JavaScript am 22.09.2026 bestätigt unveränderte Upload-Felder sowie `DISK_FULL`, `UPLOAD_IP_BLACKLIST` und `UPLOAD_DISABLED`; diese Antworten werden jetzt getrennt und ohne Antwortdaten angezeigt. Upload übernimmt zusätzlich das aktuelle Webfeld `tos=1`, weiterhin ohne API-Key. HTTP-Fehler, fehlende Benutzer, OTP-Anforderung und ungültige Serveradressen werden geprüft. Die konkrete Serverantwort des gemeldeten Accounts war in den lokalen Logs nicht vorhanden; erfolgreicher Live-Login/Upload ist noch nicht bestätigt.
|
- Vidmoly nach `2.1.48`, noch nicht veröffentlicht: Anmeldung wird über `/api/auth/me` statt anhand der Upload-Konfiguration verifiziert. Öffentliches Website-JavaScript am 22.09.2026 bestätigt unveränderte Upload-Felder sowie `DISK_FULL`, `UPLOAD_IP_BLACKLIST` und `UPLOAD_DISABLED`; diese Antworten werden jetzt getrennt und ohne Antwortdaten angezeigt. Upload übernimmt zusätzlich das aktuelle Webfeld `tos=1`, weiterhin ohne API-Key. HTTP-Fehler, fehlende Benutzer, OTP-Anforderung und ungültige Serveradressen werden geprüft. Die konkrete Serverantwort des gemeldeten Accounts war in den lokalen Logs nicht vorhanden; erfolgreicher Live-Login/Upload ist noch nicht bestätigt.
|
||||||
@@ -89,6 +90,7 @@ npm audit --omit=dev
|
|||||||
|
|
||||||
## Zuletzt verifiziert
|
## Zuletzt verifiziert
|
||||||
|
|
||||||
|
- Gültigkeitsdauer-Layout: neue Electron-Regression erfolgreich, Screenshot visuell geprüft, Lint und 18 Servertests erfolgreich. Hauptlauf 838/839 erfolgreich: Menü-Fokustest reagierte einmal zu früh auf asynchronen Fokus. Test wartet jetzt begrenzt auf den tatsächlichen Fokus statt pauschal 240 ms; kompletter Wiederholungslauf der betroffenen Datei 25/25 erfolgreich. Kein Release erstellt.
|
||||||
- Backup-Untermenü nach `2.1.49`: 838 Haupttests, 18 Servertests und Lint erfolgreich. Gezielter Electron-Test nach abschließender Fokuskorrektur erneut erfolgreich; ein Mausklick auf den Trigger hält das Menü beim Verlassen nicht unbeabsichtigt offen. Änderungen noch nicht veröffentlicht.
|
- Backup-Untermenü nach `2.1.49`: 838 Haupttests, 18 Servertests und Lint erfolgreich. Gezielter Electron-Test nach abschließender Fokuskorrektur erneut erfolgreich; ein Mausklick auf den Trigger hält das Menü beim Verlassen nicht unbeabsichtigt offen. Änderungen noch nicht veröffentlicht.
|
||||||
- Release `2.1.49`: Titel, Tags, englischer GitHub- und deutscher Forgejo-Changelog, jeweils vier Assets und Update-Metadaten geprüft. Alle acht veröffentlichten Dateien erneut heruntergeladen und Größe sowie SHA-512 mit lokalen Dateien verglichen. Echter Updater erkennt `2.1.49` ausgehend von `2.1.48` und akzeptiert englische Hinweise sowie Forgejo-Manifest. Öffentlicher Quellcheck einschließlich Screenshots erfolgreich (164 Dateien). Keine produktive Serveränderung erforderlich.
|
- Release `2.1.49`: Titel, Tags, englischer GitHub- und deutscher Forgejo-Changelog, jeweils vier Assets und Update-Metadaten geprüft. Alle acht veröffentlichten Dateien erneut heruntergeladen und Größe sowie SHA-512 mit lokalen Dateien verglichen. Echter Updater erkennt `2.1.49` ausgehend von `2.1.48` und akzeptiert englische Hinweise sowie Forgejo-Manifest. Öffentlicher Quellcheck einschließlich Screenshots erfolgreich (164 Dateien). Keine produktive Serveränderung erforderlich.
|
||||||
- Releasevorbereitung `2.1.49`: 837 Haupttests und 18 Backup-API-Tests erfolgreich, Lint erfolgreich, Produktionsaudit ohne Schwachstellen. Installer 98.033.241 Bytes, Portable 97.808.535 Bytes, beide Produktversion `2.1.49`. Gepackte Vidmoly-, Upload-, UI- und Backupmodule entsprechen dem Quellstand; keine Nutzerdaten enthalten. Installer-SHA-512 und Größe stimmen mit dem Update-Manifest überein.
|
- Releasevorbereitung `2.1.49`: 837 Haupttests und 18 Backup-API-Tests erfolgreich, Lint erfolgreich, Produktionsaudit ohne Schwachstellen. Installer 98.033.241 Bytes, Portable 97.808.535 Bytes, beide Produktversion `2.1.49`. Gepackte Vidmoly-, Upload-, UI- und Backupmodule entsprechen dem Quellstand; keine Nutzerdaten enthalten. Installer-SHA-512 und Größe stimmen mit dem Update-Manifest überein.
|
||||||
|
|||||||
+17
-7
@@ -2243,7 +2243,8 @@ select.hs-input { max-width: none; width: auto; min-width: 140px; }
|
|||||||
|
|
||||||
.online-backup-footer {
|
.online-backup-footer {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: flex-end;
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
@@ -2251,13 +2252,16 @@ select.hs-input { max-width: none; width: auto; min-width: 140px; }
|
|||||||
|
|
||||||
.online-backup-retention-field {
|
.online-backup-retention-field {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 6px;
|
grid-template-columns: 150px minmax(210px, 300px);
|
||||||
min-width: 190px;
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
flex: 1 1 370px;
|
||||||
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.online-backup-retention-field label {
|
.online-backup-retention-field label {
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
font-size: 12px;
|
font-size: 14px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2280,15 +2284,15 @@ select.hs-input { max-width: none; width: auto; min-width: 140px; }
|
|||||||
|
|
||||||
.online-backup-retention-select select {
|
.online-backup-retention-select select {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-height: 34px;
|
min-height: 40px;
|
||||||
padding: 7px 38px 7px 11px;
|
padding: 7px 38px 7px 11px;
|
||||||
border: 1px solid var(--border);
|
border: 1px solid var(--border);
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
appearance: none;
|
appearance: none;
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
background: var(--bg-card);
|
background: var(--bg-input);
|
||||||
font: inherit;
|
font: inherit;
|
||||||
font-size: 13px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.online-backup-retention-select select:focus {
|
.online-backup-retention-select select:focus {
|
||||||
@@ -2307,6 +2311,10 @@ select.hs-input { max-width: none; width: auto; min-width: 140px; }
|
|||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.online-backup-status:empty {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
.online-backup-status[data-state="success"] {
|
.online-backup-status[data-state="success"] {
|
||||||
color: var(--success);
|
color: var(--success);
|
||||||
}
|
}
|
||||||
@@ -2338,6 +2346,8 @@ select.hs-input { max-width: none; width: auto; min-width: 140px; }
|
|||||||
|
|
||||||
.online-backup-retention-field {
|
.online-backup-retention-field {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
flex-basis: auto;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -140,7 +140,8 @@ app.whenReady().then(async () => {
|
|||||||
await wait(500);
|
await wait(500);
|
||||||
assert.equal((await state()).display, 'none');
|
assert.equal((await state()).display, 'none');
|
||||||
await js('document.querySelector(".menu-submenu-trigger").focus();document.querySelector(".menu-submenu-trigger").dispatchEvent(new KeyboardEvent("keydown",{key:"ArrowLeft",bubbles:true}))');
|
await js('document.querySelector(".menu-submenu-trigger").focus();document.querySelector(".menu-submenu-trigger").dispatchEvent(new KeyboardEvent("keydown",{key:"ArrowLeft",bubbles:true}))');
|
||||||
await wait(240);
|
const focusDeadline = Date.now() + 3000;
|
||||||
|
while (await js('document.activeElement.dataset.menuAction') !== 'backup-export' && Date.now() < focusDeadline) await wait(50);
|
||||||
assert.equal(await js('document.activeElement.dataset.menuAction'), 'backup-export');
|
assert.equal(await js('document.activeElement.dataset.menuAction'), 'backup-export');
|
||||||
await js('document.activeElement.dispatchEvent(new KeyboardEvent("keydown",{key:"Escape",bubbles:true}))');
|
await js('document.activeElement.dispatchEvent(new KeyboardEvent("keydown",{key:"Escape",bubbles:true}))');
|
||||||
await wait(240);
|
await wait(240);
|
||||||
@@ -164,6 +165,58 @@ app.whenReady().then(async () => {
|
|||||||
assert.equal(data.ok, true);
|
assert.equal(data.ok, true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('online backup validity controls match settings sizing and adapt without overflow', { skip: process.platform !== 'win32' }, t => {
|
||||||
|
const root = path.resolve(__dirname, '..');
|
||||||
|
const directory = fs.mkdtempSync(path.join(os.tmpdir(), 'mhu-retention-layout-'));
|
||||||
|
t.after(() => fs.rmSync(directory, { recursive: true, force: true }));
|
||||||
|
const probe = path.join(directory, 'probe.cjs');
|
||||||
|
const result = path.join(directory, 'result.json');
|
||||||
|
fs.writeFileSync(probe, `
|
||||||
|
const { app, BrowserWindow } = require('electron');
|
||||||
|
const fs = require('node:fs');
|
||||||
|
const path = require('node:path');
|
||||||
|
const assert = require('node:assert/strict');
|
||||||
|
app.whenReady().then(async () => {
|
||||||
|
const root=process.env.MHU_RETENTION_ROOT;
|
||||||
|
const css=fs.readFileSync(path.join(root,'renderer/styles.css'),'utf8');
|
||||||
|
const source=fs.readFileSync(path.join(root,'renderer/app.js'),'utf8');
|
||||||
|
const footer=source.match(/<footer class="online-backup-footer"[\\s\\S]*?<\\/footer>/)[0];
|
||||||
|
const win=new BrowserWindow({show:false,width:1000,height:500,webPreferences:{backgroundThrottling:false}});
|
||||||
|
const wc=win.webContents;
|
||||||
|
for(const width of [1000,600,360]) {
|
||||||
|
win.setContentSize(width,500);
|
||||||
|
await wc.loadURL('data:text/html;charset=utf-8,'+encodeURIComponent('<style>'+css+'</style><main id="settings-view" style="display:block;padding:16px;width:100%"><section class="online-backup-panel"><h3>Verschlüsseltes Online-Backup</h3><div class="online-backup-key-row"><label>Schlüssel importieren</label><input class="key-input"><button class="btn btn-secondary">Importieren</button></div><div class="online-backup-status"></div>'+footer+'</section></main>'));
|
||||||
|
const layout=await wc.executeJavaScript('(() => { const s=document.querySelector("select"), b=document.querySelector("footer button"), l=document.querySelector(".online-backup-retention-field label"), i=document.querySelector(".key-input"); const rect=e=>{const r=e.getBoundingClientRect();return {left:r.left,right:r.right,top:r.top,bottom:r.bottom,height:r.height}}; return {select:rect(s),button:rect(b),input:rect(i),labelFont:getComputedStyle(l).fontSize,selectFont:getComputedStyle(s).fontSize,buttonFont:getComputedStyle(b).fontSize,arrowRight:getComputedStyle(s.parentElement,"::after").right,empty:getComputedStyle(document.querySelector(".online-backup-status")).display,scroll:document.documentElement.scrollWidth,viewport:innerWidth}; })()');
|
||||||
|
assert.equal(layout.select.height,40);
|
||||||
|
assert.equal(layout.button.height,40);
|
||||||
|
assert.equal(layout.selectFont,layout.buttonFont);
|
||||||
|
assert.equal(layout.labelFont,'14px');
|
||||||
|
assert.equal(layout.arrowRight,'14px');
|
||||||
|
assert.equal(layout.empty,'none');
|
||||||
|
assert.ok(layout.scroll<=layout.viewport);
|
||||||
|
assert.ok(layout.select.right<=layout.viewport-16);
|
||||||
|
if(width>820) { assert.equal(layout.select.top,layout.button.top); assert.equal(layout.select.left,layout.input.left); }
|
||||||
|
else assert.ok(layout.button.top>=layout.select.bottom);
|
||||||
|
await wc.executeJavaScript('document.querySelector(".online-backup-status").textContent="Status"');
|
||||||
|
assert.notEqual(await wc.executeJavaScript('getComputedStyle(document.querySelector(".online-backup-status")).display'),'none');
|
||||||
|
if(process.env.MHU_RETENTION_SCREENSHOT && width===1000) {
|
||||||
|
await wc.executeJavaScript('document.querySelector(".online-backup-status").textContent=""');
|
||||||
|
await new Promise(resolve=>setTimeout(resolve,500));
|
||||||
|
fs.writeFileSync(process.env.MHU_RETENTION_SCREENSHOT,(await wc.capturePage()).toPNG());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
win.destroy();fs.writeFileSync(process.env.MHU_RETENTION_RESULT,JSON.stringify({ok:true}));app.exit(0);
|
||||||
|
}).catch(error=>{fs.writeFileSync(process.env.MHU_RETENTION_RESULT,JSON.stringify({error:error.stack}));app.exit(1)});
|
||||||
|
`);
|
||||||
|
const execution = spawnSync(path.join(root, 'node_modules/electron/dist/electron.exe'), [probe, '--user-data-dir=' + path.join(directory, 'profile')], {
|
||||||
|
cwd: root, windowsHide: true, encoding: 'utf8', timeout: 20000,
|
||||||
|
env: { ...process.env, MHU_RETENTION_ROOT: root, MHU_RETENTION_RESULT: result }
|
||||||
|
});
|
||||||
|
const data = fs.existsSync(result) ? JSON.parse(fs.readFileSync(result, 'utf8')) : {};
|
||||||
|
assert.equal(execution.status, 0, data.error || execution.stderr);
|
||||||
|
assert.equal(data.ok, true);
|
||||||
|
});
|
||||||
|
|
||||||
class TestBrowserWindow extends EventEmitter {
|
class TestBrowserWindow extends EventEmitter {
|
||||||
constructor(options) {
|
constructor(options) {
|
||||||
super();
|
super();
|
||||||
|
|||||||
Reference in New Issue
Block a user