release: prepare v2.0.82
This commit is contained in:
@@ -39,7 +39,7 @@ describe("account operation errors", () => {
|
||||
expect(formatAccountOperationError(
|
||||
"Account konnte nicht gespeichert werden",
|
||||
new Error("Error invoking remote method: proxy_only_account:proxy_list_missing")
|
||||
)).toBe("Account konnte nicht gespeichert werden: Proxy-only ist aktiviert, aber es ist keine Proxy-Liste hinterlegt. Hinterlege sie unter Einstellungen → Geschwindigkeit.");
|
||||
)).toBe("Account konnte nicht gespeichert werden: Proxy-only ist aktiviert, aber es ist keine Proxy-Liste hinterlegt. Hinterlege sie unter Einstellungen → Geschwindigkeit & Proxy.");
|
||||
expect(formatAccountOperationError(
|
||||
"Prüfung fehlgeschlagen",
|
||||
"proxy_only_account:proxy_unreachable"
|
||||
|
||||
@@ -281,8 +281,11 @@ describe("Download-ETA", () => {
|
||||
});
|
||||
|
||||
describe("verbleibendes Downloadvolumen", () => {
|
||||
it("shows a fourth TB decimal so fast downloads stay visibly responsive", () => {
|
||||
expect(formatRemainingDownloadBytes({ bytes: Math.round(1.125 * 1024 ** 4), unknownItems: 0 })).toBe("1.1250 TB");
|
||||
it("uses TB precision that changes on every normal large-queue live tick", () => {
|
||||
const before = Math.round(1.125 * 1024 ** 4);
|
||||
const after = before - 40 * 1024 ** 2;
|
||||
expect(formatRemainingDownloadBytes({ bytes: before, unknownItems: 0 })).toBe("1.12500 TB");
|
||||
expect(formatRemainingDownloadBytes({ bytes: after, unknownItems: 0 })).toBe("1.12496 TB");
|
||||
});
|
||||
|
||||
it("summiert nur offene bekannte Restbytes und klemmt überladene Fortschritte bei null", () => {
|
||||
|
||||
+6
-4
@@ -18,6 +18,8 @@ describe("renderer localization", () => {
|
||||
expect(translateUiText("Passwort/Zugang", "en")).toBe("Password/access");
|
||||
expect(translateUiText("Animationen", "en")).toBe("Animations");
|
||||
expect(translateUiText("Animations", "de")).toBe("Animationen");
|
||||
expect(translateUiText("Geschwindigkeit & Proxy", "en")).toBe("Speed & Proxy");
|
||||
expect(translateUiText("Fehlende Arbeitsordner beim Start anlegen", "en")).toBe("Create missing work folders on startup");
|
||||
});
|
||||
|
||||
it.each([
|
||||
@@ -29,11 +31,11 @@ describe("renderer localization", () => {
|
||||
});
|
||||
|
||||
it.each([
|
||||
["Proxy-only ist aktiviert, aber es ist keine Proxy-Liste hinterlegt. Hinterlege sie unter Einstellungen → Geschwindigkeit.", "Proxy-only is enabled, but no proxy list is configured. Add one under Settings → Speed."],
|
||||
["Proxy-only ist aktiviert, aber die hinterlegte Proxy-Liste kann nicht gelesen werden. Prüfe die Datei unter Einstellungen → Geschwindigkeit.", "Proxy-only is enabled, but the configured proxy list cannot be read. Check the file under Settings → Speed."],
|
||||
["Proxy-only ist aktiviert, aber es ist keine Proxy-Liste hinterlegt. Hinterlege sie unter Einstellungen → Geschwindigkeit & Proxy.", "Proxy-only is enabled, but no proxy list is configured. Add one under Settings → Speed & Proxy."],
|
||||
["Proxy-only ist aktiviert, aber die hinterlegte Proxy-Liste kann nicht gelesen werden. Prüfe die Datei unter Einstellungen → Geschwindigkeit & Proxy.", "Proxy-only is enabled, but the configured proxy list cannot be read. Check the file under Settings → Speed & Proxy."],
|
||||
["Proxy-only ist aktiviert, aber die hinterlegte Proxy-Liste ist leer oder enthält keine gültigen HTTP-Proxys.", "Proxy-only is enabled, but the configured proxy list is empty or contains no valid HTTP proxies."],
|
||||
["Proxy-only ist aktiviert, aber der feste API-Proxy ist in der Liste nicht verfügbar. Prüfe den Listeneintrag unter Einstellungen → Geschwindigkeit.", "Proxy-only is enabled, but the fixed API proxy is not available in the list. Check the list entry under Settings → Speed."],
|
||||
["Proxy-only ist aktiviert, aber der feste API-Proxy ist nicht erreichbar oder lehnt die Verbindung ab. Prüfe den Proxy unter Einstellungen → Geschwindigkeit.", "Proxy-only is enabled, but the fixed API proxy is unreachable or refuses the connection. Check the proxy under Settings → Speed."]
|
||||
["Proxy-only ist aktiviert, aber der feste API-Proxy ist in der Liste nicht verfügbar. Prüfe den Listeneintrag unter Einstellungen → Geschwindigkeit & Proxy.", "Proxy-only is enabled, but the fixed API proxy is not available in the list. Check the list entry under Settings → Speed & Proxy."],
|
||||
["Proxy-only ist aktiviert, aber der feste API-Proxy ist nicht erreichbar oder lehnt die Verbindung ab. Prüfe den Proxy unter Einstellungen → Geschwindigkeit & Proxy.", "Proxy-only is enabled, but the fixed API proxy is unreachable or refuses the connection. Check the proxy under Settings → Speed & Proxy."]
|
||||
])("translates Proxy-only account guidance %s", (german, english) => {
|
||||
expect(translateUiText(german, "en")).toBe(english);
|
||||
expect(translateUiText(english, "de")).toBe(german);
|
||||
|
||||
@@ -4,6 +4,15 @@ import { createRendererSettings } from "../src/main/renderer-state";
|
||||
import { validateRendererSettingsUpdate } from "../src/main/renderer-settings";
|
||||
|
||||
describe("renderer settings validation", () => {
|
||||
it("projects and validates startup work-directory creation", () => {
|
||||
const current = { ...defaultSettings(), createWorkDirectoriesOnStartup: true };
|
||||
const projected = createRendererSettings(current);
|
||||
|
||||
expect(projected.createWorkDirectoriesOnStartup).toBe(true);
|
||||
expect(validateRendererSettingsUpdate({ createWorkDirectoriesOnStartup: false }, current))
|
||||
.toEqual({ createWorkDirectoriesOnStartup: false });
|
||||
});
|
||||
|
||||
it("projects and validates the semantic theme preference independently from the resolved palette", () => {
|
||||
const current = { ...defaultSettings(), theme: "dark" as const, themePreference: "system" as const };
|
||||
const projected = createRendererSettings(current);
|
||||
|
||||
@@ -391,7 +391,7 @@ describe("settings model", () => {
|
||||
{ id: "allgemein", label: "Allgemein" },
|
||||
{ id: "accounts", label: "Accounts" },
|
||||
{ id: "extract", label: "Entpacken" },
|
||||
{ id: "speed", label: "Geschwindigkeit" },
|
||||
{ id: "speed", label: "Geschwindigkeit & Proxy" },
|
||||
{ id: "cleanup", label: "Bereinigung" },
|
||||
{ id: "updates", label: "Updates" }
|
||||
]);
|
||||
@@ -642,6 +642,23 @@ describe("settings views", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("offers the disabled-by-default startup work-directory switch", () => {
|
||||
const form = buildSettingsFormViewModel({
|
||||
settings: { ...createRendererSettings(defaultSettings()), archivePasswordList: "", notifyUrl: "" },
|
||||
section: "allgemein",
|
||||
speedLimitInput: "0",
|
||||
scheduleSpeedInputs: {}
|
||||
});
|
||||
const field = form.groups.flatMap((group) => group.fields)
|
||||
.find((entry) => entry.id === "createWorkDirectoriesOnStartup");
|
||||
|
||||
expect(field).toEqual(expect.objectContaining({
|
||||
kind: "switch",
|
||||
label: "Fehlende Arbeitsordner beim Start anlegen",
|
||||
value: false
|
||||
}));
|
||||
});
|
||||
|
||||
it("projects proxy segmentation controls with safe defaults and connection totals", () => {
|
||||
const form = buildSettingsFormViewModel({
|
||||
settings: {
|
||||
@@ -833,7 +850,7 @@ describe("settings views", () => {
|
||||
const html = renderToStaticMarkup(<SettingsSidebar actions={viewActions()} model={viewModel()} />);
|
||||
expect(count(html, "data-visual-region=\"settings-sidebar\"")).toBe(1);
|
||||
for (const section of SETTINGS_SECTIONS) {
|
||||
expect(html).toContain(section.label);
|
||||
expect(html).toContain(section.label.replace(/&/g, "&"));
|
||||
}
|
||||
expect(html).toContain("aria-current=\"page\"");
|
||||
});
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
import { defaultSettings } from "../src/main/constants";
|
||||
import { ensureStartupWorkDirectories } from "../src/main/startup-work-directories";
|
||||
|
||||
const tempDirs: string[] = [];
|
||||
|
||||
function tempRoot(): string {
|
||||
const root = fs.mkdtempSync(path.join(os.tmpdir(), "mdd-startup-dirs-"));
|
||||
tempDirs.push(root);
|
||||
return root;
|
||||
}
|
||||
|
||||
afterEach(() => {
|
||||
for (const directory of tempDirs.splice(0)) {
|
||||
fs.rmSync(directory, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
describe("startup work directories", () => {
|
||||
it("does nothing while the setting is disabled", () => {
|
||||
const root = tempRoot();
|
||||
const outputDir = path.join(root, "downloads");
|
||||
const result = ensureStartupWorkDirectories({
|
||||
...defaultSettings(),
|
||||
outputDir,
|
||||
createWorkDirectoriesOnStartup: false
|
||||
});
|
||||
|
||||
expect(result).toEqual({ created: [], existing: [], failures: [] });
|
||||
expect(fs.existsSync(outputDir)).toBe(false);
|
||||
});
|
||||
|
||||
it("creates only the enabled work directories and preserves existing contents", () => {
|
||||
const root = tempRoot();
|
||||
const outputDir = path.join(root, "downloads");
|
||||
const extractDir = path.join(root, "extracted");
|
||||
const mkvLibraryDir = path.join(root, "videos");
|
||||
fs.mkdirSync(outputDir, { recursive: true });
|
||||
fs.writeFileSync(path.join(outputDir, "keep.bin"), "keep");
|
||||
|
||||
const result = ensureStartupWorkDirectories({
|
||||
...defaultSettings(),
|
||||
outputDir,
|
||||
extractDir,
|
||||
mkvLibraryDir,
|
||||
autoExtract: true,
|
||||
collectMkvToLibrary: true,
|
||||
createWorkDirectoriesOnStartup: true
|
||||
});
|
||||
|
||||
expect(result.created.map((entry) => entry.kind)).toEqual(["extract", "video-library"]);
|
||||
expect(result.existing.map((entry) => entry.kind)).toEqual(["download"]);
|
||||
expect(result.failures).toEqual([]);
|
||||
expect(fs.readFileSync(path.join(outputDir, "keep.bin"), "utf8")).toBe("keep");
|
||||
expect(fs.statSync(extractDir).isDirectory()).toBe(true);
|
||||
expect(fs.statSync(mkvLibraryDir).isDirectory()).toBe(true);
|
||||
});
|
||||
|
||||
it("skips inactive optional directories", () => {
|
||||
const root = tempRoot();
|
||||
const outputDir = path.join(root, "downloads");
|
||||
const extractDir = path.join(root, "extracted");
|
||||
const mkvLibraryDir = path.join(root, "videos");
|
||||
|
||||
const result = ensureStartupWorkDirectories({
|
||||
...defaultSettings(),
|
||||
outputDir,
|
||||
extractDir,
|
||||
mkvLibraryDir,
|
||||
autoExtract: false,
|
||||
collectMkvToLibrary: false,
|
||||
createWorkDirectoriesOnStartup: true
|
||||
});
|
||||
|
||||
expect(result.created.map((entry) => entry.kind)).toEqual(["download"]);
|
||||
expect(fs.existsSync(extractDir)).toBe(false);
|
||||
expect(fs.existsSync(mkvLibraryDir)).toBe(false);
|
||||
});
|
||||
|
||||
it("reports one invalid target without blocking the other directories", () => {
|
||||
const root = tempRoot();
|
||||
const blockedOutput = path.join(root, "blocked");
|
||||
const extractDir = path.join(root, "extracted");
|
||||
fs.writeFileSync(blockedOutput, "file");
|
||||
|
||||
const result = ensureStartupWorkDirectories({
|
||||
...defaultSettings(),
|
||||
outputDir: blockedOutput,
|
||||
extractDir,
|
||||
autoExtract: true,
|
||||
createWorkDirectoriesOnStartup: true
|
||||
});
|
||||
|
||||
expect(result.failures.map((entry) => entry.kind)).toEqual(["download"]);
|
||||
expect(result.created.map((entry) => entry.kind)).toEqual(["extract"]);
|
||||
expect(fs.statSync(extractDir).isDirectory()).toBe(true);
|
||||
});
|
||||
});
|
||||
+14
-2
@@ -301,11 +301,23 @@ describe("settings storage", () => {
|
||||
});
|
||||
|
||||
it("uses English for new installations and preserves only supported languages", () => {
|
||||
expect(defaultSettings().language).toBe("en");
|
||||
expect(normalizeSettings({ ...defaultSettings(), language: "de" }).language).toBe("de");
|
||||
expect(defaultSettings().language).toBe("en");
|
||||
expect(normalizeSettings({ ...defaultSettings(), language: "de" }).language).toBe("de");
|
||||
expect(normalizeSettings({ ...defaultSettings(), language: "fr" as "en" }).language).toBe("en");
|
||||
});
|
||||
|
||||
it("keeps startup work-directory creation opt-in and persists an explicit enable", () => {
|
||||
const legacy = { ...defaultSettings() } as Partial<AppSettings>;
|
||||
delete legacy.createWorkDirectoriesOnStartup;
|
||||
|
||||
expect(defaultSettings().createWorkDirectoriesOnStartup).toBe(false);
|
||||
expect(normalizeSettings(legacy as AppSettings).createWorkDirectoriesOnStartup).toBe(false);
|
||||
expect(normalizeSettings({
|
||||
...defaultSettings(),
|
||||
createWorkDirectoriesOnStartup: true
|
||||
}).createWorkDirectoriesOnStartup).toBe(true);
|
||||
});
|
||||
|
||||
it("migrates a legacy shared Mega-Debrid pool into only the preferred mode", () => {
|
||||
const legacy = { ...defaultSettings() } as Partial<AppSettings>;
|
||||
legacy.megaCredentials = "legacy@example.test:legacy-pass";
|
||||
|
||||
@@ -90,9 +90,10 @@ function createSettings(): AppSettings {
|
||||
providerPrimary: "realdebrid",
|
||||
providerSecondary: "megadebrid-api",
|
||||
providerTertiary: "bestdebrid",
|
||||
autoProviderFallback: true,
|
||||
outputDir: "C:\\Visual\\Downloads",
|
||||
packageName: "",
|
||||
autoProviderFallback: true,
|
||||
outputDir: "C:\\Visual\\Downloads",
|
||||
createWorkDirectoriesOnStartup: false,
|
||||
packageName: "",
|
||||
autoExtract: true,
|
||||
autoRename4sf4sj: true,
|
||||
keepGermanAudioOnly: false,
|
||||
|
||||
Reference in New Issue
Block a user