Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions packages/studio/src/components/renders/useRenderQueue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -250,12 +250,19 @@ export function useRenderQueue(projectId: string | null) {
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body),
});
} catch {
} catch (err) {
// The cause used to be discarded. Every failure — a dead server, an
// aborted request, a DNS error, a mid-render crash — surfaced as the
// same sentence, and this string is what travels into the feedback
// report too, so field reports of a render that fails *every time*
// still carried nothing to act on. Keep the CLI guidance, name the
// cause after it.
const cause = err instanceof Error ? err.message : String(err);
const failedJob: RenderJob = {
id: generateId(),
status: "failed",
progress: 0,
error: "Could not reach render server. Use `hyperframes render` from the CLI instead.",
error: `Could not reach render server: ${cause}. Use \`hyperframes render\` from the CLI instead.`,
filename: "Export failed",
createdAt: startTime,
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// @vitest-environment happy-dom

// The render POST's catch used to discard its error, so a dead server, a DNS
// failure and a mid-render crash all produced one sentence. That string is also
// what travels into the feedback report, so three field reports — one of them a
// render that failed EVERY time — arrived with nothing to act on.

import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { mountRenderQueue } from "./renderQueueTestHarness";

vi.mock("./useFfmpegStatus", async (importOriginal) => ({
...(await importOriginal<typeof import("./useFfmpegStatus")>()),
useFfmpegStatus: () => ({ status: { ok: true }, checking: false, recheck: vi.fn() }),
}));
vi.mock("../../telemetry/events", () => ({ trackStudioRenderStart: vi.fn() }));

const { useRenderQueue } = await import("./useRenderQueue");

let queue: ReturnType<typeof mountRenderQueue> | null = null;

beforeEach(() => {
vi.stubGlobal(
"fetch",
vi.fn(() => Promise.reject(new TypeError("Failed to fetch"))),
);
});

afterEach(() => {
queue?.unmount();
queue = null;
document.body.innerHTML = "";
vi.unstubAllGlobals();
});

describe("useRenderQueue transport failure", () => {
it("names the cause instead of collapsing every failure into one sentence", async () => {
queue = mountRenderQueue(useRenderQueue);
const { act } = await import("react");
await act(async () => {
await queue?.api().startRender({});
});

const job = queue.api().jobs.at(-1);
expect(job?.status).toBe("failed");
expect(job?.error).toContain("Failed to fetch");
// The CLI guidance still earns its place; it just no longer stands alone.
expect(job?.error).toContain("hyperframes render");
});
});
Loading