Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/playwright-core/src/server/har/harTracer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,8 @@ export class HarTracer {

if (request._failureText !== null)
harEntry.response._failureText = request._failureText;
if (harEntry._monotonicTime && harEntry.time === -1)
harEntry.time = monotonicTime() - harEntry._monotonicTime;
Comment thread
pavelfeldman marked this conversation as resolved.
this._recordRequestOverrides(harEntry, request);
if (this._started)
this._delegate.onEntryFinished(harEntry);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ The `action` command displays available snapshot phases (before, input, after) a
### Requests

```bash
# All network requests: method, status, URL, duration, size
# All network requests: start time (on the `trace actions` clock), method, status, URL, duration, size
npx playwright trace requests

# Filter by URL pattern
Expand Down
11 changes: 7 additions & 4 deletions packages/playwright-core/src/tools/trace/traceRequests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

import path from 'path';
import { msToString } from '@isomorphic/formatUtils';
import { loadTrace } from './traceUtils';
import { loadTrace, formatTimestamp } from './traceUtils';

export async function traceRequests(options: { grep?: string, method?: string, status?: string, failed?: boolean }) {
const trace = await loadTrace();
Expand All @@ -44,8 +44,8 @@ export async function traceRequests(options: { grep?: string, method?: string, s
console.log(' No network requests');
return;
}
console.log(` ${'#'.padStart(4)} ${'Method'.padEnd(8)} ${'Status'.padEnd(8)} ${'Name'.padEnd(45)} ${'Duration'.padStart(10)} ${'Size'.padStart(8)} ${'Route'.padEnd(10)}`);
console.log(` ${'─'.repeat(4)} ${'─'.repeat(8)} ${'─'.repeat(8)} ${'─'.repeat(45)} ${'─'.repeat(10)} ${'─'.repeat(8)} ${'─'.repeat(10)}`);
console.log(` ${'#'.padStart(4)} ${'Start'.padEnd(9)} ${'Method'.padEnd(8)} ${'Status'.padEnd(8)} ${'Name'.padEnd(45)} ${'Duration'.padStart(10)} ${'Size'.padStart(8)} ${'Route'.padEnd(10)}`);
console.log(` ${'─'.repeat(4)} ${'─'.repeat(9)} ${'─'.repeat(8)} ${'─'.repeat(8)} ${'─'.repeat(45)} ${'─'.repeat(10)} ${'─'.repeat(8)} ${'─'.repeat(10)}`);

for (const { resource: r, ordinal } of indexed) {
let name: string;
Expand All @@ -65,7 +65,8 @@ export async function traceRequests(options: { grep?: string, method?: string, s
const status = r.response.status > 0 ? String(r.response.status) : 'ERR';
const size = r.response._transferSize! > 0 ? r.response._transferSize! : r.response.bodySize;
const route = formatRouteStatus(r);
console.log(` ${(ordinal + '.').padStart(4)} ${r.request.method.padEnd(8)} ${status.padEnd(8)} ${name.padEnd(45)} ${msToString(r.time).padStart(10)} ${bytesToString(size).padStart(8)} ${route.padEnd(10)}`);
const start = r._monotonicTime ? formatTimestamp(r._monotonicTime, model.startTime) : '-';
Comment thread
pavelfeldman marked this conversation as resolved.
console.log(` ${(ordinal + '.').padStart(4)} ${start.padEnd(9)} ${r.request.method.padEnd(8)} ${status.padEnd(8)} ${name.padEnd(45)} ${msToString(r.time).padStart(10)} ${bytesToString(size).padStart(8)} ${route.padEnd(10)}`);
}
}

Expand Down Expand Up @@ -94,6 +95,8 @@ export async function traceRequest(requestId: string) {
// General
console.log(' General');
console.log(` status: ${status}`);
if (r._monotonicTime)
console.log(` start: ${formatTimestamp(r._monotonicTime, model.startTime)}`);
console.log(` duration: ${msToString(r.time)}`);
console.log(` size: ${bytesToString(size)}`);
if (r.response.content.mimeType)
Expand Down
2 changes: 2 additions & 0 deletions tests/library/tracing.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,8 @@ test('should record network failures', async ({ context, page, server }, testInf
const { events } = await parseTraceRaw(testInfo.outputPath('trace1.zip'));
const requestEvent = events.find(e => e.type === 'resource-snapshot' && !!e.snapshot.response._failureText);
expect(requestEvent).toBeTruthy();
expect(requestEvent.snapshot._monotonicTime).toBeGreaterThan(0);
Comment thread
pavelfeldman marked this conversation as resolved.
expect(requestEvent.snapshot.time).toBeGreaterThanOrEqual(0);
});

test('should not crash when browser closes mid-trace', async ({ browserType, server }, testInfo) => {
Expand Down
4 changes: 4 additions & 0 deletions tests/mcp/trace-cli-fixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,10 @@ export const test = baseTest
// Fetch
await page.evaluate(() => fetch('/feedback', { method: 'POST', body: 'What a great product!' }).then(res => res.text()));

// Aborted fetch
await page.route('**/blocked', route => route.abort());
await page.evaluate(() => fetch('/blocked').catch(() => {}));

// Navigate to another page
await page.locator('a').click();

Expand Down
13 changes: 13 additions & 0 deletions tests/mcp/trace-cli.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,18 @@ test('trace requests shows requests with ordinals', async ({ runTraceCli }) => {
expect(stdout).toMatch(/\d+\./);
});

test('trace requests shows start times and aborted request durations', async ({ runTraceCli }) => {
const { stdout, exitCode } = await runTraceCli(['requests']);
expect(exitCode).toBe(0);
expect(stdout).toContain('Start');
// Every request row carries a start timestamp in the `trace actions` Time format.
expect(stdout).toMatch(/\d+\.\s+\d+:\d{2}\.\d{3}\s/);
// The aborted request has a recorded duration rather than '-'.
const abortedRow = stdout.split('\n').find(line => line.includes('aborted'))!;
expect(abortedRow).toContain('blocked');
expect(abortedRow).toMatch(/\s\d+(\.\d+)?m?s\s/);
});

test('trace requests --method filters', async ({ runTraceCli }) => {
const { stdout, exitCode } = await runTraceCli(['requests', '--method', 'GET']);
expect(exitCode).toBe(0);
Expand All @@ -105,6 +117,7 @@ test('trace request shows details', async ({ runTraceCli }) => {
expect(exitCode).toBe(0);
expect(stdout).toContain('General');
expect(stdout).toContain('status:');
expect(stdout).toMatch(/start:\s+\d+:\d{2}\.\d{3}/);
expect(stdout).toContain('Request headers');
expect(stdout).toContain('Response headers');
});
Expand Down
Loading