@playwright/mcp 0.0.9 → 0.0.10

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/README.md CHANGED
@@ -68,6 +68,7 @@ The Playwright MCP server supports the following command-line options:
68
68
  - Chrome channels: `chrome-beta`, `chrome-canary`, `chrome-dev`
69
69
  - Edge channels: `msedge-beta`, `msedge-canary`, `msedge-dev`
70
70
  - Default: `chrome`
71
+ - `--caps <caps>`: Comma-separated list of capabilities to enable, possible values: tabs, pdf, history, wait, files, install. Default is all.
71
72
  - `--cdp-endpoint <endpoint>`: CDP endpoint to connect to
72
73
  - `--executable-path <path>`: Path to the browser executable
73
74
  - `--headless`: Run browser in headless mode (headed by default)
@@ -167,22 +168,7 @@ transport = new SSEServerTransport("/messages", res);
167
168
  server.connect(transport);
168
169
  ```
169
170
 
170
- ### Snapshot Mode
171
-
172
- The Playwright MCP provides a set of tools for browser automation. Here are all available tools:
173
-
174
- - **browser_navigate**
175
- - Description: Navigate to a URL
176
- - Parameters:
177
- - `url` (string): The URL to navigate to
178
-
179
- - **browser_go_back**
180
- - Description: Go back to the previous page
181
- - Parameters: None
182
-
183
- - **browser_go_forward**
184
- - Description: Go forward to the next page
185
- - Parameters: None
171
+ ### Snapshot-based Interactions
186
172
 
187
173
  - **browser_click**
188
174
  - Description: Perform click on a web page
@@ -210,109 +196,121 @@ The Playwright MCP provides a set of tools for browser automation. Here are all
210
196
  - `element` (string): Human-readable element description used to obtain permission to interact with the element
211
197
  - `ref` (string): Exact target element reference from the page snapshot
212
198
  - `text` (string): Text to type into the element
213
- - `submit` (boolean): Whether to submit entered text (press Enter after)
199
+ - `submit` (boolean, optional): Whether to submit entered text (press Enter after)
200
+ - `slowly` (boolean, optional): Whether to type one character at a time. Useful for triggering key handlers in the page. By default entire text is filled in at once.
214
201
 
215
202
  - **browser_select_option**
216
- - Description: Select option in a dropdown
203
+ - Description: Select an option in a dropdown
217
204
  - Parameters:
218
205
  - `element` (string): Human-readable element description used to obtain permission to interact with the element
219
206
  - `ref` (string): Exact target element reference from the page snapshot
220
- - `values` (array): Array of values to select in the dropdown.
207
+ - `values` (array): Array of values to select in the dropdown. This can be a single value or multiple values.
221
208
 
222
- - **browser_choose_file**
223
- - Description: Choose one or multiple files to upload
209
+ - **browser_snapshot**
210
+ - Description: Capture accessibility snapshot of the current page, this is better than screenshot
211
+ - Parameters: None
212
+
213
+ - **browser_take_screenshot**
214
+ - Description: Take a screenshot of the current page. You can't perform actions based on the screenshot, use browser_snapshot for actions.
224
215
  - Parameters:
225
- - `paths` (array): The absolute paths to the files to upload. Can be a single file or multiple files.
216
+ - `raw` (boolean, optional): Whether to return without compression (in PNG format). Default is false, which returns a JPEG image.
226
217
 
227
- - **browser_press_key**
228
- - Description: Press a key on the keyboard
218
+ ### Vision-based Interactions
219
+
220
+ - **browser_screen_move_mouse**
221
+ - Description: Move mouse to a given position
229
222
  - Parameters:
230
- - `key` (string): Name of the key to press or a character to generate, such as `ArrowLeft` or `a`
223
+ - `element` (string): Human-readable element description used to obtain permission to interact with the element
224
+ - `x` (number): X coordinate
225
+ - `y` (number): Y coordinate
231
226
 
232
- - **browser_snapshot**
233
- - Description: Capture accessibility snapshot of the current page (better than screenshot)
227
+ - **browser_screen_capture**
228
+ - Description: Take a screenshot of the current page
234
229
  - Parameters: None
235
230
 
236
- - **browser_save_as_pdf**
237
- - Description: Save page as PDF
238
- - Parameters: None
231
+ - **browser_screen_click**
232
+ - Description: Click left mouse button
233
+ - Parameters:
234
+ - `element` (string): Human-readable element description used to obtain permission to interact with the element
235
+ - `x` (number): X coordinate
236
+ - `y` (number): Y coordinate
239
237
 
240
- - **browser_take_screenshot**
241
- - Description: Capture screenshot of the page
238
+ - **browser_screen_drag**
239
+ - Description: Drag left mouse button
242
240
  - Parameters:
243
- - `raw` (string): Optionally returns lossless PNG screenshot. JPEG by default.
241
+ - `element` (string): Human-readable element description used to obtain permission to interact with the element
242
+ - `startX` (number): Start X coordinate
243
+ - `startY` (number): Start Y coordinate
244
+ - `endX` (number): End X coordinate
245
+ - `endY` (number): End Y coordinate
244
246
 
245
- - **browser_wait**
246
- - Description: Wait for a specified time in seconds
247
+ - **browser_screen_type**
248
+ - Description: Type text
247
249
  - Parameters:
248
- - `time` (number): The time to wait in seconds (capped at 10 seconds)
250
+ - `text` (string): Text to type
251
+ - `submit` (boolean, optional): Whether to submit entered text (press Enter after)
249
252
 
250
- - **browser_close**
251
- - Description: Close the page
253
+ - **browser_press_key**
254
+ - Description: Press a key on the keyboard
255
+ - Parameters:
256
+ - `key` (string): Name of the key to press or a character to generate, such as `ArrowLeft` or `a`
257
+
258
+ ### Tab Management
259
+
260
+ - **browser_tab_list**
261
+ - Description: List browser tabs
252
262
  - Parameters: None
253
263
 
264
+ - **browser_tab_new**
265
+ - Description: Open a new tab
266
+ - Parameters:
267
+ - `url` (string, optional): The URL to navigate to in the new tab. If not provided, the new tab will be blank.
268
+
269
+ - **browser_tab_select**
270
+ - Description: Select a tab by index
271
+ - Parameters:
272
+ - `index` (number): The index of the tab to select
254
273
 
255
- ### Vision Mode
274
+ - **browser_tab_close**
275
+ - Description: Close a tab
276
+ - Parameters:
277
+ - `index` (number, optional): The index of the tab to close. Closes current tab if not provided.
256
278
 
257
- Vision Mode provides tools for visual-based interactions using screenshots. Here are all available tools:
279
+ ### Navigation
258
280
 
259
281
  - **browser_navigate**
260
282
  - Description: Navigate to a URL
261
283
  - Parameters:
262
284
  - `url` (string): The URL to navigate to
263
285
 
264
- - **browser_go_back**
286
+ - **browser_navigate_back**
265
287
  - Description: Go back to the previous page
266
288
  - Parameters: None
267
289
 
268
- - **browser_go_forward**
290
+ - **browser_navigate_forward**
269
291
  - Description: Go forward to the next page
270
292
  - Parameters: None
271
293
 
272
- - **browser_screenshot**
273
- - Description: Capture screenshot of the current page
274
- - Parameters: None
275
-
276
- - **browser_move_mouse**
277
- - Description: Move mouse to specified coordinates
278
- - Parameters:
279
- - `x` (number): X coordinate
280
- - `y` (number): Y coordinate
281
-
282
- - **browser_click**
283
- - Description: Click at specified coordinates
284
- - Parameters:
285
- - `x` (number): X coordinate to click at
286
- - `y` (number): Y coordinate to click at
287
-
288
- - **browser_drag**
289
- - Description: Perform drag and drop operation
290
- - Parameters:
291
- - `startX` (number): Start X coordinate
292
- - `startY` (number): Start Y coordinate
293
- - `endX` (number): End X coordinate
294
- - `endY` (number): End Y coordinate
295
-
296
- - **browser_type**
297
- - Description: Type text at specified coordinates
298
- - Parameters:
299
- - `text` (string): Text to type
300
- - `submit` (boolean): Whether to submit entered text (press Enter after)
294
+ ### Keyboard
301
295
 
302
296
  - **browser_press_key**
303
297
  - Description: Press a key on the keyboard
304
298
  - Parameters:
305
299
  - `key` (string): Name of the key to press or a character to generate, such as `ArrowLeft` or `a`
306
300
 
307
- - **browser_choose_file**
301
+ ### Files and Media
302
+
303
+ - **browser_file_upload**
308
304
  - Description: Choose one or multiple files to upload
309
305
  - Parameters:
310
306
  - `paths` (array): The absolute paths to the files to upload. Can be a single file or multiple files.
311
307
 
312
- - **browser_save_as_pdf**
308
+ - **browser_pdf_save**
313
309
  - Description: Save page as PDF
314
310
  - Parameters: None
315
311
 
312
+ ### Utilities
313
+
316
314
  - **browser_wait**
317
315
  - Description: Wait for a specified time in seconds
318
316
  - Parameters:
@@ -321,3 +319,10 @@ Vision Mode provides tools for visual-based interactions using screenshots. Here
321
319
  - **browser_close**
322
320
  - Description: Close the page
323
321
  - Parameters: None
322
+
323
+ - **browser_install**
324
+ - Description: Install the browser specified in the config. Call this if you get an error about the browser not being installed.
325
+ - Parameters: None
326
+
327
+ ### Vision Mode
328
+
package/index.d.ts CHANGED
@@ -18,6 +18,8 @@
18
18
  import type { LaunchOptions } from 'playwright';
19
19
  import type { Server } from '@modelcontextprotocol/sdk/server/index.js';
20
20
 
21
+ type ToolCapability = 'core' | 'tabs' | 'pdf' | 'history' | 'wait' | 'files' | 'install';
22
+
21
23
  type Options = {
22
24
  /**
23
25
  * Path to the user data directory.
@@ -35,6 +37,11 @@ type Options = {
35
37
  * @default false
36
38
  */
37
39
  vision?: boolean;
40
+
41
+ /**
42
+ * Capabilities to enable.
43
+ */
44
+ capabilities?: ToolCapability[];
38
45
  };
39
46
 
40
47
  export function createServer(options?: Options): Server;
package/lib/context.js CHANGED
@@ -52,135 +52,241 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
52
52
  };
53
53
  Object.defineProperty(exports, "__esModule", { value: true });
54
54
  exports.Context = void 0;
55
- const child_process_1 = require("child_process");
56
- const path_1 = __importDefault(require("path"));
57
55
  const playwright = __importStar(require("playwright"));
58
56
  const yaml_1 = __importDefault(require("yaml"));
57
+ const utils_1 = require("./tools/utils");
59
58
  class Context {
60
- _options;
59
+ options;
61
60
  _browser;
62
- _page;
63
- _console = [];
64
- _createPagePromise;
65
- _fileChooser;
66
- _lastSnapshotFrames = [];
61
+ _browserContext;
62
+ _tabs = [];
63
+ _currentTab;
67
64
  constructor(options) {
68
- this._options = options;
69
- }
70
- async createPage() {
71
- if (this._createPagePromise)
72
- return this._createPagePromise;
73
- this._createPagePromise = (async () => {
74
- const { browser, page } = await this._createPage();
75
- page.on('console', event => this._console.push(event));
76
- page.on('framenavigated', frame => {
77
- if (!frame.parentFrame())
78
- this._console.length = 0;
79
- });
80
- page.on('close', () => this._onPageClose());
81
- page.on('filechooser', chooser => this._fileChooser = chooser);
82
- page.setDefaultNavigationTimeout(60000);
83
- page.setDefaultTimeout(5000);
84
- this._page = page;
85
- this._browser = browser;
86
- return page;
87
- })();
88
- return this._createPagePromise;
89
- }
90
- _onPageClose() {
65
+ this.options = options;
66
+ }
67
+ tabs() {
68
+ return this._tabs;
69
+ }
70
+ currentTab() {
71
+ if (!this._currentTab)
72
+ throw new Error('Navigate to a location to create a tab');
73
+ return this._currentTab;
74
+ }
75
+ async newTab() {
76
+ const browserContext = await this._ensureBrowserContext();
77
+ const page = await browserContext.newPage();
78
+ this._currentTab = this._tabs.find(t => t.page === page);
79
+ return this._currentTab;
80
+ }
81
+ async selectTab(index) {
82
+ this._currentTab = this._tabs[index - 1];
83
+ await this._currentTab.page.bringToFront();
84
+ }
85
+ async ensureTab() {
86
+ const context = await this._ensureBrowserContext();
87
+ if (!this._currentTab)
88
+ await context.newPage();
89
+ return this._currentTab;
90
+ }
91
+ async listTabs() {
92
+ if (!this._tabs.length)
93
+ return 'No tabs open';
94
+ const lines = ['Open tabs:'];
95
+ for (let i = 0; i < this._tabs.length; i++) {
96
+ const tab = this._tabs[i];
97
+ const title = await tab.page.title();
98
+ const url = tab.page.url();
99
+ const current = tab === this._currentTab ? ' (current)' : '';
100
+ lines.push(`- ${i + 1}:${current} [${title}] (${url})`);
101
+ }
102
+ return lines.join('\n');
103
+ }
104
+ async closeTab(index) {
105
+ const tab = index === undefined ? this.currentTab() : this._tabs[index - 1];
106
+ await tab.page.close();
107
+ return await this.listTabs();
108
+ }
109
+ _onPageCreated(page) {
110
+ const tab = new Tab(this, page, tab => this._onPageClosed(tab));
111
+ this._tabs.push(tab);
112
+ if (!this._currentTab)
113
+ this._currentTab = tab;
114
+ }
115
+ _onPageClosed(tab) {
116
+ const index = this._tabs.indexOf(tab);
117
+ if (index === -1)
118
+ return;
119
+ this._tabs.splice(index, 1);
120
+ if (this._currentTab === tab)
121
+ this._currentTab = this._tabs[Math.min(index, this._tabs.length - 1)];
91
122
  const browser = this._browser;
92
- const page = this._page;
93
- void page?.context()?.close().then(() => browser?.close()).catch(() => { });
94
- this._createPagePromise = undefined;
95
- this._browser = undefined;
96
- this._page = undefined;
123
+ if (this._browserContext && !this._tabs.length) {
124
+ void this._browserContext.close().then(() => browser?.close()).catch(() => { });
125
+ this._browser = undefined;
126
+ this._browserContext = undefined;
127
+ }
128
+ }
129
+ async close() {
130
+ if (!this._browserContext)
131
+ return;
132
+ await this._browserContext.close();
133
+ }
134
+ async _ensureBrowserContext() {
135
+ if (!this._browserContext) {
136
+ const context = await this._createBrowserContext();
137
+ this._browser = context.browser;
138
+ this._browserContext = context.browserContext;
139
+ for (const page of this._browserContext.pages())
140
+ this._onPageCreated(page);
141
+ this._browserContext.on('page', page => this._onPageCreated(page));
142
+ }
143
+ return this._browserContext;
144
+ }
145
+ async _createBrowserContext() {
146
+ if (this.options.remoteEndpoint) {
147
+ const url = new URL(this.options.remoteEndpoint);
148
+ if (this.options.browserName)
149
+ url.searchParams.set('browser', this.options.browserName);
150
+ if (this.options.launchOptions)
151
+ url.searchParams.set('launch-options', JSON.stringify(this.options.launchOptions));
152
+ const browser = await playwright[this.options.browserName ?? 'chromium'].connect(String(url));
153
+ const browserContext = await browser.newContext();
154
+ return { browser, browserContext };
155
+ }
156
+ if (this.options.cdpEndpoint) {
157
+ const browser = await playwright.chromium.connectOverCDP(this.options.cdpEndpoint);
158
+ const browserContext = browser.contexts()[0];
159
+ return { browser, browserContext };
160
+ }
161
+ const browserContext = await this._launchPersistentContext();
162
+ return { browserContext };
163
+ }
164
+ async _launchPersistentContext() {
165
+ try {
166
+ const browserType = this.options.browserName ? playwright[this.options.browserName] : playwright.chromium;
167
+ return await browserType.launchPersistentContext(this.options.userDataDir, this.options.launchOptions);
168
+ }
169
+ catch (error) {
170
+ if (error.message.includes('Executable doesn\'t exist'))
171
+ throw new Error(`Browser specified in your config is not installed. Either install it (likely) or change the config.`);
172
+ throw error;
173
+ }
174
+ }
175
+ }
176
+ exports.Context = Context;
177
+ class Tab {
178
+ context;
179
+ page;
180
+ _console = [];
181
+ _fileChooser;
182
+ _snapshot;
183
+ _onPageClose;
184
+ constructor(context, page, onPageClose) {
185
+ this.context = context;
186
+ this.page = page;
187
+ this._onPageClose = onPageClose;
188
+ page.on('console', event => this._console.push(event));
189
+ page.on('framenavigated', frame => {
190
+ if (!frame.parentFrame())
191
+ this._console.length = 0;
192
+ });
193
+ page.on('close', () => this._onClose());
194
+ page.on('filechooser', chooser => this._fileChooser = chooser);
195
+ page.setDefaultNavigationTimeout(60000);
196
+ page.setDefaultTimeout(5000);
197
+ }
198
+ _onClose() {
97
199
  this._fileChooser = undefined;
98
200
  this._console.length = 0;
201
+ this._onPageClose(this);
202
+ }
203
+ async navigate(url) {
204
+ await this.page.goto(url, { waitUntil: 'domcontentloaded' });
205
+ // Cap load event to 5 seconds, the page is operational at this point.
206
+ await this.page.waitForLoadState('load', { timeout: 5000 }).catch(() => { });
99
207
  }
100
- async install() {
101
- const channel = this._options.launchOptions?.channel ?? this._options.browserName ?? 'chrome';
102
- const cli = path_1.default.join(require.resolve('playwright/package.json'), '..', 'cli.js');
103
- const child = (0, child_process_1.fork)(cli, ['install', channel], {
104
- stdio: 'pipe',
208
+ async run(callback, options) {
209
+ try {
210
+ if (!options?.noClearFileChooser)
211
+ this._fileChooser = undefined;
212
+ if (options?.waitForCompletion)
213
+ await (0, utils_1.waitForCompletion)(this.page, () => callback(this));
214
+ else
215
+ await callback(this);
216
+ }
217
+ finally {
218
+ if (options?.captureSnapshot)
219
+ this._snapshot = await PageSnapshot.create(this.page);
220
+ }
221
+ const tabList = this.context.tabs().length > 1 ? await this.context.listTabs() + '\n\nCurrent tab:' + '\n' : '';
222
+ const snapshot = this._snapshot?.text({ status: options?.status, hasFileChooser: !!this._fileChooser }) ?? options?.status ?? '';
223
+ return {
224
+ content: [{
225
+ type: 'text',
226
+ text: tabList + snapshot,
227
+ }],
228
+ };
229
+ }
230
+ async runAndWait(callback, options) {
231
+ return await this.run(callback, {
232
+ waitForCompletion: true,
233
+ ...options,
105
234
  });
106
- const output = [];
107
- child.stdout?.on('data', data => output.push(data.toString()));
108
- child.stderr?.on('data', data => output.push(data.toString()));
109
- return new Promise((resolve, reject) => {
110
- child.on('close', code => {
111
- if (code === 0)
112
- resolve(channel);
113
- else
114
- reject(new Error(`Failed to install browser: ${output.join('')}`));
115
- });
235
+ }
236
+ async runAndWaitWithSnapshot(callback, options) {
237
+ return await this.run(callback, {
238
+ captureSnapshot: true,
239
+ waitForCompletion: true,
240
+ ...options,
116
241
  });
117
242
  }
118
- existingPage() {
119
- if (!this._page)
120
- throw new Error('Navigate to a location to create a page');
121
- return this._page;
243
+ lastSnapshot() {
244
+ if (!this._snapshot)
245
+ throw new Error('No snapshot available');
246
+ return this._snapshot;
122
247
  }
123
248
  async console() {
124
249
  return this._console;
125
250
  }
126
- async close() {
127
- if (!this._page)
128
- return;
129
- await this._page.close();
130
- }
131
251
  async submitFileChooser(paths) {
132
252
  if (!this._fileChooser)
133
253
  throw new Error('No file chooser visible');
134
254
  await this._fileChooser.setFiles(paths);
135
255
  this._fileChooser = undefined;
136
256
  }
137
- hasFileChooser() {
138
- return !!this._fileChooser;
257
+ }
258
+ class PageSnapshot {
259
+ _frameLocators = [];
260
+ _text;
261
+ constructor() {
139
262
  }
140
- clearFileChooser() {
141
- this._fileChooser = undefined;
263
+ static async create(page) {
264
+ const snapshot = new PageSnapshot();
265
+ await snapshot._build(page);
266
+ return snapshot;
142
267
  }
143
- async _createPage() {
144
- if (this._options.remoteEndpoint) {
145
- const url = new URL(this._options.remoteEndpoint);
146
- if (this._options.browserName)
147
- url.searchParams.set('browser', this._options.browserName);
148
- if (this._options.launchOptions)
149
- url.searchParams.set('launch-options', JSON.stringify(this._options.launchOptions));
150
- const browser = await playwright[this._options.browserName ?? 'chromium'].connect(String(url));
151
- const page = await browser.newPage();
152
- return { browser, page };
268
+ text(options) {
269
+ const results = [];
270
+ if (options?.status) {
271
+ results.push(options.status);
272
+ results.push('');
153
273
  }
154
- if (this._options.cdpEndpoint) {
155
- const browser = await playwright.chromium.connectOverCDP(this._options.cdpEndpoint);
156
- const browserContext = browser.contexts()[0];
157
- let [page] = browserContext.pages();
158
- if (!page)
159
- page = await browserContext.newPage();
160
- return { browser, page };
274
+ if (options?.hasFileChooser) {
275
+ results.push('- There is a file chooser visible that requires browser_file_upload to be called');
276
+ results.push('');
161
277
  }
162
- const context = await this._launchPersistentContext();
163
- const [page] = context.pages();
164
- return { page };
278
+ results.push(this._text);
279
+ return results.join('\n');
165
280
  }
166
- async _launchPersistentContext() {
167
- try {
168
- const browserType = this._options.browserName ? playwright[this._options.browserName] : playwright.chromium;
169
- return await browserType.launchPersistentContext(this._options.userDataDir, this._options.launchOptions);
170
- }
171
- catch (error) {
172
- if (error.message.includes('Executable doesn\'t exist'))
173
- throw new Error(`Browser specified in your config is not installed. Either install it (likely) or change the config.`);
174
- throw error;
175
- }
176
- }
177
- async allFramesSnapshot() {
178
- this._lastSnapshotFrames = [];
179
- const yaml = await this._allFramesSnapshot(this.existingPage());
180
- return yaml.toString().trim();
281
+ async _build(page) {
282
+ const yamlDocument = await this._snapshotFrame(page);
283
+ const lines = [];
284
+ lines.push(`- Page URL: ${page.url()}`, `- Page Title: ${await page.title()}`);
285
+ lines.push(`- Page Snapshot`, '```yaml', yamlDocument.toString().trim(), '```', '');
286
+ this._text = lines.join('\n');
181
287
  }
182
- async _allFramesSnapshot(frame) {
183
- const frameIndex = this._lastSnapshotFrames.push(frame) - 1;
288
+ async _snapshotFrame(frame) {
289
+ const frameIndex = this._frameLocators.push(frame) - 1;
184
290
  const snapshotString = await frame.locator('body').ariaSnapshot({ ref: true });
185
291
  const snapshot = yaml_1.default.parseDocument(snapshotString);
186
292
  const visit = async (node) => {
@@ -202,7 +308,7 @@ class Context {
202
308
  const ref = value.match(/\[ref=(.*)\]/)?.[1];
203
309
  if (ref) {
204
310
  try {
205
- const childSnapshot = await this._allFramesSnapshot(frame.frameLocator(`aria-ref=${ref}`));
311
+ const childSnapshot = await this._snapshotFrame(frame.frameLocator(`aria-ref=${ref}`));
206
312
  return snapshot.createPair(node.value, childSnapshot);
207
313
  }
208
314
  catch (error) {
@@ -218,11 +324,11 @@ class Context {
218
324
  return snapshot;
219
325
  }
220
326
  refLocator(ref) {
221
- let frame = this._lastSnapshotFrames[0];
327
+ let frame = this._frameLocators[0];
222
328
  const match = ref.match(/^f(\d+)(.*)/);
223
329
  if (match) {
224
330
  const frameIndex = parseInt(match[1], 10);
225
- frame = this._lastSnapshotFrames[frameIndex];
331
+ frame = this._frameLocators[frameIndex];
226
332
  ref = match[2];
227
333
  }
228
334
  if (!frame)
@@ -230,4 +336,3 @@ class Context {
230
336
  return frame.locator(`aria-ref=${ref}`);
231
337
  }
232
338
  }
233
- exports.Context = Context;