debugg-ai-mcp
Version:
MCP Server for debugg ai web browsing
128 lines (127 loc) • 4.88 kB
JavaScript
import { downloadBinary, start, stop } from '../tunnels/ngrok/index.js';
async function startTunnel(localPort, domain) {
try {
await start({
addr: localPort,
hostname: domain,
onLogEvent: (data) => {
console.error(`${localPort} | ${domain} | ngrok log: ${data}`);
},
});
return domain;
}
catch (err) {
console.error('Error starting ngrok tunnel:', err);
}
}
export class E2eTestRunner {
client;
constructor(client) {
this.client = client;
this.setup();
}
async setup() {
await this.configureNgrok();
}
async configureNgrok() {
await downloadBinary();
}
async startTunnel(port, url) {
await startTunnel(port, url);
console.error(`Tunnel started at: ${url}`);
return url;
}
/**
* Run E2E test generator for a single file *quietly* in the background.
* @param filePath absolute path of the file to test
*/
async runTests(e2eRun) {
// Start by opening an ngrok tunnel.
// call the debugg ai endpoint to start running the test
// retrieve the results when done
// save files locally somewhere
const listener = await startTunnel(3011, `${e2eRun.key}.ngrok.debugg.ai`);
console.error(`Tunnel started at: ${listener}`);
const interval = setInterval(async () => {
const newE2eRun = await this.client.e2es?.getE2eRun(e2eRun.id);
console.error(`E2E run - ${newE2eRun}`);
if (newE2eRun?.status === 'completed') {
console.error(`E2E run completed - ${newE2eRun}`);
clearInterval(interval);
await stop(listener);
}
}, 1000);
// if the run doesn't complete in time, disconnect the tunnel
const setTimer = setTimeout(async () => {
clearInterval(interval);
clearTimeout(setTimer);
await stop(listener);
}, 300000);
return undefined;
}
/**
* Create a new E2E test and run it.
* @param testPort - The port to use for the test.
* @param testDescription - The description of the test.
* @param filePath - The path to the file to test.
* @param repoName - The name of the repository.
* @param branchName - The name of the branch.
* @param repoPath - The path to the repository.
*/
async createNewE2eTest(testPort, testDescription, repoName, branchName, repoPath, filePath) {
console.error(`Creating new E2E test with description: ${testDescription}`);
const e2eTest = await this.client.e2es?.createE2eTest(testDescription, filePath ?? "", repoName, branchName, {
repoPath: repoPath
});
console.error(`E2E test created - ${e2eTest}`);
if (!e2eTest) {
console.error("Failed to create E2E test.");
return null;
}
if (!e2eTest.curRun) {
console.error("Failed to create E2E test run.");
return null;
}
return this.handleE2eRun(testPort, e2eTest.curRun);
}
async handleE2eRun(port, e2eRun) {
console.error(`🔧 Handling E2E run - ${e2eRun.uuid}`);
// Start ngrok tunnel
await startTunnel(port, `${e2eRun.key}.ngrok.debugg.ai`);
console.error(`🌐 Tunnel started at: ${e2eRun.key}.ngrok.debugg.ai`);
let stopped = false;
let lastStep = 0;
let updatedRun = e2eRun;
// Poll every second for completion
const interval = setInterval(async () => {
updatedRun = await this.client.e2es?.getE2eRun(e2eRun.id);
if (!updatedRun)
return;
console.error(`📡 Polled E2E run status: ${updatedRun.status}`);
if (updatedRun.status === 'completed') {
clearInterval(interval);
clearTimeout(timeout);
await stop(`https://${e2eRun.key}.ngrok.debugg.ai`);
// if (updatedRun.runGif) {
// fetchAndOpenGif(this.repoPath ?? "", updatedRun.runGif, updatedRun.test?.name ?? "", updatedRun.uuid);
// }
stopped = true;
}
}, 5000);
// Timeout safeguard
const timeout = setTimeout(async () => {
if (stopped)
return;
clearInterval(interval);
await stop(`https://${e2eRun.key}.ngrok.debugg.ai`);
console.error(`⏰ E2E test timed out after 15 minutes\n`);
stopped = true;
}, 900_000);
// Wait for the polling to complete or timeout to expire
while (!stopped) {
await new Promise(resolve => setTimeout(resolve, 1000));
}
return updatedRun;
}
}
export default E2eTestRunner;