strona-czynna/tests/backend-fixture.ts

173 lines
4.1 KiB
TypeScript

import { test as base } from "@playwright/test";
import { spawn, spawnSync } from "child_process";
import { v4 as uuid } from "uuid";
import getPort from "get-port";
import * as path from "path";
import * as dgram from "dgram";
import * as http from "http";
import * as fs from "fs/promises";
import { MongoClient } from "mongodb";
import { MONGO_PORT, MONGO_HOST } from "../src/back/config.js";
type TestFixtures = {
backend: { url: string; env: Record<string, string> };
setMarkdownValue: (field_name: string, value: string) => Promise<void>;
};
const c8 = "./node_modules/c8/bin/c8.js";
const app_host = process.env.TESTS_RUN_IN_DOCKER
? "test" // the docker name
: "172.17.0.1"; // ip to access localhost from within docker (playwright always runs in docker)
export async function waitForHttpPort({
port,
host = app_host,
timeout = 5000 * 20,
interval = 200,
}: {
port: number;
host?: string;
timeout?: number;
interval?: number;
}): Promise<void> {
const start = Date.now();
return new Promise((resolve, reject) => {
const check = () => {
console.log("Making a request to", { host, port });
const req = http.get({ hostname: host, port, timeout: 1000 }, (res) => {
res.destroy();
resolve(); // Port is ready
});
req.on("error", () => {
if (Date.now() - start > timeout) {
reject(new Error(`Timeout waiting for HTTP port ${port}`));
} else {
setTimeout(check, interval);
}
});
};
check();
});
}
function triggerKillswitch(port: number) {
const client = dgram.createSocket("udp4");
const message = Buffer.from("Hello, UDP!");
const HOST = "127.0.0.1";
client.send(message, port, HOST, () => {
client.close();
});
}
async function dropMongoDB(mongo_db_name: string) {
const client = new MongoClient(`mongodb://${MONGO_HOST}:${MONGO_PORT}`);
try {
await client.connect();
const db = client.db(mongo_db_name);
const result = await db.dropDatabase();
console.log(`Database "${mongo_db_name}" dropped:`, result);
} catch (err) {
console.error("Error dropping database:", err);
} finally {
await client.close();
}
}
export const test = base.extend<TestFixtures>({
backend: async ({}, use, { file, titlePath }) => {
const port = await getPort();
const mongo_db_name = `test-app-${uuid()}`;
const killswitch_port = await getPort();
const coverage_temp_dir = path.join(
"coverage",
"tmp_split",
Date.now().toString()
);
await fs.mkdir(coverage_temp_dir, {
recursive: true,
});
const env = {
...process.env,
SEALIOUS_PORT: `${port}`,
SEALIOUS_MONGO_DB_NAME: mongo_db_name,
...(process.env.TESTS_RUN_IN_DOCKER
? { MEILISEARCH_HOST: `http://meilisearch:7700` }
: {}),
SEALIOUS_KILLSWITCH_PORT: `${killswitch_port}`,
};
const backendProcess = spawn(
c8,
[
"--src",
"src",
"--reporter",
"none",
"--temp-directory",
coverage_temp_dir,
"--all",
"node",
"dist/back/index.js",
],
{
env,
stdio: "inherit",
}
);
const onEndPromise = new Promise<void>((resolve) => {
backendProcess.on("exit", async () => {
await dropMongoDB(mongo_db_name);
spawnSync(
c8,
[
"report",
"--temp-directory",
coverage_temp_dir,
"--reporter",
"lcovonly",
"--report-dir",
path.join("coverage", Date.now().toString()),
],
{ stdio: "inherit" }
);
resolve();
});
});
await waitForHttpPort({ port, timeout: 20000 });
await use({ url: `http://${app_host}:${port}`, env });
triggerKillswitch(killswitch_port);
await onEndPromise;
},
setMarkdownValue: ({ page }, use) => {
use(async (field_name: string, value: string) => {
for (let i = 1; i <= 5; i++) {
try {
await page.getByRole("button", { name: "Preview" }).click();
await page
.locator(`textarea[name="${field_name}"] ~ .CodeMirror`)
.first()
.click({ timeout: 1000 });
await page.getByRole("textbox").press("ControlOrMeta+a");
await page.getByRole("textbox").first().type("Hamster, a dentist");
break;
} catch (e) {
console.error(e);
continue;
}
}
});
},
});
export { expect } from "@playwright/test";