@@ -22,6 +22,7 @@ const generate = @import("../../runtime/generate.zig");
2222const Env = @import ("../env.zig" ).Env ;
2323const Page = @import ("../page.zig" ).Page ;
2424
25+ const urlStitch = @import ("../../url.zig" ).URL .stitch ;
2526const URL = @import ("../url/url.zig" ).URL ;
2627const Node = @import ("../dom/node.zig" ).Node ;
2728const Element = @import ("../dom/element.zig" ).Element ;
@@ -639,7 +640,7 @@ pub const HTMLInputElement = struct {
639640 pub fn set_defaultChecked (self : * parser.Input , default_checked : bool ) ! void {
640641 try parser .inputSetDefaultChecked (self , default_checked );
641642 }
642- pub fn get_from (self : * parser.Input ) ! ? * parser.Form {
643+ pub fn get_form (self : * parser.Input ) ! ? * parser.Form {
643644 return try parser .inputGetForm (self );
644645 }
645646 pub fn get_accept (self : * parser.Input ) ! []const u8 {
@@ -669,7 +670,7 @@ pub const HTMLInputElement = struct {
669670 pub fn get_maxLength (self : * parser.Input ) ! i32 {
670671 return try parser .inputGetMaxLength (self );
671672 }
672- pub fn set_maxLength (self : * parser.Input , max_length : u32 ) ! void {
673+ pub fn set_maxLength (self : * parser.Input , max_length : i32 ) ! void {
673674 try parser .inputSetMaxLength (self , max_length );
674675 }
675676 pub fn get_name (self : * parser.Input ) ! []const u8 {
@@ -687,18 +688,22 @@ pub const HTMLInputElement = struct {
687688 pub fn get_size (self : * parser.Input ) ! u32 {
688689 return try parser .inputGetSize (self );
689690 }
690- pub fn set_size (self : * parser.Input , size : u32 ) ! void {
691+ pub fn set_size (self : * parser.Input , size : i32 ) ! void {
691692 try parser .inputSetSize (self , size );
692693 }
693694 pub fn get_src (self : * parser.Input ) ! []const u8 {
694695 return try parser .inputGetSrc (self );
695696 }
696- pub fn set_src (self : * parser.Input , src : []const u8 ) ! void {
697- try parser .inputSetSrc (self , src );
697+ pub fn set_src (self : * parser.Input , src : []const u8 , page : * Page ) ! void {
698+ const new_src = try urlStitch (page .call_arena , src , page .url .raw );
699+ try parser .inputSetSrc (self , new_src );
698700 }
699701 pub fn get_type (self : * parser.Input ) ! []const u8 {
700702 return try parser .inputGetType (self );
701703 }
704+ pub fn set_type (self : * parser.Input , type_ : []const u8 ) ! void {
705+ try parser .inputSetType (self , type_ );
706+ }
702707 pub fn get_value (self : * parser.Input ) ! []const u8 {
703708 return try parser .inputGetValue (self );
704709 }
@@ -1261,3 +1266,144 @@ test "Browser.HTML.Element" {
12611266 .{ "a.href" , "https://lightpanda.io/opensource-browser/about" },
12621267 }, .{});
12631268}
1269+ test "Browser.HTML.Element.propeties" {
1270+ var runner = try testing .jsRunner (testing .tracking_allocator , .{ .url = "https://lightpanda.io/noslashattheend" });
1271+ defer runner .deinit ();
1272+ const bool_valids = [_ ]Valid {
1273+ .{ .input = "true" , .is_str = false },
1274+ .{ .input = "" , .is_str = true , .expected = "false" },
1275+ .{ .input = "13.5" , .is_str = true , .expected = "true" },
1276+ };
1277+ const str_valids = [_ ]Valid {
1278+ .{ .input = "foo" , .is_str = true },
1279+ .{ .input = "5" , .is_str = false , .expected = "5" },
1280+ .{ .input = "" , .is_str = true },
1281+ .{ .input = "document" , .is_str = false , .expected = "[object HTMLDocument]" },
1282+ };
1283+ // TODO these tests are mostly just data should we store them in Sqlite or so?
1284+ try testCreateElement (& runner , "input" );
1285+ // Valid input.form is tested separately :Browser.HTML.Element.propeties.input.form
1286+ try testProperty (& runner , "input" , "form" , "null" , "null" , &.{}, &.{.{ .input = "foo" , .is_str = true }});
1287+ try testProperty (& runner , "input" , "accept" , "" , "" , & str_valids , &.{});
1288+ try testProperty (& runner , "input" , "alt" , "" , "" , & str_valids , &.{});
1289+ try testProperty (& runner , "input" , "disabled" , "false" , "false" , & bool_valids , &.{});
1290+ try testProperty (& runner , "input" , "maxLength" , "-1" , "0" , &.{.{ .input = "5" , .is_str = false }}, &.{.{ .input = "banana" , .is_str = true }});
1291+ try testing .expectError (error .ExecutionError , runner .testCases (&.{.{ "elem_input.maxLength = -45" , null }}, .{}));
1292+ try testProperty (& runner , "input" , "name" , "" , "" , & str_valids , &.{});
1293+ try testProperty (& runner , "input" , "readOnly" , "false" , "false" , & bool_valids , &.{});
1294+ try testProperty (& runner , "input" , "size" , "20" , "20" , &.{.{ .input = "5" , .is_str = false }}, &.{.{ .input = "-26" , .is_str = false }});
1295+ try testing .expectError (error .ExecutionError , runner .testCases (&.{.{ "elem_input.size = 0" , null }}, .{}));
1296+ try testing .expectError (error .ExecutionError , runner .testCases (&.{.{ "elem_input.size = 'banana'" , null }}, .{}));
1297+ try testProperty (& runner , "input" , "src" , "" , "" , &.{
1298+ .{ .input = "foo" , .is_str = true , .expected = "https://lightpanda.io/foo" }, // TODO stitch should work with spaces -> %20
1299+ .{ .input = "-3" , .is_str = false , .expected = "https://lightpanda.io/-3" },
1300+ .{ .input = "" , .is_str = true , .expected = "https://lightpanda.io/noslashattheend" },
1301+ }, &.{});
1302+ try testProperty (& runner , "input" , "type" , "text" , "text" , &.{.{ .input = "checkbox" , .is_str = true }}, &.{.{ .input = "5" , .is_str = true }});
1303+
1304+ // Properties that are related
1305+ try runner .testCases (&.{
1306+ .{ "let input_checked = document.createElement('input')" , null },
1307+ .{ "input_checked.defaultChecked" , "false" },
1308+ .{ "input_checked.checked" , "false" },
1309+
1310+ .{ "input_checked.defaultChecked = true" , "true" },
1311+ .{ "input_checked.defaultChecked" , "true" },
1312+ .{ "input_checked.checked" , "true" }, // Also perceived as true
1313+
1314+ .{ "input_checked.checked = false" , "false" },
1315+ .{ "input_checked.defaultChecked" , "true" },
1316+ .{ "input_checked.checked" , "false" },
1317+
1318+ .{ "input_checked.defaultChecked = true" , "true" },
1319+ .{ "input_checked.checked" , "false" }, // Still false
1320+ }, .{});
1321+ try runner .testCases (&.{
1322+ .{ "let input_value = document.createElement('input')" , null },
1323+ .{ "input_value.defaultValue" , "" },
1324+ .{ "input_value.value" , "" },
1325+
1326+ .{ "input_value.defaultValue = 3.1" , "3.1" },
1327+ .{ "input_value.defaultValue" , "3.1" },
1328+ .{ "input_value.value" , "3.1" }, // Also perceived as 3.1
1329+
1330+ .{ "input_value.value = 'mango'" , "mango" },
1331+ .{ "input_value.defaultValue" , "3.1" },
1332+ .{ "input_value.value" , "mango" },
1333+
1334+ .{ "input_value.defaultValue = true" , "true" },
1335+ .{ "input_value.value" , "mango" }, // Still mango
1336+ }, .{});
1337+ }
1338+ test "Browser.HTML.Element.propeties.input.form" {
1339+ var runner = try testing .jsRunner (testing .tracking_allocator , .{ .html =
1340+ \\ <form action="test.php" target="_blank">
1341+ \\ <p>
1342+ \\ <label>First name: <input type="text" name="first-name" /></label>
1343+ \\ </p>
1344+ \\ </form>
1345+ });
1346+ defer runner .deinit ();
1347+
1348+ try runner .testCases (&.{
1349+ .{ "let elem_input = document.querySelector('input')" , null },
1350+ }, .{});
1351+ try testProperty (& runner , "input" , "form" , "[object HTMLFormElement]" , "[object HTMLFormElement]" , &.{}, &.{.{ .input = "5" , .is_str = false }});
1352+ }
1353+
1354+ const Valid = struct {
1355+ input : []const u8 ,
1356+ is_str : bool ,
1357+ expected : ? []const u8 = null , // Needed when input != expected
1358+ };
1359+ const Invalid = struct {
1360+ input : []const u8 ,
1361+ is_str : bool ,
1362+ };
1363+
1364+ fn testCreateElement (runner : * testing.JsRunner , comptime name : []const u8 ) ! void {
1365+ try runner .testCases (&.{
1366+ .{ "let elem_" ++ name ++ " = document.createElement('" ++ name ++ "')" , null },
1367+ }, .{});
1368+ }
1369+ // TODO reduce comptime
1370+ // Default is the expected value after creation and after setting an invalid value
1371+ // Valid input is expected to return itself or the expected value
1372+ // Invalid input is expected to return the default value
1373+ // .{ "elem.type", "text" }, // default
1374+ // .{ "elem.type = 'checkbox'", "checkbox" }, // valid
1375+ // .{ "elem.type", "checkbox" },
1376+ // .{ "elem.type = '5'", "5" }, // invalid
1377+ // .{ "elem.type", "text" },
1378+ fn testProperty (
1379+ runner : * testing.JsRunner ,
1380+ comptime name : []const u8 ,
1381+ comptime property : []const u8 ,
1382+ comptime initial : []const u8 ,
1383+ comptime default : []const u8 ,
1384+ comptime valids : []const Valid ,
1385+ comptime invalids : []const Invalid ,
1386+ ) ! void {
1387+ const elem_dot_prop = "elem_" ++ name ++ "." ++ property ;
1388+
1389+ try runner .testCases (&.{
1390+ .{ elem_dot_prop , initial },
1391+ }, .{});
1392+
1393+ inline for (valids ) | valid | {
1394+ const set_input = if (valid .is_str ) "'" ++ valid .input ++ "'" else valid .input ;
1395+ const expected = valid .expected orelse valid .input ;
1396+ try runner .testCases (&.{
1397+ .{ elem_dot_prop ++ " = " ++ set_input , null },
1398+ .{ elem_dot_prop , expected },
1399+ }, .{});
1400+ }
1401+
1402+ inline for (invalids ) | invalid | {
1403+ const set_input = if (invalid .is_str ) "'" ++ invalid .input ++ "'" else invalid .input ;
1404+ try runner .testCases (&.{
1405+ .{ elem_dot_prop ++ " = " ++ set_input , null },
1406+ .{ elem_dot_prop , default },
1407+ }, .{});
1408+ }
1409+ }
0 commit comments