Files
midi-tools/tests/components/workbench.test.tsx
T
2026-09-01 14:22:42 +02:00

69 lines
2.1 KiB
TypeScript

import { render, screen, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { Workbench } from "../../src/components/Workbench";
let closeCount = 0;
class FakeAudioContext {
currentTime = 0;
destination = {} as AudioDestinationNode;
async resume(): Promise<void> {}
createGain() {
return {
gain: {
value: 0,
setValueAtTime: vi.fn(),
linearRampToValueAtTime: vi.fn(),
},
connect: vi.fn(),
};
}
createOscillator() {
return {
type: "sine",
frequency: { value: 0 },
connect: vi.fn(),
start: vi.fn(),
stop: vi.fn(),
};
}
async close(): Promise<void> {
closeCount += 1;
}
}
describe("MIDI Workbench", () => {
beforeEach(() => {
closeCount = 0;
vi.stubGlobal("AudioContext", FakeAudioContext);
});
afterEach(() => vi.unstubAllGlobals());
it("shows the example and applies and undoes an edit", async () => {
const user = userEvent.setup();
render(<Workbench />);
expect(
screen.getByRole("img", { name: /Piano roll with 3 notes/u }),
).toBeVisible();
await user.clear(screen.getByLabelText("Semitones"));
await user.type(screen.getByLabelText("Semitones"), "12");
await user.click(screen.getByRole("button", { name: "Apply transpose" }));
expect(screen.getByRole("status")).toHaveTextContent("transposed");
await user.click(screen.getByRole("button", { name: "Undo edit" }));
expect(screen.getByRole("status")).toHaveTextContent("undone");
});
it("closes the active audio context on immediate stop", async () => {
const user = userEvent.setup();
render(<Workbench />);
await user.click(screen.getByRole("button", { name: "Play from start" }));
await waitFor(() =>
expect(screen.getByRole("status")).toHaveTextContent("Playing 3"),
);
await user.click(screen.getByRole("button", { name: "Stop now" }));
expect(closeCount).toBe(1);
expect(screen.getByRole("status")).toHaveTextContent("stopped immediately");
});
});