fix: replace playwright-core monkey patches with a public-API har engine - #64
Draft
vanilla-wave wants to merge 12 commits into
Draft
fix: replace playwright-core monkey patches with a public-API har engine#64vanilla-wave wants to merge 12 commits into
vanilla-wave wants to merge 12 commits into
Conversation
Contributor
|
🚀 Prerelease version published! Install this PR version: npm i --save-dev @gravity-ui/playwright-tools@2.0.2-beta.a98d26971c986205c17a06245b9260462d6f9dcc.0 |
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Problem
Playwright bundled all of
lib/server/**intolib/coreBundle.jsstarting with playwright-core 1.60.0 (PRs microsoft/playwright#40057 + #40074; first bundled artifact1.60.0-alpha-2026-04-07, first stable 1.60.0).har/getPlaywrightCoreModule.tsresolves absolute file paths insideplaywright-coreandrequire()s them:lib/server/dispatchers/localUtilsDispatcher— patched forharOpen/harLookuplib/server/har/harRecorder— patched foronEntryFinished/flushBoth now throw
MODULE_NOT_FOUND, so the whole HAR feature is dead on Playwright >= 1.60, and./fixturesis affected transitively throughmockNetworkFixturesBuilder→harPatcher.@playwright/test@1.62has nolib/at all, so the second resolution branch is dead too.coreBundle'sservernamespace exports no HAR class, and1.63.0-alphais the same shape.Worse, the failure is silent:
patchInited = trueis the first line of each init function, beforegetPlaywrightCoreModule()throws. In every worker the first four tests die with four different errors and tests 5+ register no transforms and throw nothing — the recordedhttps://base.url.placeholderis never rewritten back, every lookup returnsnoentry, andnotFound: 'abort'aborts everything.Solution
Stop reaching into
playwright-core.har/getPlaywrightCoreModule.tsis deleted — norequire()of internal paths remains.initDumps()now callsinstallHarEngine(page), which wraps the publicrouteFromHARon the client prototypes:addHarOpenTransformis applied by handing Playwright an already-rewritten copy of the dump;addHarLookupTransformwraps the client-sideLocalUtils.harLookupthat Playwright's own HAR router calls.routeFromHAR({update: true})writes to a temporary path; the record-side transforms are applied to the written dump, which is only then moved to its final location. If anything fails the dump is simply missing instead of being committed with unscrubbed headers.context.route()with a vendored port of Playwright's matcher (har/vendor/, Apache-2.0,LICENSE+NOTICEshipped). Announced once per process with aHAR engine degraded (...)warning.Two record-side notes, both of which also applied before this change:
addHarRecorderTransform/addFlushTransformrun aftercontext.close()rather than during the test. SameEntryobjects, same order, same result on disk.HarRecorder.onEntryFinishedhas had an empty body in every release from 1.22 to 1.63-alpha, so the old hook only ever worked through by-reference mutation;addFlushTransformwas additionally dead on 1.60+ becauseexport()calls_flush(), not the patchedflush().content._file, so they are not reachable from these hooks. Rewriting a body per request is whataddHarLookupTransform'stransformResultis for.Compatibility
All four documented hooks keep their names, signatures, argument shapes, return contracts and first-call-wins latch semantics.
initDumps,clearHeaders,replaceBaseUrlInEntry,setExtraHash, the path builders and all exported types are untouched.getPlaywrightCoreModulewas never exported fromhar/index.tsnor listed in theexportsmap, so deleting it is invisible from outside.Additive only:
installHarEngine,resetHarTransforms,setFixtureHarTransforms.The oldest supported version is 1.23 — earlier ones have no
routeFromHARat all, so the declared peer range^1.22is wider than what actually works.Verification
Record → post-process → replay round trip with all four hooks firing, same bytes on 1.58.1 and 1.62.1.
Validated against a large real consumer (Yandex Tracker, ~2400 committed dumps) on Playwright 1.62.1:
JSON.stringify(oldHar) === JSON.stringify(newHar), all zip blobs identical)Tests
har/had no tests at all, which is why both the 1.60 breakage and the long-deadaddFlushTransformwent unnoticed. Added 41 unit tests and 2 integration ones:har/vendor/__tests__/harBackend.test.ts— the matching semantics the whole feature rests on: exact url + method, byte-exact POST bodies, multipart-boundary tolerance, the header tie-break thatmarkIdenticalRequests/setExtraHashdepend on, document order on a tie, redirect following, the POST → GET downgrade, redirect cycles,_filesidecars and the path-traversal guard, base64 bodies, archives.har/vendor/__tests__/zip.test.ts— reads an archive recorded by Playwright itself (checked in as a fixture: it carries the data-descriptor flag, so the sizes are only valid in the central directory), round-trips, and rewrites it.har/vendor/__tests__/harJsonStringify.test.ts— the serializer is pinned to Playwright's own layout so that re-recording a dump does not produce a formatting-only diff.har/engine/__tests__/transformRegistry.test.ts— the first-call-wins latch, per-call independence, fixture vs legacy precedence, reset.har/engine/__tests__/harPostProcessor.test.ts— record-side transforms applied to the written dump, ordering between the per-entry and flush passes, orphaned blobs dropped, uncompressed dumps.tests/har/har.test.ts— a real record → transform → replay round trip against a local origin server, asserting that all four hooks fired, that the dump on disk is scrubbed and rewritten, and that replay serves the transformed body while the origin (now answeringLIVE) is never reached. Plus a recorded navigation redirect.CI
.github/workflows/har-matrix.ymlruns the unit and integration tests against Playwright 1.49.1, 1.55.0, 1.58.1, 1.59.0, 1.60.0 and latest — deliberately spanning the 1.59 → 1.60 bundling boundary that broke the previous implementation — plus a nightly, non-blockingnextcanary.nextis published daily, and the 1.60 bundling landed there 34 days before it reached a stable release.