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