From 5be40f8725aa8b49f2fe4d7cae3d563de96ddb60 Mon Sep 17 00:00:00 2001 From: Oleg Andreyev Date: Fri, 30 Nov 2018 23:52:57 +0200 Subject: [PATCH 01/65] moved driver to facebook/webdriver --- .travis.yml | 42 +- composer.json | 48 +- src/Resources/syn.js | 8 - src/Selenium2Driver.php | 708 +++++++---------------- tests/Custom/DesiredCapabilitiesTest.php | 52 -- tests/Custom/TimeoutTest.php | 41 -- tests/Custom/WebDriverTest.php | 21 - tests/Custom/WindowNameTest.php | 22 - tests/Selenium2Config.php | 23 +- 9 files changed, 273 insertions(+), 692 deletions(-) delete mode 100644 src/Resources/syn.js delete mode 100644 tests/Custom/DesiredCapabilitiesTest.php delete mode 100644 tests/Custom/TimeoutTest.php delete mode 100644 tests/Custom/WebDriverTest.php delete mode 100644 tests/Custom/WindowNameTest.php diff --git a/.travis.yml b/.travis.yml index 94e46a73..f9916434 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,41 +3,37 @@ language: php sudo: false cache: - directories: - - $HOME/.composer/cache/files + directories: + - $HOME/.composer/cache/files -php: [5.4, 5.5, 5.6, 7.0, 7.1, 7.2] +php: [7.0, 7.1, 7.2] env: - global: - - WEBDRIVER=selenium + global: + - WEBDRIVER=selenium matrix: - fast_finish: true - include: - - php: 7.0 - env: WEBDRIVER=selenium-remote - sudo: required - services: - - docker - - php: 5.3 - dist: precise - # Force using PHP 5.6 for the test server as PHP 5.3 does not have the builtin webserver - env: MINK_PHP_BIN=~/.phpenv/versions/5.6/bin/php + fast_finish: true + include: + - php: 7.0 + env: WEBDRIVER=selenium-remote + sudo: required + services: + - docker before_script: - - sh bin/run-"$WEBDRIVER".sh + - sh bin/run-"$WEBDRIVER".sh - - composer install + - composer install - # Start a webserver for web fixtures. - - vendor/bin/mink-test-server > /dev/null 2>&1 & + # Start a webserver for web fixtures. + - vendor/bin/mink-test-server > /dev/null 2>&1 & script: phpunit -v --coverage-clover=coverage.clover after_script: - - wget https://scrutinizer-ci.com/ocular.phar - - php ocular.phar code-coverage:upload --format=php-clover coverage.clover + - wget https://scrutinizer-ci.com/ocular.phar + - php ocular.phar code-coverage:upload --format=php-clover coverage.clover after_failure: - - cat /tmp/webdriver_output.txt + - cat /tmp/webdriver_output.txt diff --git a/composer.json b/composer.json index bda59eae..a6bdc457 100644 --- a/composer.json +++ b/composer.json @@ -1,46 +1,50 @@ { - "name": "behat/mink-selenium2-driver", - "description": "Selenium2 (WebDriver) driver for Mink framework", - "keywords": ["selenium", "webdriver", "javascript", "ajax", "testing", "browser"], - "homepage": "http://mink.behat.org/", - "type": "mink-driver", - "license": "MIT", - + "name": "behat/mink-selenium2-driver", + "description": "Selenium2 (WebDriver) driver for Mink framework", + "keywords": [ + "selenium", + "webdriver", + "javascript", + "ajax", + "testing", + "browser" + ], + "homepage": "http://mink.behat.org/", + "type": "mink-driver", + "license": "MIT", "authors": [ { - "name": "Pete Otaqui", - "email": "pete@otaqui.com", - "homepage": "https://github.com/pete-otaqui" + "name": "Pete Otaqui", + "email": "pete@otaqui.com", + "homepage": "https://github.com/pete-otaqui" }, { - "name": "Konstantin Kudryashov", - "email": "ever.zet@gmail.com", - "homepage": "http://everzet.com" + "name": "Konstantin Kudryashov", + "email": "ever.zet@gmail.com", + "homepage": "http://everzet.com" } ], - "require": { - "php": ">=5.3.1", - "behat/mink": "~1.7@dev", - "instaclick/php-webdriver": "~1.1" + "php": ">=5.3.1", + "behat/mink": "~1.7@dev", + "facebook/webdriver": "^1.6", + "ext-zip": "*" }, - "require-dev": { - "mink/driver-testsuite": "dev-master" + "roave/security-advisories": "dev-master", + "mink/driver-testsuite": "dev-master", + "phpunit/phpunit": "^6.5" }, - "autoload": { "psr-4": { "Behat\\Mink\\Driver\\": "src/" } }, - "autoload-dev": { "psr-4": { "Behat\\Mink\\Tests\\Driver\\": "tests" } }, - "extra": { "branch-alias": { "dev-master": "1.3.x-dev" diff --git a/src/Resources/syn.js b/src/Resources/syn.js deleted file mode 100644 index f48ee1f9..00000000 --- a/src/Resources/syn.js +++ /dev/null @@ -1,8 +0,0 @@ -/** - * Syn - 0.0.2 - * - * @copyright 2014 Bitovi - * Mon, 30 Jun 2014 22:44:59 GMT - * @license MIT - */ -!function(window){var __m2=function(){var a,b,c,d,e=window.Syn?window.Syn:{},f=function(a,b){var c;for(c in b)a[c]=b[c];return a},g={msie:!(!window.attachEvent||window.opera),opera:!!window.opera,webkit:navigator.userAgent.indexOf("AppleWebKit/")>-1,safari:navigator.userAgent.indexOf("AppleWebKit/")>-1&&-1===navigator.userAgent.indexOf("Chrome/"),gecko:navigator.userAgent.indexOf("Gecko")>-1,mobilesafari:!!navigator.userAgent.match(/Apple.*Mobile.*Safari/),rhino:navigator.userAgent.match(/Rhino/)&&!0},h=function(a,b,c){var d=c.ownerDocument.createEventObject();return f(d,b)},i={},j=1,k="_synthetic"+(new Date).getTime(),l=/keypress|keyup|keydown/,m=/load|unload|abort|error|select|change|submit|reset|focus|blur|resize|scroll/,n=function(a,b,c,d){return new n.init(a,b,c,d)};n.config=e,n.__tryFocus=function(a){try{a.focus()}catch(b){}},a=function(a,b,c){return a.addEventListener?a.addEventListener(b,c,!1):a.attachEvent("on"+b,c)},b=function(a,b,c){return a.addEventListener?a.removeEventListener(b,c,!1):a.detachEvent("on"+b,c)},c=n.config.schedule||function(a,b){setTimeout(a,b)},f(n,{init:function(a,b,c,d){var e=n.args(b,c,d),f=this;this.queue=[],this.element=e.element,"function"==typeof this[a]?this[a](e.options,e.element,function(){e.callback&&e.callback.apply(f,arguments),f.done.apply(f,arguments)}):(this.result=n.trigger(a,e.options,e.element),e.callback&&e.callback.call(this,e.element,this.result))},jquery:function(a){return window.FuncUnit&&window.FuncUnit.jQuery?window.FuncUnit.jQuery:a?n.helpers.getWindow(a).jQuery||window.jQuery:window.jQuery},args:function(){for(var a={},b=0;b0&&g.apply(this,[])},d.dispatchEvent(c),0>=h}try{window.event=c}catch(i){}return d.sourceIndex<=0||d.fireEvent&&d.fireEvent("on"+e,c)},create:{page:{event:function(a,b,c){var d,e=n.helpers.getWindow(c).document||document;if(e.createEvent)return d=e.createEvent("Events"),d.initEvent(a,!0,!0),d;try{d=h(a,b,c)}catch(f){}return d}},focus:{event:function(a,b,c){return n.onParents(c,function(a){if(n.isFocusable(a)){if("html"!==a.nodeName.toLowerCase())n.__tryFocus(a),d=a;else if(d){var b=n.helpers.getWindow(c).document;if(b!==window.document)return!1;b.activeElement?(b.activeElement.blur(),d=null):(d.blur(),d=null)}return!1}}),!0}}},support:{clickChanges:!1,clickSubmits:!1,keypressSubmits:!1,mouseupSubmits:!1,radioClickChanges:!1,focusChanges:!1,linkHrefJS:!1,keyCharacters:!1,backspaceWorks:!1,mouseDownUpClicks:!1,tabKeyTabs:!1,keypressOnAnchorClicks:!1,optionClickBubbles:!1,ready:0},trigger:function(a,b,c){b||(b={});var d,e,f,g=n.create,h=g[a]&&g[a].setup,i=l.test(a)?"key":m.test(a)?"page":"mouse",j=g[a]||{},k=g[i],o=c;return 2===n.support.ready&&h&&h(a,b,c),f=b._autoPrevent,delete b._autoPrevent,j.event?e=j.event(a,b,c):(b=k.options?k.options(a,b,c):b,!n.support.changeBubbles&&/option/i.test(c.nodeName)&&(o=c.parentNode),d=k.event(a,b,o),e=n.dispatch(d,o,a,f)),e&&2===n.support.ready&&n.defaults[a]&&n.defaults[a].call(c,b,f),e},eventSupported:function(a){var b=document.createElement("div");a="on"+a;var c=a in b;return c||(b.setAttribute(a,"return;"),c="function"==typeof b[a]),b=null,c}}),f(n.init.prototype,{then:function(a,b,c,d){n.autoDelay&&this.delay();var e=n.args(b,c,d),f=this;return this.queue.unshift(function(b){return"function"!=typeof this[a]?(this.result=n.trigger(a,e.options,e.element),e.callback&&e.callback.call(this,e.element,this.result),this):(this.element=e.element||b,void this[a](e.options,this.element,function(){e.callback&&e.callback.apply(f,arguments),f.done.apply(f,arguments)}))}),this},delay:function(a,b){"function"==typeof a&&(b=a,a=null),a=a||600;var d=this;return this.queue.unshift(function(){c(function(){b&&b.apply(d,[]),d.done.apply(d,arguments)},a)}),this},done:function(a,b){b&&(this.element=b),this.queue.length&&this.queue.pop().call(this,this.element,a)},_click:function(a,b,d,e){n.helpers.addOffset(a,b),n.trigger("mousedown",a,b),c(function(){n.trigger("mouseup",a,b),!n.support.mouseDownUpClicks||e?(n.trigger("click",a,b),d(!0)):(n.create.click.setup("click",a,b),n.defaults.click.call(b),c(function(){d(!0)},1))},1)},_rightClick:function(a,b,d){n.helpers.addOffset(a,b);var e=f(f({},n.mouse.browser.right.mouseup),a);n.trigger("mousedown",e,b),c(function(){n.trigger("mouseup",e,b),n.mouse.browser.right.contextmenu&&n.trigger("contextmenu",f(f({},n.mouse.browser.right.contextmenu),a),b),d(!0)},1)},_dblclick:function(a,b,d){n.helpers.addOffset(a,b);var e=this;this._click(a,b,function(){c(function(){e._click(a,b,function(){n.trigger("dblclick",a,b),d(!0)},!0)},2)})}});for(var o=["click","dblclick","move","drag","key","type","rightClick"],p=function(a){n[a]=function(b,c,d){return n("_"+a,b,c,d)},n.init.prototype[a]=function(b,c,d){return this.then("_"+a,b,c,d)}},q=0;qb;b++)if(b in this&&this[b]===a)return b;return-1};a.typeable=function(a){-1===c.call(b,a)&&b.push(a)},a.typeable.test=function(a){for(var c=0,d=b.length;d>c;c++)if(b[c](a))return!0;return!1};var d=a.typeable,e=/input|textarea/i;return d(function(a){return e.test(a.nodeName)}),d(function(a){return-1!==c.call(["","true"],a.getAttribute("contenteditable"))}),a}(__m2),__m5=function(a){var b=a.helpers,c=function(a){var c,d,f;if(void 0!==a.selectionStart)return document.activeElement&&document.activeElement!==a&&a.selectionStart===a.selectionEnd&&0===a.selectionStart?{start:a.value.length,end:a.value.length}:{start:a.selectionStart,end:a.selectionEnd};try{if("input"===a.nodeName.toLowerCase())return c=b.getWindow(a).document.selection.createRange(),d=a.createTextRange(),d.setEndPoint("EndToStart",c),f=d.text.length,{start:f,end:f+c.text.length};c=b.getWindow(a).document.selection.createRange(),d=c.duplicate();var g=c.duplicate(),h=c.duplicate();g.collapse(),h.collapse(!1),g.moveStart("character",-1),h.moveStart("character",-1),d.moveToElementText(a),d.setEndPoint("EndToEnd",c),f=d.text.length-c.text.length;var i=d.text.length;return 0!==f&&""===g.text&&(f+=2),0!==i&&""===h.text&&(i+=2),{start:f,end:i}}catch(j){var k=e.test(a.nodeName)?"value":"textContent";return{start:a[k].length,end:a[k].length}}},d=function(c){for(var d=b.getWindow(c).document,e=[],f=d.getElementsByTagName("*"),g=f.length,h=0;g>h;h++)a.isFocusable(f[h])&&f[h]!==d.documentElement&&e.push(f[h]);return e},e=/input|textarea/i,f=function(){var a=document.createElement("span");return null!=a.textContent?"textContent":"innerText"}(),g=function(a){return e.test(a.nodeName)?a.value:a[f]},h=function(a,b){e.test(a.nodeName)?a.value=b:a[f]=b};b.extend(a,{keycodes:{"\b":8," ":9,"\r":13,shift:16,ctrl:17,alt:18,"pause-break":19,caps:20,escape:27,"num-lock":144,"scroll-lock":145,print:44,"page-up":33,"page-down":34,end:35,home:36,left:37,up:38,right:39,down:40,insert:45,"delete":46," ":32,0:48,1:49,2:50,3:51,4:52,5:53,6:54,7:55,8:56,9:57,a:65,b:66,c:67,d:68,e:69,f:70,g:71,h:72,i:73,j:74,k:75,l:76,m:77,n:78,o:79,p:80,q:81,r:82,s:83,t:84,u:85,v:86,w:87,x:88,y:89,z:90,num0:96,num1:97,num2:98,num3:99,num4:100,num5:101,num6:102,num7:103,num8:104,num9:105,"*":106,"+":107,subtract:109,decimal:110,divide:111,";":186,"=":187,",":188,dash:189,"-":189,period:190,".":190,"forward-slash":191,"/":191,"`":192,"[":219,"\\":220,"]":221,"'":222,"left window key":91,"right window key":92,"select key":93,f1:112,f2:113,f3:114,f4:115,f5:116,f6:117,f7:118,f8:119,f9:120,f10:121,f11:122,f12:123},selectText:function(b,c,d){if(b.setSelectionRange)d?(b.selectionStart=c,b.selectionEnd=d):(a.__tryFocus(b),b.setSelectionRange(c,c));else if(b.createTextRange){var e=b.createTextRange();e.moveStart("character",c),d=d||c,e.moveEnd("character",d-b.value.length),e.select()}},getText:function(b){if(a.typeable.test(b)){var d=c(b);return b.value.substring(d.start,d.end)}var e=a.helpers.getWindow(b);return e.getSelection?e.getSelection().toString():e.document.getSelection?e.document.getSelection().toString():e.document.selection.createRange().text},getSelection:c}),b.extend(a.key,{data:function(c){if(a.key.browser[c])return a.key.browser[c];for(var d in a.key.kinds)if(b.inArray(c,a.key.kinds[d])>-1)return a.key.browser[d];return a.key.browser.character},isSpecial:function(b){for(var c=a.key.kinds.special,d=0;d-1&&a.key.defaults[d])return a.key.defaults[d];return a.key.defaults.character},defaults:{character:function(b,c,d,e,f){if(/num\d+/.test(d)&&(d=d.match(/\d+/)[0]),e||!a.support.keyCharacters&&a.typeable.test(this)){var i=g(this),j=i.substr(0,f.start),k=i.substr(f.end),l=d;h(this,j+l+k);var m="\n"===l&&a.support.textareaCarriage?2:l.length;a.selectText(this,j.length+m)}},c:function(){a.key.ctrlKey?a.key.clipboard=a.getText(this):a.key.defaults.character.apply(this,arguments)},v:function(b,c,d,e,f){a.key.ctrlKey?a.key.defaults.character.call(this,b,c,a.key.clipboard,!0,f):a.key.defaults.character.apply(this,arguments)},a:function(){a.key.ctrlKey?a.selectText(this,0,g(this).length):a.key.defaults.character.apply(this,arguments)},home:function(){a.onParents(this,function(a){return a.scrollHeight!==a.clientHeight?(a.scrollTop=0,!1):void 0})},end:function(){a.onParents(this,function(a){return a.scrollHeight!==a.clientHeight?(a.scrollTop=a.scrollHeight,!1):void 0})},"page-down":function(){a.onParents(this,function(a){if(a.scrollHeight!==a.clientHeight){var b=a.clientHeight;return a.scrollTop+=b,!1}})},"page-up":function(){a.onParents(this,function(a){if(a.scrollHeight!==a.clientHeight){var b=a.clientHeight;return a.scrollTop-=b,!1}})},"\b":function(b,c,d,e,f){if(!a.support.backspaceWorks&&a.typeable.test(this)){var i=g(this),j=i.substr(0,f.start),k=i.substr(f.end);f.start===f.end&&f.start>0?(h(this,j.substring(0,j.length-1)+k),a.selectText(this,f.start-1)):(h(this,j+k),a.selectText(this,f.start))}},"delete":function(b,c,d,e,f){if(!a.support.backspaceWorks&&a.typeable.test(this)){var i=g(this),j=i.substr(0,f.start),k=i.substr(f.end);f.start===f.end&&f.start<=g(this).length-1?h(this,j+k.substring(1)):h(this,j+k),a.selectText(this,f.start)}},"\r":function(b,c,d,e,f){var g=this.nodeName.toLowerCase();if("input"===g&&a.trigger("change",{},this),!a.support.keypressSubmits&&"input"===g){var h=a.closest(this,"form");h&&a.trigger("submit",{},h)}a.support.keyCharacters||"textarea"!==g||a.key.defaults.character.call(this,b,c,"\n",void 0,f),a.support.keypressOnAnchorClicks||"a"!==g||a.trigger("click",{},this)}," ":function(){for(var b,c,e=d(this),f=null,g=0,h=[];gg(this).length?g(this).length:f.end+1):a.selectText(this,f.end+1>g(this).length?g(this).length:f.end+1))},up:function(){/select/i.test(this.nodeName)&&(this.selectedIndex=this.selectedIndex?this.selectedIndex-1:0)},down:function(){/select/i.test(this.nodeName)&&(a.changeOnBlur(this,"selectedIndex",this.selectedIndex),this.selectedIndex=this.selectedIndex+1)},shift:function(){return null},ctrl:function(){return null}}}),b.extend(a.create,{keydown:{setup:function(c,d,e){-1!==b.inArray(d,a.key.kinds.special)&&(a.key[d+"Key"]=e)}},keypress:{setup:function(b,c,d){a.support.keyCharacters&&!a.support.keysOnNotFocused&&a.__tryFocus(d)}},keyup:{setup:function(c,d){-1!==b.inArray(d,a.key.kinds.special)&&(a.key[d+"Key"]=null)}},key:{options:function(c,d){return d="object"!=typeof d?{character:d}:d,d=b.extend({},d),d.character&&(b.extend(d,a.key.options(d.character,c)),delete d.character),d=b.extend({ctrlKey:!!a.key.ctrlKey,altKey:!!a.key.altKey,shiftKey:!!a.key.shiftKey,metaKey:!!a.key.metaKey},d)},event:function(a,c,d){var e,f=b.getWindow(d).document||document;if(f.createEvent){try{e=f.createEvent("KeyEvents"),e.initKeyEvent(a,!0,!0,window,c.ctrlKey,c.altKey,c.shiftKey,c.metaKey,c.keyCode,c.charCode)}catch(g){e=b.createBasicStandardEvent(a,c,f)}return e.synthetic=!0,e}try{e=b.createEventObject.apply(this,arguments),b.extend(e,c)}catch(g){}return e}}});var i={enter:"\r",backspace:"\b",tab:" ",space:" "};return b.extend(a.init.prototype,{_key:function(d,e,f){if(/-up$/.test(d)&&-1!==b.inArray(d.replace("-up",""),a.key.kinds.special))return a.trigger("keyup",d.replace("-up",""),e),f(!0,e);var g,h=b.getWindow(e).document.activeElement,j=a.typeable.test(e)&&c(e),k=i[d]||d,l=a.trigger("keydown",k,e),m=a.key.getDefault,n=a.key.browser.prevent,o=a.key.options(k,"keypress");return l?o?(h!==b.getWindow(e).document.activeElement&&(e=b.getWindow(e).document.activeElement),l=a.trigger("keypress",o,e),l&&(g=m(k).call(e,o,b.getWindow(e),k,void 0,j))):g=m(k).call(e,o,b.getWindow(e),k,void 0,j):o&&-1===b.inArray("keypress",n.keydown)&&(h!==b.getWindow(e).document.activeElement&&(e=b.getWindow(e).document.activeElement),a.trigger("keypress",o,e)),g&&g.nodeName&&(e=g),null!==g?a.schedule(function(){a.support.oninput&&a.trigger("input",a.key.options(k,"input"),e),a.trigger("keyup",a.key.options(k,"keyup"),e),f(l,e)},1):f(l,e),e},_type:function(a,b,c){var d=(a+"").match(/(\[[^\]]+\])|([^\[])/g),e=this,f=function(a,g){var h=d.shift();return h?(g=g||b,h.length>1&&(h=h.substr(1,h.length-2)),void e._key(h,g,f)):void c(a,g)};f()}}),a}(__m2,__m6,__m4),__m7=function(a){!function j(){if(!document.body)return void a.schedule(j,1);var b=document.createElement("div");if(document.body.appendChild(b),a.helpers.extend(b.style,{width:"100px",height:"10000px",backgroundColor:"blue",position:"absolute",top:"10px",left:"0px",zIndex:19999}),document.body.scrollTop=11,document.elementFromPoint){var c=document.elementFromPoint(3,1);c===b?a.support.elementFromClient=!0:a.support.elementFromPage=!0,document.body.removeChild(b),document.body.scrollTop=0}}();var b=function(b,c){var d,e=b.clientX,f=b.clientY,g=a.helpers.getWindow(c);if(a.support.elementFromPage){var h=a.helpers.scrollOffset(g);e+=h.left,f+=h.top}return d=g.document.elementFromPoint?g.document.elementFromPoint(e,f):c,d===g.document.documentElement&&(b.clientY<0||b.clientX<0)?c:d},c=function(c,d,e){var f=b(d,e);return a.trigger(c,d,f||e),f},d=function(c,d,e){var f=b(c,d);if(e!==f&&f&&e){var g=a.helpers.extend({},c);g.relatedTarget=f,a.trigger("mouseout",g,e),g.relatedTarget=e,a.trigger("mouseover",g,f)}return a.trigger("mousemove",c,f||d),f},e=function(c,e,f,g,h){var i,j=new Date,k=e.clientX-c.clientX,l=e.clientY-c.clientY,m=a.helpers.getWindow(g),n=b(c,g),o=m.document.createElement("div"),p=0;i=function q(){var b=new Date,i=a.helpers.scrollOffset(m),r=(0===p?0:b-j)/f,s={clientX:k*r+c.clientX,clientY:l*r+c.clientY};p++,1>r?(a.helpers.extend(o.style,{left:s.clientX+i.left+2+"px",top:s.clientY+i.top+2+"px"}),n=d(s,g,n),a.schedule(q,15)):(n=d(e,g,n),m.document.body.removeChild(o),h())},a.helpers.extend(o.style,{height:"5px",width:"5px",backgroundColor:"red",position:"absolute",zIndex:19999,fontSize:"1px"}),m.document.body.appendChild(o),i()},f=function(a,b,d,f,g){c("mousedown",a,f),e(a,b,d,f,function(){c("mouseup",b,f),g()})},g=function(b){var c=a.jquery()(b),d=c.offset();return{pageX:d.left+c.outerWidth()/2,pageY:d.top+c.outerHeight()/2}},h=function(b,c,d){var e,f=/(\d+)[x ](\d+)/,h=/(\d+)X(\d+)/,i=/([+-]\d+)[xX ]([+-]\d+)/;if("string"==typeof b&&i.test(b)&&d){var j=g(d);e=b.match(i),b={pageX:j.pageX+parseInt(e[1]),pageY:j.pageY+parseInt(e[2])}}if("string"==typeof b&&f.test(b)&&(e=b.match(f),b={pageX:parseInt(e[1]),pageY:parseInt(e[2])}),"string"==typeof b&&h.test(b)&&(e=b.match(h),b={clientX:parseInt(e[1]),clientY:parseInt(e[2])}),"string"==typeof b&&(b=a.jquery()(b,c.document)[0]),b.nodeName&&(b=g(b)),b.pageX){var k=a.helpers.scrollOffset(c);b={clientX:b.pageX-k.left,clientY:b.pageY-k.top}}return b},i=function(b,c,d){if(b.clientY<0){var e=a.helpers.scrollOffset(d),f=e.top+b.clientY-100,g=f-e.top;f>0||(f=0,g=-e.top),b.clientY=b.clientY-g,c.clientY=c.clientY-g,a.helpers.scrollOffset(d,{top:f,left:e.left})}};return a.helpers.extend(a.init.prototype,{_move:function(b,c,d){var f=a.helpers.getWindow(c),g=h(b.from||c,f,c),j=h(b.to||b,f,c);b.adjust!==!1&&i(g,j,f),e(g,j,b.duration||500,c,d)},_drag:function(b,c,d){var e=a.helpers.getWindow(c),g=h(b.from||c,e,c),j=h(b.to||b,e,c);b.adjust!==!1&&i(g,j,e),f(g,j,b.duration||500,c,d)}}),a}(__m2),__m1=function(a){return window.Syn=a,a}(__m2,__m3,__m4,__m5,__m7)}(window); diff --git a/src/Selenium2Driver.php b/src/Selenium2Driver.php index 87d25065..7661f4e0 100755 --- a/src/Selenium2Driver.php +++ b/src/Selenium2Driver.php @@ -12,13 +12,17 @@ use Behat\Mink\Exception\DriverException; use Behat\Mink\Selector\Xpath\Escaper; -use WebDriver\Element; -use WebDriver\Exception\NoSuchElement; -use WebDriver\Exception\UnknownCommand; -use WebDriver\Exception\UnknownError; -use WebDriver\Exception; -use WebDriver\Key; -use WebDriver\WebDriver; +use Facebook\WebDriver\Cookie; +use Facebook\WebDriver\Interactions\WebDriverActions; +use Facebook\WebDriver\Remote\DesiredCapabilities; +use Facebook\WebDriver\Remote\RemoteWebDriver; +use Facebook\WebDriver\WebDriverBy; +use Facebook\WebDriver\WebDriverDimension; +use Facebook\WebDriver\WebDriverElement; +use Facebook\WebDriver\WebDriverKeys; +use Facebook\WebDriver\WebDriverNavigation; +use Facebook\WebDriver\WebDriverOptions; +use Facebook\WebDriver\WebDriverSelect; /** * Selenium2 driver. @@ -29,13 +33,15 @@ class Selenium2Driver extends CoreDriver { /** * Whether the browser has been started + * * @var Boolean */ private $started = false; /** * The WebDriver instance - * @var WebDriver + * + * @var RemoteWebDriver */ private $webDriver; @@ -49,14 +55,9 @@ class Selenium2Driver extends CoreDriver */ private $desiredCapabilities; - /** - * The WebDriverSession instance - * @var \WebDriver\Session - */ - private $wdSession; - /** * The timeout configuration + * * @var array */ private $timeouts = array(); @@ -66,6 +67,13 @@ class Selenium2Driver extends CoreDriver */ private $xpathEscaper; + /** + * Wd host + * + * @var string + */ + private $wdHost; + /** * Instantiates the driver. * @@ -75,9 +83,9 @@ class Selenium2Driver extends CoreDriver */ public function __construct($browserName = 'firefox', $desiredCapabilities = null, $wdHost = 'http://localhost:4444/wd/hub') { - $this->setBrowserName($browserName); + $this->wdHost = $wdHost; + $this->browserName = $browserName; $this->setDesiredCapabilities($desiredCapabilities); - $this->setWebDriver(new WebDriver($wdHost)); $this->xpathEscaper = new Escaper(); } @@ -104,48 +112,15 @@ protected function setBrowserName($browserName = 'firefox') public function setDesiredCapabilities($desiredCapabilities = null) { if ($this->started) { - throw new DriverException("Unable to set desiredCapabilities, the session has already started"); + throw new DriverException('Unable to set desiredCapabilities, the session has already started'); } if (null === $desiredCapabilities) { $desiredCapabilities = array(); } - // Join $desiredCapabilities with defaultCapabilities - $desiredCapabilities = array_replace(self::getDefaultCapabilities(), $desiredCapabilities); - - if (isset($desiredCapabilities['firefox'])) { - foreach ($desiredCapabilities['firefox'] as $capability => $value) { - switch ($capability) { - case 'profile': - $desiredCapabilities['firefox_'.$capability] = base64_encode(file_get_contents($value)); - break; - default: - $desiredCapabilities['firefox_'.$capability] = $value; - } - } - - unset($desiredCapabilities['firefox']); - } - - // See https://sites.google.com/a/chromium.org/chromedriver/capabilities - if (isset($desiredCapabilities['chrome'])) { - - $chromeOptions = array(); - - foreach ($desiredCapabilities['chrome'] as $capability => $value) { - if ($capability == 'switches') { - $chromeOptions['args'] = $value; - } else { - $chromeOptions[$capability] = $value; - } - $desiredCapabilities['chrome.'.$capability] = $value; - } - - $desiredCapabilities['chromeOptions'] = $chromeOptions; - - unset($desiredCapabilities['chrome']); - } + $desiredCapabilities = new DesiredCapabilities($desiredCapabilities); + $desiredCapabilities->setBrowserName($this->browserName); $this->desiredCapabilities = $desiredCapabilities; } @@ -163,23 +138,13 @@ public function getDesiredCapabilities() /** * Sets the WebDriver instance * - * @param WebDriver $webDriver An instance of the WebDriver class + * @param RemoteWebDriver $webDriver An instance of the WebDriver class */ - public function setWebDriver(WebDriver $webDriver) + public function setWebDriver(RemoteWebDriver $webDriver) { $this->webDriver = $webDriver; } - /** - * Gets the WebDriverSession instance - * - * @return \WebDriver\Session - */ - public function getWebDriverSession() - { - return $this->wdSession; - } - /** * Returns the default capabilities * @@ -188,37 +153,11 @@ public function getWebDriverSession() public static function getDefaultCapabilities() { return array( - 'browserName' => 'firefox', - 'name' => 'Behat Test', + 'browserName' => 'firefox', + 'name' => 'Behat Test', ); } - /** - * Makes sure that the Syn event library has been injected into the current page, - * and return $this for a fluid interface, - * - * $this->withSyn()->executeJsOnXpath($xpath, $script); - * - * @return Selenium2Driver - */ - protected function withSyn() - { - $hasSyn = $this->wdSession->execute(array( - 'script' => 'return typeof window["Syn"]!=="undefined" && typeof window["Syn"].trigger!=="undefined"', - 'args' => array() - )); - - if (!$hasSyn) { - $synJs = file_get_contents(__DIR__.'/Resources/syn.js'); - $this->wdSession->execute(array( - 'script' => $synJs, - 'args' => array() - )); - } - - return $this; - } - /** * Creates some options for key events * @@ -240,7 +179,7 @@ protected static function charToOptions($char, $modifier = null) ); if ($modifier) { - $options[$modifier.'Key'] = 1; + $options[$modifier . 'Key'] = 1; } return json_encode($options); @@ -260,7 +199,8 @@ protected static function charToOptions($char, $modifier = null) */ protected function executeJsOnXpath($xpath, $script, $sync = true) { - return $this->executeJsOnElement($this->findElement($xpath), $script, $sync); + $element = $this->findElement($xpath); + return $this->executeJsOnElement($element, $script, $sync); } /** @@ -269,26 +209,21 @@ protected function executeJsOnXpath($xpath, $script, $sync = true) * * @example $this->executeJsOnXpath($xpath, 'return {{ELEMENT}}.childNodes.length'); * - * @param Element $element the webdriver element - * @param string $script the script to execute - * @param Boolean $sync whether to run the script synchronously (default is TRUE) + * @param WebDriverElement $element the webdriver element + * @param string $script the script to execute + * @param Boolean $sync whether to run the script synchronously (default is TRUE) * * @return mixed */ - private function executeJsOnElement(Element $element, $script, $sync = true) + private function executeJsOnElement(WebDriverElement $element, $script, $sync = true) { - $script = str_replace('{{ELEMENT}}', 'arguments[0]', $script); - - $options = array( - 'script' => $script, - 'args' => array(array('ELEMENT' => $element->getID())), - ); + $script = str_replace('{{ELEMENT}}', 'arguments[0]', $script); if ($sync) { - return $this->wdSession->execute($options); + return $this->webDriver->executeScript($script, array(array('ELEMENT' => $element->getID()))); } - return $this->wdSession->execute_async($options); + return $this->webDriver->executeAsyncScript($script, array(array('ELEMENT' => $element->getID()))); } /** @@ -297,46 +232,16 @@ private function executeJsOnElement(Element $element, $script, $sync = true) public function start() { try { - $this->wdSession = $this->webDriver->session($this->browserName, $this->desiredCapabilities); - $this->applyTimeouts(); + $this->webDriver = RemoteWebDriver::create($this->wdHost, $this->desiredCapabilities); } catch (\Exception $e) { - throw new DriverException('Could not open connection: '.$e->getMessage(), 0, $e); + throw new DriverException('Could not open connection: ' . $e->getMessage(), 0, $e); } - if (!$this->wdSession) { + if (!$this->webDriver) { throw new DriverException('Could not connect to a Selenium 2 / WebDriver server'); } - $this->started = true; - } - - /** - * Sets the timeouts to apply to the webdriver session - * - * @param array $timeouts The session timeout settings: Array of {script, implicit, page} => time in milliseconds - * - * @throws DriverException - */ - public function setTimeouts($timeouts) - { - $this->timeouts = $timeouts; - - if ($this->isStarted()) { - $this->applyTimeouts(); - } - } - /** - * Applies timeouts to the current session - */ - private function applyTimeouts() - { - try { - foreach ($this->timeouts as $type => $param) { - $this->wdSession->timeouts($type, $param); - } - } catch (UnknownError $e) { - throw new DriverException('Error setting timeout: ' . $e->getMessage(), 0, $e); - } + $this->started = true; } /** @@ -352,13 +257,13 @@ public function isStarted() */ public function stop() { - if (!$this->wdSession) { + if (!$this->webDriver) { throw new DriverException('Could not connect to a Selenium 2 / WebDriver server'); } $this->started = false; try { - $this->wdSession->close(); + $this->webDriver->quit(); } catch (\Exception $e) { throw new DriverException('Could not close connection', 0, $e); } @@ -369,7 +274,7 @@ public function stop() */ public function reset() { - $this->wdSession->deleteAllCookies(); + $this->manage()->deleteAllCookies(); } /** @@ -377,7 +282,7 @@ public function reset() */ public function visit($url) { - $this->wdSession->open($url); + $this->navigate()->to($url); } /** @@ -385,7 +290,7 @@ public function visit($url) */ public function getCurrentUrl() { - return $this->wdSession->url(); + return $this->webDriver->getCurrentURL(); } /** @@ -393,7 +298,7 @@ public function getCurrentUrl() */ public function reload() { - $this->wdSession->refresh(); + $this->navigate()->refresh(); } /** @@ -401,7 +306,7 @@ public function reload() */ public function forward() { - $this->wdSession->forward(); + $this->navigate()->forward(); } /** @@ -409,7 +314,7 @@ public function forward() */ public function back() { - $this->wdSession->back(); + $this->navigate()->back(); } /** @@ -417,7 +322,7 @@ public function back() */ public function switchToWindow($name = null) { - $this->wdSession->focusWindow($name ? $name : ''); + $this->webDriver->switchTo()->window($name); } /** @@ -425,7 +330,7 @@ public function switchToWindow($name = null) */ public function switchToIFrame($name = null) { - $this->wdSession->frame(array('id' => $name)); + $this->webDriver->switchTo()->frame($name); } /** @@ -434,18 +339,13 @@ public function switchToIFrame($name = null) public function setCookie($name, $value = null) { if (null === $value) { - $this->wdSession->deleteCookie($name); + $this->manage()->deleteCookieNamed($name); return; } - $cookieArray = array( - 'name' => $name, - 'value' => urlencode($value), - 'secure' => false, // thanks, chibimagic! - ); - - $this->wdSession->setCookie($cookieArray); + $cookie = new Cookie($name, \urlencode($value)); + $this->manage()->addCookie($cookie); } /** @@ -453,12 +353,12 @@ public function setCookie($name, $value = null) */ public function getCookie($name) { - $cookies = $this->wdSession->getAllCookies(); - foreach ($cookies as $cookie) { - if ($cookie['name'] === $name) { - return urldecode($cookie['value']); - } + $cookie = $this->manage()->getCookieNamed($name); + if (!$cookie) { + return null; } + + return \urldecode($cookie->getValue()); } /** @@ -466,7 +366,7 @@ public function getCookie($name) */ public function getContent() { - return $this->wdSession->source(); + return $this->webDriver->getPageSource(); } /** @@ -474,7 +374,7 @@ public function getContent() */ public function getScreenshot() { - return base64_decode($this->wdSession->screenshot()); + return $this->webDriver->takeScreenshot(); } /** @@ -482,7 +382,7 @@ public function getScreenshot() */ public function getWindowNames() { - return $this->wdSession->window_handles(); + return $this->webDriver->getWindowHandles(); } /** @@ -490,7 +390,7 @@ public function getWindowNames() */ public function getWindowName() { - return $this->wdSession->window_handle(); + return $this->webDriver->getWindowHandle(); } /** @@ -498,11 +398,11 @@ public function getWindowName() */ public function findElementXpaths($xpath) { - $nodes = $this->wdSession->elements('xpath', $xpath); + $nodes = $this->webDriver->findElements(WebDriverBy::xpath($xpath)); $elements = array(); foreach ($nodes as $i => $node) { - $elements[] = sprintf('(%s)[%d]', $xpath, $i+1); + $elements[] = sprintf('(%s)[%d]', $xpath, $i + 1); } return $elements; @@ -513,7 +413,8 @@ public function findElementXpaths($xpath) */ public function getTagName($xpath) { - return $this->findElement($xpath)->name(); + $element = $this->findElement($xpath); + return $element->getTagName(); } /** @@ -521,8 +422,9 @@ public function getTagName($xpath) */ public function getText($xpath) { - $node = $this->findElement($xpath); - $text = $node->text(); + $element = $this->findElement($xpath); + $text = $element->getText(); + $text = (string) str_replace(array("\r", "\r\n", "\n"), ' ', $text); return $text; @@ -549,9 +451,8 @@ public function getOuterHtml($xpath) */ public function getAttribute($xpath, $name) { - $script = 'return {{ELEMENT}}.getAttribute(' . json_encode((string) $name) . ')'; - - return $this->executeJsOnXpath($xpath, $script); + $element = $this->findElement($xpath); + return $element->getAttribute($name); } /** @@ -560,58 +461,31 @@ public function getAttribute($xpath, $name) public function getValue($xpath) { $element = $this->findElement($xpath); - $elementName = strtolower($element->name()); - $elementType = strtolower($element->attribute('type')); + $elementName = strtolower($element->getTagName()); + $elementType = strtolower($element->getAttribute('type')); // Getting the value of a checkbox returns its value if selected. if ('input' === $elementName && 'checkbox' === $elementType) { - return $element->selected() ? $element->attribute('value') : null; + return $element->isSelected() ? $element->getAttribute('value') : null; } if ('input' === $elementName && 'radio' === $elementType) { - $script = <<executeJsOnElement($element, $script); + // TODO: WebDriverRadios + return false; } // Using $element->attribute('value') on a select only returns the first selected option // even when it is a multiple select, so a custom retrieval is needed. - if ('select' === $elementName && $element->attribute('multiple')) { - $script = <<isMultiple()) { + return $element->getAllSelectedOptions(); + } - return $this->executeJsOnElement($element, $script); + return $element->getFirstSelectedOption(); } - return $element->attribute('value'); + return $element->getAttribute('value'); } /** @@ -620,33 +494,34 @@ public function getValue($xpath) public function setValue($xpath, $value) { $element = $this->findElement($xpath); - $elementName = strtolower($element->name()); + $elementName = strtolower($element->getTagName()); if ('select' === $elementName) { - if (is_array($value)) { - $this->deselectAllOptions($element); + $element = new WebDriverSelect($element); + $element->deselectAll(); + if (is_array($value)) { foreach ($value as $option) { - $this->selectOptionOnElement($element, $option, true); + $element->selectByValue($option); } return; } - $this->selectOptionOnElement($element, $value); + $element->selectByValue($value); return; } if ('input' === $elementName) { - $elementType = strtolower($element->attribute('type')); + $elementType = strtolower($element->getAttribute('type')); if (in_array($elementType, array('submit', 'image', 'button', 'reset'))) { throw new DriverException(sprintf('Impossible to set value an element with XPath "%s" as it is not a select, textarea or textbox', $xpath)); } if ('checkbox' === $elementType) { - if ($element->selected() xor (bool) $value) { + if ($element->isSelected() xor (bool) $value) { $this->clickOnElement($element); } @@ -654,14 +529,12 @@ public function setValue($xpath, $value) } if ('radio' === $elementType) { - $this->selectRadioValue($element, $value); - + throw new \Exception('Not yet'); return; } if ('file' === $elementType) { - $element->postValue(array('value' => array(strval($value)))); - + throw new \Exception('Not yet'); return; } } @@ -669,27 +542,13 @@ public function setValue($xpath, $value) $value = strval($value); if (in_array($elementName, array('input', 'textarea'))) { - $existingValueLength = strlen($element->attribute('value')); + $existingValueLength = strlen($element->getAttribute('value')); // Add the TAB key to ensure we unfocus the field as browsers are triggering the change event only // after leaving the field. - $value = str_repeat(Key::BACKSPACE . Key::DELETE, $existingValueLength) . $value; + $value = str_repeat(WebDriverKeys::BACKSPACE . WebDriverKeys::DELETE, $existingValueLength) . $value; } - $element->postValue(array('value' => array($value))); - // Remove the focus from the element if the field still has focus in - // order to trigger the change event. By doing this instead of simply - // triggering the change event for the given xpath we ensure that the - // change event will not be triggered twice for the same element if it - // has lost focus in the meanwhile. If the element has lost focus - // already then there is nothing to do as this will already have caused - // the triggering of the change event for that element. - $script = <<executeJsOnElement($element, $script); + $element->sendKeys($value); } /** @@ -700,7 +559,7 @@ public function check($xpath) $element = $this->findElement($xpath); $this->ensureInputType($element, $xpath, 'checkbox', 'check'); - if ($element->selected()) { + if ($element->isSelected()) { return; } @@ -715,7 +574,7 @@ public function uncheck($xpath) $element = $this->findElement($xpath); $this->ensureInputType($element, $xpath, 'checkbox', 'uncheck'); - if (!$element->selected()) { + if (!$element->isSelected()) { return; } @@ -727,7 +586,7 @@ public function uncheck($xpath) */ public function isChecked($xpath) { - return $this->findElement($xpath)->selected(); + return $this->isSelected($xpath); } /** @@ -736,17 +595,17 @@ public function isChecked($xpath) public function selectOption($xpath, $value, $multiple = false) { $element = $this->findElement($xpath); - $tagName = strtolower($element->name()); + $tagName = strtolower($element->getTagName()); - if ('input' === $tagName && 'radio' === strtolower($element->attribute('type'))) { + if ('input' === $tagName && 'radio' === strtolower($element->getAttribute('type'))) { $this->selectRadioValue($element, $value); return; } if ('select' === $tagName) { - $this->selectOptionOnElement($element, $value, $multiple); - + $element = new WebDriverSelect($element); + $element->selectByValue($value); return; } @@ -758,7 +617,8 @@ public function selectOption($xpath, $value, $multiple = false) */ public function isSelected($xpath) { - return $this->findElement($xpath)->selected(); + $element = $this->findElement($xpath); + return $element->isSelected(); } /** @@ -766,18 +626,14 @@ public function isSelected($xpath) */ public function click($xpath) { - $this->clickOnElement($this->findElement($xpath)); + $element = $this->findElement($xpath); + $this->clickOnElement($element); } - private function clickOnElement(Element $element) + private function clickOnElement(WebDriverElement $element) { - try { - // Move the mouse to the element as Selenium does not allow clicking on an element which is outside the viewport - $this->wdSession->moveto(array('element' => $element->getID())); - } catch (UnknownCommand $e) { - // If the Webdriver implementation does not support moveto (which is not part of the W3C WebDriver spec), proceed to the click - } - + // Move the mouse to the element as Selenium does not allow clicking on an element which is outside the viewport + $this->action()->moveToElement($element); $element->click(); } @@ -786,8 +642,9 @@ private function clickOnElement(Element $element) */ public function doubleClick($xpath) { - $this->mouseOver($xpath); - $this->wdSession->doubleclick(); + $element = $this->findElement($xpath); + $this->action()->moveToElement($element); + $this->action()->doubleClick($element); } /** @@ -795,8 +652,9 @@ public function doubleClick($xpath) */ public function rightClick($xpath) { - $this->mouseOver($xpath); - $this->wdSession->click(array('button' => 2)); + $element = $this->findElement($xpath); + $this->action()->moveToElement($element); + $this->action()->contextClick($element); } /** @@ -804,20 +662,7 @@ public function rightClick($xpath) */ public function attachFile($xpath, $path) { - $element = $this->findElement($xpath); - $this->ensureInputType($element, $xpath, 'file', 'attach a file on'); - - // Upload the file to Selenium and use the remote path. This will - // ensure that Selenium always has access to the file, even if it runs - // as a remote instance. - try { - $remotePath = $this->uploadFile($path); - } catch (\Exception $e) { - // File could not be uploaded to remote instance. Use the local path. - $remotePath = $path; - } - - $element->postValue(array('value' => array($remotePath))); + throw new \RuntimeException('Not yet'); } /** @@ -825,7 +670,8 @@ public function attachFile($xpath, $path) */ public function isVisible($xpath) { - return $this->findElement($xpath)->displayed(); + $element = $this->findElement($xpath); + return $element->isDisplayed(); } /** @@ -833,9 +679,8 @@ public function isVisible($xpath) */ public function mouseOver($xpath) { - $this->wdSession->moveto(array( - 'element' => $this->findElement($xpath)->getID() - )); + $element = $this->findElement($xpath); + $this->action()->moveToElement($element); } /** @@ -843,7 +688,9 @@ public function mouseOver($xpath) */ public function focus($xpath) { - $this->trigger($xpath, 'focus'); + $element = $this->findElement($xpath); + $this->action()->moveToElement($element); + $element->click(); } /** @@ -851,7 +698,7 @@ public function focus($xpath) */ public function blur($xpath) { - $this->trigger($xpath, 'blur'); + throw new \Exception('Not yet'); } /** @@ -859,8 +706,9 @@ public function blur($xpath) */ public function keyPress($xpath, $char, $modifier = null) { - $options = self::charToOptions($char, $modifier); - $this->trigger($xpath, 'keypress', $options); + // TODO $modifier + $element = $this->findElement($xpath); + $this->action()->sendKeys($element, $char); } /** @@ -868,8 +716,9 @@ public function keyPress($xpath, $char, $modifier = null) */ public function keyDown($xpath, $char, $modifier = null) { - $options = self::charToOptions($char, $modifier); - $this->trigger($xpath, 'keydown', $options); + // TODO $modifier + $element = $this->findElement($xpath); + $this->action()->keyDown($element, $char); } /** @@ -877,8 +726,9 @@ public function keyDown($xpath, $char, $modifier = null) */ public function keyUp($xpath, $char, $modifier = null) { - $options = self::charToOptions($char, $modifier); - $this->trigger($xpath, 'keyup', $options); + // TODO $modifier + $element = $this->findElement($xpath); + $this->action()->keyUp($element, $char); } /** @@ -886,42 +736,9 @@ public function keyUp($xpath, $char, $modifier = null) */ public function dragTo($sourceXpath, $destinationXpath) { - $source = $this->findElement($sourceXpath); + $source = $this->findElement($sourceXpath); $destination = $this->findElement($destinationXpath); - - $this->wdSession->moveto(array( - 'element' => $source->getID() - )); - - $script = <<withSyn()->executeJsOnElement($source, $script); - - $this->wdSession->buttondown(); - $this->wdSession->moveto(array( - 'element' => $destination->getID() - )); - $this->wdSession->buttonup(); - - $script = <<withSyn()->executeJsOnElement($destination, $script); + $this->action()->dragAndDrop($source, $destination); } /** @@ -934,7 +751,7 @@ public function executeScript($script) $script = '(' . $script . ')'; } - $this->wdSession->execute(array('script' => $script, 'args' => array())); + $this->webDriver->executeScript($script); } /** @@ -946,7 +763,7 @@ public function evaluateScript($script) $script = 'return ' . $script; } - return $this->wdSession->execute(array('script' => $script, 'args' => array())); + return $this->webDriver->executeScript($script); } /** @@ -955,15 +772,11 @@ public function evaluateScript($script) public function wait($timeout, $condition) { $script = "return $condition;"; - $start = microtime(true); - $end = $start + $timeout / 1000.0; + $wait = $this->webDriver->wait($timeout, 100); - do { - $result = $this->wdSession->execute(array('script' => $script, 'args' => array())); - usleep(100000); - } while (microtime(true) < $end && !$result); - - return (bool) $result; + return $wait->until(function (RemoteWebDriver $driver) use ($script) { + return $driver->executeScript($script); + }); } /** @@ -971,9 +784,12 @@ public function wait($timeout, $condition) */ public function resizeWindow($width, $height, $name = null) { - $this->wdSession->window($name ? $name : 'current')->postSize( - array('width' => $width, 'height' => $height) - ); + $dimension = new WebDriverDimension($width, $height); + if ($name) { + throw new \Exception('Named windows are not supported yet'); + } + + $this->manage()->window()->setSize($dimension); } /** @@ -981,7 +797,8 @@ public function resizeWindow($width, $height, $name = null) */ public function submitForm($xpath) { - $this->findElement($xpath)->submit(); + $element = $this->findElement($xpath); + $element->submit(); } /** @@ -989,7 +806,11 @@ public function submitForm($xpath) */ public function maximizeWindow($name = null) { - $this->wdSession->window($name ? $name : 'current')->maximize(); + if ($name) { + throw new \Exception('Named window is not supported'); + } + + $this->manage()->window()->maximize(); } /** @@ -999,216 +820,99 @@ public function maximizeWindow($name = null) */ public function getWebDriverSessionId() { - return $this->isStarted() ? basename($this->wdSession->getUrl()) : null; + return $this->webDriver->getSessionID(); } /** * @param string $xpath * - * @return Element + * @return WebDriverElement */ private function findElement($xpath) { - return $this->wdSession->element('xpath', $xpath); + return $this->webDriver->findElement(WebDriverBy::xpath($xpath)); } /** - * Selects a value in a radio button group + * Ensures the element is a checkbox * - * @param Element $element An element referencing one of the radio buttons of the group - * @param string $value The value to select + * @param WebDriverElement $element + * @param string $xpath + * @param string $type + * @param string $action * - * @throws DriverException when the value cannot be found + * @throws DriverException */ - private function selectRadioValue(Element $element, $value) + private function ensureInputType(WebDriverElement $element, $xpath, $type, $action) { - // short-circuit when we already have the right button of the group to avoid XPath queries - if ($element->attribute('value') === $value) { - $element->click(); - - return; - } - - $name = $element->attribute('name'); - - if (!$name) { - throw new DriverException(sprintf('The radio button does not have the value "%s"', $value)); - } - - $formId = $element->attribute('form'); - - try { - if (null !== $formId) { - $xpath = <<<'XPATH' -//form[@id=%1$s]//input[@type="radio" and not(@form) and @name=%2$s and @value = %3$s] -| -//input[@type="radio" and @form=%1$s and @name=%2$s and @value = %3$s] -XPATH; - - $xpath = sprintf( - $xpath, - $this->xpathEscaper->escapeLiteral($formId), - $this->xpathEscaper->escapeLiteral($name), - $this->xpathEscaper->escapeLiteral($value) - ); - $input = $this->wdSession->element('xpath', $xpath); - } else { - $xpath = sprintf( - './ancestor::form//input[@type="radio" and not(@form) and @name=%s and @value = %s]', - $this->xpathEscaper->escapeLiteral($name), - $this->xpathEscaper->escapeLiteral($value) - ); - $input = $element->element('xpath', $xpath); - } - } catch (NoSuchElement $e) { - $message = sprintf('The radio group "%s" does not have an option "%s"', $name, $value); + if ('input' !== strtolower($element->getTagName()) || $type !== strtolower($element->getAttribute('type'))) { + $message = 'Impossible to %s the element with XPath "%s" as it is not a %s input'; - throw new DriverException($message, 0, $e); + throw new DriverException(sprintf($message, $action, $xpath, $type)); } - - $input->click(); } /** - * @param Element $element - * @param string $value - * @param bool $multiple + * @param $xpath + * @param $event + * @param string $options */ - private function selectOptionOnElement(Element $element, $value, $multiple = false) + private function trigger($xpath, $event, $options = '{}') { - $escapedValue = $this->xpathEscaper->escapeLiteral($value); - // The value of an option is the normalized version of its text when it has no value attribute - $optionQuery = sprintf('.//option[@value = %s or (not(@value) and normalize-space(.) = %s)]', $escapedValue, $escapedValue); - $option = $element->element('xpath', $optionQuery); - - if ($multiple || !$element->attribute('multiple')) { - if (!$option->selected()) { - $option->click(); - } - - return; - } + $script = 'Syn.trigger("' . $event . '", ' . $options . ', {{ELEMENT}})'; + $this->withSyn()->executeJsOnXpath($xpath, $script); + } - // Deselect all options before selecting the new one - $this->deselectAllOptions($element); - $option->click(); + private function uploadFile($path) + { + throw new \RuntimeException('Not yet supported'); } /** - * Deselects all options of a multiple select - * - * Note: this implementation does not trigger a change event after deselecting the elements. + * Navigate * - * @param Element $element + * @return WebDriverNavigation */ - private function deselectAllOptions(Element $element) + private function navigate() { - $script = <<webDriver->navigate(); + } - $this->executeJsOnElement($element, $script); + return $navigation; } /** - * Ensures the element is a checkbox - * - * @param Element $element - * @param string $xpath - * @param string $type - * @param string $action + * Manage * - * @throws DriverException + * @return WebDriverOptions */ - private function ensureInputType(Element $element, $xpath, $type, $action) + private function manage() { - if ('input' !== strtolower($element->name()) || $type !== strtolower($element->attribute('type'))) { - $message = 'Impossible to %s the element with XPath "%s" as it is not a %s input'; + static $manage; - throw new DriverException(sprintf($message, $action, $xpath, $type)); + if (!$manage) { + $manage = $this->webDriver->manage(); } - } - /** - * @param $xpath - * @param $event - * @param string $options - */ - private function trigger($xpath, $event, $options = '{}') - { - $script = 'Syn.trigger("' . $event . '", ' . $options . ', {{ELEMENT}})'; - $this->withSyn()->executeJsOnXpath($xpath, $script); + return $manage; } /** - * Uploads a file to the Selenium instance. - * - * Note that uploading files is not part of the official WebDriver - * specification, but it is supported by Selenium. - * - * @param string $path The path to the file to upload. + * Action * - * @return string The remote path. - * - * @throws DriverException When PHP is compiled without zip support, or the file doesn't exist. - * @throws UnknownError When an unknown error occurred during file upload. - * @throws \Exception When a known error occurred during file upload. - * - * @see https://github.com/SeleniumHQ/selenium/blob/master/py/selenium/webdriver/remote/webelement.py#L533 + * @return WebDriverActions */ - private function uploadFile($path) + private function action() { - if (!is_file($path)) { - throw new DriverException('File does not exist locally and cannot be uploaded to the remote instance.'); - } + static $action; - if (!class_exists('ZipArchive')) { - throw new DriverException('Could not compress file, PHP is compiled without zip support.'); + if (!$action) { + $action = $this->webDriver->action(); } - // Selenium only accepts uploads that are compressed as a Zip archive. - $tempFilename = tempnam('', 'WebDriverZip'); - - $archive = new \ZipArchive(); - $result = $archive->open($tempFilename, \ZipArchive::CREATE); - if (!$result) { - throw new DriverException('Zip archive could not be created. Error ' . $result); - } - $result = $archive->addFile($path, basename($path)); - if (!$result) { - throw new DriverException('File could not be added to zip archive.'); - } - $result = $archive->close(); - if (!$result) { - throw new DriverException('Zip archive could not be closed.'); - } - - try { - $remotePath = $this->wdSession->file(array('file' => base64_encode(file_get_contents($tempFilename)))); - - // If no path is returned the file upload failed silently. In this - // case it is possible Selenium was not used but another web driver - // such as PhantomJS. - // @todo Support other drivers when (if) they get remote file transfer - // capability. - if (empty($remotePath)) { - throw new UnknownError(); - } - } catch (\Exception $e) { - // Catch any error so we can still clean up the temporary archive. - } - - unlink($tempFilename); - - if (isset($e)) { - throw $e; - } - - return $remotePath; + return $action; } - } diff --git a/tests/Custom/DesiredCapabilitiesTest.php b/tests/Custom/DesiredCapabilitiesTest.php deleted file mode 100644 index ae613fc9..00000000 --- a/tests/Custom/DesiredCapabilitiesTest.php +++ /dev/null @@ -1,52 +0,0 @@ - 'firefox', - 'version' => '30', - 'platform' => 'ANY', - 'browserVersion' => '30', - 'browser' => 'firefox', - 'name' => 'Selenium2 Mink Driver Test', - 'deviceOrientation' => 'portrait', - 'deviceType' => 'tablet', - 'selenium-version' => '2.45.0' - ); - - $driver = new Selenium2Driver('firefox', $caps); - $this->assertNotEmpty($driver->getDesiredCapabilities(), 'desiredCapabilities empty'); - $this->assertInternalType('array', $driver->getDesiredCapabilities()); - $this->assertEquals($caps, $driver->getDesiredCapabilities()); - } - - /** - * @expectedException \Behat\Mink\Exception\DriverException - * @expectedExceptionMessage Unable to set desiredCapabilities, the session has already started - */ - public function testSetDesiredCapabilities() - { - $caps = array( - 'browserName' => 'firefox', - 'version' => '30', - 'platform' => 'ANY', - 'browserVersion' => '30', - 'browser' => 'firefox', - 'name' => 'Selenium2 Mink Driver Test', - 'deviceOrientation' => 'portrait', - 'deviceType' => 'tablet', - 'selenium-version' => '2.45.0' - ); - $session = $this->getSession(); - $session->start(); - $driver = $session->getDriver(); - $driver->setDesiredCapabilities($caps); - } -} diff --git a/tests/Custom/TimeoutTest.php b/tests/Custom/TimeoutTest.php deleted file mode 100644 index 0c0dd451..00000000 --- a/tests/Custom/TimeoutTest.php +++ /dev/null @@ -1,41 +0,0 @@ -getSession()->start(); - - $this->getSession()->getDriver()->setTimeouts(array('invalid' => 0)); - } - - public function testShortTimeoutDoesNotWaitForElementToAppear() - { - $this->getSession()->getDriver()->setTimeouts(array('implicit' => 0)); - - $this->getSession()->visit($this->pathTo('/js_test.html')); - $this->findById('waitable')->click(); - - $element = $this->getSession()->getPage()->find('css', '#waitable > div'); - - $this->assertNull($element); - } - - public function testLongTimeoutWaitsForElementToAppear() - { - $this->getSession()->getDriver()->setTimeouts(array('implicit' => 5000)); - - $this->getSession()->visit($this->pathTo('/js_test.html')); - $this->findById('waitable')->click(); - $element = $this->getSession()->getPage()->find('css', '#waitable > div'); - - $this->assertNotNull($element); - } -} diff --git a/tests/Custom/WebDriverTest.php b/tests/Custom/WebDriverTest.php deleted file mode 100644 index a6f71e6d..00000000 --- a/tests/Custom/WebDriverTest.php +++ /dev/null @@ -1,21 +0,0 @@ -getSession(); - $session->start(); - /** @var Selenium2Driver $driver */ - $driver = $session->getDriver(); - $this->assertNotEmpty($driver->getWebDriverSessionId(), 'Started session has an ID'); - - $driver = new Selenium2Driver(); - $this->assertNull($driver->getWebDriverSessionId(), 'Not started session don\'t have an ID'); - } -} diff --git a/tests/Custom/WindowNameTest.php b/tests/Custom/WindowNameTest.php deleted file mode 100644 index e75aa469..00000000 --- a/tests/Custom/WindowNameTest.php +++ /dev/null @@ -1,22 +0,0 @@ -getSession(); - $session->start(); - - $windowNames = $session->getWindowNames(); - $this->assertArrayHasKey(0, $windowNames); - - $windowName = $session->getWindowName(); - - $this->assertInternalType('string', $windowName); - $this->assertContains($windowName, $windowNames, 'The current window name is one of the available window names.'); - } -} diff --git a/tests/Selenium2Config.php b/tests/Selenium2Config.php index b5da1bff..da86e3e0 100644 --- a/tests/Selenium2Config.php +++ b/tests/Selenium2Config.php @@ -19,7 +19,28 @@ public function createDriver() $browser = getenv('WEB_FIXTURES_BROWSER') ?: 'firefox'; $seleniumHost = $_SERVER['DRIVER_URL']; - return new Selenium2Driver($browser, null, $seleniumHost); + return new Selenium2Driver($browser, array( + 'browser' => $browser, + 'browserName' => $browser, + 'version' => 'ANY', + 'chromeOptions' => array( + 'args' => array( + "no-sandbox", + "dbus-stub", + "disable-dev-shm-usage", + "reduce-security-for-testing", + "allow-insecure-localhost", + "enable-logging", + "disable-setuid-sandbox", + "disable-gpu", + "disable-web-security", + "disk-cache-size=1", + "allow-running-insecure-content", + "ignore-certificate-errors", + "ignore-urlfetcher-cert-requests", + ) + ) + ), $seleniumHost); } /** From 9921b40df4f3b496cda9abac0708895901c80fc0 Mon Sep 17 00:00:00 2001 From: Oleg Andreyev Date: Sat, 1 Dec 2018 01:01:28 +0200 Subject: [PATCH 02/65] fixing initial commit --- composer.json | 5 ++- phpunit.xml.dist | 9 +++-- src/Selenium2Driver.php | 87 +++++++++++++++++++++++++++++------------ 3 files changed, 70 insertions(+), 31 deletions(-) diff --git a/composer.json b/composer.json index a6bdc457..69df8911 100644 --- a/composer.json +++ b/composer.json @@ -26,9 +26,10 @@ ], "require": { "php": ">=5.3.1", + "ext-zip": "*", + "ext-curl": "*", "behat/mink": "~1.7@dev", - "facebook/webdriver": "^1.6", - "ext-zip": "*" + "facebook/webdriver": "dev-community" }, "require-dev": { "roave/security-advisories": "dev-master", diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 58a01533..286507f3 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,6 +1,9 @@ - + tests @@ -11,8 +14,8 @@ - - + + diff --git a/src/Selenium2Driver.php b/src/Selenium2Driver.php index 7661f4e0..83dafd85 100755 --- a/src/Selenium2Driver.php +++ b/src/Selenium2Driver.php @@ -15,6 +15,7 @@ use Facebook\WebDriver\Cookie; use Facebook\WebDriver\Interactions\WebDriverActions; use Facebook\WebDriver\Remote\DesiredCapabilities; +use Facebook\WebDriver\Remote\RemoteTargetLocator; use Facebook\WebDriver\Remote\RemoteWebDriver; use Facebook\WebDriver\WebDriverBy; use Facebook\WebDriver\WebDriverDimension; @@ -22,6 +23,7 @@ use Facebook\WebDriver\WebDriverKeys; use Facebook\WebDriver\WebDriverNavigation; use Facebook\WebDriver\WebDriverOptions; +use Facebook\WebDriver\WebDriverRadios; use Facebook\WebDriver\WebDriverSelect; /** @@ -73,6 +75,10 @@ class Selenium2Driver extends CoreDriver * @var string */ private $wdHost; + private $navigation; + private $action; + private $switchTo; + private $manage; /** * Instantiates the driver. @@ -264,6 +270,12 @@ public function stop() $this->started = false; try { $this->webDriver->quit(); + + $this->webDriver = null; + $this->navigation = null; + $this->action = null; + $this->switchTo = null; + $this->manage = null; } catch (\Exception $e) { throw new DriverException('Could not close connection', 0, $e); } @@ -322,7 +334,7 @@ public function back() */ public function switchToWindow($name = null) { - $this->webDriver->switchTo()->window($name); + $this->switchTo()->window($name); } /** @@ -330,7 +342,12 @@ public function switchToWindow($name = null) */ public function switchToIFrame($name = null) { - $this->webDriver->switchTo()->frame($name); + if ($name) { + $element = $this->webDriver->findElement(WebDriverBy::name($name)); + $this->switchTo()->frame($element); + } else { + $this->switchTo()->defaultContent(); + } } /** @@ -452,7 +469,10 @@ public function getOuterHtml($xpath) public function getAttribute($xpath, $name) { $element = $this->findElement($xpath); - return $element->getAttribute($name); + // https://w3c.github.io/webdriver/#get-element-attribute + $attribute = $element->getAttribute($name); + + return $attribute ?: null; } /** @@ -470,8 +490,12 @@ public function getValue($xpath) } if ('input' === $elementName && 'radio' === $elementType) { - // TODO: WebDriverRadios - return false; + $element = new WebDriverRadios($element); + if ($element->isMultiple()) { + return $element->getAllSelectedOptions(); + } + + return $element->getFirstSelectedOption()->getAttribute('value'); } // Using $element->attribute('value') on a select only returns the first selected option @@ -482,7 +506,7 @@ public function getValue($xpath) return $element->getAllSelectedOptions(); } - return $element->getFirstSelectedOption(); + return $element->getFirstSelectedOption()->getAttribute('value'); } return $element->getAttribute('value'); @@ -598,8 +622,8 @@ public function selectOption($xpath, $value, $multiple = false) $tagName = strtolower($element->getTagName()); if ('input' === $tagName && 'radio' === strtolower($element->getAttribute('type'))) { - $this->selectRadioValue($element, $value); - + $element = new WebDriverRadios($element); + $element->selectByValue($value); return; } @@ -662,7 +686,11 @@ public function rightClick($xpath) */ public function attachFile($xpath, $path) { - throw new \RuntimeException('Not yet'); + $element = $this->findElement($xpath); + $ref = new \ReflectionMethod($element, 'upload'); + $ref->setAccessible(true); + $remotePath = $ref->invoke($element, $path); + return $remotePath; } /** @@ -680,7 +708,7 @@ public function isVisible($xpath) public function mouseOver($xpath) { $element = $this->findElement($xpath); - $this->action()->moveToElement($element); + $this->action()->moveToElement($element)->perform(); } /** @@ -689,8 +717,7 @@ public function mouseOver($xpath) public function focus($xpath) { $element = $this->findElement($xpath); - $this->action()->moveToElement($element); - $element->click(); + $this->action()->moveToElement($element)->click($element)->perform(); } /** @@ -875,13 +902,11 @@ private function uploadFile($path) */ private function navigate() { - static $navigation; - - if (!$navigation) { - $navigation = $this->webDriver->navigate(); + if (!$this->navigation) { + $this->navigation = $this->webDriver->navigate(); } - return $navigation; + return $this->navigation; } /** @@ -891,13 +916,11 @@ private function navigate() */ private function manage() { - static $manage; - - if (!$manage) { - $manage = $this->webDriver->manage(); + if (!$this->manage) { + $this->manage = $this->webDriver->manage(); } - return $manage; + return $this->manage; } /** @@ -907,12 +930,24 @@ private function manage() */ private function action() { - static $action; + if (!$this->action) { + $this->action = $this->webDriver->action(); + } - if (!$action) { - $action = $this->webDriver->action(); + return $this->action; + } + + /** + * Switch to + * + * @return RemoteTargetLocator + */ + private function switchTo() + { + if (!$this->switchTo) { + $this->switchTo = $this->webDriver->switchTo(); } - return $action; + return $this->switchTo; } } From 34197e2a5bd102a3ff2a28352581199fadf2e520 Mon Sep 17 00:00:00 2001 From: Oleg Andreyev Date: Sat, 1 Dec 2018 01:06:08 +0200 Subject: [PATCH 03/65] added chrome to travis --- .travis.yml | 7 ++++++- bin/run-selenium-remote-chrome.sh | 7 +++++++ ...n-selenium-remote.sh => run-selenium-remote-firefox.sh} | 4 ++-- 3 files changed, 15 insertions(+), 3 deletions(-) create mode 100644 bin/run-selenium-remote-chrome.sh rename bin/{run-selenium-remote.sh => run-selenium-remote-firefox.sh} (71%) diff --git a/.travis.yml b/.travis.yml index f9916434..d9372862 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,7 +16,12 @@ matrix: fast_finish: true include: - php: 7.0 - env: WEBDRIVER=selenium-remote + env: WEBDRIVER=selenium-remote-firefox + sudo: required + services: + - docker + - php: 7.0 + env: WEBDRIVER=selenium-remote-chrome sudo: required services: - docker diff --git a/bin/run-selenium-remote-chrome.sh b/bin/run-selenium-remote-chrome.sh new file mode 100644 index 00000000..5d676f67 --- /dev/null +++ b/bin/run-selenium-remote-chrome.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env sh +set -e + +echo ' Downloading selenium' +docker pull selenium/standalone-chrome:latest +echo ' Running selenium' +docker run -d -p 4444:4444 --network=host selenium/standalone-chrome:latest diff --git a/bin/run-selenium-remote.sh b/bin/run-selenium-remote-firefox.sh similarity index 71% rename from bin/run-selenium-remote.sh rename to bin/run-selenium-remote-firefox.sh index 9d2205d1..0cac878a 100644 --- a/bin/run-selenium-remote.sh +++ b/bin/run-selenium-remote-firefox.sh @@ -2,6 +2,6 @@ set -e echo ' Downloading selenium' -docker pull selenium/standalone-firefox:2.53.1 +docker pull selenium/standalone-firefox:latest echo ' Running selenium' -docker run -d -p 4444:4444 --network=host selenium/standalone-firefox:2.53.1 +docker run -d -p 4444:4444 --network=host selenium/standalone-firefox:latest From ecd9e465d62854e7be005faaff79b617396abee3 Mon Sep 17 00:00:00 2001 From: Oleg Andreyev Date: Sat, 1 Dec 2018 01:09:24 +0200 Subject: [PATCH 04/65] updated run-selenium.sh --- bin/run-selenium.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/run-selenium.sh b/bin/run-selenium.sh index e846d3ae..0cacef67 100644 --- a/bin/run-selenium.sh +++ b/bin/run-selenium.sh @@ -6,6 +6,6 @@ sh -e /etc/init.d/xvfb start export DISPLAY=:99.0 echo ' Downloading selenium' -curl -L http://selenium-release.storage.googleapis.com/2.52/selenium-server-standalone-2.52.0.jar > selenium.jar +curl -L http://selenium-release.storage.googleapis.com/2.53/selenium-server-standalone-2.53.1.jar > selenium.jar echo ' Running selenium' java -jar selenium.jar -log /tmp/webdriver.log > /tmp/webdriver_output.txt 2>&1 & From 305323df63989cd920f4ea9beb8287c645762268 Mon Sep 17 00:00:00 2001 From: Oleg Andreyev Date: Sun, 2 Dec 2018 20:10:13 +0200 Subject: [PATCH 05/65] crlf to lf for bin, removed local override in phpunit.xml.dist, some fixes to Selenium2Driver.php --- phpunit.xml.dist | 4 +- src/Selenium2Driver.php | 206 ++++++++++++++++++++++++---------------- 2 files changed, 125 insertions(+), 85 deletions(-) diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 286507f3..4ba3efe4 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -14,8 +14,8 @@ - - + + diff --git a/src/Selenium2Driver.php b/src/Selenium2Driver.php index 83dafd85..919bcdd6 100755 --- a/src/Selenium2Driver.php +++ b/src/Selenium2Driver.php @@ -13,6 +13,7 @@ use Behat\Mink\Exception\DriverException; use Behat\Mink\Selector\Xpath\Escaper; use Facebook\WebDriver\Cookie; +use Facebook\WebDriver\Exception\NoSuchElementException; use Facebook\WebDriver\Interactions\WebDriverActions; use Facebook\WebDriver\Remote\DesiredCapabilities; use Facebook\WebDriver\Remote\RemoteTargetLocator; @@ -286,7 +287,7 @@ public function stop() */ public function reset() { - $this->manage()->deleteAllCookies(); + $this->webDriver->manage()->deleteAllCookies(); } /** @@ -294,7 +295,7 @@ public function reset() */ public function visit($url) { - $this->navigate()->to($url); + $this->webDriver->navigate()->to($url); } /** @@ -310,7 +311,7 @@ public function getCurrentUrl() */ public function reload() { - $this->navigate()->refresh(); + $this->webDriver->navigate()->refresh(); } /** @@ -318,7 +319,7 @@ public function reload() */ public function forward() { - $this->navigate()->forward(); + $this->webDriver->navigate()->forward(); } /** @@ -326,7 +327,7 @@ public function forward() */ public function back() { - $this->navigate()->back(); + $this->webDriver->navigate()->back(); } /** @@ -334,7 +335,7 @@ public function back() */ public function switchToWindow($name = null) { - $this->switchTo()->window($name); + $this->webDriver->switchTo()->window($name); } /** @@ -344,9 +345,9 @@ public function switchToIFrame($name = null) { if ($name) { $element = $this->webDriver->findElement(WebDriverBy::name($name)); - $this->switchTo()->frame($element); + $this->webDriver->switchTo()->frame($element); } else { - $this->switchTo()->defaultContent(); + $this->webDriver->switchTo()->defaultContent(); } } @@ -356,13 +357,13 @@ public function switchToIFrame($name = null) public function setCookie($name, $value = null) { if (null === $value) { - $this->manage()->deleteCookieNamed($name); + $this->webDriver->manage()->deleteCookieNamed($name); return; } $cookie = new Cookie($name, \urlencode($value)); - $this->manage()->addCookie($cookie); + $this->webDriver->manage()->addCookie($cookie); } /** @@ -370,7 +371,7 @@ public function setCookie($name, $value = null) */ public function getCookie($name) { - $cookie = $this->manage()->getCookieNamed($name); + $cookie = $this->webDriver->manage()->getCookieNamed($name); if (!$cookie) { return null; } @@ -383,7 +384,8 @@ public function getCookie($name) */ public function getContent() { - return $this->webDriver->getPageSource(); + $source = $this->webDriver->getPageSource(); + return str_replace(array("\r", "\r\n", "\n"), \PHP_EOL, $source); } /** @@ -491,10 +493,6 @@ public function getValue($xpath) if ('input' === $elementName && 'radio' === $elementType) { $element = new WebDriverRadios($element); - if ($element->isMultiple()) { - return $element->getAllSelectedOptions(); - } - return $element->getFirstSelectedOption()->getAttribute('value'); } @@ -503,7 +501,9 @@ public function getValue($xpath) if ('select' === $elementName) { $element = new WebDriverSelect($element); if ($element->isMultiple()) { - return $element->getAllSelectedOptions(); + return \array_map(function (WebDriverElement $element) { + return $element->getAttribute('value'); + }, $element->getAllSelectedOptions()); } return $element->getFirstSelectedOption()->getAttribute('value'); @@ -522,9 +522,9 @@ public function setValue($xpath, $value) if ('select' === $elementName) { $element = new WebDriverSelect($element); - $element->deselectAll(); if (is_array($value)) { + $element->deselectAll(); foreach ($value as $option) { $element->selectByValue($option); } @@ -553,12 +553,13 @@ public function setValue($xpath, $value) } if ('radio' === $elementType) { - throw new \Exception('Not yet'); + $element = new WebDriverRadios($element); + $element->selectByValue($value); return; } if ('file' === $elementType) { - throw new \Exception('Not yet'); + $element->sendKeys($value); return; } } @@ -573,6 +574,14 @@ public function setValue($xpath, $value) } $element->sendKeys($value); + // Remove the focus from the element if the field still has focus in + // order to trigger the change event. By doing this instead of simply + // triggering the change event for the given xpath we ensure that the + // change event will not be triggered twice for the same element if it + // has lost focus in the meanwhile. If the element has lost focus + // already then there is nothing to do as this will already have caused + // the triggering of the change event for that element. + $element->sendKeys(WebDriverKeys::TAB); } /** @@ -629,7 +638,17 @@ public function selectOption($xpath, $value, $multiple = false) if ('select' === $tagName) { $element = new WebDriverSelect($element); - $element->selectByValue($value); + if (!$multiple && $element->isMultiple()) { + $element->deselectAll(); + } + + try { + $element->selectByValue($value); + } catch (NoSuchElementException $e) { + // option may not have value attribute, so try to select by visible text + $element->selectByVisibleText($value); + } + return; } @@ -657,8 +676,7 @@ public function click($xpath) private function clickOnElement(WebDriverElement $element) { // Move the mouse to the element as Selenium does not allow clicking on an element which is outside the viewport - $this->action()->moveToElement($element); - $element->click(); + $this->webDriver->action()->click($element)->perform(); } /** @@ -667,8 +685,7 @@ private function clickOnElement(WebDriverElement $element) public function doubleClick($xpath) { $element = $this->findElement($xpath); - $this->action()->moveToElement($element); - $this->action()->doubleClick($element); + $this->webDriver->action()->doubleClick($element)->perform(); } /** @@ -677,8 +694,7 @@ public function doubleClick($xpath) public function rightClick($xpath) { $element = $this->findElement($xpath); - $this->action()->moveToElement($element); - $this->action()->contextClick($element); + $this->webDriver->action()->contextClick($element)->perform(); } /** @@ -687,9 +703,10 @@ public function rightClick($xpath) public function attachFile($xpath, $path) { $element = $this->findElement($xpath); - $ref = new \ReflectionMethod($element, 'upload'); - $ref->setAccessible(true); - $remotePath = $ref->invoke($element, $path); + $this->ensureInputType($element, $xpath, 'file', 'attach a file on'); + + $remotePath = $element->sendKeys($path); + return $remotePath; } @@ -708,7 +725,7 @@ public function isVisible($xpath) public function mouseOver($xpath) { $element = $this->findElement($xpath); - $this->action()->moveToElement($element)->perform(); + $this->webDriver->action()->moveToElement($element)->perform(); } /** @@ -717,7 +734,7 @@ public function mouseOver($xpath) public function focus($xpath) { $element = $this->findElement($xpath); - $this->action()->moveToElement($element)->click($element)->perform(); + $this->webDriver->action()->moveToElement($element)->click($element)->perform(); } /** @@ -725,7 +742,8 @@ public function focus($xpath) */ public function blur($xpath) { - throw new \Exception('Not yet'); + $element = $this->findElement($xpath); + $this->webDriver->action()->moveToElement($element)->sendKeys($element, WebDriverKeys::TAB)->perform(); } /** @@ -733,9 +751,22 @@ public function blur($xpath) */ public function keyPress($xpath, $char, $modifier = null) { - // TODO $modifier + // @see https://w3c.github.io/uievents/#event-type-keypress $element = $this->findElement($xpath); - $this->action()->sendKeys($element, $char); + $char = $this->decodeChar($char); + $action = $this->webDriver->action(); + + $keyboard = $this->webDriver->getKeyboard(); + $this->clickOnElement($element); + if ($modifier) { + $keyboard->pressKey($this->keyModifier($modifier)); + } + $keyboard->sendKeys($char); + if ($modifier) { + $keyboard->releaseKey($this->keyModifier($modifier)); + } + + $action->perform(); } /** @@ -743,9 +774,22 @@ public function keyPress($xpath, $char, $modifier = null) */ public function keyDown($xpath, $char, $modifier = null) { - // TODO $modifier + // @see https://w3c.github.io/uievents/#event-type-keydown $element = $this->findElement($xpath); - $this->action()->keyDown($element, $char); + $char = $this->decodeChar($char); + $action = $this->webDriver->action(); + + $keyboard = $this->webDriver->getKeyboard(); + $this->clickOnElement($element); + if ($modifier) { + $keyboard->pressKey($this->keyModifier($modifier)); + } + $keyboard->sendKeys($char); + if ($modifier) { + $keyboard->releaseKey($this->keyModifier($modifier)); + } + + $action->perform(); } /** @@ -753,9 +797,21 @@ public function keyDown($xpath, $char, $modifier = null) */ public function keyUp($xpath, $char, $modifier = null) { - // TODO $modifier $element = $this->findElement($xpath); - $this->action()->keyUp($element, $char); + $char = $this->decodeChar($char); + $action = $this->webDriver->action(); + + $keyboard = $this->webDriver->getKeyboard(); + $this->clickOnElement($element); + if ($modifier) { + $keyboard->pressKey($this->keyModifier($modifier)); + } + $keyboard->sendKeys($char); + if ($modifier) { + $keyboard->releaseKey($this->keyModifier($modifier)); + } + + $action->perform(); } /** @@ -765,7 +821,7 @@ public function dragTo($sourceXpath, $destinationXpath) { $source = $this->findElement($sourceXpath); $destination = $this->findElement($destinationXpath); - $this->action()->dragAndDrop($source, $destination); + $this->webDriver->action()->dragAndDrop($source, $destination)->perform(); } /** @@ -799,9 +855,11 @@ public function evaluateScript($script) public function wait($timeout, $condition) { $script = "return $condition;"; - $wait = $this->webDriver->wait($timeout, 100); + $seconds = $timeout / 1000.0; + + $wait = $this->webDriver->wait($seconds, 100); - return $wait->until(function (RemoteWebDriver $driver) use ($script) { + return (bool) $wait->until(function (RemoteWebDriver $driver) use ($script) { return $driver->executeScript($script); }); } @@ -816,7 +874,7 @@ public function resizeWindow($width, $height, $name = null) throw new \Exception('Named windows are not supported yet'); } - $this->manage()->window()->setSize($dimension); + $this->webDriver->manage()->window()->setSize($dimension); } /** @@ -837,7 +895,7 @@ public function maximizeWindow($name = null) throw new \Exception('Named window is not supported'); } - $this->manage()->window()->maximize(); + $this->webDriver->manage()->window()->maximize(); } /** @@ -896,58 +954,40 @@ private function uploadFile($path) } /** - * Navigate + * Prepend modifier * - * @return WebDriverNavigation - */ - private function navigate() - { - if (!$this->navigation) { - $this->navigation = $this->webDriver->navigate(); - } - - return $this->navigation; - } - - /** - * Manage + * @param string $modifier * - * @return WebDriverOptions + * @return string */ - private function manage() + private function keyModifier($modifier) { - if (!$this->manage) { - $this->manage = $this->webDriver->manage(); + if ($modifier === 'alt') { + $modifier = WebDriverKeys::ALT; + } else if ($modifier === 'ctrl') { + $modifier = WebDriverKeys::CONTROL; + } else if ($modifier === 'shift') { + $modifier = WebDriverKeys::SHIFT; + } else if ($modifier === 'meta') { + $modifier = WebDriverKeys::META; } - return $this->manage; - } + return $modifier; +} /** - * Action + * Decode char * - * @return WebDriverActions - */ - private function action() - { - if (!$this->action) { - $this->action = $this->webDriver->action(); - } - - return $this->action; - } - - /** - * Switch to + * @param $char * - * @return RemoteTargetLocator + * @return string */ - private function switchTo() + private function decodeChar($char) { - if (!$this->switchTo) { - $this->switchTo = $this->webDriver->switchTo(); + if (\is_numeric($char)) { + return \chr($char); } - return $this->switchTo; + return $char; } } From 6666a62c64ab5bd39aa237924acae53ed80ef628 Mon Sep 17 00:00:00 2001 From: Oleg Andreyev Date: Sun, 2 Dec 2018 20:21:53 +0200 Subject: [PATCH 06/65] adjusted travis for chrome and ff --- .travis.yml | 10 ++++++++-- phpunit.xml.dist | 4 +++- tests/Selenium2Config.php | 31 +++++++++---------------------- 3 files changed, 20 insertions(+), 25 deletions(-) diff --git a/.travis.yml b/.travis.yml index d9372862..2544848a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,12 +16,18 @@ matrix: fast_finish: true include: - php: 7.0 - env: WEBDRIVER=selenium-remote-firefox + env: + - WEBDRIVER=selenium-remote-firefox + - WEB_FIXTURES_BROWSER=firefox + - DRIVER_OPTIONS={} sudo: required services: - docker - php: 7.0 - env: WEBDRIVER=selenium-remote-chrome + env: + - WEBDRIVER=selenium-remote-chrome + - WEB_FIXTURES_BROWSER=chrome + - DRIVER_OPTIONS={"chromeOptions":{"args":["no-sandbox","dbus-stub","disable-dev-shm-usage","reduce-security-for-testing","allow-insecure-localhost","enable-logging","disable-setuid-sandbox","disable-gpu","disable-web-security","disk-cache-size=1","allow-running-insecure-content","ignore-certificate-errors","ignore-urlfetcher-cert-requests"]}} sudo: required services: - docker diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 4ba3efe4..a25ab304 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -15,7 +15,7 @@ - + @@ -23,6 +23,8 @@ + + diff --git a/tests/Selenium2Config.php b/tests/Selenium2Config.php index da86e3e0..1390bccd 100644 --- a/tests/Selenium2Config.php +++ b/tests/Selenium2Config.php @@ -17,30 +17,17 @@ public static function getInstance() public function createDriver() { $browser = getenv('WEB_FIXTURES_BROWSER') ?: 'firefox'; + $driverOptions = getenv('DRIVER_OPTIONS') ?: []; $seleniumHost = $_SERVER['DRIVER_URL']; - return new Selenium2Driver($browser, array( - 'browser' => $browser, - 'browserName' => $browser, - 'version' => 'ANY', - 'chromeOptions' => array( - 'args' => array( - "no-sandbox", - "dbus-stub", - "disable-dev-shm-usage", - "reduce-security-for-testing", - "allow-insecure-localhost", - "enable-logging", - "disable-setuid-sandbox", - "disable-gpu", - "disable-web-security", - "disk-cache-size=1", - "allow-running-insecure-content", - "ignore-certificate-errors", - "ignore-urlfetcher-cert-requests", - ) - ) - ), $seleniumHost); + $desiredCapabilities = array( + 'browser' => $browser, + 'browserName' => $browser, + 'version' => 'ANY' + ); + $desiredCapabilities = \array_merge($desiredCapabilities, $driverOptions); + + return new Selenium2Driver($browser, $desiredCapabilities, $seleniumHost); } /** From 3a988ef7e30b02699cc60587772f7e86c6078b4d Mon Sep 17 00:00:00 2001 From: Oleg Andreyev Date: Sun, 2 Dec 2018 20:25:35 +0200 Subject: [PATCH 07/65] fixing --- tests/Selenium2Config.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Selenium2Config.php b/tests/Selenium2Config.php index 1390bccd..a6f73f16 100644 --- a/tests/Selenium2Config.php +++ b/tests/Selenium2Config.php @@ -17,7 +17,7 @@ public static function getInstance() public function createDriver() { $browser = getenv('WEB_FIXTURES_BROWSER') ?: 'firefox'; - $driverOptions = getenv('DRIVER_OPTIONS') ?: []; + $driverOptions = getenv('DRIVER_OPTIONS') ? \json_decode(getenv('DRIVER_OPTIONS'), true) : array(); $seleniumHost = $_SERVER['DRIVER_URL']; $desiredCapabilities = array( From 4b97f280338ceec2eb3ae492c75412db4a6e7bdf Mon Sep 17 00:00:00 2001 From: Oleg Andreyev Date: Sun, 2 Dec 2018 20:31:44 +0200 Subject: [PATCH 08/65] fixing --- .travis.yml | 4 ++-- composer.json | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2544848a..4789a550 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,7 +19,7 @@ matrix: env: - WEBDRIVER=selenium-remote-firefox - WEB_FIXTURES_BROWSER=firefox - - DRIVER_OPTIONS={} + - DRIVER_OPTIONS='{}' sudo: required services: - docker @@ -27,7 +27,7 @@ matrix: env: - WEBDRIVER=selenium-remote-chrome - WEB_FIXTURES_BROWSER=chrome - - DRIVER_OPTIONS={"chromeOptions":{"args":["no-sandbox","dbus-stub","disable-dev-shm-usage","reduce-security-for-testing","allow-insecure-localhost","enable-logging","disable-setuid-sandbox","disable-gpu","disable-web-security","disk-cache-size=1","allow-running-insecure-content","ignore-certificate-errors","ignore-urlfetcher-cert-requests"]}} + - DRIVER_OPTIONS=''{"chromeOptions":{"args":["no-sandbox","dbus-stub","disable-dev-shm-usage","reduce-security-for-testing","allow-insecure-localhost","enable-logging","disable-setuid-sandbox","disable-gpu","disable-web-security","disk-cache-size=1","allow-running-insecure-content","ignore-certificate-errors","ignore-urlfetcher-cert-requests"]}} sudo: required services: - docker diff --git a/composer.json b/composer.json index 69df8911..ae726a52 100644 --- a/composer.json +++ b/composer.json @@ -28,6 +28,7 @@ "php": ">=5.3.1", "ext-zip": "*", "ext-curl": "*", + "ext-json": "*", "behat/mink": "~1.7@dev", "facebook/webdriver": "dev-community" }, From ed49807121b6b535dd45e5baab24c7f8ea052d86 Mon Sep 17 00:00:00 2001 From: Oleg Andreyev Date: Sun, 2 Dec 2018 20:32:52 +0200 Subject: [PATCH 09/65] fixing --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 4789a550..f65c2a3b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -27,7 +27,7 @@ matrix: env: - WEBDRIVER=selenium-remote-chrome - WEB_FIXTURES_BROWSER=chrome - - DRIVER_OPTIONS=''{"chromeOptions":{"args":["no-sandbox","dbus-stub","disable-dev-shm-usage","reduce-security-for-testing","allow-insecure-localhost","enable-logging","disable-setuid-sandbox","disable-gpu","disable-web-security","disk-cache-size=1","allow-running-insecure-content","ignore-certificate-errors","ignore-urlfetcher-cert-requests"]}} + - DRIVER_OPTIONS='{"chromeOptions":{"args":["no-sandbox","dbus-stub","disable-dev-shm-usage","reduce-security-for-testing","allow-insecure-localhost","enable-logging","disable-setuid-sandbox","disable-gpu","disable-web-security","disk-cache-size=1","allow-running-insecure-content","ignore-certificate-errors","ignore-urlfetcher-cert-requests"]}}' sudo: required services: - docker From 2181be33583171eb06d5c71c8b39f1b1ceff5cbd Mon Sep 17 00:00:00 2001 From: Oleg Andreyev Date: Sat, 8 Dec 2018 02:25:48 +0200 Subject: [PATCH 10/65] fixing upload & wait --- src/Selenium2Driver.php | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/Selenium2Driver.php b/src/Selenium2Driver.php index 919bcdd6..54180428 100755 --- a/src/Selenium2Driver.php +++ b/src/Selenium2Driver.php @@ -16,6 +16,7 @@ use Facebook\WebDriver\Exception\NoSuchElementException; use Facebook\WebDriver\Interactions\WebDriverActions; use Facebook\WebDriver\Remote\DesiredCapabilities; +use Facebook\WebDriver\Remote\LocalFileDetector; use Facebook\WebDriver\Remote\RemoteTargetLocator; use Facebook\WebDriver\Remote\RemoteWebDriver; use Facebook\WebDriver\WebDriverBy; @@ -562,6 +563,11 @@ public function setValue($xpath, $value) $element->sendKeys($value); return; } + +// if ('color' === $elementType) { +// $this->executeJsOnElement($element, sprintf('{{ELEMENT}}.value = "%s";', $value)); +// return; +// } } $value = strval($value); @@ -705,6 +711,7 @@ public function attachFile($xpath, $path) $element = $this->findElement($xpath); $this->ensureInputType($element, $xpath, 'file', 'attach a file on'); + $element->setFileDetector(new LocalFileDetector()); $remotePath = $element->sendKeys($path); return $remotePath; @@ -860,7 +867,17 @@ public function wait($timeout, $condition) $wait = $this->webDriver->wait($seconds, 100); return (bool) $wait->until(function (RemoteWebDriver $driver) use ($script) { - return $driver->executeScript($script); + $result = $driver->executeScript($script); + // stringify result + if ($result === true) { + $result = 'true'; + } else if ($result === false) { + $result = 'false'; + } else if ($result === null) { + $result = 'null'; + } + + return (string) $result; }); } From d1cdddc615e01f6c41789abe87fb19a53478f97e Mon Sep 17 00:00:00 2001 From: Oleg Andreyev Date: Sat, 8 Dec 2018 12:58:45 +0200 Subject: [PATCH 11/65] unskipping testWindowMaximize --- tests/Selenium2Config.php | 8 -------- 1 file changed, 8 deletions(-) diff --git a/tests/Selenium2Config.php b/tests/Selenium2Config.php index a6f73f16..1be48b1e 100644 --- a/tests/Selenium2Config.php +++ b/tests/Selenium2Config.php @@ -42,14 +42,6 @@ public function skipMessage($testCase, $test) return 'WebDriver does not support setting value in color inputs. See https://code.google.com/p/selenium/issues/detail?id=7650'; } - if ( - 'Behat\Mink\Tests\Driver\Js\WindowTest' === $testCase - && (0 === strpos($test, 'testWindowMaximize')) - && 'true' === getenv('TRAVIS') - ) { - return 'Maximizing the window does not work when running the browser in Xvfb.'; - } - return parent::skipMessage($testCase, $test); } From e7ad036107c8e8ce6c56da0bd9aab7c2d07a0f75 Mon Sep 17 00:00:00 2001 From: Oleg Andreyev Date: Sun, 9 Dec 2018 18:29:47 +0200 Subject: [PATCH 12/65] updated phpunit.xml.dist with examples for FF, updated Selenium2Config to support capability from phpunit.xml, updated setRequestHeader to support user-agent --- bin/run-selenium-remote-chrome.sh | 0 bin/run-selenium-remote-firefox.sh | 0 bin/run-selenium.sh | 0 phpunit.xml.dist | 9 ++++- src/Selenium2Driver.php | 62 +++++++++++++++++++----------- tests/Selenium2Config.php | 45 +++++++++++++++++++--- 6 files changed, 86 insertions(+), 30 deletions(-) mode change 100644 => 100755 bin/run-selenium-remote-chrome.sh mode change 100644 => 100755 bin/run-selenium-remote-firefox.sh mode change 100644 => 100755 bin/run-selenium.sh diff --git a/bin/run-selenium-remote-chrome.sh b/bin/run-selenium-remote-chrome.sh old mode 100644 new mode 100755 diff --git a/bin/run-selenium-remote-firefox.sh b/bin/run-selenium-remote-firefox.sh old mode 100644 new mode 100755 diff --git a/bin/run-selenium.sh b/bin/run-selenium.sh old mode 100644 new mode 100755 diff --git a/phpunit.xml.dist b/phpunit.xml.dist index a25ab304..535cb127 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -14,8 +14,13 @@ - - + + + + + + + diff --git a/src/Selenium2Driver.php b/src/Selenium2Driver.php index 54180428..ea97465f 100755 --- a/src/Selenium2Driver.php +++ b/src/Selenium2Driver.php @@ -12,8 +12,10 @@ use Behat\Mink\Exception\DriverException; use Behat\Mink\Selector\Xpath\Escaper; +use Facebook\WebDriver\Chrome\ChromeOptions; use Facebook\WebDriver\Cookie; use Facebook\WebDriver\Exception\NoSuchElementException; +use Facebook\WebDriver\Firefox\FirefoxDriver; use Facebook\WebDriver\Interactions\WebDriverActions; use Facebook\WebDriver\Remote\DesiredCapabilities; use Facebook\WebDriver\Remote\LocalFileDetector; @@ -55,7 +57,7 @@ class Selenium2Driver extends CoreDriver private $browserName; /** - * @var array + * @var DesiredCapabilities|null */ private $desiredCapabilities; @@ -77,10 +79,6 @@ class Selenium2Driver extends CoreDriver * @var string */ private $wdHost; - private $navigation; - private $action; - private $switchTo; - private $manage; /** * Instantiates the driver. @@ -93,8 +91,14 @@ public function __construct($browserName = 'firefox', $desiredCapabilities = nul { $this->wdHost = $wdHost; $this->browserName = $browserName; - $this->setDesiredCapabilities($desiredCapabilities); - $this->xpathEscaper = new Escaper(); + + if ($browserName === 'firefox') { + $this->desiredCapabilities = DesiredCapabilities::firefox(); + } else if ($browserName === 'chrome') { + $this->desiredCapabilities = DesiredCapabilities::chrome(); + } else { + $this->desiredCapabilities = new DesiredCapabilities(); + } } /** @@ -113,30 +117,23 @@ protected function setBrowserName($browserName = 'firefox') * * See http://code.google.com/p/selenium/wiki/DesiredCapabilities * - * @param array $desiredCapabilities an array of capabilities to pass on to the WebDriver server + * @param DesiredCapabilities $desiredCapabilities * * @throws DriverException */ - public function setDesiredCapabilities($desiredCapabilities = null) + public function setDesiredCapabilities(DesiredCapabilities $desiredCapabilities = null) { if ($this->started) { throw new DriverException('Unable to set desiredCapabilities, the session has already started'); } - if (null === $desiredCapabilities) { - $desiredCapabilities = array(); - } - - $desiredCapabilities = new DesiredCapabilities($desiredCapabilities); - $desiredCapabilities->setBrowserName($this->browserName); - $this->desiredCapabilities = $desiredCapabilities; } /** * Gets the desiredCapabilities * - * @return array $desiredCapabilities + * @return DesiredCapabilities */ public function getDesiredCapabilities() { @@ -166,6 +163,32 @@ public static function getDefaultCapabilities() ); } + public function setRequestHeader($name, $value) + { + $lowerName = strtolower($name); + if ($lowerName === 'user-agent') { + if ($this->webDriver) { + throw new DriverException('Unable to update desiredCapabilities, the session has already started'); + } + + $desiredCapabilities = $this->getDesiredCapabilities(); + + if ($desiredCapabilities->getBrowserName() === 'chrome') { + $options = $desiredCapabilities->getCapability(ChromeOptions::CAPABILITY); + $options['args'] = array_merge($options['args'], array('user-agent='.$value)); + $desiredCapabilities->setCapability(ChromeOptions::CAPABILITY, $options); + } else if ($desiredCapabilities->getBrowserName() === 'firefox') { + // general.useragent.override + $options = $desiredCapabilities->getCapability(FirefoxDriver::PROFILE); + $options->setPreference('general.useragent.override', $value); + } + + return; + } + + parent::setRequestHeader($name, $value); // TODO: Change the autogenerated stub + } + /** * Creates some options for key events * @@ -272,12 +295,7 @@ public function stop() $this->started = false; try { $this->webDriver->quit(); - $this->webDriver = null; - $this->navigation = null; - $this->action = null; - $this->switchTo = null; - $this->manage = null; } catch (\Exception $e) { throw new DriverException('Could not close connection', 0, $e); } diff --git a/tests/Selenium2Config.php b/tests/Selenium2Config.php index 1be48b1e..b1c9a6e8 100644 --- a/tests/Selenium2Config.php +++ b/tests/Selenium2Config.php @@ -3,6 +3,10 @@ namespace Behat\Mink\Tests\Driver; use Behat\Mink\Driver\Selenium2Driver; +use Facebook\WebDriver\Chrome\ChromeOptions; +use Facebook\WebDriver\Firefox\FirefoxDriver; +use Facebook\WebDriver\Firefox\FirefoxProfile; +use Facebook\WebDriver\Remote\DesiredCapabilities; class Selenium2Config extends AbstractConfig { @@ -20,14 +24,43 @@ public function createDriver() $driverOptions = getenv('DRIVER_OPTIONS') ? \json_decode(getenv('DRIVER_OPTIONS'), true) : array(); $seleniumHost = $_SERVER['DRIVER_URL']; - $desiredCapabilities = array( - 'browser' => $browser, - 'browserName' => $browser, - 'version' => 'ANY' + if ($browser === 'firefox') { + $desiredCapabilities = DesiredCapabilities::firefox(); + } else if ($browser === 'chrome') { + $desiredCapabilities = DesiredCapabilities::chrome(); + } else { + $desiredCapabilities = new DesiredCapabilities(); + } + + $capabilityMap = array( + 'firefox' => FirefoxDriver::PROFILE, + 'chrome' => ChromeOptions::CAPABILITY ); - $desiredCapabilities = \array_merge($desiredCapabilities, $driverOptions); - return new Selenium2Driver($browser, $desiredCapabilities, $seleniumHost); + if (isset($capabilityMap[$browser])) { + $capability = $desiredCapabilities->getCapability($capabilityMap[$browser]); + if ($capability instanceof ChromeOptions) { + $args = isset($driverOptions['args']) ? $driverOptions['args'] : array(); + $capability->addArguments($args); + //$capability->addEncodedExtension(); + //$capability->addExtension(); + //$capability->addEncodedExtensions(); + //$capability->addExtensions(); + } else if ($capability instanceof FirefoxProfile) { + $preferences = isset($driverOptions['preference']) ? $driverOptions['preference'] : array(); + foreach ($preferences as $key => $preference) { + $capability->setPreference($key, $preference); + // $capability->setRdfFile($key, $preference); + // $capability->addExtensionDatas($key, $preference); + // $capability->addExtension($key, $preference); + } + } + } + + $driver = new Selenium2Driver($browser, array(), $seleniumHost); + $driver->setDesiredCapabilities($desiredCapabilities); + + return $driver; } /** From 9dcf5051e37f003a6e232de1fa83950ec30e4f95 Mon Sep 17 00:00:00 2001 From: Oleg Andreyev Date: Sat, 15 Dec 2018 18:45:32 +0200 Subject: [PATCH 13/65] downgrade firefox to 2.53.1 --- bin/run-selenium-remote-firefox.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/run-selenium-remote-firefox.sh b/bin/run-selenium-remote-firefox.sh index 0cac878a..bc996f9a 100755 --- a/bin/run-selenium-remote-firefox.sh +++ b/bin/run-selenium-remote-firefox.sh @@ -4,4 +4,4 @@ set -e echo ' Downloading selenium' docker pull selenium/standalone-firefox:latest echo ' Running selenium' -docker run -d -p 4444:4444 --network=host selenium/standalone-firefox:latest +docker run -d -p 4444:4444 --network=host selenium/standalone-firefox:2.53.1 From d84acc3ca25b3188f002b0f704c875fe93e55d2a Mon Sep 17 00:00:00 2001 From: Oleg Andreyev Date: Mon, 31 Dec 2018 00:47:21 +0200 Subject: [PATCH 14/65] reverted "setRequestHeader", unified key actions --- bin/run-selenium-remote-chrome.sh | 2 - bin/run-selenium-remote-firefox.sh | 3 +- phpunit.xml.dist | 2 + src/Selenium2Driver.php | 118 ++++++++++------------------- 4 files changed, 45 insertions(+), 80 deletions(-) diff --git a/bin/run-selenium-remote-chrome.sh b/bin/run-selenium-remote-chrome.sh index 5d676f67..8d707d90 100755 --- a/bin/run-selenium-remote-chrome.sh +++ b/bin/run-selenium-remote-chrome.sh @@ -1,7 +1,5 @@ #!/usr/bin/env sh set -e -echo ' Downloading selenium' -docker pull selenium/standalone-chrome:latest echo ' Running selenium' docker run -d -p 4444:4444 --network=host selenium/standalone-chrome:latest diff --git a/bin/run-selenium-remote-firefox.sh b/bin/run-selenium-remote-firefox.sh index bc996f9a..452b0d88 100755 --- a/bin/run-selenium-remote-firefox.sh +++ b/bin/run-selenium-remote-firefox.sh @@ -1,7 +1,6 @@ #!/usr/bin/env sh set -e -echo ' Downloading selenium' -docker pull selenium/standalone-firefox:latest echo ' Running selenium' +# do not use newer version, firefox is following w3c much faster then drivers docker run -d -p 4444:4444 --network=host selenium/standalone-firefox:2.53.1 diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 535cb127..a6313318 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -14,7 +14,9 @@ + + diff --git a/src/Selenium2Driver.php b/src/Selenium2Driver.php index ea97465f..0966879a 100755 --- a/src/Selenium2Driver.php +++ b/src/Selenium2Driver.php @@ -163,32 +163,6 @@ public static function getDefaultCapabilities() ); } - public function setRequestHeader($name, $value) - { - $lowerName = strtolower($name); - if ($lowerName === 'user-agent') { - if ($this->webDriver) { - throw new DriverException('Unable to update desiredCapabilities, the session has already started'); - } - - $desiredCapabilities = $this->getDesiredCapabilities(); - - if ($desiredCapabilities->getBrowserName() === 'chrome') { - $options = $desiredCapabilities->getCapability(ChromeOptions::CAPABILITY); - $options['args'] = array_merge($options['args'], array('user-agent='.$value)); - $desiredCapabilities->setCapability(ChromeOptions::CAPABILITY, $options); - } else if ($desiredCapabilities->getBrowserName() === 'firefox') { - // general.useragent.override - $options = $desiredCapabilities->getCapability(FirefoxDriver::PROFILE); - $options->setPreference('general.useragent.override', $value); - } - - return; - } - - parent::setRequestHeader($name, $value); // TODO: Change the autogenerated stub - } - /** * Creates some options for key events * @@ -759,7 +733,11 @@ public function mouseOver($xpath) public function focus($xpath) { $element = $this->findElement($xpath); - $this->webDriver->action()->moveToElement($element)->click($element)->perform(); + $action = $this->webDriver->action(); + + $action->moveToElement($element); + $action->click($element); + $action->perform(); } /** @@ -768,7 +746,11 @@ public function focus($xpath) public function blur($xpath) { $element = $this->findElement($xpath); - $this->webDriver->action()->moveToElement($element)->sendKeys($element, WebDriverKeys::TAB)->perform(); + $action = $this->webDriver->action(); + + $action->moveToElement($element); + $action->sendKeys($element, WebDriverKeys::TAB); + $action->perform(); } /** @@ -776,22 +758,7 @@ public function blur($xpath) */ public function keyPress($xpath, $char, $modifier = null) { - // @see https://w3c.github.io/uievents/#event-type-keypress - $element = $this->findElement($xpath); - $char = $this->decodeChar($char); - $action = $this->webDriver->action(); - - $keyboard = $this->webDriver->getKeyboard(); - $this->clickOnElement($element); - if ($modifier) { - $keyboard->pressKey($this->keyModifier($modifier)); - } - $keyboard->sendKeys($char); - if ($modifier) { - $keyboard->releaseKey($this->keyModifier($modifier)); - } - - $action->perform(); + $this->sendKey($xpath, $char, $modifier); } /** @@ -799,22 +766,7 @@ public function keyPress($xpath, $char, $modifier = null) */ public function keyDown($xpath, $char, $modifier = null) { - // @see https://w3c.github.io/uievents/#event-type-keydown - $element = $this->findElement($xpath); - $char = $this->decodeChar($char); - $action = $this->webDriver->action(); - - $keyboard = $this->webDriver->getKeyboard(); - $this->clickOnElement($element); - if ($modifier) { - $keyboard->pressKey($this->keyModifier($modifier)); - } - $keyboard->sendKeys($char); - if ($modifier) { - $keyboard->releaseKey($this->keyModifier($modifier)); - } - - $action->perform(); + $this->sendKey($xpath, $char, $modifier); } /** @@ -822,21 +774,7 @@ public function keyDown($xpath, $char, $modifier = null) */ public function keyUp($xpath, $char, $modifier = null) { - $element = $this->findElement($xpath); - $char = $this->decodeChar($char); - $action = $this->webDriver->action(); - - $keyboard = $this->webDriver->getKeyboard(); - $this->clickOnElement($element); - if ($modifier) { - $keyboard->pressKey($this->keyModifier($modifier)); - } - $keyboard->sendKeys($char); - if ($modifier) { - $keyboard->releaseKey($this->keyModifier($modifier)); - } - - $action->perform(); + $this->sendKey($xpath, $char, $modifier); } /** @@ -846,7 +784,10 @@ public function dragTo($sourceXpath, $destinationXpath) { $source = $this->findElement($sourceXpath); $destination = $this->findElement($destinationXpath); - $this->webDriver->action()->dragAndDrop($source, $destination)->perform(); + $action = $this->webDriver->action(); + + $action->dragAndDrop($source, $destination); + $action->perform(); } /** @@ -1025,4 +966,29 @@ private function decodeChar($char) return $char; } + + /** + * @param $xpath + * @param $char + * @param $modifier + */ + private function sendKey($xpath, $char, $modifier) + { + // @see https://w3c.github.io/uievents/#event-type-keydown + $element = $this->findElement($xpath); + $char = $this->decodeChar($char); + $action = $this->webDriver->action(); + + if ($modifier) { + $action->keyDown($element, $this->keyModifier($modifier)); + } + + $action->sendKeys($element, $char); + + if ($modifier) { + $action->keyUp($element, $this->keyModifier($modifier)); + } + + $action->perform(); + } } From 77c961fd7e9d9f9f62babe3add87eed5e04077bd Mon Sep 17 00:00:00 2001 From: Oleg Andreyev Date: Fri, 4 Jan 2019 11:25:29 +0200 Subject: [PATCH 15/65] reverted tests/Custom --- tests/Custom/DesiredCapabilitiesTest.php | 52 ++++++++++++++++++++++++ tests/Custom/TimeoutTest.php | 41 +++++++++++++++++++ tests/Custom/WebDriverTest.php | 21 ++++++++++ tests/Custom/WindowNameTest.php | 22 ++++++++++ 4 files changed, 136 insertions(+) create mode 100644 tests/Custom/DesiredCapabilitiesTest.php create mode 100644 tests/Custom/TimeoutTest.php create mode 100644 tests/Custom/WebDriverTest.php create mode 100644 tests/Custom/WindowNameTest.php diff --git a/tests/Custom/DesiredCapabilitiesTest.php b/tests/Custom/DesiredCapabilitiesTest.php new file mode 100644 index 00000000..ae613fc9 --- /dev/null +++ b/tests/Custom/DesiredCapabilitiesTest.php @@ -0,0 +1,52 @@ + 'firefox', + 'version' => '30', + 'platform' => 'ANY', + 'browserVersion' => '30', + 'browser' => 'firefox', + 'name' => 'Selenium2 Mink Driver Test', + 'deviceOrientation' => 'portrait', + 'deviceType' => 'tablet', + 'selenium-version' => '2.45.0' + ); + + $driver = new Selenium2Driver('firefox', $caps); + $this->assertNotEmpty($driver->getDesiredCapabilities(), 'desiredCapabilities empty'); + $this->assertInternalType('array', $driver->getDesiredCapabilities()); + $this->assertEquals($caps, $driver->getDesiredCapabilities()); + } + + /** + * @expectedException \Behat\Mink\Exception\DriverException + * @expectedExceptionMessage Unable to set desiredCapabilities, the session has already started + */ + public function testSetDesiredCapabilities() + { + $caps = array( + 'browserName' => 'firefox', + 'version' => '30', + 'platform' => 'ANY', + 'browserVersion' => '30', + 'browser' => 'firefox', + 'name' => 'Selenium2 Mink Driver Test', + 'deviceOrientation' => 'portrait', + 'deviceType' => 'tablet', + 'selenium-version' => '2.45.0' + ); + $session = $this->getSession(); + $session->start(); + $driver = $session->getDriver(); + $driver->setDesiredCapabilities($caps); + } +} diff --git a/tests/Custom/TimeoutTest.php b/tests/Custom/TimeoutTest.php new file mode 100644 index 00000000..0c0dd451 --- /dev/null +++ b/tests/Custom/TimeoutTest.php @@ -0,0 +1,41 @@ +getSession()->start(); + + $this->getSession()->getDriver()->setTimeouts(array('invalid' => 0)); + } + + public function testShortTimeoutDoesNotWaitForElementToAppear() + { + $this->getSession()->getDriver()->setTimeouts(array('implicit' => 0)); + + $this->getSession()->visit($this->pathTo('/js_test.html')); + $this->findById('waitable')->click(); + + $element = $this->getSession()->getPage()->find('css', '#waitable > div'); + + $this->assertNull($element); + } + + public function testLongTimeoutWaitsForElementToAppear() + { + $this->getSession()->getDriver()->setTimeouts(array('implicit' => 5000)); + + $this->getSession()->visit($this->pathTo('/js_test.html')); + $this->findById('waitable')->click(); + $element = $this->getSession()->getPage()->find('css', '#waitable > div'); + + $this->assertNotNull($element); + } +} diff --git a/tests/Custom/WebDriverTest.php b/tests/Custom/WebDriverTest.php new file mode 100644 index 00000000..a6f71e6d --- /dev/null +++ b/tests/Custom/WebDriverTest.php @@ -0,0 +1,21 @@ +getSession(); + $session->start(); + /** @var Selenium2Driver $driver */ + $driver = $session->getDriver(); + $this->assertNotEmpty($driver->getWebDriverSessionId(), 'Started session has an ID'); + + $driver = new Selenium2Driver(); + $this->assertNull($driver->getWebDriverSessionId(), 'Not started session don\'t have an ID'); + } +} diff --git a/tests/Custom/WindowNameTest.php b/tests/Custom/WindowNameTest.php new file mode 100644 index 00000000..e75aa469 --- /dev/null +++ b/tests/Custom/WindowNameTest.php @@ -0,0 +1,22 @@ +getSession(); + $session->start(); + + $windowNames = $session->getWindowNames(); + $this->assertArrayHasKey(0, $windowNames); + + $windowName = $session->getWindowName(); + + $this->assertInternalType('string', $windowName); + $this->assertContains($windowName, $windowNames, 'The current window name is one of the available window names.'); + } +} From 25a2d908d9f6956d48df166bf58c2fba031c63ab Mon Sep 17 00:00:00 2001 From: Oleg Andreyev Date: Fri, 4 Jan 2019 11:28:40 +0200 Subject: [PATCH 16/65] added fix to getAttribute, added fix to setValue file-upload, fixing createDriver, added composer overrides (WIP) --- composer.json | 14 ++++++++++-- phpunit.xml.dist | 6 ++--- src/Selenium2Driver.php | 48 ++++++++++++++++++++++----------------- tests/Selenium2Config.php | 12 ++++++++-- 4 files changed, 52 insertions(+), 28 deletions(-) diff --git a/composer.json b/composer.json index ae726a52..8b7f9810 100644 --- a/composer.json +++ b/composer.json @@ -24,17 +24,27 @@ "homepage": "http://everzet.com" } ], + "repositories": [ + { + "type": "vcs", + "url": "git@github.com:oleg-andreyev/driver-testsuite.git" + }, + { + "type": "vcs", + "url": "git@github.com:oleg-andreyev/php-webdriver.git" + } + ], "require": { "php": ">=5.3.1", "ext-zip": "*", "ext-curl": "*", "ext-json": "*", "behat/mink": "~1.7@dev", - "facebook/webdriver": "dev-community" + "facebook/webdriver": "dev-adjust-getRelatedElements" }, "require-dev": { "roave/security-advisories": "dev-master", - "mink/driver-testsuite": "dev-master", + "mink/driver-testsuite": "dev-w3c-testKeyboardEvents", "phpunit/phpunit": "^6.5" }, "autoload": { diff --git a/phpunit.xml.dist b/phpunit.xml.dist index a6313318..46d8ad96 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -16,10 +16,10 @@ - + - - + + diff --git a/src/Selenium2Driver.php b/src/Selenium2Driver.php index 0966879a..580abf39 100755 --- a/src/Selenium2Driver.php +++ b/src/Selenium2Driver.php @@ -464,10 +464,32 @@ public function getOuterHtml($xpath) public function getAttribute($xpath, $name) { $element = $this->findElement($xpath); - // https://w3c.github.io/webdriver/#get-element-attribute - $attribute = $element->getAttribute($name); - return $attribute ?: null; + /** + * If attribute is present but does not have value, it's considered as Boolean Attributes https://html.spec.whatwg.org/#boolean-attributes + * but here result may be unexpected in case of , my-attr should return TRUE, but it will return "empty string" + * + * @see https://w3c.github.io/webdriver/#get-element-attribute + */ + $hasAttribute = $this->hasAttribute($element, $name); + if ($hasAttribute) { + $value = $element->getAttribute($name); + } else { + $value = null; + } + + return $value; + } + + /** + * @param WebDriverElement $element + * @param string $name + * + * @return bool + */ + private function hasAttribute(WebDriverElement $element, $name) + { + return $this->executeJsOnElement($element, "return {{ELEMENT}}.hasAttribute('$name')"); } /** @@ -552,17 +574,12 @@ public function setValue($xpath, $value) } if ('file' === $elementType) { - $element->sendKeys($value); + $this->attachFile($xpath, $value); return; } - -// if ('color' === $elementType) { -// $this->executeJsOnElement($element, sprintf('{{ELEMENT}}.value = "%s";', $value)); -// return; -// } } - $value = strval($value); + $value = (string) $value; if (in_array($elementName, array('input', 'textarea'))) { $existingValueLength = strlen($element->getAttribute('value')); @@ -913,17 +930,6 @@ private function ensureInputType(WebDriverElement $element, $xpath, $type, $act } } - /** - * @param $xpath - * @param $event - * @param string $options - */ - private function trigger($xpath, $event, $options = '{}') - { - $script = 'Syn.trigger("' . $event . '", ' . $options . ', {{ELEMENT}})'; - $this->withSyn()->executeJsOnXpath($xpath, $script); - } - private function uploadFile($path) { throw new \RuntimeException('Not yet supported'); diff --git a/tests/Selenium2Config.php b/tests/Selenium2Config.php index b1c9a6e8..d3f9da85 100644 --- a/tests/Selenium2Config.php +++ b/tests/Selenium2Config.php @@ -39,14 +39,20 @@ public function createDriver() if (isset($capabilityMap[$browser])) { $capability = $desiredCapabilities->getCapability($capabilityMap[$browser]); - if ($capability instanceof ChromeOptions) { + if ($browser === 'chrome') { + if (!$capability) { + $capability = new ChromeOptions(); + } $args = isset($driverOptions['args']) ? $driverOptions['args'] : array(); $capability->addArguments($args); //$capability->addEncodedExtension(); //$capability->addExtension(); //$capability->addEncodedExtensions(); //$capability->addExtensions(); - } else if ($capability instanceof FirefoxProfile) { + } else if ($browser === 'firefox') { + if (!$capability) { + $capability = new FirefoxProfile(); + } $preferences = isset($driverOptions['preference']) ? $driverOptions['preference'] : array(); foreach ($preferences as $key => $preference) { $capability->setPreference($key, $preference); @@ -55,6 +61,8 @@ public function createDriver() // $capability->addExtension($key, $preference); } } + + $desiredCapabilities->setCapability($capabilityMap[$browser], $capability); } $driver = new Selenium2Driver($browser, array(), $seleniumHost); From 2f2ea4f2d643b2f7db84aa551bc72d4367386c88 Mon Sep 17 00:00:00 2001 From: Oleg Andreyev Date: Fri, 4 Jan 2019 14:21:16 +0200 Subject: [PATCH 17/65] removed accident phpunit from composer --- composer.json | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/composer.json b/composer.json index 8b7f9810..8fb40871 100644 --- a/composer.json +++ b/composer.json @@ -36,16 +36,12 @@ ], "require": { "php": ">=5.3.1", - "ext-zip": "*", - "ext-curl": "*", - "ext-json": "*", "behat/mink": "~1.7@dev", "facebook/webdriver": "dev-adjust-getRelatedElements" }, "require-dev": { "roave/security-advisories": "dev-master", - "mink/driver-testsuite": "dev-w3c-testKeyboardEvents", - "phpunit/phpunit": "^6.5" + "mink/driver-testsuite": "dev-w3c-testKeyboardEvents" }, "autoload": { "psr-4": { From fcbb4c4a076517b4e4777bfb4d2ea6c5a505e2b3 Mon Sep 17 00:00:00 2001 From: Oleg Andreyev Date: Fri, 4 Jan 2019 14:21:29 +0200 Subject: [PATCH 18/65] removed charToOptions --- src/Selenium2Driver.php | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/src/Selenium2Driver.php b/src/Selenium2Driver.php index 580abf39..f45d6c3b 100755 --- a/src/Selenium2Driver.php +++ b/src/Selenium2Driver.php @@ -163,33 +163,6 @@ public static function getDefaultCapabilities() ); } - /** - * Creates some options for key events - * - * @param string $char the character or code - * @param string $modifier one of 'shift', 'alt', 'ctrl' or 'meta' - * - * @return string a json encoded options array for Syn - */ - protected static function charToOptions($char, $modifier = null) - { - $ord = ord($char); - if (is_numeric($char)) { - $ord = $char; - } - - $options = array( - 'keyCode' => $ord, - 'charCode' => $ord - ); - - if ($modifier) { - $options[$modifier . 'Key'] = 1; - } - - return json_encode($options); - } - /** * Executes JS on a given element - pass in a js script string and {{ELEMENT}} will * be replaced with a reference to the result of the $xpath query From 8afc38bae3dda5aba9c2d8b26738f9a2ddac1331 Mon Sep 17 00:00:00 2001 From: Oleg Andreyev Date: Fri, 4 Jan 2019 14:23:59 +0200 Subject: [PATCH 19/65] reverted spaces in composer, removed roave/security-advisories --- composer.json | 37 +++++++++++++++---------------------- 1 file changed, 15 insertions(+), 22 deletions(-) diff --git a/composer.json b/composer.json index 8fb40871..cdc97c31 100644 --- a/composer.json +++ b/composer.json @@ -1,27 +1,21 @@ { - "name": "behat/mink-selenium2-driver", - "description": "Selenium2 (WebDriver) driver for Mink framework", - "keywords": [ - "selenium", - "webdriver", - "javascript", - "ajax", - "testing", - "browser" - ], - "homepage": "http://mink.behat.org/", - "type": "mink-driver", - "license": "MIT", + "name": "behat/mink-selenium2-driver", + "description": "Selenium2 (WebDriver) driver for Mink framework", + "keywords": ["selenium", "webdriver", "javascript", "ajax", "testing", "browser"], + "homepage": "http://mink.behat.org/", + "type": "mink-driver", + "license": "MIT", + "authors": [ { - "name": "Pete Otaqui", - "email": "pete@otaqui.com", - "homepage": "https://github.com/pete-otaqui" + "name": "Pete Otaqui", + "email": "pete@otaqui.com", + "homepage": "https://github.com/pete-otaqui" }, { - "name": "Konstantin Kudryashov", - "email": "ever.zet@gmail.com", - "homepage": "http://everzet.com" + "name": "Konstantin Kudryashov", + "email": "ever.zet@gmail.com", + "homepage": "http://everzet.com" } ], "repositories": [ @@ -35,12 +29,11 @@ } ], "require": { - "php": ">=5.3.1", - "behat/mink": "~1.7@dev", + "php": ">=5.3.1", + "behat/mink": "~1.7@dev", "facebook/webdriver": "dev-adjust-getRelatedElements" }, "require-dev": { - "roave/security-advisories": "dev-master", "mink/driver-testsuite": "dev-w3c-testKeyboardEvents" }, "autoload": { From c4f0b73fb71d38b20be8c2adecfed22bb9ef76f6 Mon Sep 17 00:00:00 2001 From: Oleg Andreyev Date: Fri, 4 Jan 2019 14:36:59 +0200 Subject: [PATCH 20/65] updated .travis.yml to match facebook/webdriver --- .travis.yml | 117 ++++++++++++++++++++++++++++++++++++-------------- composer.json | 2 +- 2 files changed, 85 insertions(+), 34 deletions(-) diff --git a/.travis.yml b/.travis.yml index f65c2a3b..2341eb7a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,50 +1,101 @@ language: php - sudo: false +dist: trusty cache: - directories: - - $HOME/.composer/cache/files + directories: + - $HOME/.composer/cache + - jar -php: [7.0, 7.1, 7.2] +php: + - 5.6 + - 7.0 + - 7.1 + - 7.2 + - 7.3 env: - global: - - WEBDRIVER=selenium + global: + - DISPLAY=:99.0 + - CHROMEDRIVER_VERSION="2.38" matrix: - fast_finish: true - include: - - php: 7.0 - env: - - WEBDRIVER=selenium-remote-firefox - - WEB_FIXTURES_BROWSER=firefox - - DRIVER_OPTIONS='{}' - sudo: required - services: - - docker - - php: 7.0 - env: - - WEBDRIVER=selenium-remote-chrome - - WEB_FIXTURES_BROWSER=chrome - - DRIVER_OPTIONS='{"chromeOptions":{"args":["no-sandbox","dbus-stub","disable-dev-shm-usage","reduce-security-for-testing","allow-insecure-localhost","enable-logging","disable-setuid-sandbox","disable-gpu","disable-web-security","disk-cache-size=1","allow-running-insecure-content","ignore-certificate-errors","ignore-urlfetcher-cert-requests"]}}' - sudo: required - services: - - docker + include: + # Add build to run tests against Firefox inside Travis environment + - php: 7.2 + env: BROWSER_NAME="firefox" + addons: + firefox: "45.8.0esr" -before_script: - - sh bin/run-"$WEBDRIVER".sh + # Add build to run tests against Chrome inside Travis environment + - php: 7.2 + env: + - BROWSER_NAME="chrome" + - CHROME_HEADLESS="1" + addons: + chrome: stable + + # Add build to run tests against Chrome with Chromedriver inside Travis environment + - php: 7.2 + env: + - BROWSER_NAME="chrome" + - CHROME_HEADLESS="1" + - CHROMEDRIVER="1" + addons: + chrome: stable + + # Add build to run tests against Chrome with Chromedriver inside Travis environment + - php: 7.2 + env: + - BROWSER_NAME="chrome" + - CHROME_HEADLESS="1" + - CHROMEDRIVER="1" + - CHROMEDRIVER_VERSION="latest" + addons: + chrome: stable - - composer install + # Build with lowest possible dependencies + - php: 7.2 + env: DEPENDENCIES="--prefer-lowest" - # Start a webserver for web fixtures. - - vendor/bin/mink-test-server > /dev/null 2>&1 & + # Chrome on Travis build with lowest possible dependencies + - php: 7.2 + env: + - BROWSER_NAME="chrome" + - CHROME_HEADLESS="1" + - DEPENDENCIES="--prefer-lowest" + addons: + chrome: stable + +install: + - travis_retry composer self-update + - travis_retry composer update --no-interaction $DEPENDENCIES + +before_script: + - if [[ "$BROWSER_NAME" = "chrome" && "$CHROMEDRIVER_VERSION" = "latest" ]]; then CHROMEDRIVER_VERSION=$(curl -sS https://chromedriver.storage.googleapis.com/LATEST_RELEASE); fi + - if [ "$BROWSER_NAME" = "chrome" ]; then mkdir chromedriver; wget -q -t 3 "https://chromedriver.storage.googleapis.com/$CHROMEDRIVER_VERSION/chromedriver_linux64.zip"; unzip chromedriver_linux64 -d chromedriver; fi + - if [ "$BROWSER_NAME" = "chrome" ]; then export CHROMEDRIVER_PATH=$PWD/chromedriver/chromedriver; fi + - sh -e /etc/init.d/xvfb start + - if [ ! -f jar/selenium-server-standalone-3.8.1.jar ]; then wget -q -t 3 -P jar https://selenium-release.storage.googleapis.com/3.8/selenium-server-standalone-3.8.1.jar; fi + - | + if [ "$CHROMEDRIVER" = "1" ] + then + chromedriver/chromedriver --port=4444 --url-base=wd/hub &> ./logs/chromedriver.log & + else + java -Dwebdriver.firefox.marionette=false -Dwebdriver.chrome.driver="$CHROMEDRIVER_PATH" -jar jar/selenium-server-standalone-3.8.1.jar -enablePassThrough false -log ./logs/selenium.log & + fi + - until $(echo | nc localhost 4444); do sleep 1; echo Waiting for Selenium server on port 4444...; done; echo "Selenium server started" + - php -S 127.0.0.1:8000 -t tests/functional/web/ &>>./logs/php-server.log & + - until $(echo | nc localhost 8000); do sleep 1; echo waiting for PHP server on port 8000...; done; echo "PHP server started" -script: phpunit -v --coverage-clover=coverage.clover +script: + - phpunit -v --coverage-clover=coverage.clover after_script: - - wget https://scrutinizer-ci.com/ocular.phar - - php ocular.phar code-coverage:upload --format=php-clover coverage.clover + - wget https://scrutinizer-ci.com/ocular.phar + - php ocular.phar code-coverage:upload --format=php-clover coverage.clover after_failure: - - cat /tmp/webdriver_output.txt + - if [ -f ./logs/selenium.log ]; then cat ./logs/selenium.log; fi + - if [ -f ./logs/chromedriver.log ]; then cat ./logs/chromedriver.log; fi + - if [ -f ./logs/php-server.log ]; then cat ./logs/php-server.log; fi diff --git a/composer.json b/composer.json index cdc97c31..47c41e36 100644 --- a/composer.json +++ b/composer.json @@ -29,7 +29,7 @@ } ], "require": { - "php": ">=5.3.1", + "php": ">=5.6", "behat/mink": "~1.7@dev", "facebook/webdriver": "dev-adjust-getRelatedElements" }, From 63fa6fc86dd2872c15b3b1d40f047f1b252d438a Mon Sep 17 00:00:00 2001 From: Oleg Andreyev Date: Fri, 4 Jan 2019 14:40:14 +0200 Subject: [PATCH 21/65] added no-api --- composer.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index 47c41e36..32a92b28 100644 --- a/composer.json +++ b/composer.json @@ -21,11 +21,13 @@ "repositories": [ { "type": "vcs", - "url": "git@github.com:oleg-andreyev/driver-testsuite.git" + "url": "git@github.com:oleg-andreyev/driver-testsuite.git", + "no-api": true }, { "type": "vcs", - "url": "git@github.com:oleg-andreyev/php-webdriver.git" + "url": "git@github.com:oleg-andreyev/php-webdriver.git", + "no-api": true } ], "require": { From 939caf70400e1317fabe5719f03acf470603457f Mon Sep 17 00:00:00 2001 From: Oleg Andreyev Date: Fri, 4 Jan 2019 14:45:02 +0200 Subject: [PATCH 22/65] added prefer-dist --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 2341eb7a..5f3d83af 100644 --- a/.travis.yml +++ b/.travis.yml @@ -69,7 +69,7 @@ matrix: install: - travis_retry composer self-update - - travis_retry composer update --no-interaction $DEPENDENCIES + - travis_retry composer update --prefer-dist --no-interaction $DEPENDENCIES before_script: - if [[ "$BROWSER_NAME" = "chrome" && "$CHROMEDRIVER_VERSION" = "latest" ]]; then CHROMEDRIVER_VERSION=$(curl -sS https://chromedriver.storage.googleapis.com/LATEST_RELEASE); fi From 4fc6729ccf54e92f563d8c5a3e734f2c6c01c7ad Mon Sep 17 00:00:00 2001 From: Oleg Andreyev Date: Fri, 4 Jan 2019 14:47:47 +0200 Subject: [PATCH 23/65] fix --- composer.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index 32a92b28..65e95ab3 100644 --- a/composer.json +++ b/composer.json @@ -21,12 +21,12 @@ "repositories": [ { "type": "vcs", - "url": "git@github.com:oleg-andreyev/driver-testsuite.git", + "url": "https://github.com/oleg-andreyev/driver-testsuite.git", "no-api": true }, { "type": "vcs", - "url": "git@github.com:oleg-andreyev/php-webdriver.git", + "url": "https://github.com/oleg-andreyev/php-webdriver.git", "no-api": true } ], From aa125421e1bacf887b88f02c86a4dfc795588840 Mon Sep 17 00:00:00 2001 From: Oleg Andreyev Date: Fri, 4 Jan 2019 14:50:07 +0200 Subject: [PATCH 24/65] removed bin/ --- bin/run-selenium-remote-chrome.sh | 5 ----- bin/run-selenium-remote-firefox.sh | 6 ------ bin/run-selenium.sh | 11 ----------- 3 files changed, 22 deletions(-) delete mode 100755 bin/run-selenium-remote-chrome.sh delete mode 100755 bin/run-selenium-remote-firefox.sh delete mode 100755 bin/run-selenium.sh diff --git a/bin/run-selenium-remote-chrome.sh b/bin/run-selenium-remote-chrome.sh deleted file mode 100755 index 8d707d90..00000000 --- a/bin/run-selenium-remote-chrome.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env sh -set -e - -echo ' Running selenium' -docker run -d -p 4444:4444 --network=host selenium/standalone-chrome:latest diff --git a/bin/run-selenium-remote-firefox.sh b/bin/run-selenium-remote-firefox.sh deleted file mode 100755 index 452b0d88..00000000 --- a/bin/run-selenium-remote-firefox.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env sh -set -e - -echo ' Running selenium' -# do not use newer version, firefox is following w3c much faster then drivers -docker run -d -p 4444:4444 --network=host selenium/standalone-firefox:2.53.1 diff --git a/bin/run-selenium.sh b/bin/run-selenium.sh deleted file mode 100755 index 0cacef67..00000000 --- a/bin/run-selenium.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env sh -set -e - -echo ' Starting XVFB' -sh -e /etc/init.d/xvfb start -export DISPLAY=:99.0 - -echo ' Downloading selenium' -curl -L http://selenium-release.storage.googleapis.com/2.53/selenium-server-standalone-2.53.1.jar > selenium.jar -echo ' Running selenium' -java -jar selenium.jar -log /tmp/webdriver.log > /tmp/webdriver_output.txt 2>&1 & From b01aea6b69cd63a490495e96133414aa7bc6e756 Mon Sep 17 00:00:00 2001 From: Oleg Andreyev Date: Fri, 4 Jan 2019 14:51:01 +0200 Subject: [PATCH 25/65] added logs dir --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 5f3d83af..ab0adaf6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -72,6 +72,7 @@ install: - travis_retry composer update --prefer-dist --no-interaction $DEPENDENCIES before_script: + - mkdir ./logs - if [[ "$BROWSER_NAME" = "chrome" && "$CHROMEDRIVER_VERSION" = "latest" ]]; then CHROMEDRIVER_VERSION=$(curl -sS https://chromedriver.storage.googleapis.com/LATEST_RELEASE); fi - if [ "$BROWSER_NAME" = "chrome" ]; then mkdir chromedriver; wget -q -t 3 "https://chromedriver.storage.googleapis.com/$CHROMEDRIVER_VERSION/chromedriver_linux64.zip"; unzip chromedriver_linux64 -d chromedriver; fi - if [ "$BROWSER_NAME" = "chrome" ]; then export CHROMEDRIVER_PATH=$PWD/chromedriver/chromedriver; fi From ba7fad434c26034e33f40c03553979724f9d685f Mon Sep 17 00:00:00 2001 From: Oleg Andreyev Date: Thu, 3 Jan 2019 03:55:35 +0200 Subject: [PATCH 26/65] fixing travis build - added docker for php >= 5.4 (trusty) - added php 7.3 - updated README - added DISPLAY env and xvfb for 5.3 (precise) --- .travis.yml | 63 +++++++++++++++++++++++++------------- README.md | 23 ++++++++++++++ bin/run-selenium-remote.sh | 7 ----- bin/run-selenium.sh | 11 ------- 4 files changed, 64 insertions(+), 40 deletions(-) delete mode 100644 bin/run-selenium-remote.sh delete mode 100644 bin/run-selenium.sh diff --git a/.travis.yml b/.travis.yml index 94e46a73..ef6763ef 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,43 +1,62 @@ language: php - sudo: false +dist: trusty -cache: - directories: - - $HOME/.composer/cache/files +services: + - docker -php: [5.4, 5.5, 5.6, 7.0, 7.1, 7.2] +php: + - 5.4 + - 5.5 + - 5.6 + - 7.0 + - 7.1 + - 7.2 + - 7.3 env: global: - - WEBDRIVER=selenium + - DISPLAY=:99.0 + +cache: + directories: + - $HOME/.composer/cache/files matrix: fast_finish: true include: - - php: 7.0 - env: WEBDRIVER=selenium-remote - sudo: required - services: - - docker - php: 5.3 dist: precise - # Force using PHP 5.6 for the test server as PHP 5.3 does not have the builtin webserver - env: MINK_PHP_BIN=~/.phpenv/versions/5.6/bin/php - -before_script: - - sh bin/run-"$WEBDRIVER".sh - - - composer install - # Start a webserver for web fixtures. - - vendor/bin/mink-test-server > /dev/null 2>&1 & +install: + - travis_retry composer install --no-interaction -script: phpunit -v --coverage-clover=coverage.clover +before_script: + - mkdir ./logs + - | + if [[ ${TRAVIS_PHP_VERSION:0:3} == "5.3" ]]; then + wget http://selenium-release.storage.googleapis.com/2.53/selenium-server-standalone-2.53.1.jar + sh -e /etc/init.d/xvfb start + java -jar selenium-server-standalone-2.53.1.jar &> ./logs/selenium.log & + else + docker run -p 4444:4444 --network=host -v /dev/shm:/dev/shm --shm-size 2g selenium/standalone-firefox:2.53.1 &> ./logs/selenium.log & + fi; + - until $(echo | nc localhost 4444); do sleep 1; echo Waiting for Selenium server on port 4444...; done; echo "Selenium server started" + # https://bugs.php.net/bug.php?id=71443 + - | + if [[ ${TRAVIS_PHP_VERSION:0:3} == "5.3" ]]; then + export MINK_PHP_BIN=~/.phpenv/versions/5.4/bin/php + fi; + USE_ZEND_ALLOC=0 travis_retry ${MINK_PHP_BIN-php} -d memory_limit=8G -S localhost:8002 -t ./vendor/mink/driver-testsuite/web-fixtures &> ./logs/mink-test-server.log & + - until $(echo | nc localhost 8002); do sleep 1; echo waiting for PHP server on port 8002...; done; echo "PHP server started" + +script: + - phpunit -v --coverage-clover=coverage.clover after_script: - wget https://scrutinizer-ci.com/ocular.phar - php ocular.phar code-coverage:upload --format=php-clover coverage.clover after_failure: - - cat /tmp/webdriver_output.txt + - cat ./logs/selenium.log + - cat ./logs/mink-test-server.log \ No newline at end of file diff --git a/README.md b/README.md index 01a9e3ce..61315cdd 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,29 @@ $> curl -sS http://getcomposer.org/installer | php $> php composer.phar install ``` +Testing +------------ + +1. Start WebDriver + 1. If you have Docker installed, run + ```bash + docker run -p 4444:4444 selenium/standalone-firefox:2.53.1 + ``` + 2. If you do not have Docker, but you have Java + ```bash + curl -L http://selenium-release.storage.googleapis.com/2.53/selenium-server-standalone-2.53.1.jar > selenium-server-standalone-2.53.1.jar + java -jar selenium-server-standalone-2.53.1.jar + ``` +2. Start WebServer by running + ``` bash + php -S localhost:8002 -t ./vendor/mink/driver-testsuite/web-fixtures + ``` +3. Start PhpUnit + ```bash + composer require --dev phpunit/phpunit + ./vendor/bin/phpunit -v --coverage-clover=coverage.clover + ``` + Copyright --------- diff --git a/bin/run-selenium-remote.sh b/bin/run-selenium-remote.sh deleted file mode 100644 index 9d2205d1..00000000 --- a/bin/run-selenium-remote.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env sh -set -e - -echo ' Downloading selenium' -docker pull selenium/standalone-firefox:2.53.1 -echo ' Running selenium' -docker run -d -p 4444:4444 --network=host selenium/standalone-firefox:2.53.1 diff --git a/bin/run-selenium.sh b/bin/run-selenium.sh deleted file mode 100644 index e846d3ae..00000000 --- a/bin/run-selenium.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env sh -set -e - -echo ' Starting XVFB' -sh -e /etc/init.d/xvfb start -export DISPLAY=:99.0 - -echo ' Downloading selenium' -curl -L http://selenium-release.storage.googleapis.com/2.52/selenium-server-standalone-2.52.0.jar > selenium.jar -echo ' Running selenium' -java -jar selenium.jar -log /tmp/webdriver.log > /tmp/webdriver_output.txt 2>&1 & From bea3c599c5a562e65de154ad680ccacf5b00ab34 Mon Sep 17 00:00:00 2001 From: Oleg Andreyev Date: Sat, 5 Jan 2019 18:31:58 +0200 Subject: [PATCH 27/65] adapted .travis.yml from facebook/php-webdriver --- .travis.yml | 50 +++++++++++++++++++++++++++++++------------------- 1 file changed, 31 insertions(+), 19 deletions(-) diff --git a/.travis.yml b/.travis.yml index ef6763ef..0cfe4026 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,8 +6,6 @@ services: - docker php: - - 5.4 - - 5.5 - 5.6 - 7.0 - 7.1 @@ -17,37 +15,51 @@ php: env: global: - DISPLAY=:99.0 + - BROWSER_NAME="htmlunit" + - CHROMEDRIVER_VERSION="2.38" cache: directories: - $HOME/.composer/cache/files + - jar matrix: - fast_finish: true include: - - php: 5.3 - dist: precise + # Add build to run tests against Firefox inside Travis environment + - php: 7.2 + env: BROWSER_NAME="firefox" + addons: + firefox: "45.8.0esr" + + # Add build to run tests against Chrome inside Travis environment + - php: 7.2 + env: BROWSER_NAME="chrome" CHROME_HEADLESS="1" + addons: + chrome: stable + + # Build with lowest possible dependencies + - php: 7.2 + env: DEPENDENCIES="--prefer-lowest" + + # Chrome on Travis build with lowest possible dependencies + - php: 7.2 + env: BROWSER_NAME="chrome" CHROME_HEADLESS="1" DEPENDENCIES="--prefer-lowest" + addons: + chrome: stable install: - - travis_retry composer install --no-interaction + - travis_retry composer update --no-interaction $DEPENDENCIES before_script: - mkdir ./logs - - | - if [[ ${TRAVIS_PHP_VERSION:0:3} == "5.3" ]]; then - wget http://selenium-release.storage.googleapis.com/2.53/selenium-server-standalone-2.53.1.jar - sh -e /etc/init.d/xvfb start - java -jar selenium-server-standalone-2.53.1.jar &> ./logs/selenium.log & - else - docker run -p 4444:4444 --network=host -v /dev/shm:/dev/shm --shm-size 2g selenium/standalone-firefox:2.53.1 &> ./logs/selenium.log & - fi; + - if [ "$BROWSER_NAME" = "chrome" ]; then mkdir chromedriver; wget -q -t 3 https://chromedriver.storage.googleapis.com/$CHROMEDRIVER_VERSION/chromedriver_linux64.zip; unzip chromedriver_linux64 -d chromedriver; fi + - if [ "$BROWSER_NAME" = "chrome" ]; then export CHROMEDRIVER_PATH=$PWD/chromedriver/chromedriver; fi + - sh -e /etc/init.d/xvfb start + - if [ ! -f jar/selenium-server-standalone-3.8.1.jar ]; then wget -q -t 3 -P jar https://selenium-release.storage.googleapis.com/3.8/selenium-server-standalone-3.8.1.jar; fi + - java -Dwebdriver.firefox.marionette=false -Dwebdriver.chrome.driver="$CHROMEDRIVER_PATH" -jar jar/selenium-server-standalone-3.8.1.jar -enablePassThrough false -log ./logs/selenium.log & - until $(echo | nc localhost 4444); do sleep 1; echo Waiting for Selenium server on port 4444...; done; echo "Selenium server started" # https://bugs.php.net/bug.php?id=71443 - - | - if [[ ${TRAVIS_PHP_VERSION:0:3} == "5.3" ]]; then - export MINK_PHP_BIN=~/.phpenv/versions/5.4/bin/php - fi; - USE_ZEND_ALLOC=0 travis_retry ${MINK_PHP_BIN-php} -d memory_limit=8G -S localhost:8002 -t ./vendor/mink/driver-testsuite/web-fixtures &> ./logs/mink-test-server.log & + - USE_ZEND_ALLOC=0 travis_retry php -d memory_limit=8G -S localhost:8002 -t ./vendor/mink/driver-testsuite/web-fixtures &> ./logs/mink-test-server.log & - until $(echo | nc localhost 8002); do sleep 1; echo waiting for PHP server on port 8002...; done; echo "PHP server started" script: From d705afccf61eedf653f0add24785f03167d69de4 Mon Sep 17 00:00:00 2001 From: Oleg Andreyev Date: Sat, 5 Jan 2019 19:57:19 +0200 Subject: [PATCH 28/65] returned driver specific timeouts, setTimeouts/applyTimeouts --- src/Selenium2Driver.php | 75 +++++++++++++++++++++++++++++++++-------- 1 file changed, 61 insertions(+), 14 deletions(-) diff --git a/src/Selenium2Driver.php b/src/Selenium2Driver.php index f45d6c3b..c3f68369 100755 --- a/src/Selenium2Driver.php +++ b/src/Selenium2Driver.php @@ -11,22 +11,17 @@ namespace Behat\Mink\Driver; use Behat\Mink\Exception\DriverException; -use Behat\Mink\Selector\Xpath\Escaper; -use Facebook\WebDriver\Chrome\ChromeOptions; use Facebook\WebDriver\Cookie; use Facebook\WebDriver\Exception\NoSuchElementException; -use Facebook\WebDriver\Firefox\FirefoxDriver; -use Facebook\WebDriver\Interactions\WebDriverActions; +use Facebook\WebDriver\Exception\ScriptTimeoutException; +use Facebook\WebDriver\Exception\TimeOutException; use Facebook\WebDriver\Remote\DesiredCapabilities; use Facebook\WebDriver\Remote\LocalFileDetector; -use Facebook\WebDriver\Remote\RemoteTargetLocator; use Facebook\WebDriver\Remote\RemoteWebDriver; use Facebook\WebDriver\WebDriverBy; use Facebook\WebDriver\WebDriverDimension; use Facebook\WebDriver\WebDriverElement; use Facebook\WebDriver\WebDriverKeys; -use Facebook\WebDriver\WebDriverNavigation; -use Facebook\WebDriver\WebDriverOptions; use Facebook\WebDriver\WebDriverRadios; use Facebook\WebDriver\WebDriverSelect; @@ -68,11 +63,6 @@ class Selenium2Driver extends CoreDriver */ private $timeouts = array(); - /** - * @var Escaper - */ - private $xpathEscaper; - /** * Wd host * @@ -101,6 +91,38 @@ public function __construct($browserName = 'firefox', $desiredCapabilities = nul } } + /** + * Sets the timeouts to apply to the webdriver session + * + * @param array $timeouts The session timeout settings: Array of {script, implicit, page} => time in milliseconds + */ + public function setTimeouts($timeouts) + { + $this->timeouts = $timeouts; + + if ($this->isStarted()) { + $this->applyTimeouts(); + } + } + + /** + * Applies timeouts to the current session + */ + private function applyTimeouts() + { + // @see https://w3c.github.io/webdriver/#set-timeouts + $timeouts = $this->webDriver->manage()->timeouts(); + if (isset($this->timeouts['implicit'])) { + $timeouts->implicitlyWait($this->timeouts['implicit']); + } else if (isset($this->timeouts['pageLoad'])) { + $timeouts->pageLoadTimeout($this->timeouts['pageLoad']); + } else if (isset($this->timeouts['script'])) { + $timeouts->setScriptTimeout($this->timeouts['script']); + } else { + throw new DriverException('Invalid timeout option'); + } + } + /** * Sets the browser name * @@ -211,6 +233,9 @@ public function start() { try { $this->webDriver = RemoteWebDriver::create($this->wdHost, $this->desiredCapabilities); + if (\count($this->timeouts)) { + $this->applyTimeouts(); + } } catch (\Exception $e) { throw new DriverException('Could not open connection: ' . $e->getMessage(), 0, $e); } @@ -261,7 +286,11 @@ public function reset() */ public function visit($url) { - $this->webDriver->navigate()->to($url); + try { + $this->webDriver->navigate()->to($url); + } catch (TimeOutException $e) { + throw new DriverException($e->getMessage(), $e->getCode(), $e); + } } /** @@ -277,7 +306,11 @@ public function getCurrentUrl() */ public function reload() { - $this->webDriver->navigate()->refresh(); + try { + $this->webDriver->navigate()->refresh(); + } catch (TimeOutException $e) { + throw new DriverException($e->getMessage(), $e->getCode(), $e); + } } /** @@ -793,6 +826,20 @@ public function executeScript($script) $this->webDriver->executeScript($script); } + public function executeAsyncScript($script) + { + if (preg_match('/^function[\s\(]/', $script)) { + $script = preg_replace('/;$/', '', $script); + $script = '(' . $script . ')'; + } + + try { + $this->webDriver->executeAsyncScript($script); + } catch (ScriptTimeoutException $e) { + throw new DriverException($e->getMessage(), $e->getCode(), $e); + } + } + /** * {@inheritdoc} */ From 2d4a965730f489c75bd2d92250317842c490f968 Mon Sep 17 00:00:00 2001 From: Oleg Andreyev Date: Sat, 5 Jan 2019 20:22:33 +0200 Subject: [PATCH 29/65] fixing setDesiredCapabilities and getWebDriverSessionId, added more tests for TimeoutTest --- src/Selenium2Driver.php | 20 ++++++- tests/Custom/DesiredCapabilitiesTest.php | 7 ++- tests/Custom/TimeoutTest.php | 70 +++++++++++++++++++++--- 3 files changed, 84 insertions(+), 13 deletions(-) diff --git a/src/Selenium2Driver.php b/src/Selenium2Driver.php index c3f68369..985e0dfa 100755 --- a/src/Selenium2Driver.php +++ b/src/Selenium2Driver.php @@ -89,6 +89,12 @@ public function __construct($browserName = 'firefox', $desiredCapabilities = nul } else { $this->desiredCapabilities = new DesiredCapabilities(); } + + if ($desiredCapabilities) { + foreach ($desiredCapabilities as $key => $val) { + $this->desiredCapabilities->setCapability($key, $val); + } + } } /** @@ -139,12 +145,18 @@ protected function setBrowserName($browserName = 'firefox') * * See http://code.google.com/p/selenium/wiki/DesiredCapabilities * - * @param DesiredCapabilities $desiredCapabilities + * @param DesiredCapabilities|array|null $desiredCapabilities * * @throws DriverException */ - public function setDesiredCapabilities(DesiredCapabilities $desiredCapabilities = null) + public function setDesiredCapabilities($desiredCapabilities = null) { + if (is_array($desiredCapabilities)) { + $desiredCapabilities = new DesiredCapabilities($desiredCapabilities); + } else if ($desiredCapabilities === null) { + $desiredCapabilities = new DesiredCapabilities(); + } + if ($this->started) { throw new DriverException('Unable to set desiredCapabilities, the session has already started'); } @@ -918,6 +930,10 @@ public function maximizeWindow($name = null) */ public function getWebDriverSessionId() { + if (!$this->started) { + return null; + } + return $this->webDriver->getSessionID(); } diff --git a/tests/Custom/DesiredCapabilitiesTest.php b/tests/Custom/DesiredCapabilitiesTest.php index ae613fc9..f4609b00 100644 --- a/tests/Custom/DesiredCapabilitiesTest.php +++ b/tests/Custom/DesiredCapabilitiesTest.php @@ -4,6 +4,7 @@ use Behat\Mink\Driver\Selenium2Driver; use Behat\Mink\Tests\Driver\TestCase; +use Facebook\WebDriver\Remote\DesiredCapabilities; class DesiredCapabilitiesTest extends TestCase { @@ -23,8 +24,8 @@ public function testGetDesiredCapabilities() $driver = new Selenium2Driver('firefox', $caps); $this->assertNotEmpty($driver->getDesiredCapabilities(), 'desiredCapabilities empty'); - $this->assertInternalType('array', $driver->getDesiredCapabilities()); - $this->assertEquals($caps, $driver->getDesiredCapabilities()); + $this->assertInstanceOf(DesiredCapabilities::class, $driver->getDesiredCapabilities()); + $this->assertArraySubset($caps, $driver->getDesiredCapabilities()->toArray()); } /** @@ -46,6 +47,8 @@ public function testSetDesiredCapabilities() ); $session = $this->getSession(); $session->start(); + + /** @var Selenium2Driver $driver */ $driver = $session->getDriver(); $driver->setDesiredCapabilities($caps); } diff --git a/tests/Custom/TimeoutTest.php b/tests/Custom/TimeoutTest.php index 0c0dd451..2ab763c6 100644 --- a/tests/Custom/TimeoutTest.php +++ b/tests/Custom/TimeoutTest.php @@ -2,40 +2,92 @@ namespace Behat\Mink\Tests\Driver\Custom; +use Behat\Mink\Driver\Selenium2Driver; use Behat\Mink\Tests\Driver\TestCase; class TimeoutTest extends TestCase { + /** @var \Behat\Mink\Session */ + private $session; + + /** @var Selenium2Driver */ + private $driver; + + protected function setUp() + { + parent::setUp(); + $this->session = $this->getSession(); + $this->driver = $this->session->getDriver(); + } + /** * @expectedException \Behat\Mink\Exception\DriverException */ public function testInvalidTimeoutSettingThrowsException() { - $this->getSession()->start(); - - $this->getSession()->getDriver()->setTimeouts(array('invalid' => 0)); + $this->session->start(); + $this->driver->setTimeouts(array('invalid' => 0)); } public function testShortTimeoutDoesNotWaitForElementToAppear() { - $this->getSession()->getDriver()->setTimeouts(array('implicit' => 0)); + $this->driver->setTimeouts(array('implicit' => 0)); - $this->getSession()->visit($this->pathTo('/js_test.html')); + $this->session->visit($this->pathTo('/js_test.html')); $this->findById('waitable')->click(); - $element = $this->getSession()->getPage()->find('css', '#waitable > div'); + $element = $this->session->getPage()->find('css', '#waitable > div'); $this->assertNull($element); } public function testLongTimeoutWaitsForElementToAppear() { - $this->getSession()->getDriver()->setTimeouts(array('implicit' => 5000)); + $this->driver->setTimeouts(array('implicit' => 5000)); - $this->getSession()->visit($this->pathTo('/js_test.html')); + $this->session->visit($this->pathTo('/js_test.html')); $this->findById('waitable')->click(); - $element = $this->getSession()->getPage()->find('css', '#waitable > div'); + $element = $this->session->getPage()->find('css', '#waitable > div'); $this->assertNotNull($element); } + + /** + * @expectedException \Behat\Mink\Exception\DriverException + */ + public function testPageLoadTimeout() + { + $this->driver->setTimeouts(array('pageLoad' => 1)); + $this->session->visit($this->pathTo('/page_load.php?sleep=2')); + } + + /** + * @expectedException \Behat\Mink\Exception\DriverException + */ + public function testPageReloadTimeout() + { + $this->session->visit($this->pathTo('/page_load.php?sleep=2')); + $this->driver->setTimeouts(array('pageLoad' => 1)); + $this->session->reload(); + } + + /** + * @expectedException \Behat\Mink\Exception\DriverException + */ + public function testScriptTimeout() + { + $this->driver->setTimeouts(array('script' => 1)); + $this->session->visit($this->pathTo('/js_test.html')); + + // @see https://w3c.github.io/webdriver/#execute-async-script + $this->driver->executeAsyncScript( + 'var callback = arguments[arguments.length - 1]; + setTimeout( + function(){ + callback(); + }, + 2000 + );' + ); + } } From f584e6f87ba958f077bda5c817912ad60bc1374b Mon Sep 17 00:00:00 2001 From: Oleg Andreyev Date: Sat, 5 Jan 2019 21:05:58 +0200 Subject: [PATCH 30/65] improving wait --- src/Selenium2Driver.php | 41 ++++++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/src/Selenium2Driver.php b/src/Selenium2Driver.php index 985e0dfa..ae600283 100755 --- a/src/Selenium2Driver.php +++ b/src/Selenium2Driver.php @@ -18,9 +18,11 @@ use Facebook\WebDriver\Remote\DesiredCapabilities; use Facebook\WebDriver\Remote\LocalFileDetector; use Facebook\WebDriver\Remote\RemoteWebDriver; +use Facebook\WebDriver\Remote\RemoteWebElement; use Facebook\WebDriver\WebDriverBy; use Facebook\WebDriver\WebDriverDimension; use Facebook\WebDriver\WebDriverElement; +use Facebook\WebDriver\WebDriverExpectedCondition; use Facebook\WebDriver\WebDriverKeys; use Facebook\WebDriver\WebDriverRadios; use Facebook\WebDriver\WebDriverSelect; @@ -735,13 +737,12 @@ public function rightClick($xpath) */ public function attachFile($xpath, $path) { + /** @var RemoteWebElement $element */ $element = $this->findElement($xpath); $this->ensureInputType($element, $xpath, 'file', 'attach a file on'); $element->setFileDetector(new LocalFileDetector()); - $remotePath = $element->sendKeys($path); - - return $remotePath; + return $element->sendKeys($path); } /** @@ -865,28 +866,30 @@ public function evaluateScript($script) } /** - * {@inheritdoc} + * Waits some time or until JS condition turns true. + * + * @param int $timeout timeout in milliseconds + * @param WebDriverExpectedCondition|\Closure|string $condition JS condition / Closure + * + * @return bool */ public function wait($timeout, $condition) { - $script = "return $condition;"; $seconds = $timeout / 1000.0; + $wait = $this->webDriver->wait($seconds); - $wait = $this->webDriver->wait($seconds, 100); - - return (bool) $wait->until(function (RemoteWebDriver $driver) use ($script) { - $result = $driver->executeScript($script); - // stringify result - if ($result === true) { - $result = 'true'; - } else if ($result === false) { - $result = 'false'; - } else if ($result === null) { - $result = 'null'; - } + if (is_string($condition)) { + $script = "return $condition;"; + $condition = function (RemoteWebDriver $driver) use ($script) { + return $driver->executeScript($script); + }; + } - return (string) $result; - }); + try { + return (bool) $wait->until($condition); + } catch (TimeOutException $e) { + return false; + } } /** From a58f29a13329b7feee579afb85679ad41d2414ab Mon Sep 17 00:00:00 2001 From: Oleg Andreyev Date: Sat, 5 Jan 2019 22:55:51 +0200 Subject: [PATCH 31/65] improving Selenium2Config --- tests/Selenium2Config.php | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/tests/Selenium2Config.php b/tests/Selenium2Config.php index d3f9da85..e06b5674 100644 --- a/tests/Selenium2Config.php +++ b/tests/Selenium2Config.php @@ -10,6 +10,11 @@ class Selenium2Config extends AbstractConfig { + /** + * @var Selenium2Driver + */ + private $driver; + public static function getInstance() { return new self(); @@ -68,7 +73,7 @@ public function createDriver() $driver = new Selenium2Driver($browser, array(), $seleniumHost); $driver->setDesiredCapabilities($desiredCapabilities); - return $driver; + return $this->driver = $driver; } /** @@ -83,6 +88,33 @@ public function skipMessage($testCase, $test) return 'WebDriver does not support setting value in color inputs. See https://code.google.com/p/selenium/issues/detail?id=7650'; } + $desiredCapabilities = $this->driver->getDesiredCapabilities(); + $chromeOptions = $desiredCapabilities->getCapability(ChromeOptions::CAPABILITY); + + /** @var ChromeOptions $capability */ + $headless = $desiredCapabilities->getBrowserName() === 'chrome' + && $chromeOptions instanceof ChromeOptions + && in_array('headless', $chromeOptions->toArray()['args'], true); + + if ( + 'Behat\Mink\Tests\Driver\Js\WindowTest' === $testCase + && (0 === strpos($test, 'testWindowMaximize')) + && ('true' === getenv('TRAVIS') || $headless) + ) { + return 'Maximizing the window does not work when running the browser in Xvfb/Headless.'; + } + + if ( + PHP_OS === 'Darwin' + && 'Behat\Mink\Tests\Driver\Js\EventsTest' === $testCase + && 0 === strpos($test, 'testKeyboardEvents') + ) { + // https://bugs.chromium.org/p/chromium/issues/detail?id=13891#c16 + // Control + will not trigger keypress + // Option + will output different results "special char" © + return 'MacOS does not behave same as Windows or Linux'; + } + return parent::skipMessage($testCase, $test); } From 97c2c0bcd296d70b6f6171bf56e3b9e39a1bee94 Mon Sep 17 00:00:00 2001 From: Oleg Andreyev Date: Sat, 5 Jan 2019 23:31:53 +0200 Subject: [PATCH 32/65] updated build config --- .travis.yml | 34 ++++++++------------ phpunit.xml.dist | 8 +++-- tests/Selenium2Config.php | 66 +++++++++++++++++++++++++++------------ 3 files changed, 64 insertions(+), 44 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0cfe4026..30042ec0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,6 +5,9 @@ dist: trusty services: - docker +addons: + chrome: stable + php: - 5.6 - 7.0 @@ -15,8 +18,10 @@ php: env: global: - DISPLAY=:99.0 - - BROWSER_NAME="htmlunit" + - BROWSER_NAME="chrome" + - DRIVER_OPTIONS="{'args':["headless", "no-sandbox","window-size=1024,768"]}" - CHROMEDRIVER_VERSION="2.38" + - START_XVFB=0 cache: directories: @@ -25,27 +30,14 @@ cache: matrix: include: - # Add build to run tests against Firefox inside Travis environment - - php: 7.2 - env: BROWSER_NAME="firefox" - addons: - firefox: "45.8.0esr" - - # Add build to run tests against Chrome inside Travis environment + # XVFB with regular Chrome (not headless) - php: 7.2 - env: BROWSER_NAME="chrome" CHROME_HEADLESS="1" - addons: - chrome: stable - - # Build with lowest possible dependencies - - php: 7.2 - env: DEPENDENCIES="--prefer-lowest" - - # Chrome on Travis build with lowest possible dependencies + env: + - DRIVER_OPTIONS="{'args':["no-sandbox","window-size=1024,768"]}" + - START_XVFB=1 - php: 7.2 - env: BROWSER_NAME="chrome" CHROME_HEADLESS="1" DEPENDENCIES="--prefer-lowest" - addons: - chrome: stable + env: + - DEPENDENCIES="--prefer-lowest" install: - travis_retry composer update --no-interaction $DEPENDENCIES @@ -54,7 +46,7 @@ before_script: - mkdir ./logs - if [ "$BROWSER_NAME" = "chrome" ]; then mkdir chromedriver; wget -q -t 3 https://chromedriver.storage.googleapis.com/$CHROMEDRIVER_VERSION/chromedriver_linux64.zip; unzip chromedriver_linux64 -d chromedriver; fi - if [ "$BROWSER_NAME" = "chrome" ]; then export CHROMEDRIVER_PATH=$PWD/chromedriver/chromedriver; fi - - sh -e /etc/init.d/xvfb start + - if [ "$START_XVFB" = 1]; then sh -e /etc/init.d/xvfb start; fi; - if [ ! -f jar/selenium-server-standalone-3.8.1.jar ]; then wget -q -t 3 -P jar https://selenium-release.storage.googleapis.com/3.8/selenium-server-standalone-3.8.1.jar; fi - java -Dwebdriver.firefox.marionette=false -Dwebdriver.chrome.driver="$CHROMEDRIVER_PATH" -jar jar/selenium-server-standalone-3.8.1.jar -enablePassThrough false -log ./logs/selenium.log & - until $(echo | nc localhost 4444); do sleep 1; echo Waiting for Selenium server on port 4444...; done; echo "Selenium server started" diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 46d8ad96..a9bb7081 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -18,10 +18,12 @@ - - + - + + + + diff --git a/tests/Selenium2Config.php b/tests/Selenium2Config.php index e06b5674..340a6590 100644 --- a/tests/Selenium2Config.php +++ b/tests/Selenium2Config.php @@ -25,7 +25,7 @@ public static function getInstance() */ public function createDriver() { - $browser = getenv('WEB_FIXTURES_BROWSER') ?: 'firefox'; + $browser = getenv('BROWSER_NAME') ?: 'firefox'; $driverOptions = getenv('DRIVER_OPTIONS') ? \json_decode(getenv('DRIVER_OPTIONS'), true) : array(); $seleniumHost = $_SERVER['DRIVER_URL']; @@ -45,26 +45,9 @@ public function createDriver() if (isset($capabilityMap[$browser])) { $capability = $desiredCapabilities->getCapability($capabilityMap[$browser]); if ($browser === 'chrome') { - if (!$capability) { - $capability = new ChromeOptions(); - } - $args = isset($driverOptions['args']) ? $driverOptions['args'] : array(); - $capability->addArguments($args); - //$capability->addEncodedExtension(); - //$capability->addExtension(); - //$capability->addEncodedExtensions(); - //$capability->addExtensions(); + $capability = $this->buildChromeOptions($capability, $driverOptions); } else if ($browser === 'firefox') { - if (!$capability) { - $capability = new FirefoxProfile(); - } - $preferences = isset($driverOptions['preference']) ? $driverOptions['preference'] : array(); - foreach ($preferences as $key => $preference) { - $capability->setPreference($key, $preference); - // $capability->setRdfFile($key, $preference); - // $capability->addExtensionDatas($key, $preference); - // $capability->addExtension($key, $preference); - } + $capability = $this->buildFirefoxProfile($capability, $driverOptions); } $desiredCapabilities->setCapability($capabilityMap[$browser], $capability); @@ -125,4 +108,47 @@ protected function supportsCss() { return true; } + + /** + * @param ChromeOptions|null $capability + * @param array $driverOptions + * + * @return ChromeOptions + */ + private function buildChromeOptions($capability, array $driverOptions) + { + if (!$capability) { + $capability = new ChromeOptions(); + } + $args = isset($driverOptions['args']) ? $driverOptions['args'] : []; + $capability->addArguments($args); + return $capability; + + //$capability->addEncodedExtension(); + //$capability->addExtension(); + //$capability->addEncodedExtensions(); + //$capability->addExtensions(); + } + + /** + * @param FirefoxProfile|null $capability + * @param array $driverOptions + * + * @return FirefoxProfile + */ + private function buildFirefoxProfile($capability, array $driverOptions) + { + if (!$capability) { + $capability = new FirefoxProfile(); + } + + $preferences = isset($driverOptions['preference']) ? $driverOptions['preference'] : []; + foreach ($preferences as $key => $preference) { + $capability->setPreference($key, $preference); + // $capability->setRdfFile($key, $preference); + // $capability->addExtensionDatas($key, $preference); + // $capability->addExtension($key, $preference); + } + return $capability; + } } From 643c0cc5a8b094891c829c2ce02fdb429183baca Mon Sep 17 00:00:00 2001 From: Oleg Andreyev Date: Sat, 5 Jan 2019 23:38:52 +0200 Subject: [PATCH 33/65] fixing build --- .travis.yml | 4 ++-- phpunit.xml.dist | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 30042ec0..37d5c81d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,7 +19,7 @@ env: global: - DISPLAY=:99.0 - BROWSER_NAME="chrome" - - DRIVER_OPTIONS="{'args':["headless", "no-sandbox","window-size=1024,768"]}" + - DRIVER_OPTIONS="{'args':['headless', 'no-sandbox','window-size=1024,768']}" - CHROMEDRIVER_VERSION="2.38" - START_XVFB=0 @@ -33,7 +33,7 @@ matrix: # XVFB with regular Chrome (not headless) - php: 7.2 env: - - DRIVER_OPTIONS="{'args':["no-sandbox","window-size=1024,768"]}" + - DRIVER_OPTIONS="{'args':['no-sandbox','window-size=1024,768']}" - START_XVFB=1 - php: 7.2 env: diff --git a/phpunit.xml.dist b/phpunit.xml.dist index a9bb7081..c762d8c3 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -21,7 +21,7 @@ - + From c2bb5865380d90c76184f7b6f0e6bc39154ee2c1 Mon Sep 17 00:00:00 2001 From: Oleg Andreyev Date: Sat, 5 Jan 2019 23:44:21 +0200 Subject: [PATCH 34/65] fixing build --- .travis.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 37d5c81d..0a970be3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,9 +19,9 @@ env: global: - DISPLAY=:99.0 - BROWSER_NAME="chrome" - - DRIVER_OPTIONS="{'args':['headless', 'no-sandbox','window-size=1024,768']}" + - DRIVER_OPTIONS='{"args":["headless", "no-sandbox","window-size=1024,768"]}' - CHROMEDRIVER_VERSION="2.38" - - START_XVFB=0 + - START_XVFB="0" cache: directories: @@ -33,7 +33,7 @@ matrix: # XVFB with regular Chrome (not headless) - php: 7.2 env: - - DRIVER_OPTIONS="{'args':['no-sandbox','window-size=1024,768']}" + - DRIVER_OPTIONS='{"args":["no-sandbox","window-size=1024,768"]}' - START_XVFB=1 - php: 7.2 env: @@ -46,7 +46,7 @@ before_script: - mkdir ./logs - if [ "$BROWSER_NAME" = "chrome" ]; then mkdir chromedriver; wget -q -t 3 https://chromedriver.storage.googleapis.com/$CHROMEDRIVER_VERSION/chromedriver_linux64.zip; unzip chromedriver_linux64 -d chromedriver; fi - if [ "$BROWSER_NAME" = "chrome" ]; then export CHROMEDRIVER_PATH=$PWD/chromedriver/chromedriver; fi - - if [ "$START_XVFB" = 1]; then sh -e /etc/init.d/xvfb start; fi; + - if [ "$START_XVFB" = "1" ]; then sh -e /etc/init.d/xvfb start; fi; - if [ ! -f jar/selenium-server-standalone-3.8.1.jar ]; then wget -q -t 3 -P jar https://selenium-release.storage.googleapis.com/3.8/selenium-server-standalone-3.8.1.jar; fi - java -Dwebdriver.firefox.marionette=false -Dwebdriver.chrome.driver="$CHROMEDRIVER_PATH" -jar jar/selenium-server-standalone-3.8.1.jar -enablePassThrough false -log ./logs/selenium.log & - until $(echo | nc localhost 4444); do sleep 1; echo Waiting for Selenium server on port 4444...; done; echo "Selenium server started" From a5fb84eb2d2194b92febdf02165be61baeef2b44 Mon Sep 17 00:00:00 2001 From: Oleg Andreyev Date: Sun, 6 Jan 2019 00:09:34 +0200 Subject: [PATCH 35/65] update build --- .travis.yml | 2 +- composer.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0a970be3..09533e8a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -48,7 +48,7 @@ before_script: - if [ "$BROWSER_NAME" = "chrome" ]; then export CHROMEDRIVER_PATH=$PWD/chromedriver/chromedriver; fi - if [ "$START_XVFB" = "1" ]; then sh -e /etc/init.d/xvfb start; fi; - if [ ! -f jar/selenium-server-standalone-3.8.1.jar ]; then wget -q -t 3 -P jar https://selenium-release.storage.googleapis.com/3.8/selenium-server-standalone-3.8.1.jar; fi - - java -Dwebdriver.firefox.marionette=false -Dwebdriver.chrome.driver="$CHROMEDRIVER_PATH" -jar jar/selenium-server-standalone-3.8.1.jar -enablePassThrough false -log ./logs/selenium.log & + - java -Dwebdriver.firefox.marionette=false -Dwebdriver.chrome.driver="$CHROMEDRIVER_PATH" -jar jar/selenium-server-standalone-3.8.1.jar -enablePassThrough false &> ./logs/selenium.log & - until $(echo | nc localhost 4444); do sleep 1; echo Waiting for Selenium server on port 4444...; done; echo "Selenium server started" # https://bugs.php.net/bug.php?id=71443 - USE_ZEND_ALLOC=0 travis_retry php -d memory_limit=8G -S localhost:8002 -t ./vendor/mink/driver-testsuite/web-fixtures &> ./logs/mink-test-server.log & diff --git a/composer.json b/composer.json index 65e95ab3..7b1c6abb 100644 --- a/composer.json +++ b/composer.json @@ -36,7 +36,7 @@ "facebook/webdriver": "dev-adjust-getRelatedElements" }, "require-dev": { - "mink/driver-testsuite": "dev-w3c-testKeyboardEvents" + "mink/driver-testsuite": "dev-integration-branch" }, "autoload": { "psr-4": { From d785496dd4c0774e9e13d89c605b62dc7479f2cb Mon Sep 17 00:00:00 2001 From: Oleg Andreyev Date: Sun, 6 Jan 2019 00:22:36 +0200 Subject: [PATCH 36/65] removed selenium jar --- .travis.yml | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 09533e8a..d37883de 100644 --- a/.travis.yml +++ b/.travis.yml @@ -35,21 +35,25 @@ matrix: env: - DRIVER_OPTIONS='{"args":["no-sandbox","window-size=1024,768"]}' - START_XVFB=1 + # build with lowest deps - php: 7.2 env: - DEPENDENCIES="--prefer-lowest" + # build with latest chromedriver + - php: 7.2 + env: + - CHROMEDRIVER_VERSION="latest" install: - travis_retry composer update --no-interaction $DEPENDENCIES before_script: - mkdir ./logs + - if [[ "$BROWSER_NAME" = "chrome" && "$CHROMEDRIVER_VERSION" = "latest" ]]; then CHROMEDRIVER_VERSION=`curl -sS chromedriver.storage.googleapis.com/LATEST_RELEASE`; fi - if [ "$BROWSER_NAME" = "chrome" ]; then mkdir chromedriver; wget -q -t 3 https://chromedriver.storage.googleapis.com/$CHROMEDRIVER_VERSION/chromedriver_linux64.zip; unzip chromedriver_linux64 -d chromedriver; fi - - if [ "$BROWSER_NAME" = "chrome" ]; then export CHROMEDRIVER_PATH=$PWD/chromedriver/chromedriver; fi - if [ "$START_XVFB" = "1" ]; then sh -e /etc/init.d/xvfb start; fi; - - if [ ! -f jar/selenium-server-standalone-3.8.1.jar ]; then wget -q -t 3 -P jar https://selenium-release.storage.googleapis.com/3.8/selenium-server-standalone-3.8.1.jar; fi - - java -Dwebdriver.firefox.marionette=false -Dwebdriver.chrome.driver="$CHROMEDRIVER_PATH" -jar jar/selenium-server-standalone-3.8.1.jar -enablePassThrough false &> ./logs/selenium.log & - - until $(echo | nc localhost 4444); do sleep 1; echo Waiting for Selenium server on port 4444...; done; echo "Selenium server started" + - ./chromedriver/chromedriver --port=4444 --url-base=wd/hub --verbose &> ./logs/chromedriver.log & + - until $(echo | nc localhost 4444); do sleep 1; echo Waiting for ChromeDriver on port 4444...; done; echo "ChromeDriver started" # https://bugs.php.net/bug.php?id=71443 - USE_ZEND_ALLOC=0 travis_retry php -d memory_limit=8G -S localhost:8002 -t ./vendor/mink/driver-testsuite/web-fixtures &> ./logs/mink-test-server.log & - until $(echo | nc localhost 8002); do sleep 1; echo waiting for PHP server on port 8002...; done; echo "PHP server started" @@ -62,5 +66,5 @@ after_script: - php ocular.phar code-coverage:upload --format=php-clover coverage.clover after_failure: - - cat ./logs/selenium.log + - cat ./logs/chromedriver.log - cat ./logs/mink-test-server.log \ No newline at end of file From b54d351f4876ca0aa755be450ebab0c11e3aa360 Mon Sep 17 00:00:00 2001 From: Oleg Andreyev Date: Tue, 8 Jan 2019 21:51:01 +0200 Subject: [PATCH 37/65] reusing mink-test-server instead of direct php build-in server, updated README --- .travis.yml | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index d37883de..fb4dc026 100644 --- a/.travis.yml +++ b/.travis.yml @@ -55,7 +55,7 @@ before_script: - ./chromedriver/chromedriver --port=4444 --url-base=wd/hub --verbose &> ./logs/chromedriver.log & - until $(echo | nc localhost 4444); do sleep 1; echo Waiting for ChromeDriver on port 4444...; done; echo "ChromeDriver started" # https://bugs.php.net/bug.php?id=71443 - - USE_ZEND_ALLOC=0 travis_retry php -d memory_limit=8G -S localhost:8002 -t ./vendor/mink/driver-testsuite/web-fixtures &> ./logs/mink-test-server.log & + - USE_ZEND_ALLOC=0 ./vendor/bin/mink-test-server &> ./logs/mink-test-server.log & - until $(echo | nc localhost 8002); do sleep 1; echo waiting for PHP server on port 8002...; done; echo "PHP server started" script: diff --git a/README.md b/README.md index 61315cdd..de46cb8e 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,7 @@ Testing ``` 2. Start WebServer by running ``` bash - php -S localhost:8002 -t ./vendor/mink/driver-testsuite/web-fixtures + ./vendor/bin/mink-test-server ``` 3. Start PhpUnit ```bash From 1180e1c608eb8f9f2589f7486ec171bea1a6a80c Mon Sep 17 00:00:00 2001 From: Oleg Andreyev Date: Tue, 8 Jan 2019 21:55:14 +0200 Subject: [PATCH 38/65] removed USE_ZEND_ALLOC, added travis_retry --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index fb4dc026..7d2ff01d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -55,7 +55,7 @@ before_script: - ./chromedriver/chromedriver --port=4444 --url-base=wd/hub --verbose &> ./logs/chromedriver.log & - until $(echo | nc localhost 4444); do sleep 1; echo Waiting for ChromeDriver on port 4444...; done; echo "ChromeDriver started" # https://bugs.php.net/bug.php?id=71443 - - USE_ZEND_ALLOC=0 ./vendor/bin/mink-test-server &> ./logs/mink-test-server.log & + - travis_retry ./vendor/bin/mink-test-server &> ./logs/mink-test-server.log & - until $(echo | nc localhost 8002); do sleep 1; echo waiting for PHP server on port 8002...; done; echo "PHP server started" script: From 839e9c80047b3e4f72bb36fb51441653e9b6d1ab Mon Sep 17 00:00:00 2001 From: Oleg Andreyev Date: Tue, 8 Jan 2019 21:57:43 +0200 Subject: [PATCH 39/65] renamed DEPENDENCIES to COMPOSER_FLAGS --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7d2ff01d..ea38d849 100644 --- a/.travis.yml +++ b/.travis.yml @@ -38,14 +38,14 @@ matrix: # build with lowest deps - php: 7.2 env: - - DEPENDENCIES="--prefer-lowest" + - COMPOSER_FLAGS="--prefer-lowest" # build with latest chromedriver - php: 7.2 env: - CHROMEDRIVER_VERSION="latest" install: - - travis_retry composer update --no-interaction $DEPENDENCIES + - travis_retry composer update --no-interaction $COMPOSER_FLAGS before_script: - mkdir ./logs From 1db167c7badf4c4dce4d40d225ca1fed71a58dc8 Mon Sep 17 00:00:00 2001 From: Oleg Andreyev Date: Tue, 8 Jan 2019 22:00:07 +0200 Subject: [PATCH 40/65] bumped dev-master alias to 2.0.x-dev --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 7b1c6abb..1c44b4cf 100644 --- a/composer.json +++ b/composer.json @@ -50,7 +50,7 @@ }, "extra": { "branch-alias": { - "dev-master": "1.3.x-dev" + "dev-master": "2.0.x-dev" } } } From 8a96a7faceb475322cc78ab32e3269bb22129547 Mon Sep 17 00:00:00 2001 From: Oleg Andreyev Date: Tue, 8 Jan 2019 22:03:17 +0200 Subject: [PATCH 41/65] commented WEB_FIXTURES_HOST in phpunit.xml.dist and added comment --- phpunit.xml.dist | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/phpunit.xml.dist b/phpunit.xml.dist index c762d8c3..a97185f2 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -16,7 +16,8 @@ - + + From fb7bf5c49c3dcd8f0d8f4f98e7a7a96a53394965 Mon Sep 17 00:00:00 2001 From: Oleg Andreyev Date: Tue, 8 Jan 2019 22:05:26 +0200 Subject: [PATCH 42/65] removed typehint from Selenium2Config --- tests/Selenium2Config.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/Selenium2Config.php b/tests/Selenium2Config.php index 340a6590..4fcfade1 100644 --- a/tests/Selenium2Config.php +++ b/tests/Selenium2Config.php @@ -74,7 +74,6 @@ public function skipMessage($testCase, $test) $desiredCapabilities = $this->driver->getDesiredCapabilities(); $chromeOptions = $desiredCapabilities->getCapability(ChromeOptions::CAPABILITY); - /** @var ChromeOptions $capability */ $headless = $desiredCapabilities->getBrowserName() === 'chrome' && $chromeOptions instanceof ChromeOptions && in_array('headless', $chromeOptions->toArray()['args'], true); From e79fc7a66dd12341da9fbaf6f4bd03dcea8f13b9 Mon Sep 17 00:00:00 2001 From: Oleg Andreyev Date: Tue, 8 Jan 2019 22:23:28 +0200 Subject: [PATCH 43/65] added test case for 7.0 with SELENIUM_DRIVER --- .travis.yml | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index ea38d849..f1aee3b3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,6 +22,7 @@ env: - DRIVER_OPTIONS='{"args":["headless", "no-sandbox","window-size=1024,768"]}' - CHROMEDRIVER_VERSION="2.38" - START_XVFB="0" + - SELENIUM_DRIVER="" cache: directories: @@ -30,6 +31,9 @@ cache: matrix: include: + - php: 7.0 + env: + - SELENIUM_DRIVER="2.53.1" # XVFB with regular Chrome (not headless) - php: 7.2 env: @@ -52,9 +56,13 @@ before_script: - if [[ "$BROWSER_NAME" = "chrome" && "$CHROMEDRIVER_VERSION" = "latest" ]]; then CHROMEDRIVER_VERSION=`curl -sS chromedriver.storage.googleapis.com/LATEST_RELEASE`; fi - if [ "$BROWSER_NAME" = "chrome" ]; then mkdir chromedriver; wget -q -t 3 https://chromedriver.storage.googleapis.com/$CHROMEDRIVER_VERSION/chromedriver_linux64.zip; unzip chromedriver_linux64 -d chromedriver; fi - if [ "$START_XVFB" = "1" ]; then sh -e /etc/init.d/xvfb start; fi; - - ./chromedriver/chromedriver --port=4444 --url-base=wd/hub --verbose &> ./logs/chromedriver.log & + - | + if [ ! -z "$SELENIUM_DRIVER" ]; then + docker run -p 4444:4444 "selenium/standalone-firefox:$SELENIUM_DRIVER" &> ./logs/selenium.log &; + else + ./chromedriver/chromedriver --port=4444 --url-base=wd/hub --verbose &> ./logs/chromedriver.log &; + fi; - until $(echo | nc localhost 4444); do sleep 1; echo Waiting for ChromeDriver on port 4444...; done; echo "ChromeDriver started" - # https://bugs.php.net/bug.php?id=71443 - travis_retry ./vendor/bin/mink-test-server &> ./logs/mink-test-server.log & - until $(echo | nc localhost 8002); do sleep 1; echo waiting for PHP server on port 8002...; done; echo "PHP server started" @@ -66,5 +74,6 @@ after_script: - php ocular.phar code-coverage:upload --format=php-clover coverage.clover after_failure: - - cat ./logs/chromedriver.log - - cat ./logs/mink-test-server.log \ No newline at end of file + - if [ -f ./logs/chromedriver.log ]; then cat ./logs/chromedriver.log; fi + - if [ -f ./logs/selenium.log ]; then cat ./logs/selenium.log; fi + - if [ -f ./logs/mink-test-server.log ]; then cat ./logs/mink-test-server.log; fi \ No newline at end of file From 7d68a0778f49328f11691d0bd50cc062638aabf1 Mon Sep 17 00:00:00 2001 From: Oleg Andreyev Date: Tue, 8 Jan 2019 22:33:39 +0200 Subject: [PATCH 44/65] added BROWSER_NAME for SELENIUM_DRIVER, added --network=host --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index f1aee3b3..0683f8a2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -34,6 +34,7 @@ matrix: - php: 7.0 env: - SELENIUM_DRIVER="2.53.1" + - BROWSER_NAME="firefox" # XVFB with regular Chrome (not headless) - php: 7.2 env: @@ -58,7 +59,7 @@ before_script: - if [ "$START_XVFB" = "1" ]; then sh -e /etc/init.d/xvfb start; fi; - | if [ ! -z "$SELENIUM_DRIVER" ]; then - docker run -p 4444:4444 "selenium/standalone-firefox:$SELENIUM_DRIVER" &> ./logs/selenium.log &; + docker run -p 4444:4444 --network=host "selenium/standalone-firefox:$SELENIUM_DRIVER" &> ./logs/selenium.log &; else ./chromedriver/chromedriver --port=4444 --url-base=wd/hub --verbose &> ./logs/chromedriver.log &; fi; From 77f0d9895a5a5da234a83776925520ee57c4146b Mon Sep 17 00:00:00 2001 From: Oleg Andreyev Date: Tue, 8 Jan 2019 22:38:41 +0200 Subject: [PATCH 45/65] added typehint to setTimeouts --- src/Selenium2Driver.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Selenium2Driver.php b/src/Selenium2Driver.php index ae600283..72dc1b3e 100755 --- a/src/Selenium2Driver.php +++ b/src/Selenium2Driver.php @@ -104,7 +104,7 @@ public function __construct($browserName = 'firefox', $desiredCapabilities = nul * * @param array $timeouts The session timeout settings: Array of {script, implicit, page} => time in milliseconds */ - public function setTimeouts($timeouts) + public function setTimeouts(array $timeouts) { $this->timeouts = $timeouts; From 14ca3c62cad7a157c84452518825114fbc4b3941 Mon Sep 17 00:00:00 2001 From: Oleg Andreyev Date: Tue, 8 Jan 2019 22:42:25 +0200 Subject: [PATCH 46/65] moved "if started" check up --- src/Selenium2Driver.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Selenium2Driver.php b/src/Selenium2Driver.php index 72dc1b3e..f4c9a690 100755 --- a/src/Selenium2Driver.php +++ b/src/Selenium2Driver.php @@ -153,16 +153,16 @@ protected function setBrowserName($browserName = 'firefox') */ public function setDesiredCapabilities($desiredCapabilities = null) { + if ($this->started) { + throw new DriverException('Unable to set desiredCapabilities, the session has already started'); + } + if (is_array($desiredCapabilities)) { $desiredCapabilities = new DesiredCapabilities($desiredCapabilities); } else if ($desiredCapabilities === null) { $desiredCapabilities = new DesiredCapabilities(); } - if ($this->started) { - throw new DriverException('Unable to set desiredCapabilities, the session has already started'); - } - $this->desiredCapabilities = $desiredCapabilities; } From 390326d78671c7234edc93af88e721155975f21f Mon Sep 17 00:00:00 2001 From: Oleg Andreyev Date: Tue, 8 Jan 2019 22:42:37 +0200 Subject: [PATCH 47/65] removed "setWebDriver" --- src/Selenium2Driver.php | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/Selenium2Driver.php b/src/Selenium2Driver.php index f4c9a690..45d354f3 100755 --- a/src/Selenium2Driver.php +++ b/src/Selenium2Driver.php @@ -176,16 +176,6 @@ public function getDesiredCapabilities() return $this->desiredCapabilities; } - /** - * Sets the WebDriver instance - * - * @param RemoteWebDriver $webDriver An instance of the WebDriver class - */ - public function setWebDriver(RemoteWebDriver $webDriver) - { - $this->webDriver = $webDriver; - } - /** * Returns the default capabilities * From 127434faca6e0a923837795ac10cc106ad13f90b Mon Sep 17 00:00:00 2001 From: Oleg Andreyev Date: Tue, 8 Jan 2019 22:46:18 +0200 Subject: [PATCH 48/65] added "getWebDriver" to bypass Mink API --- src/Selenium2Driver.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/Selenium2Driver.php b/src/Selenium2Driver.php index 45d354f3..76840a5c 100755 --- a/src/Selenium2Driver.php +++ b/src/Selenium2Driver.php @@ -19,6 +19,7 @@ use Facebook\WebDriver\Remote\LocalFileDetector; use Facebook\WebDriver\Remote\RemoteWebDriver; use Facebook\WebDriver\Remote\RemoteWebElement; +use Facebook\WebDriver\WebDriver; use Facebook\WebDriver\WebDriverBy; use Facebook\WebDriver\WebDriverDimension; use Facebook\WebDriver\WebDriverElement; @@ -176,6 +177,14 @@ public function getDesiredCapabilities() return $this->desiredCapabilities; } + /** + * @return WebDriver + */ + public function getWebDriver() + { + return $this->webDriver; + } + /** * Returns the default capabilities * From f0351a17e53890e3c8bee2d0a76a7ce215a77a09 Mon Sep 17 00:00:00 2001 From: Oleg Andreyev Date: Tue, 8 Jan 2019 22:47:05 +0200 Subject: [PATCH 49/65] changed visibility of executeJsOnXpath from protected to private --- src/Selenium2Driver.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Selenium2Driver.php b/src/Selenium2Driver.php index 76840a5c..965715c2 100755 --- a/src/Selenium2Driver.php +++ b/src/Selenium2Driver.php @@ -210,7 +210,7 @@ public static function getDefaultCapabilities() * * @return mixed */ - protected function executeJsOnXpath($xpath, $script, $sync = true) + private function executeJsOnXpath($xpath, $script, $sync = true) { $element = $this->findElement($xpath); return $this->executeJsOnElement($element, $script, $sync); From f6000facd993021dd8c8a425835440853d135d31 Mon Sep 17 00:00:00 2001 From: Oleg Andreyev Date: Tue, 8 Jan 2019 22:50:23 +0200 Subject: [PATCH 50/65] removed $started property in favor of isStarted, where we are checking $this->webDriver !== null; --- src/Selenium2Driver.php | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/src/Selenium2Driver.php b/src/Selenium2Driver.php index 965715c2..f416e2a9 100755 --- a/src/Selenium2Driver.php +++ b/src/Selenium2Driver.php @@ -35,13 +35,6 @@ */ class Selenium2Driver extends CoreDriver { - /** - * Whether the browser has been started - * - * @var Boolean - */ - private $started = false; - /** * The WebDriver instance * @@ -154,7 +147,7 @@ protected function setBrowserName($browserName = 'firefox') */ public function setDesiredCapabilities($desiredCapabilities = null) { - if ($this->started) { + if ($this->isStarted()) { throw new DriverException('Unable to set desiredCapabilities, the session has already started'); } @@ -256,8 +249,6 @@ public function start() if (!$this->webDriver) { throw new DriverException('Could not connect to a Selenium 2 / WebDriver server'); } - - $this->started = true; } /** @@ -265,7 +256,7 @@ public function start() */ public function isStarted() { - return $this->started; + return $this->webDriver !== null; } /** @@ -277,7 +268,6 @@ public function stop() throw new DriverException('Could not connect to a Selenium 2 / WebDriver server'); } - $this->started = false; try { $this->webDriver->quit(); $this->webDriver = null; @@ -932,7 +922,7 @@ public function maximizeWindow($name = null) */ public function getWebDriverSessionId() { - if (!$this->started) { + if (!$this->isStarted()) { return null; } From 33d7e8fad6ad514703a4707e81ed3c5df94d67a1 Mon Sep 17 00:00:00 2001 From: Oleg Andreyev Date: Tue, 8 Jan 2019 23:04:05 +0200 Subject: [PATCH 51/65] avoid override $element variable in getValue/setValue --- src/Selenium2Driver.php | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/Selenium2Driver.php b/src/Selenium2Driver.php index f416e2a9..1de76fd3 100755 --- a/src/Selenium2Driver.php +++ b/src/Selenium2Driver.php @@ -516,21 +516,21 @@ public function getValue($xpath) } if ('input' === $elementName && 'radio' === $elementType) { - $element = new WebDriverRadios($element); - return $element->getFirstSelectedOption()->getAttribute('value'); + $radios = new WebDriverRadios($element); + return $radios->getFirstSelectedOption()->getAttribute('value'); } // Using $element->attribute('value') on a select only returns the first selected option // even when it is a multiple select, so a custom retrieval is needed. if ('select' === $elementName) { - $element = new WebDriverSelect($element); - if ($element->isMultiple()) { + $select = new WebDriverSelect($element); + if ($select->isMultiple()) { return \array_map(function (WebDriverElement $element) { return $element->getAttribute('value'); - }, $element->getAllSelectedOptions()); + }, $select->getAllSelectedOptions()); } - return $element->getFirstSelectedOption()->getAttribute('value'); + return $select->getFirstSelectedOption()->getAttribute('value'); } return $element->getAttribute('value'); @@ -545,18 +545,18 @@ public function setValue($xpath, $value) $elementName = strtolower($element->getTagName()); if ('select' === $elementName) { - $element = new WebDriverSelect($element); + $select = new WebDriverSelect($element); if (is_array($value)) { - $element->deselectAll(); + $select->deselectAll(); foreach ($value as $option) { - $element->selectByValue($option); + $select->selectByValue($option); } return; } - $element->selectByValue($value); + $select->selectByValue($value); return; } @@ -577,8 +577,8 @@ public function setValue($xpath, $value) } if ('radio' === $elementType) { - $element = new WebDriverRadios($element); - $element->selectByValue($value); + $radios = new WebDriverRadios($element); + $radios->selectByValue($value); return; } From eeeb1e1f6296ab0df808f5df70adef3e25bab1f5 Mon Sep 17 00:00:00 2001 From: Oleg Andreyev Date: Tue, 8 Jan 2019 23:10:48 +0200 Subject: [PATCH 52/65] removed comment from clickOnElement --- src/Selenium2Driver.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Selenium2Driver.php b/src/Selenium2Driver.php index 1de76fd3..8728d174 100755 --- a/src/Selenium2Driver.php +++ b/src/Selenium2Driver.php @@ -699,7 +699,6 @@ public function click($xpath) private function clickOnElement(WebDriverElement $element) { - // Move the mouse to the element as Selenium does not allow clicking on an element which is outside the viewport $this->webDriver->action()->click($element)->perform(); } From 5510e51824a4129145d7bd57f9b6f6ba6d6620d6 Mon Sep 17 00:00:00 2001 From: Oleg Andreyev Date: Tue, 8 Jan 2019 23:12:53 +0200 Subject: [PATCH 53/65] removed typehint from attachFile, added correct return type to findElement --- src/Selenium2Driver.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Selenium2Driver.php b/src/Selenium2Driver.php index 8728d174..3567bc10 100755 --- a/src/Selenium2Driver.php +++ b/src/Selenium2Driver.php @@ -725,7 +725,6 @@ public function rightClick($xpath) */ public function attachFile($xpath, $path) { - /** @var RemoteWebElement $element */ $element = $this->findElement($xpath); $this->ensureInputType($element, $xpath, 'file', 'attach a file on'); @@ -931,7 +930,7 @@ public function getWebDriverSessionId() /** * @param string $xpath * - * @return WebDriverElement + * @return RemoteWebElement */ private function findElement($xpath) { From 4ba0fe550a5be3557491b02484dd21ecfae06825 Mon Sep 17 00:00:00 2001 From: Oleg Andreyev Date: Tue, 8 Jan 2019 23:20:00 +0200 Subject: [PATCH 54/65] changed exception in resizeWindow/maximizeWindow from generic Exception to UnsupportedDriverActionException --- src/Selenium2Driver.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Selenium2Driver.php b/src/Selenium2Driver.php index 3567bc10..b128495b 100755 --- a/src/Selenium2Driver.php +++ b/src/Selenium2Driver.php @@ -11,6 +11,7 @@ namespace Behat\Mink\Driver; use Behat\Mink\Exception\DriverException; +use Behat\Mink\Exception\UnsupportedDriverActionException; use Facebook\WebDriver\Cookie; use Facebook\WebDriver\Exception\NoSuchElementException; use Facebook\WebDriver\Exception\ScriptTimeoutException; @@ -886,7 +887,7 @@ public function resizeWindow($width, $height, $name = null) { $dimension = new WebDriverDimension($width, $height); if ($name) { - throw new \Exception('Named windows are not supported yet'); + throw new UnsupportedDriverActionException('Named windows are not supported yet'); } $this->webDriver->manage()->window()->setSize($dimension); @@ -907,7 +908,7 @@ public function submitForm($xpath) public function maximizeWindow($name = null) { if ($name) { - throw new \Exception('Named window is not supported'); + throw new UnsupportedDriverActionException('Named window is not supported'); } $this->webDriver->manage()->window()->maximize(); From eddbe48fe20abdee777821e926cc4b6e6d6be711 Mon Sep 17 00:00:00 2001 From: Oleg Andreyev Date: Tue, 8 Jan 2019 23:20:49 +0200 Subject: [PATCH 55/65] removed extra space in ensureInputType --- src/Selenium2Driver.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Selenium2Driver.php b/src/Selenium2Driver.php index b128495b..869649b2 100755 --- a/src/Selenium2Driver.php +++ b/src/Selenium2Driver.php @@ -948,7 +948,7 @@ private function findElement($xpath) * * @throws DriverException */ - private function ensureInputType(WebDriverElement $element, $xpath, $type, $action) + private function ensureInputType(WebDriverElement $element, $xpath, $type, $action) { if ('input' !== strtolower($element->getTagName()) || $type !== strtolower($element->getAttribute('type'))) { $message = 'Impossible to %s the element with XPath "%s" as it is not a %s input'; From 97827c08e5ee1cb8a994128f6a8740adc192bcab Mon Sep 17 00:00:00 2001 From: Oleg Andreyev Date: Tue, 8 Jan 2019 23:21:32 +0200 Subject: [PATCH 56/65] removed unused method uploadFile --- src/Selenium2Driver.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/Selenium2Driver.php b/src/Selenium2Driver.php index 869649b2..c93d0e73 100755 --- a/src/Selenium2Driver.php +++ b/src/Selenium2Driver.php @@ -957,11 +957,6 @@ private function ensureInputType(WebDriverElement $element, $xpath, $type, $acti } } - private function uploadFile($path) - { - throw new \RuntimeException('Not yet supported'); - } - /** * Prepend modifier * From d96448152de41376bb4ec19fe85d5a5bbafa8eee Mon Sep 17 00:00:00 2001 From: Oleg Andreyev Date: Tue, 8 Jan 2019 23:25:27 +0200 Subject: [PATCH 57/65] updated docblock to keyModifier/decodeChar --- src/Selenium2Driver.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Selenium2Driver.php b/src/Selenium2Driver.php index c93d0e73..86a15b78 100755 --- a/src/Selenium2Driver.php +++ b/src/Selenium2Driver.php @@ -958,7 +958,7 @@ private function ensureInputType(WebDriverElement $element, $xpath, $type, $acti } /** - * Prepend modifier + * Converts alt/ctrl/shift/meta to corresponded WebDriverKeys::* constant * * @param string $modifier * @@ -980,9 +980,9 @@ private function keyModifier($modifier) } /** - * Decode char + * Decodes char * - * @param $char + * @param int|string $char if int is passed it will be converted to char using `chr` function * * @return string */ From ae01600fd1f9139add6fee41eb8ce711102e8a82 Mon Sep 17 00:00:00 2001 From: Oleg Andreyev Date: Tue, 8 Jan 2019 23:31:15 +0200 Subject: [PATCH 58/65] updated docblock to `wait` method --- src/Selenium2Driver.php | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/Selenium2Driver.php b/src/Selenium2Driver.php index 86a15b78..98b1b743 100755 --- a/src/Selenium2Driver.php +++ b/src/Selenium2Driver.php @@ -854,12 +854,7 @@ public function evaluateScript($script) } /** - * Waits some time or until JS condition turns true. - * - * @param int $timeout timeout in milliseconds - * @param WebDriverExpectedCondition|\Closure|string $condition JS condition / Closure - * - * @return bool + * {@inheritdoc} */ public function wait($timeout, $condition) { From 12209e5b4fcb08f5f329dcb84ce818c5ecd6446d Mon Sep 17 00:00:00 2001 From: Oleg Andreyev Date: Tue, 8 Jan 2019 23:37:40 +0200 Subject: [PATCH 59/65] updated travis.yml --- .travis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0683f8a2..fa0a2407 100644 --- a/.travis.yml +++ b/.travis.yml @@ -58,10 +58,10 @@ before_script: - if [ "$BROWSER_NAME" = "chrome" ]; then mkdir chromedriver; wget -q -t 3 https://chromedriver.storage.googleapis.com/$CHROMEDRIVER_VERSION/chromedriver_linux64.zip; unzip chromedriver_linux64 -d chromedriver; fi - if [ "$START_XVFB" = "1" ]; then sh -e /etc/init.d/xvfb start; fi; - | - if [ ! -z "$SELENIUM_DRIVER" ]; then - docker run -p 4444:4444 --network=host "selenium/standalone-firefox:$SELENIUM_DRIVER" &> ./logs/selenium.log &; + if [ -z "$SELENIUM_DRIVER" ]; then + ./chromedriver/chromedriver --port=4444 --url-base=wd/hub --verbose &> ./logs/chromedriver.log & else - ./chromedriver/chromedriver --port=4444 --url-base=wd/hub --verbose &> ./logs/chromedriver.log &; + docker run --rm -p 4444:4444 "selenium/standalone-firefox:$SELENIUM_DRIVER" &> ./logs/selenium.log & fi; - until $(echo | nc localhost 4444); do sleep 1; echo Waiting for ChromeDriver on port 4444...; done; echo "ChromeDriver started" - travis_retry ./vendor/bin/mink-test-server &> ./logs/mink-test-server.log & From c00efd4b708679537492b5538b5b0ede6c9dce37 Mon Sep 17 00:00:00 2001 From: Oleg Andreyev Date: Wed, 9 Jan 2019 00:01:41 +0200 Subject: [PATCH 60/65] changed implementation of focus/blur --- src/Selenium2Driver.php | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/Selenium2Driver.php b/src/Selenium2Driver.php index 98b1b743..ea7dc0c2 100755 --- a/src/Selenium2Driver.php +++ b/src/Selenium2Driver.php @@ -759,9 +759,10 @@ public function focus($xpath) $element = $this->findElement($xpath); $action = $this->webDriver->action(); - $action->moveToElement($element); - $action->click($element); - $action->perform(); + $action->moveToElement($element)->perform(); + + // @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus + $this->executeJsOnElement($element, 'return {{ELEMENT}}.focus()'); } /** @@ -770,11 +771,9 @@ public function focus($xpath) public function blur($xpath) { $element = $this->findElement($xpath); - $action = $this->webDriver->action(); - $action->moveToElement($element); - $action->sendKeys($element, WebDriverKeys::TAB); - $action->perform(); + // @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/blur + $this->executeJsOnElement($element, 'return {{ELEMENT}}.blur()'); } /** From 741cd41ec51145ae7efc1abbf7e70067427f7cf9 Mon Sep 17 00:00:00 2001 From: Oleg Andreyev Date: Wed, 9 Jan 2019 01:07:34 +0200 Subject: [PATCH 61/65] return --network to docker --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index fa0a2407..bc0a595c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -61,7 +61,7 @@ before_script: if [ -z "$SELENIUM_DRIVER" ]; then ./chromedriver/chromedriver --port=4444 --url-base=wd/hub --verbose &> ./logs/chromedriver.log & else - docker run --rm -p 4444:4444 "selenium/standalone-firefox:$SELENIUM_DRIVER" &> ./logs/selenium.log & + docker run --rm --network=host -p 4444:4444 "selenium/standalone-firefox:$SELENIUM_DRIVER" &> ./logs/selenium.log & fi; - until $(echo | nc localhost 4444); do sleep 1; echo Waiting for ChromeDriver on port 4444...; done; echo "ChromeDriver started" - travis_retry ./vendor/bin/mink-test-server &> ./logs/mink-test-server.log & From 0959ab86faeab78c32289eef1a47b6cced6aeee0 Mon Sep 17 00:00:00 2001 From: Oleg Andreyev Date: Wed, 9 Jan 2019 01:24:16 +0200 Subject: [PATCH 62/65] update .travis.yml --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index bc0a595c..3698fc1e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -63,7 +63,7 @@ before_script: else docker run --rm --network=host -p 4444:4444 "selenium/standalone-firefox:$SELENIUM_DRIVER" &> ./logs/selenium.log & fi; - - until $(echo | nc localhost 4444); do sleep 1; echo Waiting for ChromeDriver on port 4444...; done; echo "ChromeDriver started" + - until $(echo | nc localhost 4444); do sleep 1; echo Waiting for WebDriver on port 4444...; done; echo "ChromeDriver started" - travis_retry ./vendor/bin/mink-test-server &> ./logs/mink-test-server.log & - until $(echo | nc localhost 8002); do sleep 1; echo waiting for PHP server on port 8002...; done; echo "PHP server started" From 3bba1660171c97ce2f21838f9d531d87a952f9e3 Mon Sep 17 00:00:00 2001 From: Oleg Andreyev Date: Wed, 9 Jan 2019 01:32:14 +0200 Subject: [PATCH 63/65] dummy commit (trigger build) --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index de46cb8e..fed8ace2 100644 --- a/README.md +++ b/README.md @@ -82,3 +82,4 @@ Maintainers * Christophe Coevoet [stof](https://github.com/stof) * Pete Otaqui [pete-otaqui](http://github.com/pete-otaqui) + From 35544c1a14a134f3a684d729a25edec084cacb7b Mon Sep 17 00:00:00 2001 From: Oleg Andreyev Date: Wed, 9 Jan 2019 01:53:10 +0200 Subject: [PATCH 64/65] dummy commit (trigger build) --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index fed8ace2..50bfef0e 100644 --- a/README.md +++ b/README.md @@ -81,5 +81,4 @@ Maintainers ----------- * Christophe Coevoet [stof](https://github.com/stof) -* Pete Otaqui [pete-otaqui](http://github.com/pete-otaqui) - +* Pete Otaqui [pete-otaqui](http://github.com/pete-otaqui) \ No newline at end of file From 1bc41c1c9f797ffc4ad2ae269786559cc0f9bade Mon Sep 17 00:00:00 2001 From: Oleg Andreyev Date: Thu, 10 Jan 2019 22:26:09 +0200 Subject: [PATCH 65/65] fixing build for selenium-firefox:2.53.1 --- src/Selenium2Driver.php | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/Selenium2Driver.php b/src/Selenium2Driver.php index ea7dc0c2..f7f449da 100755 --- a/src/Selenium2Driver.php +++ b/src/Selenium2Driver.php @@ -291,7 +291,14 @@ public function reset() public function visit($url) { try { - $this->webDriver->navigate()->to($url); + try { + $this->webDriver->navigate()->to($url); + } catch (ScriptTimeoutException $e) { + // selenium-firefox:2.53.1 has different exception code 'page load' and ScriptTimeoutException is thrown + if (strpos($e->getMessage(), 'Timed out waiting for page load') === 0) { + throw new TimeOutException($e->getMessage(), $e->getResults()); + } + } } catch (TimeOutException $e) { throw new DriverException($e->getMessage(), $e->getCode(), $e); } @@ -311,7 +318,14 @@ public function getCurrentUrl() public function reload() { try { - $this->webDriver->navigate()->refresh(); + try { + $this->webDriver->navigate()->refresh(); + } catch (ScriptTimeoutException $e) { + // selenium-firefox:2.53.1 has different exception code 'page load' and ScriptTimeoutException is thrown + if (strpos($e->getMessage(), 'Timed out waiting for page load') === 0) { + throw new TimeOutException($e->getMessage(), $e->getResults()); + } + } } catch (TimeOutException $e) { throw new DriverException($e->getMessage(), $e->getCode(), $e); }