You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
test(e2e): retry the PTY write spy's main-process evaluates on transient CDP failures - #34912
A Windows WSL paste e2e test failed on a CI hiccup in Playwright, not on Orca. The test helper already retries that exact hiccup when it installs its spy, but not when it reads the spy's log afterwards. Now every call in the helper retries it the same way. Test-only.
What Changed
tests/e2e/helpers/terminal-pty-write-spy.ts: clearTerminalPtyWriteLog, readTerminalPtyWrites, readTerminalPtyWriteEntries and setTerminalPtyWriteDelay now go through the existing retryTransientMainEvaluate, as installTerminalPtyWriteSpy already did.
That helper retries only "Execution context was destroyed" and "Resulting promise was garbage collected", Electron 27+'s transient CDP failures (playwright#33737), with up to 5 attempts 200 ms apart.
Assertion failures and closed-app errors still fail immediately.
All four calls are idempotent: a read, a clear and a setter of the same value.
Why
The "Real WSL terminal" job (windows-wsl-e2e.yml, --repeat-each=3, no retries) failed twice on #34694. Both times it was one repeat of terminal-windows-shell-paste-ownership.spec.ts › "existing WSL terminal keeps paste runtime after default shell changes", failing with electronApplication.evaluate: Resulting promise was garbage collected at readTerminalPtyWrites (helpers/terminal-pty-write-spy.ts:58), which is the spy read itself (run 40655378916, job 122705162336). The other repeats passed. #34694's kill-switch changes don't run in that spec: no WSL host is saved and the gate is closed, so there's no WSL host API, capability or serve-only argument. The error is the transient the repo's retry helper exists for.
Linked Issue
N/A (e2e flake).
Visual Proof
N/A: test-only.
Testing
terminal-paste-ownership.spec.ts, which uses the same spy, passes locally hidden: 5 passed, 1 skipped as Windows-only.
The Windows WSL spec runs only on CI's Windows runner.
typecheck:e2e, oxlint, the anti-slop lint, ast-grep-gates.mjs and check:code-quality:changed pass.
I manually tested these changes locally
Automated tests added/updated, or explained why not below
AI Disclosure
Review
Agent skill upstream boundary
Not applicable, or this change copies or mechanically translates no upstream skill-installer source, tests, fixtures, registry entries, path tables, comments, or documentation.
Notes
Test-only.
Checklist
This PR is small and focused
I explained what changed and why (ELI5, the user-facing before/after, the mechanism, and why over the alternatives)
Before/after screenshots or videos attached for UI changes, or N/A with reason
Self-reviewed for correctness, security, and performance
Cross-platform, SSH/remote, and path/shortcut impact considered (or N/A)
pnpm lint, pnpm typecheck, pnpm test, and pnpm build pass (or CI will cover; local preferred)
Review was skipped as selected files did not have any reviewable changes.
Run configuration
Source mode: Auto
Repository: Unavailable
Branch: Unavailable
File: No configuration file from this repository applied
Commit: Unavailable
Layers: Repository UI
Review profile: CHILL
Plan: Advanced
Run ID: 7df84679-da98-4c48-ae25-f7a3b19a6613
Commits
Reviewing files that changed from the base of the PR and between 4bb41fe and 454f81c.
You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.
Use the checkbox below for a quick retry:
🔍 Trigger review
Walkthrough
The terminal PTY write-spy clear, read, entry-read, and delay-setting helpers now wrap their main-process evaluations in retryTransientMainEvaluate. They retain their existing behavior for missing logs, returned entries, and non-negative delay values.
Priority: Low
Merge Risk: Low · previous review 4bb41
Retrying the clear can make end-to-end tests miss recorded PTY writes; make the operation replay-safe before merging.
Pre-merge checks: 5
Check name
Status
Explanation
Title check
The title clearly and concisely identifies the main change: adding retries for PTY write spy main-process evaluations after transient CDP failures.
Description check
The description is mostly complete. It explains the problem, change, rationale, testing, scope, and non-UI status. It leaves the Linked Issue as N/A despite the template requiring an issue link, leave…
Docstring Coverage
No eligible functions found in the changed files. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentat…
Linked Issues check
Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check
Check skipped because no linked issues were found for this pull request.
Full details: Description check
Explanation
The description is mostly complete. It explains the problem, change, rationale, testing, scope, and non-UI status. It leaves the Linked Issue as N/A despite the template requiring an issue link, leaves Review and AI Disclosure empty, and does not mark the full lint, typecheck, test, and build commands as passing, but these omissions do not make the description largely incomplete.
Full details: Docstring Coverage
Explanation
No eligible functions found in the changed files. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentation-only edits and deletions within surviving functions are included; unchanged diff context and fully deleted functions are excluded. Analyzed 0 functions across 1 files.
Finishing Touches: 💡 1
🛠️ Fix failing CI checks: 💡
Commit to this branch
Create a new PR
📝 Generate docstrings
Commit to this branch
Create a new PR
Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
Review info
Run configuration
Source mode: Auto
Repository: Unavailable
Branch: Unavailable
File: No configuration file from this repository applied
Commit: Unavailable
Layers: Repository UI
Review profile: CHILL
Plan: Advanced
Run ID: a5ec57d2-c6a3-4529-bfab-cb462cca5b11
Commits
Reviewing files that changed from the base of the PR and between 14ab7f0 and 4bb41fe.
Files selected for processing: 1
tests/e2e/helpers/terminal-pty-write-spy.ts
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 1 remain after this review.
Playwright ElectronApplication.evaluate "Resulting promise was garbage collected" issue 33737 callback executed side effect
Result:
<source_evidence>
<source kind="open_page">
<title>[Bug]: ElectronApplication.evaluate() is unreliable · Issue #33737 · microsoft/playwright · GitHub</title>
<location>GitHub issue 33737 in microsoft/playwright (link omitted to avoid creating a cross-reference)</location>
<excerpt>citeturn1view0 [wordlim: 200] Crawled: today; Content type: text/html; Source: open({"ref_id":"turn0search0","lineno":null}); Total lines: 367
L0: 【0†Skip to content】
L1: ## Navigation Menu
L2:
L3: 【1†Sign in】
L4:
L5: Appearance settings
L6:
L7: * Platform
L8:
L9: * AI CODE CREATION
L10: * 【2†GitHub CopilotWrite better code with AI】
L11: * 【3†GitHub Copilot appDirect agents from issue to merge】
L12: * 【4†MCP RegistryIntegrate external tools】
L13:
L14: * DEVELOPER WORKFLOWS
L15: * 【5†ActionsAutomate any workflow】
L16: * 【6†CodespacesInstant dev environments】
L17: * 【7†IssuesPlan and track work】
L18: * 【8†Code ReviewManage code changes】
L19: * 【9†Code QualityEnforce quality at merge】
L20: * APPLICATION SECURITY
L21: * 【10†GitHub Advanced SecurityFind and fix vulnerabilities】
L22: * 【11†Code securitySecure your code as you build】
L23: * 【12†Secret protectionStop leaks before they start】
L24:
L25: * EXPLORE
L26: * 【13†Why GitHub】
L27: * 【14†Documentation†docs.github.com】
L28: * 【15†Blog†github.blog】
L29: * 【16†Changelog†github.blog】
L30: * 【17†Marketplace】
L31:
L32: 【18†View all features】
L33:
L34: * Solutions
L35: * BY COMPANY SIZE
L36: * 【19†Enterprises】
L37: * 【20†Small and medium teams】
L38: * 【21†Startups】
L39: * 【22†Nonprofits】
L40:
L41: * BY USE CASE
L42: * 【23†App Modernization】
L43: * 【24†DevSecOps】
L44: * 【25†DevOps】
L45: * 【26†CI/CD】
L46: * 【27†View all use cases】
L47:
L48: * BY INDUSTRY
L49: * 【28†Healthcare】
L50: * 【29†Financial services】
L51: * 【30†Manufacturing】
L52: * 【31†Government】
L53: * 【32†See all industries】
L54:
L55: 【33†View all solutions】
L56:
L57: * Resources
L58: * EXPLORE BY TOPIC
L59: * 【34†AI】
L60: * 【35†Software Development】
L61: * 【36†DevOps】
L62: * 【37†Security】
L63: * 【38†View all topics】
L64:
L65: * EXPLORE BY TYPE
L66: * 【39†Customer stories】
L67: * 【40†Events & webinars】
L68: * 【41†Ebooks & reports】
L69: * 【42†Business insights】
L70: * 【43†GitHub Skills†skills.github.com】
L71: * SUPPORT & SERVICES
L72: * 【14†Documentation†docs.github.com】
L73: * 【44†Customer support†support.github.com】
L74: * 【45†Community forum】
L75: * 【46†Trust center】
L76: * 【47†Partners】
L77:
L78: 【48†View all resources】
L79:
L80: * Open Source
L81:
L82: * COMMUNITY
L83: * 【49†GitHub SponsorsFund open source developers】
L84: * PROGRAMS
L85: * 【50†Security Lab†securitylab.github.com】
L86: * 【51†Maintainer Community†maintainers.github.com】
L87: * 【52†GitHub Stars†stars.github.com】
L88: * 【53†Archive Program†archiveprogram.github.com】
L89:
L90: * REPOSITORIES
L91: * 【54†Topics】
L92: * 【55†Trending】
L93: * 【56†Collections】
L94:
L95: * Enterprise
L96:
L97: * ENTERPRISE SOLUTIONS
L98: * 【19†Enterprise platformAI-powered developer platform】
L99: * AVAILABLE ADD-ONS
L100: * 【10†GitHub Advanced SecurityEnterprise-grade security features】
L101: * 【57†Copilot for BusinessEnterprise-grade AI features】
L102: * 【58†Premium SupportEnterprise-grade 24/7 support】
L103:
L104: * 【59†Pricing】
L105:
L106: Search`/`
L107:
L108: 【1†Sign in】
L109:
L110: 【60†Sign up】
L111:
L112: Appearance settings
L113: You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session. You switched accounts on another tab or window. Reload to refresh your session. Dismiss alert
L114:
L115: 1. 【61†microsoft】
L116: 2. 【62†playwright】
L117: ## Repository navigation
L118:
L119: * * 【62†Code】
L120: * 【63†Issues187 (187)】
L121: * 【64†Pull requests26 (26)】
L122: * 【65†Actions】
L123: * 【66†Security and quality】
L124: * 【67†Insights】
L125:
L126: More items
L127:
L128: # [Bug]: ElectronApplication.evaluate() is unreliable #33737
L129:
L130: New issue
L131:
L132: Copy link
L133:
L134: New issue
L135:
L136: Copy link
L137:
L138: Closed as not planned
L139:
L140: Closed as not planned
L141:
L142: 【68†[Bug]: ElectronApplication.evaluate() is unreliable】#33737
L143:
L144: Copy link
L145:
L146: ## Description
L147:
L148: 【69†Jeff Robbins (jjeff)】
L149:
L150: opened 【70†on Nov 23, 2024】
L151:
L152: Issue body actions
L153: ### Version
L154:
L155: 1.49.0
L156: ### Steps to reproduce
L157: 1. Create an Electron Application
L158: 2. Package it
L159: 3. Launch it with Playwright
L160: 4. Run a test similar to the following:
L161:
L162: test('stress test: run electronApp.evaluate() over and over', async () => {
L163: const app = getApp()
L164: const iterations = 1000
L165: for (let i = 0; i < iterations; i++) {
L166: const displays = await app.evaluate(({ screen }) => screen.getAllDisplays())
L167: expect(displays).toBeTruthy()
L168: expect(displays.length).toBeGreaterThan(0)
L169: }
L170: })
L171: ### Expected behavior
L172:
L173: No errors
L174: ### Actual behavior
L175:
L176: When communicating with main or renderer processes, I regularly get errors similar to the following:
L177:
L178: * "context or browser has been closed"
L179: * "Promise was collected"
L180: * "Execution context was destroyed, most likely because of a navigation"
L181: * "Cannot read properties of undefined (reading 'getOwnerBrowserWindow')"
L182:
L183: These errors seem to happen randomly. And they will also happen with methods like `electronApplication.getBrowser(page)` or `page.evaluate()`
L184: ### Additional context
L185:
L186: This seems to have started with the release of Electron 27 and has continued in subsequent versions.
L187:
L188: I know that Electron support in Playwright is experimental. And I've been writing some workarounds for the 【71†`electron-playwright-helpers`】 library. But before I release the workarounds, I'd like to figure out what's happening.
L189:
L190: I have yet to create a minimal reproduction that generates these errors nearly as reliably as my (maximal) application.
L191: ### Environment
L192:
L193: System:
L194: OS: macOS 15.1.1
L195: CPU: (12) arm64 Apple M2 Max
L196: Memory: 262.55 MB / 32.00 GB
L197: Binaries:
L198: Node: 20.15.0 - ~/.nvm/versions/node/v20.15.0/bin/node
L199: Yarn: 1.22.18 - /usr/local/bin/yarn
L200: npm: 7.24.2 - ~/Code/missioncontrol/node_modules/.bin/npm
L201: IDEs:
L202: VSCode: 1.95.3 - /usr/local/bin/code
L203: Languages:
L204: Bash: 3.2.57 - /bin/bash
L205: npmPackages:
L206: @playwright/test: 1.49.0 => 1.49.0
L207: playwright: 1.49.0 => 1.49.0
L208: Reactions are currently unavailable
L209: ## Activity
L210:
L211: 1. ### mxschmitt commented on Nov 25, 2024
L212:
L213: 【72†Max Schmitt (mxschmitt)】
L214:
L215: 【73†on Nov 25, 2024】
L216:
L217: Contributor
L218:
L219: More actions
L220:
L221: We would like to help you - would it be possible to share a minimal repro?
L222:
L223: 2. ### Skn0tt commented on Dec 3, 2024
L224:
L225: 【74†Simon Knott (Skn0tt)】
L226:
L227: 【75†on Dec 3, 2024】
L228:
L229: Contributor
L230:
L231: More actions
L232:
L233: Please re-file and provide a self-contained reproduction, which we can run locally to reproduce your issue. Thanks for your understanding!
L234:
L235: 3. 【74†Simon Knott (Skn0tt)】
L236: closed this as 【76†not planned】【77†on Dec 3, 2024】
L237:
L238: 4. 【78†Patrick Robinson (patrob)】
L239:
L240: added 2 commits that reference this issue 【79†on Jul 15, 2026】
L241:
L242: 【80†Fix real e2e failures surfaced by the new CI gate (】【81†#402】)
L243:
L244: ...
L245:
L246: 【80†ab0669e】
L247:
L248: 【82†Run stubbed Electron e2e in CI (TD-008,】 【81†#402】) (【83†#436】)
L249:
L250: ...
L251:
L252: 【82†039a4df】
L253:
L254: 5. 【84†Neil (nwparker)】
L255:
L256: added 2 commits that reference this issue 【85†on Aug 31, 2026】
L257:
L258: 【86†test(e2e): ride out Playwright's spurious main-process evaluate rejec…】
L259:
L260: ...
L261: 【86†7c15bf4】
L262:
L263: 【87†fix(terminal): mount one surface per workspace id in the workbench (S…】
L264:
L265: ...
L266:
L267: 【87†eff3179】
L268:
L269: 6. 【88†treeman99 (treeman99)】
L270:
L271: added a commit that references this issue 【89†on Sep 2, 2026】
L272:
L273: 【90†fix(terminal): mount one surface per workspace id in the workbench (S…】
L274:
L275: ...
L276:
L277: 【90†32187ba】
L278:
L279: 7. 【91†Shane (shanedolley)】
L280:
L281: added a commit that references this issue 【92†on Sep 13, 2026】
L282:
L283: 【93†fix(terminal): mount one surface per workspace id in the workbench (S…】
L284:
L285: ...
L286:
L287: 【93†4c3106b】
L288: 8. 【94†Jens De Ketelaere (Harted)】
L289:
L290: added a commit that references this issue 【95†on Sep 23, 2026】
L291:
L292: 【96†test: ask main again when its inspector drops an evaluate answer】
L293:
L294: ...
L295:
L296: 【96†b5f79ae】
L297:
L298: 9. 【97†Brian T (odgrim)】
L299:
L300: added a commit that references this issue 【98†on Oct 1, 2026】
L301:
L302: 【99†test(e2e): retry flaky main-process evaluate in the iframe spec; drop…】
L303:
L304: ...
L305:
L306: 【99†dcb7c5c】
L307:
L308: 10. 【100†Kristian Julsgaard (Julsgaard)】
L309:
L310: added a commit that references this issue 【101†on Oct 8, 2026】
L311: 【102†test: Retry launch evaluates that V8 garbage-collects】
L312:
L313: ...
L314:
L315: 【102†bf52273】
L316:
L317: 11. 【103†Enrick Santos (enrick-dev)】
L318:
L319: added a commit that references this issue 【104†on Oct 15, 2026】
L320:
L321: 【105†fix(terminal): mount one surface per workspace id in the workbench (S…】
L322:
L323: ...
L324:
L325: 【105†a143de4】
L326:
L327: to join this conversation on GitHub. Already have an account? 【106†Sign in to comment】
L328: ## Metadata
L329:
L330: ## Metadata
L331:
L332: ### Assignees
L333:
L334: No one assigned
L335:
L336: ### Labels
L337:
L338: No labels
L339:
L340: No labels
L341:
L342: ### Type
L343:
L344: No type
L345:
L346: ### Projects
L347:
L348: No projects
L349:
L350: ### Milestone
L351:
L352: No milestone
L353:
L354: ### Relationships
L355:
L356: None yet
L357:
L358: ### Development
L359:
L360: No branches or pull requests
L361:
L362: ## Issue actions
L363:
L364: * 【107†Image†github.githubassets.com】Open in GitHub Copilot app
L365:
L366: You can’t perform that action at this time.
</excerpt>
</source>
<source kind="search">
<title>[Bug]: ElectronApplication.evaluate() is unreliable · Issue #33737 · microsoft/playwright · GitHub</title>
<location>GitHub issue 33737 in microsoft/playwright (link omitted to avoid creating a cross-reference)</location>
<excerpt>citeturn0search0 [wordlim: 200] Published: 1.9 years ago; Crawled: last month; # [Bug]: ElectronApplication.evaluate() is unreliable #33737 ... * "Promise was collected" ... And I've been writing some workarounds for the `electron-playwright-helpers` library.
# [Bug]: ElectronApplication.evaluate() is unreliable #33737
New issue
Copy link
New issue
Copy link
Closed as not planned
Closed as not planned
[Bug]: ElectronApplication.evaluate() is unreliable#33737
Copy link
## Description
Jeff Robbins (jjeff)
opened on Nov 23, 2024
Issue body actions
### Version
1.49.0
### Steps to reproduce
1. Create an Electron Application
2. Package it
3. Launch it with Playwright
4. Run a test similar to the following:
test('stress test: run electronApp.evaluate() over and over', async () => {
const app = getApp()
const iterations = 1000
for (let i = 0; i < iterations; i++) {
const displays = await app.evaluate(({ screen }) => screen.getAllDisplays())
expect(displays).toBeTruthy()
expect(displays.length).toBeGreaterThan(0)
}
})
### Expected behavior
No errors
### Actual behavior
When communicating with main or renderer processes, I regularly get errors similar to the following:
* "context or browser has been closed"
* "Promise was collected"
* "Execution context was destroyed, most likely because of a navigation"
* "Cannot read properties of undefined (reading 'getOwnerBrowserWindow')"
These errors seem to happen randomly. And they will also happen with methods like `electronApplication.getBrowser(page)` or `page.evaluate()`
### Additional context
This seems to have started with the release of Electron 27 and has continued in subsequent versions.
I know that Electron support in Playwright is experimental. And I've been writing some workarounds for the `electron-playwright-helpers` library. But before I release the workarounds, I'd like to figure out what's happening.
I have yet to create a minimal reproduction that generates these errors nearly as reliably as my (maximal) application.
### Environment
System:
OS: macOS 15.1.1
CPU: (12) arm64 Apple M2 Max
Memory: 262.55 MB / 32.00 GB
Binaries:
Node: 20.15.0 - ~/.nvm/versions/node/v20.15.0/bin/node
Yarn: 1.22.18 - /usr/local/bin/yarn
npm: 7.24.2 - ~/Code/missioncontrol/node_modules/.bin/npm
IDEs:
VSCode: 1.95.3 - /usr/local/bin/code
Languages:
Bash: 3.2.57 - /bin/bash
npmPackages:
@playwright/test: 1.49.0 => 1.49.0
playwright: 1.49.0 => 1.49.0
</excerpt>
</source>
<source kind="search">
<title>playwright/docs/src/electron-api/class-electronapplication.md at main · microsoft/playwright · GitHub</title>
<location>https://github.com/microsoft/playwright/blob/main/docs/src/electron-api/class-electronapplication.md</location>
<excerpt>citeturn0search1 [wordlim: 200] Crawled: last week; const { _electron: electron } = require('playwright'); ... If the function passed to the [`method: ElectronApplication.evaluate`] returns a [Promise], then [`method: ElectronApplication.evaluate`] would wait for the promise to resolve and return its value. ... If the function passed to the [`method: ElectronApplication.evaluateHandle`] returns a [Promise], then [`method: ElectronApplication.evaluateHandle`] would wait for the promise to resolve and return its value.
# class: ElectronApplication
* since: v1.9
* langs: js
Electron application representation. You can use [`method: Electron.launch`] to obtain the application instance. This instance you can control main electron process as well as work with Electron windows:
const { _electron: electron } = require('playwright');
(async () => {
// Launch Electron app.
const electronApp = await electron.launch({ args: ['main.js'] });
// Evaluation expression in the Electron context.
const appPath = await electronApp.evaluate(async ({ app }) => {
// This runs in the main Electron process, parameter here is always
// the result of the require('electron') in the main app script.
return app.getAppPath();
});
console.log(appPath);
// Get the first window that the app opens, wait if necessary.
const window = await electronApp.firstWindow();
// Print the title.
console.log(await window.title());
// Capture a screenshot.
await window.screenshot({ path: 'intro.png' });
// Direct Electron console to Node terminal.
window.on('console', console.log);
// Click button.
await window.click('text=Click me');
// Exit app.
await electronApp.close();
})();
## event: ElectronApplication.close
* since: v1.9
This event is issued when the application process has been terminated.
## event: ElectronApplication.console
* since: v1.42
* argument: <[ConsoleMessage]>
Emitted when JavaScript within the Electron main process calls one of console API methods, e.g. `console.log` or `console.dir`.
The arguments passed into `console.log` are available on the [ConsoleMessage] event handler argument.
Usage
electronApp.on('console', async msg => {
const values = [];
for (const arg of msg.args())
values.push(await arg.jsonValue());
console.log(...values);
});
await electronApp.evaluate(() => console.log('hello', 5, { foo: 'bar' }));
## event: ElectronApplication.window
* since: v1.9
* argument: <[Page]>
This event is issued for every window that is created and loaded in Electron. It contains a [Page] that can be used for Playwright automation.
## async method: ElectronApplication.browserWindow
* since: v1.11
* returns: <[JSHandle]>
Returns the BrowserWindow object that corresponds to the given Playwright page.
### param: ElectronApplication.browserWindow.page
* since: v1.11
* `page` <[Page]>
Page to retrieve the window for.
## async method: ElectronApplication.close
* since: v1.9
Closes Electron application.
## method: ElectronApplication.context
* since: v1.9
* returns: <[BrowserContext]>
This method returns browser context that can be used for setting up context-wide routing, etc.
## async method: ElectronApplication.evaluate
* since: v1.9
* returns: <[Serializable]>
Returns the return value of [`param: expression`].
If the function passed to the [`method: ElectronApplication.evaluate`] returns a [Promise], then [`method: ElectronApplication.evaluate`] would wait for the promise to resolve and return its value.
If the function passed to the [`method: ElectronApplication.evaluate`] returns a non-[Serializable] value, then [`method: ElectronApplication.evaluate`] returns `undefined`. Playwright also supports transferring some additional values that are not serializable by `JSON`: `-0`, `NaN`, `Infinity`, `-Infinity`.
### param: ElectronApplication.evaluate.expression = %%-evaluate-expression-%%
* since: v1.9
### param: ElectronApplication.evaluate.expression = %%-js-electron-evaluate-workerfunction-%%
* since: v1.9
### param: ElectronApplication.evaluate.arg
* since: v1.9
* `arg` ?<[EvaluationArgument]>
Optional argument to pass to [`param: expression`].
## async method: ElectronApplication.evaluateHandle
* since: v1.9
* returns: <[JSHandle]>
Returns the return value of [`param: expression`] as a [JSHandle].
The only difference between [`method: ElectronApplication.evaluate`] and [`method: ElectronApplication.evaluateHandle`] is that [`method: ElectronApplication.evaluateHandle`] returns [JSHandle].
If the function passed to the [`method: ElectronApplication.evaluateHandle`] returns a [Promise], then [`method: ElectronApplication.evaluateHandle`] would wait for the promise to resolve and return its value.
### param: ElectronApplication.evaluateHandle.expression = %%-evaluate-expression-%%
* since: v1.9
### param: ElectronApplication.evaluateHandle.expression = %%-js-electron-evaluate-workerfunction-%%
* since: v1.9
### param: ElectronApplication.evaluateHandle.arg
* since: v1.9
* `arg` ?<[EvaluationArgument]>
Optional argument to pass to [`param: expression`].
## async method: ElectronApplication.firstWindow
* since: v1.9
* returns: <[Page]>
Convenience method that waits for the first application window to be opened.
Usage
const electronApp = await electron.launch({
args: ['main.js']
});
const window = await electronApp.firstWindow();
// ...
### option: ElectronApplication.firstWindow.timeout
* since: v1.33
* `timeout` ?<[float]>
Maximum time to wait for in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can be changed by using the [`method: BrowserContext.setDefaultTimeout`].
## method: ElectronApplication.process
* since: v1.21
* returns: <[ChildProcess]>
Returns the main process for this Electron Application.
## async method: ElectronApplication.waitForEvent
* since: v1.9
* returns: <[any]>
Waits for event to fire and passes its value into the predicate function. Returns when the predicate returns truthy value. Will throw an error if the application is closed before the event is fired. Returns the event data value.
Usage
const windowPromise = electronApp.waitForEvent('window');
await mainWindow.click('button');
const window = await windowPromise;
### param: ElectronApplication.waitForEvent.event = %%-wait-for-event-event-%%
* since: v1.9
### param: ElectronApplication.waitForEvent.optionsOrPredicate
* since: v1.9
* langs: js
* `optionsOrPredicate` ?<[function]|[Object]>
* `predicate` <[function]> receives the event data and resolves to truthy value when the waiting should resolve.
* `timeout` ?<[float]> maximum time to wait for in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can be changed by using the [`method: BrowserContext.setDefaultTimeout`].
Either a predicate that receives an event or an options object. Optional.
## method: ElectronApplication.windows
* since: v1.9
* returns: <[Array]<[Page]>>
Convenience method that returns all the opened windows.</excerpt>
</source>
<source kind="search">
<title>electron-playwright-helpers - npm</title>
<location>https://www.npmjs.com/package/electron-playwright-helpers?activeTab=readme</location>
<excerpt>citeturn0search3 [wordlim: 200] Crawled: last month; ### "Resulting promise was garbage collected." is not retried by default ... await electronApp.evaluate(() => new Promise(() => {})) ... electronApp | `ElectronApplication` | | the Playwright ElectronApplication ... * the result of the evaluation
### "Resulting promise was garbage collected." is not retried by default
Playwright awaits your promise through the debugger protocol, and V8's inspector tracks it with a weak handle. If nothing in the target process references that promise, it gets collected before it can settle and the reply comes back as this error.
Two quite different things produce it, and they want opposite responses.
Case 1 — your `evaluate()` callback returned a dangling promise.
// this promise is unreachable the moment evaluate() returns - it can never settle
await electronApp.evaluate(() => new Promise(() => {}))
* It is deterministic, not intermittent. The same call fails the same way every time, so retrying only burns the timeout and buries the real cause under a "Timeout after 5000ms" message.
* Your callback body already ran. Its side effects happened; only the reply was lost. That makes an automatic retry actively unsafe for anything non-idempotent.
The fix is in the callback: return a value, or return a promise that something retains - one backed by an Electron API call, a timer, or an event listener. function retryThroughGC<T>(fn: () => Promise<T>) {
const { errorMatch } = getRetryOptions()
const existing = Array.isArray(errorMatch) ? errorMatch : [errorMatch]
return retry(fn, { errorMatch: [...existing, 'promise was garbage collected'] })
}
const win = await retryThroughGC(() => electronApp.browserWindow(page))
To turn the old retry-anyway behavior back on everywhere instead, note that `errorMatch` replaces the default list rather than extending it, so repeat the defaults you still want:
import { retry, setRetryOptions } from 'electron-playwright-helpers'
const errorMatch = [
'context or browser has been closed',
'Execution context was destroyed',
"reading 'getOwnerBrowserWindow'",
'promise was garbage collected',
]
// per call...
await retry(() => electronApp.evaluate(myFn), { errorMatch })
// ...or globally
setRetryOptions({ errorMatch })
Note that on Playwright < 1.62 you will never see this error by name. Those versions have no branch for the underlying protocol message, so it falls through to the generic "Execution context was destroyed" and is retried as if it were a teardown error.
</excerpt>
</source>
<source kind="search">
<title>ElectronApplication | Playwright</title>
<location>https://playwright.help/docs/api/class-electronapplication</location>
<excerpt>citeturn0search2 [wordlim: 200] Crawled: 10 months ago; `const { _electron: electron } = require('playwright'); ... Если функция, переданная в electronApplication.evaluate(), возвращает Promise, то electronApplication.evaluate() будет ждать разрешения промиса и вернет его значение. ... Если функция, переданная в electronApplication.evaluateHandle(), возвращает Promise, то electronApplication.evaluateHandle() будет ждать разрешения промиса и вернет его значение.
### evaluate
Добавлено в: v1.9 electronApplication.evaluate
Возвращает значение, возвращаемое pageFunction.
Если функция, переданная в electronApplication.evaluate(), возвращает Promise, то electronApplication.evaluate() будет ждать разрешения промиса и вернет его значение.
Если функция, переданная в electronApplication.evaluate(), возвращает не-Serializable значение, то electronApplication.evaluate() вернет `undefined`. Playwright также поддерживает передачу некоторых дополнительных значений, которые не сериализуемы с помощью `JSON`: `-0`, `NaN`, `Infinity`, `-Infinity`.
Использование
`await electronApplication.evaluate(pageFunction);
await electronApplication.evaluate(pageFunction, arg);
`
Аргументы
* `pageFunction` function | Electron#
Функция, которая будет оцениваться в основном процессе Electron.
* `arg` EvaluationArgument (опционально)#
Опциональный аргумент, который передается в pageFunction.
Возвращает
* Promise<Serializable>#
### evaluateHandle
Добавлено в: v1.9 electronApplication.evaluateHandle
Возвращает значение, возвращаемое pageFunction в виде JSHandle.
Единственное отличие между electronApplication.evaluate() и electronApplication.evaluateHandle() заключается в том, что electronApplication.evaluateHandle() возвращает JSHandle.
Если функция, переданная в electronApplication.evaluateHandle(), возвращает Promise, то electronApplication.evaluateHandle() будет ждать разрешения промиса и вернет его значение.
Использование
`await electronApplication.evaluateHandle(pageFunction);
await electronApplication.evaluateHandle(pageFunction, arg);
`
Аргументы
* `pageFunction` function | Electron#
Функция, которая будет оцениваться в основном процессе Electron.
* `arg` EvaluationArgument (опционально)#
Опциональный аргумент, который передается в pageFunction.
Возвращает
* Promise<JSHandle>#
### firstWindow
Добавлено в: v1.9 electronApplication.firstWindow
Удобный метод, который ждет открытия первого окна приложения.
Использование
`const electronApp = await electron.launch({
args: ['main.js']
});
const window = await electronApp.firstWindow();
// ...
`
Аргументы
* `options` Object (опционально)
* `timeout` number (опционально) Добавлено в: v1.33#
Максимальное время ожидания в миллисекундах. По умолчанию `30000` (30 секунд). Передайте `0`, чтобы отключить тайм-аут. Значение по умолчанию можно изменить с помощью browserContext.setDefaultTimeout().
Возвращает
* Promise<Page>#
### process
Добавлено в: v1.21 electronApplication.process
Возвращает основной процесс для этого приложения Electron.
Использование
`electronApplication.process();
`
Возвращает
* ChildProcess#
### waitForEvent
Добавлено в: v1.9 electronApplication.waitForEvent
Ожидает срабатывания события и передает его значение в предикатную функцию. Возвращает результат, когда предикат возвращает истинное значение. Выдаст ошибку, если приложение будет закрыто до срабатывания события. Возвращает значение данных события.
Использование
`const windowPromise = electronApp.waitForEvent('window');
await mainWindow.click('button');
</excerpt>
</source>
<omitted_sources count="17">Additional source records were omitted from this bounded result.</omitted_sources>
</source_evidence>
Citations:
- 1: GitHub issue 33737 in microsoft/playwright (link omitted to avoid creating a cross-reference)
- 2: https://www.npmjs.com/package/electron-playwright-helpers?activeTab=readme&utm_source=openai
- 3: https://github.com/microsoft/playwright/blob/main/docs/src/electron-api/class-electronapplication.md?utm_source=openai
- 4: https://www.npmjs.com/package/electron-playwright-helpers?activeTab=readme&utm_source=openai
Make the clear replay-safe before retrying it.
retryTransientMainEvaluate retries after Resulting promise was garbage collected. Playwright can report this after the evaluated callback has already run. A PTY write can therefore append an entry during the 200 ms delay, and the retry can erase it.
Use a per-call request ID and record completed clear requests in the main process. A replay of the same request must not clear the log again. Do not replace the retry with an unwrapped call unless the caller accepts that a transient failure can leave the log uncleared.
…ent CDP failures
The spy already retried its install through retryTransientMainEvaluate, but its
reads, clear and delay setter did not, so a 'Resulting promise was garbage
collected' (playwright#33737) failed a passing Windows WSL paste test.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
ELI5
A Windows WSL paste e2e test failed on a CI hiccup in Playwright, not on Orca. The test helper already retries that exact hiccup when it installs its spy, but not when it reads the spy's log afterwards. Now every call in the helper retries it the same way. Test-only.
What Changed
tests/e2e/helpers/terminal-pty-write-spy.ts:clearTerminalPtyWriteLog,readTerminalPtyWrites,readTerminalPtyWriteEntriesandsetTerminalPtyWriteDelaynow go through the existingretryTransientMainEvaluate, asinstallTerminalPtyWriteSpyalready did.Why
The "Real WSL terminal" job (
windows-wsl-e2e.yml,--repeat-each=3, no retries) failed twice on #34694. Both times it was one repeat ofterminal-windows-shell-paste-ownership.spec.ts› "existing WSL terminal keeps paste runtime after default shell changes", failing withelectronApplication.evaluate: Resulting promise was garbage collectedatreadTerminalPtyWrites(helpers/terminal-pty-write-spy.ts:58), which is the spy read itself (run 40655378916, job 122705162336). The other repeats passed. #34694's kill-switch changes don't run in that spec: no WSL host is saved and the gate is closed, so there's no WSL host API, capability or serve-only argument. The error is the transient the repo's retry helper exists for.Linked Issue
N/A (e2e flake).
Visual Proof
N/A: test-only.
Testing
terminal-paste-ownership.spec.ts, which uses the same spy, passes locally hidden: 5 passed, 1 skipped as Windows-only.The Windows WSL spec runs only on CI's Windows runner.
typecheck:e2e, oxlint, the anti-slop lint,ast-grep-gates.mjsandcheck:code-quality:changedpass.I manually tested these changes locally
Automated tests added/updated, or explained why not below
AI Disclosure
Review
Agent skill upstream boundary
Notes
Test-only.
Checklist
N/Awith reasonpnpm lint,pnpm typecheck,pnpm test, andpnpm buildpass (or CI will cover; local preferred)