mirror of
https://github.com/lightpanda-io/browser.git
synced 2025-10-29 07:03:29 +00:00
input prop testing
This commit is contained in:
@@ -22,6 +22,7 @@ const generate = @import("../../runtime/generate.zig");
|
|||||||
const Env = @import("../env.zig").Env;
|
const Env = @import("../env.zig").Env;
|
||||||
const Page = @import("../page.zig").Page;
|
const Page = @import("../page.zig").Page;
|
||||||
|
|
||||||
|
const urlStitch = @import("../../url.zig").URL.stitch;
|
||||||
const URL = @import("../url/url.zig").URL;
|
const URL = @import("../url/url.zig").URL;
|
||||||
const Node = @import("../dom/node.zig").Node;
|
const Node = @import("../dom/node.zig").Node;
|
||||||
const Element = @import("../dom/element.zig").Element;
|
const Element = @import("../dom/element.zig").Element;
|
||||||
@@ -639,7 +640,7 @@ pub const HTMLInputElement = struct {
|
|||||||
pub fn set_defaultChecked(self: *parser.Input, default_checked: bool) !void {
|
pub fn set_defaultChecked(self: *parser.Input, default_checked: bool) !void {
|
||||||
try parser.inputSetDefaultChecked(self, default_checked);
|
try parser.inputSetDefaultChecked(self, default_checked);
|
||||||
}
|
}
|
||||||
pub fn get_from(self: *parser.Input) !?*parser.Form {
|
pub fn get_form(self: *parser.Input) !?*parser.Form {
|
||||||
return try parser.inputGetForm(self);
|
return try parser.inputGetForm(self);
|
||||||
}
|
}
|
||||||
pub fn get_accept(self: *parser.Input) ![]const u8 {
|
pub fn get_accept(self: *parser.Input) ![]const u8 {
|
||||||
@@ -669,7 +670,7 @@ pub const HTMLInputElement = struct {
|
|||||||
pub fn get_maxLength(self: *parser.Input) !i32 {
|
pub fn get_maxLength(self: *parser.Input) !i32 {
|
||||||
return try parser.inputGetMaxLength(self);
|
return try parser.inputGetMaxLength(self);
|
||||||
}
|
}
|
||||||
pub fn set_maxLength(self: *parser.Input, max_length: u32) !void {
|
pub fn set_maxLength(self: *parser.Input, max_length: i32) !void {
|
||||||
try parser.inputSetMaxLength(self, max_length);
|
try parser.inputSetMaxLength(self, max_length);
|
||||||
}
|
}
|
||||||
pub fn get_name(self: *parser.Input) ![]const u8 {
|
pub fn get_name(self: *parser.Input) ![]const u8 {
|
||||||
@@ -687,18 +688,22 @@ pub const HTMLInputElement = struct {
|
|||||||
pub fn get_size(self: *parser.Input) !u32 {
|
pub fn get_size(self: *parser.Input) !u32 {
|
||||||
return try parser.inputGetSize(self);
|
return try parser.inputGetSize(self);
|
||||||
}
|
}
|
||||||
pub fn set_size(self: *parser.Input, size: u32) !void {
|
pub fn set_size(self: *parser.Input, size: i32) !void {
|
||||||
try parser.inputSetSize(self, size);
|
try parser.inputSetSize(self, size);
|
||||||
}
|
}
|
||||||
pub fn get_src(self: *parser.Input) ![]const u8 {
|
pub fn get_src(self: *parser.Input) ![]const u8 {
|
||||||
return try parser.inputGetSrc(self);
|
return try parser.inputGetSrc(self);
|
||||||
}
|
}
|
||||||
pub fn set_src(self: *parser.Input, src: []const u8) !void {
|
pub fn set_src(self: *parser.Input, src: []const u8, page: *Page) !void {
|
||||||
try parser.inputSetSrc(self, src);
|
const new_src = try urlStitch(page.call_arena, src, page.url.raw);
|
||||||
|
try parser.inputSetSrc(self, new_src);
|
||||||
}
|
}
|
||||||
pub fn get_type(self: *parser.Input) ![]const u8 {
|
pub fn get_type(self: *parser.Input) ![]const u8 {
|
||||||
return try parser.inputGetType(self);
|
return try parser.inputGetType(self);
|
||||||
}
|
}
|
||||||
|
pub fn set_type(self: *parser.Input, type_: []const u8) !void {
|
||||||
|
try parser.inputSetType(self, type_);
|
||||||
|
}
|
||||||
pub fn get_value(self: *parser.Input) ![]const u8 {
|
pub fn get_value(self: *parser.Input) ![]const u8 {
|
||||||
return try parser.inputGetValue(self);
|
return try parser.inputGetValue(self);
|
||||||
}
|
}
|
||||||
@@ -1261,3 +1266,144 @@ test "Browser.HTML.Element" {
|
|||||||
.{ "a.href", "https://lightpanda.io/opensource-browser/about" },
|
.{ "a.href", "https://lightpanda.io/opensource-browser/about" },
|
||||||
}, .{});
|
}, .{});
|
||||||
}
|
}
|
||||||
|
test "Browser.HTML.Element.propeties" {
|
||||||
|
var runner = try testing.jsRunner(testing.tracking_allocator, .{ .url = "https://lightpanda.io/noslashattheend" });
|
||||||
|
defer runner.deinit();
|
||||||
|
const bool_valids = [_]Valid{
|
||||||
|
.{ .input = "true", .is_str = false },
|
||||||
|
.{ .input = "", .is_str = true, .expected = "false" },
|
||||||
|
.{ .input = "13.5", .is_str = true, .expected = "true" },
|
||||||
|
};
|
||||||
|
const str_valids = [_]Valid{
|
||||||
|
.{ .input = "foo", .is_str = true },
|
||||||
|
.{ .input = "5", .is_str = false, .expected = "5" },
|
||||||
|
.{ .input = "", .is_str = true },
|
||||||
|
.{ .input = "document", .is_str = false, .expected = "[object HTMLDocument]" },
|
||||||
|
};
|
||||||
|
// TODO these tests are mostly just data should we store them in Sqlite or so?
|
||||||
|
try testCreateElement(&runner, "input");
|
||||||
|
// Valid input.form is tested separately :Browser.HTML.Element.propeties.input.form
|
||||||
|
try testProperty(&runner, "input", "form", "null", "null", &.{}, &.{.{ .input = "foo", .is_str = true }});
|
||||||
|
try testProperty(&runner, "input", "accept", "", "", &str_valids, &.{});
|
||||||
|
try testProperty(&runner, "input", "alt", "", "", &str_valids, &.{});
|
||||||
|
try testProperty(&runner, "input", "disabled", "false", "false", &bool_valids, &.{});
|
||||||
|
try testProperty(&runner, "input", "maxLength", "-1", "0", &.{.{ .input = "5", .is_str = false }}, &.{.{ .input = "banana", .is_str = true }});
|
||||||
|
try testing.expectError(error.ExecutionError, runner.testCases(&.{.{ "elem_input.maxLength = -45", null }}, .{}));
|
||||||
|
try testProperty(&runner, "input", "name", "", "", &str_valids, &.{});
|
||||||
|
try testProperty(&runner, "input", "readOnly", "false", "false", &bool_valids, &.{});
|
||||||
|
try testProperty(&runner, "input", "size", "20", "20", &.{.{ .input = "5", .is_str = false }}, &.{.{ .input = "-26", .is_str = false }});
|
||||||
|
try testing.expectError(error.ExecutionError, runner.testCases(&.{.{ "elem_input.size = 0", null }}, .{}));
|
||||||
|
try testing.expectError(error.ExecutionError, runner.testCases(&.{.{ "elem_input.size = 'banana'", null }}, .{}));
|
||||||
|
try testProperty(&runner, "input", "src", "", "", &.{
|
||||||
|
.{ .input = "foo", .is_str = true, .expected = "https://lightpanda.io/foo" }, // TODO stitch should work with spaces -> %20
|
||||||
|
.{ .input = "-3", .is_str = false, .expected = "https://lightpanda.io/-3" },
|
||||||
|
.{ .input = "", .is_str = true, .expected = "https://lightpanda.io/noslashattheend" },
|
||||||
|
}, &.{});
|
||||||
|
try testProperty(&runner, "input", "type", "text", "text", &.{.{ .input = "checkbox", .is_str = true }}, &.{.{ .input = "5", .is_str = true }});
|
||||||
|
|
||||||
|
// Properties that are related
|
||||||
|
try runner.testCases(&.{
|
||||||
|
.{ "let input_checked = document.createElement('input')", null },
|
||||||
|
.{ "input_checked.defaultChecked", "false" },
|
||||||
|
.{ "input_checked.checked", "false" },
|
||||||
|
|
||||||
|
.{ "input_checked.defaultChecked = true", "true" },
|
||||||
|
.{ "input_checked.defaultChecked", "true" },
|
||||||
|
.{ "input_checked.checked", "true" }, // Also perceived as true
|
||||||
|
|
||||||
|
.{ "input_checked.checked = false", "false" },
|
||||||
|
.{ "input_checked.defaultChecked", "true" },
|
||||||
|
.{ "input_checked.checked", "false" },
|
||||||
|
|
||||||
|
.{ "input_checked.defaultChecked = true", "true" },
|
||||||
|
.{ "input_checked.checked", "false" }, // Still false
|
||||||
|
}, .{});
|
||||||
|
try runner.testCases(&.{
|
||||||
|
.{ "let input_value = document.createElement('input')", null },
|
||||||
|
.{ "input_value.defaultValue", "" },
|
||||||
|
.{ "input_value.value", "" },
|
||||||
|
|
||||||
|
.{ "input_value.defaultValue = 3.1", "3.1" },
|
||||||
|
.{ "input_value.defaultValue", "3.1" },
|
||||||
|
.{ "input_value.value", "3.1" }, // Also perceived as 3.1
|
||||||
|
|
||||||
|
.{ "input_value.value = 'mango'", "mango" },
|
||||||
|
.{ "input_value.defaultValue", "3.1" },
|
||||||
|
.{ "input_value.value", "mango" },
|
||||||
|
|
||||||
|
.{ "input_value.defaultValue = true", "true" },
|
||||||
|
.{ "input_value.value", "mango" }, // Still mango
|
||||||
|
}, .{});
|
||||||
|
}
|
||||||
|
test "Browser.HTML.Element.propeties.input.form" {
|
||||||
|
var runner = try testing.jsRunner(testing.tracking_allocator, .{ .html =
|
||||||
|
\\ <form action="test.php" target="_blank">
|
||||||
|
\\ <p>
|
||||||
|
\\ <label>First name: <input type="text" name="first-name" /></label>
|
||||||
|
\\ </p>
|
||||||
|
\\ </form>
|
||||||
|
});
|
||||||
|
defer runner.deinit();
|
||||||
|
|
||||||
|
try runner.testCases(&.{
|
||||||
|
.{ "let elem_input = document.querySelector('input')", null },
|
||||||
|
}, .{});
|
||||||
|
try testProperty(&runner, "input", "form", "[object HTMLFormElement]", "[object HTMLFormElement]", &.{}, &.{.{ .input = "5", .is_str = false }});
|
||||||
|
}
|
||||||
|
|
||||||
|
const Valid = struct {
|
||||||
|
input: []const u8,
|
||||||
|
is_str: bool,
|
||||||
|
expected: ?[]const u8 = null, // Needed when input != expected
|
||||||
|
};
|
||||||
|
const Invalid = struct {
|
||||||
|
input: []const u8,
|
||||||
|
is_str: bool,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn testCreateElement(runner: *testing.JsRunner, comptime name: []const u8) !void {
|
||||||
|
try runner.testCases(&.{
|
||||||
|
.{ "let elem_" ++ name ++ " = document.createElement('" ++ name ++ "')", null },
|
||||||
|
}, .{});
|
||||||
|
}
|
||||||
|
// TODO reduce comptime
|
||||||
|
// Default is the expected value after creation and after setting an invalid value
|
||||||
|
// Valid input is expected to return itself or the expected value
|
||||||
|
// Invalid input is expected to return the default value
|
||||||
|
// .{ "elem.type", "text" }, // default
|
||||||
|
// .{ "elem.type = 'checkbox'", "checkbox" }, // valid
|
||||||
|
// .{ "elem.type", "checkbox" },
|
||||||
|
// .{ "elem.type = '5'", "5" }, // invalid
|
||||||
|
// .{ "elem.type", "text" },
|
||||||
|
fn testProperty(
|
||||||
|
runner: *testing.JsRunner,
|
||||||
|
comptime name: []const u8,
|
||||||
|
comptime property: []const u8,
|
||||||
|
comptime initial: []const u8,
|
||||||
|
comptime default: []const u8,
|
||||||
|
comptime valids: []const Valid,
|
||||||
|
comptime invalids: []const Invalid,
|
||||||
|
) !void {
|
||||||
|
const elem_dot_prop = "elem_" ++ name ++ "." ++ property;
|
||||||
|
|
||||||
|
try runner.testCases(&.{
|
||||||
|
.{ elem_dot_prop, initial },
|
||||||
|
}, .{});
|
||||||
|
|
||||||
|
inline for (valids) |valid| {
|
||||||
|
const set_input = if (valid.is_str) "'" ++ valid.input ++ "'" else valid.input;
|
||||||
|
const expected = valid.expected orelse valid.input;
|
||||||
|
try runner.testCases(&.{
|
||||||
|
.{ elem_dot_prop ++ " = " ++ set_input, null },
|
||||||
|
.{ elem_dot_prop, expected },
|
||||||
|
}, .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
inline for (invalids) |invalid| {
|
||||||
|
const set_input = if (invalid.is_str) "'" ++ invalid.input ++ "'" else invalid.input;
|
||||||
|
try runner.testCases(&.{
|
||||||
|
.{ elem_dot_prop ++ " = " ++ set_input, null },
|
||||||
|
.{ elem_dot_prop, default },
|
||||||
|
}, .{});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -2687,8 +2687,9 @@ pub fn inputGetMaxLength(input: *Input) !i32 {
|
|||||||
try DOMErr(err);
|
try DOMErr(err);
|
||||||
return max_length;
|
return max_length;
|
||||||
}
|
}
|
||||||
pub fn inputSetMaxLength(input: *Input, max_length: u32) !void {
|
pub fn inputSetMaxLength(input: *Input, max_length: i32) !void {
|
||||||
const err = c.dom_html_input_element_set_max_length(input, max_length);
|
if (max_length < 0) return error.NegativeValueNotAllowed;
|
||||||
|
const err = c.dom_html_input_element_set_max_length(input, @intCast(max_length));
|
||||||
try DOMErr(err);
|
try DOMErr(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2720,8 +2721,10 @@ pub fn inputGetSize(input: *Input) !u32 {
|
|||||||
if (size == ulongNegativeOne) return 20; // 20
|
if (size == ulongNegativeOne) return 20; // 20
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
pub fn inputSetSize(input: *Input, size: u32) !void {
|
pub fn inputSetSize(input: *Input, size: i32) !void {
|
||||||
const err = c.dom_html_input_element_set_size(input, size);
|
if (size == 0) return error.ZeroNotAllowed;
|
||||||
|
const new_size = if (size < 0) 20 else size;
|
||||||
|
const err = c.dom_html_input_element_set_size(input, @intCast(new_size));
|
||||||
try DOMErr(err);
|
try DOMErr(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2732,6 +2735,7 @@ pub fn inputGetSrc(input: *Input) ![]const u8 {
|
|||||||
const s = s_ orelse return "";
|
const s = s_ orelse return "";
|
||||||
return strToData(s);
|
return strToData(s);
|
||||||
}
|
}
|
||||||
|
// url should already be stitched!
|
||||||
pub fn inputSetSrc(input: *Input, src: []const u8) !void {
|
pub fn inputSetSrc(input: *Input, src: []const u8) !void {
|
||||||
const err = c.dom_html_input_element_set_src(input, try strFromData(src));
|
const err = c.dom_html_input_element_set_src(input, try strFromData(src));
|
||||||
try DOMErr(err);
|
try DOMErr(err);
|
||||||
@@ -2741,9 +2745,22 @@ pub fn inputGetType(input: *Input) ![]const u8 {
|
|||||||
var s_: ?*String = null;
|
var s_: ?*String = null;
|
||||||
const err = c.dom_html_input_element_get_type(input, &s_);
|
const err = c.dom_html_input_element_get_type(input, &s_);
|
||||||
try DOMErr(err);
|
try DOMErr(err);
|
||||||
const s = s_ orelse return "";
|
const s = s_ orelse return "text";
|
||||||
return strToData(s);
|
return strToData(s);
|
||||||
}
|
}
|
||||||
|
pub fn inputSetType(input: *Input, type_: []const u8) !void {
|
||||||
|
// @speed sort values by usage frequency/length
|
||||||
|
const possible_values = [_][]const u8{ "text", "search", "tel", "url", "email", "password", "date", "month", "week", "time", "datetime-local", "number", "range", "color", "checkbox", "radio", "file", "hidden", "image", "button", "submit", "reset" };
|
||||||
|
var found = false;
|
||||||
|
for (possible_values) |item| {
|
||||||
|
if (std.mem.eql(u8, type_, item)) {
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const new_type = if (found) type_ else "text";
|
||||||
|
try elementSetAttribute(@ptrCast(input), "type", new_type);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn inputGetValue(input: *Input) ![]const u8 {
|
pub fn inputGetValue(input: *Input) ![]const u8 {
|
||||||
var s_: ?*String = null;
|
var s_: ?*String = null;
|
||||||
|
|||||||
15
src/url.zig
15
src/url.zig
@@ -110,6 +110,11 @@ pub const URL = struct {
|
|||||||
}
|
}
|
||||||
return src;
|
return src;
|
||||||
}
|
}
|
||||||
|
if (src.len == 0) {
|
||||||
|
if (opts.alloc == .always) {
|
||||||
|
return allocator.dupe(u8, base);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const protocol_end: usize = blk: {
|
const protocol_end: usize = blk: {
|
||||||
if (std.mem.indexOf(u8, base, "://")) |protocol_index| {
|
if (std.mem.indexOf(u8, base, "://")) |protocol_index| {
|
||||||
@@ -256,6 +261,16 @@ test "URL: Stiching src as full path" {
|
|||||||
try testing.expectString("https://lightpanda.io/something.js", result);
|
try testing.expectString("https://lightpanda.io/something.js", result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "URL: Stitching Base & Src URLs (empty src)" {
|
||||||
|
const allocator = testing.allocator;
|
||||||
|
|
||||||
|
const base = "https://www.google.com/xyz/abc/123";
|
||||||
|
const src = "";
|
||||||
|
const result = try URL.stitch(allocator, src, base);
|
||||||
|
defer allocator.free(result);
|
||||||
|
try testing.expectString("https://www.google.com/xyz/abc/123", result);
|
||||||
|
}
|
||||||
|
|
||||||
test "URL: concatQueryString" {
|
test "URL: concatQueryString" {
|
||||||
defer testing.reset();
|
defer testing.reset();
|
||||||
const arena = testing.arena_allocator;
|
const arena = testing.arena_allocator;
|
||||||
|
|||||||
2
vendor/netsurf/libdom
vendored
2
vendor/netsurf/libdom
vendored
Submodule vendor/netsurf/libdom updated: f8fc21702b...9a1d1e41d1
Reference in New Issue
Block a user