Skip to content

Commit e8c3971

Browse files
committed
feat(webkit): allow running WebKit via WSL on Windows
1 parent ed9b6dd commit e8c3971

26 files changed

+319
-304
lines changed

.github/actions/run-test/action.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,20 @@ runs:
5151
npx playwright install --with-deps ${{ inputs.browsers-to-install }}
5252
echo "::endgroup::"
5353
shell: bash
54+
# As per https://github.com/actions/runner-images/issues/12466
55+
- name: Allow WSL network access for WSL2
56+
if: contains(inputs.browsers-to-install, 'webkit-wsl')
57+
shell: powershell
58+
run: |
59+
$wslAdapter = Get-NetAdapter | Where-Object {$_.Name -like "*WSL*"}
60+
if ($wslAdapter) {
61+
$wslIP = Get-NetIPAddress -InterfaceIndex $wslAdapter.InterfaceIndex | Where-Object {$_.AddressFamily -eq "IPv4"}
62+
if ($wslIP) {
63+
$networkRange = "$($wslIP.IPAddress)/$($wslIP.PrefixLength)"
64+
Write-Host "Found WSL network: $networkRange"
65+
netsh advfirewall firewall add rule name="WSL2-Auto" dir=in action=allow protocol=TCP remoteip=$networkRange
66+
}
67+
}
5468
- name: Run tests
5569
if: inputs.shell == 'bash'
5670
run: |

.github/workflows/tests_primary.yml

Lines changed: 8 additions & 207 deletions
Original file line numberDiff line numberDiff line change
@@ -26,218 +26,19 @@ env:
2626
DEBUG_GIT_COMMIT_INFO: 1
2727

2828
jobs:
29-
test_linux:
30-
name: ${{ matrix.os }} (${{ matrix.browser }} - Node.js ${{ matrix.node-version }})
31-
environment: ${{ github.event_name == 'push' && 'allow-uploading-flakiness-results' || null }}
32-
strategy:
33-
fail-fast: false
34-
matrix:
35-
browser: [chromium, firefox, webkit]
36-
os: [ubuntu-22.04]
37-
node-version: [18]
38-
include:
39-
- os: ubuntu-22.04
40-
node-version: 20
41-
browser: chromium
42-
- os: ubuntu-22.04
43-
node-version: 22
44-
browser: chromium
45-
- os: ubuntu-22.04
46-
node-version: 24
47-
browser: chromium
48-
runs-on: ${{ matrix.os }}
49-
permissions:
50-
id-token: write # This is required for OIDC login (azure/login) to succeed
51-
contents: read # This is required for actions/checkout to succeed
29+
test_webkit_wsl:
30+
name: Webkit WSL
31+
runs-on: windows-2025
5232
steps:
5333
- uses: actions/checkout@v4
5434
- uses: ./.github/actions/run-test
5535
with:
56-
node-version: ${{ matrix.node-version }}
57-
browsers-to-install: ${{ matrix.browser }} chromium
58-
command: npm run test -- --project=${{ matrix.browser }}-*
59-
bot-name: "${{ matrix.browser }}-${{ matrix.os }}-node${{ matrix.node-version }}"
60-
flakiness-client-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_CLIENT_ID }}
61-
flakiness-tenant-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_TENANT_ID }}
62-
flakiness-subscription-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_SUBSCRIPTION_ID }}
63-
64-
test_linux_chromium_tot:
65-
name: ${{ matrix.os }} (chromium tip-of-tree)
66-
environment: ${{ github.event_name == 'push' && 'allow-uploading-flakiness-results' || null }}
67-
strategy:
68-
fail-fast: false
69-
matrix:
70-
os: [ubuntu-22.04]
71-
runs-on: ${{ matrix.os }}
72-
permissions:
73-
id-token: write # This is required for OIDC login (azure/login) to succeed
74-
contents: read # This is required for actions/checkout to succeed
75-
steps:
76-
- uses: actions/checkout@v4
77-
- uses: ./.github/actions/run-test
78-
with:
79-
browsers-to-install: chromium-tip-of-tree
80-
command: npm run test -- --project=chromium-*
81-
bot-name: "${{ matrix.os }}-chromium-tip-of-tree"
36+
node-version: 22
37+
browsers-to-install: webkit-wsl chromium
38+
command: npm run test -- --project=webkit-* --reporter=list
39+
bot-name: "webkit-wsl-headed"
8240
flakiness-client-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_CLIENT_ID }}
8341
flakiness-tenant-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_TENANT_ID }}
8442
flakiness-subscription-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_SUBSCRIPTION_ID }}
8543
env:
86-
PWTEST_CHANNEL: chromium-tip-of-tree
87-
88-
test_test_runner:
89-
name: Test Runner
90-
environment: ${{ github.event_name == 'push' && 'allow-uploading-flakiness-results' || null }}
91-
strategy:
92-
fail-fast: false
93-
matrix:
94-
os: [ubuntu-latest, windows-latest, macos-latest]
95-
node-version: [18]
96-
shardIndex: [1, 2]
97-
shardTotal: [2]
98-
include:
99-
- os: ubuntu-latest
100-
node-version: 20
101-
shardIndex: 1
102-
shardTotal: 2
103-
- os: ubuntu-latest
104-
node-version: 20
105-
shardIndex: 2
106-
shardTotal: 2
107-
- os: ubuntu-latest
108-
node-version: 22
109-
shardIndex: 1
110-
shardTotal: 2
111-
- os: ubuntu-latest
112-
node-version: 22
113-
shardIndex: 2
114-
shardTotal: 2
115-
- os: ubuntu-latest
116-
node-version: 24
117-
shardIndex: 1
118-
shardTotal: 2
119-
- os: ubuntu-latest
120-
node-version: 24
121-
shardIndex: 2
122-
shardTotal: 2
123-
runs-on: ${{ matrix.os }}
124-
permissions:
125-
id-token: write # This is required for OIDC login (azure/login) to succeed
126-
contents: read # This is required for actions/checkout to succeed
127-
steps:
128-
- uses: actions/checkout@v4
129-
- uses: ./.github/actions/run-test
130-
with:
131-
node-version: ${{matrix.node-version}}
132-
command: npm run ttest -- --shard ${{ matrix.shardIndex }}/${{ matrix.shardTotal }}
133-
bot-name: "${{ matrix.os }}-node${{ matrix.node-version }}-${{ matrix.shardIndex }}"
134-
flakiness-client-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_CLIENT_ID }}
135-
flakiness-tenant-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_TENANT_ID }}
136-
flakiness-subscription-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_SUBSCRIPTION_ID }}
137-
env:
138-
PWTEST_CHANNEL: firefox-beta
139-
140-
test_web_components:
141-
name: Web Components
142-
runs-on: ubuntu-latest
143-
steps:
144-
- uses: actions/checkout@v4
145-
- uses: actions/setup-node@v4
146-
with:
147-
node-version: 18
148-
- run: npm ci
149-
- run: npm run build
150-
151-
- run: npx playwright install --with-deps
152-
- run: npm run test-html-reporter
153-
env:
154-
PWTEST_BOT_NAME: "web-components-html-reporter"
155-
- name: Upload blob report
156-
if: ${{ !cancelled() }}
157-
uses: ./.github/actions/upload-blob-report
158-
with:
159-
report_dir: packages/html-reporter/blob-report
160-
job_name: "web-components-html-reporter"
161-
162-
- run: npm run test-web
163-
if: ${{ !cancelled() }}
164-
env:
165-
PWTEST_BOT_NAME: "web-components-web"
166-
- name: Upload blob report
167-
if: ${{ !cancelled() }}
168-
uses: ./.github/actions/upload-blob-report
169-
with:
170-
report_dir: packages/web/blob-report
171-
job_name: "web-components-web"
172-
173-
test_vscode_extension:
174-
name: VSCode Extension
175-
runs-on: ubuntu-latest
176-
env:
177-
PWTEST_BOT_NAME: "vscode-extension"
178-
DEBUG_GIT_COMMIT_INFO: ""
179-
steps:
180-
- uses: actions/checkout@v4
181-
- uses: actions/setup-node@v4
182-
with:
183-
node-version: 18
184-
- run: npm ci
185-
env:
186-
DEBUG: pw:install
187-
- run: npm run build
188-
- run: npx playwright install chromium
189-
- name: Checkout extension
190-
run: git clone https://github.com/microsoft/playwright-vscode.git
191-
- name: Print extension revision
192-
run: git rev-parse HEAD
193-
working-directory: ./playwright-vscode
194-
- name: Remove @playwright/test from extension dependencies
195-
run: node -e "const p = require('./package.json'); delete p.devDependencies['@playwright/test']; fs.writeFileSync('./package.json', JSON.stringify(p, null, 2));"
196-
working-directory: ./playwright-vscode
197-
- name: Build extension
198-
run: npm ci && npm run build
199-
working-directory: ./playwright-vscode
200-
- name: Run extension tests
201-
run: npm run test -- --workers=1
202-
working-directory: ./playwright-vscode
203-
- name: Upload blob report
204-
if: ${{ !cancelled() }}
205-
uses: ./.github/actions/upload-blob-report
206-
with:
207-
report_dir: playwright-vscode/blob-report
208-
job_name: ${{ env.PWTEST_BOT_NAME }}
209-
210-
test_package_installations:
211-
name: "Installation Test ${{ matrix.os }}"
212-
environment: ${{ github.event_name == 'push' && 'allow-uploading-flakiness-results' || null }}
213-
strategy:
214-
fail-fast: false
215-
matrix:
216-
os:
217-
- ubuntu-latest
218-
- macos-latest
219-
- windows-latest
220-
runs-on: ${{ matrix.os }}
221-
timeout-minutes: 30
222-
permissions:
223-
id-token: write # This is required for OIDC login (azure/login) to succeed
224-
contents: read # This is required for actions/checkout to succeed
225-
steps:
226-
- uses: actions/checkout@v4
227-
- run: npm install -g yarn@1
228-
- run: npm install -g pnpm@8
229-
- name: Setup Ubuntu Binary Installation # TODO: Remove when https://github.com/electron/electron/issues/42510 is fixed
230-
if: ${{ runner.os == 'Linux' }}
231-
run: |
232-
if grep -q "Ubuntu 24" /etc/os-release; then
233-
sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0
234-
fi
235-
shell: bash
236-
- uses: ./.github/actions/run-test
237-
with:
238-
command: npm run itest
239-
bot-name: "package-installations-${{ matrix.os }}"
240-
shell: ${{ matrix.os == 'windows-latest' && 'pwsh' || 'bash' }}
241-
flakiness-client-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_CLIENT_ID }}
242-
flakiness-tenant-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_TENANT_ID }}
243-
flakiness-subscription-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_SUBSCRIPTION_ID }}
44+
PWTEST_CHANNEL: webkit-wsl
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
$ErrorActionPreference = 'Stop'
2+
3+
$Distribution = "playwright"
4+
$Username = "pwuser"
5+
6+
$distributions = (wsl --list --quiet) -split "\r?\n"
7+
if ($distributions -contains $Distribution) {
8+
Write-Host "WSL distribution '$Distribution' already exists. Skipping installation."
9+
} else {
10+
Write-Host "Installing new WSL distribution '$Distribution'..."
11+
wsl --install -d Ubuntu-24.04 --name $Distribution --no-launch
12+
wsl -d $Distribution -u root adduser --gecos GECOS --disabled-password $Username
13+
}
14+
15+
$pwshDirname = (Resolve-Path -Path $PSScriptRoot).Path;
16+
$playwrightCoreRoot = Resolve-Path (Join-Path $pwshDirname "..")
17+
18+
$initScript = @"
19+
if [ ! -f "/home/$Username/node/bin/node" ]; then
20+
mkdir -p /home/$Username/node
21+
curl -fsSL https://nodejs.org/dist/v22.17.0/node-v22.17.0-linux-x64.tar.xz -o /home/$Username/node/node-v22.17.0-linux-x64.tar.xz
22+
tar -xJf /home/$Username/node/node-v22.17.0-linux-x64.tar.xz -C /home/$Username/node --strip-components=1
23+
fi
24+
/home/$Username/node/bin/node cli.js install-deps
25+
cp bin/webkit-wsl-pipe-wrapper.mjs /home/$Username/
26+
sudo -u $Username PLAYWRIGHT_SKIP_BROWSER_GC=1 /home/$Username/node/bin/node cli.js install webkit
27+
"@ -replace "\r\n", "`n"
28+
29+
wsl -d $Distribution --cd $playwrightCoreRoot -u root -- bash -c "$initScript"
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// @ts-check
2+
import net from 'net';
3+
import { execSync, spawn } from 'child_process';
4+
5+
const socketPort = process.env.PW_WKWSL_PORT;
6+
delete process.env.PW_WKWSL_PORT;
7+
if (!socketPort)
8+
throw new Error('PW_WKWSL_PORT env var is not set');
9+
10+
const address = (() => {
11+
if (execSync('wslinfo --networking-mode', { encoding: 'utf8' }).trim() === 'nat') {
12+
const ip = execSync('ip route show', { encoding: 'utf8' }).trim().split('\n').find(line => line.includes('default'))?.split(' ')[2];
13+
if (!ip)
14+
throw new Error('Could not determine WSL IP address (NAT mode).');
15+
return ip;
16+
}
17+
return '127.0.0.1';
18+
})();
19+
20+
const socket = net.createConnection(parseInt(socketPort), address);
21+
socket.setNoDelay(true);
22+
23+
await new Promise((resolve, reject) => {
24+
socket.on('connect', resolve);
25+
socket.on('error', reject);
26+
});
27+
28+
const [executable, ...args] = process.argv.slice(2);
29+
30+
// 3 is readFD and 4 is writeFD
31+
const child = spawn(executable, args, {
32+
stdio: ['inherit', 'inherit', 'inherit', 'pipe', 'pipe']
33+
});
34+
35+
socket.pipe(/** @type {NodeJS.WritableStream} */ (child.stdio[3]));
36+
/** @type {NodeJS.ReadableStream} */ (child.stdio[4]).pipe(socket);
37+
38+
// Handle cleanup
39+
socket.on('end', () => child.kill());
40+
41+
child.on('exit', (exitCode) => {
42+
socket.end();
43+
process.exit(exitCode || 0);
44+
});
45+
46+
await new Promise((resolve, reject) => {
47+
child.on('exit', resolve);
48+
child.on('error', reject);
49+
});

packages/playwright-core/src/server/bidi/bidiChromium.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ export class BidiChromium extends BrowserType {
7777
return error;
7878
}
7979

80-
override amendEnvironment(env: Env): Env {
80+
override async amendEnvironment(env: Env) {
8181
return env;
8282
}
8383

@@ -92,7 +92,7 @@ export class BidiChromium extends BrowserType {
9292
return false;
9393
}
9494

95-
override defaultArgs(options: types.LaunchOptions, isPersistent: boolean, userDataDir: string): string[] {
95+
override async defaultArgs(options: types.LaunchOptions, isPersistent: boolean, userDataDir: string) {
9696
const chromeArguments = this._innerDefaultArgs(options);
9797
chromeArguments.push(`--user-data-dir=${userDataDir}`);
9898
chromeArguments.push('--remote-debugging-port=0');

packages/playwright-core/src/server/bidi/bidiFirefox.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ export class BidiFirefox extends BrowserType {
5757
return error;
5858
}
5959

60-
override amendEnvironment(env: Env): Env {
60+
override async amendEnvironment(env: Env) {
6161
if (!path.isAbsolute(os.homedir()))
6262
throw new Error(`Cannot launch Firefox with relative home directory. Did you set ${os.platform() === 'win32' ? 'USERPROFILE' : 'HOME'} to a relative path?`);
6363

@@ -92,7 +92,7 @@ export class BidiFirefox extends BrowserType {
9292
});
9393
}
9494

95-
override defaultArgs(options: types.LaunchOptions, isPersistent: boolean, userDataDir: string): string[] {
95+
override async defaultArgs(options: types.LaunchOptions, isPersistent: boolean, userDataDir: string) {
9696
const { args = [], headless } = options;
9797
const userDataDirArg = args.find(arg => arg.startsWith('-profile') || arg.startsWith('--profile'));
9898
if (userDataDirArg)

0 commit comments

Comments
 (0)