@agimon-ai/browse-tool 0.2.7 → 0.2.9
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 +21 -21
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.mjs +2 -2
- package/dist/cli.mjs.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{playwright-test-CGgt4gB5.cjs → playwright-test-Gy2QgWt3.cjs} +2 -2
- package/dist/{playwright-test-CGgt4gB5.cjs.map → playwright-test-Gy2QgWt3.cjs.map} +1 -1
- package/dist/streamable-http-BF_kFDOr.mjs +13 -0
- package/dist/streamable-http-BF_kFDOr.mjs.map +1 -0
- package/dist/streamable-http-DEnR3pMh.cjs +13 -0
- package/dist/streamable-http-DEnR3pMh.cjs.map +1 -0
- package/dist/stubs/playwright-test.cjs +1 -1
- package/package.json +3 -2
- package/dist/streamable-http-BFbkLZAL.mjs +0 -14
- package/dist/streamable-http-BFbkLZAL.mjs.map +0 -1
- package/dist/streamable-http-CkhxOp5e.cjs +0 -15
- package/dist/streamable-http-CkhxOp5e.cjs.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require(`./streamable-http-
|
|
1
|
+
const e=require(`./streamable-http-DEnR3pMh.cjs`);require(`./playwright-test-Gy2QgWt3.cjs`);let t=require(`@modelcontextprotocol/sdk/server/index.js`),n=require(`@modelcontextprotocol/sdk/types.js`),r=require(`liquidjs`);function i(){let e=()=>void 0;return{debug:e,info:e,warn:e,error:e}}var a=class extends Error{code=`UNKNOWN_TOOL`;recovery=`Use ListTools to see available tools.`;availableTools;constructor(e,t,n){super(`Unknown tool: ${e}. Available tools: ${t.slice(0,5).join(`, `)}${t.length>5?`, ... (${t.length} total)`:``}. Use ListTools to see all available tools.`,n),this.name=`UnknownToolError`,this.availableTools=t}},o=class extends Error{code=`TOOL_EXECUTION_ERROR`;recovery;toolName;constructor(e,t,n){super(`Tool execution failed for '${e}': ${t}`,n),this.name=`ToolExecutionError`,this.toolName=e,this.recovery=n?.recovery??`Check tool inputs and try again.`}};function s(e,t){let n=e instanceof Error?e.message:`Unknown error occurred`,r={error:{code:e instanceof Error&&`code`in e?e.code:`TOOL_EXECUTION_ERROR`,message:n,toolName:t,recovery:e instanceof Error&&`recovery`in e?e.recovery:`Check tool inputs and try again.`}};return{content:[{type:`text`,text:JSON.stringify(r,null,2)}],isError:!0}}function c(r){let c=r?.container??e.r,l=r?.logger??i(),u=new t.Server({name:`browse-tool`,version:`0.1.0`},{capabilities:{tools:{}}}),d=c.getAll(e.C.Tool),f=new Map;for(let e of d){let t=e.getDefinition();f.set(t.name,e)}return l.info(`MCP server initialized`,{toolCount:d.length}),u.setRequestHandler(n.ListToolsRequestSchema,async()=>(l.debug(`ListTools request received`),{tools:d.map(e=>e.getDefinition())})),u.setRequestHandler(n.CallToolRequestSchema,async e=>{let{name:t,arguments:n}=e.params;l.debug(`Tool call received`,{toolName:t,timestamp:new Date().toISOString()});let r=f.get(t);if(!r){let e=Array.from(f.keys());throw l.warn(`Unknown tool requested`,{toolName:t,availableTools:e,timestamp:new Date().toISOString()}),new a(t,e)}try{return await r.execute(n)}catch(e){let n=e instanceof o?e:new o(t,e instanceof Error?e.message:String(e),{cause:e});return l.error(`Tool execution failed`,{toolName:t,error:n.message,code:n.code,timestamp:new Date().toISOString()}),s(n,t)}}),u}var l='Create a browse-tool custom tool folder for use with `browse-tool mcp-serve --custom-tools <dir>`.\n\nGoal: {{ toolGoal }}\nPreferred tool name: {{ preferredToolName }}\nPage context: {{ pageContext }}\n\nRequirements:\n- Produce a `tools.yaml` file with a top-level `tools` array.\n- Each tool entry must include `name`, `description`, `script`, `capabilities`, and `inputSchema`.\n- Each tool entry may optionally include `suggestionActions` as a short text string describing the recommended next step after the tool succeeds.\n- `inputSchema` must be JSON Schema with `type: object`.\n- `inputSchema` must define at least one execution target field: `pageId` and/or `browserId`, each as `type: string` when present.\n- The script file must be `.ts` and export a named `run` function with the shape `export const run = async ({ page, browser, input, logger }) => { ... }`.\n- The `run` export runs in Node.js, not inside the page.\n- Use the provided `page` object for browser interaction. It will be a Playwright page in playwright mode or an extension page proxy in extension mode.\n- Use the provided `browser` helper when the tool needs to list pages, open a new page, or work from `browserId` instead of an existing `pageId`.\n- `browser` is not a raw Playwright browser. It exposes `browserId`, `mode`, `listPages()`, `getPage(pageId)`, `getCurrentPage()`, and `newPage({ url?, setAsCurrent? })`.\n- Use the provided `logger` object for instrumentation. It exposes `trace`, `debug`, `info`, `warn`, `error`, and `fatal`, and logs automatically attach to the active custom-tool telemetry context.\n- The logger also exposes `getTraceContext()` so a tool can return the active `traceId` and `spanId` when needed for downstream log inspection.\n- If you need DOM access, call `page.evaluate(...)` from the `run` export instead of assuming browser globals are available at module scope.\n- Keep the script self-contained and avoid relative imports.\n- Return either a plain object, a string, or an MCP-style `CallToolResult`.\n- Prefer read-only behavior unless the goal explicitly requires mutation.\n\nOutput format:\n- Show the full `tools.yaml` content.\n- Show the full `{{ preferredToolName }}.ts` content.\n- If the tool needs additional inputs beyond `pageId` or `browserId`, define them in `inputSchema` and read them from `input`.\n- Filesystem access is allowed because the script runs in Node.js.\n\nExample `tools.yaml` shape:\n```yaml\ntools:\n - name: get_post\n description: Get the current post from the open feed page\n script: get_post.ts\n suggestionActions: Open the author profile and review whether the post is worth engaging with\n capabilities:\n readOnlyHint: true\n openWorldHint: false\n inputSchema:\n type: object\n properties:\n pageId:\n type: string\n browserId:\n type: string\n selector:\n type: string\n required:\n - pageId\n```\n\nExample script shape:\n```ts\nexport const run = async ({ page, browser, input, logger }) => {\n const traceContext = logger.getTraceContext();\n logger.info("reading post", { attributes: { selector: input.selector ?? "body", browserId: browser.browserId } });\n const selector = input.selector ?? "body";\n const text = await page.evaluate((currentSelector) => {\n return document.querySelector(currentSelector)?.textContent ?? "";\n }, selector);\n return {\n pageUrl: page.url(),\n traceContext,\n text,\n };\n};\n```\n';const u={name:`custom_script_authoring`,description:`Generate a browse-tool custom tool folder with tools.yaml and TypeScript scripts.`,arguments:[{name:`toolGoal`,description:`What the custom tool should do on the page`,required:!0},{name:`toolName`,description:`Preferred snake_case tool name`,required:!1},{name:`pageContext`,description:`Optional context about the target page or workflow`,required:!1}]},d=new r.Liquid;function f(e){let t=e.toolName?.trim()||`custom_page_tool`,n=e.pageContext?.trim()||`No extra page context provided.`;return[{role:`user`,content:{type:`text`,text:d.parseAndRenderSync(l,{toolGoal:e.toolGoal,preferredToolName:t,pageContext:n})}}]}exports.PLAYWRIGHT_TYPES=e.C,exports.StdioTransportHandler=e.n,exports.StreamableHttpTransportHandler=e.t,exports.ToolExecutionError=o,exports.UnknownToolError=a,exports.container=e.r,exports.createContainer=e.i,exports.createServer=c,exports.customScriptAuthoringPrompt=u,exports.generateCustomScriptAuthoringPrompt=f;
|
|
2
2
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{C as e,i as t,n,r,t as i}from"./streamable-http-
|
|
1
|
+
import{C as e,i as t,n,r,t as i}from"./streamable-http-BF_kFDOr.mjs";import"./playwright-test-BwI7HgW7.mjs";import{Server as a}from"@modelcontextprotocol/sdk/server/index.js";import{CallToolRequestSchema as o,ListToolsRequestSchema as s}from"@modelcontextprotocol/sdk/types.js";import{Liquid as c}from"liquidjs";function l(){let e=()=>void 0;return{debug:e,info:e,warn:e,error:e}}var u=class extends Error{code=`UNKNOWN_TOOL`;recovery=`Use ListTools to see available tools.`;availableTools;constructor(e,t,n){super(`Unknown tool: ${e}. Available tools: ${t.slice(0,5).join(`, `)}${t.length>5?`, ... (${t.length} total)`:``}. Use ListTools to see all available tools.`,n),this.name=`UnknownToolError`,this.availableTools=t}},d=class extends Error{code=`TOOL_EXECUTION_ERROR`;recovery;toolName;constructor(e,t,n){super(`Tool execution failed for '${e}': ${t}`,n),this.name=`ToolExecutionError`,this.toolName=e,this.recovery=n?.recovery??`Check tool inputs and try again.`}};function f(e,t){let n=e instanceof Error?e.message:`Unknown error occurred`,r={error:{code:e instanceof Error&&`code`in e?e.code:`TOOL_EXECUTION_ERROR`,message:n,toolName:t,recovery:e instanceof Error&&`recovery`in e?e.recovery:`Check tool inputs and try again.`}};return{content:[{type:`text`,text:JSON.stringify(r,null,2)}],isError:!0}}function p(t){let n=t?.container??r,i=t?.logger??l(),c=new a({name:`browse-tool`,version:`0.1.0`},{capabilities:{tools:{}}}),p=n.getAll(e.Tool),m=new Map;for(let e of p){let t=e.getDefinition();m.set(t.name,e)}return i.info(`MCP server initialized`,{toolCount:p.length}),c.setRequestHandler(s,async()=>(i.debug(`ListTools request received`),{tools:p.map(e=>e.getDefinition())})),c.setRequestHandler(o,async e=>{let{name:t,arguments:n}=e.params;i.debug(`Tool call received`,{toolName:t,timestamp:new Date().toISOString()});let r=m.get(t);if(!r){let e=Array.from(m.keys());throw i.warn(`Unknown tool requested`,{toolName:t,availableTools:e,timestamp:new Date().toISOString()}),new u(t,e)}try{return await r.execute(n)}catch(e){let n=e instanceof d?e:new d(t,e instanceof Error?e.message:String(e),{cause:e});return i.error(`Tool execution failed`,{toolName:t,error:n.message,code:n.code,timestamp:new Date().toISOString()}),f(n,t)}}),c}var m='Create a browse-tool custom tool folder for use with `browse-tool mcp-serve --custom-tools <dir>`.\n\nGoal: {{ toolGoal }}\nPreferred tool name: {{ preferredToolName }}\nPage context: {{ pageContext }}\n\nRequirements:\n- Produce a `tools.yaml` file with a top-level `tools` array.\n- Each tool entry must include `name`, `description`, `script`, `capabilities`, and `inputSchema`.\n- Each tool entry may optionally include `suggestionActions` as a short text string describing the recommended next step after the tool succeeds.\n- `inputSchema` must be JSON Schema with `type: object`.\n- `inputSchema` must define at least one execution target field: `pageId` and/or `browserId`, each as `type: string` when present.\n- The script file must be `.ts` and export a named `run` function with the shape `export const run = async ({ page, browser, input, logger }) => { ... }`.\n- The `run` export runs in Node.js, not inside the page.\n- Use the provided `page` object for browser interaction. It will be a Playwright page in playwright mode or an extension page proxy in extension mode.\n- Use the provided `browser` helper when the tool needs to list pages, open a new page, or work from `browserId` instead of an existing `pageId`.\n- `browser` is not a raw Playwright browser. It exposes `browserId`, `mode`, `listPages()`, `getPage(pageId)`, `getCurrentPage()`, and `newPage({ url?, setAsCurrent? })`.\n- Use the provided `logger` object for instrumentation. It exposes `trace`, `debug`, `info`, `warn`, `error`, and `fatal`, and logs automatically attach to the active custom-tool telemetry context.\n- The logger also exposes `getTraceContext()` so a tool can return the active `traceId` and `spanId` when needed for downstream log inspection.\n- If you need DOM access, call `page.evaluate(...)` from the `run` export instead of assuming browser globals are available at module scope.\n- Keep the script self-contained and avoid relative imports.\n- Return either a plain object, a string, or an MCP-style `CallToolResult`.\n- Prefer read-only behavior unless the goal explicitly requires mutation.\n\nOutput format:\n- Show the full `tools.yaml` content.\n- Show the full `{{ preferredToolName }}.ts` content.\n- If the tool needs additional inputs beyond `pageId` or `browserId`, define them in `inputSchema` and read them from `input`.\n- Filesystem access is allowed because the script runs in Node.js.\n\nExample `tools.yaml` shape:\n```yaml\ntools:\n - name: get_post\n description: Get the current post from the open feed page\n script: get_post.ts\n suggestionActions: Open the author profile and review whether the post is worth engaging with\n capabilities:\n readOnlyHint: true\n openWorldHint: false\n inputSchema:\n type: object\n properties:\n pageId:\n type: string\n browserId:\n type: string\n selector:\n type: string\n required:\n - pageId\n```\n\nExample script shape:\n```ts\nexport const run = async ({ page, browser, input, logger }) => {\n const traceContext = logger.getTraceContext();\n logger.info("reading post", { attributes: { selector: input.selector ?? "body", browserId: browser.browserId } });\n const selector = input.selector ?? "body";\n const text = await page.evaluate((currentSelector) => {\n return document.querySelector(currentSelector)?.textContent ?? "";\n }, selector);\n return {\n pageUrl: page.url(),\n traceContext,\n text,\n };\n};\n```\n';const h={name:`custom_script_authoring`,description:`Generate a browse-tool custom tool folder with tools.yaml and TypeScript scripts.`,arguments:[{name:`toolGoal`,description:`What the custom tool should do on the page`,required:!0},{name:`toolName`,description:`Preferred snake_case tool name`,required:!1},{name:`pageContext`,description:`Optional context about the target page or workflow`,required:!1}]},g=new c;function _(e){let t=e.toolName?.trim()||`custom_page_tool`,n=e.pageContext?.trim()||`No extra page context provided.`;return[{role:`user`,content:{type:`text`,text:g.parseAndRenderSync(m,{toolGoal:e.toolGoal,preferredToolName:t,pageContext:n})}}]}export{e as PLAYWRIGHT_TYPES,n as StdioTransportHandler,i as StreamableHttpTransportHandler,d as ToolExecutionError,u as UnknownToolError,r as container,t as createContainer,p as createServer,h as customScriptAuthoringPrompt,_ as generateCustomScriptAuthoringPrompt};
|
|
2
2
|
//# sourceMappingURL=index.mjs.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const e=require(`./streamable-http-
|
|
1
|
+
const e=require(`./streamable-http-DEnR3pMh.cjs`);let t=require(`node:module`);const n=Symbol.for(`__locatorProxyBrand__`);var r=class e{[n]=!0;constructor(e,t){this.page=e,this.steps=t}getByRole(t,n){return new e(this.page,[...this.steps,{type:`role`,role:t,options:n}])}getByText(t,n){return new e(this.page,[...this.steps,{type:`text`,text:t,options:n}])}getByLabel(t,n){return new e(this.page,[...this.steps,{type:`label`,text:t,options:n}])}getByPlaceholder(t,n){return new e(this.page,[...this.steps,{type:`placeholder`,text:t,options:n}])}getByTestId(t){return new e(this.page,[...this.steps,{type:`testId`,testId:t}])}locator(t){return new e(this.page,[...this.steps,{type:`css`,selector:t}])}filter(t){return new e(this.page,[...this.steps,{type:`filter`,options:t}])}first(){return new e(this.page,[...this.steps,{type:`first`}])}last(){return this.nth(-1)}nth(t){return new e(this.page,[...this.steps,{type:`nth`,index:t}])}async click(e){let t=await this.resolveAndMark(e?.timeout);await this.page.click(t)}async fill(e,t){let n=await this.resolveAndMark(t?.timeout);await this.page.fill(n,e)}async type(e,t){let n=await this.resolveAndMark(t?.timeout);await this.page.type(n,e,{delay:t?.delay})}async press(e){let t=await this.resolveAndMark();await this.page.click(t),await this.page.press(e)}async hover(e){let t=await this.resolveAndMark(e?.timeout);await this.page.hover(t)}async selectOption(e){let t=await this.resolveAndMark();await this.page.selectOption(t,e)}async check(){let e=await this.resolveAndMark();await this.page.click(e)}async uncheck(){let e=await this.resolveAndMark();await this.page.click(e)}async isVisible(){let e=this.buildResolveScript(`isVisible`);return await this.page.evaluate(e)??!1}async isHidden(){return!await this.isVisible()}async isEnabled(){return!await this.evaluateProperty(`disabled`)}async isDisabled(){return!!await this.evaluateProperty(`disabled`)}async isChecked(){return!!await this.evaluateProperty(`checked`)}async evaluateProperty(e){let t=this.buildResolveScript(`getProperty`,e);return this.page.evaluate(t)}async textContent(){let e=this.buildResolveScript(`textContent`);return this.page.evaluate(e)}async innerText(){let e=this.buildResolveScript(`innerText`);return await this.page.evaluate(e)??``}async inputValue(){let e=this.buildResolveScript(`inputValue`);return await this.page.evaluate(e)??``}async count(){let e=this.buildResolveScript(`count`);return await this.page.evaluate(e)??0}async waitFor(e){let t=e?.timeout??5e3,n=e?.state??`visible`,r=Date.now();for(;Date.now()-r<t;){if(n===`visible`&&await this.isVisible()||n===`hidden`&&!await this.isVisible()||n===`attached`&&await this.count()>0||n===`detached`&&await this.count()===0)return;await new Promise(e=>setTimeout(e,100))}throw Error(`Locator waitFor("${n}") timed out after ${t}ms`)}async resolveAndMark(e){let t=e??5e3,n=Date.now(),r=`pw-${Date.now()}-${Math.random().toString(36).slice(2,8)}`;for(;Date.now()-n<t;){let e=this.buildResolveScript(`mark`,r);if(await this.page.evaluate(e))return`[data-pw-proxy="${r}"]`;await new Promise(e=>setTimeout(e,100))}throw Error(`Locator could not resolve element within ${t}ms. Steps: ${this.describeSteps()}`)}describeSteps(){return this.steps.map(e=>{switch(e.type){case`role`:return`getByRole("${e.role}"${e.options?.name?`, { name: "${String(e.options.name)}" }`:``})`;case`text`:return`getByText("${String(e.text)}")`;case`label`:return`getByLabel("${String(e.text)}")`;case`placeholder`:return`getByPlaceholder("${String(e.text)}")`;case`testId`:return`getByTestId("${e.testId}")`;case`css`:return`locator("${e.selector}")`;case`filter`:return`filter(...)`;case`first`:return`first()`;case`nth`:return`nth(${e.index})`}}).join(`.`)}serializeSteps(){return this.steps.map(e=>{if(e.type===`text`||e.type===`label`||e.type===`placeholder`)return{...e,text:e.text instanceof RegExp?{__regex__:e.text.source,flags:e.text.flags}:e.text};if(e.type===`role`&&e.options?.name instanceof RegExp)return{...e,options:{...e.options,name:{__regex__:e.options.name.source,flags:e.options.name.flags}}};if(e.type===`filter`){let t={};return e.options.hasText&&(t.hasText=e.options.hasText instanceof RegExp?{__regex__:e.options.hasText.source,flags:e.options.hasText.flags}:e.options.hasText),e.options.has&&(t.hasSteps=e.options.has.serializeSteps()),{type:`filter`,options:t}}return e})}buildResolveScript(e,t){return`(function() {
|
|
2
2
|
var steps = ${JSON.stringify(this.serializeSteps())};
|
|
3
3
|
var action = "${e}";
|
|
4
4
|
var extra = ${t===void 0?`null`:JSON.stringify(t)};
|
|
@@ -250,4 +250,4 @@ const e=require(`./streamable-http-CkhxOp5e.cjs`);let t=require(`node:module`);c
|
|
|
250
250
|
|
|
251
251
|
return null;
|
|
252
252
|
})()`}};const i=Symbol.for(`__pageProxyBrand__`),a=100,o=5e3;async function s(e,t,n=5e3){let r=Date.now();for(;Date.now()-r<n;){if(await e())return;await new Promise(e=>setTimeout(e,100))}throw Error(t)}function c(e,t){return e==null?!1:t instanceof RegExp?t.test(e):e===t}function l(e,t){return e==null?!1:t instanceof RegExp?t.test(e):e.includes(t)}function u(e,t){let n=e=>t?!e:e;return{async toBeVisible(r){await s(async()=>n(await e.isVisible()),t?`Expected element to not be visible`:`Expected element to be visible`,r?.timeout)},async toBeHidden(r){await s(async()=>n(!await e.isVisible()),t?`Expected element to not be hidden`:`Expected element to be hidden`,r?.timeout)},async toBeDisabled(r){await s(async()=>n(!!await e.evaluateProperty(`disabled`)),t?`Expected element to not be disabled`:`Expected element to be disabled`,r?.timeout)},async toBeEnabled(r){await s(async()=>n(!await e.evaluateProperty(`disabled`)),t?`Expected element to not be enabled`:`Expected element to be enabled`,r?.timeout)},async toBeChecked(r){await s(async()=>n(!!await e.evaluateProperty(`checked`)),t?`Expected element to not be checked`:`Expected element to be checked`,r?.timeout)},async toHaveValue(r,i){await s(async()=>n(c(await e.evaluateProperty(`value`),r)),t?`Expected element to not have value "${String(r)}"`:`Expected element to have value "${String(r)}"`,i?.timeout)},async toHaveText(r,i){await s(async()=>n(c((await e.textContent())?.trim()??null,r)),t?`Expected element to not have text "${String(r)}"`:`Expected element to have text "${String(r)}"`,i?.timeout)},async toContainText(r,i){await s(async()=>n(l(await e.textContent(),r)),t?`Expected element to not contain text "${String(r)}"`:`Expected element to contain text "${String(r)}"`,i?.timeout)},async toHaveCount(r,i){await s(async()=>n(await e.count()===r),t?`Expected element count to not be ${r}`:`Expected element count to be ${r}`,i?.timeout)},get not(){return u(e,!t)}}}function d(e,t){let n=e=>t?!e:e;return{async toHaveURL(r,i){await s(async()=>n(c(await e.currentUrlAsync(),r)),t?`Expected page to not have URL "${String(r)}"`:`Expected page to have URL "${String(r)}"`,i?.timeout)},async toHaveTitle(r,i){await s(async()=>n(c(await e.title(),r)),t?`Expected page to not have title "${String(r)}"`:`Expected page to have title "${String(r)}"`,i?.timeout)},get not(){return d(e,!t)}}}function f(e){let t=e;if(t[n])return u(e,!1);if(t[i])return d(e,!1);throw Error(`createExtensionExpect called with unsupported target`)}function p(){try{return(0,t.createRequire)(require(`url`).pathToFileURL(__filename).href).resolve(`playwright/test`),require(`url`).pathToFileURL(__filename).href}catch{return typeof __PLAYWRIGHT_MCP_STUB_PATH__==`string`?`file://${__PLAYWRIGHT_MCP_STUB_PATH__}`:require(`url`).pathToFileURL(__filename).href}}const m=process,h=m.__pw_initiator__;m.__pw_initiator__=void 0;const{expect:g}=(0,t.createRequire)(p())(`playwright/test`);m.__pw_initiator__=h;var _=class{currentSpecPath=``;rootDescribe;currentDescribe;constructor(){this.rootDescribe=this.createDescribeBlock(``,null),this.currentDescribe=this.rootDescribe}createDescribeBlock(e,t){return{title:e,tests:[],beforeEachHooks:[],afterEachHooks:[],children:[],parent:t}}setSpecPath(e){this.currentSpecPath=e}reset(){this.currentSpecPath=``,this.rootDescribe=this.createDescribeBlock(``,null),this.currentDescribe=this.rootDescribe}addTest(e,t,n=!1,r=!1){let i={title:e,fn:t,fullTitle:this.getFullTitle(e),only:n,skip:r};return this.currentDescribe.tests.push(i),i}enterDescribe(e){let t=this.createDescribeBlock(e,this.currentDescribe);this.currentDescribe.children.push(t),this.currentDescribe=t}exitDescribe(){this.currentDescribe.parent&&(this.currentDescribe=this.currentDescribe.parent)}addBeforeEach(e){this.currentDescribe.beforeEachHooks.push(e)}addAfterEach(e){this.currentDescribe.afterEachHooks.push(e)}getFullTitle(e){let t=[],n=this.currentDescribe;for(;n;)n.title&&t.unshift(n.title),n=n.parent;return t.push(e),t.join(` > `)}flattenTests(e,t,n){let r=[],i=[...t,...e.beforeEachHooks],a=[...e.afterEachHooks,...n];for(let t of e.tests)r.push({title:t.title,fullTitle:t.fullTitle,fn:async e=>{for(let t of i)await t(e);await t.fn(e);for(let t of a)await t(e)},only:t.only,skip:t.skip});for(let t of e.children)r.push(...this.flattenTests(t,i,a));return r}retrieveAndReset(){let e=this.flattenTests(this.rootDescribe,[],[]),t={specPath:this.currentSpecPath,root:this.rootDescribe,allTests:e,testCount:e.length};return this.reset(),t}peek(){let e=this.flattenTests(this.rootDescribe,[],[]);return{specPath:this.currentSpecPath,root:this.rootDescribe,allTests:e,testCount:e.length}}};const v=`__playwrightMcpTestCollector__`,y=globalThis;y[v]||(y[v]=new _);const b=y[v];function x(e){let t=new Map;for(let[n,r]of Object.entries(e))typeof r==`function`?t.set(n,r):Array.isArray(r)&&r.length===2&&typeof r[1]==`function`&&t.set(n,r[1]);return t}function S(e){if(e.size===0)return e=>e;let t=[...e.entries()];return e=>async n=>{let r={...n};async function i(n){if(n>=t.length){await e(r);return}let[a,o]=t[n];await o(r,async e=>{r[a]=e,await i(n+1)})}await i(0)}}function C(e){let t=S(e);function n(e,n){b.addTest(e,t(n))}return n.only=function(e,n){b.addTest(e,t(n),!0,!1)},n.skip=function(e,n){b.addTest(e,t(n),!1,!0)},n.describe=function(e,t){b.enterDescribe(e),t(),b.exitDescribe()},n.beforeEach=function(e){b.addBeforeEach(t(e))},n.afterEach=function(e){b.addAfterEach(t(e))},n.extend=function(t){return C(new Map([...e,...x(t)]))},n}function w(e,t){b.addTest(e,t)}function T(e,t){b.addTest(e,t,!0,!1)}function E(e,t){b.addTest(e,t,!1,!0)}function D(e,t){b.enterDescribe(e),t(),b.exitDescribe()}function O(e){b.addBeforeEach(e)}function k(e){b.addAfterEach(e)}w.describe=D,w.beforeEach=O,w.afterEach=k,w.only=T,w.skip=E,w.extend=function(e){return C(x(e))};function A(e){b.reset(),b.setSpecPath(e)}function j(){return b.retrieveAndReset()}function M(){return b.peek()}function N(){b.reset()}const P=(e=>{let t=e;return t?.[n]||t?.[i]?f(e):g(e)});Object.defineProperty(exports,`a`,{enumerable:!0,get:function(){return N}}),Object.defineProperty(exports,`c`,{enumerable:!0,get:function(){return r}}),Object.defineProperty(exports,`i`,{enumerable:!0,get:function(){return M}}),Object.defineProperty(exports,`n`,{enumerable:!0,get:function(){return j}}),Object.defineProperty(exports,`o`,{enumerable:!0,get:function(){return w}}),Object.defineProperty(exports,`r`,{enumerable:!0,get:function(){return A}}),Object.defineProperty(exports,`s`,{enumerable:!0,get:function(){return i}}),Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return P}});
|
|
253
|
-
//# sourceMappingURL=playwright-test-
|
|
253
|
+
//# sourceMappingURL=playwright-test-Gy2QgWt3.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"playwright-test-CGgt4gB5.cjs","names":["page: IExtensionPageProxy","steps: LocatorStep[]","filterOpts: Record<string, unknown>","testBlock: TestBlock","parts: string[]","current: DescribeBlock | null","tests: TestBlock[]","describe","test","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 { expect: playwrightExpect } = _require('playwright/test') as {\n expect: typeof import('playwright/test')['expect'];\n};\nproc.__pw_initiator__ = savedPwInitiator;\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 /** Extended fixtures added via test.extend() */\n [key: string]: unknown;\n}\n\n/**\n * A single test block collected from the spec file.\n */\nexport interface TestBlock {\n /** Test title */\n title: string;\n /** Test function to execute */\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 * 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 /** beforeEach hooks */\n beforeEachHooks: Array<(fixtures: TestFixtures) => Promise<void>>;\n /** afterEach hooks */\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}\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 };\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 * 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 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 * 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 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\n for (const test of describe.tests) {\n const wrappedFn = async (fixtures: TestFixtures): Promise<void> => {\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 tests.push({\n title: test.title,\n fullTitle: test.fullTitle,\n fn: wrappedFn,\n only: test.only,\n skip: test.skip,\n });\n }\n\n for (const child of describe.children) {\n tests.push(...this.flattenTests(child, currentBeforeEach, currentAfterEach));\n }\n\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 * 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 const wrapFn = createFixtureWrapper(fixtureDefs);\n\n function extendedTest(title: string, fn: TestFunction): void {\n collector.addTest(title, wrapFn(fn));\n }\n\n extendedTest.only = function extendedOnly(title: string, fn: TestFunction): void {\n collector.addTest(title, wrapFn(fn), true, false);\n };\n\n extendedTest.skip = function extendedSkip(title: string, fn: TestFunction): void {\n collector.addTest(title, wrapFn(fn), false, true);\n };\n\n extendedTest.describe = function extendedDescribe(title: string, fn: () => void): void {\n collector.enterDescribe(title);\n fn();\n collector.exitDescribe();\n };\n\n extendedTest.beforeEach = function extendedBeforeEach(fn: TestFunction): void {\n collector.addBeforeEach(wrapFn(fn));\n };\n\n extendedTest.afterEach = function extendedAfterEach(fn: TestFunction): void {\n collector.addAfterEach(wrapFn(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\nfunction testSkip(title: string, fn: TestFunction): void {\n collector.addTest(title, fn, false, true);\n}\n\nfunction describe(title: string, fn: () => void): void {\n collector.enterDescribe(title);\n fn();\n collector.exitDescribe();\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.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
|
+
{"version":3,"file":"playwright-test-Gy2QgWt3.cjs","names":["page: IExtensionPageProxy","steps: LocatorStep[]","filterOpts: Record<string, unknown>","testBlock: TestBlock","parts: string[]","current: DescribeBlock | null","tests: TestBlock[]","describe","test","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 { expect: playwrightExpect } = _require('playwright/test') as {\n expect: typeof import('playwright/test')['expect'];\n};\nproc.__pw_initiator__ = savedPwInitiator;\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 /** Extended fixtures added via test.extend() */\n [key: string]: unknown;\n}\n\n/**\n * A single test block collected from the spec file.\n */\nexport interface TestBlock {\n /** Test title */\n title: string;\n /** Test function to execute */\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 * 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 /** beforeEach hooks */\n beforeEachHooks: Array<(fixtures: TestFixtures) => Promise<void>>;\n /** afterEach hooks */\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}\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 };\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 * 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 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 * 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 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\n for (const test of describe.tests) {\n const wrappedFn = async (fixtures: TestFixtures): Promise<void> => {\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 tests.push({\n title: test.title,\n fullTitle: test.fullTitle,\n fn: wrappedFn,\n only: test.only,\n skip: test.skip,\n });\n }\n\n for (const child of describe.children) {\n tests.push(...this.flattenTests(child, currentBeforeEach, currentAfterEach));\n }\n\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 * 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 const wrapFn = createFixtureWrapper(fixtureDefs);\n\n function extendedTest(title: string, fn: TestFunction): void {\n collector.addTest(title, wrapFn(fn));\n }\n\n extendedTest.only = function extendedOnly(title: string, fn: TestFunction): void {\n collector.addTest(title, wrapFn(fn), true, false);\n };\n\n extendedTest.skip = function extendedSkip(title: string, fn: TestFunction): void {\n collector.addTest(title, wrapFn(fn), false, true);\n };\n\n extendedTest.describe = function extendedDescribe(title: string, fn: () => void): void {\n collector.enterDescribe(title);\n fn();\n collector.exitDescribe();\n };\n\n extendedTest.beforeEach = function extendedBeforeEach(fn: TestFunction): void {\n collector.addBeforeEach(wrapFn(fn));\n };\n\n extendedTest.afterEach = function extendedAfterEach(fn: TestFunction): void {\n collector.addAfterEach(wrapFn(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\nfunction testSkip(title: string, fn: TestFunction): void {\n collector.addTest(title, fn, false, true);\n}\n\nfunction describe(title: string, fn: () => void): void {\n collector.enterDescribe(title);\n fn();\n collector.exitDescribe();\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.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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"}
|