How to Resize the Chrome Window in Headless TestCafe
Use TestCafe’s window actions to set reliable Chrome dimensions in headless runs, avoid --window-size pitfalls, and fix common CI failures.
Use TestCafe’s window actions inside the test: call await t.resizeWindow(width, height) for explicit dimensions, await t.maximizeWindow() to fill the available screen, or await t.resizeWindowToFitDevice(deviceName) for a supported device. Launch headless Chrome with the chrome:headless alias.
TestCafe documents that headless mode still supports screenshots and browser-window resizing. The supported actions are more dependable than passing Chromium’s --window-size flag, which TestCafe documents as unreliable in native-automation and headless cases.
1. Set an exact Chrome window size
Create a JavaScript test and resize the window before asserting responsive behavior:
import { Selector } from 'testcafe';
fixture`Responsive layout`
.page`https://example.test`;
test('renders at a fixed browser size', async t => {
await t.resizeWindow(1280, 800);
const desktopNav = Selector('[data-testid="desktop-nav"]');
await t.expect(desktopNav.visible).ok();
});
Run it in headless Chrome:
testcafe "chrome:headless" tests/responsive.test.js
The width and height values are pixels for the browser window’s client area. Choose dimensions that represent the layout you need to verify, then assert behavior at that size. TestCafe’s test actions guide and window API reference document the resize actions.
2. Choose the right sizing action
| Goal | Action | Use it when |
|---|---|---|
| Known width and height | t.resizeWindow(width, height) |
A test must reproduce a specific responsive breakpoint or viewport. |
| Fill the available screen | t.maximizeWindow() |
The test should use the largest supported window in the local environment. |
| Match a named device | t.resizeWindowToFitDevice(deviceName) |
You want a device profile from TestCafe’s compatible-device list. |
These methods resize the browser window. They are separate from browser full-screen mode: t.maximizeWindow() does not activate full screen. For compatible browsers, full-screen mode uses the --start-fullscreen flag.
3. Run headless Chrome from the CLI or Runner API
CLI
testcafe "chrome:headless" tests/responsive.test.js
Runner API
import createTestCafe from 'testcafe';
const testcafe = await createTestCafe();
const runner = testcafe.createRunner();
try {
await runner
.src('tests/responsive.test.js')
.browsers('chrome:headless')
.run();
} finally {
await testcafe.close();
}
TestCafe’s Browsers guide documents the :headless suffix. You can also set a Chrome DevTools Protocol port when your environment requires one:
testcafe "chrome:headless:cdpPort=9223" tests/responsive.test.js
4. Reset dimensions between tests
TestCafe says browser windows retain their size between tests and fixtures. If tests require different dimensions, set the size in a hook or at the beginning of every test so order does not affect the result.
fixture`Responsive breakpoints`
.page`https://example.test`
.beforeEach(async t => {
await t.resizeWindow(1280, 800);
});
test('desktop layout', async t => {
await t.expect(Selector('[data-testid="desktop-nav"]').visible).ok();
});
test('mobile layout', async t => {
await t.resizeWindow(390, 844);
await t.expect(Selector('[data-testid="mobile-menu"]').visible).ok();
});
Use a hook when every test in a fixture shares a baseline. Still resize explicitly inside a test when that test changes dimensions.
5. Resize to a supported device
When the test is about a named device rather than arbitrary pixels, use resizeWindowToFitDevice with a device name from TestCafe’s compatible-device list.
fixture`Device layout`
.page`https://example.test`;
test('renders for a supported device', async t => {
await t.resizeWindowToFitDevice('iPhone X');
await t.expect(Selector('[data-testid="mobile-menu"]').visible).ok();
});
Device sizing is different from Chromium emulation. If you need touch input, orientation, or a mobile user agent, use TestCafe’s emulation configuration. TestCafe documents headless emulation with an alias such as chrome:headless:emulation:device=iphone X;cdpPort=9223 in its mobile and emulation guide.
6. Why --window-size is not the fix
Do not depend on a Chrome launch argument such as --window-size=1280,800 as your TestCafe resize mechanism. TestCafe’s browser guide records a known Chromium issue in native automation where Chromium-based browsers do not honor that flag. The TestCafe v3.6.0 release notes (April 25, 2024) also state that headless Chromium does not always honor --window-size.
Use t.resizeWindow after the browser starts. This keeps the sizing operation in the TestCafe test and makes the intended dimensions visible next to the assertions.
7. Environment limits and CI behavior
- Remote browsers: TestCafe documents that remote browser windows cannot be resized with these actions.
- Linux: a window manager that implements ICCCM/EWMH is required for browser-window operations.
- Windows: TestCafe lists .NET 4.0 or newer as a requirement.
- macOS: TestCafe documents window resizing as compatible out of the box.
- Headless mode: there is no visible GUI, but TestCafe still exposes the resize actions and screenshot support.
- User profiles: the
:userProfileoption is incompatible with:headless, emulation mode, and native automation.
CI hosts can differ in display-server and window-manager behavior. Treat the requested dimensions as the test input, and verify the installed TestCafe and Chrome versions when a provider produces different rendering.
8. A repeatable responsive-test pattern
Keep each breakpoint test independent and name the dimensions in the test so failures are diagnosable:
import { Selector } from 'testcafe';
const desktopNav = Selector('[data-testid="desktop-nav"]');
const mobileMenu = Selector('[data-testid="mobile-menu"]');
fixture`Responsive navigation`
.page`https://example.test`;
test('desktop at 1280x800', async t => {
await t.resizeWindow(1280, 800);
await t.expect(desktopNav.visible).ok();
await t.expect(mobileMenu.visible).notOk();
});
test('mobile at 390x844', async t => {
await t.resizeWindow(390, 844);
await t.expect(mobileMenu.visible).ok();
await t.expect(desktopNav.visible).notOk();
});
Use stable selectors and assert the behavior that changes at the breakpoint. Avoid asserting a screenshot’s exact pixels unless visual regression is the explicit purpose of the test.
9. Troubleshooting
Elements are hidden or TestCafe cannot find them
Cause: the current window is below the application’s responsive breakpoint, so the element is hidden or replaced.
Fix: resize before locating or asserting the element. For a layout that should fill the screen, try await t.maximizeWindow(). TestCafe’s FAQ connects missing elements with windows that are too small.
The window-size CLI flag has no effect
Cause: Chromium does not always honor --window-size in the documented native-automation and headless cases.
Fix: call await t.resizeWindow(width, height) in the test.
Resize works locally but fails on a remote provider
Cause: remote browser windows cannot be resized through TestCafe’s window actions.
Fix: configure the provider’s viewport or device capability, or run the resize-dependent test in a supported local browser environment.
Maximize does not enter full-screen mode
Cause: maximize fills the available screen; it does not invoke the browser’s full-screen API.
Fix: use the --start-fullscreen CLI flag when full-screen mode is required and supported.
Tests influence one another
Cause: TestCafe retains the browser size between tests and fixtures.
Fix: set a baseline in beforeEach or resize at the start of every test.
Headless Chrome behaves differently after an upgrade
Cause: TestCafe v3.6.0 adopted Chromium’s newer headless engine, which can change behavior. The release notes also mention automatic upgrades of insecure HTTP requests to HTTPS.
Fix: record TestCafe and Chrome versions in CI, review the release notes, and verify the test’s URL and expected layout under the installed versions.
10. Performance and reliability notes
- Headless mode avoids rendering a visible GUI and is suitable for CI, but resizing still depends on the host’s browser and window-management support.
- Resize once per test when possible. Repeated window changes add setup work and make failures harder to interpret.
- Use a small matrix of representative dimensions instead of every pixel width. Cover each breakpoint and the transitions where navigation or content changes.
- Keep dimensions, browser alias, TestCafe version, and Chrome version in CI logs so a layout failure can be reproduced.
- Do not claim that every host renders identical pixels at the same requested size; fonts, device scale, GPU settings, and browser versions can still affect screenshots.
11. Or skip the browser setup
If your goal is a clean screenshot rather than an interactive TestCafe assertion, ScreenshotNeo provides a website screenshot API. It accepts one GET request and returns PNG, JPEG, WebP, or PDF. The API can set any viewport, use device presets, apply retina scale, capture a full page or CSS-selected element, wait for a selector, delay, or network idle, and run custom CSS or JavaScript.
See the ScreenshotNeo API documentation for the request options. cURL:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
Python:
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)
Node.js:
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot failed: ${res.status}`);
const image = Buffer.from(await res.arrayBuffer());
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', image));
ScreenshotNeo removes cookie and consent banners, newsletter popups, and chat widgets before capture. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed as clean shots; response headers identify the page verdict and billing status. It also offers an MCP server with take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. The Free plan includes 1,000 shots per month without a card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.
12. FAQ
Can I resize headless Chrome without opening a GUI?
Yes. Run TestCafe with chrome:headless and call t.resizeWindow, t.maximizeWindow, or t.resizeWindowToFitDevice in the test.
What is the difference between resizing and emulation?
Resizing changes the browser window dimensions. Emulation can additionally model a device’s user agent, touch support, orientation, and other mobile behavior.
Should I use maximize for responsive tests?
Use maximize when the test is intentionally tied to the largest available local window. Use explicit width and height values for repeatable breakpoint tests.
Why do dimensions persist?
TestCafe retains the browser window size between tests and fixtures. Reset it in a hook when isolation matters.
Can a cloud browser resize with TestCafe?
TestCafe documents that remote browser windows cannot be resized with these actions. Configure the provider’s viewport instead.


