rename accessibilitySnapshot to snapshot for shorter agent usage
The function is now exposed as `snapshot()` in the executor sandbox scope. `accessibilitySnapshot` remains as a backward-compatible alias pointing to the same function, so existing agents and saved commands continue to work. Updated all code examples across skill.md, README, PLAYWRITER_AGENTS, docs, tests, and the extension welcome page to use the shorter name.
This commit is contained in:
@@ -57,7 +57,7 @@ to test CLI changes without publishing:
|
||||
Get-NetTCPConnection -LocalPort 19988 | ForEach-Object { Stop-Process -Id $_.OwningProcess -Force }
|
||||
|
||||
tsx playwriter/src/cli.ts -s 1 -e "await page.goto('https://example.com')"
|
||||
tsx playwriter/src/cli.ts -s 1 -e "console.log(await accessibilitySnapshot({ page }))"
|
||||
tsx playwriter/src/cli.ts -s 1 -e "console.log(await snapshot({ page }))"
|
||||
tsx playwriter/src/cli.ts session new
|
||||
tsx playwriter/src/cli.ts -s 1 -e "await page.click('button')"
|
||||
```
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
```bash
|
||||
playwriter session new # creates stateful sandbox, outputs session id (e.g. 1)
|
||||
playwriter -s 1 -e "await page.goto('https://example.com')"
|
||||
playwriter -s 1 -e "console.log(await accessibilitySnapshot({ page }))"
|
||||
playwriter -s 1 -e "console.log(await snapshot({ page }))"
|
||||
playwriter -s 1 -e "await page.locator('aria-ref=e5').click()"
|
||||
```
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
---
|
||||
title: Framer Plugin Iframe Snapshot Guide
|
||||
description: Step-by-step instructions to open the Framer MCP plugin iframe and run accessibilitySnapshot on it.
|
||||
description: Step-by-step instructions to open the Framer MCP plugin iframe and run snapshot on it.
|
||||
prompt: |
|
||||
Create a concise step-by-step guide to open the Framer plugin iframe and
|
||||
verify accessibilitySnapshot on that iframe using playwriter CLI. Include the
|
||||
verify snapshot on that iframe using playwriter CLI. Include the
|
||||
exact Framer project URL and the plugins.framercdn iframe URL. Also document
|
||||
the Command+K workflow to open the MCP plugin: press Command+K, search for MCP
|
||||
in the command palette, press Enter, then wait ~1 second for the iframe to
|
||||
@@ -33,7 +33,7 @@ playwriter -s 1 -e "const target = 'https://framer.com/projects/unframer-source-
|
||||
|
||||
- Verify the palette is open (look for the command dialog and MCP entry in the snapshot output):
|
||||
```bash
|
||||
playwriter -s 1 -e "console.log(await accessibilitySnapshot({ page, search: /dialog|Search…|MCP/ }));"
|
||||
playwriter -s 1 -e "console.log(await snapshot({ page, search: /dialog|Search…|MCP/ }));"
|
||||
```
|
||||
|
||||
- Search for **MCP**, press Enter, then wait about 1 second for the plugin iframe to appear.
|
||||
@@ -55,17 +55,17 @@ playwriter -s 1 -e "const iframe = page.locator(\"iframe[src*='plugins.framercdn
|
||||
|
||||
- Run the accessibility snapshot on that iframe using `contentFrame()` (FrameLocator is auto-resolved to Frame):
|
||||
```bash
|
||||
playwriter -s 1 -e "const frame = await page.locator(\"iframe[src*='plugins.framercdn.com']\").contentFrame(); console.log(await accessibilitySnapshot({ page, frame }));"
|
||||
playwriter -s 1 -e "const frame = await page.locator(\"iframe[src*='plugins.framercdn.com']\").contentFrame(); console.log(await snapshot({ page, frame }));"
|
||||
```
|
||||
|
||||
- Alternative: use `page.frames()` to get the Frame directly:
|
||||
```bash
|
||||
playwriter -s 1 -e "const frame = page.frames().find(f => f.url().includes('plugins.framercdn.com')); console.log(await accessibilitySnapshot({ page, frame }));"
|
||||
playwriter -s 1 -e "const frame = page.frames().find(f => f.url().includes('plugins.framercdn.com')); console.log(await snapshot({ page, frame }));"
|
||||
```
|
||||
|
||||
- Validate the snapshot contains MCP UI text (confirms the panel is actually loaded):
|
||||
```bash
|
||||
playwriter -s 1 -e "const frame = await page.locator(\"iframe[src*='plugins.framercdn.com']\").contentFrame(); console.log(await accessibilitySnapshot({ page, frame, search: /Control Framer with MCP|Login With Google/ }));"
|
||||
playwriter -s 1 -e "const frame = await page.locator(\"iframe[src*='plugins.framercdn.com']\").contentFrame(); console.log(await snapshot({ page, frame, search: /Control Framer with MCP|Login With Google/ }));"
|
||||
```
|
||||
|
||||
## Expected iframe URL
|
||||
|
||||
@@ -99,7 +99,7 @@ The **CLI with the skill** is the recommended approach. The skill file (`playwri
|
||||
```bash
|
||||
playwriter session new # outputs: 1
|
||||
playwriter -s 1 -e "await page.goto('https://example.com')"
|
||||
playwriter -s 1 -e "console.log(await accessibilitySnapshot({ page }))"
|
||||
playwriter -s 1 -e "console.log(await snapshot({ page }))"
|
||||
```
|
||||
|
||||
Alternatively, pass host and token as flags instead of env vars:
|
||||
|
||||
@@ -325,7 +325,7 @@ playwriter session new
|
||||
# navigate to a URL
|
||||
playwriter -s 1 -e "await page.goto('https://example.com')"
|
||||
# get the accessibility tree of the page
|
||||
playwriter -s 1 -e "console.log(await accessibilitySnapshot({ page }))"
|
||||
playwriter -s 1 -e "console.log(await snapshot({ page }))"
|
||||
# click an element by its accessibility reference
|
||||
playwriter -s 1 -e "await page.locator('aria-ref=e5').click()"</code></pre>
|
||||
</section>
|
||||
|
||||
@@ -548,7 +548,7 @@ export class PlaywrightExecutor {
|
||||
},
|
||||
}
|
||||
|
||||
const accessibilitySnapshot = async (options: {
|
||||
const snapshot = async (options: {
|
||||
page?: Page
|
||||
/** Optional frame to scope the snapshot (e.g. from iframe.contentFrame() or page.frames()) */
|
||||
frame?: Frame | FrameLocator
|
||||
@@ -564,7 +564,7 @@ export class PlaywrightExecutor {
|
||||
const { page: targetPage, frame, locator, search, showDiffSinceLastCall = true, interactiveOnly = false } = options
|
||||
const resolvedPage = targetPage || page
|
||||
if (!resolvedPage) {
|
||||
throw new Error('accessibilitySnapshot requires a page')
|
||||
throw new Error('snapshot requires a page')
|
||||
}
|
||||
|
||||
// Use new in-page implementation via getAriaSnapshot
|
||||
@@ -800,7 +800,8 @@ export class PlaywrightExecutor {
|
||||
context,
|
||||
state: this.userState,
|
||||
console: customConsole,
|
||||
accessibilitySnapshot,
|
||||
snapshot,
|
||||
accessibilitySnapshot: snapshot, // backward compat alias
|
||||
refToLocator,
|
||||
getCleanHTML,
|
||||
getPageMarkdown,
|
||||
|
||||
@@ -6,6 +6,7 @@ describe('shouldAutoReturn', () => {
|
||||
expect(shouldAutoReturn('1 + 2')).toBe(true)
|
||||
expect(shouldAutoReturn('page.title()')).toBe(true)
|
||||
expect(shouldAutoReturn('await page.title()')).toBe(true)
|
||||
expect(shouldAutoReturn('snapshot({ page })')).toBe(true)
|
||||
expect(shouldAutoReturn('accessibilitySnapshot({ page })')).toBe(true)
|
||||
expect(shouldAutoReturn('context.pages().map(p => p.url())')).toBe(true)
|
||||
})
|
||||
|
||||
@@ -172,7 +172,7 @@ describe('Relay Core Tests', () => {
|
||||
})
|
||||
}, 30000)
|
||||
|
||||
const accessibilitySnapshotTestCases = [
|
||||
const snapshotTestCases = [
|
||||
{
|
||||
name: 'hacker-news',
|
||||
url: 'https://news.ycombinator.com/item?id=1',
|
||||
@@ -185,7 +185,7 @@ describe('Relay Core Tests', () => {
|
||||
},
|
||||
]
|
||||
|
||||
for (const testCase of accessibilitySnapshotTestCases) {
|
||||
for (const testCase of snapshotTestCases) {
|
||||
it(`should get accessibility snapshot of ${testCase.name}`, async () => {
|
||||
await client.callTool({
|
||||
name: 'execute',
|
||||
@@ -205,8 +205,8 @@ describe('Relay Core Tests', () => {
|
||||
arguments: {
|
||||
code: js`
|
||||
await state.page.goto('${testCase.url}', { waitUntil: 'domcontentloaded' });
|
||||
const snapshot = await accessibilitySnapshot({ page: state.page, showDiffSinceLastCall: false, interactiveOnly: true });
|
||||
return snapshot;
|
||||
const snap = await snapshot({ page: state.page, showDiffSinceLastCall: false, interactiveOnly: true });
|
||||
return snap;
|
||||
`,
|
||||
},
|
||||
})
|
||||
@@ -228,8 +228,8 @@ describe('Relay Core Tests', () => {
|
||||
name: 'execute',
|
||||
arguments: {
|
||||
code: js`
|
||||
const snapshot = await accessibilitySnapshot({ page: state.page, showDiffSinceLastCall: false, interactiveOnly: false });
|
||||
return snapshot;
|
||||
const snap = await snapshot({ page: state.page, showDiffSinceLastCall: false, interactiveOnly: false });
|
||||
return snap;
|
||||
`,
|
||||
},
|
||||
})
|
||||
|
||||
+23
-21
@@ -69,11 +69,11 @@ playwriter -s 1 -e "await page.title()"
|
||||
playwriter -s 1 -e "await page.screenshot({ path: 'screenshot.png', scale: 'css' })"
|
||||
|
||||
# Get accessibility snapshot
|
||||
playwriter -s 1 -e "await accessibilitySnapshot({ page })"
|
||||
playwriter -s 1 -e "await snapshot({ page })"
|
||||
|
||||
# Get accessibility snapshot for a specific iframe
|
||||
const frame = await page.locator('iframe').contentFrame()
|
||||
await accessibilitySnapshot({ frame })
|
||||
await snapshot({ frame })
|
||||
```
|
||||
|
||||
**Multiline code:**
|
||||
@@ -214,19 +214,19 @@ Each step is a separate execute call. Notice how every action is followed by a s
|
||||
// 1. Open page and observe
|
||||
state.myPage = context.pages().find(p => p.url() === 'about:blank') ?? await context.newPage();
|
||||
await state.myPage.goto('https://framer.com/projects/my-project', { waitUntil: 'domcontentloaded' });
|
||||
await accessibilitySnapshot({ page: state.myPage }).then(console.log)
|
||||
await snapshot({ page: state.myPage }).then(console.log)
|
||||
```
|
||||
|
||||
```js
|
||||
// 2. Act: open command palette → observe result
|
||||
await state.myPage.keyboard.press('Meta+k');
|
||||
await accessibilitySnapshot({ page: state.myPage, search: /dialog|Search/ }).then(console.log)
|
||||
await snapshot({ page: state.myPage, search: /dialog|Search/ }).then(console.log)
|
||||
```
|
||||
|
||||
```js
|
||||
// 3. Act: type search query → observe result
|
||||
await state.myPage.keyboard.type('MCP');
|
||||
await accessibilitySnapshot({ page: state.myPage, search: /MCP/ }).then(console.log)
|
||||
await snapshot({ page: state.myPage, search: /MCP/ }).then(console.log)
|
||||
```
|
||||
|
||||
```js
|
||||
@@ -234,7 +234,7 @@ await accessibilitySnapshot({ page: state.myPage, search: /MCP/ }).then(console.
|
||||
await state.myPage.keyboard.press('Enter');
|
||||
await state.myPage.waitForTimeout(1000);
|
||||
const frame = state.myPage.frames().find(f => f.url().includes('plugins.framercdn.com'));
|
||||
await accessibilitySnapshot({ page: state.myPage, frame: frame || undefined }).then(console.log)
|
||||
await snapshot({ page: state.myPage, frame: frame || undefined }).then(console.log)
|
||||
```
|
||||
|
||||
**Other ways to observe action results:**
|
||||
@@ -261,7 +261,7 @@ Snapshots are the primary feedback mechanism, but some actions have side effects
|
||||
Always check page state after important actions (form submissions, uploads, typing). Your mental model can diverge from actual browser state:
|
||||
```js
|
||||
await page.keyboard.type('my text');
|
||||
await accessibilitySnapshot({ page, search: /my text/ })
|
||||
await snapshot({ page, search: /my text/ })
|
||||
// If verifying visual layout specifically, use screenshotWithAccessibilityLabels instead
|
||||
```
|
||||
|
||||
@@ -283,7 +283,7 @@ Locators (especially ones with `>> nth=`) can change when the page updates. Alwa
|
||||
await page.locator('[id="old-id"]').click(); // element may have changed
|
||||
|
||||
// GOOD: get fresh snapshot, then immediately use locators from it
|
||||
await accessibilitySnapshot({ page, showDiffSinceLastCall: true })
|
||||
await snapshot({ page, showDiffSinceLastCall: true })
|
||||
// Now use the NEW locators from this output
|
||||
```
|
||||
|
||||
@@ -330,7 +330,7 @@ Screenshots + image analysis is expensive and slow. Only use screenshots for vis
|
||||
await page.screenshot({ path: 'check.png', scale: 'css' });
|
||||
|
||||
// GOOD: snapshot is text — fast, cheap, searchable
|
||||
await accessibilitySnapshot({ page, search: /expected text/i })
|
||||
await snapshot({ page, search: /expected text/i })
|
||||
|
||||
// GOOD: evaluate DOM directly for content checks
|
||||
const text = await page.evaluate(() => document.querySelector('.message')?.textContent);
|
||||
@@ -375,10 +375,10 @@ After any action (click, submit, navigate), verify what happened. **Always prefe
|
||||
|
||||
```js
|
||||
// Default: use snapshot with optional filtering
|
||||
page.url() + '\n' + await accessibilitySnapshot({ page })
|
||||
page.url() + '\n' + await snapshot({ page })
|
||||
|
||||
// Filter for specific content when snapshot is large
|
||||
await accessibilitySnapshot({ page, search: /dialog|button|error/i })
|
||||
await snapshot({ page, search: /dialog|button|error/i })
|
||||
```
|
||||
|
||||
Only use `screenshotWithAccessibilityLabels({ page })` for **visual layout issues** (CSS bugs, spatial positioning, colors). For verifying text content, button states, or form values, snapshots are always sufficient.
|
||||
@@ -388,9 +388,11 @@ If nothing changed, try `await waitForPageLoad({ page, timeout: 3000 })` or you
|
||||
## accessibility snapshots
|
||||
|
||||
```js
|
||||
await accessibilitySnapshot({ page, search?, showDiffSinceLastCall? })
|
||||
await snapshot({ page, search?, showDiffSinceLastCall? })
|
||||
```
|
||||
|
||||
`accessibilitySnapshot` is still available as an alias for backward compatibility.
|
||||
|
||||
- `search` - string/regex to filter results (returns first 10 matching lines)
|
||||
- `showDiffSinceLastCall` - returns diff since last snapshot (default: `true`). Pass `false` to get full snapshot.
|
||||
|
||||
@@ -413,7 +415,7 @@ to make it unique.
|
||||
If a screenshot shows ref labels like `e3`, resolve them using the last snapshot:
|
||||
|
||||
```js
|
||||
const snapshot = await accessibilitySnapshot({ page })
|
||||
const snap = await snapshot({ page })
|
||||
const locator = refToLocator({ ref: 'e3' })
|
||||
await page.locator(locator!).click()
|
||||
```
|
||||
@@ -427,13 +429,13 @@ await page.locator('role=link[name="Blog"]').click()
|
||||
Search for specific elements:
|
||||
|
||||
```js
|
||||
const snapshot = await accessibilitySnapshot({ page, search: /button|submit/i })
|
||||
const snap = await snapshot({ page, search: /button|submit/i })
|
||||
```
|
||||
|
||||
**Filtering large snapshots in JS** — when the built-in `search` isn't enough (e.g., you need multiple patterns or custom logic), filter the snapshot string directly:
|
||||
|
||||
```js
|
||||
const snap = await accessibilitySnapshot({ page, showDiffSinceLastCall: false });
|
||||
const snap = await snapshot({ page, showDiffSinceLastCall: false });
|
||||
const relevant = snap.split('\n').filter(l =>
|
||||
l.includes('dialog') || l.includes('error') || l.includes('button')
|
||||
).join('\n');
|
||||
@@ -444,9 +446,9 @@ This is much cheaper than taking a screenshot — use it as your primary debuggi
|
||||
|
||||
## choosing between snapshot methods
|
||||
|
||||
Both `accessibilitySnapshot` and `screenshotWithAccessibilityLabels` use the same ref system, so you can combine them effectively.
|
||||
Both `snapshot` and `screenshotWithAccessibilityLabels` use the same ref system, so you can combine them effectively.
|
||||
|
||||
**Use `accessibilitySnapshot` when:**
|
||||
**Use `snapshot` when:**
|
||||
- Page has simple, semantic structure (articles, forms, lists)
|
||||
- You need to search for specific text or patterns
|
||||
- Token usage matters (text is smaller than images)
|
||||
@@ -458,11 +460,11 @@ Both `accessibilitySnapshot` and `screenshotWithAccessibilityLabels` use the sam
|
||||
- DOM order doesn't match visual order
|
||||
- You need to understand the visual hierarchy
|
||||
|
||||
**Combining both:** Use screenshot first to understand layout and identify target elements visually, then use `accessibilitySnapshot({ search: /pattern/ })` for efficient searching in subsequent calls.
|
||||
**Combining both:** Use screenshot first to understand layout and identify target elements visually, then use `snapshot({ search: /pattern/ })` for efficient searching in subsequent calls.
|
||||
|
||||
## selector best practices
|
||||
|
||||
**For unknown websites**: use `accessibilitySnapshot()` - it shows what's actually interactive with stable locators.
|
||||
**For unknown websites**: use `snapshot()` - it shows what's actually interactive with stable locators.
|
||||
|
||||
**For development** (when you have source code access), prefer stable selectors in this order:
|
||||
|
||||
@@ -746,7 +748,7 @@ await editor.edit({ url: matches[0].url, oldString: 'DEBUG = false', newString:
|
||||
|
||||
**screenshotWithAccessibilityLabels** - take a screenshot with Vimium-style visual labels overlaid on interactive elements. Shows labels, captures screenshot, then removes labels. The image and accessibility snapshot are automatically included in the response. Can be called multiple times to capture multiple screenshots. Use a timeout of **20 seconds** for complex pages.
|
||||
|
||||
Prefer this for pages with grids, image galleries, maps, or complex visual layouts where spatial position matters. For simple text-heavy pages, `accessibilitySnapshot` with search is faster and uses fewer tokens.
|
||||
Prefer this for pages with grids, image galleries, maps, or complex visual layouts where spatial position matters. For simple text-heavy pages, `snapshot` with search is faster and uses fewer tokens.
|
||||
|
||||
```js
|
||||
await screenshotWithAccessibilityLabels({ page });
|
||||
@@ -904,7 +906,7 @@ console.log(JSON.stringify(info, null, 2));
|
||||
await page.keyboard.press('Enter');
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
const snap = await accessibilitySnapshot({ page, search: /dialog|error|message/ });
|
||||
const snap = await snapshot({ page, search: /dialog|error|message/ });
|
||||
const logs = await getLatestLogs({ page, search: /error/i, count: 10 });
|
||||
console.log('UI:', snap);
|
||||
console.log('Logs:', logs);
|
||||
|
||||
Reference in New Issue
Block a user