diff --git a/tests/e2e/chrome-devtools-commands.test.ts b/tests/e2e/chrome-devtools-commands.test.ts new file mode 100644 index 000000000..7d1523f6e --- /dev/null +++ b/tests/e2e/chrome-devtools-commands.test.ts @@ -0,0 +1,70 @@ +/** + * @license + * Copyright 2026 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import assert from 'node:assert'; +import {describe, it, afterEach, beforeEach} from 'node:test'; + +import { + assertDaemonIsNotRunning, + assertDaemonIsRunning, + runCli, +} from '../utils.js'; + +describe('chrome-devtools', () => { + beforeEach(async () => { + await runCli(['stop']); + await assertDaemonIsNotRunning(); + }); + + afterEach(async () => { + await runCli(['stop']); + await assertDaemonIsNotRunning(); + }); + + it('can invoke list_pages', async () => { + await assertDaemonIsNotRunning(); + + const startResult = await runCli(['start']); + assert.strictEqual( + startResult.status, + 0, + `start command failed: ${startResult.stderr}`, + ); + + const listPagesResult = await runCli(['list_pages']); + assert.strictEqual( + listPagesResult.status, + 0, + `list_pages command failed: ${listPagesResult.stderr}`, + ); + assert( + listPagesResult.stdout.includes('about:blank'), + 'list_pages output is unexpected', + ); + + await assertDaemonIsRunning(); + }); + + it('can take screenshot', async () => { + const startResult = await runCli(['start']); + assert.strictEqual( + startResult.status, + 0, + `start command failed: ${startResult.stderr}`, + ); + + const result = await runCli(['take_screenshot']); + assert.strictEqual( + result.status, + 0, + `take_screenshot command failed: ${result.stderr}`, + ); + assert( + result.stdout.includes('.png'), + 'take_screenshot output is unexpected', + ); + }); +}); diff --git a/tests/e2e/chrome-devtools-disclaimers.test.ts b/tests/e2e/chrome-devtools-disclaimers.test.ts new file mode 100644 index 000000000..47d3b4f09 --- /dev/null +++ b/tests/e2e/chrome-devtools-disclaimers.test.ts @@ -0,0 +1,35 @@ +/** + * @license + * Copyright 2026 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import assert from 'node:assert'; +import {describe, it, afterEach, beforeEach} from 'node:test'; + +import {assertDaemonIsNotRunning, runCli} from '../utils.js'; + +describe('chrome-devtools', () => { + beforeEach(async () => { + await runCli(['stop']); + await assertDaemonIsNotRunning(); + }); + + afterEach(async () => { + await runCli(['stop']); + await assertDaemonIsNotRunning(); + }); + + it('forwards disclaimers to stderr on start', async () => { + const result = await runCli(['start']); + assert.strictEqual( + result.status, + 0, + `start command failed: ${result.stderr}`, + ); + assert( + result.stderr.includes('chrome-devtools-mcp exposes content'), + 'Disclaimer not found in stderr on start', + ); + }); +}); diff --git a/tests/e2e/chrome-devtools-start-stop.test.ts b/tests/e2e/chrome-devtools-start-stop.test.ts new file mode 100644 index 000000000..72862dd3f --- /dev/null +++ b/tests/e2e/chrome-devtools-start-stop.test.ts @@ -0,0 +1,74 @@ +/** + * @license + * Copyright 2026 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import assert from 'node:assert'; +import fs from 'node:fs'; +import os from 'node:os'; +import path from 'node:path'; +import {describe, it, afterEach, beforeEach} from 'node:test'; + +import { + assertDaemonIsNotRunning, + assertDaemonIsRunning, + runCli, +} from '../utils.js'; + +describe('chrome-devtools', () => { + beforeEach(async () => { + await runCli(['stop']); + await assertDaemonIsNotRunning(); + }); + + afterEach(async () => { + await runCli(['stop']); + await assertDaemonIsNotRunning(); + }); + + it('can start and stop the daemon', async () => { + await assertDaemonIsNotRunning(); + + const startResult = await runCli(['start']); + assert.strictEqual( + startResult.status, + 0, + `start command failed: ${startResult.stderr}`, + ); + + await assertDaemonIsRunning(); + + const stopResult = await runCli(['stop']); + assert.strictEqual( + stopResult.status, + 0, + `stop command failed: ${stopResult.stderr}`, + ); + + await assertDaemonIsNotRunning(); + }); + + it('can start the daemon with userDataDir', async () => { + const userDataDir = path.join( + os.tmpdir(), + `chrome-devtools-test-${crypto.randomUUID()}`, + ); + fs.mkdirSync(userDataDir, {recursive: true}); + + const startResult = await runCli(['start', '--userDataDir', userDataDir]); + assert.strictEqual( + startResult.status, + 0, + `start command failed: ${startResult.stderr}`, + ); + assert.ok( + !startResult.stderr.includes( + 'Arguments userDataDir and isolated are mutually exclusive', + ), + `unexpected conflict error: ${startResult.stderr}`, + ); + + await assertDaemonIsRunning(); + }); +}); diff --git a/tests/e2e/chrome-devtools-status.test.ts b/tests/e2e/chrome-devtools-status.test.ts new file mode 100644 index 000000000..a07068793 --- /dev/null +++ b/tests/e2e/chrome-devtools-status.test.ts @@ -0,0 +1,39 @@ +/** + * @license + * Copyright 2026 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import assert from 'node:assert'; +import {describe, it, afterEach, beforeEach} from 'node:test'; + +import { + assertDaemonIsNotRunning, + assertDaemonIsRunning, + runCli, +} from '../utils.js'; + +describe('chrome-devtools', () => { + beforeEach(async () => { + await runCli(['stop']); + await assertDaemonIsNotRunning(); + }); + + afterEach(async () => { + await runCli(['stop']); + await assertDaemonIsNotRunning(); + }); + + it('reports daemon status correctly', async () => { + await assertDaemonIsNotRunning(); + + const startResult = await runCli(['start']); + assert.strictEqual( + startResult.status, + 0, + `start command failed: ${startResult.stderr}`, + ); + + await assertDaemonIsRunning(); + }); +}); diff --git a/tests/e2e/chrome-devtools.test.ts b/tests/e2e/chrome-devtools.test.ts deleted file mode 100644 index c042cd72d..000000000 --- a/tests/e2e/chrome-devtools.test.ts +++ /dev/null @@ -1,177 +0,0 @@ -/** - * @license - * Copyright 2026 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -import assert from 'node:assert'; -import {spawn} from 'node:child_process'; -import fs from 'node:fs'; -import os from 'node:os'; -import path from 'node:path'; -import {describe, it, afterEach, beforeEach} from 'node:test'; - -const CLI_PATH = path.resolve('build/src/bin/chrome-devtools.js'); - -async function runCli( - args: string[], -): Promise<{status: number | null; stdout: string; stderr: string}> { - return new Promise((resolve, reject) => { - const child = spawn('node', [CLI_PATH, ...args]); - let stdout = ''; - let stderr = ''; - child.stdout.on('data', chunk => { - stdout += chunk; - process.stdout.write(chunk); - }); - child.stderr.on('data', chunk => { - stderr += chunk; - process.stderr.write(chunk); - }); - child.on('close', status => resolve({status, stdout, stderr})); - child.on('error', reject); - }); -} - -describe('chrome-devtools', () => { - async function assertDaemonIsNotRunning() { - const result = await runCli(['status']); - assert.strictEqual( - result.stdout, - 'chrome-devtools-mcp daemon is not running.\n', - ); - } - - async function assertDaemonIsRunning() { - const result = await runCli(['status']); - assert.ok( - result.stdout.startsWith('chrome-devtools-mcp daemon is running.\n'), - 'chrome-devtools-mcp daemon is not running', - ); - } - - beforeEach(async () => { - await runCli(['stop']); - await assertDaemonIsNotRunning(); - }); - - afterEach(async () => { - await runCli(['stop']); - await assertDaemonIsNotRunning(); - }); - - it('reports daemon status correctly', async () => { - await assertDaemonIsNotRunning(); - - const startResult = await runCli(['start']); - assert.strictEqual( - startResult.status, - 0, - `start command failed: ${startResult.stderr}`, - ); - - await assertDaemonIsRunning(); - }); - - it('can start and stop the daemon', async () => { - await assertDaemonIsNotRunning(); - - const startResult = await runCli(['start']); - assert.strictEqual( - startResult.status, - 0, - `start command failed: ${startResult.stderr}`, - ); - - await assertDaemonIsRunning(); - - const stopResult = await runCli(['stop']); - assert.strictEqual( - stopResult.status, - 0, - `stop command failed: ${stopResult.stderr}`, - ); - - await assertDaemonIsNotRunning(); - }); - - it('can start the daemon with userDataDir', async () => { - const userDataDir = path.join( - os.tmpdir(), - `chrome-devtools-test-${crypto.randomUUID()}`, - ); - fs.mkdirSync(userDataDir, {recursive: true}); - - const startResult = await runCli(['start', '--userDataDir', userDataDir]); - assert.strictEqual( - startResult.status, - 0, - `start command failed: ${startResult.stderr}`, - ); - assert.ok( - !startResult.stderr.includes( - 'Arguments userDataDir and isolated are mutually exclusive', - ), - `unexpected conflict error: ${startResult.stderr}`, - ); - - await assertDaemonIsRunning(); - }); - - it('can invoke list_pages', async () => { - await assertDaemonIsNotRunning(); - - const startResult = await runCli(['start']); - assert.strictEqual( - startResult.status, - 0, - `start command failed: ${startResult.stderr}`, - ); - - const listPagesResult = await runCli(['list_pages']); - assert.strictEqual( - listPagesResult.status, - 0, - `list_pages command failed: ${listPagesResult.stderr}`, - ); - assert( - listPagesResult.stdout.includes('about:blank'), - 'list_pages output is unexpected', - ); - - await assertDaemonIsRunning(); - }); - - it('can take screenshot', async () => { - const startResult = await runCli(['start']); - assert.strictEqual( - startResult.status, - 0, - `start command failed: ${startResult.stderr}`, - ); - - const result = await runCli(['take_screenshot']); - assert.strictEqual( - result.status, - 0, - `take_screenshot command failed: ${result.stderr}`, - ); - assert( - result.stdout.includes('.png'), - 'take_screenshot output is unexpected', - ); - }); - - it('forwards disclaimers to stderr on start', async () => { - const result = await runCli(['start']); - assert.strictEqual( - result.status, - 0, - `start command failed: ${result.stderr}`, - ); - assert( - result.stderr.includes('chrome-devtools-mcp exposes content'), - 'Disclaimer not found in stderr on start', - ); - }); -}); diff --git a/tests/utils.ts b/tests/utils.ts index 4d48d970c..23257616f 100644 --- a/tests/utils.ts +++ b/tests/utils.ts @@ -5,6 +5,8 @@ */ import assert from 'node:assert'; +import {spawn} from 'node:child_process'; +import path from 'node:path'; import type {CallToolResult} from '@modelcontextprotocol/sdk/types.js'; import logger from 'debug'; @@ -331,3 +333,41 @@ export function getMockBrowser(): Browser { ...mockListener(), } as Browser; } + +export const CLI_PATH = path.resolve('build/src/bin/chrome-devtools.js'); + +export async function runCli( + args: string[], +): Promise<{status: number | null; stdout: string; stderr: string}> { + return new Promise((resolve, reject) => { + const child = spawn('node', [CLI_PATH, ...args]); + let stdout = ''; + let stderr = ''; + child.stdout.on('data', chunk => { + stdout += chunk; + process.stdout.write(chunk); + }); + child.stderr.on('data', chunk => { + stderr += chunk; + process.stderr.write(chunk); + }); + child.on('close', status => resolve({status, stdout, stderr})); + child.on('error', reject); + }); +} + +export async function assertDaemonIsNotRunning() { + const result = await runCli(['status']); + assert.strictEqual( + result.stdout, + 'chrome-devtools-mcp daemon is not running.\n', + ); +} + +export async function assertDaemonIsRunning() { + const result = await runCli(['status']); + assert.ok( + result.stdout.startsWith('chrome-devtools-mcp daemon is running.\n'), + 'chrome-devtools-mcp daemon is not running', + ); +}