@@ -22,6 +22,7 @@ const generate = @import("../../runtime/generate.zig");
22
22
const Env = @import ("../env.zig" ).Env ;
23
23
const Page = @import ("../page.zig" ).Page ;
24
24
25
+ const urlStitch = @import ("../../url.zig" ).URL .stitch ;
25
26
const URL = @import ("../url/url.zig" ).URL ;
26
27
const Node = @import ("../dom/node.zig" ).Node ;
27
28
const Element = @import ("../dom/element.zig" ).Element ;
@@ -639,7 +640,7 @@ pub const HTMLInputElement = struct {
639
640
pub fn set_defaultChecked (self : * parser.Input , default_checked : bool ) ! void {
640
641
try parser .inputSetDefaultChecked (self , default_checked );
641
642
}
642
- pub fn get_from (self : * parser.Input ) ! ? * parser.Form {
643
+ pub fn get_form (self : * parser.Input ) ! ? * parser.Form {
643
644
return try parser .inputGetForm (self );
644
645
}
645
646
pub fn get_accept (self : * parser.Input ) ! []const u8 {
@@ -669,7 +670,7 @@ pub const HTMLInputElement = struct {
669
670
pub fn get_maxLength (self : * parser.Input ) ! i32 {
670
671
return try parser .inputGetMaxLength (self );
671
672
}
672
- pub fn set_maxLength (self : * parser.Input , max_length : u32 ) ! void {
673
+ pub fn set_maxLength (self : * parser.Input , max_length : i32 ) ! void {
673
674
try parser .inputSetMaxLength (self , max_length );
674
675
}
675
676
pub fn get_name (self : * parser.Input ) ! []const u8 {
@@ -687,18 +688,22 @@ pub const HTMLInputElement = struct {
687
688
pub fn get_size (self : * parser.Input ) ! u32 {
688
689
return try parser .inputGetSize (self );
689
690
}
690
- pub fn set_size (self : * parser.Input , size : u32 ) ! void {
691
+ pub fn set_size (self : * parser.Input , size : i32 ) ! void {
691
692
try parser .inputSetSize (self , size );
692
693
}
693
694
pub fn get_src (self : * parser.Input ) ! []const u8 {
694
695
return try parser .inputGetSrc (self );
695
696
}
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 );
698
700
}
699
701
pub fn get_type (self : * parser.Input ) ! []const u8 {
700
702
return try parser .inputGetType (self );
701
703
}
704
+ pub fn set_type (self : * parser.Input , type_ : []const u8 ) ! void {
705
+ try parser .inputSetType (self , type_ );
706
+ }
702
707
pub fn get_value (self : * parser.Input ) ! []const u8 {
703
708
return try parser .inputGetValue (self );
704
709
}
@@ -1261,3 +1266,144 @@ test "Browser.HTML.Element" {
1261
1266
.{ "a.href" , "https://lightpanda.io/opensource-browser/about" },
1262
1267
}, .{});
1263
1268
}
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