Prepare v2.0.74 sidepanel reliability release

Restore and persist the package-based link collector with progressive metadata, bounded high-volume updates, safe hydration, visible selection, and complete localization.

Harden download controls, snapshot ordering, history pagination, statistics recovery, settings saves, backup imports, notification persistence, and Windows storage races with regression coverage.
This commit is contained in:
Sucukdeluxe
2026-08-26 20:35:06 +02:00
parent bdcf9e3754
commit 2ea494cd5c
69 changed files with 7660 additions and 883 deletions
+196
View File
@@ -94,6 +94,140 @@ describe("renderer localization", () => {
}
});
it.each([
["Download gestartet", "Download started"],
["Download beendet", "Download finished"],
["Nachbearbeitung gestartet", "Post-processing started"],
["Gesamtdauer", "Total duration"],
["Erfolgreich / Fehlgeschlagen / Abgebrochen", "Successful / Failed / Cancelled"],
["Archive / Parts / Ausgaben", "Archives / parts / outputs"],
["Fehlerphase", "Failure phase"],
["Fehlerkategorie", "Error category"],
["Download / Offline / Entpacken / Remux / Cleanup / Nachbearbeitung", "Download / Offline / Extraction / Remux / Cleanup / Post-processing"],
["Downloaddauer (Altbestand)", "Download duration (legacy)"],
["Archivvorgänge", "Archive operations"],
["Remuxvorgänge", "Remux operations"],
["Keine Archivvorgänge", "No archive operations"],
["Keine Remuxvorgänge", "No remux operations"]
])("translates history lifecycle text %s in both directions", (german, english) => {
expect(translateUiText(german, "en")).toBe(english);
expect(translateUiText(english, "de")).toBe(german);
});
it.each([
["2 Parts", "2 parts"],
["1.250 Parts", "1,250 parts"]
])("translates dynamic history operation text %s in both directions", (german, english) => {
expect(translateUiText(german, "en")).toBe(english);
expect(translateUiText(english, "de")).toBe(german);
});
it.each([
["Letzte 24 Stunden", "Last 24 hours"],
["Account-Traffic der vergangenen 24 Stunden.", "Account traffic over the past 24 hours."],
["Letzte 24 Stunden: Werte seit Beginn der Aufzeichnung.", "Last 24 hours: values since tracking began."],
["Heutige Werte stammen aus der lokalen Statistikaufzeichnung.", "Today's values come from local statistics tracking."],
["Datenmenge und Dateien stammen aus den Gesamtzählern; Ergebnisse und Durchschnitt seit Beginn der Statistikaufzeichnung.", "Data volume and files come from the total counters; results and averages cover the period since statistics tracking began."],
["Dateien werden nicht minutengenau nach Account erfasst", "File counts are not tracked per account at minute precision"],
["Ergebnisse werden nicht minutengenau nach Account erfasst", "Results are not tracked per account at minute precision"],
["Aktive Downloadzeit wird nur tagesweise erfasst", "Active download time is tracked by day only"],
["Fehler werden nicht minutengenau nach Account erfasst", "Errors are not tracked per account at minute precision"],
["Letzte sieben Tage", "Last seven days"],
["Letzte 30 Tage", "Last 30 days"],
["Seit Beginn der Statistikaufzeichnung", "Since statistics tracking began"],
["Noch keine aktive Downloadzeit mit übertragenen Daten erfasst", "No active download time with transferred data has been recorded yet"],
["In den vergangenen 24 Stunden wurde noch kein Account-Traffic erfasst.", "No account traffic has been recorded in the past 24 hours."],
["In diesem Zeitraum wurden noch keine Providerwerte erfasst.", "No provider values have been recorded for this period."]
])("translates statistics copy %s in both directions", (german, english) => {
expect(translateUiText(german, "en")).toBe(english);
expect(translateUiText(english, "de")).toBe(german);
});
it.each([
["Letzte sieben Tage: 1 erfasster Tag wird bis heute zusammengefasst.", "Last seven days: 1 recorded day is summarized through today."],
["Letzte 30 Tage: 12 erfasste Tage werden bis heute zusammengefasst.", "Last 30 days: 12 recorded days are summarized through today."],
["1,5 GB", "1.5 GB"],
["1.250 GB", "1,250 GB"],
["1,5 MB/s", "1.5 MB/s"],
["50,5 %", "50.5 %"],
["Daten: 1.250 GB", "Data: 1,250 GB"],
["Dateien: 1.250", "Files: 1,250"],
["Erfolg: 50,5 %", "Success: 50.5 %"],
["Fehler: 1.250", "Errors: 1,250"],
["Provider: 1.250", "Providers: 1,250"],
["Accounts: 1.250", "Accounts: 1,250"],
["5 fertig · 2 Fehler", "5 completed · 2 errors"],
["1.250 fertig · 2.500 Fehler", "1,250 completed · 2,500 errors"]
])("localizes dynamic statistics text %s in both directions", (german, english) => {
expect(translateUiText(german, "en")).toBe(english);
expect(translateUiText(english, "de")).toBe(german);
});
it.each([
["1.250 GB", "de", "1.250 GB"],
["1,5 GB", "de", "1,5 GB"],
["1,250 GB", "en", "1,250 GB"],
["1.5 GB", "en", "1.5 GB"],
["50,5 %", "de", "50,5 %"],
["50.5 %", "en", "50.5 %"]
])("preserves statistics numbers already formatted for %s", (value, language, expected) => {
expect(translateUiText(value, language as "de" | "en")).toBe(expected);
});
it.each([
["Hell", "Light"],
["Dunkel", "Dark"]
])("translates the German theme name %s in both directions", (german, english) => {
expect(translateUiText(german, "en")).toBe(english);
expect(translateUiText(english, "de")).toBe(german);
});
it.each([
["Entpackte Downloads sind ausgeblendet", "Extracted downloads are hidden"],
["Deaktiviere „Entpackte Einträge ausblenden“, um sie wieder anzuzeigen.", "Disable “Hide extracted entries” to show them again."],
["Downloads können derzeit nicht gestartet werden", "Downloads cannot be started right now"]
])("translates download state text %s in both directions", (german, english) => {
expect(translateUiText(german, "en")).toBe(english);
expect(translateUiText(english, "de")).toBe(german);
});
it.each([
["Start fehlgeschlagen: IPC_TIMEOUT", "Start failed: IPC_TIMEOUT"],
["Pause fehlgeschlagen: IPC_TIMEOUT", "Pause failed: IPC_TIMEOUT"]
])("translates download action failures %s in both directions", (german, english) => {
expect(translateUiText(german, "en")).toBe(english);
expect(translateUiText(english, "de")).toBe(german);
});
it.each([
["Ausgewählte Downloads starten (1.250)", "Start selected downloads (1,250)"],
["Ausgewählte Pakete exportieren (1.250)", "Export selected packages (1,250)"],
["Ausgewählte Dateien exportieren (1.250)", "Export selected files (1,250)"],
["Alle 1.250 umschalten", "Toggle all 1,250"],
["Ausgewählte Dateien entfernen (1.250)", "Remove selected files (1,250)"],
["Zurücksetzen (1.250)", "Reset (1,250)"],
["überspringen (1.250)", "skip (1,250)"],
["Ausgewählte entfernen (1.250)", "Remove selected (1,250)"]
])("localizes dynamic download action %s in both directions", (german, english) => {
expect(translateUiText(german, "en")).toBe(english);
expect(translateUiText(english, "de")).toBe(german);
});
it.each([
["Ausgewählte Downloads starten (1250)", "Start selected downloads (1,250)", "Ausgewählte Downloads starten (1.250)"],
["Ausgewählte Pakete exportieren (1250)", "Export selected packages (1,250)", "Ausgewählte Pakete exportieren (1.250)"],
["Ausgewählte Dateien exportieren (1250)", "Export selected files (1,250)", "Ausgewählte Dateien exportieren (1.250)"],
["Alle 1250 umschalten", "Toggle all 1,250", "Alle 1.250 umschalten"],
["Ausgewählte Dateien entfernen (1250)", "Remove selected files (1,250)", "Ausgewählte Dateien entfernen (1.250)"],
["Zurücksetzen (1250)", "Reset (1,250)", "Zurücksetzen (1.250)"],
["überspringen (1250)", "skip (1,250)", "überspringen (1.250)"],
["Ausgewählte entfernen (1250)", "Remove selected (1,250)", "Ausgewählte entfernen (1.250)"]
])("formats raw download action counts in both target locales", (germanRaw, english, germanLocalized) => {
expect(translateUiText(germanRaw, "en")).toBe(english);
expect(translateUiText(germanRaw, "de")).toBe(germanLocalized);
expect(translateUiText(english, "de")).toBe(germanLocalized);
});
it("translates dynamic queue and availability values", () => {
expect(translateUiText("26/26 online", "en")).toBe("26/26 online");
expect(translateUiText("3 abgebrochen", "en")).toBe("3 cancelled");
@@ -140,6 +274,68 @@ describe("renderer localization", () => {
expect(translateUiText("abc••••\nKlicken zum Kopieren", "en")).toBe("Click to copy abc••••");
});
it.each([
["Pakete:", "Packages:"],
["Links:", "Links:"],
["Downloads übergeben", "Transfer downloads"],
["Linksammler-Filter", "Link collector filters"],
["Suche und Paketdarstellung", "Search and package display"],
["Gesammelte Downloadpakete", "Collected download packages"],
["Analyse läuft im Hintergrund", "Analysis is running in the background"],
["Name, URL oder Hoster", "Name, URL, or host"],
["Teilweise online", "Partially online"],
["Hinzugefügt", "Added"],
["Passe Suche oder Statusfilter an.", "Adjust the search or status filter."],
["Die ersten Links erscheinen sofort nach dem Import.", "The first links appear immediately after import."],
["Links werden vorbereitet", "Links are being prepared"],
["Füge Links hinzu, um Pakete vor dem Download zu prüfen.", "Add links to check packages before downloading."],
["Links erscheinen sofort und werden anschließend im Hintergrund geprüft.", "Links appear immediately and are then checked in the background."],
["Eine URL pro Zeile", "One URL per line"],
["Keine übertragbaren Links ausgewählt", "No transferable links selected"],
["Alle sichtbaren Links auswählen", "Select all visible links"],
["Der Linksammler ist zu groß, um gespeichert zu werden.", "The link collector is too large to save."]
])("translates collector label %s in both directions", (german, english) => {
expect(translateUiText(german, "en")).toBe(english);
expect(translateUiText(english, "de")).toBe(german);
});
it.each([
["Auswahl übergeben (3)", "Send selection (3)"],
["Alle übergeben (20.000)", "Send all (20,000)"],
["Paket Staffel 1 auswählen", "Select package Staffel 1"],
["2.001 Dateien", "2,001 files"],
["20.000/20.000 online", "20,000/20,000 online"],
["Pakete: 2.001", "Packages: 2,001"],
["Links: 20.000", "Links: 20,000"],
["Ausgewählt: 1.250", "Selected: 1,250"],
["Der Linksammler kann höchstens 2.000 Pakete enthalten.", "The link collector can contain at most 2,000 packages."],
["Der Linksammler kann höchstens 20.000 Links enthalten.", "The link collector can contain at most 20,000 links."],
["Der Queue-Export überschreitet das Collector-Limit von 2.000 Paketen.", "The queue export exceeds the link collector limit of 2,000 packages."],
["Der Queue-Export überschreitet das Collector-Limit von 20.000 Links.", "The queue export exceeds the link collector limit of 20,000 links."],
["1.250", "1,250"]
])("translates dynamic collector text %s in both directions", (german, english) => {
expect(translateUiText(german, "en")).toBe(english);
expect(translateUiText(english, "de")).toBe(german);
});
it("translates nested collector persistence and import failures", () => {
expect(translateUiText("Links konnten nicht vorbereitet werden: Error: Der Queue-Export ist ungültig.", "en"))
.toBe("Links could not be prepared: Error: The queue export is invalid.");
expect(translateUiText("Linksammler konnte nicht gespeichert werden: Error: disk full", "en"))
.toBe("Link collector could not be saved: Error: disk full");
});
it.each([
["1 Paket(e), 2 Link(s) gesammelt", "1 package(s), 2 link(s) collected"],
["DLC gesammelt: 1 Paket(e), 2 Link(s)", "DLC collected: 1 package(s), 2 link(s)"],
["1 von 2 Link(s) übergeben; Sammlung bleibt erhalten", "1 of 2 link(s) transferred; collection remains"],
["1 Paket(e), 2 Link(s) übergeben", "1 package(s), 2 link(s) transferred"],
["Linksammler-Payload ist ungültig", "Link collector payload is invalid"]
])("translates collector runtime feedback %s in both directions", (german, english) => {
expect(translateUiText(german, "en")).toBe(english);
expect(translateUiText(english, "de")).toBe(german);
});
it.each([
["Verfügbarkeit", "Availability"],
["Hinzugefügt am", "Added on"],