feat: support page reload (#462)

- merges navigation tools into a single tool.
- adds reload functionality.
- in a follow-up we will add options to reload.
This commit is contained in:
Alex Rudenko authored and GitHub committed 2025-10-24 09:00:04 +00:00
1 parent 21af8daf04
commit d177087981
4 files changed
+99 -79

No files matched your search

+1 -2
View File
@@ -247,11 +247,10 @@ If you run into any issues, checkout our [troubleshooting guide](./docs/troubles
- [`hover`](docs/tool-reference.md#hover)
- [`press_key`](docs/tool-reference.md#press_key)
- [`upload_file`](docs/tool-reference.md#upload_file)
- **Navigation automation** (7 tools)
- **Navigation automation** (6 tools)
- [`close_page`](docs/tool-reference.md#close_page)
- [`list_pages`](docs/tool-reference.md#list_pages)
- [`navigate_page`](docs/tool-reference.md#navigate_page)
- [`navigate_page_history`](docs/tool-reference.md#navigate_page_history)
- [`new_page`](docs/tool-reference.md#new_page)
- [`select_page`](docs/tool-reference.md#select_page)
- [`wait_for`](docs/tool-reference.md#wait_for)
+3 -14
View File
@@ -11,11 +11,10 @@
- [`hover`](#hover)
- [`press_key`](#press_key)
- [`upload_file`](#upload_file)
- **[Navigation automation](#navigation-automation)** (7 tools)
- **[Navigation automation](#navigation-automation)** (6 tools)
- [`close_page`](#close_page)
- [`list_pages`](#list_pages)
- [`navigate_page`](#navigate_page)
- [`navigate_page_history`](#navigate_page_history)
- [`new_page`](#new_page)
- [`select_page`](#select_page)
- [`wait_for`](#wait_for)
@@ -151,18 +150,8 @@
**Parameters:**
- **timeout** (integer) _(optional)_: Maximum wait time in milliseconds. If set to 0, the default timeout will be used.
- **url** (string) **(required)**: URL to navigate the page to
---
### `navigate_page_history`
**Description:** Navigates the currently selected page.
**Parameters:**
- **navigate** (enum: "back", "forward") **(required)**: Whether to navigate back or navigate forward in the selected pages history
- **timeout** (integer) _(optional)_: Maximum wait time in milliseconds. If set to 0, the default timeout will be used.
- **type** (enum: "url", "back", "forward", "reload") _(optional)_: Navigate the page by URL, back or forward in history, or reload.
- **url** (string) _(optional)_: Target URL (only type=url)
---
+65 -37
View File
@@ -105,35 +105,13 @@ export const navigatePage = defineTool({
readOnlyHint: false,
},
schema: {
url: zod.string().describe('URL to navigate the page to'),
...timeoutSchema,
},
handler: async (request, response, context) => {
const page = context.getSelectedPage();
await context.waitForEventsAfterAction(async () => {
await page.goto(request.params.url, {
timeout: request.params.timeout,
});
});
response.setIncludePages(true);
},
});
export const navigatePageHistory = defineTool({
name: 'navigate_page_history',
description: `Navigates the currently selected page.`,
annotations: {
category: ToolCategory.NAVIGATION,
readOnlyHint: false,
},
schema: {
navigate: zod
.enum(['back', 'forward'])
type: zod
.enum(['url', 'back', 'forward', 'reload'])
.optional()
.describe(
'Whether to navigate back or navigate forward in the selected pages history',
'Navigate the page by URL, back or forward in history, or reload.',
),
url: zod.string().optional().describe('Target URL (only type=url)'),
...timeoutSchema,
},
handler: async (request, response, context) => {
@@ -142,18 +120,68 @@ export const navigatePageHistory = defineTool({
timeout: request.params.timeout,
};
try {
if (request.params.navigate === 'back') {
await page.goBack(options);
} else {
await page.goForward(options);
}
} catch (error) {
response.appendResponseLine(
`Unable to navigate ${request.params.navigate} in currently selected page. ${error.message}`,
);
if (!request.params.type && !request.params.url) {
throw new Error('Either URL or a type is required.');
}
if (!request.params.type) {
request.params.type = 'url';
}
await context.waitForEventsAfterAction(async () => {
switch (request.params.type) {
case 'url':
if (!request.params.url) {
throw new Error('A URL is required for navigation of type=url.');
}
try {
await page.goto(request.params.url, options);
response.appendResponseLine(
`Successfully navigated to ${request.params.url}.`,
);
} catch (error) {
response.appendResponseLine(
`Unable to navigate in the selected page: ${error.message}.`,
);
}
break;
case 'back':
try {
await page.goBack(options);
response.appendResponseLine(
`Successfully navigated back to ${page.url()}.`,
);
} catch (error) {
response.appendResponseLine(
`Unable to navigate back in the selected page: ${error.message}.`,
);
}
break;
case 'forward':
try {
await page.goForward(options);
response.appendResponseLine(
`Successfully navigated forward to ${page.url()}.`,
);
} catch (error) {
response.appendResponseLine(
`Unable to navigate forward in the selected page: ${error.message}.`,
);
}
break;
case 'reload':
try {
await page.reload(options);
response.appendResponseLine(`Successfully reloaded the page.`);
} catch (error) {
response.appendResponseLine(
`Unable to reload the selected page: ${error.message}.`,
);
}
break;
}
});
response.setIncludePages(true);
},
});
+30 -26
View File
@@ -14,7 +14,6 @@ import {
closePage,
selectPage,
navigatePage,
navigatePageHistory,
resizePage,
handleDialog,
} from '../../src/tools/pages.js';
@@ -29,7 +28,7 @@ describe('pages', () => {
});
});
});
describe('browser_new_page', () => {
describe('new_page', () => {
it('create a page', async () => {
await withBrowser(async (response, context) => {
assert.strictEqual(context.getSelectedPageIdx(), 0);
@@ -43,7 +42,7 @@ describe('pages', () => {
});
});
});
describe('browser_close_page', () => {
describe('close_page', () => {
it('closes a page', async () => {
await withBrowser(async (response, context) => {
const page = await context.newPage();
@@ -67,7 +66,7 @@ describe('pages', () => {
});
});
});
describe('browser_select_page', () => {
describe('select_page', () => {
it('selects a page', async () => {
await withBrowser(async (response, context) => {
await context.newPage();
@@ -78,7 +77,7 @@ describe('pages', () => {
});
});
});
describe('browser_navigate_page', () => {
describe('navigate_page', () => {
it('navigates to correct page', async () => {
await withBrowser(async (response, context) => {
await navigatePage.handler(
@@ -118,17 +117,11 @@ describe('pages', () => {
}
});
});
});
describe('browser_navigate_page_history', () => {
it('go back', async () => {
await withBrowser(async (response, context) => {
const page = context.getSelectedPage();
await page.goto('data:text/html,<div>Hello MCP</div>');
await navigatePageHistory.handler(
{params: {navigate: 'back'}},
response,
context,
);
await navigatePage.handler({params: {type: 'back'}}, response, context);
assert.equal(
await page.evaluate(() => document.location.href),
@@ -142,8 +135,8 @@ describe('pages', () => {
const page = context.getSelectedPage();
await page.goto('data:text/html,<div>Hello MCP</div>');
await page.goBack();
await navigatePageHistory.handler(
{params: {navigate: 'forward'}},
await navigatePage.handler(
{params: {type: 'forward'}},
response,
context,
);
@@ -155,10 +148,27 @@ describe('pages', () => {
assert.ok(response.includePages);
});
});
it('reload', async () => {
await withBrowser(async (response, context) => {
const page = context.getSelectedPage();
await page.goto('data:text/html,<div>Hello MCP</div>');
await navigatePage.handler(
{params: {type: 'reload'}},
response,
context,
);
assert.equal(
await page.evaluate(() => document.location.href),
'data:text/html,<div>Hello MCP</div>',
);
assert.ok(response.includePages);
});
});
it('go forward with error', async () => {
await withBrowser(async (response, context) => {
await navigatePageHistory.handler(
{params: {navigate: 'forward'}},
await navigatePage.handler(
{params: {type: 'forward'}},
response,
context,
);
@@ -166,31 +176,25 @@ describe('pages', () => {
assert.ok(
response.responseLines
.at(0)
?.startsWith(
'Unable to navigate forward in currently selected page.',
),
?.startsWith('Unable to navigate forward in the selected page:'),
);
assert.ok(response.includePages);
});
});
it('go back with error', async () => {
await withBrowser(async (response, context) => {
await navigatePageHistory.handler(
{params: {navigate: 'back'}},
response,
context,
);
await navigatePage.handler({params: {type: 'back'}}, response, context);
assert.ok(
response.responseLines
.at(0)
?.startsWith('Unable to navigate back in currently selected page.'),
?.startsWith('Unable to navigate back in the selected page:'),
);
assert.ok(response.includePages);
});
});
});
describe('browser_resize', () => {
describe('resize', () => {
it('create a page', async () => {
await withBrowser(async (response, context) => {
assert.strictEqual(context.getSelectedPageIdx(), 0);