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
8 changes: 8 additions & 0 deletions src/components/ai-edition/v4/SpeedControl.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,14 @@ describe("SpeedControl", () => {
expect(updateSpeedValue).toHaveBeenCalledWith("sp1", 12);
});

it("commits a speed typed with a multiplier suffix, like the zoom field", () => {
const { updateSpeedValue, field } = renderControl(2);
fireEvent.change(field, { target: { value: "1,25×" } });
fireEvent.blur(field);
expect(updateSpeedValue).toHaveBeenCalledWith("sp1", 1.25);
expect(field).toHaveValue("");
});

it("commits on Enter as well as on blur, and only once", () => {
const { updateSpeedValue, field } = renderControl(1);
fireEvent.change(field, { target: { value: "7.5" } });
Expand Down
30 changes: 30 additions & 0 deletions src/components/video-editor/customPlaybackSpeed.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,36 @@ describe("parseCustomPlaybackSpeedInput", () => {
});
});

it.each([
"1.25x",
"1.25×",
"1.25X",
"1,25×",
"1.25 x",
" 1.25x ",
])("accepts a trailing multiplier suffix: %j", (draft) => {
// The field shows "2×"; zoom accepts the same suffix.
expect(parseCustomPlaybackSpeedInput(draft)).toEqual({
status: "valid",
draft,
speed: 1.25,
});
});

it.each(["1.25xx", "x1.25", "1x2"])("rejects a misplaced or repeated suffix: %j", (draft) => {
expect(parseCustomPlaybackSpeedInput(draft)).toEqual({ status: "empty", draft });
});

it.each(["", " ", "x", "×"])("treats a blank or bare-suffix draft as empty: %j", (draft) => {
// Stripping the suffix leaves "", and Number("") is 0, which would read as too-slow.
expect(parseCustomPlaybackSpeedInput(draft)).toEqual({ status: "empty", draft });
});

it("still applies the editor bounds to a suffixed speed", () => {
expect(parseCustomPlaybackSpeedInput("16.1x")).toEqual({ status: "too-fast", draft: "16.1x" });
expect(parseCustomPlaybackSpeedInput("0.2×")).toEqual({ status: "too-slow", draft: "0.2×" });
});

it("accepts the maximum editor speed, the preview's own ceiling", () => {
expect(parseCustomPlaybackSpeedInput("16")).toEqual({
status: "valid",
Expand Down
12 changes: 11 additions & 1 deletion src/components/video-editor/customPlaybackSpeed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,17 @@ export type CustomPlaybackSpeedInputResult =
| { status: "valid"; draft: string; speed: PlaybackSpeed };

export function parseCustomPlaybackSpeedInput(draft: string): CustomPlaybackSpeedInputResult {
const normalized = Number(draft.replace(/,/g, "."));
// The field's placeholder reads "2×", so a typed "1.25x" or "1.25×" is a speed, not noise.
// Same trailing suffix the zoom field accepts (`ZoomLevelControl`).
const sanitized = draft
.trim()
.replace(/,/g, ".")
.replace(/\s*[×x]$/i, "");
// Number("") is 0, which would report a blank draft, or a bare "x", as too-slow.
if (sanitized === "") {
return { status: "empty", draft };
}
const normalized = Number(sanitized);

if (!Number.isFinite(normalized)) {
return { status: "empty", draft };
Expand Down
Loading