Files
AI/참고/playwright-main/tests/mcp/trace-cli.spec.ts
2026-05-12 19:40:31 +09:00

261 lines
11 KiB
TypeScript

/**
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import fs from 'fs';
import path from 'path';
import { test, expect } from './trace-cli-fixtures';
test.skip(({ mcpBrowser }) => mcpBrowser !== 'chrome', 'Chrome-only');
test('trace open shows metadata', async ({ traceFile, runTraceCli }) => {
const { stdout, exitCode } = await runTraceCli(['open', traceFile]);
expect(exitCode).toBe(0);
expect(stdout).toContain('Browser:');
expect(stdout).toContain('chromium');
expect(stdout).toContain('Viewport:');
expect(stdout).toContain('800x600');
expect(stdout).toContain('Actions:');
expect(stdout).toContain('Pages:');
expect(stdout).toContain('Network:');
});
test('trace actions shows actions with ordinals', async ({ runTraceCli }) => {
const { stdout, exitCode } = await runTraceCli(['actions']);
expect(exitCode).toBe(0);
// Should have ordinal numbers
expect(stdout).toMatch(/^\s+\d+\.\s/m);
// Should have formatted action titles
expect(stdout).toContain('Navigate');
expect(stdout).toContain('Click');
expect(stdout).toContain('Fill');
});
test('trace actions --grep filters actions', async ({ runTraceCli }) => {
const { stdout, exitCode } = await runTraceCli(['actions', '--grep', 'Click']);
expect(exitCode).toBe(0);
expect(stdout).toContain('Click');
expect(stdout).not.toContain('Navigate');
expect(stdout).not.toContain('Fill');
});
test('trace action displays action details', async ({ runTraceCli }) => {
// First get an action ordinal from list
const { stdout: listOutput } = await runTraceCli(['actions', '--grep', 'Navigate']);
const match = listOutput.match(/^\s+(\d+)\.\s/m);
expect(match).toBeTruthy();
const { stdout, exitCode } = await runTraceCli(['action', match![1]]);
expect(exitCode).toBe(0);
expect(stdout).toContain('Navigate');
expect(stdout).toContain('Time');
expect(stdout).toContain('start:');
expect(stdout).toContain('duration:');
expect(stdout).toContain('Parameters');
});
test('trace action reports available snapshots', async ({ runTraceCli }) => {
const { stdout: listOutput } = await runTraceCli(['actions', '--grep', 'Click']);
const match = listOutput.match(/^\s+(\d+)\.\s/m);
expect(match).toBeTruthy();
const { stdout, exitCode } = await runTraceCli(['action', match![1]]);
expect(exitCode).toBe(0);
expect(stdout).toContain('Snapshots');
expect(stdout).toContain('available:');
});
test('trace action with invalid action ID', async ({ runTraceCli }) => {
const { stderr, exitCode } = await runTraceCli(['action', '999999']);
expect(exitCode).toBe(1);
expect(stderr).toContain('not found');
});
test('trace requests shows requests with ordinals', async ({ runTraceCli }) => {
const { stdout, exitCode } = await runTraceCli(['requests']);
expect(exitCode).toBe(0);
expect(stdout).toContain('Method');
expect(stdout).toContain('Status');
expect(stdout).toContain('GET');
expect(stdout).toMatch(/\d+\./);
});
test('trace requests --method filters', async ({ runTraceCli }) => {
const { stdout, exitCode } = await runTraceCli(['requests', '--method', 'GET']);
expect(exitCode).toBe(0);
expect(stdout).toContain('GET');
expect(stdout).not.toContain('POST');
});
test('trace request shows details', async ({ runTraceCli }) => {
const { stdout, exitCode } = await runTraceCli(['request', '1']);
expect(exitCode).toBe(0);
expect(stdout).toContain('General');
expect(stdout).toContain('status:');
expect(stdout).toContain('Request headers');
expect(stdout).toContain('Response headers');
});
test('trace request shows request and response body', async ({ traceCwd, runTraceCli }) => {
const { stdout, exitCode } = await runTraceCli(['requests', '--grep', 'feedback']);
expect(exitCode).toBe(0);
expect(stdout).toContain('feedback');
const ordinal = stdout.match(/^\s+(\d+)\.\s/m)![1];
const { stdout: requestOutput, exitCode: requestExitCode } = await runTraceCli(['request', ordinal]);
expect(requestExitCode).toBe(0);
expect(requestOutput).toContain('Request body');
const requestBodyPath = path.join('.playwright-cli', 'trace', 'resources', '60e4c013c41de11280ed8ba99dd94144124a0c35.txt');
expect(requestOutput).toContain(' ' + requestBodyPath);
expect(fs.readFileSync(path.join(traceCwd, requestBodyPath), 'utf-8')).toEqual('What a great product!');
expect(requestOutput).toContain('Response body');
expect(requestOutput).toContain('application/json');
const responseBodyPath = path.join('.playwright-cli', 'trace', 'resources', '030b61c2fb7042fb4fc0ef4287a4ae3c0d98ed68.json');
expect(requestOutput).toContain(' ' + responseBodyPath);
expect(fs.readFileSync(path.join(traceCwd, responseBodyPath), 'utf-8')).toEqual(JSON.stringify({ received: true }));
});
test('trace request with invalid ID', async ({ runTraceCli }) => {
const { stderr, exitCode } = await runTraceCli(['request', '999999']);
expect(exitCode).toBe(1);
expect(stderr).toContain('not found');
});
test('trace console shows messages', async ({ runTraceCli }) => {
const { stdout, exitCode } = await runTraceCli(['console']);
expect(exitCode).toBe(0);
expect(stdout).toContain('info message');
expect(stdout).toContain('warning message');
expect(stdout).toContain('error message');
});
test('trace console --errors-only', async ({ runTraceCli }) => {
const { stdout, exitCode } = await runTraceCli(['console', '--errors-only']);
expect(exitCode).toBe(0);
expect(stdout).toContain('error message');
expect(stdout).not.toContain('info message');
});
test('trace errors', async ({ runTraceCli }) => {
const { stdout, exitCode } = await runTraceCli(['errors']);
expect(exitCode).toBe(0);
// Our test trace may or may not have errors, just verify it doesn't crash
expect(stdout).toBeTruthy();
});
test('trace snapshot runs command on snapshot', async ({ runTraceCli }) => {
const { stdout: listOutput } = await runTraceCli(['actions', '--grep', 'Navigate']);
const match = listOutput.match(/^\s+(\d+)\.\s/m);
expect(match).toBeTruthy();
const { stdout, exitCode } = await runTraceCli(['snapshot', match![1]]);
expect(exitCode).toBe(0);
expect(stdout).toBeTruthy();
});
test('trace snapshot --name before', async ({ runTraceCli }) => {
const { stdout: listOutput } = await runTraceCli(['actions', '--grep', 'Click']);
const match = listOutput.match(/^\s+(\d+)\.\s/m);
expect(match).toBeTruthy();
const { stdout, exitCode } = await runTraceCli(['snapshot', '--name', 'before', match![1]]);
expect(exitCode).toBe(0);
expect(stdout).toBeTruthy();
});
test('trace snapshot resolves inner frames', async ({ runTraceCli }) => {
const { stdout: listOutput } = await runTraceCli(['actions', '--grep', 'Click']);
const ordinals = [...listOutput.matchAll(/^\s+(\d+)\.\s/gm)].map(m => m[1]);
expect(ordinals.length).toBeGreaterThanOrEqual(2);
const anchorClickOrdinal = ordinals[ordinals.length - 1];
const { stdout } = await runTraceCli(['snapshot', '--name', 'after', anchorClickOrdinal]);
expect(stdout).toContain('Innermost');
});
test('trace snapshot replays sub-resource stylesheets from the archive', async ({ runTraceCli }) => {
const { stdout: listOutput } = await runTraceCli(['actions', '--grep', 'Click']);
const match = listOutput.match(/^\s+(\d+)\.\s/m);
expect(match).toBeTruthy();
const ordinal = match![1];
const { stdout, exitCode } = await runTraceCli([
'snapshot', '--name', 'before', ordinal,
'--', 'eval', 'el => getComputedStyle(el).color', '#styled',
]);
expect(exitCode).toBe(0);
expect(stdout).toContain('rgb(255, 0, 0)');
});
test('trace screenshot saves image file', async ({ runTraceCli }, testInfo) => {
const { stdout: listOutput } = await runTraceCli(['actions', '--grep', 'Navigate']);
const match = listOutput.match(/^\s+(\d+)\.\s/m);
expect(match).toBeTruthy();
const outPath = testInfo.outputPath('test-screenshot.png');
const { stdout, exitCode } = await runTraceCli(['screenshot', match![1], '-o', outPath]);
// Screenshot may or may not be available depending on timing
if (exitCode === 0) {
expect(stdout).toContain('Screenshot saved to');
expect(fs.existsSync(outPath)).toBe(true);
}
});
test('trace close removes extracted trace', async ({ traceFile, runTraceCli }) => {
const { exitCode } = await runTraceCli(['close']);
expect(exitCode).toBe(0);
// Subsequent commands should fail because trace is closed.
const { stderr, exitCode: exitCode2 } = await runTraceCli(['actions']);
expect(exitCode2).toBe(1);
expect(stderr).toContain('No trace opened');
// Re-open for remaining tests.
const { exitCode: exitCode3 } = await runTraceCli(['open', traceFile]);
expect(exitCode3).toBe(0);
});
test('trace open with .trace file creates link', async ({ traceFile, traceCwd, runTraceCli }) => {
// Extract the zip using the CLI to get raw trace files.
const extractCwd = path.join(path.dirname(traceFile), 'extracted');
fs.mkdirSync(extractCwd, { recursive: true });
const cliPath = path.resolve(__dirname, '../../packages/playwright-core/cli.js');
const { execFileSync } = require('child_process');
execFileSync(process.execPath, [cliPath, 'trace', 'open', traceFile], { cwd: extractCwd });
const extractDir = path.join(extractCwd, '.playwright-cli', 'trace');
const traceEntries = fs.readdirSync(extractDir).filter((f: string) => f.endsWith('.trace'));
expect(traceEntries.length).toBeGreaterThan(0);
const dotTraceFile = path.join(extractDir, traceEntries[0]);
const { exitCode } = await runTraceCli(['open', dotTraceFile]);
expect(exitCode).toBe(0);
const linkFile = path.join(traceCwd, '.playwright-cli', 'trace', '.link');
expect(fs.existsSync(linkFile)).toBe(true);
expect(fs.readFileSync(linkFile, 'utf-8')).toBe(dotTraceFile);
const { stdout: actionsOutput, exitCode: actionsExitCode } = await runTraceCli(['actions']);
expect(actionsExitCode).toBe(0);
expect(actionsOutput).toContain('Navigate');
expect(actionsOutput).toContain('Click');
});
test('trace attachments lists attachments', async ({ runTraceCli }) => {
const { stdout, exitCode } = await runTraceCli(['attachments']);
expect(exitCode).toBe(0);
// Our test trace has no attachments, just verify it doesn't crash
expect(stdout).toBeTruthy();
});