@@ -340,4 +340,255 @@ public function testSelectWithEmptyOption()
340340 $ this ->assertStringContainsString ('<option value="1">Option 1</option> ' , $ result );
341341 $ this ->assertStringContainsString ('<option value="2">Option 2</option> ' , $ result );
342342 }
343+
344+ /**
345+ * @testdox can create a select element with icon data attributes.
346+ */
347+ public function testSelectWithIcon ()
348+ {
349+ $ result = $ this ->formBuilder ->select (
350+ name: 'my-select ' ,
351+ list: [
352+ '1 ' => 'Regular Option ' ,
353+ '2 ' => ['Option With Icon ' , 'icon-refresh ' ],
354+ ],
355+ selected: null ,
356+ options: []
357+ );
358+
359+ $ this ->assertElementIs ('select ' , $ result );
360+ $ this ->assertElementAttributeEquals ('name ' , 'my-select ' , $ result );
361+ $ this ->assertStringContainsString ('<option value="1">Regular Option</option> ' , $ result );
362+ $ this ->assertStringContainsString ('<option value="2" data-icon="icon-refresh">Option With Icon</option> ' , $ result );
363+ }
364+
365+ /**
366+ * @testdox can create a select element with image data attributes.
367+ */
368+ public function testSelectWithImage ()
369+ {
370+ $ result = $ this ->formBuilder ->select (
371+ name: 'my-select ' ,
372+ list: [
373+ '1 ' => 'Regular Option ' ,
374+ '2 ' => ['Option With Image ' , 'myImage.jpeg ' ],
375+ ],
376+ selected: null ,
377+ options: []
378+ );
379+
380+ $ this ->assertElementIs ('select ' , $ result );
381+ $ this ->assertElementAttributeEquals ('name ' , 'my-select ' , $ result );
382+ $ this ->assertStringContainsString ('<option value="1">Regular Option</option> ' , $ result );
383+ $ this ->assertStringContainsString ('<option value="2" data-image="myImage.jpeg">Option With Image</option> ' , $ result );
384+ }
385+
386+ /**
387+ * @testdox can create a select element with image data attributes.
388+ */
389+ public function testSelectWithSelectedImage ()
390+ {
391+ $ result = $ this ->formBuilder ->select (
392+ name: 'my-select ' ,
393+ list: [
394+ '1 ' => 'Regular Option ' ,
395+ '2 ' => ['Option With Image ' , 'myImage.jpeg ' ],
396+ ],
397+ selected: '2 ' ,
398+ options: []
399+ );
400+
401+ $ this ->assertElementIs ('select ' , $ result );
402+ $ this ->assertElementAttributeEquals ('name ' , 'my-select ' , $ result );
403+ $ this ->assertStringContainsString ('<option value="1">Regular Option</option> ' , $ result );
404+ $ this ->assertStringContainsString ('<option value="2" selected="selected" data-image="myImage.jpeg">Option With Image</option> ' , $ result );
405+ }
406+
407+ /**
408+ * @testdox can create a select element with optgroups.
409+ */
410+ public function testSelectWithOptgroups ()
411+ {
412+ $ result = $ this ->formBuilder ->select (
413+ name: 'my-select ' ,
414+ list: [
415+ 'Group 1 ' => [
416+ 'g1-opt1 ' => 'Group 1 Option 1 ' ,
417+ 'g1-opt2 ' => 'Group 1 Option 2 ' ,
418+ ],
419+ 'Group 2 ' => [
420+ 'g2-opt1 ' => 'Group 2 Option 1 ' ,
421+ 'g2-opt2 ' => 'Group 2 Option 2 ' ,
422+ ],
423+ ],
424+ selected: null ,
425+ options: []
426+ );
427+
428+ $ this ->assertElementIs ('select ' , $ result );
429+ $ this ->assertElementAttributeEquals ('name ' , 'my-select ' , $ result );
430+ $ this ->assertStringContainsString ('<optgroup label="Group 1"> ' , $ result );
431+ $ this ->assertStringContainsString ('<optgroup label="Group 2"> ' , $ result );
432+ $ this ->assertStringContainsString ('<option value="g1-opt1">Group 1 Option 1</option> ' , $ result );
433+ $ this ->assertStringContainsString ('<option value="g1-opt2">Group 1 Option 2</option> ' , $ result );
434+ $ this ->assertStringContainsString ('<option value="g2-opt1">Group 2 Option 1</option> ' , $ result );
435+ $ this ->assertStringContainsString ('<option value="g2-opt2">Group 2 Option 2</option> ' , $ result );
436+ $ this ->assertStringContainsString ('</optgroup> ' , $ result );
437+ }
438+
439+ /**
440+ * @testdox can create a select element with optgroups containing icons and images.
441+ */
442+ public function testSelectWithOptgroupsAndIconsImages ()
443+ {
444+ $ result = $ this ->formBuilder ->select (
445+ name: 'my-select ' ,
446+ list: [
447+ 'option1 ' => 'Regular option ' ,
448+ 'option2 ' => ['Option With Image ' , 'myImage.jpeg ' ],
449+ 'Group1 ' => [
450+ 'group1-opt1 ' => 'OptGroup Option1 regular option ' ,
451+ 'group1-opt2 ' => ['OptGroup Option2 with icon ' , 'icon-refresh ' ],
452+ 'group1-opt3 ' => ['OptGroup Option3 with image ' , 'otherImage.png ' ],
453+ ],
454+ 'Group2 ' => [
455+ 'group2-opt1 ' => 'OptGroup2 Option1 ' ,
456+ 'group2-opt2 ' => 'OptGroup2 Option2 ' ,
457+ ],
458+ ],
459+ selected: null ,
460+ options: []
461+ );
462+
463+ $ this ->assertElementIs ('select ' , $ result );
464+ $ this ->assertElementAttributeEquals ('name ' , 'my-select ' , $ result );
465+
466+ // Regular options
467+ $ this ->assertStringContainsString ('<option value="option1">Regular option</option> ' , $ result );
468+ $ this ->assertStringContainsString ('<option value="option2" data-image="myImage.jpeg">Option With Image</option> ' , $ result );
469+
470+ // Optgroups
471+ $ this ->assertStringContainsString ('<optgroup label="Group1"> ' , $ result );
472+ $ this ->assertStringContainsString ('<optgroup label="Group2"> ' , $ result );
473+
474+ // Options inside optgroups
475+ $ this ->assertStringContainsString ('<option value="group1-opt1">OptGroup Option1 regular option</option> ' , $ result );
476+ $ this ->assertStringContainsString ('<option value="group1-opt2" data-icon="icon-refresh">OptGroup Option2 with icon</option> ' , $ result );
477+ $ this ->assertStringContainsString ('<option value="group1-opt3" data-image="otherImage.png">OptGroup Option3 with image</option> ' , $ result );
478+ $ this ->assertStringContainsString ('<option value="group2-opt1">OptGroup2 Option1</option> ' , $ result );
479+ $ this ->assertStringContainsString ('<option value="group2-opt2">OptGroup2 Option2</option> ' , $ result );
480+ }
481+
482+ /**
483+ * @testdox can create a select element with backward compatibility for simple string options.
484+ */
485+ public function testSelectBackwardCompatibility ()
486+ {
487+ $ result = $ this ->formBuilder ->select (
488+ name: 'my-select ' ,
489+ list: [
490+ '1 ' => 'Option 1 ' ,
491+ '2 ' => 'Option 2 ' ,
492+ '3 ' => 'Option 3 ' ,
493+ ],
494+ selected: '2 ' ,
495+ options: []
496+ );
497+
498+ $ this ->assertElementIs ('select ' , $ result );
499+ $ this ->assertElementAttributeEquals ('name ' , 'my-select ' , $ result );
500+ $ this ->assertStringContainsString ('<option value="1">Option 1</option> ' , $ result );
501+ $ this ->assertStringContainsString ('<option value="2" selected="selected">Option 2</option> ' , $ result );
502+ $ this ->assertStringContainsString ('<option value="3">Option 3</option> ' , $ result );
503+ $ this ->assertStringNotContainsString ('data-icon ' , $ result );
504+ $ this ->assertStringNotContainsString ('data-image ' , $ result );
505+ }
506+
507+ /**
508+ * @testdox can create a select element with backward compatibility for optgroup integer keys
509+ */
510+ public function testSelectBackwardCompatibilityOptgroupIdItemsKeys ()
511+ {
512+ // this simulates grouped options base on a model with ids as keys
513+ $ result = $ this ->formBuilder ->select (
514+ name: 'my-select ' ,
515+ list: [
516+ 'Group1 ' => [
517+ 1 => 'Option 1 ' ,
518+ 2 => 'Option 2 ' ,
519+ ],
520+ 'Group2 ' => [
521+ 3 => 'Option 3 ' ,
522+ 4 => 'Option 4 ' ,
523+ ],
524+ ],
525+ selected: 2 ,
526+ options: []
527+ );
528+
529+ $ this ->assertElementIs ('select ' , $ result );
530+ $ this ->assertElementAttributeEquals ('name ' , 'my-select ' , $ result );
531+
532+ // Optgroups
533+ $ this ->assertStringContainsString ('<optgroup label="Group1"> ' , $ result );
534+ $ this ->assertStringContainsString ('<optgroup label="Group2"> ' , $ result );
535+
536+ // Options inside optgroups
537+ $ this ->assertStringContainsString ('<option value="1">Option 1</option> ' , $ result );
538+ $ this ->assertStringContainsString ('<option value="2" selected="selected">Option 2</option> ' , $ result );
539+ $ this ->assertStringContainsString ('<option value="3">Option 3</option> ' , $ result );
540+ $ this ->assertStringContainsString ('<option value="4">Option 4</option> ' , $ result );
541+ $ this ->assertStringNotContainsString ('data-icon ' , $ result );
542+ $ this ->assertStringNotContainsString ('data-image ' , $ result );
543+ }
544+
545+ /**
546+ * @testdox show case where backward compatibility is broken (expected)
547+ */
548+ public function testSelectBackwardCompatibilityBrokenOptGroup ()
549+ {
550+ // optgroup syntax with two items with integer keys starting at zero are seen as a regular option with an icon
551+ $ result = $ this ->formBuilder ->select (
552+ name: 'my-select ' ,
553+ list: [
554+ 'Group1 ' => [
555+ 0 => 'Option 1 ' ,
556+ 1 => 'Option 2 ' ,
557+ ],
558+ ],
559+ options: []
560+ );
561+
562+ $ this ->assertElementIs ('select ' , $ result );
563+ $ this ->assertElementAttributeEquals ('name ' , 'my-select ' , $ result );
564+
565+ // Options inside optgroups
566+ $ this ->assertStringContainsString ('<option value="Group1" data-icon="Option 2">Option 1</option> ' , $ result );
567+ $ this ->assertStringContainsString ('data-icon ' , $ result );
568+ }
569+
570+ /**
571+ * @testdox properly escapes HTML in option labels and values.
572+ */
573+ public function testSelectHtmlEscaping ()
574+ {
575+ $ result = $ this ->formBuilder ->select (
576+ name: 'my-select ' ,
577+ list: [
578+ '<script> ' => 'Normal Label ' ,
579+ 'safe-value ' => ['<b>Bold Label</b> ' , 'icon-test ' ],
580+ ],
581+ selected: null ,
582+ options: []
583+ );
584+
585+ $ this ->assertElementIs ('select ' , $ result );
586+
587+ $ this ->assertStringContainsString ('value="<script>" ' , $ result );
588+ $ this ->assertStringContainsString ('<b>Bold Label</b> ' , $ result );
589+
590+ // Ensure dangerous tags are not rendered as raw HTML
591+ $ this ->assertStringNotContainsString ('value="<script>" ' , $ result );
592+ $ this ->assertStringNotContainsString ('<b>Bold Label</b> ' , $ result );
593+ }
343594}
0 commit comments