diff --git a/packages/playwright-core/src/server/launchApp.ts b/packages/playwright-core/src/server/launchApp.ts
index ee65f3ca27258..0431400d2198c 100644
--- a/packages/playwright-core/src/server/launchApp.ts
+++ b/packages/playwright-core/src/server/launchApp.ts
@@ -27,6 +27,48 @@ import type { CRPage } from './chromium/crPage';
import type { Page } from './page';
import type * as types from './types';
+/**
+ * Get Chromium rendering arguments for WSL UI mode workarounds.
+ * Addresses transparent window issues in WSL with Intel iGPU + discrete GPU.
+ *
+ * Environment variables (in precedence order):
+ * - PW_UI_DISABLE_GPU=1: Disable GPU entirely
+ * - PW_UI_USE_SWIFTSHADER=1: Force SwiftShader software rendering
+ * - PW_UI_USE_DISCRETE_GPU=1: Force discrete GPU selection
+ * - Auto-detect WSL and use SwiftShader as fallback
+ */
+function getWSLRenderingArgs(): string[] {
+ // Check explicit environment variable overrides first
+ if (process.env.PW_UI_DISABLE_GPU === '1')
+ return ['--disable-gpu', '--disable-software-rasterizer'];
+
+ if (process.env.PW_UI_USE_SWIFTSHADER === '1')
+ return ['--use-gl=swiftshader'];
+
+ if (process.env.PW_UI_USE_DISCRETE_GPU === '1')
+ return ['--use-gl=angle', '--use-angle=d3d11'];
+
+ // Auto-detect WSL and apply SwiftShader fallback
+ if (isWSL())
+ return ['--use-gl=swiftshader'];
+
+ return [];
+}
+
+/**
+ * Detect if running under WSL (Windows Subsystem for Linux)
+ */
+function isWSL(): boolean {
+ if (process.platform !== 'linux')
+ return false;
+
+ return (
+ fs.existsSync('/proc/sys/fs/binfmt_misc/WSLInterop') ||
+ (fs.existsSync('/proc/version') &&
+ fs.readFileSync('/proc/version', 'utf8').toLowerCase().includes('microsoft')) ||
+ !!process.env.WSL_DISTRO_NAME
+ );
+}
export async function launchApp(browserType: BrowserType, options: {
sdkLanguage: string,
@@ -44,6 +86,12 @@ export async function launchApp(browserType: BrowserType, options: {
...(options.windowPosition ? [`--window-position=${options.windowPosition.x},${options.windowPosition.y}`] : []),
'--test-type=',
);
+ // WSL UI rendering workarounds for transparent window issues
+ // See: https://github.com/microsoft/playwright/issues/37287
+ const gpuArgs = getWSLRenderingArgs();
+ if (gpuArgs.length > 0)
+ args.push(...gpuArgs);
+
if (!channel && !options.persistentContextOptions?.executablePath)
channel = findChromiumChannelBestEffort(options.sdkLanguage);
}
diff --git a/tests/playwright-test/ui-mode-wsl-rendering.spec.ts b/tests/playwright-test/ui-mode-wsl-rendering.spec.ts
new file mode 100644
index 0000000000000..9aab55c2904ff
--- /dev/null
+++ b/tests/playwright-test/ui-mode-wsl-rendering.spec.ts
@@ -0,0 +1,208 @@
+/**
+ * Copyright (c) Microsoft Corporation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { test as base, expect } from './ui-mode-fixtures';
+import fs from 'fs';
+
+const test = base.extend<{}>({});
+
+// Helper function to create a simple test file
+const createTestFile = () => ({
+ 'test.spec.ts': `
+ import { test, expect } from '@playwright/test';
+ test('sample test', async ({ page }) => {
+ await page.goto('data:text/html,
Hello
');
+ await expect(page.locator('h1')).toHaveText('Hello');
+ });
+ `
+});
+
+// Helper function to verify UI mode loads correctly
+const verifyUILoads = async (page: any) => {
+ await expect(page.locator('[data-testid="test-tree"]')).toBeVisible({ timeout: 10000 });
+};
+
+test.describe('UI mode WSL rendering workarounds', () => {
+ test('should use SwiftShader by default on WSL', async ({ runUITest }) => {
+ const originalPlatform = process.platform;
+ const originalEnv = { ...process.env };
+
+ // Mock WSL environment
+ Object.defineProperty(process, 'platform', { value: 'linux', configurable: true });
+ process.env.WSL_DISTRO_NAME = 'Ubuntu';
+
+ try {
+ const { page } = await runUITest(createTestFile());
+ await verifyUILoads(page);
+ } finally {
+ // Restore original environment
+ Object.defineProperty(process, 'platform', { value: originalPlatform, configurable: true });
+ Object.assign(process.env, originalEnv);
+ }
+ });
+
+ test('should respect PW_UI_DISABLE_GPU environment variable', async ({ runUITest }) => {
+ const originalEnv = { ...process.env };
+ process.env.PW_UI_DISABLE_GPU = '1';
+
+ try {
+ const { page } = await runUITest(createTestFile());
+ await verifyUILoads(page);
+ } finally {
+ Object.assign(process.env, originalEnv);
+ }
+ });
+
+ test('should respect PW_UI_USE_SWIFTSHADER environment variable', async ({ runUITest }) => {
+ const originalEnv = { ...process.env };
+ process.env.PW_UI_USE_SWIFTSHADER = '1';
+
+ try {
+ const { page } = await runUITest(createTestFile());
+ await verifyUILoads(page);
+ } finally {
+ Object.assign(process.env, originalEnv);
+ }
+ });
+
+ test('should respect PW_UI_USE_DISCRETE_GPU environment variable', async ({ runUITest }) => {
+ const originalEnv = { ...process.env };
+ process.env.PW_UI_USE_DISCRETE_GPU = '1';
+
+ try {
+ const { page } = await runUITest(createTestFile());
+ await verifyUILoads(page);
+ } finally {
+ Object.assign(process.env, originalEnv);
+ }
+ });
+
+ test('should prioritize PW_UI_DISABLE_GPU over other options', async ({ runUITest }) => {
+ const originalEnv = { ...process.env };
+ process.env.PW_UI_DISABLE_GPU = '1';
+ process.env.PW_UI_USE_SWIFTSHADER = '1';
+ process.env.PW_UI_USE_DISCRETE_GPU = '1';
+
+ try {
+ const { page } = await runUITest(createTestFile());
+ await verifyUILoads(page);
+ } finally {
+ Object.assign(process.env, originalEnv);
+ }
+ });
+
+ test('should prioritize PW_UI_USE_SWIFTSHADER over PW_UI_USE_DISCRETE_GPU', async ({ runUITest }) => {
+ const originalEnv = { ...process.env };
+ process.env.PW_UI_USE_SWIFTSHADER = '1';
+ process.env.PW_UI_USE_DISCRETE_GPU = '1';
+
+ try {
+ const { page } = await runUITest(createTestFile());
+ await verifyUILoads(page);
+ } finally {
+ Object.assign(process.env, originalEnv);
+ }
+ });
+
+ test('should detect WSL via WSLInterop file', async ({ runUITest }) => {
+ const originalPlatform = process.platform;
+ const originalEnv = { ...process.env };
+ const originalExistsSync = fs.existsSync;
+
+ // Mock WSL environment
+ Object.defineProperty(process, 'platform', { value: 'linux', configurable: true });
+ delete process.env.WSL_DISTRO_NAME; // Ensure other WSL detection is off
+ fs.existsSync = (path: string) => {
+ if (path === '/proc/sys/fs/binfmt_misc/WSLInterop')
+ return true;
+ return originalExistsSync(path);
+ };
+
+ try {
+ const { page } = await runUITest(createTestFile());
+ await verifyUILoads(page);
+ } finally {
+ // Restore original environment
+ Object.defineProperty(process, 'platform', { value: originalPlatform, configurable: true });
+ Object.assign(process.env, originalEnv);
+ fs.existsSync = originalExistsSync;
+ }
+ });
+
+ test('should detect WSL via /proc/version', async ({ runUITest }) => {
+ const originalPlatform = process.platform;
+ const originalEnv = { ...process.env };
+
+ // Mock WSL environment with Microsoft in /proc/version
+ Object.defineProperty(process, 'platform', { value: 'linux', configurable: true });
+
+ // Mock fs.existsSync and fs.readFileSync for /proc/version
+ const originalExistsSync = fs.existsSync;
+ const originalReadFileSync = fs.readFileSync;
+
+ fs.existsSync = (path: string) => {
+ if (path === '/proc/version')
+ return true;
+
+ return originalExistsSync(path);
+ };
+
+ fs.readFileSync = (path: string, encoding?: any) => {
+ if (path === '/proc/version')
+ return 'Linux version 5.10.102.1-microsoft-standard-WSL2' as any;
+
+ return originalReadFileSync(path, encoding);
+ };
+
+ try {
+ const { page } = await runUITest(createTestFile());
+ await verifyUILoads(page);
+ } finally {
+ // Restore original environment
+ Object.defineProperty(process, 'platform', { value: originalPlatform, configurable: true });
+ Object.assign(process.env, originalEnv);
+ fs.existsSync = originalExistsSync;
+ fs.readFileSync = originalReadFileSync;
+ }
+ });
+
+ test('should not apply WSL workarounds on non-Linux platforms', async ({ runUITest }) => {
+ const originalPlatform = process.platform;
+ const originalEnv = { ...process.env };
+
+ // Mock non-Linux platform
+ Object.defineProperty(process, 'platform', { value: 'darwin', configurable: true });
+
+ try {
+ const { page } = await runUITest({
+ 'test.spec.ts': `
+ import { test, expect } from '@playwright/test';
+ test('sample test', async ({ page }) => {
+ await page.goto('data:text/html,Hello
');
+ await expect(page.locator('h1')).toHaveText('Hello');
+ });
+ `
+ });
+
+ // Verify the UI mode loads correctly without WSL workarounds
+ await expect(page.locator('[data-testid="test-tree"]')).toBeVisible({ timeout: 10000 });
+ } finally {
+ // Restore original environment
+ Object.defineProperty(process, 'platform', { value: originalPlatform, configurable: true });
+ Object.assign(process.env, originalEnv);
+ }
+ });
+});