fix(clip): clip mode keeps its range, its focus and its keys through the player's own moves
- A mark on the very end seeks a moment short of it; while a clip is marked the end of the item neither closes the player nor steps the queue, and Plex's auto-skip waits. - Plex: Escape and Back peel what is over the picture (info, up-next, the countdown) before the clip; Back under a dialog is the dialog's; no range opens while the length is unknown. - YouTube: the focus goes back to the scissors the user can see, else the player; the panel keeps its name, Loop and focus when the view is maximised or restored under it. - A typed time stays in its field until taken, then shows where it landed; unreadable stays marked invalid; a decimal comma is read (ui/TimecodeField, the editor's fields too). - RangeTrack: a drag is its pointer's, a cancelled drag reports final, an adornment sits beside the slider, a caller-chosen focus ring (amber on the clip strip). CropFrame: a drag is its pointer's. - The editor's remove-cut is a real button at the cut's foot; a segment's keep toggle sits over the cut's hit area.
This commit is contained in:
@@ -6,7 +6,7 @@
|
||||
// now says how many made it (also when `req` has already shown the refusal in its dialog), deselects
|
||||
// them, holds the cut list still while it queues, and a retry goes on with the same "(i/N)" series.
|
||||
import { describe, it, expect, vi, beforeAll, afterEach } from "vitest";
|
||||
import { render, screen, fireEvent, waitFor, cleanup, act } from "@testing-library/react";
|
||||
import { render, screen, fireEvent, waitFor, cleanup, act, within } from "@testing-library/react";
|
||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||
|
||||
vi.mock("react-i18next", async (importOriginal) => ({
|
||||
@@ -124,3 +124,39 @@ describe("VideoEditor — a cut on the keyboard", () => {
|
||||
expect(cut.getAttribute("aria-valuenow")).toBe("99.9");
|
||||
});
|
||||
});
|
||||
|
||||
// A cut's remove button and the keep toggle of the segment after it: the button is a button a click
|
||||
// or a key works (it sat inside the cut's slider, pointer-only, its arrows moving the cut), and it
|
||||
// sits at the cut's foot, clear of the toggle at the segment's head; the toggle sits over the cut's
|
||||
// hit area that reaches into its segment, so a press on it is the toggle's.
|
||||
describe("VideoEditor — a cut's remove button", () => {
|
||||
const split = () => {
|
||||
vi.spyOn(api, "downloadStoryboard").mockResolvedValue({ available: false } as never);
|
||||
render(
|
||||
<QueryClientProvider client={new QueryClient()}>
|
||||
<VideoEditor job={job} onClose={() => {}} />
|
||||
</QueryClientProvider>,
|
||||
);
|
||||
const video = document.querySelector("video")!;
|
||||
Object.defineProperty(video, "currentTime", { value: 50, configurable: true, writable: true });
|
||||
fireEvent.timeUpdate(video);
|
||||
fireEvent.click(screen.getByText("editor.splitHere"));
|
||||
};
|
||||
|
||||
it("removes the cut on a click (Enter on a focused button is one), and is not part of the slider", () => {
|
||||
split();
|
||||
const remove = screen.getByRole("button", { name: "editor.removeCut" });
|
||||
expect(screen.getByRole("slider", { name: "editor.cutN" }).contains(remove)).toBe(false);
|
||||
fireEvent.click(remove);
|
||||
expect(screen.queryByRole("slider", { name: "editor.cutN" })).toBeNull();
|
||||
});
|
||||
|
||||
it("sits at the cut's foot, under the next segment's keep toggle at its head", () => {
|
||||
split();
|
||||
const remove = screen.getByRole("button", { name: "editor.removeCut" });
|
||||
expect(remove.className).toMatch(/\bbottom-0\b/);
|
||||
const track = screen.getByRole("group", { name: "editor.timeline" });
|
||||
const toggles = within(track).getAllByRole("button", { pressed: true });
|
||||
for (const toggle of toggles) expect(toggle.className).toMatch(/\bz-menu\b/);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user