Initial Commit
This commit is contained in:
132
packages/media/audio/audio-controllers/tests/fake-midi.ts
Normal file
132
packages/media/audio/audio-controllers/tests/fake-midi.ts
Normal file
@@ -0,0 +1,132 @@
|
||||
/**
|
||||
* Fake MIDI fixture — supplies the package's spec-mandated injectable seam.
|
||||
* Lives outside `src/` so it does not appear in coverage reports yet remains
|
||||
* shared between the unit, integration, and e2e suites.
|
||||
*/
|
||||
|
||||
import type {
|
||||
MidiAccessAdapter,
|
||||
MidiAccessLike,
|
||||
MidiInputLike,
|
||||
MidiOutputLike,
|
||||
} from "../src/base/midi-controller-plugin.types.js";
|
||||
|
||||
export interface FakeMidiInput extends MidiInputLike {
|
||||
emit(data: ReadonlyArray<number>, timeStamp?: number): void;
|
||||
}
|
||||
|
||||
export interface FakeMidiOutput extends MidiOutputLike {
|
||||
readonly sent: number[][];
|
||||
disconnect(): void;
|
||||
}
|
||||
|
||||
export interface FakeMidiAccess extends MidiAccessLike {
|
||||
readonly mutableInputs: Map<string, FakeMidiInput>;
|
||||
readonly mutableOutputs: Map<string, FakeMidiOutput>;
|
||||
disconnectPort(port: FakeMidiInput | FakeMidiOutput): void;
|
||||
reconnectPort(port: FakeMidiInput | FakeMidiOutput): void;
|
||||
}
|
||||
|
||||
function createInput(id: string, name: string): FakeMidiInput {
|
||||
const port: FakeMidiInput = {
|
||||
id,
|
||||
name,
|
||||
state: "connected",
|
||||
type: "input",
|
||||
onmidimessage: null,
|
||||
emit(data: ReadonlyArray<number>, timeStamp = 0) {
|
||||
port.onmidimessage?.({
|
||||
data: new Uint8Array(data),
|
||||
timeStamp,
|
||||
});
|
||||
},
|
||||
} as FakeMidiInput;
|
||||
return port;
|
||||
}
|
||||
|
||||
function createOutput(id: string, name: string): FakeMidiOutput {
|
||||
const sent: number[][] = [];
|
||||
let connectedState: "connected" | "disconnected" = "connected";
|
||||
const port = {
|
||||
id,
|
||||
name,
|
||||
get state(): "connected" | "disconnected" {
|
||||
return connectedState;
|
||||
},
|
||||
type: "output" as const,
|
||||
sent,
|
||||
send(data: ReadonlyArray<number> | Uint8Array) {
|
||||
sent.push(Array.isArray(data) ? data.slice() : Array.from(data));
|
||||
},
|
||||
disconnect() {
|
||||
connectedState = "disconnected";
|
||||
},
|
||||
} satisfies FakeMidiOutput;
|
||||
return port;
|
||||
}
|
||||
|
||||
export function createFakeAccess(
|
||||
options: { readonly inputName?: string; readonly outputName?: string } = {}
|
||||
): FakeMidiAccess {
|
||||
const inputName = options.inputName ?? "Demo Input";
|
||||
const outputName = options.outputName ?? "Demo Output";
|
||||
const input = createInput("in-1", inputName);
|
||||
const output = createOutput("out-1", outputName);
|
||||
const mutableInputs = new Map<string, FakeMidiInput>([[input.id, input]]);
|
||||
const mutableOutputs = new Map<string, FakeMidiOutput>([
|
||||
[output.id, output],
|
||||
]);
|
||||
const access: FakeMidiAccess = {
|
||||
get inputs(): ReadonlyMap<string, MidiInputLike> {
|
||||
return mutableInputs;
|
||||
},
|
||||
get outputs(): ReadonlyMap<string, MidiOutputLike> {
|
||||
return mutableOutputs;
|
||||
},
|
||||
mutableInputs,
|
||||
mutableOutputs,
|
||||
onstatechange: null,
|
||||
disconnectPort(port) {
|
||||
if (port.type === "input") mutableInputs.delete(port.id);
|
||||
else {
|
||||
port.disconnect();
|
||||
mutableOutputs.delete(port.id);
|
||||
}
|
||||
access.onstatechange?.({
|
||||
port: { ...port, state: "disconnected" },
|
||||
});
|
||||
},
|
||||
reconnectPort(port) {
|
||||
if (port.type === "input") mutableInputs.set(port.id, port);
|
||||
else mutableOutputs.set(port.id, port);
|
||||
access.onstatechange?.({
|
||||
port: { ...port, state: "connected" },
|
||||
});
|
||||
},
|
||||
};
|
||||
return access;
|
||||
}
|
||||
|
||||
export function createFakeAdapter(
|
||||
access: FakeMidiAccess,
|
||||
options: { readonly reject?: Error } = {}
|
||||
): MidiAccessAdapter {
|
||||
return {
|
||||
async requestAccess() {
|
||||
if (options.reject) throw options.reject;
|
||||
return access;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function getFirstInput(access: FakeMidiAccess): FakeMidiInput {
|
||||
const first = access.mutableInputs.values().next().value;
|
||||
if (!first) throw new Error("no input");
|
||||
return first;
|
||||
}
|
||||
|
||||
export function getFirstOutput(access: FakeMidiAccess): FakeMidiOutput {
|
||||
const first = access.mutableOutputs.values().next().value;
|
||||
if (!first) throw new Error("no output");
|
||||
return first;
|
||||
}
|
||||
Reference in New Issue
Block a user