@agimon-ai/browse-tool 0.2.19 → 0.2.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.cjs +1 -1
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.mjs +1 -1
- package/dist/cli.mjs.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{playwright-test-Czm92Vp-.cjs → playwright-test-CGAZOnPw.cjs} +3 -3
- package/dist/playwright-test-CGAZOnPw.cjs.map +1 -0
- package/dist/{playwright-test-yNsP599T.mjs → playwright-test-lZlw-zhY.mjs} +2 -2
- package/dist/playwright-test-lZlw-zhY.mjs.map +1 -0
- package/dist/streamable-http-B2_gj_b3.mjs +15 -0
- package/dist/streamable-http-B2_gj_b3.mjs.map +1 -0
- package/dist/{streamable-http-DfMXvzhA.cjs → streamable-http-DsuJYpv3.cjs} +5 -5
- package/dist/streamable-http-DsuJYpv3.cjs.map +1 -0
- package/dist/stubs/playwright-test.cjs +1 -1
- package/dist/stubs/playwright-test.mjs +1 -1
- package/package.json +4 -4
- package/dist/playwright-test-Czm92Vp-.cjs.map +0 -1
- package/dist/playwright-test-yNsP599T.mjs.map +0 -1
- package/dist/streamable-http-C_i3KS1P.mjs +0 -15
- package/dist/streamable-http-C_i3KS1P.mjs.map +0 -1
- package/dist/streamable-http-DfMXvzhA.cjs.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
const e=require(`../playwright-test-
|
|
1
|
+
const e=require(`../playwright-test-CGAZOnPw.cjs`);exports.SkipTestError=e.t,exports.chromium=e.n,exports.defineConfig=e.r,exports.devices=e.i,exports.expect=e.a,exports.firefox=e.o,exports.getCollectedSuite=e.s,exports.initCollector=e.c,exports.mergeExpects=e.l,exports.mergeTests=e.u,exports.peekCollectedSuite=e.d,exports.request=e.f,exports.resetCollector=e.p,exports.selectors=e.m,exports.splitUseConfig=e.h,exports.test=e.g,exports.webkit=e._;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{_ as e,a as t,c as n,d as r,f as i,g as a,h as o,i as s,l as c,m as l,n as u,o as d,p as f,r as p,s as m,t as h,u as g}from"../playwright-test-lZlw-zhY.mjs";export{h as SkipTestError,u as chromium,p as defineConfig,s as devices,t as expect,d as firefox,m as getCollectedSuite,n as initCollector,c as mergeExpects,g as mergeTests,r as peekCollectedSuite,i as request,f as resetCollector,l as selectors,o as splitUseConfig,a as test,e as webkit};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agimon-ai/browse-tool",
|
|
3
3
|
"description": "MCP server for browser automation using Playwright with profile management, page registry, and multi-browser support",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.21",
|
|
5
5
|
"license": "BUSL-1.1",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"mcp",
|
|
@@ -43,9 +43,9 @@
|
|
|
43
43
|
"esbuild": "^0.25.0",
|
|
44
44
|
"ws": "8.18.0",
|
|
45
45
|
"zod": "^4.3.6",
|
|
46
|
-
"@agimon-ai/foundation-port-registry": "0.2.
|
|
47
|
-
"@agimon-ai/
|
|
48
|
-
"@agimon-ai/
|
|
46
|
+
"@agimon-ai/foundation-port-registry": "0.2.13",
|
|
47
|
+
"@agimon-ai/log-sink-mcp": "0.2.13",
|
|
48
|
+
"@agimon-ai/foundation-process-registry": "0.2.9"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"@types/chrome": "0.0.322",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"playwright-test-Czm92Vp-.cjs","names":["page: IExtensionPageProxy","steps: LocatorStep[]","filterOpts: Record<string, unknown>","defs: Map<string, FixtureFunction>","parent: FixtureScope | null","testBlock: TestBlock","parentScope: FixtureScope | null","test","describe","parts: string[]","current: DescribeBlock | null","tests: TestBlock[]","allTestsInBlock: TestBlock[]","suite: TestSuite","resolved: TestFixtures","expect: typeof playwrightExpect"],"sources":["../src/services/LocatorProxy.ts","../src/utils/extension-expect.ts","../src/stubs/playwright-test.ts"],"sourcesContent":["/**\n * LocatorProxy\n *\n * DESIGN PATTERNS:\n * - Proxy pattern for Playwright Locator API compatibility\n * - Builder pattern for chainable locator steps\n * - Adapter pattern to bridge DOM evaluation and MCP browser tools\n *\n * CODING STANDARDS:\n * - Use async/await for asynchronous operations\n * - Throw descriptive errors for error cases\n * - Match Playwright Locator interface methods\n * - Route actions through ExtensionPageProxy\n *\n * AVOID:\n * - Blocking operations\n * - Missing error handling\n * - Mutating locator steps (always return new instances)\n */\n\nimport type { IExtensionPageProxy } from './ExtensionPageProxy.js';\n\n/**\n * Brand symbol for identifying LocatorProxy instances in expect()\n */\nexport const LOCATOR_PROXY_BRAND = Symbol.for('__locatorProxyBrand__');\n\n/**\n * A single step in the locator chain\n */\nexport type LocatorStep =\n | { type: 'role'; role: string; options?: { name?: string | RegExp; exact?: boolean } }\n | { type: 'text'; text: string | RegExp; options?: { exact?: boolean } }\n | { type: 'label'; text: string | RegExp; options?: { exact?: boolean } }\n | { type: 'placeholder'; text: string | RegExp; options?: { exact?: boolean } }\n | { type: 'testId'; testId: string }\n | { type: 'css'; selector: string }\n | { type: 'filter'; options: { hasText?: string | RegExp; has?: LocatorProxy } }\n | { type: 'first' }\n | { type: 'nth'; index: number };\n\n/**\n * Action types that buildResolveScript can execute\n */\ntype ResolveAction = 'mark' | 'isVisible' | 'getProperty' | 'textContent' | 'count' | 'innerText' | 'inputValue';\n\n/**\n * Chainable locator proxy that mimics Playwright's Locator API.\n * Accumulates locator steps and resolves them in-page at action time.\n */\nexport class LocatorProxy {\n readonly [LOCATOR_PROXY_BRAND] = true;\n\n constructor(\n private readonly page: IExtensionPageProxy,\n private readonly steps: LocatorStep[],\n ) {}\n\n // ── Chaining methods ──────────────────────────────────────────────\n\n getByRole(role: string, options?: { name?: string | RegExp; exact?: boolean }): LocatorProxy {\n return new LocatorProxy(this.page, [...this.steps, { type: 'role', role, options }]);\n }\n\n getByText(text: string | RegExp, options?: { exact?: boolean }): LocatorProxy {\n return new LocatorProxy(this.page, [...this.steps, { type: 'text', text, options }]);\n }\n\n getByLabel(text: string | RegExp, options?: { exact?: boolean }): LocatorProxy {\n return new LocatorProxy(this.page, [...this.steps, { type: 'label', text, options }]);\n }\n\n getByPlaceholder(text: string | RegExp, options?: { exact?: boolean }): LocatorProxy {\n return new LocatorProxy(this.page, [...this.steps, { type: 'placeholder', text, options }]);\n }\n\n getByTestId(testId: string): LocatorProxy {\n return new LocatorProxy(this.page, [...this.steps, { type: 'testId', testId }]);\n }\n\n locator(selector: string): LocatorProxy {\n return new LocatorProxy(this.page, [...this.steps, { type: 'css', selector }]);\n }\n\n filter(options: { hasText?: string | RegExp; has?: LocatorProxy }): LocatorProxy {\n return new LocatorProxy(this.page, [...this.steps, { type: 'filter', options }]);\n }\n\n first(): LocatorProxy {\n return new LocatorProxy(this.page, [...this.steps, { type: 'first' }]);\n }\n\n last(): LocatorProxy {\n return this.nth(-1);\n }\n\n nth(index: number): LocatorProxy {\n return new LocatorProxy(this.page, [...this.steps, { type: 'nth', index }]);\n }\n\n // ── Action methods ────────────────────────────────────────────────\n\n async click(options?: { timeout?: number }): Promise<void> {\n const selector = await this.resolveAndMark(options?.timeout);\n await this.page.click(selector);\n }\n\n async fill(value: string, options?: { timeout?: number }): Promise<void> {\n const selector = await this.resolveAndMark(options?.timeout);\n await this.page.fill(selector, value);\n }\n\n async type(text: string, options?: { delay?: number; timeout?: number }): Promise<void> {\n const selector = await this.resolveAndMark(options?.timeout);\n await this.page.type(selector, text, { delay: options?.delay });\n }\n\n async press(key: string): Promise<void> {\n const selector = await this.resolveAndMark();\n await this.page.click(selector);\n await this.page.press(key);\n }\n\n async hover(options?: { timeout?: number }): Promise<void> {\n const selector = await this.resolveAndMark(options?.timeout);\n await this.page.hover(selector);\n }\n\n async selectOption(values: string | string[]): Promise<void> {\n const selector = await this.resolveAndMark();\n await this.page.selectOption(selector, values);\n }\n\n async check(): Promise<void> {\n const selector = await this.resolveAndMark();\n await this.page.click(selector);\n }\n\n async uncheck(): Promise<void> {\n const selector = await this.resolveAndMark();\n await this.page.click(selector);\n }\n\n async isVisible(): Promise<boolean> {\n const script = this.buildResolveScript('isVisible');\n const result = await this.page.evaluate<boolean>(script);\n return result ?? false;\n }\n\n async isHidden(): Promise<boolean> {\n return !(await this.isVisible());\n }\n\n async isEnabled(): Promise<boolean> {\n const disabled = await this.evaluateProperty('disabled');\n return !disabled;\n }\n\n async isDisabled(): Promise<boolean> {\n const disabled = await this.evaluateProperty('disabled');\n return !!disabled;\n }\n\n async isChecked(): Promise<boolean> {\n const checked = await this.evaluateProperty('checked');\n return !!checked;\n }\n\n // ── Helper / query methods ────────────────────────────────────────\n\n async evaluateProperty(prop: string): Promise<unknown> {\n const script = this.buildResolveScript('getProperty', prop);\n return this.page.evaluate<unknown>(script);\n }\n\n async textContent(): Promise<string | null> {\n const script = this.buildResolveScript('textContent');\n return this.page.evaluate<string | null>(script);\n }\n\n async innerText(): Promise<string> {\n const script = this.buildResolveScript('innerText');\n const result = await this.page.evaluate<string>(script);\n return result ?? '';\n }\n\n async inputValue(): Promise<string> {\n const script = this.buildResolveScript('inputValue');\n const result = await this.page.evaluate<string>(script);\n return result ?? '';\n }\n\n async count(): Promise<number> {\n const script = this.buildResolveScript('count');\n const result = await this.page.evaluate<number>(script);\n return result ?? 0;\n }\n\n async waitFor(options?: { state?: 'visible' | 'hidden' | 'attached' | 'detached'; timeout?: number }): Promise<void> {\n const timeout = options?.timeout ?? 5000;\n const state = options?.state ?? 'visible';\n const start = Date.now();\n const pollInterval = 100;\n\n while (Date.now() - start < timeout) {\n if (state === 'visible' && (await this.isVisible())) return;\n if (state === 'hidden' && !(await this.isVisible())) return;\n if (state === 'attached' && (await this.count()) > 0) return;\n if (state === 'detached' && (await this.count()) === 0) return;\n await new Promise((r) => setTimeout(r, pollInterval));\n }\n\n throw new Error(`Locator waitFor(\"${state}\") timed out after ${timeout}ms`);\n }\n\n // ── Internal ──────────────────────────────────────────────────────\n\n /**\n * Resolves the locator chain in the page, marks the target element\n * with a data-pw-proxy attribute, and returns the CSS selector.\n */\n private async resolveAndMark(timeout?: number): Promise<string> {\n const effectiveTimeout = timeout ?? 5000;\n const start = Date.now();\n const pollInterval = 100;\n const markId = `pw-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;\n\n while (Date.now() - start < effectiveTimeout) {\n const script = this.buildResolveScript('mark', markId);\n const found = await this.page.evaluate<boolean>(script);\n if (found) {\n return `[data-pw-proxy=\"${markId}\"]`;\n }\n await new Promise((r) => setTimeout(r, pollInterval));\n }\n\n throw new Error(`Locator could not resolve element within ${effectiveTimeout}ms. Steps: ${this.describeSteps()}`);\n }\n\n /**\n * Human-readable description of the locator chain for error messages.\n */\n private describeSteps(): string {\n return this.steps\n .map((step) => {\n switch (step.type) {\n case 'role':\n return `getByRole(\"${step.role}\"${step.options?.name ? `, { name: \"${String(step.options.name)}\" }` : ''})`;\n case 'text':\n return `getByText(\"${String(step.text)}\")`;\n case 'label':\n return `getByLabel(\"${String(step.text)}\")`;\n case 'placeholder':\n return `getByPlaceholder(\"${String(step.text)}\")`;\n case 'testId':\n return `getByTestId(\"${step.testId}\")`;\n case 'css':\n return `locator(\"${step.selector}\")`;\n case 'filter':\n return 'filter(...)';\n case 'first':\n return 'first()';\n case 'nth':\n return `nth(${step.index})`;\n }\n })\n .join('.');\n }\n\n /**\n * Serializes locator steps to JSON-safe format, converting RegExp\n * instances to { __regex__, flags } objects.\n */\n private serializeSteps(): unknown[] {\n return this.steps.map((step) => {\n if (step.type === 'text' || step.type === 'label' || step.type === 'placeholder') {\n return {\n ...step,\n text: step.text instanceof RegExp ? { __regex__: step.text.source, flags: step.text.flags } : step.text,\n };\n }\n if (step.type === 'role' && step.options?.name instanceof RegExp) {\n return {\n ...step,\n options: {\n ...step.options,\n name: { __regex__: step.options.name.source, flags: step.options.name.flags },\n },\n };\n }\n if (step.type === 'filter') {\n const filterOpts: Record<string, unknown> = {};\n if (step.options.hasText) {\n filterOpts.hasText =\n step.options.hasText instanceof RegExp\n ? { __regex__: step.options.hasText.source, flags: step.options.hasText.flags }\n : step.options.hasText;\n }\n if (step.options.has) {\n filterOpts.hasSteps = (step.options.has as LocatorProxy).serializeSteps();\n }\n return { type: 'filter', options: filterOpts };\n }\n return step;\n });\n }\n\n /**\n * Builds a self-contained JavaScript script that runs in the page context.\n * Resolves the locator step chain using DOM APIs and returns the result\n * based on the requested action.\n */\n private buildResolveScript(action: ResolveAction, extra?: string): string {\n const stepsJson = JSON.stringify(this.serializeSteps());\n\n return `(function() {\n var steps = ${stepsJson};\n var action = \"${action}\";\n var extra = ${extra !== undefined ? JSON.stringify(extra) : 'null'};\n\n function toRegex(v) {\n if (v && typeof v === 'object' && v.__regex__) return new RegExp(v.__regex__, v.flags || '');\n return null;\n }\n\n function matchText(actual, expected, exact) {\n if (!actual) return false;\n var regex = toRegex(expected);\n if (regex) return regex.test(actual);\n if (exact) return actual === expected;\n return actual.toLowerCase().indexOf(expected.toLowerCase()) !== -1;\n }\n\n var ROLE_MAP = {\n heading: 'h1,h2,h3,h4,h5,h6,[role=\"heading\"]',\n link: 'a[href],[role=\"link\"]',\n button: 'button,input[type=\"button\"],input[type=\"submit\"],input[type=\"reset\"],[role=\"button\"]',\n textbox: 'input:not([type]),input[type=\"text\"],input[type=\"email\"],input[type=\"password\"],input[type=\"search\"],input[type=\"tel\"],input[type=\"url\"],input[type=\"number\"],textarea,[role=\"textbox\"]',\n checkbox: 'input[type=\"checkbox\"],[role=\"checkbox\"]',\n radio: 'input[type=\"radio\"],[role=\"radio\"]',\n combobox: 'select,[role=\"combobox\"],[role=\"listbox\"]',\n navigation: 'nav,[role=\"navigation\"]',\n main: 'main,[role=\"main\"]',\n banner: 'header,[role=\"banner\"]',\n contentinfo: 'footer,[role=\"contentinfo\"]',\n region: 'section[aria-label],section[aria-labelledby],[role=\"region\"]',\n list: 'ul,ol,[role=\"list\"]',\n listitem: 'li,[role=\"listitem\"]',\n img: 'img,[role=\"img\"]',\n dialog: 'dialog,[role=\"dialog\"],[role=\"alertdialog\"]',\n tab: '[role=\"tab\"]',\n tablist: '[role=\"tablist\"]',\n tabpanel: '[role=\"tabpanel\"]',\n cell: 'td,[role=\"cell\"]',\n row: 'tr,[role=\"row\"]',\n table: 'table,[role=\"table\"],[role=\"grid\"]',\n menuitem: '[role=\"menuitem\"]',\n menu: '[role=\"menu\"],[role=\"menubar\"]',\n switch: '[role=\"switch\"]',\n alert: '[role=\"alert\"]',\n status: '[role=\"status\"]',\n progressbar: 'progress,[role=\"progressbar\"]',\n spinbutton: 'input[type=\"number\"],[role=\"spinbutton\"]',\n slider: 'input[type=\"range\"],[role=\"slider\"]',\n separator: 'hr,[role=\"separator\"]',\n tooltip: '[role=\"tooltip\"]',\n tree: '[role=\"tree\"]',\n treeitem: '[role=\"treeitem\"]',\n group: 'fieldset,[role=\"group\"]',\n article: 'article,[role=\"article\"]',\n complementary: 'aside,[role=\"complementary\"]',\n form: 'form,[role=\"form\"]',\n search: '[role=\"search\"]'\n };\n\n function getAccessibleName(el) {\n if (el.getAttribute('aria-label')) return el.getAttribute('aria-label');\n var labelledBy = el.getAttribute('aria-labelledby');\n if (labelledBy) {\n var labelEl = document.getElementById(labelledBy);\n if (labelEl) return labelEl.textContent.trim();\n }\n if (el.id) {\n var labels = document.querySelectorAll('label[for=\"' + el.id + '\"]');\n if (labels.length > 0) return labels[0].textContent.trim();\n }\n var parentLabel = el.closest('label');\n if (parentLabel) {\n var clone = parentLabel.cloneNode(true);\n var inputs = clone.querySelectorAll('input,select,textarea');\n inputs.forEach(function(inp) { inp.remove(); });\n var t = clone.textContent.trim();\n if (t) return t;\n }\n if (el.placeholder) return el.placeholder;\n if (el.title) return el.title;\n if (el.alt) return el.alt;\n return (el.textContent || '').trim();\n }\n\n function resolveSteps(candidates, stepsToResolve) {\n for (var i = 0; i < stepsToResolve.length; i++) {\n var step = stepsToResolve[i];\n\n if (step.type === 'role') {\n var sel = ROLE_MAP[step.role] || '[role=\"' + step.role + '\"]';\n var matching = [];\n for (var c = 0; c < candidates.length; c++) {\n var children = candidates[c].querySelectorAll(sel);\n for (var j = 0; j < children.length; j++) matching.push(children[j]);\n if (candidates[c].matches && candidates[c].matches(sel)) matching.push(candidates[c]);\n }\n if (step.options && step.options.name != null) {\n var name = step.options.name;\n var exact = step.options.exact || false;\n matching = matching.filter(function(el) {\n return matchText(getAccessibleName(el), name, exact);\n });\n }\n candidates = matching;\n\n } else if (step.type === 'text') {\n var textFiltered = [];\n for (var ci = 0; ci < candidates.length; ci++) {\n var allEls = candidates[ci].querySelectorAll('*');\n for (var ji = 0; ji < allEls.length; ji++) {\n if (matchText(allEls[ji].textContent, step.text, step.options && step.options.exact)) {\n textFiltered.push(allEls[ji]);\n }\n }\n if (matchText(candidates[ci].textContent, step.text, step.options && step.options.exact)) {\n textFiltered.push(candidates[ci]);\n }\n }\n candidates = textFiltered;\n\n } else if (step.type === 'label') {\n var labeled = [];\n var allLabels = document.querySelectorAll('label');\n for (var li = 0; li < allLabels.length; li++) {\n var labelText = allLabels[li].textContent.trim();\n if (matchText(labelText, step.text, step.options && step.options.exact)) {\n var forAttr = allLabels[li].getAttribute('for');\n if (forAttr) {\n var target = document.getElementById(forAttr);\n if (target) labeled.push(target);\n }\n var inner = allLabels[li].querySelectorAll('input,select,textarea');\n for (var ki = 0; ki < inner.length; ki++) labeled.push(inner[ki]);\n }\n }\n var byAria = document.querySelectorAll('[aria-label]');\n for (var ai = 0; ai < byAria.length; ai++) {\n if (matchText(byAria[ai].getAttribute('aria-label'), step.text, step.options && step.options.exact)) {\n labeled.push(byAria[ai]);\n }\n }\n candidates = labeled;\n\n } else if (step.type === 'placeholder') {\n var phMatches = [];\n for (var pi = 0; pi < candidates.length; pi++) {\n var phInputs = candidates[pi].querySelectorAll('[placeholder]');\n for (var pj = 0; pj < phInputs.length; pj++) {\n if (matchText(phInputs[pj].placeholder, step.text, step.options && step.options.exact)) {\n phMatches.push(phInputs[pj]);\n }\n }\n if (candidates[pi].placeholder && matchText(candidates[pi].placeholder, step.text, step.options && step.options.exact)) {\n phMatches.push(candidates[pi]);\n }\n }\n candidates = phMatches;\n\n } else if (step.type === 'testId') {\n var tidMatches = [];\n for (var ti = 0; ti < candidates.length; ti++) {\n var tidFound = candidates[ti].querySelectorAll('[data-testid=\"' + step.testId + '\"]');\n for (var tj = 0; tj < tidFound.length; tj++) tidMatches.push(tidFound[tj]);\n if (candidates[ti].getAttribute && candidates[ti].getAttribute('data-testid') === step.testId) {\n tidMatches.push(candidates[ti]);\n }\n }\n candidates = tidMatches;\n\n } else if (step.type === 'css') {\n var cssMatches = [];\n for (var si = 0; si < candidates.length; si++) {\n var cssFound = candidates[si].querySelectorAll(step.selector);\n for (var sj = 0; sj < cssFound.length; sj++) cssMatches.push(cssFound[sj]);\n }\n candidates = cssMatches;\n\n } else if (step.type === 'filter') {\n if (step.options.hasText) {\n candidates = candidates.filter(function(el) {\n return matchText(el.textContent, step.options.hasText, false);\n });\n }\n if (step.options.hasSteps) {\n candidates = candidates.filter(function(el) {\n var sub = resolveSteps([el], step.options.hasSteps);\n return sub.length > 0;\n });\n }\n\n } else if (step.type === 'first') {\n candidates = candidates.length > 0 ? [candidates[0]] : [];\n\n } else if (step.type === 'nth') {\n var idx = step.index;\n if (idx < 0) idx = candidates.length + idx;\n candidates = (idx >= 0 && idx < candidates.length) ? [candidates[idx]] : [];\n }\n\n candidates = candidates.filter(function(el, elIdx, arr) {\n return arr.indexOf(el) === elIdx;\n });\n }\n return candidates;\n }\n\n var candidates = resolveSteps([document], steps);\n\n if (action === 'count') return candidates.length;\n\n if (action === 'isVisible') {\n if (candidates.length === 0) return false;\n var el = candidates[0];\n var rect = el.getBoundingClientRect();\n var style = window.getComputedStyle(el);\n return rect.width > 0 && rect.height > 0 && style.visibility !== 'hidden' && style.display !== 'none';\n }\n\n if (candidates.length === 0) {\n if (action === 'textContent') return null;\n if (action === 'innerText') return '';\n if (action === 'inputValue') return '';\n if (action === 'getProperty') return null;\n return false;\n }\n\n var target = candidates[0];\n\n if (action === 'mark') {\n target.setAttribute('data-pw-proxy', extra);\n return true;\n }\n\n if (action === 'getProperty') {\n return target[extra];\n }\n\n if (action === 'textContent') {\n return target.textContent;\n }\n\n if (action === 'innerText') {\n return target.innerText || target.textContent || '';\n }\n\n if (action === 'inputValue') {\n return target.value || '';\n }\n\n return null;\n})()`;\n }\n}\n","/**\n * Extension Expect - Custom assertion wrapper for proxy objects\n *\n * DESIGN PATTERNS:\n * - Adapter pattern to bridge Playwright expect with proxy objects\n * - Polling pattern for async DOM assertions\n * - Pure functions with no side effects (except polling)\n *\n * CODING STANDARDS:\n * - Export individual functions, not classes\n * - Use descriptive function names with verbs\n * - Keep functions small and focused\n *\n * AVOID:\n * - Direct Playwright expect usage on proxy objects\n * - Blocking operations without timeout\n * - Missing negation support\n */\n\nimport { LOCATOR_PROXY_BRAND, type LocatorProxy } from '../services/LocatorProxy.js';\n\n/**\n * Brand symbol for identifying ExtensionPageProxy instances\n */\nexport const PAGE_PROXY_BRAND = Symbol.for('__pageProxyBrand__');\n\n/**\n * Interface for page proxy objects that support URL checking\n */\ninterface PageProxyLike {\n [PAGE_PROXY_BRAND]: boolean;\n currentUrlAsync(): Promise<string>;\n}\n\n/**\n * Polling configuration\n */\nconst POLL_INTERVAL_MS = 100;\nconst DEFAULT_TIMEOUT_MS = 5000;\n\n/**\n * Polls a condition until it passes or times out.\n * Throws assertion error on timeout.\n */\nasync function pollUntil(\n check: () => Promise<boolean>,\n errorMessage: string,\n timeout: number = DEFAULT_TIMEOUT_MS,\n): Promise<void> {\n const start = Date.now();\n\n while (Date.now() - start < timeout) {\n const passed = await check();\n if (passed) return;\n await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));\n }\n\n throw new Error(errorMessage);\n}\n\n/**\n * Assertion options with optional timeout\n */\ninterface AssertionOptions {\n timeout?: number;\n}\n\n/**\n * Assertion set for locator proxy objects\n */\ninterface LocatorAssertions {\n toBeVisible(options?: AssertionOptions): Promise<void>;\n toBeHidden(options?: AssertionOptions): Promise<void>;\n toBeDisabled(options?: AssertionOptions): Promise<void>;\n toBeEnabled(options?: AssertionOptions): Promise<void>;\n toBeChecked(options?: AssertionOptions): Promise<void>;\n toHaveValue(expected: string | RegExp, options?: AssertionOptions): Promise<void>;\n toHaveText(expected: string | RegExp, options?: AssertionOptions): Promise<void>;\n toContainText(expected: string | RegExp, options?: AssertionOptions): Promise<void>;\n toHaveCount(expected: number, options?: AssertionOptions): Promise<void>;\n not: LocatorAssertions;\n}\n\n/**\n * Assertion set for page proxy objects\n */\ninterface PageAssertions {\n toHaveURL(expected: string | RegExp, options?: AssertionOptions): Promise<void>;\n toHaveTitle(expected: string | RegExp, options?: AssertionOptions): Promise<void>;\n not: PageAssertions;\n}\n\n/**\n * Checks if a string matches the expected value (string or RegExp)\n */\nfunction matchesExpected(actual: string | null | undefined, expected: string | RegExp): boolean {\n if (actual == null) return false;\n if (expected instanceof RegExp) return expected.test(actual);\n return actual === expected;\n}\n\n/**\n * Checks if a string contains the expected value (string or RegExp)\n */\nfunction containsExpected(actual: string | null | undefined, expected: string | RegExp): boolean {\n if (actual == null) return false;\n if (expected instanceof RegExp) return expected.test(actual);\n return actual.includes(expected);\n}\n\n/**\n * Creates locator assertions for a LocatorProxy target.\n */\nfunction createLocatorAssertions(target: LocatorProxy, negated: boolean): LocatorAssertions {\n const assert = (condition: boolean): boolean => (negated ? !condition : condition);\n\n const assertions: LocatorAssertions = {\n async toBeVisible(options?: AssertionOptions): Promise<void> {\n await pollUntil(\n async () => assert(await target.isVisible()),\n negated ? 'Expected element to not be visible' : 'Expected element to be visible',\n options?.timeout,\n );\n },\n\n async toBeHidden(options?: AssertionOptions): Promise<void> {\n await pollUntil(\n async () => assert(!(await target.isVisible())),\n negated ? 'Expected element to not be hidden' : 'Expected element to be hidden',\n options?.timeout,\n );\n },\n\n async toBeDisabled(options?: AssertionOptions): Promise<void> {\n await pollUntil(\n async () => assert(!!(await target.evaluateProperty('disabled'))),\n negated ? 'Expected element to not be disabled' : 'Expected element to be disabled',\n options?.timeout,\n );\n },\n\n async toBeEnabled(options?: AssertionOptions): Promise<void> {\n await pollUntil(\n async () => assert(!(await target.evaluateProperty('disabled'))),\n negated ? 'Expected element to not be enabled' : 'Expected element to be enabled',\n options?.timeout,\n );\n },\n\n async toBeChecked(options?: AssertionOptions): Promise<void> {\n await pollUntil(\n async () => assert(!!(await target.evaluateProperty('checked'))),\n negated ? 'Expected element to not be checked' : 'Expected element to be checked',\n options?.timeout,\n );\n },\n\n async toHaveValue(expected: string | RegExp, options?: AssertionOptions): Promise<void> {\n await pollUntil(\n async () => {\n const value = (await target.evaluateProperty('value')) as string;\n return assert(matchesExpected(value, expected));\n },\n negated\n ? `Expected element to not have value \"${String(expected)}\"`\n : `Expected element to have value \"${String(expected)}\"`,\n options?.timeout,\n );\n },\n\n async toHaveText(expected: string | RegExp, options?: AssertionOptions): Promise<void> {\n await pollUntil(\n async () => {\n const text = await target.textContent();\n return assert(matchesExpected(text?.trim() ?? null, expected));\n },\n negated\n ? `Expected element to not have text \"${String(expected)}\"`\n : `Expected element to have text \"${String(expected)}\"`,\n options?.timeout,\n );\n },\n\n async toContainText(expected: string | RegExp, options?: AssertionOptions): Promise<void> {\n await pollUntil(\n async () => {\n const text = await target.textContent();\n return assert(containsExpected(text, expected));\n },\n negated\n ? `Expected element to not contain text \"${String(expected)}\"`\n : `Expected element to contain text \"${String(expected)}\"`,\n options?.timeout,\n );\n },\n\n async toHaveCount(expected: number, options?: AssertionOptions): Promise<void> {\n await pollUntil(\n async () => {\n const count = await target.count();\n return assert(count === expected);\n },\n negated ? `Expected element count to not be ${expected}` : `Expected element count to be ${expected}`,\n options?.timeout,\n );\n },\n\n get not(): LocatorAssertions {\n return createLocatorAssertions(target, !negated);\n },\n };\n\n return assertions;\n}\n\n/**\n * Creates page assertions for a page proxy target.\n */\nfunction createPageAssertions(target: PageProxyLike, negated: boolean): PageAssertions {\n const assert = (condition: boolean): boolean => (negated ? !condition : condition);\n\n const assertions: PageAssertions = {\n async toHaveURL(expected: string | RegExp, options?: AssertionOptions): Promise<void> {\n await pollUntil(\n async () => {\n const currentUrl = await target.currentUrlAsync();\n return assert(matchesExpected(currentUrl, expected));\n },\n negated\n ? `Expected page to not have URL \"${String(expected)}\"`\n : `Expected page to have URL \"${String(expected)}\"`,\n options?.timeout,\n );\n },\n\n async toHaveTitle(expected: string | RegExp, options?: AssertionOptions): Promise<void> {\n await pollUntil(\n async () => {\n const page = target as unknown as { title(): Promise<string> };\n const title = await page.title();\n return assert(matchesExpected(title, expected));\n },\n negated\n ? `Expected page to not have title \"${String(expected)}\"`\n : `Expected page to have title \"${String(expected)}\"`,\n options?.timeout,\n );\n },\n\n get not(): PageAssertions {\n return createPageAssertions(target, !negated);\n },\n };\n\n return assertions;\n}\n\n/**\n * Creates an extension-mode expect wrapper for proxy objects.\n * Detects LocatorProxy and PageProxy via brand symbols and returns\n * the appropriate assertion set with polling semantics.\n */\nexport function createExtensionExpect(target: unknown): LocatorAssertions | PageAssertions {\n const obj = target as Record<symbol, boolean>;\n\n if (obj[LOCATOR_PROXY_BRAND]) {\n return createLocatorAssertions(target as LocatorProxy, false);\n }\n\n if (obj[PAGE_PROXY_BRAND]) {\n return createPageAssertions(target as PageProxyLike, false);\n }\n\n throw new Error('createExtensionExpect called with unsupported target');\n}\n","/**\n * PlaywrightTestStub - Proxy module for @playwright/test\n *\n * DESIGN PATTERNS:\n * - Proxy pattern to intercept Playwright test API calls\n * - Collector pattern to gather test blocks for sequential execution\n * - Module aliasing compatible structure\n *\n * CODING STANDARDS:\n * - Collect test blocks into retrievable TestSuite structure\n * - Support test(), test.describe(), test.beforeEach(), test.afterEach()\n * - Re-export real expect from @playwright/test\n * - Clear collected tests after retrieval\n *\n * AVOID:\n * - Executing tests immediately (defer to SpecRunner)\n * - Modifying the real @playwright/test behavior\n * - Global state that persists across spec files\n */\n\nimport { createRequire } from 'node:module';\nimport type { Browser, BrowserContext, Page } from 'playwright';\nimport { LOCATOR_PROXY_BRAND } from '../services/LocatorProxy.js';\nimport { PAGE_PROXY_BRAND, createExtensionExpect } from '../utils/extension-expect.js';\n\n/**\n * Compile-time constant injected by SpecBundlerService via Bun's `define`.\n * Contains the absolute path to this stub file's real location on disk.\n * Only present when the stub is bundled into a spec output file.\n */\ndeclare const __PLAYWRIGHT_MCP_STUB_PATH__: string;\n\n/**\n * Resolves a base path for createRequire that can find `playwright/test`.\n * When loaded from the stub's real location, import.meta.url works.\n * When bundled into a temp output (e.g. /tmp/browse-tool-bundles/),\n * import.meta.url points to the bundle and playwright can't be found,\n * so we fall back to the compile-time injected stub path.\n */\nfunction resolveRequireBase(): string {\n try {\n const req = createRequire(import.meta.url);\n req.resolve('playwright/test');\n return import.meta.url;\n } catch {\n if (typeof __PLAYWRIGHT_MCP_STUB_PATH__ === 'string') {\n return `file://${__PLAYWRIGHT_MCP_STUB_PATH__}`;\n }\n return import.meta.url;\n }\n}\n\n// Clear Playwright's double-load guard before importing playwright/test\n// to avoid \"Requiring @playwright/test second time\" when the stub is bundled\n// into spec files that are loaded alongside the MCP server's own playwright import\nconst proc = process as unknown as Record<string, unknown>;\nconst savedPwInitiator = proc.__pw_initiator__;\nproc.__pw_initiator__ = undefined;\nconst _require = createRequire(resolveRequireBase());\nconst _realPlaywrightTest = _require('playwright/test') as Record<string, unknown> & {\n expect: typeof import('playwright/test')['expect'];\n};\nconst { expect: playwrightExpect } = _realPlaywrightTest;\nproc.__pw_initiator__ = savedPwInitiator;\n\n/**\n * Re-export all real playwright/test exports that specs may use.\n * Our custom `test` and `expect` override the real ones; everything\n * else (request, chromium, firefox, webkit, devices, selectors,\n * defineConfig, mergeExpects, mergeTests) is passed through unchanged.\n */\nexport const { request, chromium, firefox, webkit, devices, selectors, defineConfig, mergeExpects, mergeTests } =\n _realPlaywrightTest as Record<string, unknown>;\n\n/**\n * Fixtures passed to test functions.\n * Compatible with Playwright's test fixture pattern.\n * Browser is optional for extension mode which uses persistent context.\n */\nexport interface TestFixtures {\n page: Page;\n context: BrowserContext;\n browser?: Browser;\n /** Playwright module instance for creating API request contexts */\n playwright?: typeof import('playwright');\n /** API request context scoped to the test */\n request?: import('playwright').APIRequestContext;\n /** Extended fixtures added via test.extend() */\n [key: string]: unknown;\n}\n\n/**\n * A single test block collected from the spec file.\n * The `fn` stores the RAW test function — fixture resolution happens in flattenTests.\n */\nexport interface TestBlock {\n /** Test title */\n title: string;\n /** Test function (raw — no fixture wrapping at collection time) */\n fn: (fixtures: TestFixtures) => Promise<void>;\n /** Full path including describe blocks */\n fullTitle: string;\n /** Whether test is marked with test.only() */\n only: boolean;\n /** Whether test is marked with test.skip() */\n skip: boolean;\n}\n\n/**\n * Holds fixture definitions for a describe scope.\n * Fixtures are inherited from parent scopes and merged with local definitions.\n */\nclass FixtureScope {\n constructor(\n private readonly defs: Map<string, FixtureFunction>,\n private readonly parent: FixtureScope | null = null,\n ) {}\n\n /** Merges all fixture defs from root to this scope */\n getAllDefs(): Map<string, FixtureFunction> {\n const parentDefs = this.parent?.getAllDefs() ?? new Map<string, FixtureFunction>();\n return new Map([...parentDefs, ...this.defs]);\n }\n\n /** Creates a child scope that inherits from this one */\n extend(defs: Map<string, FixtureFunction>): FixtureScope {\n return new FixtureScope(defs, this);\n }\n}\n\n/**\n * A describe block containing tests and hooks.\n */\nexport interface DescribeBlock {\n /** Describe block title */\n title: string;\n /** Tests within this describe */\n tests: TestBlock[];\n /** beforeAll hooks (run once before first test in block) */\n beforeAllHooks?: Array<(fixtures: TestFixtures) => Promise<void>>;\n /** afterAll hooks (run once after last test in block) */\n afterAllHooks?: Array<(fixtures: TestFixtures) => Promise<void>>;\n /** beforeEach hooks (raw — not fixture-wrapped) */\n beforeEachHooks: Array<(fixtures: TestFixtures) => Promise<void>>;\n /** afterEach hooks (raw — not fixture-wrapped) */\n afterEachHooks: Array<(fixtures: TestFixtures) => Promise<void>>;\n /** Nested describe blocks */\n children: DescribeBlock[];\n /** Parent describe block (null for root) */\n parent: DescribeBlock | null;\n /** Fixture scope for this describe block — merged from test.extend() calls */\n fixtureScope: FixtureScope | null;\n}\n\n/**\n * Complete test suite collected from a spec file.\n */\nexport interface TestSuite {\n /** Spec file path or identifier */\n specPath: string;\n /** Root describe block (unnamed) */\n root: DescribeBlock;\n /** Flattened list of all tests in execution order */\n allTests: TestBlock[];\n /** Total test count */\n testCount: number;\n}\n\n/**\n * Global collector state for the current spec file.\n * Reset when a new spec is loaded or after retrieval.\n */\nclass TestCollector {\n private currentSpecPath = '';\n private rootDescribe: DescribeBlock;\n private currentDescribe: DescribeBlock;\n\n constructor() {\n this.rootDescribe = this.createDescribeBlock('', null);\n this.currentDescribe = this.rootDescribe;\n }\n\n private createDescribeBlock(title: string, parent: DescribeBlock | null): DescribeBlock {\n return {\n title,\n tests: [],\n beforeEachHooks: [],\n afterEachHooks: [],\n children: [],\n parent,\n fixtureScope: null,\n };\n }\n\n /**\n * Sets the current spec file path.\n */\n setSpecPath(specPath: string): void {\n this.currentSpecPath = specPath;\n }\n\n /**\n * Resets the collector for a new spec file.\n */\n reset(): void {\n this.currentSpecPath = '';\n this.rootDescribe = this.createDescribeBlock('', null);\n this.currentDescribe = this.rootDescribe;\n }\n\n /**\n * Adds a test to the current describe block.\n * Returns the TestBlock for flag modification (only/skip).\n */\n addTest(title: string, fn: (fixtures: TestFixtures) => Promise<void>, only = false, skip = false): TestBlock {\n const fullTitle = this.getFullTitle(title);\n const testBlock: TestBlock = { title, fn, fullTitle, only, skip };\n this.currentDescribe.tests.push(testBlock);\n return testBlock;\n }\n\n /**\n * Sets or merges fixture definitions on the current describe block.\n * Called by createTestFunction when tests/hooks are registered.\n */\n ensureFixtureScope(defs: Map<string, FixtureFunction>): void {\n if (defs.size === 0) return;\n // Only set once per describe block — subsequent calls with the same\n // or subset defs are no-ops. This prevents duplicate fixture entries\n // when multiple tests/hooks register in the same block.\n if (this.currentDescribe.fixtureScope) return;\n\n // Find parent scope from ancestor describe blocks\n let parentScope: FixtureScope | null = null;\n let ancestor = this.currentDescribe.parent;\n while (ancestor) {\n if (ancestor.fixtureScope) {\n parentScope = ancestor.fixtureScope;\n break;\n }\n ancestor = ancestor.parent;\n }\n this.currentDescribe.fixtureScope = new FixtureScope(defs, parentScope);\n }\n\n /**\n * Enters a new describe block.\n */\n enterDescribe(title: string): void {\n const newDescribe = this.createDescribeBlock(title, this.currentDescribe);\n this.currentDescribe.children.push(newDescribe);\n this.currentDescribe = newDescribe;\n }\n\n /**\n * Exits the current describe block.\n */\n exitDescribe(): void {\n if (this.currentDescribe.parent) {\n this.currentDescribe = this.currentDescribe.parent;\n }\n }\n\n /**\n * Adds a beforeAll hook to the current describe block.\n * In the stub, beforeAll runs once before the first test in the describe.\n */\n addBeforeAll(fn: (fixtures: TestFixtures) => Promise<void>): void {\n if (!this.currentDescribe.beforeAllHooks) {\n this.currentDescribe.beforeAllHooks = [];\n }\n this.currentDescribe.beforeAllHooks.push(fn);\n }\n\n /**\n * Adds an afterAll hook to the current describe block.\n */\n addAfterAll(fn: (fixtures: TestFixtures) => Promise<void>): void {\n if (!this.currentDescribe.afterAllHooks) {\n this.currentDescribe.afterAllHooks = [];\n }\n this.currentDescribe.afterAllHooks.push(fn);\n }\n\n /**\n * Adds a beforeEach hook to the current describe block.\n */\n addBeforeEach(fn: (fixtures: TestFixtures) => Promise<void>): void {\n this.currentDescribe.beforeEachHooks.push(fn);\n }\n\n /**\n * Adds an afterEach hook to the current describe block.\n */\n addAfterEach(fn: (fixtures: TestFixtures) => Promise<void>): void {\n this.currentDescribe.afterEachHooks.push(fn);\n }\n\n /**\n * Marks all tests in the current describe block as skipped.\n * Used when test.skip(true) is called inside a describe block.\n */\n markCurrentDescribeSkipped(): void {\n for (const test of this.currentDescribe.tests) {\n test.skip = true;\n }\n for (const child of this.currentDescribe.children) {\n this.markDescribeSkipped(child);\n }\n }\n\n private markDescribeSkipped(describe: DescribeBlock): void {\n for (const test of describe.tests) {\n test.skip = true;\n }\n for (const child of describe.children) {\n this.markDescribeSkipped(child);\n }\n }\n\n /**\n * Gets the full title path for a test.\n */\n private getFullTitle(testTitle: string): string {\n const parts: string[] = [];\n let current: DescribeBlock | null = this.currentDescribe;\n\n while (current) {\n if (current.title) {\n parts.unshift(current.title);\n }\n current = current.parent;\n }\n\n parts.push(testTitle);\n return parts.join(' > ');\n }\n\n /**\n * Flattens all tests into execution order, including hooks.\n */\n /**\n * Collects fixture defs from the describe block and all ancestors.\n */\n private collectFixtureDefs(describe: DescribeBlock): Map<string, FixtureFunction> {\n return describe.fixtureScope?.getAllDefs() ?? new Map();\n }\n\n private flattenTests(\n describe: DescribeBlock,\n ancestorBeforeEach: Array<(fixtures: TestFixtures) => Promise<void>>,\n ancestorAfterEach: Array<(fixtures: TestFixtures) => Promise<void>>,\n ): TestBlock[] {\n const tests: TestBlock[] = [];\n\n const currentBeforeEach = [...ancestorBeforeEach, ...describe.beforeEachHooks];\n const currentAfterEach = [...describe.afterEachHooks, ...ancestorAfterEach];\n const beforeAllHooks = describe.beforeAllHooks ?? [];\n const afterAllHooks = describe.afterAllHooks ?? [];\n\n // Build ONE fixture wrapper from the describe scope — shared by all tests + hooks\n const fixtureDefs = this.collectFixtureDefs(describe);\n const wrapWithFixtures = createFixtureWrapper(fixtureDefs);\n\n let beforeAllRan = false;\n const allTestsInBlock: TestBlock[] = [];\n\n for (const test of describe.tests) {\n // Combine hooks + test body into one raw function,\n // then wrap with fixtures ONCE. Fixtures resolve once and are\n // shared between beforeAll, beforeEach, test body, and afterEach.\n const combinedFn = async (fixtures: TestFixtures): Promise<void> => {\n if (!beforeAllRan && beforeAllHooks.length > 0) {\n beforeAllRan = true;\n for (const hook of beforeAllHooks) {\n await hook(fixtures);\n }\n }\n\n for (const hook of currentBeforeEach) {\n await hook(fixtures);\n }\n\n await test.fn(fixtures);\n\n for (const hook of currentAfterEach) {\n await hook(fixtures);\n }\n };\n\n allTestsInBlock.push({\n title: test.title,\n fullTitle: test.fullTitle,\n fn: wrapWithFixtures(combinedFn),\n only: test.only,\n skip: test.skip,\n });\n }\n\n for (const child of describe.children) {\n allTestsInBlock.push(...this.flattenTests(child, currentBeforeEach, currentAfterEach));\n }\n\n // Wrap the last test to run afterAll hooks after it completes\n if (afterAllHooks.length > 0 && allTestsInBlock.length > 0) {\n const lastTest = allTestsInBlock[allTestsInBlock.length - 1];\n const originalFn = lastTest.fn;\n lastTest.fn = async (fixtures: TestFixtures): Promise<void> => {\n try {\n await originalFn(fixtures);\n } finally {\n for (const hook of afterAllHooks) {\n await hook(fixtures);\n }\n }\n };\n }\n\n tests.push(...allTestsInBlock);\n return tests;\n }\n\n /**\n * Retrieves the collected test suite and resets for the next spec.\n */\n retrieveAndReset(): TestSuite {\n const allTests = this.flattenTests(this.rootDescribe, [], []);\n\n const suite: TestSuite = {\n specPath: this.currentSpecPath,\n root: this.rootDescribe,\n allTests,\n testCount: allTests.length,\n };\n\n this.reset();\n return suite;\n }\n\n /**\n * Retrieves the collected test suite without resetting.\n * Useful for inspection.\n */\n peek(): TestSuite {\n const allTests = this.flattenTests(this.rootDescribe, [], []);\n\n return {\n specPath: this.currentSpecPath,\n root: this.rootDescribe,\n allTests,\n testCount: allTests.length,\n };\n }\n}\n\n/**\n * Thrown by test.skip() inside a test body to signal the test should be skipped.\n */\nexport class SkipTestError extends Error {\n readonly isSkip = true;\n constructor(reason?: string) {\n super(reason ?? 'Skipped');\n this.name = 'SkipTestError';\n }\n}\n\n/**\n * Global test collector instance.\n * Uses globalThis to ensure the bundled code and SpecRunner share the same collector.\n */\nconst COLLECTOR_KEY = '__playwrightMcpTestCollector__';\nconst globalAny = globalThis as unknown as Record<string, TestCollector>;\nif (!globalAny[COLLECTOR_KEY]) {\n globalAny[COLLECTOR_KEY] = new TestCollector();\n}\nconst collector = globalAny[COLLECTOR_KEY];\n\n/**\n * Test function type that matches Playwright's test() signature.\n */\ntype TestFunction = (fixtures: TestFixtures) => Promise<void>;\n\n/**\n * Fixture definition function following Playwright's async use() pattern.\n * Setup runs before use(), teardown runs after use() returns.\n */\ntype FixtureFunction = (fixtures: Record<string, unknown>, use: (value: unknown) => Promise<void>) => Promise<void>;\n\n/**\n * Parses fixture definitions from the object passed to test.extend().\n * Supports both direct functions and [options, fn] tuple format.\n */\nfunction parseFixtureDefs(fixtures: Record<string, unknown>): Map<string, FixtureFunction> {\n const defs = new Map<string, FixtureFunction>();\n for (const [name, definition] of Object.entries(fixtures)) {\n if (typeof definition === 'function') {\n defs.set(name, definition as FixtureFunction);\n } else if (Array.isArray(definition) && definition.length === 2 && typeof definition[1] === 'function') {\n defs.set(name, definition[1] as FixtureFunction);\n }\n }\n return defs;\n}\n\n/**\n * Wraps a test/hook function to resolve custom fixtures before execution.\n * Uses recursive continuation chaining so each fixture's use() callback\n * scopes the next fixture setup, enabling proper teardown in reverse order.\n */\nfunction createFixtureWrapper(fixtureDefs: Map<string, FixtureFunction>): (fn: TestFunction) => TestFunction {\n if (fixtureDefs.size === 0) return (fn) => fn;\n\n const entries = [...fixtureDefs.entries()];\n\n return (fn: TestFunction): TestFunction => {\n return async (baseFixtures: TestFixtures) => {\n const resolved: TestFixtures = { ...baseFixtures };\n\n async function resolveChain(index: number): Promise<void> {\n if (index >= entries.length) {\n await fn(resolved);\n return;\n }\n\n const [name, setup] = entries[index];\n await setup(resolved, async (value: unknown) => {\n resolved[name] = value;\n await resolveChain(index + 1);\n });\n }\n\n await resolveChain(0);\n };\n };\n}\n\n/**\n * Creates a test function with optional custom fixture definitions.\n * Returned function has the same API surface as Playwright's test().\n */\nfunction createTestFunction(fixtureDefs: Map<string, FixtureFunction>): typeof test {\n // Push fixture defs to the current describe scope whenever a test/hook is registered.\n // This ensures the scope is set even if extend() is called before describe().\n function ensureScope(): void {\n collector.ensureFixtureScope(fixtureDefs);\n }\n\n function extendedTest(title: string, fn: TestFunction): void {\n ensureScope();\n collector.addTest(title, fn);\n }\n\n extendedTest.only = function extendedOnly(title: string, fn: TestFunction): void {\n ensureScope();\n collector.addTest(title, fn, true, false);\n };\n\n extendedTest.skip = function extendedSkip(\n titleOrCondition?: string | boolean,\n fnOrReason?: TestFunction | string,\n ): void {\n if (titleOrCondition === undefined || titleOrCondition === true) {\n throw new SkipTestError(typeof fnOrReason === 'string' ? fnOrReason : 'Skipped');\n }\n if (titleOrCondition === false) return;\n if (typeof titleOrCondition === 'string' && typeof fnOrReason === 'function') {\n ensureScope();\n collector.addTest(titleOrCondition, fnOrReason as TestFunction, false, true);\n }\n };\n\n extendedTest.describe = function extendedDescribe(title: string, fn: () => void): void {\n collector.enterDescribe(title);\n try {\n fn();\n } catch (error) {\n if (error instanceof SkipTestError) {\n collector.markCurrentDescribeSkipped();\n } else {\n throw error;\n }\n }\n collector.exitDescribe();\n };\n\n extendedTest.beforeAll = function extendedBeforeAll(fn: TestFunction): void {\n ensureScope();\n collector.addBeforeAll(fn);\n };\n\n extendedTest.afterAll = function extendedAfterAll(fn: TestFunction): void {\n ensureScope();\n collector.addAfterAll(fn);\n };\n\n extendedTest.beforeEach = function extendedBeforeEach(fn: TestFunction): void {\n ensureScope();\n collector.addBeforeEach(fn);\n };\n\n extendedTest.afterEach = function extendedAfterEach(fn: TestFunction): void {\n ensureScope();\n collector.addAfterEach(fn);\n };\n\n extendedTest.extend = function extendAgain(newFixtures: Record<string, unknown>): typeof test {\n const merged = new Map([...fixtureDefs, ...parseFixtureDefs(newFixtures)]);\n return createTestFunction(merged);\n };\n\n return extendedTest as typeof test;\n}\n\n/**\n * Proxy test function that collects tests instead of executing them.\n * Exported directly with methods attached to match Playwright's API surface.\n */\nexport function test(title: string, fn: TestFunction): void {\n collector.addTest(title, fn);\n}\n\nfunction testOnly(title: string, fn: TestFunction): void {\n collector.addTest(title, fn, true, false);\n}\n\n/**\n * Handles both overloads of test.skip():\n * - test.skip(title, fn) — define a skipped test\n * - test.skip(condition, reason) — skip current test conditionally (inside test body)\n * - test.skip() — unconditionally skip current test (inside test body)\n */\nfunction testSkip(titleOrCondition?: string | boolean, fnOrReason?: TestFunction | string): void {\n // test.skip() or test.skip(true) inside a test body — throw to skip\n if (titleOrCondition === undefined || titleOrCondition === true) {\n throw new SkipTestError(typeof fnOrReason === 'string' ? fnOrReason : 'Skipped');\n }\n // test.skip(false, reason) — don't skip, just continue\n if (titleOrCondition === false) {\n return;\n }\n // test.skip(title, fn) — register a skipped test\n if (typeof titleOrCondition === 'string' && typeof fnOrReason === 'function') {\n collector.addTest(titleOrCondition, fnOrReason as TestFunction, false, true);\n }\n}\n\nfunction describe(title: string, fn: () => void): void {\n collector.enterDescribe(title);\n try {\n fn();\n } catch (error) {\n if (error instanceof SkipTestError) {\n // test.skip(true) inside describe — mark all collected tests in this block as skipped\n collector.markCurrentDescribeSkipped();\n } else {\n throw error;\n }\n }\n collector.exitDescribe();\n}\n\nfunction beforeAll(fn: TestFunction): void {\n collector.addBeforeAll(fn);\n}\n\nfunction afterAll(fn: TestFunction): void {\n collector.addAfterAll(fn);\n}\n\nfunction beforeEach(fn: TestFunction): void {\n collector.addBeforeEach(fn);\n}\n\nfunction afterEach(fn: TestFunction): void {\n collector.addAfterEach(fn);\n}\n\ntest.describe = describe;\ntest.beforeAll = beforeAll;\ntest.afterAll = afterAll;\ntest.beforeEach = beforeEach;\ntest.afterEach = afterEach;\ntest.only = testOnly;\ntest.skip = testSkip;\n\n/**\n * Extends test with custom fixtures.\n * Returns a new test function that resolves fixtures using the use() continuation pattern.\n * Supports chaining: test.extend({...}).extend({...})\n */\ntest.extend = function extend(fixtures: Record<string, unknown>): typeof test {\n return createTestFunction(parseFixtureDefs(fixtures));\n};\n\n/**\n * Initializes the collector for a new spec file.\n * Call this before loading a spec file.\n */\nexport function initCollector(specPath: string): void {\n collector.reset();\n collector.setSpecPath(specPath);\n}\n\n/**\n * Retrieves the collected test suite and resets the collector.\n * Call this after loading a spec file.\n */\nexport function getCollectedSuite(): TestSuite {\n return collector.retrieveAndReset();\n}\n\n/**\n * Peeks at the current collected test suite without resetting.\n */\nexport function peekCollectedSuite(): TestSuite {\n return collector.peek();\n}\n\n/**\n * Resets the collector without retrieving.\n * Useful for cleanup on error.\n */\nexport function resetCollector(): void {\n collector.reset();\n}\n\n/**\n * Wrapper around Playwright's expect that detects proxy objects\n * and routes assertions through extension-mode DOM evaluation.\n * Falls back to real Playwright expect for non-proxy targets.\n * Typed as `typeof playwrightExpect` to preserve Playwright's overloaded signatures.\n */\nexport const expect: typeof playwrightExpect = ((target: unknown) => {\n const obj = target as Record<symbol, boolean>;\n if (obj?.[LOCATOR_PROXY_BRAND] || obj?.[PAGE_PROXY_BRAND]) {\n return createExtensionExpect(target);\n }\n return playwrightExpect(target);\n}) as typeof playwrightExpect;\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"playwright-test-yNsP599T.mjs","names":["page: IExtensionPageProxy","steps: LocatorStep[]","filterOpts: Record<string, unknown>","defs: Map<string, FixtureFunction>","parent: FixtureScope | null","testBlock: TestBlock","parentScope: FixtureScope | null","test","describe","parts: string[]","current: DescribeBlock | null","tests: TestBlock[]","allTestsInBlock: TestBlock[]","suite: TestSuite","resolved: TestFixtures","expect: typeof playwrightExpect"],"sources":["../src/services/LocatorProxy.ts","../src/utils/extension-expect.ts","../src/stubs/playwright-test.ts"],"sourcesContent":["/**\n * LocatorProxy\n *\n * DESIGN PATTERNS:\n * - Proxy pattern for Playwright Locator API compatibility\n * - Builder pattern for chainable locator steps\n * - Adapter pattern to bridge DOM evaluation and MCP browser tools\n *\n * CODING STANDARDS:\n * - Use async/await for asynchronous operations\n * - Throw descriptive errors for error cases\n * - Match Playwright Locator interface methods\n * - Route actions through ExtensionPageProxy\n *\n * AVOID:\n * - Blocking operations\n * - Missing error handling\n * - Mutating locator steps (always return new instances)\n */\n\nimport type { IExtensionPageProxy } from './ExtensionPageProxy.js';\n\n/**\n * Brand symbol for identifying LocatorProxy instances in expect()\n */\nexport const LOCATOR_PROXY_BRAND = Symbol.for('__locatorProxyBrand__');\n\n/**\n * A single step in the locator chain\n */\nexport type LocatorStep =\n | { type: 'role'; role: string; options?: { name?: string | RegExp; exact?: boolean } }\n | { type: 'text'; text: string | RegExp; options?: { exact?: boolean } }\n | { type: 'label'; text: string | RegExp; options?: { exact?: boolean } }\n | { type: 'placeholder'; text: string | RegExp; options?: { exact?: boolean } }\n | { type: 'testId'; testId: string }\n | { type: 'css'; selector: string }\n | { type: 'filter'; options: { hasText?: string | RegExp; has?: LocatorProxy } }\n | { type: 'first' }\n | { type: 'nth'; index: number };\n\n/**\n * Action types that buildResolveScript can execute\n */\ntype ResolveAction = 'mark' | 'isVisible' | 'getProperty' | 'textContent' | 'count' | 'innerText' | 'inputValue';\n\n/**\n * Chainable locator proxy that mimics Playwright's Locator API.\n * Accumulates locator steps and resolves them in-page at action time.\n */\nexport class LocatorProxy {\n readonly [LOCATOR_PROXY_BRAND] = true;\n\n constructor(\n private readonly page: IExtensionPageProxy,\n private readonly steps: LocatorStep[],\n ) {}\n\n // ── Chaining methods ──────────────────────────────────────────────\n\n getByRole(role: string, options?: { name?: string | RegExp; exact?: boolean }): LocatorProxy {\n return new LocatorProxy(this.page, [...this.steps, { type: 'role', role, options }]);\n }\n\n getByText(text: string | RegExp, options?: { exact?: boolean }): LocatorProxy {\n return new LocatorProxy(this.page, [...this.steps, { type: 'text', text, options }]);\n }\n\n getByLabel(text: string | RegExp, options?: { exact?: boolean }): LocatorProxy {\n return new LocatorProxy(this.page, [...this.steps, { type: 'label', text, options }]);\n }\n\n getByPlaceholder(text: string | RegExp, options?: { exact?: boolean }): LocatorProxy {\n return new LocatorProxy(this.page, [...this.steps, { type: 'placeholder', text, options }]);\n }\n\n getByTestId(testId: string): LocatorProxy {\n return new LocatorProxy(this.page, [...this.steps, { type: 'testId', testId }]);\n }\n\n locator(selector: string): LocatorProxy {\n return new LocatorProxy(this.page, [...this.steps, { type: 'css', selector }]);\n }\n\n filter(options: { hasText?: string | RegExp; has?: LocatorProxy }): LocatorProxy {\n return new LocatorProxy(this.page, [...this.steps, { type: 'filter', options }]);\n }\n\n first(): LocatorProxy {\n return new LocatorProxy(this.page, [...this.steps, { type: 'first' }]);\n }\n\n last(): LocatorProxy {\n return this.nth(-1);\n }\n\n nth(index: number): LocatorProxy {\n return new LocatorProxy(this.page, [...this.steps, { type: 'nth', index }]);\n }\n\n // ── Action methods ────────────────────────────────────────────────\n\n async click(options?: { timeout?: number }): Promise<void> {\n const selector = await this.resolveAndMark(options?.timeout);\n await this.page.click(selector);\n }\n\n async fill(value: string, options?: { timeout?: number }): Promise<void> {\n const selector = await this.resolveAndMark(options?.timeout);\n await this.page.fill(selector, value);\n }\n\n async type(text: string, options?: { delay?: number; timeout?: number }): Promise<void> {\n const selector = await this.resolveAndMark(options?.timeout);\n await this.page.type(selector, text, { delay: options?.delay });\n }\n\n async press(key: string): Promise<void> {\n const selector = await this.resolveAndMark();\n await this.page.click(selector);\n await this.page.press(key);\n }\n\n async hover(options?: { timeout?: number }): Promise<void> {\n const selector = await this.resolveAndMark(options?.timeout);\n await this.page.hover(selector);\n }\n\n async selectOption(values: string | string[]): Promise<void> {\n const selector = await this.resolveAndMark();\n await this.page.selectOption(selector, values);\n }\n\n async check(): Promise<void> {\n const selector = await this.resolveAndMark();\n await this.page.click(selector);\n }\n\n async uncheck(): Promise<void> {\n const selector = await this.resolveAndMark();\n await this.page.click(selector);\n }\n\n async isVisible(): Promise<boolean> {\n const script = this.buildResolveScript('isVisible');\n const result = await this.page.evaluate<boolean>(script);\n return result ?? false;\n }\n\n async isHidden(): Promise<boolean> {\n return !(await this.isVisible());\n }\n\n async isEnabled(): Promise<boolean> {\n const disabled = await this.evaluateProperty('disabled');\n return !disabled;\n }\n\n async isDisabled(): Promise<boolean> {\n const disabled = await this.evaluateProperty('disabled');\n return !!disabled;\n }\n\n async isChecked(): Promise<boolean> {\n const checked = await this.evaluateProperty('checked');\n return !!checked;\n }\n\n // ── Helper / query methods ────────────────────────────────────────\n\n async evaluateProperty(prop: string): Promise<unknown> {\n const script = this.buildResolveScript('getProperty', prop);\n return this.page.evaluate<unknown>(script);\n }\n\n async textContent(): Promise<string | null> {\n const script = this.buildResolveScript('textContent');\n return this.page.evaluate<string | null>(script);\n }\n\n async innerText(): Promise<string> {\n const script = this.buildResolveScript('innerText');\n const result = await this.page.evaluate<string>(script);\n return result ?? '';\n }\n\n async inputValue(): Promise<string> {\n const script = this.buildResolveScript('inputValue');\n const result = await this.page.evaluate<string>(script);\n return result ?? '';\n }\n\n async count(): Promise<number> {\n const script = this.buildResolveScript('count');\n const result = await this.page.evaluate<number>(script);\n return result ?? 0;\n }\n\n async waitFor(options?: { state?: 'visible' | 'hidden' | 'attached' | 'detached'; timeout?: number }): Promise<void> {\n const timeout = options?.timeout ?? 5000;\n const state = options?.state ?? 'visible';\n const start = Date.now();\n const pollInterval = 100;\n\n while (Date.now() - start < timeout) {\n if (state === 'visible' && (await this.isVisible())) return;\n if (state === 'hidden' && !(await this.isVisible())) return;\n if (state === 'attached' && (await this.count()) > 0) return;\n if (state === 'detached' && (await this.count()) === 0) return;\n await new Promise((r) => setTimeout(r, pollInterval));\n }\n\n throw new Error(`Locator waitFor(\"${state}\") timed out after ${timeout}ms`);\n }\n\n // ── Internal ──────────────────────────────────────────────────────\n\n /**\n * Resolves the locator chain in the page, marks the target element\n * with a data-pw-proxy attribute, and returns the CSS selector.\n */\n private async resolveAndMark(timeout?: number): Promise<string> {\n const effectiveTimeout = timeout ?? 5000;\n const start = Date.now();\n const pollInterval = 100;\n const markId = `pw-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;\n\n while (Date.now() - start < effectiveTimeout) {\n const script = this.buildResolveScript('mark', markId);\n const found = await this.page.evaluate<boolean>(script);\n if (found) {\n return `[data-pw-proxy=\"${markId}\"]`;\n }\n await new Promise((r) => setTimeout(r, pollInterval));\n }\n\n throw new Error(`Locator could not resolve element within ${effectiveTimeout}ms. Steps: ${this.describeSteps()}`);\n }\n\n /**\n * Human-readable description of the locator chain for error messages.\n */\n private describeSteps(): string {\n return this.steps\n .map((step) => {\n switch (step.type) {\n case 'role':\n return `getByRole(\"${step.role}\"${step.options?.name ? `, { name: \"${String(step.options.name)}\" }` : ''})`;\n case 'text':\n return `getByText(\"${String(step.text)}\")`;\n case 'label':\n return `getByLabel(\"${String(step.text)}\")`;\n case 'placeholder':\n return `getByPlaceholder(\"${String(step.text)}\")`;\n case 'testId':\n return `getByTestId(\"${step.testId}\")`;\n case 'css':\n return `locator(\"${step.selector}\")`;\n case 'filter':\n return 'filter(...)';\n case 'first':\n return 'first()';\n case 'nth':\n return `nth(${step.index})`;\n }\n })\n .join('.');\n }\n\n /**\n * Serializes locator steps to JSON-safe format, converting RegExp\n * instances to { __regex__, flags } objects.\n */\n private serializeSteps(): unknown[] {\n return this.steps.map((step) => {\n if (step.type === 'text' || step.type === 'label' || step.type === 'placeholder') {\n return {\n ...step,\n text: step.text instanceof RegExp ? { __regex__: step.text.source, flags: step.text.flags } : step.text,\n };\n }\n if (step.type === 'role' && step.options?.name instanceof RegExp) {\n return {\n ...step,\n options: {\n ...step.options,\n name: { __regex__: step.options.name.source, flags: step.options.name.flags },\n },\n };\n }\n if (step.type === 'filter') {\n const filterOpts: Record<string, unknown> = {};\n if (step.options.hasText) {\n filterOpts.hasText =\n step.options.hasText instanceof RegExp\n ? { __regex__: step.options.hasText.source, flags: step.options.hasText.flags }\n : step.options.hasText;\n }\n if (step.options.has) {\n filterOpts.hasSteps = (step.options.has as LocatorProxy).serializeSteps();\n }\n return { type: 'filter', options: filterOpts };\n }\n return step;\n });\n }\n\n /**\n * Builds a self-contained JavaScript script that runs in the page context.\n * Resolves the locator step chain using DOM APIs and returns the result\n * based on the requested action.\n */\n private buildResolveScript(action: ResolveAction, extra?: string): string {\n const stepsJson = JSON.stringify(this.serializeSteps());\n\n return `(function() {\n var steps = ${stepsJson};\n var action = \"${action}\";\n var extra = ${extra !== undefined ? JSON.stringify(extra) : 'null'};\n\n function toRegex(v) {\n if (v && typeof v === 'object' && v.__regex__) return new RegExp(v.__regex__, v.flags || '');\n return null;\n }\n\n function matchText(actual, expected, exact) {\n if (!actual) return false;\n var regex = toRegex(expected);\n if (regex) return regex.test(actual);\n if (exact) return actual === expected;\n return actual.toLowerCase().indexOf(expected.toLowerCase()) !== -1;\n }\n\n var ROLE_MAP = {\n heading: 'h1,h2,h3,h4,h5,h6,[role=\"heading\"]',\n link: 'a[href],[role=\"link\"]',\n button: 'button,input[type=\"button\"],input[type=\"submit\"],input[type=\"reset\"],[role=\"button\"]',\n textbox: 'input:not([type]),input[type=\"text\"],input[type=\"email\"],input[type=\"password\"],input[type=\"search\"],input[type=\"tel\"],input[type=\"url\"],input[type=\"number\"],textarea,[role=\"textbox\"]',\n checkbox: 'input[type=\"checkbox\"],[role=\"checkbox\"]',\n radio: 'input[type=\"radio\"],[role=\"radio\"]',\n combobox: 'select,[role=\"combobox\"],[role=\"listbox\"]',\n navigation: 'nav,[role=\"navigation\"]',\n main: 'main,[role=\"main\"]',\n banner: 'header,[role=\"banner\"]',\n contentinfo: 'footer,[role=\"contentinfo\"]',\n region: 'section[aria-label],section[aria-labelledby],[role=\"region\"]',\n list: 'ul,ol,[role=\"list\"]',\n listitem: 'li,[role=\"listitem\"]',\n img: 'img,[role=\"img\"]',\n dialog: 'dialog,[role=\"dialog\"],[role=\"alertdialog\"]',\n tab: '[role=\"tab\"]',\n tablist: '[role=\"tablist\"]',\n tabpanel: '[role=\"tabpanel\"]',\n cell: 'td,[role=\"cell\"]',\n row: 'tr,[role=\"row\"]',\n table: 'table,[role=\"table\"],[role=\"grid\"]',\n menuitem: '[role=\"menuitem\"]',\n menu: '[role=\"menu\"],[role=\"menubar\"]',\n switch: '[role=\"switch\"]',\n alert: '[role=\"alert\"]',\n status: '[role=\"status\"]',\n progressbar: 'progress,[role=\"progressbar\"]',\n spinbutton: 'input[type=\"number\"],[role=\"spinbutton\"]',\n slider: 'input[type=\"range\"],[role=\"slider\"]',\n separator: 'hr,[role=\"separator\"]',\n tooltip: '[role=\"tooltip\"]',\n tree: '[role=\"tree\"]',\n treeitem: '[role=\"treeitem\"]',\n group: 'fieldset,[role=\"group\"]',\n article: 'article,[role=\"article\"]',\n complementary: 'aside,[role=\"complementary\"]',\n form: 'form,[role=\"form\"]',\n search: '[role=\"search\"]'\n };\n\n function getAccessibleName(el) {\n if (el.getAttribute('aria-label')) return el.getAttribute('aria-label');\n var labelledBy = el.getAttribute('aria-labelledby');\n if (labelledBy) {\n var labelEl = document.getElementById(labelledBy);\n if (labelEl) return labelEl.textContent.trim();\n }\n if (el.id) {\n var labels = document.querySelectorAll('label[for=\"' + el.id + '\"]');\n if (labels.length > 0) return labels[0].textContent.trim();\n }\n var parentLabel = el.closest('label');\n if (parentLabel) {\n var clone = parentLabel.cloneNode(true);\n var inputs = clone.querySelectorAll('input,select,textarea');\n inputs.forEach(function(inp) { inp.remove(); });\n var t = clone.textContent.trim();\n if (t) return t;\n }\n if (el.placeholder) return el.placeholder;\n if (el.title) return el.title;\n if (el.alt) return el.alt;\n return (el.textContent || '').trim();\n }\n\n function resolveSteps(candidates, stepsToResolve) {\n for (var i = 0; i < stepsToResolve.length; i++) {\n var step = stepsToResolve[i];\n\n if (step.type === 'role') {\n var sel = ROLE_MAP[step.role] || '[role=\"' + step.role + '\"]';\n var matching = [];\n for (var c = 0; c < candidates.length; c++) {\n var children = candidates[c].querySelectorAll(sel);\n for (var j = 0; j < children.length; j++) matching.push(children[j]);\n if (candidates[c].matches && candidates[c].matches(sel)) matching.push(candidates[c]);\n }\n if (step.options && step.options.name != null) {\n var name = step.options.name;\n var exact = step.options.exact || false;\n matching = matching.filter(function(el) {\n return matchText(getAccessibleName(el), name, exact);\n });\n }\n candidates = matching;\n\n } else if (step.type === 'text') {\n var textFiltered = [];\n for (var ci = 0; ci < candidates.length; ci++) {\n var allEls = candidates[ci].querySelectorAll('*');\n for (var ji = 0; ji < allEls.length; ji++) {\n if (matchText(allEls[ji].textContent, step.text, step.options && step.options.exact)) {\n textFiltered.push(allEls[ji]);\n }\n }\n if (matchText(candidates[ci].textContent, step.text, step.options && step.options.exact)) {\n textFiltered.push(candidates[ci]);\n }\n }\n candidates = textFiltered;\n\n } else if (step.type === 'label') {\n var labeled = [];\n var allLabels = document.querySelectorAll('label');\n for (var li = 0; li < allLabels.length; li++) {\n var labelText = allLabels[li].textContent.trim();\n if (matchText(labelText, step.text, step.options && step.options.exact)) {\n var forAttr = allLabels[li].getAttribute('for');\n if (forAttr) {\n var target = document.getElementById(forAttr);\n if (target) labeled.push(target);\n }\n var inner = allLabels[li].querySelectorAll('input,select,textarea');\n for (var ki = 0; ki < inner.length; ki++) labeled.push(inner[ki]);\n }\n }\n var byAria = document.querySelectorAll('[aria-label]');\n for (var ai = 0; ai < byAria.length; ai++) {\n if (matchText(byAria[ai].getAttribute('aria-label'), step.text, step.options && step.options.exact)) {\n labeled.push(byAria[ai]);\n }\n }\n candidates = labeled;\n\n } else if (step.type === 'placeholder') {\n var phMatches = [];\n for (var pi = 0; pi < candidates.length; pi++) {\n var phInputs = candidates[pi].querySelectorAll('[placeholder]');\n for (var pj = 0; pj < phInputs.length; pj++) {\n if (matchText(phInputs[pj].placeholder, step.text, step.options && step.options.exact)) {\n phMatches.push(phInputs[pj]);\n }\n }\n if (candidates[pi].placeholder && matchText(candidates[pi].placeholder, step.text, step.options && step.options.exact)) {\n phMatches.push(candidates[pi]);\n }\n }\n candidates = phMatches;\n\n } else if (step.type === 'testId') {\n var tidMatches = [];\n for (var ti = 0; ti < candidates.length; ti++) {\n var tidFound = candidates[ti].querySelectorAll('[data-testid=\"' + step.testId + '\"]');\n for (var tj = 0; tj < tidFound.length; tj++) tidMatches.push(tidFound[tj]);\n if (candidates[ti].getAttribute && candidates[ti].getAttribute('data-testid') === step.testId) {\n tidMatches.push(candidates[ti]);\n }\n }\n candidates = tidMatches;\n\n } else if (step.type === 'css') {\n var cssMatches = [];\n for (var si = 0; si < candidates.length; si++) {\n var cssFound = candidates[si].querySelectorAll(step.selector);\n for (var sj = 0; sj < cssFound.length; sj++) cssMatches.push(cssFound[sj]);\n }\n candidates = cssMatches;\n\n } else if (step.type === 'filter') {\n if (step.options.hasText) {\n candidates = candidates.filter(function(el) {\n return matchText(el.textContent, step.options.hasText, false);\n });\n }\n if (step.options.hasSteps) {\n candidates = candidates.filter(function(el) {\n var sub = resolveSteps([el], step.options.hasSteps);\n return sub.length > 0;\n });\n }\n\n } else if (step.type === 'first') {\n candidates = candidates.length > 0 ? [candidates[0]] : [];\n\n } else if (step.type === 'nth') {\n var idx = step.index;\n if (idx < 0) idx = candidates.length + idx;\n candidates = (idx >= 0 && idx < candidates.length) ? [candidates[idx]] : [];\n }\n\n candidates = candidates.filter(function(el, elIdx, arr) {\n return arr.indexOf(el) === elIdx;\n });\n }\n return candidates;\n }\n\n var candidates = resolveSteps([document], steps);\n\n if (action === 'count') return candidates.length;\n\n if (action === 'isVisible') {\n if (candidates.length === 0) return false;\n var el = candidates[0];\n var rect = el.getBoundingClientRect();\n var style = window.getComputedStyle(el);\n return rect.width > 0 && rect.height > 0 && style.visibility !== 'hidden' && style.display !== 'none';\n }\n\n if (candidates.length === 0) {\n if (action === 'textContent') return null;\n if (action === 'innerText') return '';\n if (action === 'inputValue') return '';\n if (action === 'getProperty') return null;\n return false;\n }\n\n var target = candidates[0];\n\n if (action === 'mark') {\n target.setAttribute('data-pw-proxy', extra);\n return true;\n }\n\n if (action === 'getProperty') {\n return target[extra];\n }\n\n if (action === 'textContent') {\n return target.textContent;\n }\n\n if (action === 'innerText') {\n return target.innerText || target.textContent || '';\n }\n\n if (action === 'inputValue') {\n return target.value || '';\n }\n\n return null;\n})()`;\n }\n}\n","/**\n * Extension Expect - Custom assertion wrapper for proxy objects\n *\n * DESIGN PATTERNS:\n * - Adapter pattern to bridge Playwright expect with proxy objects\n * - Polling pattern for async DOM assertions\n * - Pure functions with no side effects (except polling)\n *\n * CODING STANDARDS:\n * - Export individual functions, not classes\n * - Use descriptive function names with verbs\n * - Keep functions small and focused\n *\n * AVOID:\n * - Direct Playwright expect usage on proxy objects\n * - Blocking operations without timeout\n * - Missing negation support\n */\n\nimport { LOCATOR_PROXY_BRAND, type LocatorProxy } from '../services/LocatorProxy.js';\n\n/**\n * Brand symbol for identifying ExtensionPageProxy instances\n */\nexport const PAGE_PROXY_BRAND = Symbol.for('__pageProxyBrand__');\n\n/**\n * Interface for page proxy objects that support URL checking\n */\ninterface PageProxyLike {\n [PAGE_PROXY_BRAND]: boolean;\n currentUrlAsync(): Promise<string>;\n}\n\n/**\n * Polling configuration\n */\nconst POLL_INTERVAL_MS = 100;\nconst DEFAULT_TIMEOUT_MS = 5000;\n\n/**\n * Polls a condition until it passes or times out.\n * Throws assertion error on timeout.\n */\nasync function pollUntil(\n check: () => Promise<boolean>,\n errorMessage: string,\n timeout: number = DEFAULT_TIMEOUT_MS,\n): Promise<void> {\n const start = Date.now();\n\n while (Date.now() - start < timeout) {\n const passed = await check();\n if (passed) return;\n await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));\n }\n\n throw new Error(errorMessage);\n}\n\n/**\n * Assertion options with optional timeout\n */\ninterface AssertionOptions {\n timeout?: number;\n}\n\n/**\n * Assertion set for locator proxy objects\n */\ninterface LocatorAssertions {\n toBeVisible(options?: AssertionOptions): Promise<void>;\n toBeHidden(options?: AssertionOptions): Promise<void>;\n toBeDisabled(options?: AssertionOptions): Promise<void>;\n toBeEnabled(options?: AssertionOptions): Promise<void>;\n toBeChecked(options?: AssertionOptions): Promise<void>;\n toHaveValue(expected: string | RegExp, options?: AssertionOptions): Promise<void>;\n toHaveText(expected: string | RegExp, options?: AssertionOptions): Promise<void>;\n toContainText(expected: string | RegExp, options?: AssertionOptions): Promise<void>;\n toHaveCount(expected: number, options?: AssertionOptions): Promise<void>;\n not: LocatorAssertions;\n}\n\n/**\n * Assertion set for page proxy objects\n */\ninterface PageAssertions {\n toHaveURL(expected: string | RegExp, options?: AssertionOptions): Promise<void>;\n toHaveTitle(expected: string | RegExp, options?: AssertionOptions): Promise<void>;\n not: PageAssertions;\n}\n\n/**\n * Checks if a string matches the expected value (string or RegExp)\n */\nfunction matchesExpected(actual: string | null | undefined, expected: string | RegExp): boolean {\n if (actual == null) return false;\n if (expected instanceof RegExp) return expected.test(actual);\n return actual === expected;\n}\n\n/**\n * Checks if a string contains the expected value (string or RegExp)\n */\nfunction containsExpected(actual: string | null | undefined, expected: string | RegExp): boolean {\n if (actual == null) return false;\n if (expected instanceof RegExp) return expected.test(actual);\n return actual.includes(expected);\n}\n\n/**\n * Creates locator assertions for a LocatorProxy target.\n */\nfunction createLocatorAssertions(target: LocatorProxy, negated: boolean): LocatorAssertions {\n const assert = (condition: boolean): boolean => (negated ? !condition : condition);\n\n const assertions: LocatorAssertions = {\n async toBeVisible(options?: AssertionOptions): Promise<void> {\n await pollUntil(\n async () => assert(await target.isVisible()),\n negated ? 'Expected element to not be visible' : 'Expected element to be visible',\n options?.timeout,\n );\n },\n\n async toBeHidden(options?: AssertionOptions): Promise<void> {\n await pollUntil(\n async () => assert(!(await target.isVisible())),\n negated ? 'Expected element to not be hidden' : 'Expected element to be hidden',\n options?.timeout,\n );\n },\n\n async toBeDisabled(options?: AssertionOptions): Promise<void> {\n await pollUntil(\n async () => assert(!!(await target.evaluateProperty('disabled'))),\n negated ? 'Expected element to not be disabled' : 'Expected element to be disabled',\n options?.timeout,\n );\n },\n\n async toBeEnabled(options?: AssertionOptions): Promise<void> {\n await pollUntil(\n async () => assert(!(await target.evaluateProperty('disabled'))),\n negated ? 'Expected element to not be enabled' : 'Expected element to be enabled',\n options?.timeout,\n );\n },\n\n async toBeChecked(options?: AssertionOptions): Promise<void> {\n await pollUntil(\n async () => assert(!!(await target.evaluateProperty('checked'))),\n negated ? 'Expected element to not be checked' : 'Expected element to be checked',\n options?.timeout,\n );\n },\n\n async toHaveValue(expected: string | RegExp, options?: AssertionOptions): Promise<void> {\n await pollUntil(\n async () => {\n const value = (await target.evaluateProperty('value')) as string;\n return assert(matchesExpected(value, expected));\n },\n negated\n ? `Expected element to not have value \"${String(expected)}\"`\n : `Expected element to have value \"${String(expected)}\"`,\n options?.timeout,\n );\n },\n\n async toHaveText(expected: string | RegExp, options?: AssertionOptions): Promise<void> {\n await pollUntil(\n async () => {\n const text = await target.textContent();\n return assert(matchesExpected(text?.trim() ?? null, expected));\n },\n negated\n ? `Expected element to not have text \"${String(expected)}\"`\n : `Expected element to have text \"${String(expected)}\"`,\n options?.timeout,\n );\n },\n\n async toContainText(expected: string | RegExp, options?: AssertionOptions): Promise<void> {\n await pollUntil(\n async () => {\n const text = await target.textContent();\n return assert(containsExpected(text, expected));\n },\n negated\n ? `Expected element to not contain text \"${String(expected)}\"`\n : `Expected element to contain text \"${String(expected)}\"`,\n options?.timeout,\n );\n },\n\n async toHaveCount(expected: number, options?: AssertionOptions): Promise<void> {\n await pollUntil(\n async () => {\n const count = await target.count();\n return assert(count === expected);\n },\n negated ? `Expected element count to not be ${expected}` : `Expected element count to be ${expected}`,\n options?.timeout,\n );\n },\n\n get not(): LocatorAssertions {\n return createLocatorAssertions(target, !negated);\n },\n };\n\n return assertions;\n}\n\n/**\n * Creates page assertions for a page proxy target.\n */\nfunction createPageAssertions(target: PageProxyLike, negated: boolean): PageAssertions {\n const assert = (condition: boolean): boolean => (negated ? !condition : condition);\n\n const assertions: PageAssertions = {\n async toHaveURL(expected: string | RegExp, options?: AssertionOptions): Promise<void> {\n await pollUntil(\n async () => {\n const currentUrl = await target.currentUrlAsync();\n return assert(matchesExpected(currentUrl, expected));\n },\n negated\n ? `Expected page to not have URL \"${String(expected)}\"`\n : `Expected page to have URL \"${String(expected)}\"`,\n options?.timeout,\n );\n },\n\n async toHaveTitle(expected: string | RegExp, options?: AssertionOptions): Promise<void> {\n await pollUntil(\n async () => {\n const page = target as unknown as { title(): Promise<string> };\n const title = await page.title();\n return assert(matchesExpected(title, expected));\n },\n negated\n ? `Expected page to not have title \"${String(expected)}\"`\n : `Expected page to have title \"${String(expected)}\"`,\n options?.timeout,\n );\n },\n\n get not(): PageAssertions {\n return createPageAssertions(target, !negated);\n },\n };\n\n return assertions;\n}\n\n/**\n * Creates an extension-mode expect wrapper for proxy objects.\n * Detects LocatorProxy and PageProxy via brand symbols and returns\n * the appropriate assertion set with polling semantics.\n */\nexport function createExtensionExpect(target: unknown): LocatorAssertions | PageAssertions {\n const obj = target as Record<symbol, boolean>;\n\n if (obj[LOCATOR_PROXY_BRAND]) {\n return createLocatorAssertions(target as LocatorProxy, false);\n }\n\n if (obj[PAGE_PROXY_BRAND]) {\n return createPageAssertions(target as PageProxyLike, false);\n }\n\n throw new Error('createExtensionExpect called with unsupported target');\n}\n","/**\n * PlaywrightTestStub - Proxy module for @playwright/test\n *\n * DESIGN PATTERNS:\n * - Proxy pattern to intercept Playwright test API calls\n * - Collector pattern to gather test blocks for sequential execution\n * - Module aliasing compatible structure\n *\n * CODING STANDARDS:\n * - Collect test blocks into retrievable TestSuite structure\n * - Support test(), test.describe(), test.beforeEach(), test.afterEach()\n * - Re-export real expect from @playwright/test\n * - Clear collected tests after retrieval\n *\n * AVOID:\n * - Executing tests immediately (defer to SpecRunner)\n * - Modifying the real @playwright/test behavior\n * - Global state that persists across spec files\n */\n\nimport { createRequire } from 'node:module';\nimport type { Browser, BrowserContext, Page } from 'playwright';\nimport { LOCATOR_PROXY_BRAND } from '../services/LocatorProxy.js';\nimport { PAGE_PROXY_BRAND, createExtensionExpect } from '../utils/extension-expect.js';\n\n/**\n * Compile-time constant injected by SpecBundlerService via Bun's `define`.\n * Contains the absolute path to this stub file's real location on disk.\n * Only present when the stub is bundled into a spec output file.\n */\ndeclare const __PLAYWRIGHT_MCP_STUB_PATH__: string;\n\n/**\n * Resolves a base path for createRequire that can find `playwright/test`.\n * When loaded from the stub's real location, import.meta.url works.\n * When bundled into a temp output (e.g. /tmp/browse-tool-bundles/),\n * import.meta.url points to the bundle and playwright can't be found,\n * so we fall back to the compile-time injected stub path.\n */\nfunction resolveRequireBase(): string {\n try {\n const req = createRequire(import.meta.url);\n req.resolve('playwright/test');\n return import.meta.url;\n } catch {\n if (typeof __PLAYWRIGHT_MCP_STUB_PATH__ === 'string') {\n return `file://${__PLAYWRIGHT_MCP_STUB_PATH__}`;\n }\n return import.meta.url;\n }\n}\n\n// Clear Playwright's double-load guard before importing playwright/test\n// to avoid \"Requiring @playwright/test second time\" when the stub is bundled\n// into spec files that are loaded alongside the MCP server's own playwright import\nconst proc = process as unknown as Record<string, unknown>;\nconst savedPwInitiator = proc.__pw_initiator__;\nproc.__pw_initiator__ = undefined;\nconst _require = createRequire(resolveRequireBase());\nconst _realPlaywrightTest = _require('playwright/test') as Record<string, unknown> & {\n expect: typeof import('playwright/test')['expect'];\n};\nconst { expect: playwrightExpect } = _realPlaywrightTest;\nproc.__pw_initiator__ = savedPwInitiator;\n\n/**\n * Re-export all real playwright/test exports that specs may use.\n * Our custom `test` and `expect` override the real ones; everything\n * else (request, chromium, firefox, webkit, devices, selectors,\n * defineConfig, mergeExpects, mergeTests) is passed through unchanged.\n */\nexport const { request, chromium, firefox, webkit, devices, selectors, defineConfig, mergeExpects, mergeTests } =\n _realPlaywrightTest as Record<string, unknown>;\n\n/**\n * Fixtures passed to test functions.\n * Compatible with Playwright's test fixture pattern.\n * Browser is optional for extension mode which uses persistent context.\n */\nexport interface TestFixtures {\n page: Page;\n context: BrowserContext;\n browser?: Browser;\n /** Playwright module instance for creating API request contexts */\n playwright?: typeof import('playwright');\n /** API request context scoped to the test */\n request?: import('playwright').APIRequestContext;\n /** Extended fixtures added via test.extend() */\n [key: string]: unknown;\n}\n\n/**\n * A single test block collected from the spec file.\n * The `fn` stores the RAW test function — fixture resolution happens in flattenTests.\n */\nexport interface TestBlock {\n /** Test title */\n title: string;\n /** Test function (raw — no fixture wrapping at collection time) */\n fn: (fixtures: TestFixtures) => Promise<void>;\n /** Full path including describe blocks */\n fullTitle: string;\n /** Whether test is marked with test.only() */\n only: boolean;\n /** Whether test is marked with test.skip() */\n skip: boolean;\n}\n\n/**\n * Holds fixture definitions for a describe scope.\n * Fixtures are inherited from parent scopes and merged with local definitions.\n */\nclass FixtureScope {\n constructor(\n private readonly defs: Map<string, FixtureFunction>,\n private readonly parent: FixtureScope | null = null,\n ) {}\n\n /** Merges all fixture defs from root to this scope */\n getAllDefs(): Map<string, FixtureFunction> {\n const parentDefs = this.parent?.getAllDefs() ?? new Map<string, FixtureFunction>();\n return new Map([...parentDefs, ...this.defs]);\n }\n\n /** Creates a child scope that inherits from this one */\n extend(defs: Map<string, FixtureFunction>): FixtureScope {\n return new FixtureScope(defs, this);\n }\n}\n\n/**\n * A describe block containing tests and hooks.\n */\nexport interface DescribeBlock {\n /** Describe block title */\n title: string;\n /** Tests within this describe */\n tests: TestBlock[];\n /** beforeAll hooks (run once before first test in block) */\n beforeAllHooks?: Array<(fixtures: TestFixtures) => Promise<void>>;\n /** afterAll hooks (run once after last test in block) */\n afterAllHooks?: Array<(fixtures: TestFixtures) => Promise<void>>;\n /** beforeEach hooks (raw — not fixture-wrapped) */\n beforeEachHooks: Array<(fixtures: TestFixtures) => Promise<void>>;\n /** afterEach hooks (raw — not fixture-wrapped) */\n afterEachHooks: Array<(fixtures: TestFixtures) => Promise<void>>;\n /** Nested describe blocks */\n children: DescribeBlock[];\n /** Parent describe block (null for root) */\n parent: DescribeBlock | null;\n /** Fixture scope for this describe block — merged from test.extend() calls */\n fixtureScope: FixtureScope | null;\n}\n\n/**\n * Complete test suite collected from a spec file.\n */\nexport interface TestSuite {\n /** Spec file path or identifier */\n specPath: string;\n /** Root describe block (unnamed) */\n root: DescribeBlock;\n /** Flattened list of all tests in execution order */\n allTests: TestBlock[];\n /** Total test count */\n testCount: number;\n}\n\n/**\n * Global collector state for the current spec file.\n * Reset when a new spec is loaded or after retrieval.\n */\nclass TestCollector {\n private currentSpecPath = '';\n private rootDescribe: DescribeBlock;\n private currentDescribe: DescribeBlock;\n\n constructor() {\n this.rootDescribe = this.createDescribeBlock('', null);\n this.currentDescribe = this.rootDescribe;\n }\n\n private createDescribeBlock(title: string, parent: DescribeBlock | null): DescribeBlock {\n return {\n title,\n tests: [],\n beforeEachHooks: [],\n afterEachHooks: [],\n children: [],\n parent,\n fixtureScope: null,\n };\n }\n\n /**\n * Sets the current spec file path.\n */\n setSpecPath(specPath: string): void {\n this.currentSpecPath = specPath;\n }\n\n /**\n * Resets the collector for a new spec file.\n */\n reset(): void {\n this.currentSpecPath = '';\n this.rootDescribe = this.createDescribeBlock('', null);\n this.currentDescribe = this.rootDescribe;\n }\n\n /**\n * Adds a test to the current describe block.\n * Returns the TestBlock for flag modification (only/skip).\n */\n addTest(title: string, fn: (fixtures: TestFixtures) => Promise<void>, only = false, skip = false): TestBlock {\n const fullTitle = this.getFullTitle(title);\n const testBlock: TestBlock = { title, fn, fullTitle, only, skip };\n this.currentDescribe.tests.push(testBlock);\n return testBlock;\n }\n\n /**\n * Sets or merges fixture definitions on the current describe block.\n * Called by createTestFunction when tests/hooks are registered.\n */\n ensureFixtureScope(defs: Map<string, FixtureFunction>): void {\n if (defs.size === 0) return;\n // Only set once per describe block — subsequent calls with the same\n // or subset defs are no-ops. This prevents duplicate fixture entries\n // when multiple tests/hooks register in the same block.\n if (this.currentDescribe.fixtureScope) return;\n\n // Find parent scope from ancestor describe blocks\n let parentScope: FixtureScope | null = null;\n let ancestor = this.currentDescribe.parent;\n while (ancestor) {\n if (ancestor.fixtureScope) {\n parentScope = ancestor.fixtureScope;\n break;\n }\n ancestor = ancestor.parent;\n }\n this.currentDescribe.fixtureScope = new FixtureScope(defs, parentScope);\n }\n\n /**\n * Enters a new describe block.\n */\n enterDescribe(title: string): void {\n const newDescribe = this.createDescribeBlock(title, this.currentDescribe);\n this.currentDescribe.children.push(newDescribe);\n this.currentDescribe = newDescribe;\n }\n\n /**\n * Exits the current describe block.\n */\n exitDescribe(): void {\n if (this.currentDescribe.parent) {\n this.currentDescribe = this.currentDescribe.parent;\n }\n }\n\n /**\n * Adds a beforeAll hook to the current describe block.\n * In the stub, beforeAll runs once before the first test in the describe.\n */\n addBeforeAll(fn: (fixtures: TestFixtures) => Promise<void>): void {\n if (!this.currentDescribe.beforeAllHooks) {\n this.currentDescribe.beforeAllHooks = [];\n }\n this.currentDescribe.beforeAllHooks.push(fn);\n }\n\n /**\n * Adds an afterAll hook to the current describe block.\n */\n addAfterAll(fn: (fixtures: TestFixtures) => Promise<void>): void {\n if (!this.currentDescribe.afterAllHooks) {\n this.currentDescribe.afterAllHooks = [];\n }\n this.currentDescribe.afterAllHooks.push(fn);\n }\n\n /**\n * Adds a beforeEach hook to the current describe block.\n */\n addBeforeEach(fn: (fixtures: TestFixtures) => Promise<void>): void {\n this.currentDescribe.beforeEachHooks.push(fn);\n }\n\n /**\n * Adds an afterEach hook to the current describe block.\n */\n addAfterEach(fn: (fixtures: TestFixtures) => Promise<void>): void {\n this.currentDescribe.afterEachHooks.push(fn);\n }\n\n /**\n * Marks all tests in the current describe block as skipped.\n * Used when test.skip(true) is called inside a describe block.\n */\n markCurrentDescribeSkipped(): void {\n for (const test of this.currentDescribe.tests) {\n test.skip = true;\n }\n for (const child of this.currentDescribe.children) {\n this.markDescribeSkipped(child);\n }\n }\n\n private markDescribeSkipped(describe: DescribeBlock): void {\n for (const test of describe.tests) {\n test.skip = true;\n }\n for (const child of describe.children) {\n this.markDescribeSkipped(child);\n }\n }\n\n /**\n * Gets the full title path for a test.\n */\n private getFullTitle(testTitle: string): string {\n const parts: string[] = [];\n let current: DescribeBlock | null = this.currentDescribe;\n\n while (current) {\n if (current.title) {\n parts.unshift(current.title);\n }\n current = current.parent;\n }\n\n parts.push(testTitle);\n return parts.join(' > ');\n }\n\n /**\n * Flattens all tests into execution order, including hooks.\n */\n /**\n * Collects fixture defs from the describe block and all ancestors.\n */\n private collectFixtureDefs(describe: DescribeBlock): Map<string, FixtureFunction> {\n return describe.fixtureScope?.getAllDefs() ?? new Map();\n }\n\n private flattenTests(\n describe: DescribeBlock,\n ancestorBeforeEach: Array<(fixtures: TestFixtures) => Promise<void>>,\n ancestorAfterEach: Array<(fixtures: TestFixtures) => Promise<void>>,\n ): TestBlock[] {\n const tests: TestBlock[] = [];\n\n const currentBeforeEach = [...ancestorBeforeEach, ...describe.beforeEachHooks];\n const currentAfterEach = [...describe.afterEachHooks, ...ancestorAfterEach];\n const beforeAllHooks = describe.beforeAllHooks ?? [];\n const afterAllHooks = describe.afterAllHooks ?? [];\n\n // Build ONE fixture wrapper from the describe scope — shared by all tests + hooks\n const fixtureDefs = this.collectFixtureDefs(describe);\n const wrapWithFixtures = createFixtureWrapper(fixtureDefs);\n\n let beforeAllRan = false;\n const allTestsInBlock: TestBlock[] = [];\n\n for (const test of describe.tests) {\n // Combine hooks + test body into one raw function,\n // then wrap with fixtures ONCE. Fixtures resolve once and are\n // shared between beforeAll, beforeEach, test body, and afterEach.\n const combinedFn = async (fixtures: TestFixtures): Promise<void> => {\n if (!beforeAllRan && beforeAllHooks.length > 0) {\n beforeAllRan = true;\n for (const hook of beforeAllHooks) {\n await hook(fixtures);\n }\n }\n\n for (const hook of currentBeforeEach) {\n await hook(fixtures);\n }\n\n await test.fn(fixtures);\n\n for (const hook of currentAfterEach) {\n await hook(fixtures);\n }\n };\n\n allTestsInBlock.push({\n title: test.title,\n fullTitle: test.fullTitle,\n fn: wrapWithFixtures(combinedFn),\n only: test.only,\n skip: test.skip,\n });\n }\n\n for (const child of describe.children) {\n allTestsInBlock.push(...this.flattenTests(child, currentBeforeEach, currentAfterEach));\n }\n\n // Wrap the last test to run afterAll hooks after it completes\n if (afterAllHooks.length > 0 && allTestsInBlock.length > 0) {\n const lastTest = allTestsInBlock[allTestsInBlock.length - 1];\n const originalFn = lastTest.fn;\n lastTest.fn = async (fixtures: TestFixtures): Promise<void> => {\n try {\n await originalFn(fixtures);\n } finally {\n for (const hook of afterAllHooks) {\n await hook(fixtures);\n }\n }\n };\n }\n\n tests.push(...allTestsInBlock);\n return tests;\n }\n\n /**\n * Retrieves the collected test suite and resets for the next spec.\n */\n retrieveAndReset(): TestSuite {\n const allTests = this.flattenTests(this.rootDescribe, [], []);\n\n const suite: TestSuite = {\n specPath: this.currentSpecPath,\n root: this.rootDescribe,\n allTests,\n testCount: allTests.length,\n };\n\n this.reset();\n return suite;\n }\n\n /**\n * Retrieves the collected test suite without resetting.\n * Useful for inspection.\n */\n peek(): TestSuite {\n const allTests = this.flattenTests(this.rootDescribe, [], []);\n\n return {\n specPath: this.currentSpecPath,\n root: this.rootDescribe,\n allTests,\n testCount: allTests.length,\n };\n }\n}\n\n/**\n * Thrown by test.skip() inside a test body to signal the test should be skipped.\n */\nexport class SkipTestError extends Error {\n readonly isSkip = true;\n constructor(reason?: string) {\n super(reason ?? 'Skipped');\n this.name = 'SkipTestError';\n }\n}\n\n/**\n * Global test collector instance.\n * Uses globalThis to ensure the bundled code and SpecRunner share the same collector.\n */\nconst COLLECTOR_KEY = '__playwrightMcpTestCollector__';\nconst globalAny = globalThis as unknown as Record<string, TestCollector>;\nif (!globalAny[COLLECTOR_KEY]) {\n globalAny[COLLECTOR_KEY] = new TestCollector();\n}\nconst collector = globalAny[COLLECTOR_KEY];\n\n/**\n * Test function type that matches Playwright's test() signature.\n */\ntype TestFunction = (fixtures: TestFixtures) => Promise<void>;\n\n/**\n * Fixture definition function following Playwright's async use() pattern.\n * Setup runs before use(), teardown runs after use() returns.\n */\ntype FixtureFunction = (fixtures: Record<string, unknown>, use: (value: unknown) => Promise<void>) => Promise<void>;\n\n/**\n * Parses fixture definitions from the object passed to test.extend().\n * Supports both direct functions and [options, fn] tuple format.\n */\nfunction parseFixtureDefs(fixtures: Record<string, unknown>): Map<string, FixtureFunction> {\n const defs = new Map<string, FixtureFunction>();\n for (const [name, definition] of Object.entries(fixtures)) {\n if (typeof definition === 'function') {\n defs.set(name, definition as FixtureFunction);\n } else if (Array.isArray(definition) && definition.length === 2 && typeof definition[1] === 'function') {\n defs.set(name, definition[1] as FixtureFunction);\n }\n }\n return defs;\n}\n\n/**\n * Wraps a test/hook function to resolve custom fixtures before execution.\n * Uses recursive continuation chaining so each fixture's use() callback\n * scopes the next fixture setup, enabling proper teardown in reverse order.\n */\nfunction createFixtureWrapper(fixtureDefs: Map<string, FixtureFunction>): (fn: TestFunction) => TestFunction {\n if (fixtureDefs.size === 0) return (fn) => fn;\n\n const entries = [...fixtureDefs.entries()];\n\n return (fn: TestFunction): TestFunction => {\n return async (baseFixtures: TestFixtures) => {\n const resolved: TestFixtures = { ...baseFixtures };\n\n async function resolveChain(index: number): Promise<void> {\n if (index >= entries.length) {\n await fn(resolved);\n return;\n }\n\n const [name, setup] = entries[index];\n await setup(resolved, async (value: unknown) => {\n resolved[name] = value;\n await resolveChain(index + 1);\n });\n }\n\n await resolveChain(0);\n };\n };\n}\n\n/**\n * Creates a test function with optional custom fixture definitions.\n * Returned function has the same API surface as Playwright's test().\n */\nfunction createTestFunction(fixtureDefs: Map<string, FixtureFunction>): typeof test {\n // Push fixture defs to the current describe scope whenever a test/hook is registered.\n // This ensures the scope is set even if extend() is called before describe().\n function ensureScope(): void {\n collector.ensureFixtureScope(fixtureDefs);\n }\n\n function extendedTest(title: string, fn: TestFunction): void {\n ensureScope();\n collector.addTest(title, fn);\n }\n\n extendedTest.only = function extendedOnly(title: string, fn: TestFunction): void {\n ensureScope();\n collector.addTest(title, fn, true, false);\n };\n\n extendedTest.skip = function extendedSkip(\n titleOrCondition?: string | boolean,\n fnOrReason?: TestFunction | string,\n ): void {\n if (titleOrCondition === undefined || titleOrCondition === true) {\n throw new SkipTestError(typeof fnOrReason === 'string' ? fnOrReason : 'Skipped');\n }\n if (titleOrCondition === false) return;\n if (typeof titleOrCondition === 'string' && typeof fnOrReason === 'function') {\n ensureScope();\n collector.addTest(titleOrCondition, fnOrReason as TestFunction, false, true);\n }\n };\n\n extendedTest.describe = function extendedDescribe(title: string, fn: () => void): void {\n collector.enterDescribe(title);\n try {\n fn();\n } catch (error) {\n if (error instanceof SkipTestError) {\n collector.markCurrentDescribeSkipped();\n } else {\n throw error;\n }\n }\n collector.exitDescribe();\n };\n\n extendedTest.beforeAll = function extendedBeforeAll(fn: TestFunction): void {\n ensureScope();\n collector.addBeforeAll(fn);\n };\n\n extendedTest.afterAll = function extendedAfterAll(fn: TestFunction): void {\n ensureScope();\n collector.addAfterAll(fn);\n };\n\n extendedTest.beforeEach = function extendedBeforeEach(fn: TestFunction): void {\n ensureScope();\n collector.addBeforeEach(fn);\n };\n\n extendedTest.afterEach = function extendedAfterEach(fn: TestFunction): void {\n ensureScope();\n collector.addAfterEach(fn);\n };\n\n extendedTest.extend = function extendAgain(newFixtures: Record<string, unknown>): typeof test {\n const merged = new Map([...fixtureDefs, ...parseFixtureDefs(newFixtures)]);\n return createTestFunction(merged);\n };\n\n return extendedTest as typeof test;\n}\n\n/**\n * Proxy test function that collects tests instead of executing them.\n * Exported directly with methods attached to match Playwright's API surface.\n */\nexport function test(title: string, fn: TestFunction): void {\n collector.addTest(title, fn);\n}\n\nfunction testOnly(title: string, fn: TestFunction): void {\n collector.addTest(title, fn, true, false);\n}\n\n/**\n * Handles both overloads of test.skip():\n * - test.skip(title, fn) — define a skipped test\n * - test.skip(condition, reason) — skip current test conditionally (inside test body)\n * - test.skip() — unconditionally skip current test (inside test body)\n */\nfunction testSkip(titleOrCondition?: string | boolean, fnOrReason?: TestFunction | string): void {\n // test.skip() or test.skip(true) inside a test body — throw to skip\n if (titleOrCondition === undefined || titleOrCondition === true) {\n throw new SkipTestError(typeof fnOrReason === 'string' ? fnOrReason : 'Skipped');\n }\n // test.skip(false, reason) — don't skip, just continue\n if (titleOrCondition === false) {\n return;\n }\n // test.skip(title, fn) — register a skipped test\n if (typeof titleOrCondition === 'string' && typeof fnOrReason === 'function') {\n collector.addTest(titleOrCondition, fnOrReason as TestFunction, false, true);\n }\n}\n\nfunction describe(title: string, fn: () => void): void {\n collector.enterDescribe(title);\n try {\n fn();\n } catch (error) {\n if (error instanceof SkipTestError) {\n // test.skip(true) inside describe — mark all collected tests in this block as skipped\n collector.markCurrentDescribeSkipped();\n } else {\n throw error;\n }\n }\n collector.exitDescribe();\n}\n\nfunction beforeAll(fn: TestFunction): void {\n collector.addBeforeAll(fn);\n}\n\nfunction afterAll(fn: TestFunction): void {\n collector.addAfterAll(fn);\n}\n\nfunction beforeEach(fn: TestFunction): void {\n collector.addBeforeEach(fn);\n}\n\nfunction afterEach(fn: TestFunction): void {\n collector.addAfterEach(fn);\n}\n\ntest.describe = describe;\ntest.beforeAll = beforeAll;\ntest.afterAll = afterAll;\ntest.beforeEach = beforeEach;\ntest.afterEach = afterEach;\ntest.only = testOnly;\ntest.skip = testSkip;\n\n/**\n * Extends test with custom fixtures.\n * Returns a new test function that resolves fixtures using the use() continuation pattern.\n * Supports chaining: test.extend({...}).extend({...})\n */\ntest.extend = function extend(fixtures: Record<string, unknown>): typeof test {\n return createTestFunction(parseFixtureDefs(fixtures));\n};\n\n/**\n * Initializes the collector for a new spec file.\n * Call this before loading a spec file.\n */\nexport function initCollector(specPath: string): void {\n collector.reset();\n collector.setSpecPath(specPath);\n}\n\n/**\n * Retrieves the collected test suite and resets the collector.\n * Call this after loading a spec file.\n */\nexport function getCollectedSuite(): TestSuite {\n return collector.retrieveAndReset();\n}\n\n/**\n * Peeks at the current collected test suite without resetting.\n */\nexport function peekCollectedSuite(): TestSuite {\n return collector.peek();\n}\n\n/**\n * Resets the collector without retrieving.\n * Useful for cleanup on error.\n */\nexport function resetCollector(): void {\n collector.reset();\n}\n\n/**\n * Wrapper around Playwright's expect that detects proxy objects\n * and routes assertions through extension-mode DOM evaluation.\n * Falls back to real Playwright expect for non-proxy targets.\n * Typed as `typeof playwrightExpect` to preserve Playwright's overloaded signatures.\n */\nexport const expect: typeof playwrightExpect = ((target: unknown) => {\n const obj = target as Record<symbol, boolean>;\n if (obj?.[LOCATOR_PROXY_BRAND] || obj?.[PAGE_PROXY_BRAND]) {\n return createExtensionExpect(target);\n }\n return playwrightExpect(target);\n}) as typeof playwrightExpect;\n"],"mappings":"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"}
|