Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 1 addition & 7 deletions src/browser/browser.zig
Original file line number Diff line number Diff line change
Expand Up @@ -112,11 +112,5 @@ pub const Browser = struct {

const testing = @import("../testing.zig");
test "Browser" {
var runner = try testing.jsRunner(testing.tracking_allocator, .{});
defer runner.deinit();

// this will crash if ICU isn't properly configured / ininitialized
try runner.testCases(&.{
.{ "new Intl.DateTimeFormat()", "[object Intl.DateTimeFormat]" },
}, .{});
try testing.htmlRunner("browser.html");
}
30 changes: 2 additions & 28 deletions src/browser/crypto/crypto.zig
Original file line number Diff line number Diff line change
Expand Up @@ -66,32 +66,6 @@ const RandomValues = union(enum) {
};

const testing = @import("../../testing.zig");
test "Browser.Crypto" {
var runner = try testing.jsRunner(testing.tracking_allocator, .{});
defer runner.deinit();

try runner.testCases(&.{
.{ "const a = crypto.randomUUID();", "undefined" },
.{ "const b = crypto.randomUUID();", "undefined" },
.{ "a.length;", "36" },
.{ "b.length;", "36" },
.{ "a == b;", "false" },
}, .{});

try runner.testCases(&.{
.{ "try { crypto.getRandomValues(new BigUint64Array(8193)) } catch(e) { e.message == 'QuotaExceededError' }", "true" },
.{ "let r1 = new Int32Array(5)", "undefined" },
.{ "let r2 = crypto.getRandomValues(r1)", "undefined" },
.{ "new Set(r1).size", "5" },
.{ "new Set(r2).size", "5" },
.{ "r1.every((v, i) => v === r2[i])", "true" },
}, .{});

try runner.testCases(&.{
.{ "var r3 = new Uint8Array(16)", null },
.{ "let r4 = crypto.getRandomValues(r3)", "undefined" },
.{ "r4[6] = 10", null },
.{ "r4[6]", "10" },
.{ "r3[6]", "10" },
}, .{});
test "Browser: Crypto" {
try testing.htmlRunner("crypto.html");
}
10 changes: 2 additions & 8 deletions src/browser/css/css.zig
Original file line number Diff line number Diff line change
Expand Up @@ -190,12 +190,6 @@ test "parse" {
}

const testing = @import("../../testing.zig");
test "Browser.HTML.CSS" {
var runner = try testing.jsRunner(testing.tracking_allocator, .{});
defer runner.deinit();

try runner.testCases(&.{
.{ "CSS.supports('display: flex')", "true" },
.{ "CSS.supports('text-decoration-style', 'blink')", "true" },
}, .{});
test "Browser: CSS" {
try testing.htmlRunner("css.html");
}
27 changes: 2 additions & 25 deletions src/browser/encoding/TextDecoder.zig
Original file line number Diff line number Diff line change
Expand Up @@ -79,29 +79,6 @@ pub fn _decode(self: *const TextDecoder, v: []const u8) ![]const u8 {
}

const testing = @import("../../testing.zig");
test "Browser.Encoding.TextDecoder" {
var runner = try testing.jsRunner(testing.tracking_allocator, .{
.html = "",
});
defer runner.deinit();

try runner.testCases(&.{
.{ "let d1 = new TextDecoder();", null },
.{ "d1.encoding;", "utf-8" },
.{ "d1.fatal", "false" },
.{ "d1.ignoreBOM", "false" },
.{ "d1.decode(new Uint8Array([240, 160, 174, 183]))", "𠮷" },
.{ "d1.decode(new Uint8Array([0xEF, 0xBB, 0xBF, 240, 160, 174, 183]))", "𠮷" },
.{ "d1.decode(new Uint8Array([49, 50]).buffer)", "12" },

.{ "let d2 = new TextDecoder('utf8', {fatal: true})", null },
.{
\\ try {
\\ let data = new Uint8Array([240, 240, 160, 174, 183]);
\\ d2.decode(data);
\\ } catch (e) {e}
,
"Error: InvalidUtf8",
},
}, .{});
test "Browser: Encoding.TextDecoder" {
try testing.htmlRunner("encoding/decoder.html");
}
18 changes: 2 additions & 16 deletions src/browser/encoding/TextEncoder.zig
Original file line number Diff line number Diff line change
Expand Up @@ -43,20 +43,6 @@ pub fn _encode(_: *const TextEncoder, v: []const u8) !Env.TypedArray(u8) {
}

const testing = @import("../../testing.zig");
test "Browser.Encoding.TextEncoder" {
var runner = try testing.jsRunner(testing.tracking_allocator, .{
.html = "",
});
defer runner.deinit();

try runner.testCases(&.{
.{ "var encoder = new TextEncoder();", null },
.{ "encoder.encoding;", "utf-8" },
.{ "encoder.encode('€');", "226,130,172" },

// Invalid utf-8 sequence.
// Result with chrome:
// .{ "encoder.encode(new Uint8Array([0xE2,0x28,0xA1]))", "50,50,54,44,52,48,44,49,54,49" },
.{ "try {encoder.encode(new Uint8Array([0xE2,0x28,0xA1])) } catch (e) { e };", "Error: InvalidUtf8" },
}, .{});
test "Browser: Encoding.TextEncoder" {
try testing.htmlRunner("encoding/encoder.html");
}
21 changes: 2 additions & 19 deletions src/browser/events/custom_event.zig
Original file line number Diff line number Diff line change
Expand Up @@ -58,23 +58,6 @@ pub const CustomEvent = struct {
};

const testing = @import("../../testing.zig");
test "Browser.CustomEvent" {
var runner = try testing.jsRunner(testing.tracking_allocator, .{});
defer runner.deinit();

try runner.testCases(&.{
.{ "let capture = null", "undefined" },
.{ "const el = document.createElement('div');", "undefined" },
.{ "el.addEventListener('c1', (e) => { capture = 'c1-' + new String(e.detail)})", "undefined" },
.{ "el.addEventListener('c2', (e) => { capture = 'c2-' + new String(e.detail.over)})", "undefined" },

.{ "el.dispatchEvent(new CustomEvent('c1'));", "true" },
.{ "capture", "c1-null" },

.{ "el.dispatchEvent(new CustomEvent('c1', {detail: '123'}));", "true" },
.{ "capture", "c1-123" },

.{ "el.dispatchEvent(new CustomEvent('c2', {detail: {over: 9000}}));", "true" },
.{ "capture", "c2-9000" },
}, .{});
test "Browser: Events.Custom" {
try testing.htmlRunner("events/custom.html");
}
160 changes: 7 additions & 153 deletions src/browser/events/event.zig
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,11 @@ pub const Event = struct {
return try parser.eventIsTrusted(self);
}

pub fn get_timestamp(self: *parser.Event) !u32 {
return try parser.eventTimestamp(self);
// Even though this is supposed to to provide microsecond resolution, browser
// return coarser values to protect against fingerprinting. libdom returns
// seconds, which is good enough.
pub fn get_timeStamp(self: *parser.Event) !u32 {
return parser.eventTimestamp(self);
}

// Methods
Expand Down Expand Up @@ -386,155 +389,6 @@ const SignalCallback = struct {
};

const testing = @import("../../testing.zig");
test "Browser.Event" {
var runner = try testing.jsRunner(testing.tracking_allocator, .{});
defer runner.deinit();

try runner.testCases(&.{
.{ "let content = document.getElementById('content')", "undefined" },
.{ "let para = document.getElementById('para')", "undefined" },
.{ "var nb = 0; var evt", "undefined" },
}, .{});

try runner.testCases(&.{
.{
\\ content.addEventListener('target', function(e) {
\\ evt = e; nb = nb + 1;
\\ e.preventDefault();
\\ })
,
"undefined",
},
.{ "content.dispatchEvent(new Event('target', {bubbles: true, cancelable: true}))", "false" },
.{ "nb", "1" },
.{ "evt.target === content", "true" },
.{ "evt.bubbles", "true" },
.{ "evt.cancelable", "true" },
.{ "evt.defaultPrevented", "true" },
.{ "evt.isTrusted", "true" },
.{ "evt.timestamp > 1704063600", "true" }, // 2024/01/01 00:00
// event.type, event.currentTarget, event.phase checked in EventTarget
}, .{});

try runner.testCases(&.{
.{ "nb = 0", "0" },
.{
\\ content.addEventListener('stop',function(e) {
\\ e.stopPropagation();
\\ nb = nb + 1;
\\ }, true)
,
"undefined",
},
// the following event listener will not be invoked
.{
\\ para.addEventListener('stop',function(e) {
\\ nb = nb + 1;
\\ })
,
"undefined",
},
.{ "para.dispatchEvent(new Event('stop'))", "true" },
.{ "nb", "1" }, // will be 2 if event was not stopped at content event listener
}, .{});

try runner.testCases(&.{
.{ "nb = 0", "0" },
.{
\\ content.addEventListener('immediate', function(e) {
\\ e.stopImmediatePropagation();
\\ nb = nb + 1;
\\ })
,
"undefined",
},
// the following event listener will not be invoked
.{
\\ content.addEventListener('immediate', function(e) {
\\ nb = nb + 1;
\\ })
,
"undefined",
},
.{ "content.dispatchEvent(new Event('immediate'))", "true" },
.{ "nb", "1" }, // will be 2 if event was not stopped at first content event listener
}, .{});

try runner.testCases(&.{
.{ "nb = 0", "0" },
.{
\\ content.addEventListener('legacy', function(e) {
\\ evt = e; nb = nb + 1;
\\ })
,
"undefined",
},
.{ "let evtLegacy = document.createEvent('Event')", "undefined" },
.{ "evtLegacy.initEvent('legacy')", "undefined" },
.{ "content.dispatchEvent(evtLegacy)", "true" },
.{ "nb", "1" },
}, .{});

try runner.testCases(&.{
.{ "var nb = 0; var evt = null; function cbk(event) { nb ++; evt=event; }", "undefined" },
.{ "document.addEventListener('count', cbk)", "undefined" },
.{ "document.removeEventListener('count', cbk)", "undefined" },
.{ "document.dispatchEvent(new Event('count'))", "true" },
.{ "nb", "0" },
}, .{});

try runner.testCases(&.{
.{ "nb = 0; function cbk(event) { nb ++; }", null },
.{ "document.addEventListener('count', cbk, {once: true})", null },
.{ "document.dispatchEvent(new Event('count'))", "true" },
.{ "document.dispatchEvent(new Event('count'))", "true" },
.{ "document.dispatchEvent(new Event('count'))", "true" },
.{ "nb", "1" },
.{ "document.removeEventListener('count', cbk)", "undefined" },
}, .{});

try runner.testCases(&.{
.{ "nb = 0; function cbk(event) { nb ++; }", null },
.{ "let ac = new AbortController()", null },
.{ "document.addEventListener('count', cbk, {signal: ac.signal})", null },
.{ "document.dispatchEvent(new Event('count'))", "true" },
.{ "document.dispatchEvent(new Event('count'))", "true" },
.{ "ac.abort()", null },
.{ "document.dispatchEvent(new Event('count'))", "true" },
.{ "nb", "2" },
.{ "document.removeEventListener('count', cbk)", "undefined" },
}, .{});

try runner.testCases(&.{
.{ "new Event('').composedPath()", "" },
.{
\\ let div1 = document.createElement('div');
\\ let sr1 = div1.attachShadow({mode: 'open'});
\\ sr1.innerHTML = "<p id=srp1></p>";
\\ document.getElementsByTagName('body')[0].appendChild(div1);
\\ let cp = null;
\\ div1.addEventListener('click', (e) => {
\\ cp = e.composedPath().map((n) => n.id || n.nodeName || n.toString());
\\ });
\\ sr1.getElementById('srp1').click();
\\ cp.join(', ');
,
"srp1, #document-fragment, DIV, BODY, HTML, #document, [object Window]",
},

.{
\\ let div2 = document.createElement('div');
\\ let sr2 = div2.attachShadow({mode: 'closed'});
\\ sr2.innerHTML = "<p id=srp2></p>";
\\ document.getElementsByTagName('body')[0].appendChild(div2);
\\ cp = null;
\\ div2.addEventListener('click', (e) => {
\\ cp = e.composedPath().map((n) => n.id || n.nodeName || n.toString());
\\ });
\\ sr2.getElementById('srp2').click();
\\ cp.join(', ');
,
"DIV, BODY, HTML, #document, [object Window]",
},
}, .{});
test "Browser: Event" {
try testing.htmlRunner("events/event.html");
}
32 changes: 2 additions & 30 deletions src/browser/events/mouse_event.zig
Original file line number Diff line number Diff line change
Expand Up @@ -107,34 +107,6 @@ pub const MouseEvent = struct {
};

const testing = @import("../../testing.zig");
test "Browser.MouseEvent" {
var runner = try testing.jsRunner(testing.tracking_allocator, .{});
defer runner.deinit();

try runner.testCases(&.{
// Default MouseEvent
.{ "let event = new MouseEvent('click')", "undefined" },
.{ "event.type", "click" },
.{ "event instanceof MouseEvent", "true" },
.{ "event instanceof Event", "true" },
.{ "event.clientX", "0" },
.{ "event.clientY", "0" },
.{ "event.screenX", "0" },
.{ "event.screenY", "0" },
// MouseEvent with parameters
.{ "let new_event = new MouseEvent('click', { 'button': 0, 'clientX': 10, 'clientY': 20 })", "undefined" },
.{ "new_event.button", "0" },
.{ "new_event.x", "10" },
.{ "new_event.y", "20" },
.{ "new_event.screenX", "10" },
.{ "new_event.screenY", "20" },
// MouseEvent Listener
.{ "let me = new MouseEvent('click')", "undefined" },
.{ "me instanceof Event", "true" },
.{ "var eevt = null; function ccbk(event) { eevt = event; }", "undefined" },
.{ "document.addEventListener('click', ccbk)", "undefined" },
.{ "document.dispatchEvent(me)", "true" },
.{ "eevt.type", "click" },
.{ "eevt instanceof MouseEvent", "true" },
}, .{});
test "Browser: Events.Mouse" {
try testing.htmlRunner("events/mouse.html");
}
4 changes: 2 additions & 2 deletions src/browser/html/window.zig
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,6 @@ const TimerCallback = struct {

const testing = @import("../../testing.zig");
test "Browser: Window" {
try testing.newRunner("window/window.html");
try testing.newRunner("window/frames.html");
try testing.htmlRunner("window/window.html");
try testing.htmlRunner("window/frames.html");
}
2 changes: 1 addition & 1 deletion src/main.zig
Original file line number Diff line number Diff line change
Expand Up @@ -796,7 +796,7 @@ fn testHTTPHandler(req: *std.http.Server.Request) !void {
});
}

if (std.mem.startsWith(u8, path, "/src/browser/tests/")) {
if (std.mem.startsWith(u8, path, "/src/tests/")) {
// strip off leading / so that it's relative to CWD
return TestHTTPServer.sendFile(req, path[1..]);
}
Expand Down
4 changes: 2 additions & 2 deletions src/testing.zig
Original file line number Diff line number Diff line change
Expand Up @@ -506,7 +506,7 @@ pub fn shutdown() void {
test_app.deinit();
}

pub fn newRunner(file: []const u8) !void {
pub fn htmlRunner(file: []const u8) !void {
defer _ = arena_instance.reset(.retain_capacity);
const page = try test_session.createPage();
defer test_session.removePage();
Expand All @@ -516,7 +516,7 @@ pub fn newRunner(file: []const u8) !void {
try_catch.init(js_context);
defer try_catch.deinit();

const url = try std.fmt.allocPrint(arena_allocator, "http://localhost:9582/src/browser/tests/{s}", .{file});
const url = try std.fmt.allocPrint(arena_allocator, "http://localhost:9582/src/tests/{s}", .{file});
try page.navigate(url, .{});
page.wait(2);

Expand Down
5 changes: 5 additions & 0 deletions src/tests/browser.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<script src="testing.js"></script>
<script id=intl>
// this will crash if ICU isn't properly configured / ininitialized
testing.expectEqual("[object Intl.DateTimeFormat]", new Intl.DateTimeFormat().toString());
</script>
Loading