Files
obs-tools/test/e2e/run-extra.mjs
T
2026-10-14 07:00:33 +02:00

123 lines
5.7 KiB
JavaScript

// Second page: monitoring, set volume, scene above, and an OpenDeck multi action.
// Needs the profile from layout-extra.json.
import { ObsClient } from "../../de.felitendo.obs-tools.sdPlugin/plugin/lib/obs.js";
import { readLocalObsConfig } from "../../de.felitendo.obs-tools.sdPlugin/plugin/lib/connections.js";
import { faderToDb } from "../../de.felitendo.obs-tools.sdPlugin/plugin/lib/volume.js";
const DECK = process.env.VIRTUAL_DECK_URL || "http://127.0.0.1:57190";
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
const press = (key, ms = 80) => fetch(`${DECK}/api/key/${key}/press?ms=${ms}`, { method: "POST" });
const cfg = readLocalObsConfig();
const obs = new ObsClient({ port: cfg.port, password: cfg.password });
await new Promise((resolve) => (obs.once("connected", resolve), obs.start()));
const call = (t, d) => obs.call(t, d);
async function waitFor(what, fn, timeout = 5000) {
const until = Date.now() + timeout;
let last;
while (Date.now() < until) {
if ((last = await fn())) return last;
await sleep(100);
}
throw new Error(`timeout waiting for ${what}`);
}
let failed = 0;
async function test(name, fn) {
try {
await fn();
console.log(`ok ${name}`);
} catch (error) {
failed++;
console.log(`FAIL ${name}: ${error.message}`);
}
}
const input = "Desktop-Audio";
await call("SetCurrentProgramScene", { sceneName: "Szene" });
await call("SetInputAudioMonitorType", { inputName: input, monitorType: "OBS_MONITORING_TYPE_NONE" });
await sleep(500);
await test("monitor set, press again turns it off", async () => {
await press(0);
await waitFor("monitor only", async () => (await call("GetInputAudioMonitorType", { inputName: input })).monitorType === "OBS_MONITORING_TYPE_MONITOR_ONLY");
await sleep(300);
await press(0);
await waitFor("monitor off", async () => (await call("GetInputAudioMonitorType", { inputName: input })).monitorType === "OBS_MONITORING_TYPE_NONE");
});
await test("set volume to 50% of the fader", async () => {
await call("SetInputVolume", { inputName: input, inputVolumeDb: 0 });
await press(8);
const target = faderToDb(0.5);
await waitFor(`${target.toFixed(2)} dB`, async () => Math.abs((await call("GetInputVolume", { inputName: input })).inputVolumeDb - target) < 0.05);
await call("SetInputVolume", { inputName: input, inputVolumeDb: 0 });
});
await test("scene above (previous in the list)", async () => {
const { scenes } = await call("GetSceneList");
const order = [...scenes].sort((a, b) => b.sceneIndex - a.sceneIndex).map((s) => s.sceneName);
const expected = order[(order.indexOf("Szene") - 1 + order.length) % order.length];
await press(5);
await waitFor(`program = ${expected}`, async () => (await call("GetCurrentProgramScene")).sceneName === expected);
await call("SetCurrentProgramScene", { sceneName: "Szene" });
await sleep(300);
});
await test("multi action: studio on, mute, scene goes live directly", async () => {
await call("SetStudioModeEnabled", { studioModeEnabled: false });
await call("SetInputMute", { inputName: input, inputMuted: false });
await sleep(300);
await press(9);
await waitFor("studio mode on", async () => (await call("GetStudioModeEnabled")).studioModeEnabled);
await waitFor("muted", async () => (await call("GetInputMute", { inputName: input })).inputMuted);
await waitFor("program = Scene 2", async () => (await call("GetCurrentProgramScene")).sceneName === "Scene 2", 6000);
// "enable" and "mute" must not toggle back on a second run.
await sleep(500);
await press(9);
await sleep(1200);
if (!(await call("GetStudioModeEnabled")).studioModeEnabled) throw new Error("second run turned studio mode off");
if (!(await call("GetInputMute", { inputName: input })).inputMuted) throw new Error("second run unmuted");
await call("SetStudioModeEnabled", { studioModeEnabled: false });
await call("SetInputMute", { inputName: input, inputMuted: false });
await call("SetCurrentProgramScene", { sceneName: "Szene" });
});
await test("image source: picture per key, auto hide, press again hides", async () => {
const { sceneItemId } = await call("GetSceneItemId", { sceneName: "Szene", sourceName: "Picture" });
const shown = async () => (await call("GetSceneItemEnabled", { sceneName: "Szene", sceneItemId })).sceneItemEnabled;
const file = async () => (await call("GetInputSettings", { inputName: "Picture" })).inputSettings.file;
await call("SetSceneItemEnabled", { sceneName: "Szene", sceneItemId, sceneItemEnabled: false });
await sleep(300);
await press(12);
await waitFor("GG shown", async () => (await shown()) && (await file()).endsWith("obstools-gg.png"));
await waitFor("hidden after 2 s", async () => !(await shown()), 4000);
await press(13);
await waitFor("BRB shown", async () => (await shown()) && (await file()).endsWith("obstools-brb.png"));
await sleep(400);
await press(13);
await waitFor("hidden by second press", async () => !(await shown()));
});
await test("set text: count down from 3, stops at 0, double press, hold", async () => {
const text = async () => (await call("GetInputSettings", { inputName: "Title Text" })).inputSettings.text;
const pressAndWait = async (expected, ms) => {
await press(14, ms);
await waitFor(expected, async () => (await text()) === expected);
// A pause, or the next press would make a double press.
await sleep(500);
};
await pressAndWait("Live in 3", 1000);
await pressAndWait("Live in 2");
await pressAndWait("Live in 1");
await pressAndWait("Live in 0");
await pressAndWait("Live in 0");
await press(14);
await press(14);
await waitFor("Live in 1 (double press counts up)", async () => (await text()) === "Live in 1");
await sleep(500);
await pressAndWait("Live in 3", 1000);
});
obs.stop();
console.log(failed ? `${failed} failed` : "all passed");
process.exit(failed ? 1 : 0);