Skip to content
Open
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: 1 addition & 1 deletion packages/playwright-core/src/tools/backend/pdf.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { formatObject } from '@isomorphic/stringUtils';
import { defineTabTool } from './tool';

const pdfSchema = z.object({
filename: z.string().optional().describe('File name to save the pdf to. Defaults to `page-{timestamp}.pdf` if not specified. Prefer relative file names to stay within the output directory.'),
filename: z.string().optional().describe('File name to save the pdf to. Defaults to `page-{timestamp}.pdf` in the output directory if not specified. An explicit name is resolved against the working directory, not the output directory.'),
});

const pdf = defineTabTool({
Expand Down
8 changes: 6 additions & 2 deletions packages/playwright-core/src/tools/backend/response.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,10 +87,14 @@ export class Response {

async resolveClientFile(template: FilenameTemplate, title: string): Promise<ResolvedFile> {
let fileName: string;
if (template.suggestedFilename)
if (template.suggestedFilename) {
fileName = await this.resolveClientFilename(template.suggestedFilename);
else
// outputFile() creates the parent directory for auto-named files, do the same
// for an explicit one so that "sub/page.png" does not fail with ENOENT.
await fs.promises.mkdir(path.dirname(fileName), { recursive: true });
} else {
fileName = await this._context.outputFile(template, { origin: 'llm' });
}
const relativeName = this._computeRelativeTo(fileName);
const printableLink = `- [${title}](${relativeName})`;
return { fileName, relativeName, printableLink };
Expand Down
2 changes: 1 addition & 1 deletion packages/playwright-core/src/tools/backend/screenshot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ type ImageFormat = 'png' | 'jpeg' | 'webp';

const screenshotSchema = optionalElementSchema.extend({
type: z.enum(['png', 'jpeg', 'webp']).optional().describe('Image format for the screenshot. If unset, inferred from the filename extension, otherwise png.'),
filename: z.string().optional().describe('File name to save the screenshot to. Defaults to `page-{timestamp}.{png|jpeg|webp}` if not specified. Prefer relative file names to stay within the output directory.'),
filename: z.string().optional().describe('File name to save the screenshot to. Defaults to `page-{timestamp}.{png|jpeg|webp}` in the output directory if not specified. An explicit name is resolved against the working directory, not the output directory.'),
fullPage: z.boolean().optional().describe('When true, takes a screenshot of the full scrollable page, instead of the currently visible viewport. Cannot be used with element screenshots.'),
scale: z.enum(['css', 'device']).default('css').describe('Image resolution scale. "css" produces a screenshot sized in CSS pixels (smaller, consistent across devices). "device" produces a high-resolution screenshot using device pixels (larger, accounts for the device pixel ratio). Default is css.'),
});
Expand Down
20 changes: 20 additions & 0 deletions tests/mcp/screenshot.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,26 @@ test('browser_take_screenshot (filename: "output.png")', async ({ client, server
expect(files[0]).toMatch(/^output\.png$/);
});

test('browser_take_screenshot (filename with a subdirectory)', async ({ client, server }, testInfo) => {
expect(await client.callTool({
name: 'browser_navigate',
arguments: { url: server.HELLO_WORLD },
})).toHaveResponse({
code: expect.stringContaining(`page.goto('http://localhost`),
});

expect(await client.callTool({
name: 'browser_take_screenshot',
arguments: {
filename: 'sub/dir/output.png',
},
})).toHaveResponse({
result: expect.stringContaining('output.png'),
});

expect(fs.existsSync(testInfo.outputPath('sub', 'dir', 'output.png'))).toBeTruthy();
});

test('browser_take_screenshot (imageResponses=omit)', async ({ startClient, server }, testInfo) => {
const outputDir = testInfo.outputPath('output');
const { client } = await startClient({
Expand Down