mcporter/tests/chrome-devtools-compat.test.ts
2026-05-14 12:51:16 +01:00

103 lines
4.2 KiB
TypeScript

import fs from 'node:fs/promises';
import os from 'node:os';
import path from 'node:path';
import { afterEach, describe, expect, it } from 'vitest';
import { patchChromeDevtoolsMcp } from '../src/chrome-devtools-auto-connect-patch.js';
import {
applyChromeDevtoolsCompat,
resolveChromeDevtoolsCompatPatchPath,
shouldApplyChromeDevtoolsCompat,
} from '../src/chrome-devtools-compat.js';
describe('chrome-devtools compatibility', () => {
afterEach(() => {
delete process.env.MCPORTER_DISABLE_CHROME_DEVTOOLS_COMPAT;
});
it('enables the patch for autoConnect chrome-devtools commands', () => {
expect(shouldApplyChromeDevtoolsCompat('npx', ['-y', 'chrome-devtools-mcp@latest', '--autoConnect'])).toBe(true);
expect(shouldApplyChromeDevtoolsCompat('npx', ['-y', 'chrome-devtools-mcp', '--auto-connect'])).toBe(true);
});
it('does not patch non-autoConnect commands', () => {
expect(shouldApplyChromeDevtoolsCompat('npx', ['-y', 'chrome-devtools-mcp@latest'])).toBe(false);
});
it('allows opting out of the compatibility patch', () => {
process.env.MCPORTER_DISABLE_CHROME_DEVTOOLS_COMPAT = '1';
expect(shouldApplyChromeDevtoolsCompat('npx', ['-y', 'chrome-devtools-mcp@latest', '--autoConnect'])).toBe(false);
});
it('allows opting out from the merged server env', () => {
const result = applyChromeDevtoolsCompat({ MCPORTER_DISABLE_CHROME_DEVTOOLS_COMPAT: '1' }, 'npx', [
'-y',
'chrome-devtools-mcp@latest',
'--autoConnect',
]);
expect(result).toEqual({ env: { MCPORTER_DISABLE_CHROME_DEVTOOLS_COMPAT: '1' }, applied: false });
});
it('injects a NODE_OPTIONS import for matching commands', () => {
const result = applyChromeDevtoolsCompat({}, 'npx', ['-y', 'chrome-devtools-mcp@latest', '--autoConnect']);
expect(result.applied).toBe(true);
expect(result.env.NODE_OPTIONS).toContain('--import=file://');
expect(result.env.NODE_OPTIONS).toContain('chrome-devtools-auto-connect-patch.js');
});
it('preserves existing NODE_OPTIONS', () => {
const result = applyChromeDevtoolsCompat({ NODE_OPTIONS: '--trace-warnings' }, 'npx', [
'-y',
'chrome-devtools-mcp@latest',
'--autoConnect',
]);
expect(result.applied).toBe(true);
expect(result.env.NODE_OPTIONS).toContain('--trace-warnings');
expect(result.env.NODE_OPTIONS).toContain('chrome-devtools-auto-connect-patch.js');
});
it('materializes a JavaScript fallback patch when build output is missing', async () => {
const tmp = await fs.mkdtemp(path.join(os.tmpdir(), 'mcporter-cdpmcp-fallback-'));
const patchPath = resolveChromeDevtoolsCompatPatchPath([], tmp);
expect(patchPath).toBe(path.join(tmp, 'mcporter-chrome-devtools-auto-connect-patch.js'));
await expect(fs.readFile(patchPath!, 'utf8')).resolves.toContain('MCPORTER_DEVTOOLS_TIMEOUT_PATCH');
});
it('patches an npx .bin symlink target idempotently', async () => {
const tmp = await fs.mkdtemp(path.join(os.tmpdir(), 'mcporter-cdpmcp-'));
const packageBinDir = path.join(tmp, 'node_modules/chrome-devtools-mcp/build/src/bin');
const binDir = path.join(tmp, 'node_modules/.bin');
const contextPath = path.join(tmp, 'node_modules/chrome-devtools-mcp/build/src/McpContext.js');
const binPath = path.join(packageBinDir, 'chrome-devtools-mcp.js');
const shimPath = path.join(binDir, 'chrome-devtools-mcp');
await fs.mkdir(packageBinDir, { recursive: true });
await fs.mkdir(binDir, { recursive: true });
await fs.writeFile(binPath, '#!/usr/bin/env node\n');
await fs.writeFile(
contextPath,
`const NAVIGATION_TIMEOUT = 10_000;
async function detect(page, mcpPage) {
if (await page.hasDevTools()) {
mcpPage.devToolsPage = await page.openDevTools();
}
}
`
);
await fs.symlink('../chrome-devtools-mcp/build/src/bin/chrome-devtools-mcp.js', shimPath);
patchChromeDevtoolsMcp(shimPath);
patchChromeDevtoolsMcp(shimPath);
const patched = await fs.readFile(contextPath, 'utf8');
expect(patched.match(/MCPORTER_DEVTOOLS_TIMEOUT_PATCH/g)).toHaveLength(1);
expect(patched).toContain('mcporterWithTimeout(page.hasDevTools(), false)');
expect(patched).toContain('mcporterWithTimeout(page.openDevTools(), undefined)');
});
});