refactor: add support for CLI sessionIds in tests (#1919)
Closes https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1119
This commit is contained in:
@@ -70,7 +70,6 @@ const nodeArgs = [
|
||||
'--test-reporter',
|
||||
(process.env['NODE_TEST_REPORTER'] ?? process.env['CI']) ? 'spec' : 'dot',
|
||||
'--test-force-exit',
|
||||
'--test-concurrency=1',
|
||||
'--test',
|
||||
'--test-timeout=120000',
|
||||
...flags,
|
||||
|
||||
+67
-43
@@ -31,9 +31,9 @@ await checkForUpdates(
|
||||
'Run `npm install -g chrome-devtools-mcp@latest` and `chrome-devtools start` to update and restart the daemon.',
|
||||
);
|
||||
|
||||
async function start(args: string[]) {
|
||||
async function start(args: string[], sessionId: string) {
|
||||
const combinedArgs = [...args, ...defaultArgs];
|
||||
await startDaemon(combinedArgs);
|
||||
await startDaemon(combinedArgs, sessionId);
|
||||
logDisclaimers(parseArguments(VERSION, combinedArgs));
|
||||
}
|
||||
|
||||
@@ -78,6 +78,12 @@ const y = yargs(hideBin(process.argv))
|
||||
.usage(
|
||||
`Run 'chrome-devtools <command> --help' for help on the specific command.`,
|
||||
)
|
||||
.option('sessionId', {
|
||||
type: 'string',
|
||||
description: 'Session ID for daemon scoping',
|
||||
default: '',
|
||||
hidden: true,
|
||||
})
|
||||
.demandCommand()
|
||||
.version(VERSION)
|
||||
.strict()
|
||||
@@ -96,8 +102,8 @@ y.command(
|
||||
)
|
||||
.strict(),
|
||||
async argv => {
|
||||
if (isDaemonRunning()) {
|
||||
await stopDaemon();
|
||||
if (isDaemonRunning(argv.sessionId)) {
|
||||
await stopDaemon(argv.sessionId);
|
||||
}
|
||||
// Defaults but we do not want to affect the yargs conflict resolution.
|
||||
if (argv.isolated === undefined && argv.userDataDir === undefined) {
|
||||
@@ -107,46 +113,60 @@ y.command(
|
||||
argv.headless = true;
|
||||
}
|
||||
const args = serializeArgs(cliOptions, argv);
|
||||
await start(args);
|
||||
await start(args, argv.sessionId);
|
||||
process.exit(0);
|
||||
},
|
||||
).strict(); // Re-enable strict validation for other commands; this is applied to the yargs instance itself
|
||||
|
||||
y.command('status', 'Checks if chrome-devtools-mcp is running', async () => {
|
||||
if (isDaemonRunning()) {
|
||||
console.log('chrome-devtools-mcp daemon is running.');
|
||||
const response = await sendCommand({
|
||||
method: 'status',
|
||||
});
|
||||
if (response.success) {
|
||||
const data = JSON.parse(response.result) as {
|
||||
pid: number | null;
|
||||
socketPath: string;
|
||||
startDate: string;
|
||||
version: string;
|
||||
args: string[];
|
||||
};
|
||||
console.log(
|
||||
`pid=${data.pid} socket=${data.socketPath} start-date=${data.startDate} version=${data.version}`,
|
||||
y.command(
|
||||
'status',
|
||||
'Checks if chrome-devtools-mcp is running',
|
||||
y => y,
|
||||
async argv => {
|
||||
if (isDaemonRunning(argv.sessionId)) {
|
||||
console.log('chrome-devtools-mcp daemon is running.');
|
||||
const response = await sendCommand(
|
||||
{
|
||||
method: 'status',
|
||||
},
|
||||
argv.sessionId,
|
||||
);
|
||||
console.log(`args=${JSON.stringify(data.args)}`);
|
||||
if (response.success) {
|
||||
const data = JSON.parse(response.result) as {
|
||||
pid: number | null;
|
||||
socketPath: string;
|
||||
startDate: string;
|
||||
version: string;
|
||||
args: string[];
|
||||
};
|
||||
console.log(
|
||||
`pid=${data.pid} socket=${data.socketPath} start-date=${data.startDate} version=${data.version}`,
|
||||
);
|
||||
console.log(`args=${JSON.stringify(data.args)}`);
|
||||
} else {
|
||||
console.error('Error:', response.error);
|
||||
process.exit(1);
|
||||
}
|
||||
} else {
|
||||
console.error('Error:', response.error);
|
||||
process.exit(1);
|
||||
console.log('chrome-devtools-mcp daemon is not running.');
|
||||
}
|
||||
} else {
|
||||
console.log('chrome-devtools-mcp daemon is not running.');
|
||||
}
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
y.command('stop', 'Stop chrome-devtools-mcp if any', async () => {
|
||||
if (!isDaemonRunning()) {
|
||||
process.exit(0);
|
||||
}
|
||||
await stopDaemon();
|
||||
process.exit(0);
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
y.command(
|
||||
'stop',
|
||||
'Stop chrome-devtools-mcp if any',
|
||||
y => y,
|
||||
async argv => {
|
||||
const sessionId = argv.sessionId as string;
|
||||
if (!isDaemonRunning(sessionId)) {
|
||||
process.exit(0);
|
||||
}
|
||||
await stopDaemon(sessionId);
|
||||
process.exit(0);
|
||||
},
|
||||
);
|
||||
|
||||
for (const [commandName, commandDef] of Object.entries(commands)) {
|
||||
const args = commandDef.args;
|
||||
@@ -213,9 +233,10 @@ for (const [commandName, commandDef] of Object.entries(commands)) {
|
||||
}
|
||||
},
|
||||
async argv => {
|
||||
const sessionId = argv.sessionId as string;
|
||||
try {
|
||||
if (!isDaemonRunning()) {
|
||||
await start([]);
|
||||
if (!isDaemonRunning(sessionId)) {
|
||||
await start([], sessionId);
|
||||
}
|
||||
|
||||
const commandArgs: Record<string, unknown> = {};
|
||||
@@ -225,11 +246,14 @@ for (const [commandName, commandDef] of Object.entries(commands)) {
|
||||
}
|
||||
}
|
||||
|
||||
const response = await sendCommand({
|
||||
method: 'invoke_tool',
|
||||
tool: commandName,
|
||||
args: commandArgs,
|
||||
});
|
||||
const response = await sendCommand(
|
||||
{
|
||||
method: 'invoke_tool',
|
||||
tool: commandName,
|
||||
args: commandArgs,
|
||||
},
|
||||
sessionId,
|
||||
);
|
||||
|
||||
if (response.success) {
|
||||
console.log(
|
||||
|
||||
+10
-9
@@ -67,13 +67,13 @@ function waitForFile(filePath: string, removed = false) {
|
||||
});
|
||||
}
|
||||
|
||||
export async function startDaemon(mcpArgs: string[] = []) {
|
||||
if (isDaemonRunning()) {
|
||||
export async function startDaemon(mcpArgs: string[] = [], sessionId: string) {
|
||||
if (isDaemonRunning(sessionId)) {
|
||||
logger('Daemon is already running');
|
||||
return;
|
||||
}
|
||||
|
||||
const pidFilePath = getPidFilePath();
|
||||
const pidFilePath = getPidFilePath(sessionId);
|
||||
|
||||
if (fs.existsSync(pidFilePath)) {
|
||||
fs.unlinkSync(pidFilePath);
|
||||
@@ -83,7 +83,7 @@ export async function startDaemon(mcpArgs: string[] = []) {
|
||||
const child = spawn(process.execPath, [DAEMON_SCRIPT_PATH, ...mcpArgs], {
|
||||
detached: true,
|
||||
stdio: 'ignore',
|
||||
env: process.env,
|
||||
env: {...process.env, CHROME_DEVTOOLS_MCP_SESSION_ID: sessionId},
|
||||
cwd: process.cwd(),
|
||||
windowsHide: true,
|
||||
});
|
||||
@@ -99,8 +99,9 @@ const SEND_COMMAND_TIMEOUT = 60_000; // ms
|
||||
*/
|
||||
export async function sendCommand(
|
||||
command: DaemonMessage,
|
||||
sessionId: string,
|
||||
): Promise<DaemonResponse> {
|
||||
const socketPath = getSocketPath();
|
||||
const socketPath = getSocketPath(sessionId);
|
||||
|
||||
const socket = net.createConnection({
|
||||
path: socketPath,
|
||||
@@ -133,15 +134,15 @@ export async function sendCommand(
|
||||
});
|
||||
}
|
||||
|
||||
export async function stopDaemon() {
|
||||
if (!isDaemonRunning()) {
|
||||
export async function stopDaemon(sessionId: string) {
|
||||
if (!isDaemonRunning(sessionId)) {
|
||||
logger('Daemon is not running');
|
||||
return;
|
||||
}
|
||||
|
||||
const pidFilePath = getPidFilePath();
|
||||
const pidFilePath = getPidFilePath(sessionId);
|
||||
|
||||
await sendCommand({method: 'stop'});
|
||||
await sendCommand({method: 'stop'}, sessionId);
|
||||
|
||||
await waitForFile(pidFilePath, /*removed=*/ true);
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ import {VERSION} from '../version.js';
|
||||
import type {DaemonMessage} from './types.js';
|
||||
import {
|
||||
DAEMON_CLIENT_NAME,
|
||||
getDaemonPid,
|
||||
getPidFilePath,
|
||||
getSocketPath,
|
||||
INDEX_SCRIPT_PATH,
|
||||
@@ -30,19 +29,20 @@ import {
|
||||
isDaemonRunning,
|
||||
} from './utils.js';
|
||||
|
||||
const pid = getDaemonPid();
|
||||
if (isDaemonRunning(pid)) {
|
||||
const sessionId = process.env.CHROME_DEVTOOLS_MCP_SESSION_ID || '';
|
||||
logger(`Daemon sessionId: ${sessionId}`);
|
||||
if (isDaemonRunning(sessionId)) {
|
||||
logger('Another daemon process is running.');
|
||||
process.exit(1);
|
||||
}
|
||||
const pidFilePath = getPidFilePath();
|
||||
const pidFilePath = getPidFilePath(sessionId);
|
||||
fs.mkdirSync(path.dirname(pidFilePath), {
|
||||
recursive: true,
|
||||
});
|
||||
fs.writeFileSync(pidFilePath, process.pid.toString());
|
||||
logger(`Writing ${process.pid.toString()} to ${pidFilePath}`);
|
||||
|
||||
const socketPath = getSocketPath();
|
||||
const socketPath = getSocketPath(sessionId);
|
||||
|
||||
const startDate = new Date();
|
||||
const mcpServerArgs = process.argv.slice(2);
|
||||
|
||||
+19
-14
@@ -24,56 +24,60 @@ const APP_NAME = 'chrome-devtools-mcp';
|
||||
export const DAEMON_CLIENT_NAME = 'chrome-devtools-cli-daemon';
|
||||
|
||||
// Using these paths due to strict limits on the POSIX socket path length.
|
||||
export function getSocketPath(): string {
|
||||
export function getSocketPath(sessionId: string): string {
|
||||
const uid = os.userInfo().uid;
|
||||
const suffix = sessionId ? `-${sessionId}` : '';
|
||||
const appName = APP_NAME + suffix;
|
||||
|
||||
if (IS_WINDOWS) {
|
||||
// Windows uses Named Pipes, not file paths.
|
||||
// This format is required for server.listen()
|
||||
return path.join('\\\\.\\pipe', APP_NAME, 'server.sock');
|
||||
return path.join('\\\\.\\pipe', appName, 'server.sock');
|
||||
}
|
||||
|
||||
// 1. Try XDG_RUNTIME_DIR (Linux standard, sometimes macOS)
|
||||
if (process.env.XDG_RUNTIME_DIR) {
|
||||
return path.join(process.env.XDG_RUNTIME_DIR, APP_NAME, 'server.sock');
|
||||
return path.join(process.env.XDG_RUNTIME_DIR, appName, 'server.sock');
|
||||
}
|
||||
|
||||
// 2. macOS/Unix Fallback: Use /tmp/
|
||||
// We use /tmp/ because it is much shorter than ~/Library/Application Support/
|
||||
// and keeps us well under the 104-character limit.
|
||||
return path.join('/tmp', `${APP_NAME}-${uid}.sock`);
|
||||
return path.join('/tmp', `${appName}-${uid}.sock`);
|
||||
}
|
||||
|
||||
export function getRuntimeHome(): string {
|
||||
export function getRuntimeHome(sessionId: string): string {
|
||||
const platform = os.platform();
|
||||
const uid = os.userInfo().uid;
|
||||
const suffix = sessionId ? `-${sessionId}` : '';
|
||||
const appName = APP_NAME + suffix;
|
||||
|
||||
// 1. Check for the modern Unix standard
|
||||
if (process.env.XDG_RUNTIME_DIR) {
|
||||
return path.join(process.env.XDG_RUNTIME_DIR, APP_NAME);
|
||||
return path.join(process.env.XDG_RUNTIME_DIR, appName);
|
||||
}
|
||||
|
||||
// 2. Fallback for macOS and older Linux
|
||||
if (platform === 'darwin' || platform === 'linux') {
|
||||
// /tmp is cleared on boot, making it perfect for PIDs
|
||||
return path.join('/tmp', `${APP_NAME}-${uid}`);
|
||||
return path.join('/tmp', `${appName}-${uid}`);
|
||||
}
|
||||
|
||||
// 3. Windows Fallback
|
||||
return path.join(os.tmpdir(), APP_NAME);
|
||||
return path.join(os.tmpdir(), appName);
|
||||
}
|
||||
|
||||
export const IS_WINDOWS = os.platform() === 'win32';
|
||||
|
||||
export function getPidFilePath() {
|
||||
const runtimeDir = getRuntimeHome();
|
||||
export function getPidFilePath(sessionId: string) {
|
||||
const runtimeDir = getRuntimeHome(sessionId);
|
||||
return path.join(runtimeDir, 'daemon.pid');
|
||||
}
|
||||
|
||||
export function getDaemonPid() {
|
||||
export function getDaemonPid(sessionId: string) {
|
||||
try {
|
||||
const pidFile = getPidFilePath();
|
||||
logger(`Daemon pid file ${pidFile}`);
|
||||
const pidFile = getPidFilePath(sessionId);
|
||||
logger(`Daemon pid file ${pidFile} sessionId=${sessionId}`);
|
||||
if (!fs.existsSync(pidFile)) {
|
||||
return null;
|
||||
}
|
||||
@@ -89,7 +93,8 @@ export function getDaemonPid() {
|
||||
}
|
||||
}
|
||||
|
||||
export function isDaemonRunning(pid = getDaemonPid()): pid is number {
|
||||
export function isDaemonRunning(sessionId: string): boolean {
|
||||
const pid = getDaemonPid(sessionId);
|
||||
if (pid) {
|
||||
try {
|
||||
process.kill(pid, 0); // Throws if process doesn't exist
|
||||
|
||||
+33
-14
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
|
||||
import assert from 'node:assert';
|
||||
import crypto from 'node:crypto';
|
||||
import {describe, it, afterEach, beforeEach} from 'node:test';
|
||||
|
||||
import {
|
||||
@@ -16,39 +17,57 @@ import {isDaemonRunning} from '../../src/daemon/utils.js';
|
||||
|
||||
describe('daemon client', () => {
|
||||
describe('start/stop', () => {
|
||||
let sessionId: string;
|
||||
|
||||
beforeEach(async () => {
|
||||
await stopDaemon();
|
||||
sessionId = crypto.randomUUID();
|
||||
await stopDaemon(sessionId);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await stopDaemon();
|
||||
await stopDaemon(sessionId);
|
||||
});
|
||||
|
||||
it('should start and stop daemon', async () => {
|
||||
assert.ok(!isDaemonRunning(), 'Daemon should not be running initially');
|
||||
assert.ok(
|
||||
!isDaemonRunning(sessionId),
|
||||
'Daemon should not be running initially',
|
||||
);
|
||||
|
||||
await startDaemon();
|
||||
assert.ok(isDaemonRunning(), 'Daemon should be running after start');
|
||||
await startDaemon([], sessionId);
|
||||
assert.ok(
|
||||
isDaemonRunning(sessionId),
|
||||
'Daemon should be running after start',
|
||||
);
|
||||
|
||||
await stopDaemon();
|
||||
assert.ok(!isDaemonRunning(), 'Daemon should not be running after stop');
|
||||
await stopDaemon(sessionId);
|
||||
assert.ok(
|
||||
!isDaemonRunning(sessionId),
|
||||
'Daemon should not be running after stop',
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle starting daemon when already running', async () => {
|
||||
await startDaemon();
|
||||
assert.ok(isDaemonRunning(), 'Daemon should be running');
|
||||
await startDaemon([], sessionId);
|
||||
assert.ok(isDaemonRunning(sessionId), 'Daemon should be running');
|
||||
|
||||
// Starting again should be a no-op
|
||||
await startDaemon();
|
||||
assert.ok(isDaemonRunning(), 'Daemon should still be running');
|
||||
await startDaemon([], sessionId);
|
||||
assert.ok(isDaemonRunning(sessionId), 'Daemon should still be running');
|
||||
});
|
||||
|
||||
it('should handle stopping daemon when not running', async () => {
|
||||
assert.ok(!isDaemonRunning(), 'Daemon should not be running initially');
|
||||
assert.ok(
|
||||
!isDaemonRunning(sessionId),
|
||||
'Daemon should not be running initially',
|
||||
);
|
||||
|
||||
// Stopping when not running should be a no-op
|
||||
await stopDaemon();
|
||||
assert.ok(!isDaemonRunning(), 'Daemon should still not be running');
|
||||
await stopDaemon(sessionId);
|
||||
assert.ok(
|
||||
!isDaemonRunning(sessionId),
|
||||
'Daemon should still not be running',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
|
||||
import assert from 'node:assert';
|
||||
import crypto from 'node:crypto';
|
||||
import {describe, it, afterEach, beforeEach} from 'node:test';
|
||||
|
||||
import {
|
||||
@@ -14,27 +15,30 @@ import {
|
||||
} from '../utils.js';
|
||||
|
||||
describe('chrome-devtools', () => {
|
||||
let sessionId: string;
|
||||
|
||||
beforeEach(async () => {
|
||||
await runCli(['stop']);
|
||||
await assertDaemonIsNotRunning();
|
||||
sessionId = crypto.randomUUID();
|
||||
await runCli(['stop'], sessionId);
|
||||
await assertDaemonIsNotRunning(sessionId);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await runCli(['stop']);
|
||||
await assertDaemonIsNotRunning();
|
||||
await runCli(['stop'], sessionId);
|
||||
await assertDaemonIsNotRunning(sessionId);
|
||||
});
|
||||
|
||||
it('can invoke list_pages', async () => {
|
||||
await assertDaemonIsNotRunning();
|
||||
await assertDaemonIsNotRunning(sessionId);
|
||||
|
||||
const startResult = await runCli(['start']);
|
||||
const startResult = await runCli(['start'], sessionId);
|
||||
assert.strictEqual(
|
||||
startResult.status,
|
||||
0,
|
||||
`start command failed: ${startResult.stderr}`,
|
||||
);
|
||||
|
||||
const listPagesResult = await runCli(['list_pages']);
|
||||
const listPagesResult = await runCli(['list_pages'], sessionId);
|
||||
assert.strictEqual(
|
||||
listPagesResult.status,
|
||||
0,
|
||||
@@ -45,18 +49,18 @@ describe('chrome-devtools', () => {
|
||||
'list_pages output is unexpected',
|
||||
);
|
||||
|
||||
await assertDaemonIsRunning();
|
||||
await assertDaemonIsRunning(sessionId);
|
||||
});
|
||||
|
||||
it('can take screenshot', async () => {
|
||||
const startResult = await runCli(['start']);
|
||||
const startResult = await runCli(['start'], sessionId);
|
||||
assert.strictEqual(
|
||||
startResult.status,
|
||||
0,
|
||||
`start command failed: ${startResult.stderr}`,
|
||||
);
|
||||
|
||||
const result = await runCli(['take_screenshot']);
|
||||
const result = await runCli(['take_screenshot'], sessionId);
|
||||
assert.strictEqual(
|
||||
result.status,
|
||||
0,
|
||||
|
||||
@@ -5,23 +5,27 @@
|
||||
*/
|
||||
|
||||
import assert from 'node:assert';
|
||||
import crypto from 'node:crypto';
|
||||
import {describe, it, afterEach, beforeEach} from 'node:test';
|
||||
|
||||
import {assertDaemonIsNotRunning, runCli} from '../utils.js';
|
||||
|
||||
describe('chrome-devtools', () => {
|
||||
let sessionId: string;
|
||||
|
||||
beforeEach(async () => {
|
||||
await runCli(['stop']);
|
||||
await assertDaemonIsNotRunning();
|
||||
sessionId = crypto.randomUUID();
|
||||
await runCli(['stop'], sessionId);
|
||||
await assertDaemonIsNotRunning(sessionId);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await runCli(['stop']);
|
||||
await assertDaemonIsNotRunning();
|
||||
await runCli(['stop'], sessionId);
|
||||
await assertDaemonIsNotRunning(sessionId);
|
||||
});
|
||||
|
||||
it('forwards disclaimers to stderr on start', async () => {
|
||||
const result = await runCli(['start']);
|
||||
const result = await runCli(['start'], sessionId);
|
||||
assert.strictEqual(
|
||||
result.status,
|
||||
0,
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
|
||||
import assert from 'node:assert';
|
||||
import crypto from 'node:crypto';
|
||||
import fs from 'node:fs';
|
||||
import os from 'node:os';
|
||||
import path from 'node:path';
|
||||
@@ -17,36 +18,39 @@ import {
|
||||
} from '../utils.js';
|
||||
|
||||
describe('chrome-devtools', () => {
|
||||
let sessionId: string;
|
||||
|
||||
beforeEach(async () => {
|
||||
await runCli(['stop']);
|
||||
await assertDaemonIsNotRunning();
|
||||
sessionId = crypto.randomUUID();
|
||||
await runCli(['stop'], sessionId);
|
||||
await assertDaemonIsNotRunning(sessionId);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await runCli(['stop']);
|
||||
await assertDaemonIsNotRunning();
|
||||
await runCli(['stop'], sessionId);
|
||||
await assertDaemonIsNotRunning(sessionId);
|
||||
});
|
||||
|
||||
it('can start and stop the daemon', async () => {
|
||||
await assertDaemonIsNotRunning();
|
||||
await assertDaemonIsNotRunning(sessionId);
|
||||
|
||||
const startResult = await runCli(['start']);
|
||||
const startResult = await runCli(['start'], sessionId);
|
||||
assert.strictEqual(
|
||||
startResult.status,
|
||||
0,
|
||||
`start command failed: ${startResult.stderr}`,
|
||||
);
|
||||
|
||||
await assertDaemonIsRunning();
|
||||
await assertDaemonIsRunning(sessionId);
|
||||
|
||||
const stopResult = await runCli(['stop']);
|
||||
const stopResult = await runCli(['stop'], sessionId);
|
||||
assert.strictEqual(
|
||||
stopResult.status,
|
||||
0,
|
||||
`stop command failed: ${stopResult.stderr}`,
|
||||
);
|
||||
|
||||
await assertDaemonIsNotRunning();
|
||||
await assertDaemonIsNotRunning(sessionId);
|
||||
});
|
||||
|
||||
it('can start the daemon with userDataDir', async () => {
|
||||
@@ -56,7 +60,10 @@ describe('chrome-devtools', () => {
|
||||
);
|
||||
fs.mkdirSync(userDataDir, {recursive: true});
|
||||
|
||||
const startResult = await runCli(['start', '--userDataDir', userDataDir]);
|
||||
const startResult = await runCli(
|
||||
['start', '--userDataDir', userDataDir],
|
||||
sessionId,
|
||||
);
|
||||
assert.strictEqual(
|
||||
startResult.status,
|
||||
0,
|
||||
@@ -69,6 +76,6 @@ describe('chrome-devtools', () => {
|
||||
`unexpected conflict error: ${startResult.stderr}`,
|
||||
);
|
||||
|
||||
await assertDaemonIsRunning();
|
||||
await assertDaemonIsRunning(sessionId);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
|
||||
import assert from 'node:assert';
|
||||
import crypto from 'node:crypto';
|
||||
import {describe, it, afterEach, beforeEach} from 'node:test';
|
||||
|
||||
import {
|
||||
@@ -14,26 +15,29 @@ import {
|
||||
} from '../utils.js';
|
||||
|
||||
describe('chrome-devtools', () => {
|
||||
let sessionId: string;
|
||||
|
||||
beforeEach(async () => {
|
||||
await runCli(['stop']);
|
||||
await assertDaemonIsNotRunning();
|
||||
sessionId = crypto.randomUUID();
|
||||
await runCli(['stop'], sessionId);
|
||||
await assertDaemonIsNotRunning(sessionId);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await runCli(['stop']);
|
||||
await assertDaemonIsNotRunning();
|
||||
await runCli(['stop'], sessionId);
|
||||
await assertDaemonIsNotRunning(sessionId);
|
||||
});
|
||||
|
||||
it('reports daemon status correctly', async () => {
|
||||
await assertDaemonIsNotRunning();
|
||||
await assertDaemonIsNotRunning(sessionId);
|
||||
|
||||
const startResult = await runCli(['start']);
|
||||
const startResult = await runCli(['start'], sessionId);
|
||||
assert.strictEqual(
|
||||
startResult.status,
|
||||
0,
|
||||
`start command failed: ${startResult.stderr}`,
|
||||
);
|
||||
|
||||
await assertDaemonIsRunning();
|
||||
await assertDaemonIsRunning(sessionId);
|
||||
});
|
||||
});
|
||||
|
||||
+10
-5
@@ -352,9 +352,14 @@ export const CLI_PATH = path.resolve('build/src/bin/chrome-devtools.js');
|
||||
|
||||
export async function runCli(
|
||||
args: string[],
|
||||
sessionId?: string,
|
||||
): Promise<{status: number | null; stdout: string; stderr: string}> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const child = spawn('node', [CLI_PATH, ...args]);
|
||||
const finalArgs = [...args];
|
||||
if (sessionId) {
|
||||
finalArgs.push('--sessionId', sessionId);
|
||||
}
|
||||
const child = spawn('node', [CLI_PATH, ...finalArgs]);
|
||||
let stdout = '';
|
||||
let stderr = '';
|
||||
child.stdout.on('data', chunk => {
|
||||
@@ -370,16 +375,16 @@ export async function runCli(
|
||||
});
|
||||
}
|
||||
|
||||
export async function assertDaemonIsNotRunning() {
|
||||
const result = await runCli(['status']);
|
||||
export async function assertDaemonIsNotRunning(sessionId?: string) {
|
||||
const result = await runCli(['status'], sessionId);
|
||||
assert.strictEqual(
|
||||
result.stdout,
|
||||
'chrome-devtools-mcp daemon is not running.\n',
|
||||
);
|
||||
}
|
||||
|
||||
export async function assertDaemonIsRunning() {
|
||||
const result = await runCli(['status']);
|
||||
export async function assertDaemonIsRunning(sessionId?: string) {
|
||||
const result = await runCli(['status'], sessionId);
|
||||
assert.ok(
|
||||
result.stdout.startsWith('chrome-devtools-mcp daemon is running.\n'),
|
||||
'chrome-devtools-mcp daemon is not running',
|
||||
|
||||
Reference in New Issue
Block a user