Fixes #491 When `--start-maximized` or `--window-size` is passed as a browser arg, Playwright's default viewport (1280x720) overrides the browser's own window sizing, making those flags have no effect on the page content. This change auto-detects those args and sets `viewport: null` so Playwright defers to the browser's window size. Explicit viewport values still take priority. Also allows `viewport: null` in the launch protocol for agents that want to disable viewport emulation directly.
This commit is contained in:
@@ -484,6 +484,42 @@ describe('BrowserManager', () => {
|
|||||||
expect(size?.width).toBe(1920);
|
expect(size?.width).toBe(1920);
|
||||||
expect(size?.height).toBe(1080);
|
expect(size?.height).toBe(1080);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should disable viewport when --start-maximized is in args', async () => {
|
||||||
|
const testBrowser = new BrowserManager();
|
||||||
|
await testBrowser.launch({ headless: true, args: ['--start-maximized'] });
|
||||||
|
const page = testBrowser.getPage();
|
||||||
|
expect(page.viewportSize()).toBeNull();
|
||||||
|
await testBrowser.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should disable viewport when --window-size is in args', async () => {
|
||||||
|
const testBrowser = new BrowserManager();
|
||||||
|
await testBrowser.launch({ headless: true, args: ['--window-size=800,600'] });
|
||||||
|
const page = testBrowser.getPage();
|
||||||
|
expect(page.viewportSize()).toBeNull();
|
||||||
|
await testBrowser.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should use default viewport when no window size args', async () => {
|
||||||
|
const testBrowser = new BrowserManager();
|
||||||
|
await testBrowser.launch({ headless: true });
|
||||||
|
const page = testBrowser.getPage();
|
||||||
|
expect(page.viewportSize()).toEqual({ width: 1280, height: 720 });
|
||||||
|
await testBrowser.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should use explicit viewport even with --start-maximized', async () => {
|
||||||
|
const testBrowser = new BrowserManager();
|
||||||
|
await testBrowser.launch({
|
||||||
|
headless: true,
|
||||||
|
args: ['--start-maximized'],
|
||||||
|
viewport: { width: 800, height: 600 },
|
||||||
|
});
|
||||||
|
const page = testBrowser.getPage();
|
||||||
|
expect(page.viewportSize()).toEqual({ width: 800, height: 600 });
|
||||||
|
await testBrowser.close();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('snapshot', () => {
|
describe('snapshot', () => {
|
||||||
|
|||||||
+17
-6
@@ -1156,7 +1156,6 @@ export class BrowserManager {
|
|||||||
|
|
||||||
const launcher =
|
const launcher =
|
||||||
browserType === 'firefox' ? firefox : browserType === 'webkit' ? webkit : chromium;
|
browserType === 'firefox' ? firefox : browserType === 'webkit' ? webkit : chromium;
|
||||||
const viewport = options.viewport ?? { width: 1280, height: 720 };
|
|
||||||
|
|
||||||
// Build base args array with file access flags if enabled
|
// Build base args array with file access flags if enabled
|
||||||
// --allow-file-access-from-files: allows file:// URLs to read other file:// URLs via XHR/fetch
|
// --allow-file-access-from-files: allows file:// URLs to read other file:// URLs via XHR/fetch
|
||||||
@@ -1170,6 +1169,18 @@ export class BrowserManager {
|
|||||||
? fileAccessArgs
|
? fileAccessArgs
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
|
// Auto-detect args that control window size and disable viewport emulation
|
||||||
|
// so Playwright doesn't override the browser's own sizing behavior
|
||||||
|
const hasWindowSizeArgs = baseArgs?.some(
|
||||||
|
(arg) => arg === '--start-maximized' || arg.startsWith('--window-size=')
|
||||||
|
);
|
||||||
|
const viewport =
|
||||||
|
options.viewport !== undefined
|
||||||
|
? options.viewport
|
||||||
|
: hasWindowSizeArgs
|
||||||
|
? null
|
||||||
|
: { width: 1280, height: 720 };
|
||||||
|
|
||||||
let context: BrowserContext;
|
let context: BrowserContext;
|
||||||
if (hasExtensions) {
|
if (hasExtensions) {
|
||||||
// Extensions require persistent context in a temp directory
|
// Extensions require persistent context in a temp directory
|
||||||
@@ -1597,16 +1608,16 @@ export class BrowserManager {
|
|||||||
/**
|
/**
|
||||||
* Create a new window (new context)
|
* Create a new window (new context)
|
||||||
*/
|
*/
|
||||||
async newWindow(viewport?: {
|
async newWindow(viewport?: { width: number; height: number } | null): Promise<{
|
||||||
width: number;
|
index: number;
|
||||||
height: number;
|
total: number;
|
||||||
}): Promise<{ index: number; total: number }> {
|
}> {
|
||||||
if (!this.browser) {
|
if (!this.browser) {
|
||||||
throw new Error('Browser not launched');
|
throw new Error('Browser not launched');
|
||||||
}
|
}
|
||||||
|
|
||||||
const context = await this.browser.newContext({
|
const context = await this.browser.newContext({
|
||||||
viewport: viewport ?? { width: 1280, height: 720 },
|
viewport: viewport === undefined ? { width: 1280, height: 720 } : viewport,
|
||||||
});
|
});
|
||||||
context.setDefaultTimeout(60000);
|
context.setDefaultTimeout(60000);
|
||||||
this.contexts.push(context);
|
this.contexts.push(context);
|
||||||
|
|||||||
@@ -598,6 +598,31 @@ describe('parseCommand', () => {
|
|||||||
expect(result.command.allowFileAccess).toBe(false);
|
expect(result.command.allowFileAccess).toBe(false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should parse launch with viewport dimensions', () => {
|
||||||
|
const result = parseCommand(
|
||||||
|
cmd({ id: '1', action: 'launch', viewport: { width: 1920, height: 1080 } })
|
||||||
|
);
|
||||||
|
expect(result.success).toBe(true);
|
||||||
|
if (result.success) {
|
||||||
|
expect(result.command.viewport).toEqual({ width: 1920, height: 1080 });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should parse launch with viewport null', () => {
|
||||||
|
const result = parseCommand(cmd({ id: '1', action: 'launch', viewport: null }));
|
||||||
|
expect(result.success).toBe(true);
|
||||||
|
if (result.success) {
|
||||||
|
expect(result.command.viewport).toBeNull();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should reject launch with invalid viewport', () => {
|
||||||
|
const result = parseCommand(
|
||||||
|
cmd({ id: '1', action: 'launch', viewport: { width: -1, height: 720 } })
|
||||||
|
);
|
||||||
|
expect(result.success).toBe(false);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('mouse actions', () => {
|
describe('mouse actions', () => {
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ const launchSchema = baseCommandSchema.extend({
|
|||||||
width: z.number().positive(),
|
width: z.number().positive(),
|
||||||
height: z.number().positive(),
|
height: z.number().positive(),
|
||||||
})
|
})
|
||||||
|
.nullable()
|
||||||
.optional(),
|
.optional(),
|
||||||
browser: z.enum(['chromium', 'firefox', 'webkit']).optional(),
|
browser: z.enum(['chromium', 'firefox', 'webkit']).optional(),
|
||||||
cdpPort: z.number().positive().optional(),
|
cdpPort: z.number().positive().optional(),
|
||||||
@@ -822,6 +823,7 @@ const windowNewSchema = baseCommandSchema.extend({
|
|||||||
width: z.number().positive(),
|
width: z.number().positive(),
|
||||||
height: z.number().positive(),
|
height: z.number().positive(),
|
||||||
})
|
})
|
||||||
|
.nullable()
|
||||||
.optional(),
|
.optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -10,7 +10,7 @@ export interface BaseCommand {
|
|||||||
export interface LaunchCommand extends BaseCommand {
|
export interface LaunchCommand extends BaseCommand {
|
||||||
action: 'launch';
|
action: 'launch';
|
||||||
headless?: boolean;
|
headless?: boolean;
|
||||||
viewport?: { width: number; height: number };
|
viewport?: { width: number; height: number } | null;
|
||||||
browser?: 'chromium' | 'firefox' | 'webkit';
|
browser?: 'chromium' | 'firefox' | 'webkit';
|
||||||
headers?: Record<string, string>;
|
headers?: Record<string, string>;
|
||||||
executablePath?: string;
|
executablePath?: string;
|
||||||
@@ -850,7 +850,7 @@ export interface TabCloseCommand extends BaseCommand {
|
|||||||
|
|
||||||
export interface WindowNewCommand extends BaseCommand {
|
export interface WindowNewCommand extends BaseCommand {
|
||||||
action: 'window_new';
|
action: 'window_new';
|
||||||
viewport?: { width: number; height: number };
|
viewport?: { width: number; height: number } | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Union of all command types
|
// Union of all command types
|
||||||
|
|||||||
Reference in New Issue
Block a user