mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-10-10 13:22:09 -04:00
Merge pull request #3088 from lightpanda-io/improve-test-output-and-logs
tests: improve test output and test log handling
This commit is contained in:
44 files changed
+127
-256
No files matched your search
@@ -119,16 +119,8 @@ run-debug: build-dev
|
||||
@printf "\033[36mRunning...\033[0m\n"
|
||||
@./zig-out/bin/lightpanda || (printf "\033[33mRun ERROR\033[0m\n"; exit 1;)
|
||||
|
||||
## Test - `grep` is used to filter out the huge compile command on build
|
||||
ifeq ($(OS), macos)
|
||||
test:
|
||||
@script -q /dev/null sh -c 'TEST_FILTER="${F}" $(ZIG) build $(ZIGFLAGS) test -freference-trace' 2>&1 \
|
||||
| grep --line-buffered -v "^/.*zig test -freference-trace"
|
||||
else
|
||||
test:
|
||||
@script -qec 'TEST_FILTER="${F}" $(ZIG) build $(ZIGFLAGS) test -freference-trace' /dev/null 2>&1 \
|
||||
| grep --line-buffered -v "^/.*zig test -freference-trace"
|
||||
endif
|
||||
TEST_FILTER="${F}" $(ZIG) build $(ZIGFLAGS) test -freference-trace
|
||||
|
||||
## Run demo/runner end to end tests
|
||||
end2end:
|
||||
|
||||
@@ -730,8 +730,6 @@ pub fn getNodeDetails(
|
||||
const testing = @import("testing.zig");
|
||||
|
||||
test "SemanticTree backendDOMNodeId" {
|
||||
defer testing.reset();
|
||||
|
||||
var registry: CDPNode.Registry = .init(testing.allocator);
|
||||
defer registry.deinit();
|
||||
|
||||
@@ -756,7 +754,6 @@ test "SemanticTree backendDOMNodeId" {
|
||||
}
|
||||
|
||||
test "SemanticTree max_depth" {
|
||||
defer testing.reset();
|
||||
var registry: CDPNode.Registry = .init(testing.allocator);
|
||||
defer registry.deinit();
|
||||
|
||||
|
||||
+2
-4
@@ -331,8 +331,7 @@ test "server: buildJSONVersionResponse" {
|
||||
}
|
||||
|
||||
test "Client: http invalid request" {
|
||||
const filter: testing.LogFilter = .init(&.{.cdp});
|
||||
defer filter.deinit();
|
||||
testing.silenceLog(&.{.cdp});
|
||||
|
||||
var c = try createTestClient();
|
||||
defer c.deinit();
|
||||
@@ -449,8 +448,7 @@ test "Client: read invalid websocket message" {
|
||||
}
|
||||
|
||||
{
|
||||
const filter: testing.LogFilter = .init(&.{.cdp});
|
||||
defer filter.deinit();
|
||||
testing.expectLog(&.{.cdp});
|
||||
// length of message is 0, 0, 0, 0, 0, 16, 0, 1 i.e: 1024 * 1024 + 1
|
||||
try assertWebSocketError(1009, &.{ 129, 255, 0, 0, 0, 0, 0, 16, 0, 1, 'm', 'a', 's', 'k' });
|
||||
}
|
||||
|
||||
@@ -3460,8 +3460,7 @@ test "Frame: urlBasename" {
|
||||
}
|
||||
|
||||
test "WebApi: Frame" {
|
||||
const filter: testing.LogFilter = .init(&.{.http});
|
||||
defer filter.deinit();
|
||||
testing.silenceLog(&.{.http});
|
||||
try testing.htmlRunner("page", .{});
|
||||
}
|
||||
|
||||
@@ -3470,8 +3469,7 @@ test "WebApi: Frames" {
|
||||
}
|
||||
|
||||
test "WebApi: Frame Blob" {
|
||||
const filter: testing.LogFilter = .init(&.{ .frame, .browser, .js });
|
||||
defer filter.deinit();
|
||||
testing.silenceLog(&.{ .frame, .browser, .js });
|
||||
try testing.htmlRunner("frames/blob", .{});
|
||||
}
|
||||
|
||||
@@ -3524,6 +3522,8 @@ test "Page: isSameOrigin" {
|
||||
}
|
||||
|
||||
test "Frame: httpMetadata after navigation" {
|
||||
testing.expectLog(&.{.http});
|
||||
|
||||
const page = try testing.pageTest("page/meta.html", .{});
|
||||
defer page.close();
|
||||
|
||||
@@ -3544,8 +3544,6 @@ test "Frame: httpMetadata 404" {
|
||||
}
|
||||
|
||||
test "Frame: 401" {
|
||||
defer testing.reset();
|
||||
|
||||
var page = try testing.pageTest("401", .{});
|
||||
defer page.close();
|
||||
|
||||
|
||||
@@ -360,8 +360,6 @@ fn isSpecialUrl(url: []const u8) bool {
|
||||
|
||||
const testing = @import("../testing.zig");
|
||||
test "ImportMap: exact match" {
|
||||
defer testing.reset();
|
||||
|
||||
const im = try testParse(
|
||||
\\{ "imports": { "moment": "/node_modules/moment/index.js" } }
|
||||
, "https://example.com/app/index.html");
|
||||
@@ -371,8 +369,6 @@ test "ImportMap: exact match" {
|
||||
}
|
||||
|
||||
test "ImportMap: trailing slash prefix match" {
|
||||
defer testing.reset();
|
||||
|
||||
const im = try testParse(
|
||||
\\{ "imports": { "moment/": "/node_modules/moment/src/" } }
|
||||
, "https://example.com/app/index.html");
|
||||
@@ -382,8 +378,6 @@ test "ImportMap: trailing slash prefix match" {
|
||||
}
|
||||
|
||||
test "ImportMap: specificity — longest match wins" {
|
||||
defer testing.reset();
|
||||
|
||||
const im = try testParse(
|
||||
\\{ "imports": {
|
||||
\\ "a": "/1",
|
||||
@@ -413,8 +407,6 @@ test "ImportMap: specificity — longest match wins" {
|
||||
}
|
||||
|
||||
test "ImportMap: scopes — most specific scope wins" {
|
||||
defer testing.reset();
|
||||
|
||||
const im = try testParse(
|
||||
\\{
|
||||
\\ "imports": { "a": "/a-1.mjs", "b": "/b-1.mjs", "d": "/d-1.mjs" },
|
||||
@@ -444,8 +436,6 @@ test "ImportMap: scopes — most specific scope wins" {
|
||||
}
|
||||
|
||||
test "ImportMap: bare specifier with no match returns null" {
|
||||
defer testing.reset();
|
||||
|
||||
const im = try testParse(
|
||||
\\{ "imports": { "moment": "/m.js" } }
|
||||
, "https://example.com/app/index.html");
|
||||
@@ -455,8 +445,6 @@ test "ImportMap: bare specifier with no match returns null" {
|
||||
}
|
||||
|
||||
test "ImportMap: URL-like specifier falls back to itself" {
|
||||
defer testing.reset();
|
||||
|
||||
const im: ImportMap = .empty;
|
||||
|
||||
const r = try testResolve(&im, "https://example.com/app.mjs", "./foo.js");
|
||||
@@ -464,8 +452,6 @@ test "ImportMap: URL-like specifier falls back to itself" {
|
||||
}
|
||||
|
||||
test "ImportMap: null entry throws (no fallback)" {
|
||||
defer testing.reset();
|
||||
|
||||
const im = try testParse(
|
||||
\\{ "imports": { "blocked": null } }
|
||||
, "https://example.com/app/index.html");
|
||||
@@ -474,8 +460,6 @@ test "ImportMap: null entry throws (no fallback)" {
|
||||
}
|
||||
|
||||
test "ImportMap: backtracking out of prefix throws" {
|
||||
defer testing.reset();
|
||||
|
||||
const im = try testParse(
|
||||
\\{ "imports": { "moment/": "/node_modules/moment/src/" } }
|
||||
, "https://example.com/app/index.html");
|
||||
@@ -484,7 +468,6 @@ test "ImportMap: backtracking out of prefix throws" {
|
||||
}
|
||||
|
||||
test "ImportMap: merge — first-wins on imports, new keys added" {
|
||||
defer testing.reset();
|
||||
const base: [:0]const u8 = "https://example.com/app/index.html";
|
||||
|
||||
var im = try testParse(
|
||||
@@ -504,7 +487,6 @@ test "ImportMap: merge — first-wins on imports, new keys added" {
|
||||
}
|
||||
|
||||
test "ImportMap: merge — same-prefix scopes merge their imports" {
|
||||
defer testing.reset();
|
||||
const base: [:0]const u8 = "https://example.com/app/index.html";
|
||||
|
||||
var im = try testParse(
|
||||
|
||||
@@ -810,8 +810,6 @@ fn trimRight(s: []const u8) []const u8 {
|
||||
|
||||
const testing = @import("../testing.zig");
|
||||
test "Mime: invalid" {
|
||||
defer testing.reset();
|
||||
|
||||
const invalids = [_][]const u8{
|
||||
"",
|
||||
"text",
|
||||
@@ -828,7 +826,6 @@ test "Mime: invalid" {
|
||||
}
|
||||
|
||||
test "Mime: malformed parameters are ignored" {
|
||||
defer testing.reset();
|
||||
|
||||
// These should all parse successfully as text/html with malformed params ignored
|
||||
const valid_with_malformed_params = [_][]const u8{
|
||||
@@ -851,8 +848,6 @@ test "Mime: malformed parameters are ignored" {
|
||||
}
|
||||
|
||||
test "Mime: parse common" {
|
||||
defer testing.reset();
|
||||
|
||||
try expect(.{ .content_type = .{ .text_xml = {} } }, "text/xml");
|
||||
try expect(.{ .content_type = .{ .text_html = {} } }, "text/html");
|
||||
try expect(.{ .content_type = .{ .text_plain = {} } }, "text/plain");
|
||||
@@ -888,8 +883,6 @@ test "Mime: parse common" {
|
||||
}
|
||||
|
||||
test "Mime: parse uncommon" {
|
||||
defer testing.reset();
|
||||
|
||||
const text_csv = Expectation{
|
||||
.content_type = .{ .other = {} },
|
||||
};
|
||||
@@ -902,8 +895,6 @@ test "Mime: parse uncommon" {
|
||||
}
|
||||
|
||||
test "Mime: parse charset" {
|
||||
defer testing.reset();
|
||||
|
||||
try expect(.{
|
||||
.content_type = .{ .text_xml = {} },
|
||||
.charset = "utf-8",
|
||||
@@ -936,7 +927,6 @@ test "Mime: parse charset" {
|
||||
}
|
||||
|
||||
test "Mime: parse charset (WHATWG parameter semantics)" {
|
||||
defer testing.reset();
|
||||
|
||||
// First charset wins (not last).
|
||||
try expect(.{ .content_type = .{ .text_html = {} }, .charset = "gbk" }, "text/html;charset=gbk;charset=utf-8");
|
||||
@@ -959,8 +949,6 @@ test "Mime: parse charset (WHATWG parameter semantics)" {
|
||||
}
|
||||
|
||||
test "Mime: isHTML" {
|
||||
defer testing.reset();
|
||||
|
||||
const assert = struct {
|
||||
fn assert(expected: bool, input: []const u8) !void {
|
||||
const mutable_input = try testing.arena_allocator.dupe(u8, input);
|
||||
@@ -977,8 +965,6 @@ test "Mime: isHTML" {
|
||||
}
|
||||
|
||||
test "Mime: isXML" {
|
||||
defer testing.reset();
|
||||
|
||||
const assert = struct {
|
||||
fn assert(expected: bool, input: []const u8) !void {
|
||||
const mutable_input = try testing.arena_allocator.dupe(u8, input);
|
||||
@@ -1105,7 +1091,6 @@ fn expect(expected: Expectation, input: []const u8) !void {
|
||||
}
|
||||
|
||||
test "Mime: serialize" {
|
||||
defer testing.reset();
|
||||
const arena = testing.arena_allocator;
|
||||
|
||||
const expectSerialize = struct {
|
||||
|
||||
@@ -407,7 +407,8 @@ fn appendFrameExecutions(frame: *Frame, origin: []const u8, arena: Allocator, li
|
||||
const testing = @import("../testing.zig");
|
||||
|
||||
test "Page: js_error_count" {
|
||||
defer testing.reset();
|
||||
testing.expectLog(&.{ .js, .js, .js });
|
||||
|
||||
// One uncaught top-level script exception, one uncaught timer-callback
|
||||
// exception.
|
||||
const page = try testing.pageTest("page_js_error.html", .{});
|
||||
|
||||
@@ -474,7 +474,6 @@ test "Runner: waitForSelector timeout" {
|
||||
}
|
||||
|
||||
test "Runner: waitForSelector" {
|
||||
defer testing.reset();
|
||||
const page = try testing.pageTest("runner/runner1.html", .{});
|
||||
|
||||
var runner = page.session.runner(.{});
|
||||
|
||||
@@ -566,7 +566,6 @@ const PreloadedScript = struct {
|
||||
const testing = @import("../testing.zig");
|
||||
|
||||
test "ScriptManager: PreloadedScript.shutdownCallback drops a .loading preload" {
|
||||
defer testing.reset();
|
||||
const page = try testing.pageTest("mcp_nav.html", .{});
|
||||
defer page.close();
|
||||
|
||||
@@ -598,7 +597,6 @@ test "ScriptManager: PreloadedScript.shutdownCallback drops a .loading preload"
|
||||
}
|
||||
|
||||
test "ScriptManager: waitForPreload stops when teardown is pending" {
|
||||
defer testing.reset();
|
||||
const page = try testing.pageTest("mcp_nav.html", .{});
|
||||
defer page.close();
|
||||
|
||||
|
||||
@@ -1080,7 +1080,6 @@ pub const ImportedModule = struct {
|
||||
const testing = @import("../testing.zig");
|
||||
|
||||
test "ScriptManagerBase: shutdownCallback fails a .loading module" {
|
||||
defer testing.reset();
|
||||
const page = try testing.pageTest("mcp_nav.html", .{});
|
||||
defer page.close();
|
||||
const frame = page.frame().?;
|
||||
@@ -1115,7 +1114,6 @@ test "ScriptManagerBase: shutdownCallback fails a .loading module" {
|
||||
}
|
||||
|
||||
test "ScriptManagerBase: waitForImport stops when teardown is pending" {
|
||||
defer testing.reset();
|
||||
const page = try testing.pageTest("mcp_nav.html", .{});
|
||||
defer page.close();
|
||||
const frame = page.frame().?;
|
||||
|
||||
@@ -213,8 +213,6 @@ fn isFirstMatch(self: SelectorPath, target: *Element, candidate: []const u8) boo
|
||||
const testing = @import("../testing.zig");
|
||||
|
||||
fn expectSelector(comptime selector: []const u8, comptime expected: []const u8) !void {
|
||||
defer testing.reset();
|
||||
|
||||
var page = try testing.pageTest("selector_path.html", .{});
|
||||
defer page.close();
|
||||
const frame = page.frame().?;
|
||||
|
||||
@@ -692,8 +692,6 @@ test "URL: isCompleteHTTPUrl" {
|
||||
}
|
||||
|
||||
test "URL: resolve regression (#1093)" {
|
||||
defer testing.reset();
|
||||
|
||||
const Case = struct {
|
||||
base: [:0]const u8,
|
||||
path: [:0]const u8,
|
||||
@@ -715,8 +713,6 @@ test "URL: resolve regression (#1093)" {
|
||||
}
|
||||
|
||||
test "URL: resolve" {
|
||||
defer testing.reset();
|
||||
|
||||
const Case = struct {
|
||||
base: [:0]const u8,
|
||||
path: [:0]const u8,
|
||||
@@ -961,8 +957,6 @@ test "URL: resolve" {
|
||||
}
|
||||
|
||||
test "URL: resolve strips tab and newline from input" {
|
||||
defer testing.reset();
|
||||
|
||||
const Case = struct {
|
||||
base: [:0]const u8,
|
||||
path: [:0]const u8,
|
||||
@@ -994,7 +988,6 @@ test "URL: resolve strips tab and newline from input" {
|
||||
}
|
||||
|
||||
test "URL: resolve validates ASCII punycode (xn--) labels" {
|
||||
defer testing.reset();
|
||||
|
||||
// Valid punycode is left untouched.
|
||||
const ok = try resolve(testing.arena_allocator, "https://example.com/", "https://xn--rksmrgs-5wao1o.se/x", .{});
|
||||
@@ -1006,8 +999,6 @@ test "URL: resolve validates ASCII punycode (xn--) labels" {
|
||||
}
|
||||
|
||||
test "URL: resolve with encoding" {
|
||||
defer testing.reset();
|
||||
|
||||
const Case = struct {
|
||||
base: [:0]const u8,
|
||||
path: [:0]const u8,
|
||||
@@ -1176,7 +1167,6 @@ test "URL: resolve with encoding" {
|
||||
}
|
||||
|
||||
test "URL: eqlDocument" {
|
||||
defer testing.reset();
|
||||
{
|
||||
const url = "https://lightpanda.io/about";
|
||||
try testing.expectEqual(true, eqlDocument(url, url));
|
||||
@@ -1244,7 +1234,6 @@ test "URL: eqlDocument" {
|
||||
}
|
||||
|
||||
test "URL: concatQueryString" {
|
||||
defer testing.reset();
|
||||
const arena = testing.arena_allocator;
|
||||
|
||||
{
|
||||
@@ -1274,7 +1263,6 @@ test "URL: concatQueryString" {
|
||||
}
|
||||
|
||||
test "URL: getRobotsUrl" {
|
||||
defer testing.reset();
|
||||
const arena = testing.arena_allocator;
|
||||
|
||||
{
|
||||
@@ -1302,7 +1290,6 @@ test "URL: getRobotsUrl" {
|
||||
}
|
||||
|
||||
test "URL: unescape" {
|
||||
defer testing.reset();
|
||||
const arena = testing.arena_allocator;
|
||||
|
||||
{
|
||||
@@ -1437,8 +1424,6 @@ test "URL: setPathname percent-encodes" {
|
||||
}
|
||||
|
||||
test "URL: getOrigin" {
|
||||
defer testing.reset();
|
||||
|
||||
const Case = struct {
|
||||
url: [:0]const u8,
|
||||
expected: ?[]const u8,
|
||||
@@ -1680,8 +1665,6 @@ test "URL: resolve path scheme" {
|
||||
}
|
||||
|
||||
test "URL: resolveNavigation defaults a schemeless host to http (curl-like)" {
|
||||
defer testing.reset();
|
||||
|
||||
const Case = struct {
|
||||
url: [:0]const u8,
|
||||
expected: [:0]const u8,
|
||||
|
||||
@@ -72,28 +72,24 @@ fn base64Decode(arena: Allocator, input: []const u8) ![]const u8 {
|
||||
|
||||
const testing = @import("../testing.zig");
|
||||
test "data_url: plain text, default content-type" {
|
||||
defer testing.reset();
|
||||
const r = try parse(testing.arena_allocator, "data:,Hello%2C%20World");
|
||||
try testing.expectString("text/plain;charset=US-ASCII", r.content_type);
|
||||
try testing.expectString("Hello, World", r.body);
|
||||
}
|
||||
|
||||
test "data_url: explicit mediatype" {
|
||||
defer testing.reset();
|
||||
const r = try parse(testing.arena_allocator, "data:text/html,<b>hi</b>");
|
||||
try testing.expectString("text/html", r.content_type);
|
||||
try testing.expectString("<b>hi</b>", r.body);
|
||||
}
|
||||
|
||||
test "data_url: base64" {
|
||||
defer testing.reset();
|
||||
const r = try parse(testing.arena_allocator, "data:text/plain;base64,SGVsbG8=");
|
||||
try testing.expectString("text/plain", r.content_type);
|
||||
try testing.expectString("Hello", r.body);
|
||||
}
|
||||
|
||||
test "data_url: base64 without padding decodes (forgiving)" {
|
||||
defer testing.reset();
|
||||
const r = try parse(testing.arena_allocator, "data:application/octet-stream;base64,SGVsbG8");
|
||||
try testing.expectString("Hello", r.body);
|
||||
|
||||
@@ -106,7 +102,6 @@ test "data_url: base64 without padding decodes (forgiving)" {
|
||||
}
|
||||
|
||||
test "data_url: forgiving-base64 rejects misplaced/over-padding" {
|
||||
defer testing.reset();
|
||||
const arena = testing.arena_allocator;
|
||||
try std.testing.expectError(error.InvalidBase64, parse(arena, "data:;base64,abcd=")); // len % 4 == 1
|
||||
try std.testing.expectError(error.InvalidBase64, parse(arena, "data:;base64,="));
|
||||
@@ -115,18 +110,15 @@ test "data_url: forgiving-base64 rejects misplaced/over-padding" {
|
||||
}
|
||||
|
||||
test "data_url: bare charset gets text/plain prefix" {
|
||||
defer testing.reset();
|
||||
const r = try parse(testing.arena_allocator, "data:;charset=utf-8,x");
|
||||
try testing.expectString("text/plain;charset=utf-8", r.content_type);
|
||||
}
|
||||
|
||||
test "data_url: empty body" {
|
||||
defer testing.reset();
|
||||
const r = try parse(testing.arena_allocator, "data:text/plain,");
|
||||
try testing.expectString("", r.body);
|
||||
}
|
||||
|
||||
test "data_url: missing comma is an error" {
|
||||
defer testing.reset();
|
||||
try std.testing.expectError(error.InvalidDataUrl, parse(testing.arena_allocator, "data:text/plain"));
|
||||
}
|
||||
@@ -382,7 +382,6 @@ const testing = @import("../testing.zig");
|
||||
// <base> element), so reusing one frame across opts would leak that mutation
|
||||
// into later dumps.
|
||||
fn expectDump(opts: Opts, expected: []const u8) !void {
|
||||
defer testing.reset();
|
||||
var page = try testing.pageTest("dump.html", .{});
|
||||
defer page.close();
|
||||
|
||||
|
||||
@@ -288,7 +288,6 @@ fn testForms(html: []const u8) ![]FormInfo {
|
||||
}
|
||||
|
||||
test "browser.forms: login form" {
|
||||
defer testing.reset();
|
||||
const forms = try testForms(
|
||||
\\<form action="/login" method="POST">
|
||||
\\ <input type="email" name="email" required placeholder="Email">
|
||||
@@ -310,7 +309,6 @@ test "browser.forms: login form" {
|
||||
}
|
||||
|
||||
test "browser.forms: form with select" {
|
||||
defer testing.reset();
|
||||
const forms = try testForms(
|
||||
\\<form>
|
||||
\\ <select name="color">
|
||||
@@ -330,7 +328,6 @@ test "browser.forms: form with select" {
|
||||
}
|
||||
|
||||
test "browser.forms: form with textarea" {
|
||||
defer testing.reset();
|
||||
const forms = try testForms(
|
||||
\\<form method="POST">
|
||||
\\ <textarea name="message" placeholder="Your message"></textarea>
|
||||
@@ -345,7 +342,6 @@ test "browser.forms: form with textarea" {
|
||||
}
|
||||
|
||||
test "browser.forms: empty form skipped" {
|
||||
defer testing.reset();
|
||||
const forms = try testForms(
|
||||
\\<form action="/empty">
|
||||
\\ <p>No fields here</p>
|
||||
@@ -357,7 +353,6 @@ test "browser.forms: empty form skipped" {
|
||||
}
|
||||
|
||||
test "browser.forms: hidden inputs excluded" {
|
||||
defer testing.reset();
|
||||
const forms = try testForms(
|
||||
\\<form>
|
||||
\\ <input type="hidden" name="csrf" value="token123">
|
||||
@@ -372,7 +367,6 @@ test "browser.forms: hidden inputs excluded" {
|
||||
}
|
||||
|
||||
test "browser.forms: multiple forms" {
|
||||
defer testing.reset();
|
||||
const forms = try testForms(
|
||||
\\<form action="/search" method="GET">
|
||||
\\ <input type="text" name="q" placeholder="Search">
|
||||
@@ -390,7 +384,6 @@ test "browser.forms: multiple forms" {
|
||||
}
|
||||
|
||||
test "browser.forms: disabled fields flagged" {
|
||||
defer testing.reset();
|
||||
const forms = try testForms(
|
||||
\\<form>
|
||||
\\ <input type="text" name="enabled_field">
|
||||
@@ -406,7 +399,6 @@ test "browser.forms: disabled fields flagged" {
|
||||
}
|
||||
|
||||
test "browser.forms: disabled fieldset" {
|
||||
defer testing.reset();
|
||||
const forms = try testForms(
|
||||
\\<form>
|
||||
\\ <fieldset disabled>
|
||||
@@ -424,7 +416,6 @@ test "browser.forms: disabled fieldset" {
|
||||
}
|
||||
|
||||
test "browser.forms: external field via form attribute" {
|
||||
defer testing.reset();
|
||||
const forms = try testForms(
|
||||
\\<input type="text" name="external" form="myform">
|
||||
\\<form id="myform" action="/submit">
|
||||
@@ -438,7 +429,6 @@ test "browser.forms: external field via form attribute" {
|
||||
}
|
||||
|
||||
test "browser.forms: checkbox and radio return value attribute" {
|
||||
defer testing.reset();
|
||||
const forms = try testForms(
|
||||
\\<form>
|
||||
\\ <input type="checkbox" name="agree" value="yes" checked>
|
||||
@@ -456,7 +446,6 @@ test "browser.forms: checkbox and radio return value attribute" {
|
||||
}
|
||||
|
||||
test "browser.forms: form without action or method" {
|
||||
defer testing.reset();
|
||||
const forms = try testForms(
|
||||
\\<form>
|
||||
\\ <input type="text" name="q">
|
||||
|
||||
@@ -155,8 +155,6 @@ fn hasNonRemoteScheme(href: []const u8) bool {
|
||||
const testing = @import("../../testing.zig");
|
||||
|
||||
test "preload: prescan" {
|
||||
defer testing.reset();
|
||||
|
||||
const page = try testing.pageTest("mcp_nav.html", .{});
|
||||
defer page.close();
|
||||
const frame = page.frame().?;
|
||||
|
||||
@@ -290,8 +290,7 @@ pub const JsApi = struct {
|
||||
|
||||
const testing = @import("../../testing.zig");
|
||||
test "WebApi: EventTarget" {
|
||||
const filter: testing.LogFilter = .init(&.{ .js, .event });
|
||||
defer filter.deinit();
|
||||
testing.silenceLog(&.{ .js, .event });
|
||||
|
||||
// we create thousands of these per frame. Nothing should bloat it.
|
||||
try testing.expectEqual(16, @sizeOf(EventTarget));
|
||||
|
||||
@@ -139,7 +139,6 @@ pub const JsApi = struct {
|
||||
|
||||
const testing = @import("../../testing.zig");
|
||||
test "WebApi: SharedWorker" {
|
||||
const filter: testing.LogFilter = .init(&.{.http});
|
||||
defer filter.deinit();
|
||||
testing.silenceLog(&.{.http});
|
||||
try testing.htmlRunner("shared_worker", .{ .timeout_ms = 8000 });
|
||||
}
|
||||
@@ -489,8 +489,7 @@ pub const JsApi = struct {
|
||||
|
||||
const testing = @import("../../testing.zig");
|
||||
test "WebApi: Worker" {
|
||||
const filter: testing.LogFilter = .init(&.{.http});
|
||||
defer filter.deinit();
|
||||
testing.silenceLog(&.{.http});
|
||||
|
||||
// Worker tests chain a worker-script fetch with a dynamic-import fetch
|
||||
// and a cross-context postMessage. The default 2 s assertion budget can
|
||||
|
||||
@@ -946,7 +946,6 @@ test "normalizePropertyValue: collapse duplicate two-value shorthands" {
|
||||
}
|
||||
|
||||
test "normalizePropertyValue: anchor() canonical order" {
|
||||
defer testing.reset();
|
||||
const cases = .{
|
||||
// Dashed ident should come before keyword
|
||||
.{ "left", "anchor(left --foo)", "anchor(--foo left)" },
|
||||
|
||||
@@ -216,8 +216,7 @@ pub const JsApi = struct {
|
||||
|
||||
const testing = @import("../../../testing.zig");
|
||||
test "WebApi: CSSStyleSheet" {
|
||||
const filter: testing.LogFilter = .init(&.{.js});
|
||||
defer filter.deinit();
|
||||
testing.silenceLog(&.{.js});
|
||||
try testing.htmlRunner("css/stylesheet.html", .{});
|
||||
}
|
||||
|
||||
|
||||
@@ -157,5 +157,6 @@ pub const JsApi = struct {
|
||||
|
||||
const testing = @import("../../../testing.zig");
|
||||
test "WebApi: Svg" {
|
||||
testing.expectLog(&.{ .not_implemented, .not_implemented });
|
||||
try testing.htmlRunner("element/svg", .{});
|
||||
}
|
||||
@@ -297,8 +297,7 @@ test "WebApi: HTML.Link" {
|
||||
}
|
||||
|
||||
test "WebApi: HTML.Link external stylesheet" {
|
||||
const filter: testing.LogFilter = .init(&.{.http});
|
||||
defer filter.deinit();
|
||||
testing.silenceLog(&.{.http});
|
||||
try testing.htmlRunner("css/external_stylesheet.html", .{ .load_external_stylesheets = true });
|
||||
}
|
||||
|
||||
@@ -307,7 +306,6 @@ test "WebApi: HTML.Link external stylesheet" {
|
||||
// request gate during the sync window). Otherwise the deferred-script queue
|
||||
// never drains and the document is stuck at readyState "loading".
|
||||
test "WebApi: HTML.Link deferred script then external stylesheet" {
|
||||
const filter: testing.LogFilter = .init(&.{.http});
|
||||
defer filter.deinit();
|
||||
testing.silenceLog(&.{.http});
|
||||
try testing.htmlRunner("css/deferred_script_then_stylesheet.html", .{ .load_external_stylesheets = true });
|
||||
}
|
||||
@@ -194,7 +194,7 @@ pub const Build = struct {
|
||||
|
||||
const testing = @import("../../../../testing.zig");
|
||||
test "WebApi: Script" {
|
||||
const filter: testing.LogFilter = .init(&.{.http});
|
||||
defer filter.deinit();
|
||||
testing.silenceLog(&.{.http});
|
||||
testing.expectLog(&.{ .js, .js });
|
||||
try testing.htmlRunner("element/html/script", .{});
|
||||
}
|
||||
@@ -650,13 +650,11 @@ pub const JsApi = struct {
|
||||
|
||||
const testing = @import("../../../testing.zig");
|
||||
test "WebApi: EventSource" {
|
||||
const filter: testing.LogFilter = .init(&.{.http});
|
||||
defer filter.deinit();
|
||||
testing.silenceLog(&.{.http});
|
||||
try testing.htmlRunner("net/eventsource.html", .{});
|
||||
}
|
||||
|
||||
test "WebApi: EventSource in worker" {
|
||||
const filter: testing.LogFilter = .init(&.{.http});
|
||||
defer filter.deinit();
|
||||
testing.silenceLog(&.{.http});
|
||||
try testing.htmlRunner("net/eventsource_worker.html", .{});
|
||||
}
|
||||
@@ -1046,6 +1046,7 @@ pub const JsApi = struct {
|
||||
|
||||
const testing = @import("../../../testing.zig");
|
||||
test "WebApi: WebSocket" {
|
||||
testing.expectLog(&.{.websocket});
|
||||
try testing.htmlRunner("net/websocket.html", .{});
|
||||
}
|
||||
|
||||
|
||||
@@ -136,14 +136,12 @@ pub fn stripUtf8Bom(bytes: []const u8) []const u8 {
|
||||
|
||||
const testing = @import("../../../testing.zig");
|
||||
test "BodyInit: bytes pass through with text/plain" {
|
||||
defer testing.reset();
|
||||
const r = try (BodyInit{ .bytes = "hello" }).extract(testing.arena_allocator);
|
||||
try testing.expectString("hello", r.bytes);
|
||||
try testing.expectString("text/plain;charset=UTF-8", r.content_type.?);
|
||||
}
|
||||
|
||||
test "BodyInit: URLSearchParams emit urlencoded body + content-type" {
|
||||
defer testing.reset();
|
||||
const arena = try testing.test_app.arena_pool.acquire(.small, "body_init test");
|
||||
defer arena.release();
|
||||
|
||||
@@ -158,7 +156,6 @@ test "BodyInit: URLSearchParams emit urlencoded body + content-type" {
|
||||
}
|
||||
|
||||
test "BodyInit: FormData emits multipart with random boundary" {
|
||||
defer testing.reset();
|
||||
const arena = try testing.test_app.arena_pool.acquire(.small, "body_init test");
|
||||
defer arena.release();
|
||||
|
||||
@@ -184,7 +181,6 @@ test "BodyInit: FormData emits multipart with random boundary" {
|
||||
}
|
||||
|
||||
test "BodyInit: buffer source has no default Content-Type" {
|
||||
defer testing.reset();
|
||||
const r = try (BodyInit{ .buffer = .{ .values = "hello" } }).extract(testing.arena_allocator);
|
||||
try testing.expectString("hello", r.bytes);
|
||||
try testing.expectEqual(true, r.content_type == null);
|
||||
|
||||
@@ -1666,7 +1666,6 @@ test "Selector: Parser.parseNthPattern" {
|
||||
}
|
||||
|
||||
test "Selector: Parser.attributeValue" {
|
||||
defer testing.reset();
|
||||
const arena = testing.arena_allocator;
|
||||
|
||||
// Unquoted identifier value (unchanged path).
|
||||
@@ -1759,7 +1758,6 @@ test "Selector: Parser.attributeValue" {
|
||||
}
|
||||
|
||||
test "Selector: Parser.attributeName" {
|
||||
defer testing.reset();
|
||||
const arena = testing.arena_allocator;
|
||||
|
||||
// Plain name (fast path).
|
||||
|
||||
@@ -1449,6 +1449,8 @@ test "cdp: disconnect latches so the worker keeps exiting" {
|
||||
}
|
||||
|
||||
test "cdp: tick sends a close frame on pending terminate" {
|
||||
testing.expectLog(&.{.cdp});
|
||||
|
||||
var ctx = try testing.context();
|
||||
defer ctx.deinit();
|
||||
|
||||
|
||||
@@ -188,8 +188,7 @@ test "cdp.Emulation: setUserAgentOverride with valid user agent" {
|
||||
}
|
||||
|
||||
test "cdp.Emulation: setUserAgentOverride ignores mozilla" {
|
||||
const filter: testing.LogFilter = .init(&.{.not_implemented});
|
||||
defer filter.deinit();
|
||||
testing.silenceLog(&.{.not_implemented});
|
||||
|
||||
var ctx = try testing.context();
|
||||
defer ctx.deinit();
|
||||
@@ -206,8 +205,7 @@ test "cdp.Emulation: setUserAgentOverride ignores mozilla" {
|
||||
}
|
||||
|
||||
test "cdp.Emulation: setUserAgentOverride ignores mozilla case insensitive" {
|
||||
const filter: testing.LogFilter = .init(&.{.not_implemented});
|
||||
defer filter.deinit();
|
||||
testing.silenceLog(&.{.not_implemented});
|
||||
|
||||
var ctx = try testing.context();
|
||||
defer ctx.deinit();
|
||||
@@ -224,8 +222,7 @@ test "cdp.Emulation: setUserAgentOverride ignores mozilla case insensitive" {
|
||||
}
|
||||
|
||||
test "cdp.Emulation: setUserAgentOverride rejects non-printable characters" {
|
||||
const filter: testing.LogFilter = .init(&.{.not_implemented});
|
||||
defer filter.deinit();
|
||||
testing.silenceLog(&.{.not_implemented});
|
||||
|
||||
var ctx = try testing.context();
|
||||
defer ctx.deinit();
|
||||
@@ -241,8 +238,7 @@ test "cdp.Emulation: setUserAgentOverride rejects non-printable characters" {
|
||||
}
|
||||
|
||||
test "cdp.Emulation: setUserAgentOverride with optional params" {
|
||||
const filter: testing.LogFilter = .init(&.{.not_implemented});
|
||||
defer filter.deinit();
|
||||
testing.silenceLog(&.{.not_implemented});
|
||||
|
||||
var ctx = try testing.context();
|
||||
defer ctx.deinit();
|
||||
|
||||
@@ -652,8 +652,7 @@ test "cdp.network setExtraHTTPHeaders" {
|
||||
}
|
||||
|
||||
test "cdp.network setExtraHTTPHeaders rejects non-printable User-Agent" {
|
||||
const filter: testing.LogFilter = .init(&.{.not_implemented});
|
||||
defer filter.deinit();
|
||||
testing.silenceLog(&.{.not_implemented});
|
||||
|
||||
var ctx = try testing.context();
|
||||
defer ctx.deinit();
|
||||
@@ -674,8 +673,7 @@ test "cdp.network setExtraHTTPHeaders rejects non-printable User-Agent" {
|
||||
}
|
||||
|
||||
test "cdp.network setExtraHTTPHeaders rejects a Mozilla User-Agent" {
|
||||
const filter: testing.LogFilter = .init(&.{.not_implemented});
|
||||
defer filter.deinit();
|
||||
testing.silenceLog(&.{.not_implemented});
|
||||
|
||||
var ctx = try testing.context();
|
||||
defer ctx.deinit();
|
||||
@@ -709,8 +707,7 @@ test "cdp.network setExtraHTTPHeaders accepts valid User-Agent" {
|
||||
}
|
||||
|
||||
test "cdp.network setExtraHTTPHeaders rejects a Mozilla User-Agent smuggled via a colon in the key" {
|
||||
const filter: testing.LogFilter = .init(&.{.not_implemented});
|
||||
defer filter.deinit();
|
||||
testing.silenceLog(&.{.not_implemented});
|
||||
|
||||
var ctx = try testing.context();
|
||||
defer ctx.deinit();
|
||||
@@ -731,8 +728,7 @@ test "cdp.network setExtraHTTPHeaders rejects a Mozilla User-Agent smuggled via
|
||||
}
|
||||
|
||||
test "cdp.network setExtraHTTPHeaders rejects a header that smuggles CRLF" {
|
||||
const filter: testing.LogFilter = .init(&.{.not_implemented});
|
||||
defer filter.deinit();
|
||||
testing.silenceLog(&.{.not_implemented});
|
||||
|
||||
var ctx = try testing.context();
|
||||
defer ctx.deinit();
|
||||
@@ -989,8 +985,7 @@ test "cdp.Network: configured CDP ignores setCacheDisabled" {
|
||||
}
|
||||
|
||||
test "cdp.Network: setBlockedURLs blocks requests with inspector reason" {
|
||||
const filter: testing.LogFilter = .init(&.{.http});
|
||||
defer filter.deinit();
|
||||
testing.silenceLog(&.{.http});
|
||||
|
||||
var ctx = try testing.context();
|
||||
defer ctx.deinit();
|
||||
|
||||
@@ -1215,9 +1215,7 @@ test "cdp.frame: child navigation preserves node registry" {
|
||||
}
|
||||
|
||||
test "cdp.frame: captureScreenshot" {
|
||||
const LogFilter = @import("../../testing.zig").LogFilter;
|
||||
const filter: LogFilter = .init(&.{.not_implemented});
|
||||
defer filter.deinit();
|
||||
testing.silenceLog(&.{.not_implemented});
|
||||
|
||||
var ctx = try testing.context();
|
||||
defer ctx.deinit();
|
||||
@@ -1235,9 +1233,7 @@ test "cdp.frame: captureScreenshot" {
|
||||
}
|
||||
|
||||
test "cdp.frame: printToPDF" {
|
||||
const LogFilter = @import("../../testing.zig").LogFilter;
|
||||
const filter: LogFilter = .init(&.{.not_implemented});
|
||||
defer filter.deinit();
|
||||
testing.silenceLog(&.{.not_implemented});
|
||||
|
||||
var ctx = try testing.context();
|
||||
defer ctx.deinit();
|
||||
@@ -1536,8 +1532,7 @@ test "cdp.frame: navigate does not follow Location on a non-redirect 3xx" {
|
||||
}
|
||||
|
||||
test "cdp.frame: navigate answers with errorText when the navigation fails" {
|
||||
const filter: testing.LogFilter = .init(&.{.frame});
|
||||
defer filter.deinit();
|
||||
testing.silenceLog(&.{.frame});
|
||||
|
||||
// A root navigation that fails before commit (here: connection refused —
|
||||
// nothing listens on port 1) must still answer the Page.navigate command.
|
||||
|
||||
@@ -169,8 +169,7 @@ pub fn consoleMessage(arena: Allocator, bc: *CDP.BrowserContext, event: *const N
|
||||
const testing = @import("../testing.zig");
|
||||
|
||||
test "cdp.runtime: consoleAPICalled type matches the console method" {
|
||||
const filter: testing.LogFilter = .init(&.{.js});
|
||||
defer filter.deinit();
|
||||
testing.silenceLog(&.{.js});
|
||||
|
||||
// Wire types per the CDP protocol: console.log -> "log",
|
||||
// console.warn -> "warning" (not "warn"), console.info -> "info",
|
||||
|
||||
+2
-1
@@ -34,7 +34,8 @@ pub const expectError = base.expectError;
|
||||
pub const expectEqualSlices = base.expectEqualSlices;
|
||||
pub const pageTest = base.pageTest;
|
||||
pub const newString = base.newString;
|
||||
pub const LogFilter = base.LogFilter;
|
||||
pub const expectLog = base.expectLog;
|
||||
pub const silenceLog = base.silenceLog;
|
||||
|
||||
const TestContext = struct {
|
||||
read_at: usize = 0,
|
||||
|
||||
+30
@@ -99,6 +99,28 @@ pub fn enabled(scope: Scope, level: Level) bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
// The number of log lines each scope is expected to emit for the current test.
|
||||
// A line from a scope with a pending count is consumed instead of written; see
|
||||
// testing.expectLog.
|
||||
var expected_logs: [num_scopes]u16 = @splat(0);
|
||||
|
||||
// Registers one expected log line per entry in `scopes`.
|
||||
pub fn expectLog(comptime scopes: []const Scope) void {
|
||||
comptime std.debug.assert(IS_TEST);
|
||||
inline for (scopes) |scope| {
|
||||
expected_logs[@intFromEnum(scope)] += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Called after each test. Returns the expectations that were never met.
|
||||
pub fn resetTestState() [num_scopes]u16 {
|
||||
std.debug.assert(IS_TEST);
|
||||
const unmet = expected_logs;
|
||||
expected_logs = @splat(0);
|
||||
opts.scope_enabled = @splat(true);
|
||||
return unmet;
|
||||
}
|
||||
|
||||
// Ugliness to support complex debug parameters. Could add better support for
|
||||
// this directly in writeValue, but we [currently] only need this in one place
|
||||
// and I kind of don't want to encourage / make this easy.
|
||||
@@ -151,6 +173,14 @@ pub fn log(scope: Scope, level: Level, msg: []const u8, data: anytype) void {
|
||||
return;
|
||||
}
|
||||
|
||||
if (comptime IS_TEST) {
|
||||
const expected = &expected_logs[@intFromEnum(scope)];
|
||||
if (expected.* > 0) {
|
||||
expected.* -= 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (sink) |s| {
|
||||
var buf: [4096]u8 = undefined;
|
||||
var w: std.Io.Writer = .fixed(&buf);
|
||||
|
||||
@@ -248,7 +248,6 @@ pub fn handleResourceRead(self: *Self, arena: std.mem.Allocator, req: protocol.R
|
||||
}
|
||||
|
||||
test "MCP.Server - Integration: synchronous smoke test" {
|
||||
defer testing.reset();
|
||||
const allocator = testing.allocator;
|
||||
const app = testing.test_app;
|
||||
|
||||
|
||||
@@ -186,7 +186,6 @@ pub const JsonEscapingWriter = struct {
|
||||
const testing = @import("../testing.zig");
|
||||
|
||||
test "MCP.protocol - request parsing" {
|
||||
defer testing.reset();
|
||||
const raw_json =
|
||||
\\{
|
||||
\\ "jsonrpc": "2.0",
|
||||
@@ -222,7 +221,6 @@ test "MCP.protocol - request parsing" {
|
||||
}
|
||||
|
||||
test "MCP.protocol - ping request parsing" {
|
||||
defer testing.reset();
|
||||
const raw_json =
|
||||
\\{
|
||||
\\ "jsonrpc": "2.0",
|
||||
@@ -243,7 +241,6 @@ test "MCP.protocol - ping request parsing" {
|
||||
}
|
||||
|
||||
test "MCP.protocol - response formatting" {
|
||||
defer testing.reset();
|
||||
const response = Response{
|
||||
.id = .{ .integer = 42 },
|
||||
.result = .{ .string = "success" },
|
||||
@@ -257,7 +254,6 @@ test "MCP.protocol - response formatting" {
|
||||
}
|
||||
|
||||
test "MCP.protocol - error formatting" {
|
||||
defer testing.reset();
|
||||
const response = Response{
|
||||
.id = .{ .string = "abc" },
|
||||
.@"error" = .{
|
||||
@@ -274,7 +270,6 @@ test "MCP.protocol - error formatting" {
|
||||
}
|
||||
|
||||
test "MCP.protocol - JsonEscapingWriter" {
|
||||
defer testing.reset();
|
||||
var aw: std.Io.Writer.Allocating = .init(testing.arena_allocator);
|
||||
defer aw.deinit();
|
||||
|
||||
@@ -288,7 +283,6 @@ test "MCP.protocol - JsonEscapingWriter" {
|
||||
}
|
||||
|
||||
test "MCP.protocol - Tool serialization" {
|
||||
defer testing.reset();
|
||||
const t = Tool{
|
||||
.name = "test",
|
||||
.inputSchema = minify(
|
||||
|
||||
+1
-3
@@ -123,7 +123,6 @@ const Server = @import("Server.zig");
|
||||
const testing = @import("../testing.zig");
|
||||
|
||||
test "MCP.router - handleMessage - synchronous unit tests" {
|
||||
defer testing.reset();
|
||||
const allocator = testing.allocator;
|
||||
const app = testing.test_app;
|
||||
|
||||
@@ -168,8 +167,7 @@ test "MCP.router - handleMessage - synchronous unit tests" {
|
||||
|
||||
// 5. Parse error
|
||||
{
|
||||
const filter: testing.LogFilter = .init(&.{.mcp});
|
||||
defer filter.deinit();
|
||||
testing.expectLog(&.{.mcp});
|
||||
|
||||
try handleMessage(server, aa, "invalid json");
|
||||
try testing.expectJson("{\"jsonrpc\": \"2.0\", \"id\": null, \"error\": {\"code\": -32700}}", out_alloc.writer.buffered());
|
||||
|
||||
@@ -271,7 +271,6 @@ const router = @import("router.zig");
|
||||
const testing = @import("../testing.zig");
|
||||
|
||||
test "MCP - evaluate error reporting" {
|
||||
defer testing.reset();
|
||||
var out: std.Io.Writer.Allocating = .init(testing.arena_allocator);
|
||||
const server = try testLoadPage("about:blank", &out.writer);
|
||||
defer server.deinit();
|
||||
@@ -300,7 +299,6 @@ test "MCP - evaluate error reporting" {
|
||||
}
|
||||
|
||||
test "MCP - evaluate: top-level return runs in an async wrapper" {
|
||||
defer testing.reset();
|
||||
var out: std.Io.Writer.Allocating = .init(testing.arena_allocator);
|
||||
const server = try testLoadPage("about:blank", &out.writer);
|
||||
defer server.deinit();
|
||||
@@ -324,7 +322,6 @@ test "MCP - evaluate: top-level return runs in an async wrapper" {
|
||||
}
|
||||
|
||||
test "MCP - evaluate: top-level await runs in an async wrapper" {
|
||||
defer testing.reset();
|
||||
var out: std.Io.Writer.Allocating = .init(testing.arena_allocator);
|
||||
const server = try testLoadPage("about:blank", &out.writer);
|
||||
defer server.deinit();
|
||||
@@ -348,7 +345,6 @@ test "MCP - evaluate: top-level await runs in an async wrapper" {
|
||||
}
|
||||
|
||||
test "MCP - evaluate: let declaration does not leak across calls" {
|
||||
defer testing.reset();
|
||||
var out: std.Io.Writer.Allocating = .init(testing.arena_allocator);
|
||||
const server = try testLoadPage("about:blank", &out.writer);
|
||||
defer server.deinit();
|
||||
@@ -386,7 +382,6 @@ test "MCP - evaluate: let declaration does not leak across calls" {
|
||||
}
|
||||
|
||||
test "MCP - evaluate: bare expression still returns its value" {
|
||||
defer testing.reset();
|
||||
var out: std.Io.Writer.Allocating = .init(testing.arena_allocator);
|
||||
const server = try testLoadPage("about:blank", &out.writer);
|
||||
defer server.deinit();
|
||||
@@ -410,7 +405,6 @@ test "MCP - evaluate: bare expression still returns its value" {
|
||||
}
|
||||
|
||||
test "MCP - evaluate: object return serializes as JSON" {
|
||||
defer testing.reset();
|
||||
var out: std.Io.Writer.Allocating = .init(testing.arena_allocator);
|
||||
const server = try testLoadPage("about:blank", &out.writer);
|
||||
defer server.deinit();
|
||||
@@ -434,7 +428,6 @@ test "MCP - evaluate: object return serializes as JSON" {
|
||||
}
|
||||
|
||||
test "MCP - evaluate: localStorage persists across navigations and is origin-scoped" {
|
||||
defer testing.reset();
|
||||
var out: std.Io.Writer.Allocating = .init(testing.arena_allocator);
|
||||
const server = try testLoadPage("http://localhost:9582/src/browser/tests/mcp_actions.html", &out.writer);
|
||||
defer server.deinit();
|
||||
@@ -524,7 +517,6 @@ test "MCP - evaluate: localStorage persists across navigations and is origin-sco
|
||||
}
|
||||
|
||||
test "MCP - evaluate: save= value is readable via lp.<name> in next evaluate" {
|
||||
defer testing.reset();
|
||||
var out: std.Io.Writer.Allocating = .init(testing.arena_allocator);
|
||||
const server = try testLoadPage("about:blank", &out.writer);
|
||||
defer server.deinit();
|
||||
@@ -561,7 +553,6 @@ test "MCP - evaluate: save= value is readable via lp.<name> in next evaluate" {
|
||||
}
|
||||
|
||||
test "MCP - evaluate: save= a bare string round-trips without JSON.stringify" {
|
||||
defer testing.reset();
|
||||
var out: std.Io.Writer.Allocating = .init(testing.arena_allocator);
|
||||
const server = try testLoadPage("about:blank", &out.writer);
|
||||
defer server.deinit();
|
||||
@@ -598,7 +589,6 @@ test "MCP - evaluate: save= a bare string round-trips without JSON.stringify" {
|
||||
}
|
||||
|
||||
test "MCP - evaluate: lp.* mutations auto-sync between evaluates" {
|
||||
defer testing.reset();
|
||||
var out: std.Io.Writer.Allocating = .init(testing.arena_allocator);
|
||||
const server = try testLoadPage("about:blank", &out.writer);
|
||||
defer server.deinit();
|
||||
@@ -635,7 +625,6 @@ test "MCP - evaluate: lp.* mutations auto-sync between evaluates" {
|
||||
}
|
||||
|
||||
test "MCP - evaluate: lp.* survives navigation" {
|
||||
defer testing.reset();
|
||||
var out: std.Io.Writer.Allocating = .init(testing.arena_allocator);
|
||||
const server = try testLoadPage("http://localhost:9582/src/browser/tests/mcp_actions.html", &out.writer);
|
||||
defer server.deinit();
|
||||
@@ -686,7 +675,6 @@ test "MCP - evaluate: lp.* survives navigation" {
|
||||
}
|
||||
|
||||
test "MCP - evaluate: delete lp.<key> removes from bridge store" {
|
||||
defer testing.reset();
|
||||
var out: std.Io.Writer.Allocating = .init(testing.arena_allocator);
|
||||
const server = try testLoadPage("about:blank", &out.writer);
|
||||
defer server.deinit();
|
||||
@@ -737,7 +725,6 @@ test "MCP - evaluate: delete lp.<key> removes from bridge store" {
|
||||
}
|
||||
|
||||
test "MCP - extract: save= exposes the result as lp.<name>" {
|
||||
defer testing.reset();
|
||||
var out: std.Io.Writer.Allocating = .init(testing.arena_allocator);
|
||||
const server = try testLoadPage("http://localhost:9582/src/browser/tests/mcp_actions.html", &out.writer);
|
||||
defer server.deinit();
|
||||
@@ -777,7 +764,6 @@ test "MCP - extract: save= exposes the result as lp.<name>" {
|
||||
}
|
||||
|
||||
test "MCP - evaluate: Promise.resolve return value is awaited" {
|
||||
defer testing.reset();
|
||||
var out: std.Io.Writer.Allocating = .init(testing.arena_allocator);
|
||||
const server = try testLoadPage("about:blank", &out.writer);
|
||||
defer server.deinit();
|
||||
@@ -800,7 +786,6 @@ test "MCP - evaluate: Promise.resolve return value is awaited" {
|
||||
}
|
||||
|
||||
test "MCP - evaluate: async IIFE resolves to returned value" {
|
||||
defer testing.reset();
|
||||
var out: std.Io.Writer.Allocating = .init(testing.arena_allocator);
|
||||
const server = try testLoadPage("about:blank", &out.writer);
|
||||
defer server.deinit();
|
||||
@@ -823,7 +808,6 @@ test "MCP - evaluate: async IIFE resolves to returned value" {
|
||||
}
|
||||
|
||||
test "MCP - evaluate: rejected Promise surfaces as is_error" {
|
||||
defer testing.reset();
|
||||
var out: std.Io.Writer.Allocating = .init(testing.arena_allocator);
|
||||
const server = try testLoadPage("about:blank", &out.writer);
|
||||
defer server.deinit();
|
||||
@@ -845,7 +829,6 @@ test "MCP - evaluate: rejected Promise surfaces as is_error" {
|
||||
}
|
||||
|
||||
test "MCP - evaluate: async IIFE without explicit return resolves to empty text" {
|
||||
defer testing.reset();
|
||||
var out: std.Io.Writer.Allocating = .init(testing.arena_allocator);
|
||||
const server = try testLoadPage("about:blank", &out.writer);
|
||||
defer server.deinit();
|
||||
@@ -868,7 +851,6 @@ test "MCP - evaluate: async IIFE without explicit return resolves to empty text"
|
||||
}
|
||||
|
||||
test "MCP - evaluate: lp.* mutations inside async IIFE survive to the next evaluate" {
|
||||
defer testing.reset();
|
||||
var out: std.Io.Writer.Allocating = .init(testing.arena_allocator);
|
||||
const server = try testLoadPage("about:blank", &out.writer);
|
||||
defer server.deinit();
|
||||
@@ -905,7 +887,6 @@ test "MCP - evaluate: lp.* mutations inside async IIFE survive to the next evalu
|
||||
}
|
||||
|
||||
test "MCP - save rejects unsafe path" {
|
||||
defer testing.reset();
|
||||
var out: std.Io.Writer.Allocating = .init(testing.arena_allocator);
|
||||
const server = try testLoadPage("about:blank", &out.writer);
|
||||
defer server.deinit();
|
||||
@@ -918,7 +899,6 @@ test "MCP - save rejects unsafe path" {
|
||||
}
|
||||
|
||||
test "MCP - save writes the script to disk" {
|
||||
defer testing.reset();
|
||||
var out: std.Io.Writer.Allocating = .init(testing.arena_allocator);
|
||||
const server = try testLoadPage("about:blank", &out.writer);
|
||||
defer server.deinit();
|
||||
@@ -938,7 +918,6 @@ test "MCP - save writes the script to disk" {
|
||||
}
|
||||
|
||||
test "MCP - tree rejects stale backendNodeId instead of dumping whole document" {
|
||||
defer testing.reset();
|
||||
var out: std.Io.Writer.Allocating = .init(testing.arena_allocator);
|
||||
const server = try testLoadPage("about:blank", &out.writer);
|
||||
defer server.deinit();
|
||||
@@ -952,7 +931,6 @@ test "MCP - tree rejects stale backendNodeId instead of dumping whole document"
|
||||
}
|
||||
|
||||
test "MCP - PascalCase argument keys from LLMs are normalized to canonical" {
|
||||
defer testing.reset();
|
||||
var out: std.Io.Writer.Allocating = .init(testing.arena_allocator);
|
||||
const server = try testLoadPage("http://localhost:9582/src/browser/tests/mcp_actions.html", &out.writer);
|
||||
defer server.deinit();
|
||||
@@ -967,7 +945,6 @@ test "MCP - PascalCase argument keys from LLMs are normalized to canonical" {
|
||||
}
|
||||
|
||||
test "MCP - Actions: click, fill, scroll, hover, press, selectOption, setChecked" {
|
||||
defer testing.reset();
|
||||
const aa = testing.arena_allocator;
|
||||
|
||||
var out: std.Io.Writer.Allocating = .init(aa);
|
||||
@@ -1105,7 +1082,6 @@ test "MCP - Actions: click, fill, scroll, hover, press, selectOption, setChecked
|
||||
// left the registry intact, and a second click on the same id dereferenced
|
||||
// a freed DOMNode.
|
||||
test "MCP - click that navigates clears node registry" {
|
||||
defer testing.reset();
|
||||
const aa = testing.arena_allocator;
|
||||
|
||||
var out: std.Io.Writer.Allocating = .init(aa);
|
||||
@@ -1131,7 +1107,6 @@ test "MCP - click that navigates clears node registry" {
|
||||
}
|
||||
|
||||
test "MCP - Actions by selector: hover, selectOption, setChecked" {
|
||||
defer testing.reset();
|
||||
const aa = testing.arena_allocator;
|
||||
|
||||
var out: std.Io.Writer.Allocating = .init(aa);
|
||||
@@ -1200,7 +1175,6 @@ test "MCP - Actions by selector: hover, selectOption, setChecked" {
|
||||
}
|
||||
|
||||
test "MCP - findElement" {
|
||||
defer testing.reset();
|
||||
const aa = testing.arena_allocator;
|
||||
|
||||
var out: std.Io.Writer.Allocating = .init(aa);
|
||||
@@ -1245,7 +1219,6 @@ test "MCP - findElement" {
|
||||
}
|
||||
|
||||
test "MCP - waitForSelector: existing element" {
|
||||
defer testing.reset();
|
||||
var out: std.Io.Writer.Allocating = .init(testing.arena_allocator);
|
||||
const server = try testLoadPage(
|
||||
"http://localhost:9582/src/browser/tests/mcp_wait_for_selector.html",
|
||||
@@ -1263,7 +1236,6 @@ test "MCP - waitForSelector: existing element" {
|
||||
}
|
||||
|
||||
test "MCP - waitForSelector: delayed element" {
|
||||
defer testing.reset();
|
||||
var out: std.Io.Writer.Allocating = .init(testing.arena_allocator);
|
||||
const server = try testLoadPage(
|
||||
"http://localhost:9582/src/browser/tests/mcp_wait_for_selector.html",
|
||||
@@ -1281,7 +1253,6 @@ test "MCP - waitForSelector: delayed element" {
|
||||
}
|
||||
|
||||
test "MCP - waitForSelector: timeout" {
|
||||
defer testing.reset();
|
||||
var out: std.Io.Writer.Allocating = .init(testing.arena_allocator);
|
||||
const server = try testLoadPage(
|
||||
"http://localhost:9582/src/browser/tests/mcp_wait_for_selector.html",
|
||||
@@ -1302,7 +1273,6 @@ test "MCP - waitForSelector: timeout" {
|
||||
}
|
||||
|
||||
test "MCP - markdown: full page, selector scope, maxBytes truncation" {
|
||||
defer testing.reset();
|
||||
var out: std.Io.Writer.Allocating = .init(testing.arena_allocator);
|
||||
const server = try testLoadPage("http://localhost:9582/src/browser/tests/mcp_actions.html", &out.writer);
|
||||
defer server.deinit();
|
||||
@@ -1331,7 +1301,6 @@ test "MCP - markdown: full page, selector scope, maxBytes truncation" {
|
||||
}
|
||||
|
||||
test "MCP - html: full document, selector subtree, backendNodeId subtree" {
|
||||
defer testing.reset();
|
||||
var out: std.Io.Writer.Allocating = .init(testing.arena_allocator);
|
||||
const server = try testLoadPage("http://localhost:9582/src/browser/tests/mcp_press_form.html", &out.writer);
|
||||
defer server.deinit();
|
||||
@@ -1357,7 +1326,6 @@ test "MCP - html: full document, selector subtree, backendNodeId subtree" {
|
||||
}
|
||||
|
||||
test "MCP - waitForScript: truthy returns, falsy times out" {
|
||||
defer testing.reset();
|
||||
var out: std.Io.Writer.Allocating = .init(testing.arena_allocator);
|
||||
const server = try testLoadPage("about:blank", &out.writer);
|
||||
defer server.deinit();
|
||||
@@ -1377,7 +1345,6 @@ test "MCP - waitForScript: truthy returns, falsy times out" {
|
||||
}
|
||||
|
||||
test "MCP - press Enter on form input triggers submit (lowercase alias)" {
|
||||
defer testing.reset();
|
||||
const aa = testing.arena_allocator;
|
||||
var out: std.Io.Writer.Allocating = .init(aa);
|
||||
const server = try testLoadPage("http://localhost:9582/src/browser/tests/mcp_press_form.html", &out.writer);
|
||||
@@ -1399,7 +1366,6 @@ test "MCP - press Enter on form input triggers submit (lowercase alias)" {
|
||||
}
|
||||
|
||||
test "MCP - getCookies: defaults to current page, url filter, all flag" {
|
||||
defer testing.reset();
|
||||
var out: std.Io.Writer.Allocating = .init(testing.arena_allocator);
|
||||
const server = try testLoadPage("http://localhost:9582/src/browser/tests/mcp_press_form.htm", &out.writer);
|
||||
defer server.deinit();
|
||||
@@ -1439,7 +1405,6 @@ test "MCP - getCookies: defaults to current page, url filter, all flag" {
|
||||
}
|
||||
|
||||
test "MCP - getCookies without a loaded page refuses instead of dumping the jar" {
|
||||
defer testing.reset();
|
||||
var out: std.Io.Writer.Allocating = .init(testing.arena_allocator);
|
||||
var server = try Server.init(testing.allocator, testing.test_app, &out.writer);
|
||||
defer server.deinit();
|
||||
@@ -1456,7 +1421,6 @@ test "MCP - getCookies without a loaded page refuses instead of dumping the jar"
|
||||
}
|
||||
|
||||
test "MCP - waitForState with bad state surfaces rich error" {
|
||||
defer testing.reset();
|
||||
var out: std.Io.Writer.Allocating = .init(testing.arena_allocator);
|
||||
const server = try testLoadPage("about:blank", &out.writer);
|
||||
defer server.deinit();
|
||||
@@ -1472,7 +1436,6 @@ test "MCP - waitForState with bad state surfaces rich error" {
|
||||
}
|
||||
|
||||
test "MCP - sessions: new, list, attach isolation, close" {
|
||||
defer testing.reset();
|
||||
const aa = testing.arena_allocator;
|
||||
var out: std.Io.Writer.Allocating = .init(aa);
|
||||
var server = try Server.init(testing.allocator, testing.test_app, &out.writer);
|
||||
|
||||
Vendored
+1
-3
@@ -554,9 +554,7 @@ test "FsCache: put override" {
|
||||
}
|
||||
|
||||
test "FsCache: garbage file" {
|
||||
const LogFilter = @import("../../testing.zig").LogFilter;
|
||||
const filter: LogFilter = .init(&.{.cache});
|
||||
defer filter.deinit();
|
||||
@import("../../testing.zig").silenceLog(&.{.cache});
|
||||
|
||||
var setup = try setupCache();
|
||||
defer {
|
||||
|
||||
@@ -1029,8 +1029,7 @@ test "Headers.set adds a new header and preserves defaults" {
|
||||
}
|
||||
|
||||
test "opensocketCallback: private IPv4 returns CURL_SOCKET_BAD" {
|
||||
const lf: testing.LogFilter = .init(&.{.http});
|
||||
defer lf.deinit();
|
||||
testing.silenceLog(&.{.http});
|
||||
|
||||
const filter = IpFilter.init(true, null);
|
||||
var sa = makeSockAddrV4(.{ 127, 0, 0, 1 });
|
||||
|
||||
+1
-29
@@ -997,7 +997,6 @@ fn terminateRuntimeSoon(runtime: *Runtime) void {
|
||||
}
|
||||
|
||||
test "agent script runtime: goto and evaluate dispatch through browser tools" {
|
||||
defer testing.reset();
|
||||
defer testing.test_session.closeAllPages();
|
||||
|
||||
var registry = CDPNode.Registry.init(testing.allocator);
|
||||
@@ -1019,8 +1018,6 @@ test "agent script runtime: goto and evaluate dispatch through browser tools" {
|
||||
}
|
||||
|
||||
test "agent script runtime: Page must be called with new" {
|
||||
defer testing.reset();
|
||||
|
||||
var registry = CDPNode.Registry.init(testing.allocator);
|
||||
defer registry.deinit();
|
||||
|
||||
@@ -1032,8 +1029,6 @@ test "agent script runtime: Page must be called with new" {
|
||||
}
|
||||
|
||||
test "agent script runtime: a method on an un-navigated page errors" {
|
||||
defer testing.reset();
|
||||
|
||||
var registry = CDPNode.Registry.init(testing.allocator);
|
||||
defer registry.deinit();
|
||||
|
||||
@@ -1048,7 +1043,6 @@ test "agent script runtime: a method on an un-navigated page errors" {
|
||||
}
|
||||
|
||||
test "agent script runtime: page.close stales the handle" {
|
||||
defer testing.reset();
|
||||
defer testing.test_session.closeAllPages();
|
||||
|
||||
var registry = CDPNode.Registry.init(testing.allocator);
|
||||
@@ -1069,7 +1063,6 @@ test "agent script runtime: page.close stales the handle" {
|
||||
}
|
||||
|
||||
test "agent script runtime: parallel gotos coexist and route per page" {
|
||||
defer testing.reset();
|
||||
defer testing.test_session.closeAllPages();
|
||||
|
||||
var registry = CDPNode.Registry.init(testing.allocator);
|
||||
@@ -1094,7 +1087,6 @@ test "agent script runtime: parallel gotos coexist and route per page" {
|
||||
}
|
||||
|
||||
test "agent script runtime: goto resolves $LP_* placeholders" {
|
||||
defer testing.reset();
|
||||
defer testing.test_session.closeAllPages();
|
||||
|
||||
var registry = CDPNode.Registry.init(testing.allocator);
|
||||
@@ -1118,7 +1110,6 @@ extern fn setenv(name: [*:0]u8, value: [*:0]u8, override: c_int) c_int;
|
||||
extern fn unsetenv(name: [*:0]u8) c_int;
|
||||
|
||||
test "agent script runtime: goto with invalid arguments rejects instead of crashing" {
|
||||
defer testing.reset();
|
||||
defer testing.test_session.closeAllPages();
|
||||
|
||||
var registry = CDPNode.Registry.init(testing.allocator);
|
||||
@@ -1138,7 +1129,6 @@ test "agent script runtime: goto with invalid arguments rejects instead of crash
|
||||
}
|
||||
|
||||
test "agent script runtime: a tool-triggered navigation keeps the handle routable" {
|
||||
defer testing.reset();
|
||||
defer testing.test_session.closeAllPages();
|
||||
|
||||
var registry = CDPNode.Registry.init(testing.allocator);
|
||||
@@ -1159,7 +1149,6 @@ test "agent script runtime: a tool-triggered navigation keeps the handle routabl
|
||||
}
|
||||
|
||||
test "agent script runtime: re-goto on the same page object replaces its page" {
|
||||
defer testing.reset();
|
||||
defer testing.test_session.closeAllPages();
|
||||
|
||||
var registry = CDPNode.Registry.init(testing.allocator);
|
||||
@@ -1178,10 +1167,8 @@ test "agent script runtime: re-goto on the same page object replaces its page" {
|
||||
}
|
||||
|
||||
test "agent script runtime: a failed navigation rejects the goto promise" {
|
||||
const filter: testing.LogFilter = .init(&.{.frame});
|
||||
defer filter.deinit();
|
||||
testing.silenceLog(&.{.frame});
|
||||
|
||||
defer testing.reset();
|
||||
defer testing.test_session.closeAllPages();
|
||||
|
||||
var registry = CDPNode.Registry.init(testing.allocator);
|
||||
@@ -1203,7 +1190,6 @@ test "agent script runtime: a failed navigation rejects the goto promise" {
|
||||
}
|
||||
|
||||
test "agent script runtime: extract returns a JavaScript object" {
|
||||
defer testing.reset();
|
||||
defer testing.test_session.closeAllPages();
|
||||
|
||||
var registry = CDPNode.Registry.init(testing.allocator);
|
||||
@@ -1258,7 +1244,6 @@ test "agent script runtime: extract returns a JavaScript object" {
|
||||
}
|
||||
|
||||
test "agent script runtime: extract tolerates list selectors that match nothing" {
|
||||
defer testing.reset();
|
||||
defer testing.test_session.closeAllPages();
|
||||
|
||||
var registry = CDPNode.Registry.init(testing.allocator);
|
||||
@@ -1287,7 +1272,6 @@ test "agent script runtime: extract tolerates list selectors that match nothing"
|
||||
}
|
||||
|
||||
test "agent script runtime: strict-mode scripts can call primitives" {
|
||||
defer testing.reset();
|
||||
defer testing.test_session.closeAllPages();
|
||||
|
||||
var registry = CDPNode.Registry.init(testing.allocator);
|
||||
@@ -1306,8 +1290,6 @@ test "agent script runtime: strict-mode scripts can call primitives" {
|
||||
}
|
||||
|
||||
test "agent script runtime: promise microtasks run to completion" {
|
||||
defer testing.reset();
|
||||
|
||||
var registry = CDPNode.Registry.init(testing.allocator);
|
||||
defer registry.deinit();
|
||||
|
||||
@@ -1326,7 +1308,6 @@ test "agent script runtime: promise microtasks run to completion" {
|
||||
}
|
||||
|
||||
test "agent script runtime: primitives re-entered from argument callbacks stay isolated" {
|
||||
defer testing.reset();
|
||||
defer testing.test_session.closeAllPages();
|
||||
|
||||
var registry = CDPNode.Registry.init(testing.allocator);
|
||||
@@ -1354,7 +1335,6 @@ test "agent script runtime: primitives re-entered from argument callbacks stay i
|
||||
}
|
||||
|
||||
test "agent script runtime: terminate interrupts local JavaScript" {
|
||||
defer testing.reset();
|
||||
defer testing.test_session.closeAllPages();
|
||||
|
||||
var registry = CDPNode.Registry.init(testing.allocator);
|
||||
@@ -1372,7 +1352,6 @@ test "agent script runtime: terminate interrupts local JavaScript" {
|
||||
}
|
||||
|
||||
test "agent script runtime: agent variables persist and page globals are isolated" {
|
||||
defer testing.reset();
|
||||
defer testing.test_session.closeAllPages();
|
||||
|
||||
var registry = CDPNode.Registry.init(testing.allocator);
|
||||
@@ -1397,7 +1376,6 @@ test "agent script runtime: agent variables persist and page globals are isolate
|
||||
}
|
||||
|
||||
test "agent script runtime: page evaluate cannot see agent primitives or bindings" {
|
||||
defer testing.reset();
|
||||
defer testing.test_session.closeAllPages();
|
||||
|
||||
var registry = CDPNode.Registry.init(testing.allocator);
|
||||
@@ -1417,8 +1395,6 @@ test "agent script runtime: page evaluate cannot see agent primitives or binding
|
||||
}
|
||||
|
||||
test "agent script runtime: console is available in agent context" {
|
||||
defer testing.reset();
|
||||
|
||||
var registry = CDPNode.Registry.init(testing.allocator);
|
||||
defer registry.deinit();
|
||||
|
||||
@@ -1438,7 +1414,6 @@ test "agent script runtime: console is available in agent context" {
|
||||
}
|
||||
|
||||
test "agent script runtime: tool errors throw and stop execution" {
|
||||
defer testing.reset();
|
||||
defer testing.test_session.closeAllPages();
|
||||
|
||||
var registry = CDPNode.Registry.init(testing.allocator);
|
||||
@@ -1465,7 +1440,6 @@ test "agent script runtime: tool errors throw and stop execution" {
|
||||
}
|
||||
|
||||
test "agent script runtime: builtin argument marshalling (positional + options)" {
|
||||
defer testing.reset();
|
||||
defer testing.test_session.closeAllPages();
|
||||
|
||||
var registry = CDPNode.Registry.init(testing.allocator);
|
||||
@@ -1524,8 +1498,6 @@ test "agent script runtime: builtin argument marshalling (positional + options)"
|
||||
}
|
||||
|
||||
test "agent script runtime: top-level await runs in an async wrapper" {
|
||||
defer testing.reset();
|
||||
|
||||
var registry = CDPNode.Registry.init(testing.allocator);
|
||||
defer registry.deinit();
|
||||
|
||||
|
||||
+26
-3
@@ -90,7 +90,11 @@ const Runner = struct {
|
||||
|
||||
Printer.fmt("\r\x1b[0K", .{}); // beginning of line and clear to end of line
|
||||
|
||||
var after_each: ?std.builtin.TestFn = null;
|
||||
for (builtin.test_functions) |t| {
|
||||
if (isAfterEach(t)) {
|
||||
after_each = t;
|
||||
}
|
||||
if (isSetup(t)) {
|
||||
t.func() catch |err| {
|
||||
Printer.status(.fail, "\nsetup \"{s}\" failed: {}\n", .{ t.name, err });
|
||||
@@ -104,7 +108,7 @@ const Runner = struct {
|
||||
const webapi_html_test_mode = self.env.filter == null and self.env.subfilter != null;
|
||||
|
||||
for (builtin.test_functions) |t| {
|
||||
if (isSetup(t) or isTeardown(t)) {
|
||||
if (isSetup(t) or isTeardown(t) or isAfterEach(t)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -145,7 +149,16 @@ const Runner = struct {
|
||||
|
||||
current_test = friendly_name;
|
||||
std.testing.allocator_instance = .{};
|
||||
const result = t.func();
|
||||
var result = t.func();
|
||||
if (after_each) |ae| {
|
||||
// always runs, so that it can reset state, but it can only
|
||||
// turn a passing test into a failing one.
|
||||
ae.func() catch |err| {
|
||||
if (result) |_| {
|
||||
result = err;
|
||||
} else |_| {}
|
||||
};
|
||||
}
|
||||
current_test = null;
|
||||
|
||||
if (webapi_html_test_mode and self.subtests.items.len == 0) {
|
||||
@@ -256,6 +269,12 @@ const Runner = struct {
|
||||
}
|
||||
};
|
||||
|
||||
// When only part of a test runs, expectations about what the whole test logs
|
||||
// can't be enforced.
|
||||
pub fn hasSubfilter() bool {
|
||||
return RUNNER.env.subfilter != null;
|
||||
}
|
||||
|
||||
pub fn shouldRun(name: []const u8) bool {
|
||||
const sf = RUNNER.env.subfilter orelse return true;
|
||||
return std.mem.indexOf(u8, name, sf) != null;
|
||||
@@ -384,7 +403,7 @@ const Env = struct {
|
||||
.filter = filter,
|
||||
.subfilter = subfilter,
|
||||
.metrics = readEnvBool(map, "METRICS", false),
|
||||
.verbose = readEnvBool(map, "TEST_VERBOSE", true),
|
||||
.verbose = readEnvBool(map, "TEST_VERBOSE", false),
|
||||
.fail_first = readEnvBool(map, "TEST_FAIL_FIRST", false),
|
||||
};
|
||||
}
|
||||
@@ -444,6 +463,10 @@ fn isTeardown(t: std.builtin.TestFn) bool {
|
||||
return std.mem.endsWith(u8, t.name, "tests:afterAll");
|
||||
}
|
||||
|
||||
fn isAfterEach(t: std.builtin.TestFn) bool {
|
||||
return std.mem.endsWith(u8, t.name, "tests:afterEach");
|
||||
}
|
||||
|
||||
pub const TrackingAllocator = struct {
|
||||
parent_allocator: Allocator,
|
||||
free_count: usize = 0,
|
||||
|
||||
+29
-16
@@ -1042,24 +1042,37 @@ fn testHTTPHandler(req: *std.http.Server.Request) !void {
|
||||
unreachable;
|
||||
}
|
||||
|
||||
/// LogFilter provides a scoped way to suppress specific log categories during tests.
|
||||
/// This is useful for tests that trigger expected errors or warnings.
|
||||
pub const LogFilter = struct {
|
||||
old_filter: [log.num_scopes]bool,
|
||||
/// Declares the log lines a test expects to emit: one entry per line, so
|
||||
/// `&.{ .js, .js, .http }` covers two `js` lines and one `http` line.
|
||||
pub const expectLog = log.expectLog;
|
||||
|
||||
/// Sets the log filter to suppress the specified scope(s).
|
||||
/// Returns a LogFilter that should be deinitialized to restore previous filters.
|
||||
pub fn init(comptime scopes: []const log.Scope) LogFilter {
|
||||
comptime std.debug.assert(@TypeOf(scopes) == []const log.Scope);
|
||||
const old_filter = log.opts.scope_enabled;
|
||||
inline for (scopes) |scope| {
|
||||
log.opts.scope_enabled[@intFromEnum(scope)] = false;
|
||||
/// Suppresses every line from `scopes` for the rest of the test.
|
||||
pub fn silenceLog(comptime scopes: []const log.Scope) void {
|
||||
inline for (scopes) |scope| {
|
||||
log.opts.scope_enabled[@intFromEnum(scope)] = false;
|
||||
}
|
||||
}
|
||||
|
||||
test "tests:afterEach" {
|
||||
defer reset();
|
||||
const unmet = log.resetTestState();
|
||||
if (@import("root").hasSubfilter()) {
|
||||
// only part of the test ran, so expectations about what it logs
|
||||
// don't hold.
|
||||
return;
|
||||
}
|
||||
|
||||
var failed = false;
|
||||
for (unmet, 0..) |count, i| {
|
||||
if (count == 0) {
|
||||
continue;
|
||||
}
|
||||
return .{ .old_filter = old_filter };
|
||||
failed = true;
|
||||
const scope: log.Scope = @enumFromInt(i);
|
||||
std.debug.print("expected {d} more {s} log line(s)\n", .{ count, @tagName(scope) });
|
||||
}
|
||||
|
||||
/// Restores the log filters to their previous state.
|
||||
pub fn deinit(self: LogFilter) void {
|
||||
log.opts.scope_enabled = self.old_filter;
|
||||
if (failed) {
|
||||
return error.UnmetLogExpectation;
|
||||
}
|
||||
};
|
||||
}
|
||||
Reference in new issue
Block a user