{"version":3,"file":"playwright-test-DNBba0cT.mjs","names":["page"],"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 type { RunSpecTestInfo } from '../services/TestInfoAdapter.js';\nimport { createExtensionExpect, PAGE_PROXY_BRAND } 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 * Config collected from test.use() and test.describe.configure().\n * Unknown keys are preserved to keep the collector extensible.\n */\nexport interface CollectedScopeConfig {\n  mode?: 'default' | 'parallel' | 'serial';\n  use?: Record<string, unknown>;\n  [key: string]: unknown;\n}\n\nconst BUILTIN_RUNTIME_USE_KEYS = new Set([\n  'acceptDownloads',\n  'baseURL',\n  'bypassCSP',\n  'colorScheme',\n  'deviceScaleFactor',\n  'extraHTTPHeaders',\n  'forcedColors',\n  'geolocation',\n  'hasTouch',\n  'httpCredentials',\n  'ignoreHTTPSErrors',\n  'isMobile',\n  'javaScriptEnabled',\n  'locale',\n  'offline',\n  'permissions',\n  'proxy',\n  'recordHar',\n  'reducedMotion',\n  'screen',\n  'serviceWorkers',\n  'storageState',\n  'timezoneId',\n  'userAgent',\n  'viewport',\n]);\n\nconst BUILTIN_UNSUPPORTED_USE_KEYS = new Set([\n  'browserName',\n  'channel',\n  'devtools',\n  'headless',\n  'launchOptions',\n  'slowMo',\n]);\n\nconst RESERVED_USE_FIXTURE_NAMES = new Set(['browser', 'context', 'page', 'playwright', 'request']);\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, testInfo?: RunSpecTestInfo) => 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  /** Resolved config inherited from describe/use scopes. */\n  config: CollectedScopeConfig;\n  /** Nearest serial describe scope id, if serial mode is enabled. */\n  serialScopeId?: number;\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  /** Stable id for this describe scope. */\n  id: number;\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?: TestFunction[];\n  /** afterAll hooks (run once after last test in block) */\n  afterAllHooks?: TestFunction[];\n  /** beforeEach hooks (raw — not fixture-wrapped) */\n  beforeEachHooks: TestFunction[];\n  /** afterEach hooks (raw — not fixture-wrapped) */\n  afterEachHooks: TestFunction[];\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  /** Scope config declared via test.use() and test.describe.configure(). */\n  config: CollectedScopeConfig;\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 nextDescribeId = 0;\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      id: this.nextDescribeId++,\n      title,\n      tests: [],\n      beforeEachHooks: [],\n      afterEachHooks: [],\n      children: [],\n      parent,\n      fixtureScope: null,\n      config: {},\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.nextDescribeId = 0;\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: TestFunction, only = false, skip = false): TestBlock {\n    const fullTitle = this.getFullTitle(title);\n    const testBlock: TestBlock = {\n      title,\n      fn: (fixtures, testInfo) => fn(fixtures, testInfo as RunSpecTestInfo),\n      fullTitle,\n      only,\n      skip,\n      config: {},\n    };\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   * Merges scope config into the current describe block.\n   */\n  configureDescribe(config: CollectedScopeConfig): void {\n    this.currentDescribe.config = mergeScopeConfig(this.currentDescribe.config, config);\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: TestFunction): 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: TestFunction): 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: TestFunction): void {\n    this.currentDescribe.beforeEachHooks.push(fn);\n  }\n\n  /**\n   * Adds an afterEach hook to the current describe block.\n   */\n  addAfterEach(fn: TestFunction): 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: TestFunction[],\n    ancestorAfterEach: TestFunction[],\n    inheritedConfig: CollectedScopeConfig = {},\n    inheritedSerialScopeId?: number,\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    const currentConfig = mergeScopeConfig(inheritedConfig, describe.config);\n    const currentSerialScopeId = currentConfig.mode === 'serial' ? describe.id : inheritedSerialScopeId;\n\n    // Build ONE fixture wrapper from the describe scope — shared by all tests + hooks\n    const fixtureDefs = this.collectFixtureDefs(describe);\n    const { fixtureOverrides } = splitUseConfig(currentConfig.use);\n    const wrapWithFixtures = createFixtureWrapper(fixtureDefs, fixtureOverrides);\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, testInfo?: RunSpecTestInfo): Promise<void> => {\n        if (!beforeAllRan && beforeAllHooks.length > 0) {\n          beforeAllRan = true;\n          for (const hook of beforeAllHooks) {\n            await hook(fixtures, testInfo as RunSpecTestInfo);\n          }\n        }\n\n        for (const hook of currentBeforeEach) {\n          await hook(fixtures, testInfo as RunSpecTestInfo);\n        }\n\n        await test.fn(fixtures, testInfo);\n\n        for (const hook of currentAfterEach) {\n          await hook(fixtures, testInfo as RunSpecTestInfo);\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        config: currentConfig,\n        serialScopeId: currentSerialScopeId,\n      });\n    }\n\n    for (const child of describe.children) {\n      allTestsInBlock.push(\n        ...this.flattenTests(child, currentBeforeEach, currentAfterEach, currentConfig, currentSerialScopeId),\n      );\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, testInfo?: RunSpecTestInfo): Promise<void> => {\n        try {\n          await originalFn(fixtures, testInfo);\n        } finally {\n          for (const hook of afterAllHooks) {\n            await hook(fixtures, testInfo as RunSpecTestInfo);\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, testInfo: RunSpecTestInfo) => Promise<void>;\n\ntype DescribeFunction = ((title: string, fn: () => void) => void) & {\n  configure: (config: CollectedScopeConfig) => void;\n};\n\ninterface TestApi {\n  (title: string, fn: TestFunction): void;\n  only(title: string, fn: TestFunction): void;\n  skip(titleOrCondition?: string | boolean, fnOrReason?: TestFunction | string): void;\n  describe: DescribeFunction;\n  beforeAll(fn: TestFunction): void;\n  afterAll(fn: TestFunction): void;\n  beforeEach(fn: TestFunction): void;\n  afterEach(fn: TestFunction): void;\n  use(useConfig: Record<string, unknown>): void;\n  extend(fixtures: Record<string, unknown>): TestApi;\n}\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\nfunction mergeScopeConfig(base: CollectedScopeConfig, patch: CollectedScopeConfig): CollectedScopeConfig {\n  const merged: CollectedScopeConfig = { ...base };\n  for (const [key, value] of Object.entries(patch)) {\n    if (key === 'use') {\n      merged.use = { ...base.use, ...(value as Record<string, unknown> | undefined) };\n      continue;\n    }\n    merged[key] = value;\n  }\n  return merged;\n}\n\nexport function splitUseConfig(useConfig: Record<string, unknown> | undefined): {\n  runtimeUse: Record<string, unknown>;\n  fixtureOverrides: Record<string, unknown>;\n  unsupportedKeys: string[];\n} {\n  const runtimeUse: Record<string, unknown> = {};\n  const fixtureOverrides: Record<string, unknown> = {};\n  const unsupportedKeys: string[] = [];\n\n  if (!useConfig) {\n    return { runtimeUse, fixtureOverrides, unsupportedKeys };\n  }\n\n  for (const [key, value] of Object.entries(useConfig)) {\n    if (BUILTIN_RUNTIME_USE_KEYS.has(key)) {\n      runtimeUse[key] = value;\n      continue;\n    }\n\n    if (BUILTIN_UNSUPPORTED_USE_KEYS.has(key)) {\n      unsupportedKeys.push(key);\n      continue;\n    }\n\n    if (RESERVED_USE_FIXTURE_NAMES.has(key)) {\n      unsupportedKeys.push(key);\n      continue;\n    }\n\n    fixtureOverrides[key] = value;\n  }\n\n  return { runtimeUse, fixtureOverrides, unsupportedKeys };\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(\n  fixtureDefs: Map<string, FixtureFunction>,\n  fixtureOverrides: Record<string, unknown>,\n): (\n  fn: (fixtures: TestFixtures, testInfo?: RunSpecTestInfo) => Promise<void>,\n) => (fixtures: TestFixtures, testInfo?: RunSpecTestInfo) => Promise<void> {\n  if (fixtureDefs.size === 0 && Object.keys(fixtureOverrides).length === 0) return (fn) => fn;\n\n  const entries = [...fixtureDefs.entries()];\n\n  return (fn) => {\n    return async (baseFixtures: TestFixtures, testInfo?: RunSpecTestInfo) => {\n      const resolved: TestFixtures = { ...baseFixtures, ...fixtureOverrides };\n\n      async function resolveChain(index: number): Promise<void> {\n        if (index >= entries.length) {\n          await fn(resolved, testInfo);\n          return;\n        }\n\n        const [name, setup] = entries[index];\n        if (Object.hasOwn(fixtureOverrides, name)) {\n          resolved[name] = fixtureOverrides[name];\n          await resolveChain(index + 1);\n          return;\n        }\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>): TestApi {\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  const extendedDescribe = 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  extendedDescribe.configure = function extendedDescribeConfigure(config: CollectedScopeConfig): void {\n    collector.configureDescribe(config);\n  };\n  extendedTest.describe = extendedDescribe;\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.use = function extendedUse(useConfig: Record<string, unknown>): void {\n    collector.configureDescribe({ use: useConfig });\n  };\n\n  extendedTest.extend = function extendAgain(newFixtures: Record<string, unknown>): TestApi {\n    const merged = new Map([...fixtureDefs, ...parseFixtureDefs(newFixtures)]);\n    return createTestFunction(merged);\n  };\n\n  return extendedTest as TestApi;\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 */\nconst testImpl = function test(title: string, fn: TestFunction): void {\n  collector.addTest(title, fn);\n};\nexport const test = testImpl as TestApi;\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\nconst describeApi = describe as DescribeFunction;\ndescribeApi.configure = function configureDescribe(config: CollectedScopeConfig): void {\n  collector.configureDescribe(config);\n};\ntest.describe = describeApi;\ntest.beforeAll = beforeAll;\ntest.afterAll = afterAll;\ntest.beforeEach = beforeEach;\ntest.afterEach = afterEach;\ntest.use = function use(useConfig: Record<string, unknown>): void {\n  collector.configureDescribe({ use: useConfig });\n};\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>): TestApi {\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 *\n * Implemented as a Proxy with only an `apply` trap so every static property\n * (`expect.objectContaining`, `expect.poll`, `expect.soft`, `expect.not`, ...)\n * resolves on the real Playwright export. A plain wrapper function would drop\n * them — Playwright's own `expect` is itself a Proxy that synthesizes\n * `soft`/`poll`/`configure` in its `get` trap, so they cannot be copied with\n * `Object.assign` either (RXX2-70).\n */\nexport const expect: typeof playwrightExpect = new Proxy(playwrightExpect, {\n  apply(target, thisArg, args: [unknown, ...unknown[]]) {\n    const obj = args[0] as Record<symbol, boolean>;\n    if (obj?.[LOCATOR_PROXY_BRAND] || obj?.[PAGE_PROXY_BRAND]) {\n      return createExtensionExpect(args[0]);\n    }\n    return Reflect.apply(target, thisArg, args);\n  },\n});\n"],"mappings":"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"}