evcc-io/tests/simulator.ts

93 lines
2.7 KiB
TypeScript

import os from "os";
import path from "path";
import fs from "fs";
import waitOn from "wait-on";
import axios from "axios";
import { spawn } from "child_process";
import { Transform } from "stream";
import type { Page } from "@playwright/test";
const LOG_ENABLED = !process.env["GITHUB_ACTIONS"];
function workerPort() {
const index = parseInt(process.env["TEST_WORKER_INDEX"] ?? "-1");
return 13000 + index;
}
function logPrefix() {
return `[worker:${process.env["TEST_WORKER_INDEX"]}]`;
}
function createSteamLog() {
return new Transform({
transform(chunk: Buffer, _, callback) {
const lines = chunk.toString().split("\n");
lines.forEach((line) => {
if (line.trim()) log(line);
});
callback();
},
});
}
function log(...args: any[]) {
// uncomment for debugging
if (LOG_ENABLED) {
console.log(logPrefix(), ...args);
}
}
export function simulatorHost() {
return `localhost:${workerPort()}`;
}
export function simulatorUrl() {
return `http://${simulatorHost()}`;
}
export function simulatorConfig() {
const input = "./tests/simulator.evcc.yaml";
const content = fs.readFileSync(input, "utf8");
const result = content.replace(/localhost:7072/g, simulatorHost());
// per-worker file name, multiple workers write their own port concurrently
const resultName = `simulator-${workerPort()}.evcc.generated.yaml`;
const resultPath = path.join(os.tmpdir(), resultName);
fs.writeFileSync(resultPath, result);
return resultPath;
}
export async function startSimulator() {
const port = workerPort();
log("starting simulator", { port });
log(`wait until port ${port} is available`);
await waitOn({ resources: [`tcp:${port}`], reverse: true, log: LOG_ENABLED });
const instance = spawn("npm", ["run", "simulator", "--", "--port", port.toString()]);
const steamLog = createSteamLog();
instance.stdout.pipe(steamLog);
instance.stderr.pipe(steamLog);
instance.on("exit", (code) => {
log("simulator terminated", { code, port });
steamLog.end();
});
await waitOn({ resources: [`${simulatorUrl()}/api/state`], log: LOG_ENABLED });
}
export async function stopSimulator() {
const port = workerPort();
log("shutting down simulator", { port });
await axios.post(`${simulatorUrl()}/api/shutdown`);
log(`wait until port ${port} is closed`);
await waitOn({ resources: [`tcp:localhost:${port}`], reverse: true, log: LOG_ENABLED });
}
export const simulatorApply = async (page: Page) => {
await Promise.all([
page.waitForResponse(
(response) => response.url().includes("/api/state") && response.request().method() === "POST"
),
page.getByRole("button", { name: "Apply changes" }).click(),
]);
};