From ab7a504aa5a591fb50cb7c06f8da99250a32b8c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Narbona?= Date: Wed, 11 Mar 2026 19:06:48 +0100 Subject: [PATCH 1/7] fix: download individual yml files sequentially to avoid Chromium download limit When downloading individual .yml backup files, all downloads were triggered simultaneously via forEach + link.click(). Chromium blocks rapid programmatic downloads after ~10, causing only model1* files to be saved. Changed to sequential downloads with a 200ms delay between each file. Added test verifying all 43 models are returned correctly. --- .../pages/backup/BackupCreateFlow.tsx | 43 ++++++---- src/shared/backend/__tests__/backup.spec.ts | 86 +++++++++++++++++++ 2 files changed, 114 insertions(+), 15 deletions(-) diff --git a/src/renderer/pages/backup/BackupCreateFlow.tsx b/src/renderer/pages/backup/BackupCreateFlow.tsx index 8495059..b8600c6 100644 --- a/src/renderer/pages/backup/BackupCreateFlow.tsx +++ b/src/renderer/pages/backup/BackupCreateFlow.tsx @@ -201,7 +201,7 @@ const BackupCreateFlow: React.FC = () => { includeLabels, }, }) - .then((result) => { + .then(async (result) => { clearInterval(interval); setProgress(100); @@ -211,20 +211,33 @@ const BackupCreateFlow: React.FC = () => { base64Data: string; }[]; - // Download each file - files.forEach((file: { fileName: string; base64Data: string }) => { - const blob = new Blob([Buffer.from(file.base64Data, "base64")], { - type: "application/octet-stream", - }); - const url = URL.createObjectURL(blob); - const link = document.createElement("a"); - link.href = url; - link.download = file.fileName; - document.body.appendChild(link); - link.click(); - document.body.removeChild(link); - URL.revokeObjectURL(url); - }); + // Download each file sequentially with a delay to avoid + // Chromium's rapid download blocking (limits ~10 concurrent + // programmatic downloads) + await files.reduce( + (chain, file) => + chain.then( + () => + new Promise((resolve) => { + const blob = new Blob( + [Buffer.from(file.base64Data, "base64")], + { + type: "application/octet-stream", + } + ); + const url = URL.createObjectURL(blob); + const link = document.createElement("a"); + link.href = url; + link.download = file.fileName; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + URL.revokeObjectURL(url); + setTimeout(resolve, 200); + }) + ), + Promise.resolve() + ); void message.success( t(`{{count}} files downloaded`, { count: files.length }) diff --git a/src/shared/backend/__tests__/backup.spec.ts b/src/shared/backend/__tests__/backup.spec.ts index 269fa11..a9f7d1a 100644 --- a/src/shared/backend/__tests__/backup.spec.ts +++ b/src/shared/backend/__tests__/backup.spec.ts @@ -1395,6 +1395,92 @@ describe("Backup", () => { expect(content).toContain("My Quad"); }); + it("should download all models when many are selected (e.g. 43)", async () => { + const modelsPath = await setupSdcardDirectory(tempDir.path); + + // Create 43 model files to simulate a real-world scenario + const modelNames: string[] = []; + for (let i = 1; i <= 43; i += 1) { + const modelName = `model${i}`; + modelNames.push(modelName); + await fs.writeFile( + path.join(modelsPath, `${modelName}.yml`), + createModelYaml(`Model ${i}`) + ); + } + + // Pick directory + const handle = await getOriginPrivateDirectory( + nodeAdapter, + tempDir.path + ); + // @ts-expect-error readonly but testing + handle.name = tempDir.path; + requestWritableDirectory.mockResolvedValueOnce(handle); + + const pickResult = await backend.mutate({ + mutation: gql` + mutation { + pickSdcardDirectory { + id + } + } + `, + }); + + const directoryId = (pickResult.data?.pickSdcardDirectory as any)?.id; + + // Download all 43 models + const { data, errors } = await backend.mutate({ + mutation: gql` + mutation DownloadModels( + $directoryId: ID! + $selectedModels: [String!]! + ) { + downloadIndividualModels( + directoryId: $directoryId + selectedModels: $selectedModels + ) { + fileName + base64Data + } + } + `, + variables: { + directoryId, + selectedModels: modelNames, + }, + }); + + expect(errors).toBeFalsy(); + expect(data?.downloadIndividualModels).toHaveLength(43); + + // Verify all 43 files are present + const downloadedFiles = ( + data?.downloadIndividualModels as { + fileName: string; + base64Data: string; + }[] + ).map((m) => m.fileName); + + for (let i = 1; i <= 43; i += 1) { + expect(downloadedFiles).toContain(`model${i}.yml`); + } + + // Verify content of a few samples + const model25 = ( + data?.downloadIndividualModels as { + fileName: string; + base64Data: string; + }[] + ).find((m) => m.fileName === "model25.yml"); + const content = Buffer.from( + model25?.base64Data ?? "", + "base64" + ).toString("utf-8"); + expect(content).toContain("Model 25"); + }); + it("should include labels file when requested", async () => { const modelsPath = await setupSdcardDirectory(tempDir.path); From b8ee07233094f158d2a202f94fd29da81ec2ee70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Narbona?= Date: Wed, 11 Mar 2026 22:03:16 +0100 Subject: [PATCH 2/7] test: add component test verifying all individual yml files download sequentially --- .../__tests__/BackupCreateFlow.spec.tsx | 198 +++++++++++++++++- 1 file changed, 196 insertions(+), 2 deletions(-) diff --git a/src/renderer/pages/backup/__tests__/BackupCreateFlow.spec.tsx b/src/renderer/pages/backup/__tests__/BackupCreateFlow.spec.tsx index d857348..8b28356 100644 --- a/src/renderer/pages/backup/__tests__/BackupCreateFlow.spec.tsx +++ b/src/renderer/pages/backup/__tests__/BackupCreateFlow.spec.tsx @@ -1,9 +1,19 @@ import React from "react"; import { render } from "test-utils/testing-library"; -import { screen, waitFor } from "@testing-library/react"; +import { screen, waitFor, fireEvent } from "@testing-library/react"; import { MockedProvider } from "@apollo/client/testing"; import BackupCreateFlow from "renderer/pages/backup/BackupCreateFlow"; -import { describe, it, expect, vi, beforeAll, afterAll } from "vitest"; +import { + describe, + it, + expect, + vi, + beforeAll, + afterAll, + beforeEach, + afterEach, +} from "vitest"; +import gql from "graphql-tag"; // Mock environment vi.mock("shared/environment", () => ({ @@ -199,3 +209,187 @@ describe("", () => { }); }); }); + +describe(" sequential individual download", () => { + let downloadedFiles: string[]; + + beforeEach(() => { + downloadedFiles = []; + + // Track which files are downloaded via link.click + const originalCreateElement = document.createElement.bind(document); + vi.spyOn(document, "createElement").mockImplementation((( + tagName: string + ) => { + const element = originalCreateElement(tagName); + if (tagName === "a") { + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any + (element as any).click = vi.fn(() => { + downloadedFiles.push((element as HTMLAnchorElement).download); + }); + } + return element; + }) as typeof document.createElement); + + global.URL.createObjectURL = vi.fn(() => "mock-url"); + global.URL.revokeObjectURL = vi.fn(); + }); + + afterEach(() => { + vi.restoreAllMocks(); + }); + + it("should download all selected individual model files, not just the first 10", async () => { + const directoryId = "test-dir-id"; + const modelCount = 11; + const modelNames = Array.from( + { length: modelCount }, + (_, i) => `model${i + 1}` + ); + const base64 = Buffer.from("content").toString("base64"); + + const mocks = [ + { + request: { + query: gql` + mutation PickSdcardDirectory { + pickSdcardDirectory { + id + } + } + `, + }, + result: { + data: { + pickSdcardDirectory: { id: directoryId }, + }, + }, + }, + { + request: { + query: gql` + query SdcardDirectoryInfo($directoryId: ID!) { + sdcardModelsDirectory(id: $directoryId) { + id + name + isValid + hasLabels + pack { + target + version + } + } + } + `, + variables: { directoryId }, + }, + result: { + data: { + sdcardModelsDirectory: { + id: directoryId, + name: "MODELS", + isValid: true, + hasLabels: false, + pack: null, + }, + }, + }, + }, + { + request: { + query: gql` + query SdcardModelsWithNames($directoryId: ID!) { + sdcardModelsWithNames(directoryId: $directoryId) { + fileName + displayName + } + } + `, + variables: { directoryId }, + }, + result: { + data: { + sdcardModelsWithNames: modelNames.map((name) => ({ + fileName: name, + displayName: `Model ${name}`, + })), + }, + }, + }, + { + request: { + query: gql` + mutation DownloadIndividualModels( + $directoryId: ID! + $selectedModels: [String!]! + $includeLabels: Boolean + ) { + downloadIndividualModels( + directoryId: $directoryId + selectedModels: $selectedModels + includeLabels: $includeLabels + ) { + fileName + base64Data + } + } + `, + variables: { + directoryId, + selectedModels: modelNames, + includeLabels: false, + }, + }, + result: { + data: { + downloadIndividualModels: modelNames.map((name) => ({ + fileName: `${name}.yml`, + base64Data: base64, + })), + }, + }, + }, + ]; + + render( + + + + ); + + // Trigger SD card selection + fireEvent.click(screen.getByText("Select SD Card")); + + // Wait for Apollo mutation response and models to load + await waitFor( + () => { + expect(screen.getByText("Select all")).toBeInTheDocument(); + }, + { timeout: 3000 } + ); + + // Select all models + fireEvent.click(screen.getByText("Select all")); + + // Switch to individual .yml format + fireEvent.click(screen.getByText("Individual .yml files")); + + // Trigger backup download + fireEvent.click(screen.getByRole("button", { name: /create backup/i })); + + // All 11 files should be downloaded (proves the 10-file Chromium limit is + // bypassed by downloading sequentially with a delay between each file). + // Allow enough time for 11 * 200ms sequential downloads. + await waitFor( + () => { + expect(downloadedFiles).toHaveLength(modelCount); + }, + { timeout: 4000 } + ); + + // Verify each model file was included + modelNames.forEach((name) => { + expect(downloadedFiles).toContain(`${name}.yml`); + }); + }, 10000); // extend test timeout to accommodate 11 * 200ms sequential downloads +}); From c675f07f5622991b744162f1ce260ef736820ae6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Narbona?= Date: Thu, 12 Mar 2026 20:28:07 +0100 Subject: [PATCH 3/7] fix: use legacyDownload + p-limit + delay for individual yml downloads As suggested in code review: - Replace manual Blob/link DOM manipulation with legacyDownload (js-file-download) - Use p-limit(5) for concurrency limiting instead of fully sequential reduce chain - Use delay() utility from shared/tools instead of raw setTimeout - Simplify test by mocking legacyDownload directly --- .../pages/backup/BackupCreateFlow.tsx | 43 ++++++-------- .../__tests__/BackupCreateFlow.spec.tsx | 59 +++---------------- 2 files changed, 25 insertions(+), 77 deletions(-) diff --git a/src/renderer/pages/backup/BackupCreateFlow.tsx b/src/renderer/pages/backup/BackupCreateFlow.tsx index b8600c6..d3ff6ad 100644 --- a/src/renderer/pages/backup/BackupCreateFlow.tsx +++ b/src/renderer/pages/backup/BackupCreateFlow.tsx @@ -21,6 +21,9 @@ import { useMutation, gql, useQuery } from "@apollo/client"; import environment from "shared/environment"; import checks from "renderer/compatibility/checks"; import { useTranslation } from "react-i18next"; +import legacyDownload from "js-file-download"; +import pLimit from "p-limit"; +import { delay } from "shared/tools"; const notAvailable = !environment.isElectron && !checks.hasFilesystemApi; @@ -211,32 +214,20 @@ const BackupCreateFlow: React.FC = () => { base64Data: string; }[]; - // Download each file sequentially with a delay to avoid - // Chromium's rapid download blocking (limits ~10 concurrent - // programmatic downloads) - await files.reduce( - (chain, file) => - chain.then( - () => - new Promise((resolve) => { - const blob = new Blob( - [Buffer.from(file.base64Data, "base64")], - { - type: "application/octet-stream", - } - ); - const url = URL.createObjectURL(blob); - const link = document.createElement("a"); - link.href = url; - link.download = file.fileName; - document.body.appendChild(link); - link.click(); - document.body.removeChild(link); - URL.revokeObjectURL(url); - setTimeout(resolve, 200); - }) - ), - Promise.resolve() + // Limit concurrent downloads to avoid Chromium's rapid + // simultaneous download blocking (~10 at a time) + const limit = pLimit(5); + await Promise.all( + files.map((file, i) => + limit(async () => { + await delay(i * 200); + legacyDownload( + Buffer.from(file.base64Data, "base64"), + file.fileName, + "application/octet-stream" + ); + }) + ) ); void message.success( diff --git a/src/renderer/pages/backup/__tests__/BackupCreateFlow.spec.tsx b/src/renderer/pages/backup/__tests__/BackupCreateFlow.spec.tsx index 8b28356..4667c8b 100644 --- a/src/renderer/pages/backup/__tests__/BackupCreateFlow.spec.tsx +++ b/src/renderer/pages/backup/__tests__/BackupCreateFlow.spec.tsx @@ -3,17 +3,11 @@ import { render } from "test-utils/testing-library"; import { screen, waitFor, fireEvent } from "@testing-library/react"; import { MockedProvider } from "@apollo/client/testing"; import BackupCreateFlow from "renderer/pages/backup/BackupCreateFlow"; -import { - describe, - it, - expect, - vi, - beforeAll, - afterAll, - beforeEach, - afterEach, -} from "vitest"; +import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; import gql from "graphql-tag"; +import legacyDownload from "js-file-download"; + +vi.mock("js-file-download"); // Mock environment vi.mock("shared/environment", () => ({ @@ -30,29 +24,6 @@ vi.mock("renderer/compatibility/checks", () => ({ })); describe("", () => { - beforeAll(() => { - // Mock document.createElement for download links - const originalCreateElement = document.createElement.bind(document); - vi.spyOn(document, "createElement").mockImplementation((( - tagName: string - ) => { - const element = originalCreateElement(tagName); - if (tagName === "a") { - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any - (element as any).click = vi.fn(); - } - return element; - }) as typeof document.createElement); - - // Mock URL.createObjectURL and revokeObjectURL - global.URL.createObjectURL = vi.fn(() => "mock-url"); - global.URL.revokeObjectURL = vi.fn(); - }); - - afterAll(() => { - vi.restoreAllMocks(); - }); - it("should render the component", () => { render( @@ -212,27 +183,13 @@ describe("", () => { describe(" sequential individual download", () => { let downloadedFiles: string[]; + const mockLegacyDownload = vi.mocked(legacyDownload); beforeEach(() => { downloadedFiles = []; - - // Track which files are downloaded via link.click - const originalCreateElement = document.createElement.bind(document); - vi.spyOn(document, "createElement").mockImplementation((( - tagName: string - ) => { - const element = originalCreateElement(tagName); - if (tagName === "a") { - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any - (element as any).click = vi.fn(() => { - downloadedFiles.push((element as HTMLAnchorElement).download); - }); - } - return element; - }) as typeof document.createElement); - - global.URL.createObjectURL = vi.fn(() => "mock-url"); - global.URL.revokeObjectURL = vi.fn(); + mockLegacyDownload.mockImplementation((data: unknown, filename: string) => { + downloadedFiles.push(filename); + }); }); afterEach(() => { From 7592f4f9decdfd5dca6476cda6a7fee98463bc37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Narbona?= Date: Sat, 14 Mar 2026 11:04:00 +0100 Subject: [PATCH 4/7] fix: adjust download limit and delay for individual yml files to optimize Chromium handling --- src/renderer/pages/backup/BackupCreateFlow.tsx | 10 ++++++---- .../pages/backup/__tests__/BackupCreateFlow.spec.tsx | 6 +++--- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/renderer/pages/backup/BackupCreateFlow.tsx b/src/renderer/pages/backup/BackupCreateFlow.tsx index d3ff6ad..0e0642c 100644 --- a/src/renderer/pages/backup/BackupCreateFlow.tsx +++ b/src/renderer/pages/backup/BackupCreateFlow.tsx @@ -215,17 +215,19 @@ const BackupCreateFlow: React.FC = () => { }[]; // Limit concurrent downloads to avoid Chromium's rapid - // simultaneous download blocking (~10 at a time) - const limit = pLimit(5); + // simultaneous download blocking (~10 at a time). Each slot + // holds for 200ms after triggering so Chromium registers it + // before the next batch starts. + const limit = pLimit(10); await Promise.all( - files.map((file, i) => + files.map((file) => limit(async () => { - await delay(i * 200); legacyDownload( Buffer.from(file.base64Data, "base64"), file.fileName, "application/octet-stream" ); + await delay(200); }) ) ); diff --git a/src/renderer/pages/backup/__tests__/BackupCreateFlow.spec.tsx b/src/renderer/pages/backup/__tests__/BackupCreateFlow.spec.tsx index 4667c8b..a850ea9 100644 --- a/src/renderer/pages/backup/__tests__/BackupCreateFlow.spec.tsx +++ b/src/renderer/pages/backup/__tests__/BackupCreateFlow.spec.tsx @@ -335,8 +335,8 @@ describe(" sequential individual download", () => { fireEvent.click(screen.getByRole("button", { name: /create backup/i })); // All 11 files should be downloaded (proves the 10-file Chromium limit is - // bypassed by downloading sequentially with a delay between each file). - // Allow enough time for 11 * 200ms sequential downloads. + // bypassed by downloading in batches of 10 with a 200ms delay per slot). + // With pLimit(10), 11 files need 2 batches (~400ms total). await waitFor( () => { expect(downloadedFiles).toHaveLength(modelCount); @@ -348,5 +348,5 @@ describe(" sequential individual download", () => { modelNames.forEach((name) => { expect(downloadedFiles).toContain(`${name}.yml`); }); - }, 10000); // extend test timeout to accommodate 11 * 200ms sequential downloads + }, 10000); // extend test timeout to accommodate batched downloads with 200ms delay per slot }); From cfbd6368b60eaaf541c99278b8c73364a8cef331 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Narbona?= Date: Mon, 16 Mar 2026 12:34:38 +0100 Subject: [PATCH 5/7] fix: implement sequential downloads for individual yml files to bypass Chromium's simultaneous download limit --- .../pages/backup/BackupCreateFlow.tsx | 31 ++++++++----------- .../__tests__/BackupCreateFlow.spec.tsx | 10 +++--- 2 files changed, 18 insertions(+), 23 deletions(-) diff --git a/src/renderer/pages/backup/BackupCreateFlow.tsx b/src/renderer/pages/backup/BackupCreateFlow.tsx index 0e0642c..b80081f 100644 --- a/src/renderer/pages/backup/BackupCreateFlow.tsx +++ b/src/renderer/pages/backup/BackupCreateFlow.tsx @@ -22,7 +22,6 @@ import environment from "shared/environment"; import checks from "renderer/compatibility/checks"; import { useTranslation } from "react-i18next"; import legacyDownload from "js-file-download"; -import pLimit from "p-limit"; import { delay } from "shared/tools"; const notAvailable = !environment.isElectron && !checks.hasFilesystemApi; @@ -214,23 +213,19 @@ const BackupCreateFlow: React.FC = () => { base64Data: string; }[]; - // Limit concurrent downloads to avoid Chromium's rapid - // simultaneous download blocking (~10 at a time). Each slot - // holds for 200ms after triggering so Chromium registers it - // before the next batch starts. - const limit = pLimit(10); - await Promise.all( - files.map((file) => - limit(async () => { - legacyDownload( - Buffer.from(file.base64Data, "base64"), - file.fileName, - "application/octet-stream" - ); - await delay(200); - }) - ) - ); + // Trigger downloads sequentially with a 200ms gap between each. + // Chromium blocks programmatic downloads when more than ~10 are + // fired simultaneously; a sequential approach avoids that entirely. + // eslint-disable-next-line no-restricted-syntax + for (const file of files) { + legacyDownload( + Buffer.from(file.base64Data, "base64"), + file.fileName, + "application/octet-stream" + ); + // eslint-disable-next-line no-await-in-loop + await delay(200); + } void message.success( t(`{{count}} files downloaded`, { count: files.length }) diff --git a/src/renderer/pages/backup/__tests__/BackupCreateFlow.spec.tsx b/src/renderer/pages/backup/__tests__/BackupCreateFlow.spec.tsx index a850ea9..e3dc3c7 100644 --- a/src/renderer/pages/backup/__tests__/BackupCreateFlow.spec.tsx +++ b/src/renderer/pages/backup/__tests__/BackupCreateFlow.spec.tsx @@ -334,19 +334,19 @@ describe(" sequential individual download", () => { // Trigger backup download fireEvent.click(screen.getByRole("button", { name: /create backup/i })); - // All 11 files should be downloaded (proves the 10-file Chromium limit is - // bypassed by downloading in batches of 10 with a 200ms delay per slot). - // With pLimit(10), 11 files need 2 batches (~400ms total). + // All 11 files should be downloaded. Chromium blocks simultaneous + // programmatic downloads above ~10; the sequential approach (one every + // 200ms) avoids that entirely. 11 files → ~2200ms total. await waitFor( () => { expect(downloadedFiles).toHaveLength(modelCount); }, - { timeout: 4000 } + { timeout: 5000 } ); // Verify each model file was included modelNames.forEach((name) => { expect(downloadedFiles).toContain(`${name}.yml`); }); - }, 10000); // extend test timeout to accommodate batched downloads with 200ms delay per slot + }, 15000); // 11 files × 200ms = ~2200ms, plus test overhead }); From e2c5df28dde0e02167f682cb9c9ddbffe35f2b24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Narbona?= Date: Mon, 16 Mar 2026 20:44:14 +0100 Subject: [PATCH 6/7] fix: ensure sequential downloads complete instantly in tests by mocking delay --- .../__tests__/BackupCreateFlow.spec.tsx | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/renderer/pages/backup/__tests__/BackupCreateFlow.spec.tsx b/src/renderer/pages/backup/__tests__/BackupCreateFlow.spec.tsx index e3dc3c7..546be87 100644 --- a/src/renderer/pages/backup/__tests__/BackupCreateFlow.spec.tsx +++ b/src/renderer/pages/backup/__tests__/BackupCreateFlow.spec.tsx @@ -9,6 +9,12 @@ import legacyDownload from "js-file-download"; vi.mock("js-file-download"); +// Make delay a no-op so sequential downloads complete instantly in tests +vi.mock("shared/tools", async (importOriginal) => ({ + ...(await importOriginal()), + delay: () => Promise.resolve(), +})); + // Mock environment vi.mock("shared/environment", () => ({ default: { @@ -335,18 +341,15 @@ describe(" sequential individual download", () => { fireEvent.click(screen.getByRole("button", { name: /create backup/i })); // All 11 files should be downloaded. Chromium blocks simultaneous - // programmatic downloads above ~10; the sequential approach (one every - // 200ms) avoids that entirely. 11 files → ~2200ms total. - await waitFor( - () => { - expect(downloadedFiles).toHaveLength(modelCount); - }, - { timeout: 5000 } - ); + // programmatic downloads above ~10; the sequential approach (one per + // 200ms in production, instant in tests) avoids that entirely. + await waitFor(() => { + expect(downloadedFiles).toHaveLength(modelCount); + }); // Verify each model file was included modelNames.forEach((name) => { expect(downloadedFiles).toContain(`${name}.yml`); }); - }, 15000); // 11 files × 200ms = ~2200ms, plus test overhead + }); }); From 594eaa2d986ac50b439a2159c0d1eb230210559b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Narbona?= Date: Wed, 18 Mar 2026 10:44:29 +0100 Subject: [PATCH 7/7] fix: adjust delay for sequential downloads based on environment to improve test performance --- src/renderer/pages/backup/BackupCreateFlow.tsx | 2 +- .../pages/backup/__tests__/BackupCreateFlow.spec.tsx | 6 ------ 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/src/renderer/pages/backup/BackupCreateFlow.tsx b/src/renderer/pages/backup/BackupCreateFlow.tsx index b80081f..abad1c5 100644 --- a/src/renderer/pages/backup/BackupCreateFlow.tsx +++ b/src/renderer/pages/backup/BackupCreateFlow.tsx @@ -224,7 +224,7 @@ const BackupCreateFlow: React.FC = () => { "application/octet-stream" ); // eslint-disable-next-line no-await-in-loop - await delay(200); + await delay(process.env.NODE_ENV === "test" ? 1 : 200); } void message.success( diff --git a/src/renderer/pages/backup/__tests__/BackupCreateFlow.spec.tsx b/src/renderer/pages/backup/__tests__/BackupCreateFlow.spec.tsx index 546be87..e1557ae 100644 --- a/src/renderer/pages/backup/__tests__/BackupCreateFlow.spec.tsx +++ b/src/renderer/pages/backup/__tests__/BackupCreateFlow.spec.tsx @@ -9,12 +9,6 @@ import legacyDownload from "js-file-download"; vi.mock("js-file-download"); -// Make delay a no-op so sequential downloads complete instantly in tests -vi.mock("shared/tools", async (importOriginal) => ({ - ...(await importOriginal()), - delay: () => Promise.resolve(), -})); - // Mock environment vi.mock("shared/environment", () => ({ default: {