@@ -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 ;
@@ -216,8 +217,7 @@ pub const HTMLAnchorElement = struct {
216
217
}
217
218
218
219
pub fn set_href (self : * parser.Anchor , href : []const u8 , page : * const Page ) ! void {
219
- const stitch = @import ("../../url.zig" ).stitch ;
220
- const full = try stitch (page .call_arena , href , page .url .raw , .{});
220
+ const full = try urlStitch (page .call_arena , href , page .url .raw , .{});
221
221
return try parser .anchorSetHref (self , full );
222
222
}
223
223
@@ -647,6 +647,89 @@ pub const HTMLInputElement = struct {
647
647
pub const Self = parser .Input ;
648
648
pub const prototype = * HTMLElement ;
649
649
pub const subtype = .node ;
650
+
651
+ pub fn get_defaultValue (self : * parser.Input ) ! []const u8 {
652
+ return try parser .inputGetDefaultValue (self );
653
+ }
654
+ pub fn set_defaultValue (self : * parser.Input , default_value : []const u8 ) ! void {
655
+ try parser .inputSetDefaultValue (self , default_value );
656
+ }
657
+ pub fn get_defaultChecked (self : * parser.Input ) ! bool {
658
+ return try parser .inputGetDefaultChecked (self );
659
+ }
660
+ pub fn set_defaultChecked (self : * parser.Input , default_checked : bool ) ! void {
661
+ try parser .inputSetDefaultChecked (self , default_checked );
662
+ }
663
+ pub fn get_form (self : * parser.Input ) ! ? * parser.Form {
664
+ return try parser .inputGetForm (self );
665
+ }
666
+ pub fn get_accept (self : * parser.Input ) ! []const u8 {
667
+ return try parser .inputGetAccept (self );
668
+ }
669
+ pub fn set_accept (self : * parser.Input , accept : []const u8 ) ! void {
670
+ try parser .inputSetAccept (self , accept );
671
+ }
672
+ pub fn get_alt (self : * parser.Input ) ! []const u8 {
673
+ return try parser .inputGetAlt (self );
674
+ }
675
+ pub fn set_alt (self : * parser.Input , alt : []const u8 ) ! void {
676
+ try parser .inputSetAlt (self , alt );
677
+ }
678
+ pub fn get_checked (self : * parser.Input ) ! bool {
679
+ return try parser .inputGetChecked (self );
680
+ }
681
+ pub fn set_checked (self : * parser.Input , checked : bool ) ! void {
682
+ try parser .inputSetChecked (self , checked );
683
+ }
684
+ pub fn get_disabled (self : * parser.Input ) ! bool {
685
+ return try parser .inputGetDisabled (self );
686
+ }
687
+ pub fn set_disabled (self : * parser.Input , disabled : bool ) ! void {
688
+ try parser .inputSetDisabled (self , disabled );
689
+ }
690
+ pub fn get_maxLength (self : * parser.Input ) ! i32 {
691
+ return try parser .inputGetMaxLength (self );
692
+ }
693
+ pub fn set_maxLength (self : * parser.Input , max_length : i32 ) ! void {
694
+ try parser .inputSetMaxLength (self , max_length );
695
+ }
696
+ pub fn get_name (self : * parser.Input ) ! []const u8 {
697
+ return try parser .inputGetName (self );
698
+ }
699
+ pub fn set_name (self : * parser.Input , name : []const u8 ) ! void {
700
+ try parser .inputSetName (self , name );
701
+ }
702
+ pub fn get_readOnly (self : * parser.Input ) ! bool {
703
+ return try parser .inputGetReadOnly (self );
704
+ }
705
+ pub fn set_readOnly (self : * parser.Input , read_only : bool ) ! void {
706
+ try parser .inputSetReadOnly (self , read_only );
707
+ }
708
+ pub fn get_size (self : * parser.Input ) ! u32 {
709
+ return try parser .inputGetSize (self );
710
+ }
711
+ pub fn set_size (self : * parser.Input , size : i32 ) ! void {
712
+ try parser .inputSetSize (self , size );
713
+ }
714
+ pub fn get_src (self : * parser.Input ) ! []const u8 {
715
+ return try parser .inputGetSrc (self );
716
+ }
717
+ pub fn set_src (self : * parser.Input , src : []const u8 , page : * Page ) ! void {
718
+ const new_src = try urlStitch (page .call_arena , src , page .url .raw , .{ .alloc = .if_needed });
719
+ try parser .inputSetSrc (self , new_src );
720
+ }
721
+ pub fn get_type (self : * parser.Input ) ! []const u8 {
722
+ return try parser .inputGetType (self );
723
+ }
724
+ pub fn set_type (self : * parser.Input , type_ : []const u8 ) ! void {
725
+ try parser .inputSetType (self , type_ );
726
+ }
727
+ pub fn get_value (self : * parser.Input ) ! []const u8 {
728
+ return try parser .inputGetValue (self );
729
+ }
730
+ pub fn set_value (self : * parser.Input , value : []const u8 ) ! void {
731
+ try parser .inputSetValue (self , value );
732
+ }
650
733
};
651
734
652
735
pub const HTMLLIElement = struct {
@@ -1210,3 +1293,139 @@ test "Browser.HTML.Element" {
1210
1293
.{ "document.activeElement === focused" , "true" },
1211
1294
}, .{});
1212
1295
}
1296
+ test "Browser.HTML.HtmlInputElement.propeties" {
1297
+ var runner = try testing .jsRunner (testing .tracking_allocator , .{ .url = "https://lightpanda.io/noslashattheend" });
1298
+ defer runner .deinit ();
1299
+ var alloc = std .heap .ArenaAllocator .init (runner .app .allocator );
1300
+ defer alloc .deinit ();
1301
+ const arena = alloc .allocator ();
1302
+
1303
+ try runner .testCases (&.{.{ "let elem_input = document.createElement('input')" , null }}, .{});
1304
+
1305
+ try runner .testCases (&.{.{ "elem_input.form" , "null" }}, .{}); // Initial value
1306
+ // Valid input.form is tested separately :Browser.HTML.HtmlInputElement.propeties.form
1307
+ try testProperty (arena , & runner , "elem_input.form" , "null" , &.{.{ .input = "'foo'" }}); // Invalid
1308
+
1309
+ try runner .testCases (&.{.{ "elem_input.accept" , "" }}, .{}); // Initial value
1310
+ try testProperty (arena , & runner , "elem_input.accept" , null , & str_valids ); // Valid
1311
+
1312
+ try runner .testCases (&.{.{ "elem_input.alt" , "" }}, .{}); // Initial value
1313
+ try testProperty (arena , & runner , "elem_input.alt" , null , & str_valids ); // Valid
1314
+
1315
+ try runner .testCases (&.{.{ "elem_input.disabled" , "false" }}, .{}); // Initial value
1316
+ try testProperty (arena , & runner , "elem_input.disabled" , null , & bool_valids ); // Valid
1317
+
1318
+ try runner .testCases (&.{.{ "elem_input.maxLength" , "-1" }}, .{}); // Initial value
1319
+ try testProperty (arena , & runner , "elem_input.maxLength" , null , &.{.{ .input = "5" }}); // Valid
1320
+ try testProperty (arena , & runner , "elem_input.maxLength" , "0" , &.{.{ .input = "'banana'" }}); // Invalid
1321
+ try testing .expectError (error .ExecutionError , runner .testCases (&.{.{ "elem_input.maxLength = -45" , null }}, .{})); // Error
1322
+
1323
+ try runner .testCases (&.{.{ "elem_input.name" , "" }}, .{}); // Initial value
1324
+ try testProperty (arena , & runner , "elem_input.name" , null , & str_valids ); // Valid
1325
+
1326
+ try runner .testCases (&.{.{ "elem_input.readOnly" , "false" }}, .{}); // Initial value
1327
+ try testProperty (arena , & runner , "elem_input.readOnly" , null , & bool_valids ); // Valid
1328
+
1329
+ try runner .testCases (&.{.{ "elem_input.size" , "20" }}, .{}); // Initial value
1330
+ try testProperty (arena , & runner , "elem_input.size" , null , &.{.{ .input = "5" }}); // Valid
1331
+ try testProperty (arena , & runner , "elem_input.size" , "20" , &.{.{ .input = "-26" }}); // Invalid
1332
+ try testing .expectError (error .ExecutionError , runner .testCases (&.{.{ "elem_input.size = 0" , null }}, .{})); // Error
1333
+ try testing .expectError (error .ExecutionError , runner .testCases (&.{.{ "elem_input.size = 'banana'" , null }}, .{})); // Error
1334
+
1335
+ try runner .testCases (&.{.{ "elem_input.src" , "" }}, .{}); // Initial value
1336
+ try testProperty (arena , & runner , "elem_input.src" , null , &.{
1337
+ .{ .input = "'foo'" , .expected = "https://lightpanda.io/foo" }, // TODO stitch should work with spaces -> %20
1338
+ .{ .input = "-3" , .expected = "https://lightpanda.io/-3" },
1339
+ .{ .input = "''" , .expected = "https://lightpanda.io/noslashattheend" },
1340
+ });
1341
+
1342
+ try runner .testCases (&.{.{ "elem_input.type" , "text" }}, .{}); // Initial value
1343
+ try testProperty (arena , & runner , "elem_input.type" , null , &.{.{ .input = "'checkbox'" , .expected = "checkbox" }}); // Valid
1344
+ try testProperty (arena , & runner , "elem_input.type" , "text" , &.{.{ .input = "'5'" }}); // Invalid
1345
+
1346
+ // Properties that are related
1347
+ try runner .testCases (&.{
1348
+ .{ "let input_checked = document.createElement('input')" , null },
1349
+ .{ "input_checked.defaultChecked" , "false" },
1350
+ .{ "input_checked.checked" , "false" },
1351
+
1352
+ .{ "input_checked.defaultChecked = true" , "true" },
1353
+ .{ "input_checked.defaultChecked" , "true" },
1354
+ .{ "input_checked.checked" , "true" }, // Also perceived as true
1355
+
1356
+ .{ "input_checked.checked = false" , "false" },
1357
+ .{ "input_checked.defaultChecked" , "true" },
1358
+ .{ "input_checked.checked" , "false" },
1359
+
1360
+ .{ "input_checked.defaultChecked = true" , "true" },
1361
+ .{ "input_checked.checked" , "false" }, // Still false
1362
+ }, .{});
1363
+ try runner .testCases (&.{
1364
+ .{ "let input_value = document.createElement('input')" , null },
1365
+ .{ "input_value.defaultValue" , "" },
1366
+ .{ "input_value.value" , "" },
1367
+
1368
+ .{ "input_value.defaultValue = 3.1" , "3.1" },
1369
+ .{ "input_value.defaultValue" , "3.1" },
1370
+ .{ "input_value.value" , "3.1" }, // Also perceived as 3.1
1371
+
1372
+ .{ "input_value.value = 'mango'" , "mango" },
1373
+ .{ "input_value.defaultValue" , "3.1" },
1374
+ .{ "input_value.value" , "mango" },
1375
+
1376
+ .{ "input_value.defaultValue = true" , "true" },
1377
+ .{ "input_value.value" , "mango" }, // Still mango
1378
+ }, .{});
1379
+ }
1380
+ test "Browser.HTML.HtmlInputElement.propeties.form" {
1381
+ var runner = try testing .jsRunner (testing .tracking_allocator , .{ .html =
1382
+ \\ <form action="test.php" target="_blank">
1383
+ \\ <p>
1384
+ \\ <label>First name: <input type="text" name="first-name" /></label>
1385
+ \\ </p>
1386
+ \\ </form>
1387
+ });
1388
+ defer runner .deinit ();
1389
+
1390
+ try runner .testCases (&.{
1391
+ .{ "let elem_input = document.querySelector('input')" , null },
1392
+ }, .{});
1393
+ try runner .testCases (&.{.{ "elem_input.form" , "[object HTMLFormElement]" }}, .{}); // Initial value
1394
+ try runner .testCases (&.{
1395
+ .{ "elem_input.form = 'foo'" , null },
1396
+ .{ "elem_input.form" , "[object HTMLFormElement]" }, // Invalid
1397
+ }, .{});
1398
+ }
1399
+
1400
+ const Check = struct {
1401
+ input : []const u8 ,
1402
+ expected : ? []const u8 = null , // Needed when input != expected
1403
+ };
1404
+ const bool_valids = [_ ]Check {
1405
+ .{ .input = "true" },
1406
+ .{ .input = "''" , .expected = "false" },
1407
+ .{ .input = "13.5" , .expected = "true" },
1408
+ };
1409
+ const str_valids = [_ ]Check {
1410
+ .{ .input = "'foo'" , .expected = "foo" },
1411
+ .{ .input = "5" , .expected = "5" },
1412
+ .{ .input = "''" , .expected = "" },
1413
+ .{ .input = "document" , .expected = "[object HTMLDocument]" },
1414
+ };
1415
+
1416
+ // .{ "elem.type = '5'", "5" },
1417
+ // .{ "elem.type", "text" },
1418
+ fn testProperty (
1419
+ arena : std.mem.Allocator ,
1420
+ runner : * testing.JsRunner ,
1421
+ elem_dot_prop : []const u8 ,
1422
+ always : ? []const u8 , // Ignores checks' expected if set
1423
+ checks : []const Check ,
1424
+ ) ! void {
1425
+ for (checks ) | check | {
1426
+ try runner .testCases (&.{
1427
+ .{ try std .mem .concat (arena , u8 , &.{ elem_dot_prop , " = " , check .input }), null },
1428
+ .{ elem_dot_prop , always orelse check .expected orelse check .input },
1429
+ }, .{});
1430
+ }
1431
+ }
0 commit comments