feat: add playwright integration feature #141
Open
miinhho wants to merge 7 commits intofacebook:mainfrom
Open
Conversation
New package giving existing Playwright tests opt-in memory-leak detection via a single fixture. Destructuring `memlab` in the test body activates CDP-based heap capture; omitting it leaves the test untouched. - src/snapshot.ts: CDPLike duck-typed session + writeHeapSnapshot / forceFullGC, usable against Playwright or Puppeteer CDP without reshaping the existing @memlab/e2e code path. - src/capturer.ts: PlaywrightHeapCapturer low-level API (attach, snapshot, findLeaks via SnapshotResultReader). - src/test.ts: test.extend fixture that captures baseline/target/final, attaches memlab-leaks.json plus raw .heapsnapshot artifacts to testInfo, and soft-fails on detected leaks. Non-Chromium projects get a no-op fixture with a memlab-skip annotation. - __tests__/smoke.mjs: manual spike verifying CDP snapshot round-trip.
…lter Verifies the package end-to-end against a leaky React component (setInterval closure without cleanup), a clean counterpart, and a no-op control. - snapshot.ts: discard CDP console entries before the GC cycle so detached DOM nodes held by Chrome's console ring aren't reported as leaks. - test.ts: post-filter leak traces whose retainer path is owned by CDP inspector state (DevTools console, Inspector, CommandLineAPI). These are artifacts of how Playwright drives the page, not application leaks. - tsconfig.json: drop the base paths extend so Playwright's TS loader resolves @memlab/* via node_modules instead of rewriting to monorepo source files (which hit a circular import at require time). - __tests__/: React fixtures (unpkg UMD, no build step), playwright config, shielded tsconfig with empty paths, and the spec itself. Run with `npm run test-e2e` inside packages/playwright.
The previous spec used React UMD via unpkg loaded from file:// URLs, which skips JSX compilation, module resolution, and the HTTP origin semantics of any real application. This commit swaps in a Vite dev server running a production-shaped React 18 fixture so the spec exercises the same code paths users hit in practice. - packages/playwright/__tests__/fixtures/vite-react/: new fixture app with JSX (Leaky + Clean components driven by ?mode=leaky|clean), registered as a monorepo workspace so `npm install` at the root pulls in its deps. - playwright.config.ts: webServer launches `vite` on 127.0.0.1:5174 and Playwright sets baseURL so specs use relative paths. - react-leak.spec.ts: navigate to http://.../?mode=leaky|clean instead of pathToFileURL(...). - HMR disabled in vite.config.ts — the HMR websocket client pollutes heap snapshots and muddies the leak signal. All three existing specs continue to pass (leaky detected, clean clean, no-op fixture inert).
Adds a 7-closure-pattern × leaky/clean matrix (plus detached-DOM) that asserts each leak's retainer trace includes the retained payload (>=1MB) and that, without a user leakFilter, memlab's built-in rules silently miss closure-retained JS state -- the gap that leakFilter exists to address. Required adding the leakFilter option on PlaywrightHeapCapturer and expanding the fixture App into ?mode=-routed leak-pattern components. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…rting Split fixture into concern-scoped modules (types.ts, leak.ts, fixture.ts, thin index.ts barrel). Collapse the `@memlab/playwright/test` subpath into the root entry so users import from `@memlab/playwright`. Bring package metadata in line with other memlab packages: add LICENSE, publishConfig.access=public, tsconfig extends ../../tsconfig.base.json, test-pkg runs the e2e suite, README trimmed to the house format. Fix leakSummary: ISerializedInfo is a recursive dict where retainedSize is encoded inside keys as \`\$retained-size:N\`, so reading flat \`retainedSize\`/\`name\`/\`type\` was always undefined and the auto-mode teardown message always rendered \"facebook#1: leak\". It now picks the first non-\$tabsOrder trace key as the one-line digest. isInspectorArtifact walks the full serialized trace instead of only top-level keys so DevTools/Inspector retainers appearing mid-chain are filtered too. Expose GC tuning through MemlabFixture.configure({gc: {...}}) instead of the hardcoded 6-cycle; tighten CDPLike (send returns Promise<unknown>, event handlers stay \`any\` to accept narrower Playwright/Puppeteer signatures under strict function types). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace the auto-teardown's array dump with a scalar assertion, pick the leaked-node key (not the Window root) for summaries, strip internal $tabsOrder metadata from attached JSON, and skip heap-snapshot attachment on clean runs. Add expectNoLeaks() for the common "run a flow, assert no leaks" pattern with triage-friendly failure messages. Replace the algorithm-focused matrix spec with an integration-focused fixture spec. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The fixture only needs to exercise leak patterns (detached DOM, subscription, interval closure) against a real HTTP origin — React and Vite were incidental. Replace with a static HTML + vanilla JS page served by a 27-line node:http script. Drops four test-only dependencies (react, react-dom, vite, @vitejs/plugin-react), removes the CI npm-install step, and simplifies clean-pkg. Rename the smoke spec to drop the now-inaccurate [react] tag. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.
Summary
New
@memlab/playwrightpackage — a Playwright fixture that attaches memlab's leak detection to existing Playwright tests by destructuring amemlabparameter. No scenario rewrite, no Puppeteer dependency.Motivation
memlab's existing APIs (
@memlab/api,memlab run) drive the browser through a Puppeteer-based scenario runner. Teams already running Playwright e2e suites can't attach leak detection without rewriting their specs. This package bridges the gap by piggybacking on the user's existing page via a CDP session.Design notes
forceFullGCviaHeapProfiler.collectGarbage CDP, no--expose-gcflag required.memlab.configure({gc, leakFilter})for per-project GC tuning and custom filters. Merge semantics preserve prior fields.formatLeakMessagepicks the$memLabTag:leakedhop (not the Window root), strips memlab-internal$tabsOrdermetadata from attached JSON, and heap snapshots only attach when leaks are detected (skips ~13 MB per clean test).API
memlab.baseline(): Capture baseline heap snapshotmemlab.target()/memlab.final(): Manual phase controlmemlab.configure({gc?, leakFilter?}): Per-test tuningmemlab.findLeaks(): ReturnsISerializedInfo[] | nullmemlab.expectNoLeaks(): Hard assertion with triage-friendly errorIf neither
findLeaksnorexpectNoLeaksis called, the fixture auto-captures missing phases and soft-asserts no leaks during teardown.