Files
midi-tools/tests/browser/app.spec.ts
T
zemion 06e095a2eb
Verify / verify (push) Canceled after 0s
Release MIDI Tools 0.2.0
2026-09-02 09:14:48 +02:00

111 lines
3.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { expect, test } from "@playwright/test";
import { Buffer } from "node:buffer";
for (const path of ["/", "/deep/nested/midi/"]) {
test(`inspects and edits MIDI locally at ${path}`, async ({ page }) => {
const external: string[] = [];
page.on("request", (request) => {
if (!request.url().startsWith("http://127.0.0.1:4212"))
external.push(request.url());
});
await page.goto(path);
await expect(
page.getByRole("heading", {
name: "See, edit and hear the timeline locally.",
}),
).toBeVisible();
await expect(
page.getByRole("img", { name: /Piano roll with 3 notes/u }),
).toBeVisible();
await page.getByLabel("Semitones").fill("12");
await page.getByRole("button", { name: "Apply transpose" }).click();
await expect(page.getByRole("status")).toContainText("transposed");
await expect(
page.getByRole("heading", { name: "Event inspector" }),
).toBeVisible();
expect(external).toEqual([]);
});
}
test("parses and exports through the MIDI worker", async ({ page }) => {
await page.goto("/");
await page.getByLabel("Open MIDI file").setInputFiles({
name: "worker.mid",
mimeType: "audio/midi",
buffer: midiFixture(),
});
await expect(page.getByRole("status")).toContainText("Parsed type 0 MIDI");
const pending = page.waitForEvent("download");
await page.getByRole("button", { name: "Download MIDI" }).click();
expect((await pending).suggestedFilename()).toBe("worker.mid");
await expect(page.getByRole("status")).toContainText(
"MIDI export generated in the worker",
);
});
test("edits a paired note and inspects a local SoundFont", async ({ page }) => {
await page.goto("/");
await page.getByRole("button", { name: "Select first note" }).click();
await page.getByLabel("Pitch (0127)").fill("61");
await page.getByLabel("Velocity").fill("77");
await page.getByRole("button", { name: "Apply note edit" }).click();
await expect(page.getByRole("status")).toContainText("note updated");
await page.getByLabel("Open SoundFont file").setInputFiles({
name: "fixture.sf2",
mimeType: "audio/x-soundfont",
buffer: soundFontFixture(),
});
await expect(page.getByRole("status")).toContainText(
"Inspected the local SoundFont structure",
);
await expect(page.getByText("Fixture bank", { exact: true })).toBeVisible();
});
test("keeps the installed MIDI application available offline", async ({
page,
context,
}) => {
await page.goto("/");
await page.evaluate(async () => navigator.serviceWorker.ready);
await page.reload();
await context.setOffline(true);
try {
await page.reload();
await expect(
page.getByRole("heading", {
name: "See, edit and hear the timeline locally.",
}),
).toBeVisible();
} finally {
await context.setOffline(false);
}
});
function midiFixture(): Buffer {
return Buffer.from([
0x4d, 0x54, 0x68, 0x64, 0, 0, 0, 6, 0, 0, 0, 1, 1, 0xe0, 0x4d, 0x54, 0x72,
0x6b, 0, 0, 0, 12, 0, 0x90, 60, 100, 0x83, 0x60, 60, 0, 0, 0xff, 0x2f, 0,
]);
}
function soundFontFixture(): Buffer {
const chunk = (id: string, data: Buffer) => {
const output = Buffer.alloc(8 + data.length + (data.length & 1));
output.write(id, 0, "ascii");
output.writeUInt32LE(data.length, 4);
data.copy(output, 8);
return output;
};
const list = (id: string, ...children: Buffer[]) =>
chunk("LIST", Buffer.concat([Buffer.from(id, "ascii"), ...children]));
const body = Buffer.concat([
Buffer.from("sfbk", "ascii"),
list("INFO", chunk("INAM", Buffer.from("Fixture bank\0"))),
list("pdta", chunk("phdr", Buffer.alloc(76))),
]);
const output = Buffer.alloc(8);
output.write("RIFF", 0, "ascii");
output.writeUInt32LE(body.length, 4);
return Buffer.concat([output, body]);
}