Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
c2d87a9
Update sponsorship contact information in README
Vinyzu May 10, 2026
461b828
fix: investigate Playwright v1.60.0 release failure (#199)
github-actions[bot] May 20, 2026
e32c982
[Patch-Comparison] Automatic Commit: fix: investigate Playwright v1.6…
Vinyzu May 20, 2026
98ec5a6
Fix querySelector Strict ContextOption
Vinyzu May 21, 2026
5032dbf
[Patch-Comparison] Automatic Commit: Fix querySelector Strict Context…
Vinyzu May 21, 2026
d27ed4d
Fix v1.60.0 Issues (#208)
Vinyzu May 30, 2026
b021c30
[Patch-Comparison] Automatic Commit: Fix v1.60.0 Issues (#208)
Vinyzu May 30, 2026
5e8ab7e
Fix Visibility Checks (#210)
Vinyzu Jun 2, 2026
9af99c3
[Patch-Comparison] Automatic Commit: Fix Visibility Checks (#210)
Vinyzu Jun 2, 2026
35b6ccb
Refactor evaluateExpression methods in JSHandle class and remove unne…
Vinyzu Jun 3, 2026
6c07501
[Patch-Comparison] Automatic Commit: Refactor evaluateExpression meth…
Vinyzu Jun 3, 2026
8b38855
Add NodeMaven Sponsor to README
Vinyzu Jun 9, 2026
b11c5db
Move Report.json to Summary + Upload
Vinyzu Jun 16, 2026
bb99e5b
fix: investigate Playwright v1.61.0 release failure (#216)
github-actions[bot] Jun 17, 2026
f6b50c6
[Patch-Comparison] Automatic Commit: fix: investigate Playwright v1.6…
Vinyzu Jun 17, 2026
042bbe4
Add ColaProxy Sponsor
Vinyzu Jun 21, 2026
2c9250d
fix: investigate Playwright v1.61.1 release failure (#219)
github-actions[bot] Jun 23, 2026
dc5e356
[Patch-Comparison] Automatic Commit: fix: investigate Playwright v1.6…
Vinyzu Jun 23, 2026
d133a63
fix old driver bugs, refactor/formatting, improve test workflows (#222)
Vinyzu Jul 11, 2026
0096f54
[Patch-Comparison] Automatic Commit: fix old driver bugs, refactor/fo…
Vinyzu Jul 11, 2026
3be03d9
Update NodeMaven Sponsor
Vinyzu Jul 15, 2026
2ca2bc9
Missed a Link Update
Vinyzu Jul 15, 2026
9c36c24
Merge upstream/main into main
Jul 29, 2026
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
64 changes: 58 additions & 6 deletions .github/workflows/patchright_release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,8 @@ jobs:

let pageTestsStepLink = 'Unavailable';
let libraryTestsStepLink = 'Unavailable';
let pageTestsStepUrl = null;
let libraryTestsStepUrl = null;
try {
const jobsResponse = await github.rest.actions.listJobsForWorkflowRun({
owner: context.repo.owner,
Expand All @@ -189,15 +191,62 @@ jobs:
const pageStep = (testsJob.steps || []).find((step) => step.name === 'Run Page Tests');
const libraryStep = (testsJob.steps || []).find((step) => step.name === 'Run Library Tests');

if (pageStep?.number)
pageTestsStepLink = `[Run Page Tests](${testsJob.html_url}#step:${pageStep.number}:1)`;
if (libraryStep?.number)
libraryTestsStepLink = `[Run Library Tests](${testsJob.html_url}#step:${libraryStep.number}:1)`;
if (pageStep?.number) {
pageTestsStepUrl = `${testsJob.html_url}#step:${pageStep.number}:1`;
pageTestsStepLink = `[Run Page Tests](${pageTestsStepUrl})`;
}
if (libraryStep?.number) {
libraryTestsStepUrl = `${testsJob.html_url}#step:${libraryStep.number}:1`;
libraryTestsStepLink = `[Run Library Tests](${libraryTestsStepUrl})`;
}
}
} catch (error) {
// Keep defaults when job/step links are unavailable.
}

let reportObj = null;
try {
reportObj = JSON.parse(reportJsonText);
} catch {
// leave null; summary will reflect that the report was unreadable
}

let reportArtifact = null;
try {
const artifactsResponse = await github.rest.actions.listWorkflowRunArtifacts({
owner,
repo,
run_id: context.runId,
});
reportArtifact = artifactsResponse.data.artifacts.find((a) => a.name === 'report.json') || null;
} catch (error) {
reportArtifact = { error: String(error.message) };
}

const summaryPayload = {
old_version: oldVersion,
new_version: rawNewVersion,
summary: reportObj?.summary || { total: 0, affected: 0, unaffected: 0 },
workflow_run_url: runUrl,
commit: commitSha,
test_links: {
page_tests: pageTestsStepUrl,
library_tests: libraryTestsStepUrl,
},
affected_symbols: (reportObj?.affected || []).map((row) => ({
symbol: row.symbol,
kind: row.kind,
change_type: row.changeType,
playwright_file: row.playwrightFile,
patch_file: row.patchFile,
})),
report_artifact: reportArtifact && reportArtifact.id ? {
artifact_id: reportArtifact.id,
name: reportArtifact.name,
archive_download_url: reportArtifact.archive_download_url,
} : (reportArtifact && reportArtifact.error ? { error: reportArtifact.error } : null),
};

const issueBody = [
`## Release Failed: Playwright ${oldVersion} → ${rawNewVersion}`,
'',
Expand All @@ -215,11 +264,14 @@ jobs:
'',
affectedTable,
'',
'Full `check_patch_impact` report (`report.json`):',
'### Machine-readable summary',
'',
'```json',
reportJsonText,
JSON.stringify(summaryPayload, null, 2),
'```',
'',
'The full `report.json` artifact is linked in `report_artifact.archive_download_url` above.',
'',
'cc @Vinyzu',
].join('\n');

Expand Down
11 changes: 8 additions & 3 deletions .github/workflows/patchright_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,15 @@ jobs:
cd playwright
npx playwright install-deps

- name: Install Chromium Browser
- name: Install Chromium and Chrome Browsers
run: |
cd playwright
npx playwright install chromium
npx playwright install chromium chrome

- name: Run Detection Smoke Test
id: test_detection
continue-on-error: ${{ github.event_name == 'workflow_call' }}
run: xvfb-run -a node utils/detection_smoke_test.mjs

- name: Modify Tests for Patchright
run: npx tsx utils/modify_tests.ts
Expand All @@ -106,7 +111,7 @@ jobs:
id: set_test_outcome
if: always()
run: |
if [ "${{ steps.test_page.outcome }}" = "failure" ] || [ "${{ steps.test_library.outcome }}" = "failure" ]; then
if [ "${{ steps.test_detection.outcome }}" = "failure" ] || [ "${{ steps.test_page.outcome }}" = "failure" ] || [ "${{ steps.test_library.outcome }}" = "failure" ]; then
echo "test_outcome=failure" >> "$GITHUB_OUTPUT"
else
echo "test_outcome=success" >> "$GITHUB_OUTPUT"
Expand Down
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -161,8 +161,12 @@ cython_debug/
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
.idea/
.DS_Store
.vscode/

# Node.js
node_modules/
package-lock.json
playwright/

# MCP
.playwright-mcp/
48 changes: 47 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
<details open>
<summary><h3>Sponsors</h1></summary>

<sup>Want to Sponsor this Project? [Contact Me](https://discordapp.com/users/935224495126487150)!</sup>
<sup>Want to Sponsor this Project? Contact [@vinyzu](https://discordapp.com/users/935224495126487150) on Discord!</sup>

</br>

Expand Down Expand Up @@ -91,6 +91,52 @@ Built for developers who need **reliable**, anti-detection proxy infrastructure.

<sup> Start fast. Scale globally. Stay stable with [RapidProxy](https://www.rapidproxy.io/?ref=Vinyzu). </sup>

---

[<img width="60%" alt="NodeMaven Banner" src="https://github.com/user-attachments/assets/8125db5d-efe5-432a-aca4-fda912f78119" />](https://go.nodemaven.com/VinyzuGH)
</br>
#### [NodeMaven](https://go.nodemaven.com/VinyzuGH): Your reliable proxy provider with High Quality IPs. </br> Superb solution for automation, web scraping, SEO research, and social media management.

Why [NodeMaven](https://go.nodemaven.com/VinyzuGH)?

✅ 99.9% Uptime
</br>
🎯 Sticky sessions up to 7 days
</br>
🔍 IP Filtering: Proxies are guaranteed to have fraud score <97%
</br>
🔰 No KYC required
</br>
💸 Cashback on Traffic - Burn GB and earn up to 10% back

**Special codes** for Patchright users:
</br>
🔥 **PATCHRIGHT35** - **35% off** to Mobile and Residential Proxies
</br>
🔥 **PATCHRIGHT40** - **40% off** to ISP (Static) Proxies

<sup>Choose quality and stability with [NodeMaven](https://go.nodemaven.com/VinyzuGH).</sup>

---

[<img width="80%" alt="ColaProxy Banner" src="https://github.com/user-attachments/assets/4b5b22c4-955a-47cf-b2f1-3c2aa5c500ba" />](https://colaproxy.com/?utm_source=viny&utm_medium=viny&ref=viny)

#### [**ColaProxy**](https://colaproxy.com/?utm_source=viny&utm_medium=viny&ref=viny) : Low-Cost Residential Proxies for Scalable Automation, Scraping, and Global Data Access

Why choose [**ColaProxy**](https://colaproxy.com/?utm_source=viny&utm_medium=viny&ref=viny)?

* Dynamic Residential Proxies from as low as **$0.3/GB**
* Massive residential IP pool across 195+ countries
* 99.9% uptime with stable and fast connections
* HTTP / HTTPS / SOCKS5 protocol support
* Rotating & sticky sessions for flexible automation
* Precise country-level geo-targeting
* 24/7 expert support

Need reliable proxies for Patchright automation? [ColaProxy](https://colaproxy.com/?utm_source=viny&utm_medium=viny&ref=viny) offers cost-effective residential, ISP, and mobile proxy solutions for scraping, testing, account management, SEO monitoring, and global data collection. Use coupon code **COLA20** to enjoy 20% off your order.

<sup>Affordable, stable, and built for developers. Start scaling your Patchright projects with [**ColaProxy**](https://colaproxy.com/?utm_source=viny&utm_medium=viny&ref=viny).</sup>

</details>

---
Expand Down
6 changes: 2 additions & 4 deletions driver_patches/XPathSelectorEnginePatch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,7 @@ export function patchXPathSelectorEngine(project: Project) {
.getInitializerIfKindOrThrow(SyntaxKind.ObjectLiteralExpression);

// -- evaluateExpression Method --
const queryAllMethod = assertDefined(
xPathEngineLiteral.getProperty("queryAll")
);
const queryAllMethod = assertDefined(xPathEngineLiteral.getProperty("queryAll"));
queryAllMethod.asKindOrThrow(SyntaxKind.MethodDeclaration).insertStatements(0, `
if (root.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
const result: Element[] = [];
Expand Down Expand Up @@ -54,4 +52,4 @@ export function patchXPathSelectorEngine(project: Project) {
return result;
}
`);
}
}
6 changes: 4 additions & 2 deletions driver_patches/browserContextDispatcherPatch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ import { assertDefined } from "./utils.ts";
// ----------------------------------------------
export function patchBrowserContextDispatcher(project: Project) {
// Add source file to the project
const contextDispatcherSourceFile = project.addSourceFileAtPath("packages/playwright-core/src/server/dispatchers/browserContextDispatcher.ts");
const contextDispatcherSourceFile = project.addSourceFileAtPath(
"packages/playwright-core/src/server/dispatchers/browserContextDispatcher.ts",
);

// ------- BrowserContextDispatcher Class -------
const contextDispatcherClass = contextDispatcherSourceFile.getClassOrThrow("BrowserContextDispatcher");
Expand All @@ -18,7 +20,7 @@ export function patchBrowserContextDispatcher(project: Project) {
contextDispatcherConstructor
.getBodyOrThrow()
.getDescendantsOfKind(SyntaxKind.ExpressionStatement)
.find((stmt) => stmt.getText().startsWith("this._dialogHandler =")),
.find(stmt => stmt.getText().startsWith("this._dialogHandler =")),
);
dialogHandlerAssignment.replaceWithText(`
this._dialogHandler = dialog => {
Expand Down
23 changes: 12 additions & 11 deletions driver_patches/browserContextPatch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,28 +12,29 @@ export function patchBrowserContext(project: Project) {
const browserContextClass = browserContextSourceFile.getClassOrThrow("BrowserContext");

// -- _initialize Method --
const initializeMethod = browserContextClass.getMethodOrThrow("_initialize");
const initializeMethod = browserContextClass.getMethodOrThrow("initialize");
// Getting the service worker registration call
const initializeMethodCall = assertDefined(
initializeMethod
.getDescendantsOfKind(SyntaxKind.CallExpression)
.find((call) =>
call.getExpression().getText().includes("addInitScript") &&
call.getArguments().some((arg) =>
arg.getText().includes("navigator.serviceWorker.register")
)
)
.getDescendantsOfKind(SyntaxKind.CallExpression)
.find(
call =>
call.getExpression().getText().includes("addInitScript") &&
call.getArguments().some(arg => arg.getText().includes("navigator.serviceWorker.register")),
),
);
const initScriptArgument = assertDefined(
initializeMethodCall.getArguments()[1] ?? initializeMethodCall.getArguments()[0]
initializeMethodCall.getArguments()[1] ?? initializeMethodCall.getArguments()[0],
);
// Replace the service worker registration call with a custom one, which is less obvious
initScriptArgument.replaceWithText("`if (navigator.serviceWorker) navigator.serviceWorker.register = async () => { };`");
initScriptArgument.replaceWithText(
"`if (navigator.serviceWorker) navigator.serviceWorker.register = async () => { };`",
);

// -- exposeBinding Method --
const exposeBindingMethod = browserContextClass.getMethodOrThrow("exposeBinding");
// Remove old loop and logic for localFrames and isolated world creation
exposeBindingMethod.getStatements().forEach((statement) => {
exposeBindingMethod.getStatements().forEach(statement => {
const text = statement.getText();
// Check if the statement matches the patterns
if (text.includes("this.doAddInitScript(binding.initScript)"))
Expand Down
21 changes: 21 additions & 0 deletions driver_patches/buildPatch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { type Project, SyntaxKind } from "ts-morph";

// --------------------
// utils/build/build.js
// --------------------
export function patchBuild(project: Project) {
const buildSourceFile = project.addSourceFileAtPath("utils/build/build.js");
const bundleAssertion = buildSourceFile.getFunctionOrThrow("assertCoreBundleHasNoNodeModules");
const markerSearch = bundleAssertion
.getDescendantsOfKind(SyntaxKind.VariableDeclaration)
.find(declaration => declaration.getName() === "idx")
?.getInitializerOrThrow();
if (!markerSearch) throw new Error("Could not find the coreBundle node_modules marker check");
markerSearch.replaceWithText("lines[i].search(/(?:require|import)\\s*\\(\\s*['\"][^'\"]*node_modules\\//)");
buildSourceFile.replaceWithText(
buildSourceFile
.getFullText()
.replace("coreBundle.js contains 'node_modules/' references", "coreBundle.js contains runtime node_modules paths")
.replace("coreBundle.js: no node_modules/ references", "coreBundle.js: no runtime node_modules paths"),
);
}
13 changes: 9 additions & 4 deletions driver_patches/chromiumPatch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,21 @@ export function patchChromium(project: Project) {
// -- _innerDefaultArgs Method --
const innerDefaultArgsMethod = chromiumClass.getMethodOrThrow("_innerDefaultArgs");
// Get all the if statements in the method and modify to always use the --headless=new flag
innerDefaultArgsMethod.getDescendantsOfKind(SyntaxKind.IfStatement).forEach((ifStatement) => {
innerDefaultArgsMethod.getDescendantsOfKind(SyntaxKind.IfStatement).forEach(ifStatement => {
if (ifStatement.getExpression().getText().includes("process.env.PLAYWRIGHT_CHROMIUM_USE_HEADLESS_NEW"))
ifStatement.replaceWithText("chromeArguments.push('--headless=new');");
});

// Remove --enable-unsafe-swiftshader switches from innerDefaultArgs
innerDefaultArgsMethod
.getDescendantsOfKind(SyntaxKind.ExpressionStatement)
.filter((statement) => {
return statement.getText().includes("chromeArguments.push('--enable-unsafe-swiftshader')") || statement.getText().includes('chromeArguments.push("--enable-unsafe-swiftshader")');
.filter(statement => {
return (
statement.getText().includes("chromeArguments.push('--enable-unsafe-swiftshader')") ||
statement.getText().includes('chromeArguments.push("--enable-unsafe-swiftshader")')
);
})
.forEach((statement) => { statement.remove(); });
.forEach(statement => {
statement.remove();
});
}
18 changes: 11 additions & 7 deletions driver_patches/chromiumSwitchesPatch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import { type Project, SyntaxKind } from "ts-morph";
// -----------------------------------
export function patchChromiumSwitches(project: Project) {
// Add source file to the project
const chromiumSwitchesSourceFile = project.addSourceFileAtPath("packages/playwright-core/src/server/chromium/chromiumSwitches.ts");
const chromiumSwitchesSourceFile = project.addSourceFileAtPath(
"packages/playwright-core/src/server/chromium/chromiumSwitches.ts",
);

// -- chromiumSwitches Array Variable --
const chromiumSwitchesArrow = chromiumSwitchesSourceFile
Expand All @@ -30,13 +32,15 @@ export function patchChromiumSwitches(project: Project) {
"'--metrics-recording-only'",
"'--unsafely-disable-devtools-self-xss-warnings'",
"'--disable-back-forward-cache'",
"'--disable-features=ImprovedCookieControls,LazyFrameLoading,GlobalMediaControls,DestroyProfileOnBrowserClose,MediaRouter,DialMediaRouteProvider,AcceptCHFrame,AutoExpandDetailsElement,CertificateTransparencyComponentUpdater,AvoidUnnecessaryBeforeUnloadCheckSync,Translate,HttpsUpgrades,PaintHolding,ThirdPartyStoragePartitioning,LensOverlay,PlzDedicatedWorker'"
"'--disable-features=ImprovedCookieControls,LazyFrameLoading,GlobalMediaControls,DestroyProfileOnBrowserClose,MediaRouter,DialMediaRouteProvider,AcceptCHFrame,AutoExpandDetailsElement,CertificateTransparencyComponentUpdater,AvoidUnnecessaryBeforeUnloadCheckSync,Translate,HttpsUpgrades,PaintHolding,ThirdPartyStoragePartitioning,LensOverlay,PlzDedicatedWorker'",
];
chromiumSwitchesArray
.getElements()
.filter((element) => switchesToDisable.includes(element.getText()))
.forEach((element) => { chromiumSwitchesArray.removeElement(element); });

// Add custom switches to the array
.filter(element => switchesToDisable.includes(element.getText()))
.forEach(element => {
chromiumSwitchesArray.removeElement(element);
});

// Add custom switches to the array
chromiumSwitchesArray.addElement(`'--disable-blink-features=AutomationControlled'`);
}
}
Loading
Loading