Skip to content

[pull] main from microsoft:main #81

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 38 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
8169ba0
feat(webkit): roll to r2182 (#36183)
microsoft-playwright-automation[bot] Jun 4, 2025
a0479cb
fix(test runner): prevent esm loader deadlock (#36187)
dgozman Jun 4, 2025
85519de
chore: update browser_patches to 9768715bd (#36191)
mxschmitt Jun 4, 2025
6e14ce0
chore: update WebKit version to 18.5 (#36190)
mxschmitt Jun 4, 2025
126239b
test: skip permission tests on Debian 11 (#36194)
mxschmitt Jun 4, 2025
5659432
test: skip scrolling tests on Android (#36195)
mxschmitt Jun 4, 2025
5f2e74a
test: skip another permission tests on Debian 11 (#36198)
mxschmitt Jun 4, 2025
0b2f80b
fix(chromium): refer to x86 and x86_64 as "x86" arch (#36193)
agg23 Jun 4, 2025
3abb8bd
test: unflake "should delete header with undefined value" (#36197)
dgozman Jun 4, 2025
6dc6651
test: mark inspector tests as slow (#36199)
mxschmitt Jun 4, 2025
2e57a78
chore: add release notes for v1.53 (#36196)
mxschmitt Jun 4, 2025
227b123
chore: mark v1.54.0-next (#36200)
mxschmitt Jun 4, 2025
e6bf21f
chore: launchServer() with userDataDir (#36185)
dgozman Jun 4, 2025
4b40a74
fix: allow matching against `about:blank` and other custom URLs (#36180)
agg23 Jun 4, 2025
085f7a8
fix: capture snapshot for ai during navigation (#36203)
pavelfeldman Jun 4, 2025
bd5a23f
chore: get rid of ready state type (#36177)
pavelfeldman Jun 4, 2025
090a451
fix(ai snapshot): wait for blocking CSS (#36206)
Skn0tt Jun 5, 2025
b25cb7f
chore: use 'Clear' step name for locator.clear() (#36208)
mxschmitt Jun 5, 2025
e313b8b
fix(trace-viewer): show fallback command on less precise paths (#36202)
agg23 Jun 5, 2025
3ab097b
chore: prepare to language ports roll (#36211)
dgozman Jun 5, 2025
a0eb891
chore: ensure progress runs with a timeout in fetch() (#36192)
dgozman Jun 5, 2025
2bc8ed0
docs(context): document the `browser()` call from persistent context …
agg23 Jun 5, 2025
1870739
fix: ensure ElementHandlerDispatcher has FrameDispatcher parent (#36214)
dgozman Jun 6, 2025
301481f
chore(client): remove unneeded _wrapApiCall(internal) (#36224)
Skn0tt Jun 6, 2025
34c2954
fix: java style selector string (#36227)
Skn0tt Jun 6, 2025
5f053c7
feat(chromium-tip-of-tree): roll to r1338 (#36221)
microsoft-playwright-automation[bot] Jun 6, 2025
237de4f
feat(chromium): roll to 1178 (#36231)
mxschmitt Jun 6, 2025
c930e67
feat(cookie): export/import chips cookies (#36168)
yury-s Jun 6, 2025
0ace76a
docs: mention trace viewer as debugging tool for the Mouse API class …
gwennlbh Jun 6, 2025
a7df837
test: remove special handling for getSelection() with Firefox from te…
whimboo Jun 7, 2025
f66a835
devops: add roll stable-test-runner automation (#36229)
mxschmitt Jun 10, 2025
937a921
feat(webkit): roll to r2183 (#36254)
microsoft-playwright-automation[bot] Jun 10, 2025
93dfdbf
chore(typo): if you intend (#36259)
Skn0tt Jun 10, 2025
92994a8
fix: restore proper class name escaping (#36258)
dgozman Jun 10, 2025
3cb987f
fix(html-reporter): race condition where form submission used stale f…
mxschmitt Jun 10, 2025
a8f9c4d
test: unflake a few tests on Android (#36262)
dgozman Jun 10, 2025
d86787d
chore: roll stable-test-runner to 1.53.0-beta-1749049851000 (#36201)
mxschmitt Jun 10, 2025
c396674
test: add cookie with SameSite attribute (#36255)
yury-s Jun 10, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions .github/workflows/roll_stable_test_runner.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
name: "PR: bump stable-test-runner"
on:
workflow_dispatch:
schedule:
# At 10:00am UTC (3AM PST) every Monday
- cron: "0 10 * * 1"
jobs:
trigger-roll:
name: Trigger Roll
runs-on: ubuntu-24.04
if: github.repository == 'microsoft/playwright'
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
- run: |
npm install @playwright/test@next
VERSION=$(node -e "console.log(require('./package.json').dependencies['@playwright/test'].replace('^', ''))")
echo "VERSION=$VERSION" >> $GITHUB_OUTPUT
working-directory: tests/playwright-test/stable-test-runner/
id: bump
- name: Prepare branch
id: prepare-branch
run: |
if [[ "$(git status --porcelain)" == "" ]]; then
echo "there are no changes";
exit 0;
fi
echo "HAS_CHANGES=1" >> $GITHUB_OUTPUT
BRANCH_NAME="roll-stable-test-runner/$(date +%Y-%b-%d)"
echo "BRANCH_NAME=$BRANCH_NAME" >> $GITHUB_OUTPUT
git config --global user.name microsoft-playwright-automation[bot]
git config --global user.email 203992400+microsoft-playwright-automation[bot]@users.noreply.github.com
git checkout -b "$BRANCH_NAME"
git add .
git commit -m "test: roll stable-test-runner to ${{ steps.bump.outputs.VERSION }}"
git push origin $BRANCH_NAME
- uses: actions/create-github-app-token@v2
id: app-token
with:
app-id: ${{ vars.PLAYWRIGHT_APP_ID }}
private-key: ${{ secrets.PLAYWRIGHT_PRIVATE_KEY }}
- name: Create Pull Request
if: ${{ steps.prepare-branch.outputs.HAS_CHANGES == '1' }}
uses: actions/github-script@v7
with:
github-token: ${{ steps.app-token.outputs.token }}
script: |
await github.rest.pulls.create({
owner: 'microsoft',
repo: 'playwright',
head: 'microsoft:${{ steps.prepare-branch.outputs.BRANCH_NAME }}',
base: 'main',
title: 'test: roll stable-test-runner to ${{ steps.bump.outputs.VERSION }}',
});
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
# 🎭 Playwright

[![npm version](https://img.shields.io/npm/v/playwright.svg)](https://www.npmjs.com/package/playwright) <!-- GEN:chromium-version-badge -->[![Chromium version](https://img.shields.io/badge/chromium-138.0.7204.4-blue.svg?logo=google-chrome)](https://www.chromium.org/Home)<!-- GEN:stop --> <!-- GEN:firefox-version-badge -->[![Firefox version](https://img.shields.io/badge/firefox-139.0-blue.svg?logo=firefoxbrowser)](https://www.mozilla.org/en-US/firefox/new/)<!-- GEN:stop --> <!-- GEN:webkit-version-badge -->[![WebKit version](https://img.shields.io/badge/webkit-18.4-blue.svg?logo=safari)](https://webkit.org/)<!-- GEN:stop --> [![Join Discord](https://img.shields.io/badge/join-discord-infomational)](https://aka.ms/playwright/discord)
[![npm version](https://img.shields.io/npm/v/playwright.svg)](https://www.npmjs.com/package/playwright) <!-- GEN:chromium-version-badge -->[![Chromium version](https://img.shields.io/badge/chromium-138.0.7204.15-blue.svg?logo=google-chrome)](https://www.chromium.org/Home)<!-- GEN:stop --> <!-- GEN:firefox-version-badge -->[![Firefox version](https://img.shields.io/badge/firefox-139.0-blue.svg?logo=firefoxbrowser)](https://www.mozilla.org/en-US/firefox/new/)<!-- GEN:stop --> <!-- GEN:webkit-version-badge -->[![WebKit version](https://img.shields.io/badge/webkit-18.5-blue.svg?logo=safari)](https://webkit.org/)<!-- GEN:stop --> [![Join Discord](https://img.shields.io/badge/join-discord-infomational)](https://aka.ms/playwright/discord)

## [Documentation](https://playwright.dev) | [API reference](https://playwright.dev/docs/api/class-playwright)

Playwright is a framework for Web Testing and Automation. It allows testing [Chromium](https://www.chromium.org/Home), [Firefox](https://www.mozilla.org/en-US/firefox/new/) and [WebKit](https://webkit.org/) with a single API. Playwright is built to enable cross-browser web automation that is **ever-green**, **capable**, **reliable** and **fast**.

| | Linux | macOS | Windows |
| :--- | :---: | :---: | :---: |
| Chromium <!-- GEN:chromium-version -->138.0.7204.4<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| WebKit <!-- GEN:webkit-version -->18.4<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| Chromium <!-- GEN:chromium-version -->138.0.7204.15<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| WebKit <!-- GEN:webkit-version -->18.5<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| Firefox <!-- GEN:firefox-version -->139.0<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |

Headless execution is supported for all browsers on all platforms. Check out [system requirements](https://playwright.dev/docs/intro#system-requirements) for details.
Expand Down
2 changes: 1 addition & 1 deletion browser_patches/firefox/UPSTREAM_CONFIG.sh
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
REMOTE_URL="https://github.com/mozilla/gecko-dev"
BASE_BRANCH="release"
BASE_REVISION="5e1efb776a56e399f6810204a2eca13f18a3eba6"
BASE_REVISION="9cbfae27052e4aaeb064d2d08e7e869f31ee4288"
10 changes: 3 additions & 7 deletions browser_patches/firefox/juggler/Helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@

const uuidGen = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator);

class Helper {
export class Helper {
decorateAsEventEmitter(objectToDecorate) {
const { EventEmitter } = ChromeUtils.import('resource://gre/modules/EventEmitter.jsm');
const { EventEmitter } = ChromeUtils.importESModule('resource://gre/modules/EventEmitter.sys.mjs');
const emitter = new EventEmitter();
objectToDecorate.on = emitter.on.bind(emitter);
objectToDecorate.addEventListener = emitter.on.bind(emitter);
Expand Down Expand Up @@ -172,7 +172,7 @@ class Helper {

const helper = new Helper();

class EventWatcher {
export class EventWatcher {
constructor(receiver, eventNames, pendingEventWatchers = new Set()) {
this._pendingEventWatchers = pendingEventWatchers;
this._pendingEventWatchers.add(this);
Expand Down Expand Up @@ -233,7 +233,3 @@ class EventWatcher {
}
}

var EXPORTED_SYMBOLS = [ "Helper", "EventWatcher" ];
this.Helper = Helper;
this.EventWatcher = EventWatcher;

8 changes: 3 additions & 5 deletions browser_patches/firefox/juggler/JugglerFrameParent.jsm
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
"use strict";

const { TargetRegistry } = ChromeUtils.import('chrome://juggler/content/TargetRegistry.js');
const { Helper } = ChromeUtils.import('chrome://juggler/content/Helper.js');
const { TargetRegistry } = ChromeUtils.importESModule('chrome://juggler/content/TargetRegistry.js');
const { Helper } = ChromeUtils.importESModule('chrome://juggler/content/Helper.js');

const helper = new Helper();

var EXPORTED_SYMBOLS = ['JugglerFrameParent'];

class JugglerFrameParent extends JSWindowActorParent {
export class JugglerFrameParent extends JSWindowActorParent {
constructor() {
super();
}
Expand Down
76 changes: 48 additions & 28 deletions browser_patches/firefox/juggler/NetworkObserver.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@

"use strict";

const {Helper} = ChromeUtils.import('chrome://juggler/content/Helper.js');
const {NetUtil} = ChromeUtils.import('resource://gre/modules/NetUtil.jsm');
const { ChannelEventSinkFactory } = ChromeUtils.import("chrome://remote/content/cdp/observers/ChannelEventSink.jsm");
const {Helper} = ChromeUtils.importESModule('chrome://juggler/content/Helper.js');
const {NetUtil} = ChromeUtils.importESModule('resource://gre/modules/NetUtil.sys.mjs');
const { ChannelEventSinkFactory } = ChromeUtils.importESModule("chrome://remote/content/cdp/observers/ChannelEventSink.sys.mjs");


const Cc = Components.classes;
Expand All @@ -28,7 +28,7 @@ const MAX_RESPONSE_STORAGE_SIZE = 100 * 1024 * 1024;

const pageNetworkSymbol = Symbol('PageNetwork');

class PageNetwork {
export class PageNetwork {
static forPageTarget(target) {
if (!target)
return undefined;
Expand Down Expand Up @@ -143,9 +143,10 @@ class NetworkRequest {
const target = this._networkObserver._targetRegistry.targetForBrowserId(browsingContext.browserId);
this._pageNetwork = PageNetwork.forPageTarget(target);
}
this._expectingInterception = false;
this._shouldYieldInterceptionToServiceWorker = false;
this._expectingResumedRequest = undefined; // { method, headers, postData }
this._overriddenHeadersForRedirect = redirectedFrom?._overriddenHeadersForRedirect;
this._sentOnRequest = false;
this._sentOnResponse = false;
this._fulfilled = false;

Expand Down Expand Up @@ -318,9 +319,8 @@ class NetworkRequest {
const interceptController = this._fallThroughInterceptController();
if (interceptController && interceptController.shouldPrepareForIntercept(aURI, channel)) {
// We assume that interceptController is a service worker if there is one,
// and yield interception to it. We are not going to intercept ourselves,
// so we send onRequest now.
this._sendOnRequest(false);
// and yield interception to it.
this._shouldYieldInterceptionToServiceWorker = true;
return true;
}

Expand All @@ -329,34 +329,31 @@ class NetworkRequest {
return false;
}

// We do not want to intercept any redirects, because we are not able
// to intercept subresource redirects, and it's unreliable for main requests.
// We do not sendOnRequest here, because redirects do that in constructor.
if (this.redirectedFromId)
return false;

const shouldIntercept = this._shouldIntercept();
if (!shouldIntercept) {
// We are not intercepting - ready to issue onRequest.
this._sendOnRequest(false);
return false;
}

this._expectingInterception = true;
return true;
}

// nsINetworkInterceptController
channelIntercepted(intercepted) {
if (!this._expectingInterception) {
// We are not intercepting, fall-through.
const interceptController = this._fallThroughInterceptController();
if (interceptController)
interceptController.channelIntercepted(intercepted);
// Yield to a service worker if determined so in shouldPrepareForIntercept().
const serviceWorker = this._shouldYieldInterceptionToServiceWorker ? this._fallThroughInterceptController() : undefined;
// Clear the flag to avoid an infinite loop. After service worker, we should intercept ourselves.
this._shouldYieldInterceptionToServiceWorker = false;

if (serviceWorker) {
const interceptedChannel = intercepted.QueryInterface(Ci.nsIInterceptedChannel);
// If service worker will not actually intercept the request, we want to be called again.
interceptedChannel.interceptAfterServiceWorkerResets();
serviceWorker.channelIntercepted(intercepted);
return;
}

this._expectingInterception = false;
this._interceptedChannel = intercepted.QueryInterface(Ci.nsIInterceptedChannel);

const pageNetwork = this._pageNetwork;
Expand Down Expand Up @@ -410,6 +407,7 @@ class NetworkRequest {
// See https://github.com/microsoft/playwright/issues/9418#issuecomment-944836244
if (aRequest !== this.httpChannel)
return;
this._sendOnRequest(false);
try {
this._originalListener.onStartRequest(aRequest);
} catch (e) {
Expand Down Expand Up @@ -447,6 +445,10 @@ class NetworkRequest {
}

_shouldIntercept() {
// We do not want to intercept any redirects, because we are not able
// to intercept subresource redirects, and it's unreliable for main requests.
if (this.redirectedFromId)
return false;
const pageNetwork = this._pageNetwork;
if (!pageNetwork)
return false;
Expand All @@ -467,8 +469,15 @@ class NetworkRequest {
}

_sendOnRequest(isIntercepted) {
// Note: we call _sendOnRequest either after we intercepted the request,
// or at the first moment we know that we are not going to intercept.
if (this._sentOnRequest) {
// We can come here twice because:
// - Redirects call _sendOnRequest in the constructor and from inside interception.
// - All other requests might call _sendOnRequest from onStartRequest and from inside interception.
// - All requests call _sendOnRequest from _sendOnResponse to avoid responses without requests.
return;
}
this._sentOnRequest = true;

const pageNetwork = this._pageNetwork;
if (!pageNetwork)
return;
Expand All @@ -491,11 +500,21 @@ class NetworkRequest {
}

_sendOnResponse(fromCache, opt_statusCode, opt_statusText) {
// For internal redirects, and perhaps something else that we lack test coverage for,
// we can arrive here before onStartRequest has fired. Make sure we
// notify about the request first.
this._sendOnRequest(false);

if (this._sentOnResponse) {
// We can come here twice because of internal redirects, e.g. service workers.
// We can come here twice because of an internal redirect, for example:
// - request was intercepted by a service worker;
// - HSTS redirect;
// - CORS preflight;
// - who knows what else?
return;
}
this._sentOnResponse = true;

const pageNetwork = this._pageNetwork;
if (!pageNetwork)
return;
Expand Down Expand Up @@ -576,7 +595,7 @@ class NetworkRequest {
}
}

class NetworkObserver {
export class NetworkObserver {
static instance() {
return NetworkObserver._instance || null;
}
Expand Down Expand Up @@ -776,6 +795,10 @@ function clearRequestHeaders(httpChannel) {
// We cannot remove the "host" header.
if (header.name.toLowerCase() === 'host')
continue;
// Keep the "cookie" header. If there is an override, it will be set anyway.
// Otherwise, we may delete a cookie that was set for a redirect.
if (header.name.toLowerCase() === 'cookie')
continue;
httpChannel.setRequestHeader(header.name, '', false /* merge */);
}
}
Expand Down Expand Up @@ -964,6 +987,3 @@ PageNetwork.Events = {
RequestFailed: Symbol('PageNetwork.Events.RequestFailed'),
};

var EXPORTED_SYMBOLS = ['NetworkObserver', 'PageNetwork'];
this.NetworkObserver = NetworkObserver;
this.PageNetwork = PageNetwork;
5 changes: 1 addition & 4 deletions browser_patches/firefox/juggler/SimpleChannel.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ class SimpleChannel {

_setTimeout(cb, timeout) {
// Lazy load on first call.
this._setTimeout = ChromeUtils.import('resource://gre/modules/Timer.jsm').setTimeout;
this._setTimeout = ChromeUtils.importESModule('resource://gre/modules/Timer.sys.mjs').setTimeout;
this._setTimeout(cb, timeout);
}

Expand Down Expand Up @@ -251,6 +251,3 @@ class SimpleChannel {
}
}
}

var EXPORTED_SYMBOLS = ['SimpleChannel'];
this.SimpleChannel = SimpleChannel;
Loading