From a7911b7ef48aa9235510f218b99b4af3b5800b36 Mon Sep 17 00:00:00 2001
From: Alex Arnaud <alex.arnaud@biblibre.com>
Date: Mon, 24 Feb 2025 15:00:23 +0100
Subject: [PATCH] hotline#218564 : Replace ZendAfi element timePicker from
 select to real timepicker

---
 VERSIONS_HOTLINE/218564                       |  1 +
 doc/extern_libs.org                           |  1 +
 library/Class/ScriptLoader.php                | 10 +++++++
 library/ZendAfi/Form/Element/TimePicker.php   | 29 +++++++++++++++++--
 .../js/timepicker/jquery.timepicker.min.css   | 11 +++++++
 .../js/timepicker/jquery.timepicker.min.js    | 10 +++++++
 .../RendezVous/RendezVousAdminTest.php        | 22 +++++++-------
 7 files changed, 70 insertions(+), 14 deletions(-)
 create mode 100644 VERSIONS_HOTLINE/218564
 create mode 100644 public/admin/js/timepicker/jquery.timepicker.min.css
 create mode 100644 public/admin/js/timepicker/jquery.timepicker.min.js

diff --git a/VERSIONS_HOTLINE/218564 b/VERSIONS_HOTLINE/218564
new file mode 100644
index 00000000000..37f2329d1dc
--- /dev/null
+++ b/VERSIONS_HOTLINE/218564
@@ -0,0 +1 @@
+ - correctif #218564 : Rendez-vous : les sélecteurs d'heures de début et fin deviennent un Jquery timepicker
\ No newline at end of file
diff --git a/doc/extern_libs.org b/doc/extern_libs.org
index 12482f7baa4..add9e2106ee 100644
--- a/doc/extern_libs.org
+++ b/doc/extern_libs.org
@@ -49,3 +49,4 @@
 | phpseclib                               | MIT                      |               | Avis communautaires                                                  | X ajout d'un autoload.php                                  | https://github.com/phpseclib/phpseclib                                                                                         |
 | leaflet.fullscreen                      | MIT                      |               | bouton plein écran sur la carte des bibliothèques                    |                                                            | https://github.com/brunob/leaflet.fullscreen                                                                                   |
 | snowflake                               | ?                        |               | Charte Noel partie administration                                    | X passage en plugin JQuery                                 | https://github.com/mahefnawy/snowflake/                                                                                        |
+| Timepicker                              | GPL-2.0                  | x             | Choisir les heures dans le module rendez-vous                        |                                                            | https://github.com/wvega/timepicker                                                                                                                               |
diff --git a/library/Class/ScriptLoader.php b/library/Class/ScriptLoader.php
index a7c620908ba..5f63a42eec0 100644
--- a/library/Class/ScriptLoader.php
+++ b/library/Class/ScriptLoader.php
@@ -960,4 +960,14 @@ class Class_ScriptLoader {
     array_map(fn($line) => $this->cssAddLine($line),$script_loader->styleSheetsArray());
     return $this;
   }
+
+
+  public function addBackTimePicker(): self
+  {
+    return $this
+      ->loadBackJQuery()
+      ->loadJQueryUI()
+      ->addAdminScript('timepicker/jquery.timepicker.min.js')
+      ->addStyleSheet(URL_ADMIN_JS.'timepicker/jquery.timepicker.min.css');
+  }
 }
diff --git a/library/ZendAfi/Form/Element/TimePicker.php b/library/ZendAfi/Form/Element/TimePicker.php
index d11b4190fc5..8777ee9fbd8 100644
--- a/library/ZendAfi/Form/Element/TimePicker.php
+++ b/library/ZendAfi/Form/Element/TimePicker.php
@@ -20,9 +20,11 @@
  */
 
 
-class ZendAfi_Form_Element_TimePicker extends Zend_Form_Element_Select {
+class ZendAfi_Form_Element_TimePicker extends Zend_Form_Element {
   use Trait_TimeSource;
 
+  const TIME_FORMAT = 'HH:mm';
+
   protected
     $_min = '00:00',
     $_max = '23:55',
@@ -31,8 +33,7 @@ class ZendAfi_Form_Element_TimePicker extends Zend_Form_Element_Select {
   public function init() {
     parent::init();
 
-    $this->setMultiOptions($this->getTimeSource()
-                           ->getPossibleHours($this->_min, $this->_max, $this->_step));
+    $this->_addScript();
   }
 
 
@@ -49,4 +50,26 @@ class ZendAfi_Form_Element_TimePicker extends Zend_Form_Element_Select {
   public function setStep($value){
     $this->_step = $value;
   }
+
+
+  protected function _addScript(): void
+  {
+    Class_ScriptLoader::getInstance()
+      ->addBackTimePicker()
+      ->addJqueryReady(sprintf('$("#%s").timepicker(
+{timeFormat: \'%s\',
+startTime: \'%s\',
+minTime: \'%s\',
+maxTime: \'%s\',
+interval: %d,
+change: function() {
+  $(this).change();
+}});',
+                               $this->getId(),
+                               static::TIME_FORMAT,
+                               $this->_min,
+                               $this->_min,
+                               $this->_max,
+                               $this->_step));
+  }
 }
diff --git a/public/admin/js/timepicker/jquery.timepicker.min.css b/public/admin/js/timepicker/jquery.timepicker.min.css
new file mode 100644
index 00000000000..bb60921e612
--- /dev/null
+++ b/public/admin/js/timepicker/jquery.timepicker.min.css
@@ -0,0 +1,11 @@
+/**
+ * jQuery Timepicker - v1.3.5 - 2016-07-10
+ * http://timepicker.co
+ *
+ * Enhances standard form input fields helping users to select (or type) times.
+ *
+ * Copyright (c) 2016 Willington Vega; Licensed MIT, GPL
+ */
+
+
+.ui-timepicker-container{position:absolute;overflow:hidden;box-sizing:border-box}.ui-timepicker{box-sizing:content-box;display:block;height:205px;list-style:none outside none;margin:0;padding:0 1px;text-align:center}.ui-timepicker-viewport{box-sizing:content-box;display:block;height:205px;margin:0;padding:0;overflow:auto;overflow-x:hidden}.ui-timepicker-standard{font-family:Verdana,Arial,sans-serif;font-size:1.1em;background-color:#FFF;border:1px solid #AAA;color:#222;margin:0;padding:2px}.ui-timepicker-standard a{border:1px solid transparent;color:#222;display:block;padding:.2em .4em;text-decoration:none}.ui-timepicker-standard .ui-state-hover{background-color:#DADADA;border:1px solid #999;font-weight:400;color:#212121}.ui-timepicker-standard .ui-menu-item{margin:0;padding:0}.ui-timepicker-corners,.ui-timepicker-corners .ui-corner-all{-moz-border-radius:4px;-webkit-border-radius:4px;border-radius:4px}.ui-timepicker-hidden{display:none}.ui-timepicker-no-scrollbar .ui-timepicker{border:0}
\ No newline at end of file
diff --git a/public/admin/js/timepicker/jquery.timepicker.min.js b/public/admin/js/timepicker/jquery.timepicker.min.js
new file mode 100644
index 00000000000..fe895a0bece
--- /dev/null
+++ b/public/admin/js/timepicker/jquery.timepicker.min.js
@@ -0,0 +1,10 @@
+/**
+ * jQuery Timepicker - v1.3.5 - 2016-07-10
+ * http://timepicker.co
+ *
+ * Enhances standard form input fields helping users to select (or type) times.
+ *
+ * Copyright (c) 2016 Willington Vega; Licensed MIT, GPL
+ */
+
+!function(a){"object"==typeof module&&"object"==typeof module.exports?a(require("jquery"),window,document):"undefined"!=typeof jQuery&&a(jQuery,window,document)}(function(a,b,c,d){!function(){function b(a,b,c){return new Array(c+1-a.length).join(b)+a}function d(){if(1===arguments.length){var b=arguments[0];return"string"==typeof b&&(b=a.fn.timepicker.parseTime(b)),new Date(0,0,0,b.getHours(),b.getMinutes(),b.getSeconds())}return 3===arguments.length?new Date(0,0,0,arguments[0],arguments[1],arguments[2]):2===arguments.length?new Date(0,0,0,arguments[0],arguments[1],0):new Date(0,0,0)}a.TimePicker=function(){var b=this;b.container=a(".ui-timepicker-container"),b.ui=b.container.find(".ui-timepicker"),0===b.container.length&&(b.container=a("<div></div>").addClass("ui-timepicker-container").addClass("ui-timepicker-hidden ui-helper-hidden").appendTo("body").hide(),b.ui=a("<div></div>").addClass("ui-timepicker").addClass("ui-widget ui-widget-content ui-menu").addClass("ui-corner-all").appendTo(b.container),b.viewport=a("<ul></ul>").addClass("ui-timepicker-viewport").appendTo(b.ui),a.fn.jquery>="1.4.2"&&b.ui.delegate("a","mouseenter.timepicker",function(){b.activate(!1,a(this).parent())}).delegate("a","mouseleave.timepicker",function(){b.deactivate(!1)}).delegate("a","click.timepicker",function(c){c.preventDefault(),b.select(!1,a(this).parent())}))},a.TimePicker.count=0,a.TimePicker.instance=function(){return a.TimePicker._instance||(a.TimePicker._instance=new a.TimePicker),a.TimePicker._instance},a.TimePicker.prototype={keyCode:{ALT:18,BLOQ_MAYUS:20,CTRL:17,DOWN:40,END:35,ENTER:13,HOME:36,LEFT:37,NUMPAD_ENTER:108,PAGE_DOWN:34,PAGE_UP:33,RIGHT:39,SHIFT:16,TAB:9,UP:38},_items:function(b,c){var e,f,g=this,h=a("<ul></ul>"),i=null;for(-1===b.options.timeFormat.indexOf("m")&&b.options.interval%60!==0&&(b.options.interval=60*Math.max(Math.round(b.options.interval/60),1)),e=c?d(c):b.options.startTime?d(b.options.startTime):d(b.options.startHour,b.options.startMinutes),f=new Date(e.getTime()+864e5);f>e;)g._isValidTime(b,e)&&(i=a("<li>").addClass("ui-menu-item").appendTo(h),a("<a>").addClass("ui-corner-all").text(a.fn.timepicker.formatTime(b.options.timeFormat,e)).appendTo(i),i.data("time-value",e)),e=new Date(e.getTime()+60*b.options.interval*1e3);return h.children()},_isValidTime:function(a,b){var c=null,e=null;return b=d(b),null!==a.options.minTime?c=d(a.options.minTime):(null!==a.options.minHour||null!==a.options.minMinutes)&&(c=d(a.options.minHour,a.options.minMinutes)),null!==a.options.maxTime?e=d(a.options.maxTime):(null!==a.options.maxHour||null!==a.options.maxMinutes)&&(e=d(a.options.maxHour,a.options.maxMinutes)),null!==c&&null!==e?b>=c&&e>=b:null!==c?b>=c:null!==e?e>=b:!0},_hasScroll:function(){var a="undefined"!=typeof this.ui.prop?"prop":"attr";return this.ui.height()<this.ui[a]("scrollHeight")},_move:function(a,b,c){var d=this;if(d.closed()&&d.open(a),!d.active)return void d.activate(a,d.viewport.children(c));var e=d.active[b+"All"](".ui-menu-item").eq(0);e.length?d.activate(a,e):d.activate(a,d.viewport.children(c))},register:function(b,c){var d=this,e={};e.element=a(b),e.element.data("TimePicker")||(e.options=a.metadata?a.extend({},c,e.element.metadata()):a.extend({},c),e.widget=d,a.extend(e,{next:function(){return d.next(e)},previous:function(){return d.previous(e)},first:function(){return d.first(e)},last:function(){return d.last(e)},selected:function(){return d.selected(e)},open:function(){return d.open(e)},close:function(){return d.close(e)},closed:function(){return d.closed(e)},destroy:function(){return d.destroy(e)},parse:function(a){return d.parse(e,a)},format:function(a,b){return d.format(e,a,b)},getTime:function(){return d.getTime(e)},setTime:function(a,b){return d.setTime(e,a,b)},option:function(a,b){return d.option(e,a,b)}}),d._setDefaultTime(e),d._addInputEventsHandlers(e),e.element.data("TimePicker",e))},_setDefaultTime:function(b){"now"===b.options.defaultTime?b.setTime(d(new Date)):b.options.defaultTime&&b.options.defaultTime.getFullYear?b.setTime(d(b.options.defaultTime)):b.options.defaultTime&&b.setTime(a.fn.timepicker.parseTime(b.options.defaultTime))},_addInputEventsHandlers:function(b){var c=this;b.element.bind("keydown.timepicker",function(a){switch(a.which||a.keyCode){case c.keyCode.ENTER:case c.keyCode.NUMPAD_ENTER:a.preventDefault(),c.closed()?b.element.trigger("change.timepicker"):c.select(b,c.active);break;case c.keyCode.UP:b.previous();break;case c.keyCode.DOWN:b.next();break;default:c.closed()||b.close(!0)}}).bind("focus.timepicker",function(){b.open()}).bind("blur.timepicker",function(){setTimeout(function(){b.element.data("timepicker-user-clicked-outside")&&b.close()})}).bind("change.timepicker",function(){b.closed()&&b.setTime(a.fn.timepicker.parseTime(b.element.val()))})},select:function(b,c){var d=this,e=b===!1?d.instance:b;d.setTime(e,a.fn.timepicker.parseTime(c.children("a").text())),d.close(e,!0)},activate:function(a,b){var c=this,d=a===!1?c.instance:a;if(d===c.instance){if(c.deactivate(),c._hasScroll()){var e=b.offset().top-c.ui.offset().top,f=c.ui.scrollTop(),g=c.ui.height();0>e?c.ui.scrollTop(f+e):e>=g&&c.ui.scrollTop(f+e-g+b.height())}c.active=b.eq(0).children("a").addClass("ui-state-hover").attr("id","ui-active-item").end()}},deactivate:function(){var a=this;a.active&&(a.active.children("a").removeClass("ui-state-hover").removeAttr("id"),a.active=null)},next:function(a){return(this.closed()||this.instance===a)&&this._move(a,"next",".ui-menu-item:first"),a.element},previous:function(a){return(this.closed()||this.instance===a)&&this._move(a,"prev",".ui-menu-item:last"),a.element},first:function(a){return this.instance===a?this.active&&0===this.active.prevAll(".ui-menu-item").length:!1},last:function(a){return this.instance===a?this.active&&0===this.active.nextAll(".ui-menu-item").length:!1},selected:function(a){return this.instance===a&&this.active?this.active:null},open:function(b){var d=this,e=b.getTime(),f=b.options.dynamic&&e;if(!b.options.dropdown)return b.element;switch(b.element.data("timepicker-event-namespace",Math.random()),a(c).bind("click.timepicker-"+b.element.data("timepicker-event-namespace"),function(a){b.element.get(0)===a.target?b.element.data("timepicker-user-clicked-outside",!1):b.element.data("timepicker-user-clicked-outside",!0).blur()}),(b.rebuild||!b.items||f)&&(b.items=d._items(b,f?e:null)),(b.rebuild||d.instance!==b||f)&&(a.fn.jquery<"1.4.2"?(d.viewport.children().remove(),d.viewport.append(b.items),d.viewport.find("a").bind("mouseover.timepicker",function(){d.activate(b,a(this).parent())}).bind("mouseout.timepicker",function(){d.deactivate(b)}).bind("click.timepicker",function(c){c.preventDefault(),d.select(b,a(this).parent())})):(d.viewport.children().detach(),d.viewport.append(b.items))),b.rebuild=!1,d.container.removeClass("ui-helper-hidden ui-timepicker-hidden ui-timepicker-standard ui-timepicker-corners").show(),b.options.theme){case"standard":d.container.addClass("ui-timepicker-standard");break;case"standard-rounded-corners":d.container.addClass("ui-timepicker-standard ui-timepicker-corners")}d.container.hasClass("ui-timepicker-no-scrollbar")||b.options.scrollbar||(d.container.addClass("ui-timepicker-no-scrollbar"),d.viewport.css({paddingRight:40}));var g=d.container.outerHeight()-d.container.height(),h=b.options.zindex?b.options.zindex:b.element.offsetParent().css("z-index"),i=b.element.offset();d.container.css({top:i.top+b.element.outerHeight(),left:i.left}),d.container.show(),d.container.css({left:b.element.offset().left,height:d.ui.outerHeight()+g,width:b.element.outerWidth(),zIndex:h,cursor:"default"});var j=d.container.width()-(d.ui.outerWidth()-d.ui.width());return d.ui.css({width:j}),d.viewport.css({width:j}),b.items.css({width:j}),d.instance=b,e?b.items.each(function(){var c,f=a(this);return c=a.fn.jquery<"1.4.2"?a.fn.timepicker.parseTime(f.find("a").text()):f.data("time-value"),c.getTime()===e.getTime()?(d.activate(b,f),!1):!0}):d.deactivate(b),b.element},close:function(b){var d=this;return d.instance===b&&(d.container.addClass("ui-helper-hidden ui-timepicker-hidden").hide(),d.ui.scrollTop(0),d.ui.children().removeClass("ui-state-hover")),a(c).unbind("click.timepicker-"+b.element.data("timepicker-event-namespace")),b.element},closed:function(){return this.ui.is(":hidden")},destroy:function(a){var b=this;return b.close(a,!0),a.element.unbind(".timepicker").data("TimePicker",null)},parse:function(b,c){return a.fn.timepicker.parseTime(c)},format:function(b,c,d){return d=d||b.options.timeFormat,a.fn.timepicker.formatTime(d,c)},getTime:function(b){var c=this,d=a.fn.timepicker.parseTime(b.element.val());return d instanceof Date&&!c._isValidTime(b,d)?null:d instanceof Date&&b.selectedTime?b.format(d)===b.format(b.selectedTime)?b.selectedTime:d:d instanceof Date?d:null},setTime:function(b,c,e){var f=this,g=b.selectedTime;if("string"==typeof c&&(c=b.parse(c)),c&&c.getMinutes&&f._isValidTime(b,c)){if(c=d(c),b.selectedTime=c,b.element.val(b.format(c,b.options.timeFormat)),e)return b}else b.selectedTime=null;return(null!==g||null!==b.selectedTime)&&(b.element.trigger("time-change",[c]),a.isFunction(b.options.change)&&b.options.change.apply(b.element,[c])),b.element},option:function(b,c,d){if("undefined"==typeof d)return b.options[c];var e,f,g=b.getTime();"string"==typeof c?(e={},e[c]=d):e=c,f=["minHour","minMinutes","minTime","maxHour","maxMinutes","maxTime","startHour","startMinutes","startTime","timeFormat","interval","dropdown"],a.each(e,function(c){b.options[c]=e[c],b.rebuild=b.rebuild||a.inArray(c,f)>-1}),b.rebuild&&b.setTime(g)}},a.TimePicker.defaults={timeFormat:"hh:mm p",minHour:null,minMinutes:null,minTime:null,maxHour:null,maxMinutes:null,maxTime:null,startHour:null,startMinutes:null,startTime:null,interval:30,dynamic:!0,theme:"standard",zindex:null,dropdown:!0,scrollbar:!1,change:function(){}},a.TimePicker.methods={chainable:["next","previous","open","close","destroy","setTime"]},a.fn.timepicker=function(b){if("string"==typeof b){var c,d,e=Array.prototype.slice.call(arguments,1);return c="option"===b&&arguments.length>2?"each":-1!==a.inArray(b,a.TimePicker.methods.chainable)?"each":"map",d=this[c](function(){var c=a(this),d=c.data("TimePicker");return"object"==typeof d?d[b].apply(d,e):void 0}),"map"===c&&1===this.length?a.makeArray(d).shift():"map"===c?a.makeArray(d):d}if(1===this.length&&this.data("TimePicker"))return this.data("TimePicker");var f=a.extend({},a.TimePicker.defaults,b);return this.each(function(){a.TimePicker.instance().register(this,f)})},a.fn.timepicker.formatTime=function(a,c){var d=c.getHours(),e=d%12,f=c.getMinutes(),g=c.getSeconds(),h={hh:b((0===e?12:e).toString(),"0",2),HH:b(d.toString(),"0",2),mm:b(f.toString(),"0",2),ss:b(g.toString(),"0",2),h:0===e?12:e,H:d,m:f,s:g,p:d>11?"PM":"AM"},i=a,j="";for(j in h)h.hasOwnProperty(j)&&(i=i.replace(new RegExp(j,"g"),h[j]));return i=i.replace(new RegExp("a","g"),d>11?"pm":"am")},a.fn.timepicker.parseTime=function(){var b=[[/^(\d+)$/,"$1"],[/^:(\d)$/,"$10"],[/^:(\d+)/,"$1"],[/^(\d):([7-9])$/,"0$10$2"],[/^(\d):(\d\d)$/,"$1$2"],[/^(\d):(\d{1,})$/,"0$1$20"],[/^(\d\d):([7-9])$/,"$10$2"],[/^(\d\d):(\d)$/,"$1$20"],[/^(\d\d):(\d*)$/,"$1$2"],[/^(\d{3,}):(\d)$/,"$10$2"],[/^(\d{3,}):(\d{2,})/,"$1$2"],[/^(\d):(\d):(\d)$/,"0$10$20$3"],[/^(\d{1,2}):(\d):(\d\d)/,"$10$2$3"]],c=b.length;return function(e){var f=d(new Date),g=!1,h=!1,i=!1,j=!1,k=!1;if("undefined"==typeof e||!e.toLowerCase)return null;e=e.toLowerCase(),g=/a/.test(e),h=g?!1:/p/.test(e),e=e.replace(/[^0-9:]/g,"").replace(/:+/g,":");for(var l=0;c>l;l+=1)if(b[l][0].test(e)){e=e.replace(b[l][0],b[l][1]);break}return e=e.replace(/:/g,""),1===e.length?i=e:2===e.length?i=e:3===e.length||5===e.length?(i=e.substr(0,1),j=e.substr(1,2),k=e.substr(3,2)):(4===e.length||e.length>5)&&(i=e.substr(0,2),j=e.substr(2,2),k=e.substr(4,2)),e.length>0&&e.length<5&&(e.length<3&&(j=0),k=0),i===!1||j===!1||k===!1?!1:(i=parseInt(i,10),j=parseInt(j,10),k=parseInt(k,10),g&&12===i?i=0:h&&12>i&&(i+=12),i>24?e.length>=6?a.fn.timepicker.parseTime(e.substr(0,5)):a.fn.timepicker.parseTime(e+"0"+(g?"a":"")+(h?"p":"")):(f.setHours(i,j,k),f))}}()}()});
\ No newline at end of file
diff --git a/tests/scenarios/RendezVous/RendezVousAdminTest.php b/tests/scenarios/RendezVous/RendezVousAdminTest.php
index 861216db705..0223b5e5bb8 100644
--- a/tests/scenarios/RendezVous/RendezVousAdminTest.php
+++ b/tests/scenarios/RendezVous/RendezVousAdminTest.php
@@ -188,16 +188,16 @@ class RendezVousAdminAddActionTest extends RendezVousAdminTestCase {
 
 
   /** @test */
-  public function begintimeShouldBePresentStartAt8EndAt18() {
-    $this->assertXPath('//select[@name="begin_time"]//option[1][@value="08:00"]');
-    $this->assertXPath('//select[@name="begin_time"]//option[last()][@value="22:00"]');
- }
+  public function beginTimeShouldBeDisplayedAndTimePickerized()
+  {
+    $this->assertXPath('//input[@name="begin_time"]');
+  }
 
 
   /** @test */
-  public function endtimeShouldBePresentStartAt8EndAt18() {
-    $this->assertXPath('//select[@name="end_time"]//option[1][@value="08:00"]');
-    $this->assertXPath('//select[@name="end_time"]//option[last()][@value="22:00"]');
+  public function endTimeShouldBeDisplayedAndTimePickerized()
+  {
+    $this->assertXPath('//input[@name="end_time"]');
   }
 
 
@@ -413,13 +413,13 @@ class RendezVousAdminEditActionTest extends RendezVousAdminTestCase {
 
   /** @test */
   public function begintimeShouldBe09_15() {
-    $this->assertXPath('//select[@name="begin_time"]//option[@value="09:15"][@selected]');
+    $this->assertXPath('//input[@name="begin_time"][@value="09:15"]');
   }
 
 
   /** @test */
   public function endtimeShouldBe10_30() {
-    $this->assertXPath('//select[@name="end_time"]//option[@value="10:30"][@selected]');
+    $this->assertXPath('//input[@name="end_time"][@value="10:30"]');
   }
 
 
@@ -507,13 +507,13 @@ class RendezVousAdminDuplicateValidActionTest extends RendezVousAdminTestCase {
 
   /** @test */
   public function begintimeShouldBe09_15() {
-    $this->assertXPath('//select[@name="begin_time"]//option[@value="09:15"][@selected]');
+    $this->assertXPath('//input[@name="begin_time"][@value="09:15"]');
   }
 
 
   /** @test */
   public function endtimeShouldBe10_30() {
-    $this->assertXPath('//select[@name="end_time"]//option[@value="10:30"][@selected]');
+    $this->assertXPath('//input[@name="end_time"][@value="10:30"]');
   }
 
 
-- 
GitLab