From e26c709ae250b51330d3a7edd651aa3225c5a0b3 Mon Sep 17 00:00:00 2001 From: Pavel Feldman Date: Tue, 17 Mar 2026 11:46:59 -0700 Subject: [PATCH] feat(devices): add Native device descriptors with platform-aware user agents Add "Native" variants of Desktop Chrome, Edge, and Firefox device descriptors that resolve the user agent platform tag based on the current OS instead of hardcoding Windows. --- packages/playwright-client/types/types.d.ts | 6 ++ .../src/server/deviceDescriptors.ts | 21 ++++- .../src/server/deviceDescriptorsSource.json | 90 +++++++++++++++++++ packages/playwright-core/types/types.d.ts | 6 ++ tests/library/browsercontext-device.spec.ts | 19 ++++ 5 files changed, 141 insertions(+), 1 deletion(-) diff --git a/packages/playwright-client/types/types.d.ts b/packages/playwright-client/types/types.d.ts index 3f8e9d677c7cd..a2efee74ab6b9 100644 --- a/packages/playwright-client/types/types.d.ts +++ b/packages/playwright-client/types/types.d.ts @@ -23565,12 +23565,18 @@ type Devices = { "Moto G4": DeviceDescriptor; "Moto G4 landscape": DeviceDescriptor; "Desktop Chrome HiDPI": DeviceDescriptor; + "Desktop Chrome HiDPI Native": DeviceDescriptor; "Desktop Edge HiDPI": DeviceDescriptor; + "Desktop Edge HiDPI Native": DeviceDescriptor; "Desktop Firefox HiDPI": DeviceDescriptor; + "Desktop Firefox HiDPI Native": DeviceDescriptor; "Desktop Safari": DeviceDescriptor; "Desktop Chrome": DeviceDescriptor; + "Desktop Chrome Native": DeviceDescriptor; "Desktop Edge": DeviceDescriptor; + "Desktop Edge Native": DeviceDescriptor; "Desktop Firefox": DeviceDescriptor; + "Desktop Firefox Native": DeviceDescriptor; [key: string]: DeviceDescriptor; } diff --git a/packages/playwright-core/src/server/deviceDescriptors.ts b/packages/playwright-core/src/server/deviceDescriptors.ts index c4ad1f73fa747..725d2c4edd31f 100644 --- a/packages/playwright-core/src/server/deviceDescriptors.ts +++ b/packages/playwright-core/src/server/deviceDescriptors.ts @@ -19,5 +19,24 @@ import deviceDescriptorsSource from './deviceDescriptorsSource.json'; import type { Devices } from './types'; +function platformTag(): string { + switch (process.platform) { + case 'darwin': return 'Macintosh; Intel Mac OS X 10_15_7'; + case 'linux': return 'X11; Linux x86_64'; + default: return 'Windows NT 10.0; Win64; x64'; + } +} -export const deviceDescriptors = deviceDescriptorsSource as Devices; +function resolveDescriptors(source: Devices): Devices { + const platform = platformTag(); + const result: Devices = {}; + for (const [name, descriptor] of Object.entries(source)) { + if (descriptor.userAgent.includes('{{platform}}')) + result[name] = { ...descriptor, userAgent: descriptor.userAgent.replace('{{platform}}', platform) }; + else + result[name] = descriptor; + } + return result; +} + +export const deviceDescriptors = resolveDescriptors(deviceDescriptorsSource as Devices); diff --git a/packages/playwright-core/src/server/deviceDescriptorsSource.json b/packages/playwright-core/src/server/deviceDescriptorsSource.json index 40d56122332a7..d37e5596a6f95 100644 --- a/packages/playwright-core/src/server/deviceDescriptorsSource.json +++ b/packages/playwright-core/src/server/deviceDescriptorsSource.json @@ -1686,6 +1686,21 @@ "hasTouch": false, "defaultBrowserType": "chromium" }, + "Desktop Chrome HiDPI Native": { + "userAgent": "Mozilla/5.0 ({{platform}}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.7680.31 Safari/537.36", + "screen": { + "width": 1792, + "height": 1120 + }, + "viewport": { + "width": 1280, + "height": 720 + }, + "deviceScaleFactor": 2, + "isMobile": false, + "hasTouch": false, + "defaultBrowserType": "chromium" + }, "Desktop Edge HiDPI": { "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.7680.31 Safari/537.36 Edg/146.0.7680.31", "screen": { @@ -1701,6 +1716,21 @@ "hasTouch": false, "defaultBrowserType": "chromium" }, + "Desktop Edge HiDPI Native": { + "userAgent": "Mozilla/5.0 ({{platform}}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.7680.31 Safari/537.36 Edg/146.0.7680.31", + "screen": { + "width": 1792, + "height": 1120 + }, + "viewport": { + "width": 1280, + "height": 720 + }, + "deviceScaleFactor": 2, + "isMobile": false, + "hasTouch": false, + "defaultBrowserType": "chromium" + }, "Desktop Firefox HiDPI": { "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:148.0.2) Gecko/20100101 Firefox/148.0.2", "screen": { @@ -1716,6 +1746,21 @@ "hasTouch": false, "defaultBrowserType": "firefox" }, + "Desktop Firefox HiDPI Native": { + "userAgent": "Mozilla/5.0 ({{platform}}; rv:148.0.2) Gecko/20100101 Firefox/148.0.2", + "screen": { + "width": 1792, + "height": 1120 + }, + "viewport": { + "width": 1280, + "height": 720 + }, + "deviceScaleFactor": 2, + "isMobile": false, + "hasTouch": false, + "defaultBrowserType": "firefox" + }, "Desktop Safari": { "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.0 Safari/605.1.15", "screen": { @@ -1746,6 +1791,21 @@ "hasTouch": false, "defaultBrowserType": "chromium" }, + "Desktop Chrome Native": { + "userAgent": "Mozilla/5.0 ({{platform}}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.7680.31 Safari/537.36", + "screen": { + "width": 1920, + "height": 1080 + }, + "viewport": { + "width": 1280, + "height": 720 + }, + "deviceScaleFactor": 1, + "isMobile": false, + "hasTouch": false, + "defaultBrowserType": "chromium" + }, "Desktop Edge": { "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.7680.31 Safari/537.36 Edg/146.0.7680.31", "screen": { @@ -1761,6 +1821,21 @@ "hasTouch": false, "defaultBrowserType": "chromium" }, + "Desktop Edge Native": { + "userAgent": "Mozilla/5.0 ({{platform}}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.7680.31 Safari/537.36 Edg/146.0.7680.31", + "screen": { + "width": 1920, + "height": 1080 + }, + "viewport": { + "width": 1280, + "height": 720 + }, + "deviceScaleFactor": 1, + "isMobile": false, + "hasTouch": false, + "defaultBrowserType": "chromium" + }, "Desktop Firefox": { "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:148.0.2) Gecko/20100101 Firefox/148.0.2", "screen": { @@ -1775,5 +1850,20 @@ "isMobile": false, "hasTouch": false, "defaultBrowserType": "firefox" + }, + "Desktop Firefox Native": { + "userAgent": "Mozilla/5.0 ({{platform}}; rv:148.0.2) Gecko/20100101 Firefox/148.0.2", + "screen": { + "width": 1920, + "height": 1080 + }, + "viewport": { + "width": 1280, + "height": 720 + }, + "deviceScaleFactor": 1, + "isMobile": false, + "hasTouch": false, + "defaultBrowserType": "firefox" } } \ No newline at end of file diff --git a/packages/playwright-core/types/types.d.ts b/packages/playwright-core/types/types.d.ts index 3f8e9d677c7cd..a2efee74ab6b9 100644 --- a/packages/playwright-core/types/types.d.ts +++ b/packages/playwright-core/types/types.d.ts @@ -23565,12 +23565,18 @@ type Devices = { "Moto G4": DeviceDescriptor; "Moto G4 landscape": DeviceDescriptor; "Desktop Chrome HiDPI": DeviceDescriptor; + "Desktop Chrome HiDPI Native": DeviceDescriptor; "Desktop Edge HiDPI": DeviceDescriptor; + "Desktop Edge HiDPI Native": DeviceDescriptor; "Desktop Firefox HiDPI": DeviceDescriptor; + "Desktop Firefox HiDPI Native": DeviceDescriptor; "Desktop Safari": DeviceDescriptor; "Desktop Chrome": DeviceDescriptor; + "Desktop Chrome Native": DeviceDescriptor; "Desktop Edge": DeviceDescriptor; + "Desktop Edge Native": DeviceDescriptor; "Desktop Firefox": DeviceDescriptor; + "Desktop Firefox Native": DeviceDescriptor; [key: string]: DeviceDescriptor; } diff --git a/tests/library/browsercontext-device.spec.ts b/tests/library/browsercontext-device.spec.ts index c36acda6fe364..b161ad52eea97 100644 --- a/tests/library/browsercontext-device.spec.ts +++ b/tests/library/browsercontext-device.spec.ts @@ -104,6 +104,25 @@ it.describe('device', () => { await context.close(); }); + it('should resolve native device user agent to current platform', async ({ playwright, browser, server }) => { + const device = playwright.devices['Desktop Chrome Native']; + expect(device.userAgent).not.toContain('{{platform}}'); + const expectedPlatform = { + 'darwin': 'Macintosh; Intel Mac OS X 10_15_7', + 'linux': 'X11; Linux x86_64', + 'win32': 'Windows NT 10.0; Win64; x64', + }[process.platform] ?? 'Windows NT 10.0; Win64; x64'; + expect(device.userAgent).toContain(expectedPlatform); + + const context = await browser.newContext({ ...device }); + const page = await context.newPage(); + await page.goto(server.PREFIX + '/mobile.html'); + const userAgent = await page.evaluate(() => navigator.userAgent); + expect(userAgent).toContain(expectedPlatform); + expect(userAgent).not.toContain('{{platform}}'); + await context.close(); + }); + it('should emulate viewport and screen size', async ({ contextFactory, playwright }) => { const device = playwright.devices['iPhone 12']; const context = await contextFactory(device);