From dd22c55d23fb9c720411cf827b7b8cdbe0fe8089 Mon Sep 17 00:00:00 2001 From: Karl Seguin Date: Fri, 5 Sep 2025 13:52:08 +0800 Subject: [PATCH] migrate to htmlRunne (plus zig fmt) --- src/browser/dom/document.zig | 16 -- src/browser/dom/document_fragment.zig | 39 +--- src/browser/dom/document_type.zig | 17 +- src/browser/dom/dom_parser.zig | 10 +- src/browser/dom/element.zig | 275 +------------------------- src/browser/page.zig | 4 +- src/browser/session.zig | 2 - src/cdp/cdp.zig | 4 +- src/http/Client.zig | 2 +- src/runtime/js.zig | 8 +- src/tests/dom/document_fragment.html | 33 ++++ src/tests/dom/document_type.html | 12 ++ src/tests/dom/dom_parser.html | 6 + src/tests/dom/element.html | 264 +++++++++++++++++++++++++ 14 files changed, 330 insertions(+), 362 deletions(-) create mode 100644 src/tests/dom/document_fragment.html create mode 100644 src/tests/dom/document_type.html create mode 100644 src/tests/dom/dom_parser.html create mode 100644 src/tests/dom/element.html diff --git a/src/browser/dom/document.zig b/src/browser/dom/document.zig index f9a5e5b9..a82ec6e3 100644 --- a/src/browser/dom/document.zig +++ b/src/browser/dom/document.zig @@ -316,20 +316,4 @@ pub const Document = struct { const testing = @import("../../testing.zig"); test "Browser: DOM.Document" { try testing.htmlRunner("dom/document.html"); - - // const Case = testing.JsRunner.Case; - // const tags = comptime parser.Tag.all(); - // var createElements: [(tags.len) * 2]Case = undefined; - // inline for (tags, 0..) |tag, i| { - // const tag_name = @tagName(tag); - // createElements[i * 2] = Case{ - // "var " ++ tag_name ++ "Elem = document.createElement('" ++ tag_name ++ "')", - // "undefined", - // }; - // createElements[(i * 2) + 1] = Case{ - // tag_name ++ "Elem.localName", - // tag_name, - // }; - // } - // try runner.testCases(&createElements, .{}); } diff --git a/src/browser/dom/document_fragment.zig b/src/browser/dom/document_fragment.zig index 2eb3709b..7841d466 100644 --- a/src/browser/dom/document_fragment.zig +++ b/src/browser/dom/document_fragment.zig @@ -91,41 +91,6 @@ pub const DocumentFragment = struct { }; const testing = @import("../../testing.zig"); -test "Browser.DOM.DocumentFragment" { - var runner = try testing.jsRunner(testing.tracking_allocator, .{}); - defer runner.deinit(); - - try runner.testCases(&.{ - .{ "const dc = new DocumentFragment()", "undefined" }, - .{ "dc.constructor.name", "DocumentFragment" }, - }, .{}); - - try runner.testCases(&.{ - .{ "const dc1 = new DocumentFragment()", "undefined" }, - .{ "const dc2 = new DocumentFragment()", "undefined" }, - .{ "dc1.isEqualNode(dc1)", "true" }, - .{ "dc1.isEqualNode(dc2)", "true" }, - }, .{}); - - try runner.testCases(&.{ - .{ "let f = document.createDocumentFragment()", null }, - .{ "let d = document.createElement('div');", null }, - .{ "d.childElementCount", "0" }, - - .{ "d.id = 'x';", null }, - .{ "document.getElementById('x') == null;", "true" }, - .{ "f.append(d);", null }, - .{ "f.childElementCount", "1" }, - .{ "f.children[0].id", "x" }, - .{ "document.getElementById('x') == null;", "true" }, - - .{ "document.getElementsByTagName('body')[0].append(f.cloneNode(true));", null }, - .{ "document.getElementById('x') != null;", "true" }, - - .{ "document.querySelector('.hello')", "null" }, - .{ "document.querySelectorAll('.hello').length", "0" }, - - .{ "document.querySelector('#x').id", "x" }, - .{ "document.querySelectorAll('#x')[0].id", "x" }, - }, .{}); +test "Browser: DOM.DocumentFragment" { + try testing.htmlRunner("dom/document_fragment.html"); } diff --git a/src/browser/dom/document_type.zig b/src/browser/dom/document_type.zig index d4425dc2..f1f923de 100644 --- a/src/browser/dom/document_type.zig +++ b/src/browser/dom/document_type.zig @@ -62,19 +62,6 @@ pub const DocumentType = struct { }; const testing = @import("../../testing.zig"); -test "Browser.DOM.DocumentType" { - var runner = try testing.jsRunner(testing.tracking_allocator, .{}); - defer runner.deinit(); - - try runner.testCases(&.{ - .{ "let dt1 = document.implementation.createDocumentType('qname1', 'pid1', 'sys1');", "undefined" }, - .{ "let dt2 = document.implementation.createDocumentType('qname2', 'pid2', 'sys2');", "undefined" }, - .{ "let dt3 = document.implementation.createDocumentType('qname1', 'pid1', 'sys1');", "undefined" }, - .{ "dt1.isEqualNode(dt1)", "true" }, - .{ "dt1.isEqualNode(dt3)", "true" }, - .{ "dt1.isEqualNode(dt2)", "false" }, - .{ "dt2.isEqualNode(dt3)", "false" }, - .{ "dt1.isEqualNode(document)", "false" }, - .{ "document.isEqualNode(dt1)", "false" }, - }, .{}); +test "Browser: DOM.DocumentType" { + try testing.htmlRunner("dom/document_type.html"); } diff --git a/src/browser/dom/dom_parser.zig b/src/browser/dom/dom_parser.zig index 505d2b10..081c7e6f 100644 --- a/src/browser/dom/dom_parser.zig +++ b/src/browser/dom/dom_parser.zig @@ -36,12 +36,6 @@ pub const DOMParser = struct { }; const testing = @import("../../testing.zig"); -test "Browser.DOM.DOMParser" { - var runner = try testing.jsRunner(testing.tracking_allocator, .{}); - defer runner.deinit(); - - try runner.testCases(&.{ - .{ "const dp = new DOMParser()", "undefined" }, - .{ "dp.parseFromString('
abc
', 'text/html')", "[object HTMLDocument]" }, - }, .{}); +test "Browser: DOM.Parser" { + try testing.htmlRunner("dom/dom_parser.html"); } diff --git a/src/browser/dom/element.zig b/src/browser/dom/element.zig index bc30e5e5..5043c4cb 100644 --- a/src/browser/dom/element.zig +++ b/src/browser/dom/element.zig @@ -593,277 +593,6 @@ pub const Element = struct { // ----- const testing = @import("../../testing.zig"); -test "Browser.DOM.Element" { - var runner = try testing.jsRunner(testing.tracking_allocator, .{}); - defer runner.deinit(); - - try runner.testCases(&.{ - .{ "let g = document.getElementById('content')", "undefined" }, - .{ "g.namespaceURI", "http://www.w3.org/1999/xhtml" }, - .{ "g.prefix", "null" }, - .{ "g.localName", "div" }, - .{ "g.tagName", "DIV" }, - }, .{}); - - try runner.testCases(&.{ - .{ "let gs = document.getElementById('content')", "undefined" }, - .{ "gs.id", "content" }, - .{ "gs.id = 'foo'", "foo" }, - .{ "gs.id", "foo" }, - .{ "gs.id = 'content'", "content" }, - .{ "gs.className", "" }, - .{ "let gs2 = document.getElementById('para-empty')", "undefined" }, - .{ "gs2.className", "ok empty" }, - .{ "gs2.className = 'foo bar baz'", "foo bar baz" }, - .{ "gs2.className", "foo bar baz" }, - .{ "gs2.className = 'ok empty'", "ok empty" }, - .{ "let cl = gs2.classList", "undefined" }, - .{ "cl.length", "2" }, - }, .{}); - - try runner.testCases(&.{ - .{ "const el2 = document.createElement('div');", "undefined" }, - .{ "el2.id = 'closest'; el2.className = 'ok';", "ok" }, - .{ "el2.closest('#closest')", "[object HTMLDivElement]" }, - .{ "el2.closest('.ok')", "[object HTMLDivElement]" }, - .{ "el2.closest('#9000')", "null" }, - .{ "el2.closest('.notok')", "null" }, - - .{ "const sp = document.createElement('span');", "undefined" }, - .{ "el2.appendChild(sp);", "[object HTMLSpanElement]" }, - .{ "sp.closest('#closest')", "[object HTMLDivElement]" }, - .{ "sp.closest('#9000')", "null" }, - }, .{}); - - try runner.testCases(&.{ - .{ "let a = document.getElementById('content')", "undefined" }, - .{ "a.hasAttributes()", "true" }, - .{ "a.attributes.length", "1" }, - .{ "a.getAttributeNames()", "id" }, - .{ "a.getAttribute('id')", "content" }, - .{ "a.attributes['id'].value", "content" }, - .{ - \\ let x = ''; - \\ for (const attr of a.attributes) { - \\ x += attr.name + '=' + attr.value; - \\ } - \\ x; - , - "id=content", - }, - - .{ "a.hasAttribute('foo')", "false" }, - .{ "a.getAttribute('foo')", "null" }, - - .{ "a.setAttribute('foo', 'bar')", "undefined" }, - .{ "a.hasAttribute('foo')", "true" }, - .{ "a.getAttribute('foo')", "bar" }, - .{ "a.getAttributeNames()", "id,foo" }, - .{ " try { a.setAttribute('.foo', 'invalid') } catch (e) { e }", "Error: InvalidCharacterError" }, - - .{ "a.setAttribute('foo', 'baz')", "undefined" }, - .{ "a.hasAttribute('foo')", "true" }, - .{ "a.getAttribute('foo')", "baz" }, - - .{ "a.removeAttribute('foo')", "undefined" }, - .{ "a.hasAttribute('foo')", "false" }, - .{ "a.getAttribute('foo')", "null" }, - }, .{}); - - try runner.testCases(&.{ - .{ "let b = document.getElementById('content')", "undefined" }, - .{ "b.toggleAttribute('foo')", "true" }, - .{ "b.hasAttribute('foo')", "true" }, - .{ "b.getAttribute('foo')", "" }, - - .{ "b.toggleAttribute('foo')", "false" }, - .{ "b.hasAttribute('foo')", "false" }, - }, .{}); - - try runner.testCases(&.{ - .{ "let c = document.getElementById('content')", "undefined" }, - .{ "c.children.length", "3" }, - .{ "c.firstElementChild.nodeName", "A" }, - .{ "c.lastElementChild.nodeName", "P" }, - .{ "c.childElementCount", "3" }, - - .{ "c.prepend(document.createTextNode('foo'))", "undefined" }, - .{ "c.append(document.createTextNode('bar'))", "undefined" }, - }, .{}); - - try runner.testCases(&.{ - .{ "let d = document.getElementById('para')", "undefined" }, - .{ "d.previousElementSibling.nodeName", "P" }, - .{ "d.nextElementSibling", "null" }, - }, .{}); - - try runner.testCases(&.{ - .{ "let e = document.getElementById('content')", "undefined" }, - .{ "e.querySelector('foo')", "null" }, - .{ "e.querySelector('#foo')", "null" }, - .{ "e.querySelector('#link').id", "link" }, - .{ "e.querySelector('#para').id", "para" }, - .{ "e.querySelector('*').id", "link" }, - .{ "e.querySelector('')", "null" }, - .{ "e.querySelector('*').id", "link" }, - .{ "e.querySelector('#content')", "null" }, - .{ "e.querySelector('#para').id", "para" }, - .{ "e.querySelector('.ok').id", "link" }, - .{ "e.querySelector('a ~ p').id", "para-empty" }, - - .{ "e.querySelectorAll('foo').length", "0" }, - .{ "e.querySelectorAll('#foo').length", "0" }, - .{ "e.querySelectorAll('#link').length", "1" }, - .{ "e.querySelectorAll('#link').item(0).id", "link" }, - .{ "e.querySelectorAll('#para').length", "1" }, - .{ "e.querySelectorAll('#para').item(0).id", "para" }, - .{ "e.querySelectorAll('*').length", "4" }, - .{ "e.querySelectorAll('p').length", "2" }, - .{ "e.querySelectorAll('.ok').item(0).id", "link" }, - }, .{}); - - try runner.testCases(&.{ - .{ "let f = document.getElementById('content')", "undefined" }, - .{ "let ff = document.createAttribute('foo')", "undefined" }, - .{ "f.setAttributeNode(ff)", "null" }, - .{ "f.getAttributeNode('foo').name", "foo" }, - .{ "f.removeAttributeNode(ff).name", "foo" }, - .{ "f.getAttributeNode('bar')", "null" }, - }, .{}); - - try runner.testCases(&.{ - .{ "document.getElementById('para').innerHTML", " And" }, - .{ "document.getElementById('para-empty').innerHTML.trim()", "" }, - - .{ "let h = document.getElementById('para-empty')", "undefined" }, - .{ "const prev = h.innerHTML", "undefined" }, - .{ "h.innerHTML = '

hello world

'", "

hello world

" }, - .{ "h.innerHTML", "

hello world

" }, - .{ "h.firstChild.nodeName", "P" }, - .{ "h.firstChild.id", "hello" }, - .{ "h.firstChild.textContent", "hello world" }, - .{ "h.innerHTML = prev; true", "true" }, - .{ "document.getElementById('para-empty').innerHTML.trim()", "" }, - }, .{}); - - try runner.testCases(&.{ - .{ "document.getElementById('para').outerHTML", "

And

" }, - }, .{}); - - try runner.testCases(&.{ - .{ "document.getElementById('para').clientWidth", "1" }, - .{ "document.getElementById('para').clientHeight", "1" }, - - .{ "let r1 = document.getElementById('para').getBoundingClientRect()", "undefined" }, - .{ "r1.x", "0" }, - .{ "r1.y", "0" }, - .{ "r1.width", "1" }, - .{ "r1.height", "1" }, - - .{ "let r2 = document.getElementById('content').getBoundingClientRect()", "undefined" }, - .{ "r2.x", "1" }, - .{ "r2.y", "0" }, - .{ "r2.width", "1" }, - .{ "r2.height", "1" }, - - .{ "let r3 = document.getElementById('para').getBoundingClientRect()", "undefined" }, - .{ "r3.x", "0" }, - .{ "r3.y", "0" }, - .{ "r3.width", "1" }, - .{ "r3.height", "1" }, - - .{ "document.getElementById('para').clientWidth", "2" }, - .{ "document.getElementById('para').clientHeight", "1" }, - - .{ "let r4 = document.createElement('div').getBoundingClientRect()", null }, - .{ "r4.x", "0" }, - .{ "r4.y", "0" }, - .{ "r4.width", "0" }, - .{ "r4.height", "0" }, - - // Test setup causes WrongDocument or HierarchyRequest error unlike in chrome/firefox - // .{ // An element of another document, even if created from the main document, is not rendered. - // \\ let div5 = document.createElement('div'); - // \\ const newDoc = document.implementation.createHTMLDocument("New Document"); - // \\ newDoc.body.appendChild(div5); - // \\ let r5 = div5.getBoundingClientRect(); - // , - // null, - // }, - // .{ "r5.x", "0" }, - // .{ "r5.y", "0" }, - // .{ "r5.width", "0" }, - // .{ "r5.height", "0" }, - }, .{}); - - try runner.testCases(&.{ - .{ "const el = document.createElement('div');", "undefined" }, - .{ "el.id = 'matches'; el.className = 'ok';", "ok" }, - .{ "el.matches('#matches')", "true" }, - .{ "el.matches('.ok')", "true" }, - .{ "el.matches('#9000')", "false" }, - .{ "el.matches('.notok')", "false" }, - }, .{}); - - try runner.testCases(&.{ - .{ "const el3 = document.createElement('div');", "undefined" }, - .{ "el3.scrollIntoViewIfNeeded();", "undefined" }, - .{ "el3.scrollIntoViewIfNeeded(false);", "undefined" }, - }, .{}); - - // before - try runner.testCases(&.{ - .{ "const before_container = document.createElement('div');", "undefined" }, - .{ "document.append(before_container);", "undefined" }, - .{ "const b1 = document.createElement('div');", "undefined" }, - .{ "before_container.append(b1);", "undefined" }, - - .{ "const b1_a = document.createElement('p');", "undefined" }, - .{ "b1.before(b1_a, 'over 9000');", "undefined" }, - .{ "before_container.innerHTML", "

over 9000
" }, - }, .{}); - - // after - try runner.testCases(&.{ - .{ "const after_container = document.createElement('div');", "undefined" }, - .{ "document.append(after_container);", "undefined" }, - .{ "const a1 = document.createElement('div');", "undefined" }, - .{ "after_container.append(a1);", "undefined" }, - - .{ "const a1_a = document.createElement('p');", "undefined" }, - .{ "a1.after('over 9000', a1_a);", "undefined" }, - .{ "after_container.innerHTML", "
over 9000

" }, - }, .{}); - - try runner.testCases(&.{ - .{ "var div1 = document.createElement('div');", null }, - .{ "div1.innerHTML = \"
a\"", null }, - .{ "div1.getElementsByTagName('a').length", "1" }, - }, .{}); - - try runner.testCases(&.{ - .{ "document.createElement('a').hasAttributes()", "false" }, - .{ "var fc; (fc = document.createElement('div')).innerHTML = '" }, - - .{ "fc; (fc = document.createElement('div')).innerHTML = '

hello

" }, - }, .{}); - - try runner.testCases(&.{ - .{ "const rm = document.createElement('div')", null }, - .{ "rm.getAttributeNames()", "" }, - .{ "rm.id = 'to-remove'", null }, - .{ "document.getElementsByTagName('body')[0].appendChild(rm)", null }, - .{ "document.getElementById('to-remove') != null", "true" }, - .{ "rm.remove()", "undefined" }, - .{ "document.getElementById('to-remove') != null", "false" }, - }, .{}); - - try runner.testCases(&.{ - .{ "const div2 = document.createElement('div');", null }, - .{ "div2.innerHTML = '

a

';", null }, - .{ "div2.innerHTML", "

a

" }, - .{ "div2.childNodes[0].getAttributeNames()", "id,.lit$id" }, - }, .{}); +test "Browser: DOM.Element" { + try testing.htmlRunner("dom/element.html"); } diff --git a/src/browser/page.zig b/src/browser/page.zig index 6362b620..80fe3ef6 100644 --- a/src/browser/page.zig +++ b/src/browser/page.zig @@ -350,7 +350,7 @@ pub const Page = struct { // Look, we want to exit ASAP, but we don't want // to exit so fast that we've run none of the // background jobs. - break :blk 50; + break :blk if (comptime builtin.is_test) 5 else 50; } // No http transfers, no cdp extra socket, no // scheduled tasks, we're done. @@ -397,7 +397,7 @@ pub const Page = struct { // we _could_ http_client.tick(ms_to_wait), but this has // the same result, and I feel is more correct. return .no_page; - } + }, } const ms_elapsed = timer.lap() / 1_000_000; diff --git a/src/browser/session.zig b/src/browser/session.zig index a9c590a4..87713d4d 100644 --- a/src/browser/session.zig +++ b/src/browser/session.zig @@ -176,8 +176,6 @@ pub const Session = struct { } }; - - const QueuedNavigation = struct { url: []const u8, opts: NavigateOpts, diff --git a/src/cdp/cdp.zig b/src/cdp/cdp.zig index e7ce2086..3fcf14dc 100644 --- a/src/cdp/cdp.zig +++ b/src/cdp/cdp.zig @@ -117,9 +117,7 @@ pub fn CDPT(comptime TypeProvider: type) type { // scheduled task. So we run this directly in order to process any // timeouts (or http events) which are ready to be processed. - pub fn hasPage() bool { - - } + pub fn hasPage() bool {} pub fn pageWait(self: *Self, ms: i32) Session.WaitResult { const session = &(self.browser.session orelse return .no_page); return session.wait(ms); diff --git a/src/http/Client.zig b/src/http/Client.zig index 15b519d3..fcf97696 100644 --- a/src/http/Client.zig +++ b/src/http/Client.zig @@ -387,7 +387,7 @@ fn makeRequest(self: *Client, handle: *Handle, transfer: *Transfer) !void { _ = try self.perform(0); } -pub const PerformStatus = enum{ +pub const PerformStatus = enum { extra_socket, normal, }; diff --git a/src/runtime/js.zig b/src/runtime/js.zig index b343b3eb..32f1e382 100644 --- a/src/runtime/js.zig +++ b/src/runtime/js.zig @@ -331,18 +331,16 @@ pub fn Env(comptime State: type, comptime WebApis: type) type { fn promiseRejectCallback(v8_msg: v8.C_PromiseRejectMessage) callconv(.c) void { const msg = v8.PromiseRejectMessage.initFromC(v8_msg); - const isolate = msg.getPromise().toObject().getIsolate(); + const isolate = msg.getPromise().toObject().getIsolate(); const v8_context = isolate.getCurrentContext(); const context: *JsContext = @ptrFromInt(v8_context.getEmbedderData(1).castTo(v8.BigInt).getUint64()); const value = - if (msg.getValue()) |v8_value| valueToString(context.call_arena, v8_value, isolate, v8_context) catch |err| @errorName(err) - else "no value"; + if (msg.getValue()) |v8_value| valueToString(context.call_arena, v8_value, isolate, v8_context) catch |err| @errorName(err) else "no value"; - log.debug(.js, "unhandled rejection", .{.value =value}); + log.debug(.js, "unhandled rejection", .{ .value = value }); } - // ExecutionWorld closely models a JS World. // https://chromium.googlesource.com/chromium/src/+/master/third_party/blink/renderer/bindings/core/v8/V8BindingDesign.md#World // https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/scripting/ExecutionWorld diff --git a/src/tests/dom/document_fragment.html b/src/tests/dom/document_fragment.html new file mode 100644 index 00000000..d42d5a68 --- /dev/null +++ b/src/tests/dom/document_fragment.html @@ -0,0 +1,33 @@ + + + + diff --git a/src/tests/dom/document_type.html b/src/tests/dom/document_type.html new file mode 100644 index 00000000..f98df200 --- /dev/null +++ b/src/tests/dom/document_type.html @@ -0,0 +1,12 @@ + + diff --git a/src/tests/dom/dom_parser.html b/src/tests/dom/dom_parser.html new file mode 100644 index 00000000..9a5f91db --- /dev/null +++ b/src/tests/dom/dom_parser.html @@ -0,0 +1,6 @@ + + diff --git a/src/tests/dom/element.html b/src/tests/dom/element.html new file mode 100644 index 00000000..0c7dce1c --- /dev/null +++ b/src/tests/dom/element.html @@ -0,0 +1,264 @@ + + +
+ OK +

+ +

+

And

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +