mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-03-28 15:40:04 +00:00
Improve MCP tools test
Add helper to navigate to page, to reduce the boilerplate in each test. Reduce waitForSelector time from 200ms to 20ms to speed up tests.
This commit is contained in:
@@ -8,7 +8,7 @@
|
|||||||
el.id = "delayed";
|
el.id = "delayed";
|
||||||
el.textContent = "Appeared after delay";
|
el.textContent = "Appeared after delay";
|
||||||
document.body.appendChild(el);
|
document.body.appendChild(el);
|
||||||
}, 200);
|
}, 20);
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -629,22 +629,14 @@ fn performGoto(server: *Server, url: [:0]const u8, id: std.json.Value) !void {
|
|||||||
try runner.wait(.{ .ms = 2000 });
|
try runner.wait(.{ .ms = 2000 });
|
||||||
}
|
}
|
||||||
|
|
||||||
const testing = @import("../testing.zig");
|
|
||||||
const router = @import("router.zig");
|
const router = @import("router.zig");
|
||||||
|
const testing = @import("../testing.zig");
|
||||||
|
|
||||||
test "MCP - evaluate error reporting" {
|
test "MCP - evaluate error reporting" {
|
||||||
defer testing.reset();
|
defer testing.reset();
|
||||||
const allocator = testing.allocator;
|
var out: std.io.Writer.Allocating = .init(testing.arena_allocator);
|
||||||
const app = testing.test_app;
|
const server = try testLoadPage("about:blank", &out.writer);
|
||||||
|
|
||||||
var out_alloc: std.io.Writer.Allocating = .init(testing.arena_allocator);
|
|
||||||
defer out_alloc.deinit();
|
|
||||||
|
|
||||||
var server = try Server.init(allocator, app, &out_alloc.writer);
|
|
||||||
defer server.deinit();
|
defer server.deinit();
|
||||||
_ = try server.session.createPage();
|
|
||||||
|
|
||||||
const aa = testing.arena_allocator;
|
|
||||||
|
|
||||||
// Call evaluate with a script that throws an error
|
// Call evaluate with a script that throws an error
|
||||||
const msg =
|
const msg =
|
||||||
@@ -661,39 +653,25 @@ test "MCP - evaluate error reporting" {
|
|||||||
\\}
|
\\}
|
||||||
;
|
;
|
||||||
|
|
||||||
try router.handleMessage(server, aa, msg);
|
try router.handleMessage(server, testing.arena_allocator, msg);
|
||||||
|
|
||||||
try testing.expectJson(
|
try testing.expectJson(.{ .id = 1, .result = .{
|
||||||
\\{
|
.isError = true,
|
||||||
\\ "id": 1,
|
.content = &.{.{ .type = "text" }},
|
||||||
\\ "result": {
|
} }, out.written());
|
||||||
\\ "isError": true,
|
|
||||||
\\ "content": [
|
|
||||||
\\ { "type": "text" }
|
|
||||||
\\ ]
|
|
||||||
\\ }
|
|
||||||
\\}
|
|
||||||
, out_alloc.writer.buffered());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
test "MCP - Actions: click, fill, scroll" {
|
test "MCP - Actions: click, fill, scroll" {
|
||||||
defer testing.reset();
|
defer testing.reset();
|
||||||
const allocator = testing.allocator;
|
const aa = testing.arena_allocator;
|
||||||
const app = testing.test_app;
|
|
||||||
|
|
||||||
var out_alloc: std.io.Writer.Allocating = .init(testing.arena_allocator);
|
var out: std.io.Writer.Allocating = .init(aa);
|
||||||
defer out_alloc.deinit();
|
const server = try testLoadPage("http://localhost:9582/src/browser/tests/mcp_actions.html", &out.writer);
|
||||||
|
|
||||||
var server = try Server.init(allocator, app, &out_alloc.writer);
|
|
||||||
defer server.deinit();
|
defer server.deinit();
|
||||||
|
|
||||||
const aa = testing.arena_allocator;
|
const page = &server.session.page.?;
|
||||||
const page = try server.session.createPage();
|
|
||||||
const url = "http://localhost:9582/src/browser/tests/mcp_actions.html";
|
|
||||||
try page.navigate(url, .{ .reason = .address_bar, .kind = .{ .push = null } });
|
|
||||||
var runner = try server.session.runner(.{});
|
|
||||||
try runner.wait(.{ .ms = 2000 });
|
|
||||||
|
|
||||||
|
{
|
||||||
// Test Click
|
// Test Click
|
||||||
const btn = page.document.getElementById("btn", page).?.asNode();
|
const btn = page.document.getElementById("btn", page).?.asNode();
|
||||||
const btn_id = (try server.node_registry.register(btn)).id;
|
const btn_id = (try server.node_registry.register(btn)).id;
|
||||||
@@ -701,10 +679,12 @@ test "MCP - Actions: click, fill, scroll" {
|
|||||||
const btn_id_str = std.fmt.bufPrint(&btn_id_buf, "{d}", .{btn_id}) catch unreachable;
|
const btn_id_str = std.fmt.bufPrint(&btn_id_buf, "{d}", .{btn_id}) catch unreachable;
|
||||||
const click_msg = try std.mem.concat(aa, u8, &.{ "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"tools/call\",\"params\":{\"name\":\"click\",\"arguments\":{\"backendNodeId\":", btn_id_str, "}}}" });
|
const click_msg = try std.mem.concat(aa, u8, &.{ "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"tools/call\",\"params\":{\"name\":\"click\",\"arguments\":{\"backendNodeId\":", btn_id_str, "}}}" });
|
||||||
try router.handleMessage(server, aa, click_msg);
|
try router.handleMessage(server, aa, click_msg);
|
||||||
try testing.expect(std.mem.indexOf(u8, out_alloc.writer.buffered(), "Clicked element") != null);
|
try testing.expect(std.mem.indexOf(u8, out.written(), "Clicked element") != null);
|
||||||
try testing.expect(std.mem.indexOf(u8, out_alloc.writer.buffered(), "Page url: http://localhost:9582/src/browser/tests/mcp_actions.html") != null);
|
try testing.expect(std.mem.indexOf(u8, out.written(), "Page url: http://localhost:9582/src/browser/tests/mcp_actions.html") != null);
|
||||||
out_alloc.clearRetainingCapacity();
|
out.clearRetainingCapacity();
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
// Test Fill Input
|
// Test Fill Input
|
||||||
const inp = page.document.getElementById("inp", page).?.asNode();
|
const inp = page.document.getElementById("inp", page).?.asNode();
|
||||||
const inp_id = (try server.node_registry.register(inp)).id;
|
const inp_id = (try server.node_registry.register(inp)).id;
|
||||||
@@ -712,10 +692,12 @@ test "MCP - Actions: click, fill, scroll" {
|
|||||||
const inp_id_str = std.fmt.bufPrint(&inp_id_buf, "{d}", .{inp_id}) catch unreachable;
|
const inp_id_str = std.fmt.bufPrint(&inp_id_buf, "{d}", .{inp_id}) catch unreachable;
|
||||||
const fill_msg = try std.mem.concat(aa, u8, &.{ "{\"jsonrpc\":\"2.0\",\"id\":2,\"method\":\"tools/call\",\"params\":{\"name\":\"fill\",\"arguments\":{\"backendNodeId\":", inp_id_str, ",\"text\":\"hello\"}}}" });
|
const fill_msg = try std.mem.concat(aa, u8, &.{ "{\"jsonrpc\":\"2.0\",\"id\":2,\"method\":\"tools/call\",\"params\":{\"name\":\"fill\",\"arguments\":{\"backendNodeId\":", inp_id_str, ",\"text\":\"hello\"}}}" });
|
||||||
try router.handleMessage(server, aa, fill_msg);
|
try router.handleMessage(server, aa, fill_msg);
|
||||||
try testing.expect(std.mem.indexOf(u8, out_alloc.writer.buffered(), "Filled element") != null);
|
try testing.expect(std.mem.indexOf(u8, out.written(), "Filled element") != null);
|
||||||
try testing.expect(std.mem.indexOf(u8, out_alloc.writer.buffered(), "with \\\"hello\\\"") != null);
|
try testing.expect(std.mem.indexOf(u8, out.written(), "with \\\"hello\\\"") != null);
|
||||||
out_alloc.clearRetainingCapacity();
|
out.clearRetainingCapacity();
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
// Test Fill Select
|
// Test Fill Select
|
||||||
const sel = page.document.getElementById("sel", page).?.asNode();
|
const sel = page.document.getElementById("sel", page).?.asNode();
|
||||||
const sel_id = (try server.node_registry.register(sel)).id;
|
const sel_id = (try server.node_registry.register(sel)).id;
|
||||||
@@ -723,10 +705,12 @@ test "MCP - Actions: click, fill, scroll" {
|
|||||||
const sel_id_str = std.fmt.bufPrint(&sel_id_buf, "{d}", .{sel_id}) catch unreachable;
|
const sel_id_str = std.fmt.bufPrint(&sel_id_buf, "{d}", .{sel_id}) catch unreachable;
|
||||||
const fill_sel_msg = try std.mem.concat(aa, u8, &.{ "{\"jsonrpc\":\"2.0\",\"id\":3,\"method\":\"tools/call\",\"params\":{\"name\":\"fill\",\"arguments\":{\"backendNodeId\":", sel_id_str, ",\"text\":\"opt2\"}}}" });
|
const fill_sel_msg = try std.mem.concat(aa, u8, &.{ "{\"jsonrpc\":\"2.0\",\"id\":3,\"method\":\"tools/call\",\"params\":{\"name\":\"fill\",\"arguments\":{\"backendNodeId\":", sel_id_str, ",\"text\":\"opt2\"}}}" });
|
||||||
try router.handleMessage(server, aa, fill_sel_msg);
|
try router.handleMessage(server, aa, fill_sel_msg);
|
||||||
try testing.expect(std.mem.indexOf(u8, out_alloc.writer.buffered(), "Filled element") != null);
|
try testing.expect(std.mem.indexOf(u8, out.written(), "Filled element") != null);
|
||||||
try testing.expect(std.mem.indexOf(u8, out_alloc.writer.buffered(), "with \\\"opt2\\\"") != null);
|
try testing.expect(std.mem.indexOf(u8, out.written(), "with \\\"opt2\\\"") != null);
|
||||||
out_alloc.clearRetainingCapacity();
|
out.clearRetainingCapacity();
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
// Test Scroll
|
// Test Scroll
|
||||||
const scrollbox = page.document.getElementById("scrollbox", page).?.asNode();
|
const scrollbox = page.document.getElementById("scrollbox", page).?.asNode();
|
||||||
const scrollbox_id = (try server.node_registry.register(scrollbox)).id;
|
const scrollbox_id = (try server.node_registry.register(scrollbox)).id;
|
||||||
@@ -734,8 +718,9 @@ test "MCP - Actions: click, fill, scroll" {
|
|||||||
const scroll_id_str = std.fmt.bufPrint(&scroll_id_buf, "{d}", .{scrollbox_id}) catch unreachable;
|
const scroll_id_str = std.fmt.bufPrint(&scroll_id_buf, "{d}", .{scrollbox_id}) catch unreachable;
|
||||||
const scroll_msg = try std.mem.concat(aa, u8, &.{ "{\"jsonrpc\":\"2.0\",\"id\":4,\"method\":\"tools/call\",\"params\":{\"name\":\"scroll\",\"arguments\":{\"backendNodeId\":", scroll_id_str, ",\"y\":50}}}" });
|
const scroll_msg = try std.mem.concat(aa, u8, &.{ "{\"jsonrpc\":\"2.0\",\"id\":4,\"method\":\"tools/call\",\"params\":{\"name\":\"scroll\",\"arguments\":{\"backendNodeId\":", scroll_id_str, ",\"y\":50}}}" });
|
||||||
try router.handleMessage(server, aa, scroll_msg);
|
try router.handleMessage(server, aa, scroll_msg);
|
||||||
try testing.expect(std.mem.indexOf(u8, out_alloc.writer.buffered(), "Scrolled to x: 0, y: 50") != null);
|
try testing.expect(std.mem.indexOf(u8, out.written(), "Scrolled to x: 0, y: 50") != null);
|
||||||
out_alloc.clearRetainingCapacity();
|
out.clearRetainingCapacity();
|
||||||
|
}
|
||||||
|
|
||||||
// Evaluate assertions
|
// Evaluate assertions
|
||||||
var ls: js.Local.Scope = undefined;
|
var ls: js.Local.Scope = undefined;
|
||||||
@@ -746,111 +731,79 @@ test "MCP - Actions: click, fill, scroll" {
|
|||||||
try_catch.init(&ls.local);
|
try_catch.init(&ls.local);
|
||||||
defer try_catch.deinit();
|
defer try_catch.deinit();
|
||||||
|
|
||||||
const result = try ls.local.compileAndRun("window.clicked === true && window.inputVal === 'hello' && window.changed === true && window.selChanged === 'opt2' && window.scrolled === true", null);
|
const result = try ls.local.exec(
|
||||||
|
\\ window.clicked === true && window.inputVal === 'hello' &&
|
||||||
|
\\ window.changed === true && window.selChanged === 'opt2' &&
|
||||||
|
\\ window.scrolled === true
|
||||||
|
, null);
|
||||||
|
|
||||||
try testing.expect(result.isTrue());
|
try testing.expect(result.isTrue());
|
||||||
}
|
}
|
||||||
|
|
||||||
test "MCP - waitForSelector: existing element" {
|
test "MCP - waitForSelector: existing element" {
|
||||||
defer testing.reset();
|
defer testing.reset();
|
||||||
const allocator = testing.allocator;
|
var out: std.io.Writer.Allocating = .init(testing.arena_allocator);
|
||||||
const app = testing.test_app;
|
const server = try testLoadPage(
|
||||||
|
"http://localhost:9582/src/browser/tests/mcp_wait_for_selector.html",
|
||||||
var out_alloc: std.io.Writer.Allocating = .init(testing.arena_allocator);
|
&out.writer,
|
||||||
defer out_alloc.deinit();
|
);
|
||||||
|
|
||||||
var server = try Server.init(allocator, app, &out_alloc.writer);
|
|
||||||
defer server.deinit();
|
defer server.deinit();
|
||||||
|
|
||||||
const aa = testing.arena_allocator;
|
|
||||||
const page = try server.session.createPage();
|
|
||||||
const url = "http://localhost:9582/src/browser/tests/mcp_wait_for_selector.html";
|
|
||||||
try page.navigate(url, .{ .reason = .address_bar, .kind = .{ .push = null } });
|
|
||||||
var runner = try server.session.runner(.{});
|
|
||||||
try runner.wait(.{ .ms = 2000 });
|
|
||||||
|
|
||||||
// waitForSelector on an element that already exists returns immediately
|
// waitForSelector on an element that already exists returns immediately
|
||||||
const msg =
|
const msg =
|
||||||
\\{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"waitForSelector","arguments":{"selector":"#existing","timeout":2000}}}
|
\\{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"waitForSelector","arguments":{"selector":"#existing","timeout":2000}}}
|
||||||
;
|
;
|
||||||
try router.handleMessage(server, aa, msg);
|
try router.handleMessage(server, testing.arena_allocator, msg);
|
||||||
|
|
||||||
try testing.expectJson(
|
try testing.expectJson(.{ .id = 1, .result = .{ .content = &.{.{ .type = "text" }} } }, out.written());
|
||||||
\\{
|
|
||||||
\\ "id": 1,
|
|
||||||
\\ "result": {
|
|
||||||
\\ "content": [
|
|
||||||
\\ { "type": "text" }
|
|
||||||
\\ ]
|
|
||||||
\\ }
|
|
||||||
\\}
|
|
||||||
, out_alloc.writer.buffered());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
test "MCP - waitForSelector: delayed element" {
|
test "MCP - waitForSelector: delayed element" {
|
||||||
defer testing.reset();
|
defer testing.reset();
|
||||||
const allocator = testing.allocator;
|
var out: std.io.Writer.Allocating = .init(testing.arena_allocator);
|
||||||
const app = testing.test_app;
|
const server = try testLoadPage(
|
||||||
|
"http://localhost:9582/src/browser/tests/mcp_wait_for_selector.html",
|
||||||
var out_alloc: std.io.Writer.Allocating = .init(testing.arena_allocator);
|
&out.writer,
|
||||||
defer out_alloc.deinit();
|
);
|
||||||
|
|
||||||
var server = try Server.init(allocator, app, &out_alloc.writer);
|
|
||||||
defer server.deinit();
|
defer server.deinit();
|
||||||
|
|
||||||
const aa = testing.arena_allocator;
|
|
||||||
const page = try server.session.createPage();
|
|
||||||
const url = "http://localhost:9582/src/browser/tests/mcp_wait_for_selector.html";
|
|
||||||
try page.navigate(url, .{ .reason = .address_bar, .kind = .{ .push = null } });
|
|
||||||
var runner = try server.session.runner(.{});
|
|
||||||
try runner.wait(.{ .ms = 2000 });
|
|
||||||
|
|
||||||
// waitForSelector on an element added after 200ms via setTimeout
|
// waitForSelector on an element added after 200ms via setTimeout
|
||||||
const msg =
|
const msg =
|
||||||
\\{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"waitForSelector","arguments":{"selector":"#delayed","timeout":5000}}}
|
\\{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"waitForSelector","arguments":{"selector":"#delayed","timeout":5000}}}
|
||||||
;
|
;
|
||||||
try router.handleMessage(server, aa, msg);
|
try router.handleMessage(server, testing.arena_allocator, msg);
|
||||||
|
|
||||||
try testing.expectJson(
|
try testing.expectJson(.{ .id = 1, .result = .{ .content = &.{.{ .type = "text" }} } }, out.written());
|
||||||
\\{
|
|
||||||
\\ "id": 1,
|
|
||||||
\\ "result": {
|
|
||||||
\\ "content": [
|
|
||||||
\\ { "type": "text" }
|
|
||||||
\\ ]
|
|
||||||
\\ }
|
|
||||||
\\}
|
|
||||||
, out_alloc.writer.buffered());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
test "MCP - waitForSelector: timeout" {
|
test "MCP - waitForSelector: timeout" {
|
||||||
defer testing.reset();
|
defer testing.reset();
|
||||||
const allocator = testing.allocator;
|
var out: std.io.Writer.Allocating = .init(testing.arena_allocator);
|
||||||
const app = testing.test_app;
|
const server = try testLoadPage(
|
||||||
|
"http://localhost:9582/src/browser/tests/mcp_wait_for_selector.html",
|
||||||
var out_alloc: std.io.Writer.Allocating = .init(testing.arena_allocator);
|
&out.writer,
|
||||||
defer out_alloc.deinit();
|
);
|
||||||
|
|
||||||
var server = try Server.init(allocator, app, &out_alloc.writer);
|
|
||||||
defer server.deinit();
|
defer server.deinit();
|
||||||
|
|
||||||
const aa = testing.arena_allocator;
|
|
||||||
const page = try server.session.createPage();
|
|
||||||
const url = "http://localhost:9582/src/browser/tests/mcp_wait_for_selector.html";
|
|
||||||
try page.navigate(url, .{ .reason = .address_bar, .kind = .{ .push = null } });
|
|
||||||
var runner = try server.session.runner(.{});
|
|
||||||
try runner.wait(.{ .ms = 2000 });
|
|
||||||
|
|
||||||
// waitForSelector with a short timeout on a non-existent element should error
|
// waitForSelector with a short timeout on a non-existent element should error
|
||||||
const msg =
|
const msg =
|
||||||
\\{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"waitForSelector","arguments":{"selector":"#nonexistent","timeout":100}}}
|
\\{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"waitForSelector","arguments":{"selector":"#nonexistent","timeout":100}}}
|
||||||
;
|
;
|
||||||
try router.handleMessage(server, aa, msg);
|
try router.handleMessage(server, testing.arena_allocator, msg);
|
||||||
|
try testing.expectJson(.{
|
||||||
try testing.expectJson(
|
.id = 1,
|
||||||
\\{
|
.@"error" = struct {}{},
|
||||||
\\ "id": 1,
|
}, out.written());
|
||||||
\\ "error": {}
|
}
|
||||||
\\}
|
|
||||||
, out_alloc.writer.buffered());
|
fn testLoadPage(url: [:0]const u8, writer: *std.Io.Writer) !*Server {
|
||||||
|
var server = try Server.init(testing.allocator, testing.test_app, writer);
|
||||||
|
errdefer server.deinit();
|
||||||
|
|
||||||
|
const page = try server.session.createPage();
|
||||||
|
try page.navigate(url, .{});
|
||||||
|
|
||||||
|
var runner = try server.session.runner(.{});
|
||||||
|
try runner.wait(.{ .ms = 2000 });
|
||||||
|
return server;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user