{"id":14625,"date":"2016-09-08T17:19:42","date_gmt":"2016-09-08T14:19:42","guid":{"rendered":"https:\/\/www.webcodegeeks.com\/?p=14625"},"modified":"2016-09-08T17:19:42","modified_gmt":"2016-09-08T14:19:42","slug":"laravel-behat-using-selenium-headless-chrome","status":"publish","type":"post","link":"https:\/\/www.webcodegeeks.com\/web-development\/laravel-behat-using-selenium-headless-chrome\/","title":{"rendered":"Laravel and Behat Using Selenium and Headless Chrome"},"content":{"rendered":"<p>Let\u2019s take a look at using Codeship for Selenium and Headless Chrome testing, which is key for interacting with JavaScript features on your site. I also want to show you how to troubleshoot those rare moments when there\u2019s an issue on the CI but not on your local build, by using Codeship\u2019s SSH feature and <a href=\"https:\/\/saucelabs.com\/\">Sauce Lab\u2019s<\/a> remote connections. You can see <a href=\"https:\/\/github.com\/alnutile\/codeship-behat\">all the code here<\/a>.<\/p>\n<h2>Setting Up Your Local Environment<\/h2>\n<p>First, we need to set up Codeship to test our app with every git push. You can see this demonstrated previously on the blog, thanks to <a href=\"https:\/\/blog.codeship.com\/getting-started-laravel-codeship\/\">Matthew Setter\u2019s post about Laravel and Codeship<\/a>. Following that, you should have things working and <a href=\"https:\/\/phpunit.de\/\">PHPUnit<\/a> running. Now let\u2019s add <a href=\"http:\/\/docs.behat.org\/en\/v3.0\/\">Behat<\/a> to this.<\/p>\n<p>In this post, we\u2019ll have a Host (Mac) and a Guest (Linux), thanks to <a href=\"https:\/\/laravel.com\/docs\/5.3\/homestead\">Homestead<\/a>. At the end of the article, I\u2019ll list some links for Windows as well.<\/p>\n<p>Here\u2019s a look at the Host-Guest workflow.<\/p>\n<p><a href=\"http:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2016\/09\/behat_drives.png\"><img decoding=\"async\" class=\"size-full wp-image-14647 aligncenter\" src=\"http:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2016\/09\/behat_drives.png\" alt=\"behat_drives\" width=\"589\" height=\"247\" srcset=\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2016\/09\/behat_drives.png 589w, https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2016\/09\/behat_drives-300x126.png 300w\" sizes=\"(max-width: 589px) 100vw, 589px\" \/><\/a><\/p>\n<p>The first part of this setup is based on a <a href=\"https:\/\/github.com\/laracasts\/Behat-Laravel-Extension\">Laravel Behat-oriented library<\/a> started by <a href=\"https:\/\/laracasts.com\/\">Laracast\u2019s<\/a> creator Jeffrey Way. After you follow the install steps there, you will have a working version of Behat that integrates with your Laravel application in some nice ways, including migrations and transactions hooks.<\/p>\n<p>But even after that, I need to take it one step further. I need to get Selenium set up both as a server and a <a href=\"https:\/\/github.com\/Behat\/MinkExtension\">Mink Extension<\/a>. This will get you going for the Mink and Selenium driver:<\/p>\n<pre class=\" brush:php\">composer require \"behat\/mink-selenium2-driver\":\"^1.3\"<\/pre>\n<p>For the Selenium server, this will be a bit harder but not by much. Remember this is on your Host; the above was on your Guest. Basically, <a href=\"https:\/\/www.npmjs.com\/package\/selenium-standalone\">this tutorial<\/a> will walk you through an easy install of Selenium on any Host OS. For me and my Mac, I use <code>brew<\/code> to set up Node.js. From there, I follow those three steps to get going. When I\u2019m done, I have a terminal in the background just running Selenium.<\/p>\n<p><a href=\"http:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2016\/09\/teminal.png\"><img decoding=\"async\" class=\"aligncenter size-full wp-image-14648\" src=\"http:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2016\/09\/teminal.png\" alt=\"teminal\" width=\"719\" height=\"481\" srcset=\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2016\/09\/teminal.png 719w, https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2016\/09\/teminal-300x201.png 300w\" sizes=\"(max-width: 719px) 100vw, 719px\" \/><\/a><\/p>\n<h2>Your First Behat Test<\/h2>\n<p>At this point, I need to make a <code>behat.yml<\/code> in the root of my application and fill it with the following:<\/p>\n<pre class=\" brush:php\">default:\r\n  suites:\r\n    user_auth:\r\n      contexts: [ UserAuthenticationContext ]\r\n      filters: { tags: '@user_auth' }\r\n\r\n  extensions:\r\n    Laracasts\\Behat:\r\n        # env_path: .env.behat\r\n    Behat\\MinkExtension:\r\n        base_url: https:\/\/codeship-behat.dev\r\n        default_session: laravel\r\n        laravel: ~\r\n        selenium2:\r\n          wd_host: \"http:\/\/192.168.10.1:4444\/wd\/hub\"\r\n        browser_name: chrome<\/pre>\n<p>We\u2019ll build off this in a moment to add Codeship. But for now, I have one <strong>suite<\/strong> to get started (<code>user_auth<\/code>) and one <strong>profile<\/strong> (<code>default<\/code>). I have the <code>base_url<\/code> for the local site (<code>https:\/\/codeship-behat.dev<\/code>), and for now it\u2019s using my application\u2019s <code>.env<\/code> file as seen on this line in the <code># env_path: .env.behat<\/code>. This will change for the Codeship profile.<\/p>\n<p>Notice too that I used the IP of my Host to talk to Selenium from inside my Guest \u2014 <code>http:\/\/192.168.10.1:4444\/wd\/hub<\/code>; one more thing that will change for the Codeship profile.<\/p>\n<h2>Initialize Behat and Write a Test<\/h2>\n<p>So now to prove all of this is working, I start by running:<\/p>\n<pre class=\" brush:php\">vendor\/bin\/behat --init<\/pre>\n<p>You now have a <code>features<\/code> folder in the root of your application and inside of that, a <code>bootstrap<\/code> folder, and finally, inside of that, a <code>FeatureContext.php<\/code> file.<\/p>\n<p>Now to make my <code>feature<\/code> file <code>features\/user_auth.feature<\/code>:<\/p>\n<p><a href=\"http:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2016\/09\/feature_file.png\"><img decoding=\"async\" class=\"aligncenter size-full wp-image-14649\" src=\"http:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2016\/09\/feature_file.png\" alt=\"feature_file\" width=\"335\" height=\"166\" srcset=\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2016\/09\/feature_file.png 335w, https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2016\/09\/feature_file-300x149.png 300w\" sizes=\"(max-width: 335px) 100vw, 335px\" \/><\/a><\/p>\n<p>And I need to fill in the details:<\/p>\n<pre class=\" brush:php\">Feature: User Login Area\r\n  User can log into the site\r\n  As an anonymous user\r\n  So that they can see secured parts of the site\r\n\r\n  @happy_path @user_auth @javascript\r\n  Scenario: Logging in with Success\r\n    Given I visit the login page\r\n    And I fill in the form with my username and password and submit the form\r\n    Then I should see \"You are logged in!\"<\/pre>\n<p>Focusing on the <code>@happy_path<\/code> for now, we tag it <code>@user_auth<\/code> so we know it is part of the suite as seen in the <code>behat.yml<\/code> file above <code>filters: { tags: '@user_auth' }<\/code>. I could have used folders to organize my suites but chose tags for now.<\/p>\n<p>Now if I run:<\/p>\n<pre class=\" brush:php\">vendor\/bin\/behat --suite user_auth --init<\/pre>\n<p>I get a file (<code>features\/bootstrap\/UserAuthenticationContext.php<\/code>) that\u2019s pretty empty and needs to be told to extend <code>MinkContext<\/code>. So I just need to change the \u201cextends\u201d section so it looks like this:<\/p>\n<pre class=\" brush:php\">&lt;?php\r\n\r\nuse Behat\\Behat\\Hook\\Scope\\AfterStepScope;\r\nuse Behat\\Behat\\Tester\\Exception\\PendingException;\r\nuse Behat\\Behat\\Context\\Context;\r\nuse Behat\\Behat\\Context\\SnippetAcceptingContext;\r\nuse Behat\\Gherkin\\Node\\PyStringNode;\r\nuse Behat\\Gherkin\\Node\\TableNode;\r\nuse Behat\\Mink\\Driver\\Selenium2Driver;\r\nuse Behat\\MinkExtension\\Context\\MinkContext;\r\n\r\n\/**\r\n * Defines application features from the specific context.\r\n *\/\r\nclass UserAuthenticationContext extends MinkContext implements Context, SnippetAcceptingContext\r\n{\r\n    public function __construct()\r\n    {\r\n\r\n\r\n    }<\/pre>\n<p>Now we want to take our <code>feature<\/code>, which has some custom steps in there, and have Behat stub these out in the <code>features\/bootstrap\/UserAuthenticationContext.php<\/code>.<\/p>\n<pre class=\" brush:php\">vendor\/bin\/behat --suite user_auth --append-snippets<\/pre>\n<p>That file will now be full of stubbed-out functions that have the annotations to connect to your feature\u2019s steps that throw a <code>PendingException<\/code> to let you know there\u2019s more work to do.<\/p>\n<p>Keep in mind that <code>Then I should see \"You are logged in!\"<\/code> in this example is a Mink-related step, so there\u2019s nothing else I need to do. However, <code>And I fill in the form with my username and password and submit the form<\/code> is custom, so I need to fill in some code there.<\/p>\n<pre class=\" brush:php\">\/**\r\n     * @Given I fill in the form with my username and password and submit the form\r\n     *\/\r\n    public function iFillInTheFormWithMyUsernameAndPasswordAndSubmitTheForm()\r\n    {\r\n        $this-&gt;fillField('email', 'foo@foo.com');\r\n        $this-&gt;fillField('password', env('EXAMPLE_USER_PASSWORD'));\r\n        $this-&gt;pressButton('Login');\r\n    }<\/pre>\n<p>Now our test is ready to run. I\u2019m talking to the DOM in the above steps, so if I remove the <code>@javascript<\/code> from that test and run:<\/p>\n<pre class=\" brush:php\">vendor\/bin\/behat --suite user_auth<\/pre>\n<p><a href=\"http:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2016\/09\/with_out_javascript.png\"><img decoding=\"async\" class=\"aligncenter size-full wp-image-14650\" src=\"http:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2016\/09\/with_out_javascript.png\" alt=\"with_out_javascript\" width=\"582\" height=\"279\" srcset=\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2016\/09\/with_out_javascript.png 582w, https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2016\/09\/with_out_javascript-300x144.png 300w\" sizes=\"(max-width: 582px) 100vw, 582px\" \/><\/a><\/p>\n<p>We aren\u2019t talking to Selenium but to <a href=\"http:\/\/symfony.com\/doc\/current\/components\/browser_kit.html\">BrowerKit<\/a>. Note how fast it is!<\/p>\n<p>And add the tag back and run again:<\/p>\n<p><a href=\"http:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2016\/09\/with_javascript.png\"><img decoding=\"async\" class=\"aligncenter size-full wp-image-14651\" src=\"http:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2016\/09\/with_javascript.png\" alt=\"with_javascript\" width=\"556\" height=\"282\" srcset=\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2016\/09\/with_javascript.png 556w, https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2016\/09\/with_javascript-300x152.png 300w\" sizes=\"(max-width: 556px) 100vw, 556px\" \/><\/a><\/p>\n<p>Be careful to only use <code>@javascript<\/code> when really needed.<\/p>\n<p>&nbsp;<\/p>\n<p>As you can see, it\u2019s 0m7.64s with <code>@javascript<\/code> and 0m2.09s without! So be careful to only use <code>@javascript<\/code> when really needed (<em>e.g.<\/em>, when the page you\u2019re testing has JavaScript that you are focusing on). So my Behat test can have two scenarios: one has <code>@javascritp<\/code> and one does not. Or the entire <code>feature<\/code> can be marked <code>@javascript<\/code> if needed.<\/p>\n<pre class=\" brush:php\">@javascript\r\nFeature: User Login Area\r\n  User can log into the site\r\n  As an anonymous user\r\n  So that they can see secured parts of the site<\/pre>\n<h2>Four Steps to Set Up Codeship for Headless Chrome<\/h2>\n<p>Now that the test is passing locally, let\u2019s get to Codeship.<\/p>\n<h2>Step 1: Add a profile to <code>behat.yml<\/code><\/h2>\n<p>For Codeship, that looks like this:<\/p>\n<pre class=\" brush:php\">codeship_non_sauce:\r\n    extensions:\r\n        Laracasts\\Behat:\r\n            env_path: .env.codeship\r\n        Behat\\MinkExtension:\r\n            base_url: http:\/\/127.0.0.1:8080\r\n            default_session: laravel\r\n            laravel: ~\r\n            selenium2:\r\n              wd_host: 'http:\/\/127.0.0.1:4444\/wd\/hub'\r\n            browser_name: chrome<\/pre>\n<p>This leaves our <code>behat.yml<\/code> looking like this:<\/p>\n<pre class=\" brush:php\">default:\r\n  suites:\r\n    user_auth:\r\n      contexts: [ UserAuthenticationContext ]\r\n      filters: { tags: '@user_auth' }\r\n\r\n  extensions:\r\n    Laracasts\\Behat:\r\n        # env_path: .env.behat\r\n    Behat\\MinkExtension:\r\n        base_url: https:\/\/codeship-behat.dev\r\n        default_session: laravel\r\n        laravel: ~\r\n        selenium2:\r\n          wd_host: \"http:\/\/192.168.10.1:4444\/wd\/hub\"\r\n        browser_name: chrome\r\n\r\n\r\ncodeship_non_sauce:\r\n    extensions:\r\n        Laracasts\\Behat:\r\n            env_path: .env.codeship\r\n        Behat\\MinkExtension:\r\n            base_url: http:\/\/127.0.0.1:8080\r\n            default_session: laravel\r\n            laravel: ~\r\n            selenium2:\r\n              wd_host: 'http:\/\/127.0.0.1:4444\/wd\/hub'\r\n            browser_name: chrome<\/pre>\n<p>What we are doing is setting up a <code>.env.codeship<\/code> just for Codeship settings, as well as setting a new <code>base_url<\/code> and using Selenium on <code>127.0.0.1<\/code>.<\/p>\n<h2>Step 2: The <code>.env.codeship<\/code> file<\/h2>\n<p>Make that file in the root of your application and add to it this:<\/p>\n<pre class=\" brush:php\">APP_ENV=codeship\r\nAPP_KEY=base64:w0k4ZmTt89FApLdUaAsubNXH1eQcHR8vyat\/ZvmqRso=\r\nAPP_DEBUG=true\r\nAPP_LOG_LEVEL=debug\r\nAPP_URL=http:\/\/127.0.0.1:8080\r\n\r\nDB_HOST=localhost\r\nDB_DATABASE=test\r\nDB_PASSWORD=test\r\nDB_CONNECTION=mysql\r\nDB_USERNAME=root\r\n\r\nQUEUE_DRIVER=sync\r\n\r\nMAIL_DRIVER=log\r\n\r\nEXAMPLE_USER_PASSWORD=quahf1Kaib2Ienei<\/pre>\n<p>At this point, we set up our environment for the needed database and <code>APP_URL<\/code>, as well as making sure <code>QUEUE<\/code> is in sync mode and <code>MAIL_DRIVER<\/code> is just log.<\/p>\n<h3>Step 3: Update our <code>config\/database.php<\/code><\/h3>\n<p>Modify the <code>config\/database.php<\/code> to look like this:<\/p>\n<pre class=\" brush:php\">'mysql' =&gt; [\r\n            'driver'    =&gt; 'mysql',\r\n            'host'      =&gt; env('DB_HOST', 'localhost'),\r\n            'database'  =&gt; env('DB_DATABASE', 'forge'),\r\n            'username'  =&gt; env('DB_USERNAME', 'forge'),\r\n            'password'  =&gt; env('DB_PASSWORD', ''),\r\n            'charset'   =&gt; 'utf8',\r\n            'collation' =&gt; 'utf8_unicode_ci',\r\n            'prefix'    =&gt; '',\r\n            'strict'    =&gt; false,\r\n        ],<\/pre>\n<p>Replacing the default <code>mysql<\/code> settings with the above will help us swap out the settings as needed for Codeship and its database work.<\/p>\n<h3>Step 4: Scripting the setup of Selenium and a local Laravel server<\/h3>\n<p>Now we need a script to set up Codeship for testing. When setting up a Codeship project, you\u2019ll have a Setup Commands window as seen below. In here, I added <code>ci\/setup.sh<\/code>.<\/p>\n<p><a href=\"http:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2016\/09\/ci_setup.png\"><img decoding=\"async\" class=\"aligncenter size-full wp-image-14652\" src=\"http:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2016\/09\/ci_setup.png\" alt=\"ci_setup\" width=\"568\" height=\"391\" srcset=\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2016\/09\/ci_setup.png 568w, https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2016\/09\/ci_setup-300x207.png 300w\" sizes=\"(max-width: 568px) 100vw, 568px\" \/><\/a><\/p>\n<p>This is placed into a script so that if I have to SSH into Codeship to recreate the environment to see what a test is failing, I can just do the one command.<\/p>\n<p>Next, I make the folder <code>ci<\/code> and then in there, <code>setup.sh<\/code>. This will look like:<\/p>\n<pre class=\" brush:php\">#!\/bin\/sh\r\n\r\n###\r\n# This is thanks to Codeship Docs\r\n# But I wanted a newer version of Selenium\r\n###\r\n\r\nSELENIUM_VERSION=${SELENIUM_VERSION:=\"2.53.1\"}\r\nSELENIUM_PORT=${SELENIUM_PORT:=\"4444\"}\r\nSELENIUM_OPTIONS=${SELENIUM_OPTIONS:=\"\"}\r\nSELENIUM_WAIT_TIME=${SELENIUM_WAIT_TIME:=\"10\"}\r\n\r\nset -e\r\n\r\nMINOR_VERSION=${SELENIUM_VERSION%.*}\r\nCACHED_DOWNLOAD=\"${HOME}\/cache\/selenium-server-standalone-${SELENIUM_VERSION}.jar\"\r\n\r\nwget --continue --output-document \"${CACHED_DOWNLOAD}\" \"http:\/\/selenium-release.storage.googleapis.com\/${MINOR_VERSION}\/selenium-server-standalone-${SELENIUM_VERSION}.jar\"\r\njava -jar \"${CACHED_DOWNLOAD}\" -port \"${SELENIUM_PORT}\" ${SELENIUM_OPTIONS} -log \/tmp\/sel.log 2&gt;&amp;1 &amp;\r\nsleep \"${SELENIUM_WAIT_TIME}\"\r\necho \"Selenium ${SELENIUM_VERSION} is now ready to connect on port ${SELENIUM_PORT}...\"\r\n\r\n\r\n## Now we are ready to talk to Selenium let's start the Application server\r\n\r\ncp .env.codeship .env\r\nphp artisan serve --port=8080 -n -q &amp;\r\nsleep 3<\/pre>\n<p>Basically I download Selenium standalone, then run it. After that, I copy over the <code>.env.codeship<\/code> file to <code>.env<\/code>. This ensures that the server will run with that one so it will line up with the <code>behat.yml<\/code> I\u2019m using. If I didn\u2019t do this, there would be no <code>.env<\/code> since this is not part of Git, and I need to make sure the env settings are correct for Codeship.<\/p>\n<p>Keep in mind I could have placed all of this into the Codeship environment UI settings. However, as I mentioned before, I find this comes in handy when I want to SSH in and set up Codeship to help troubleshoot an issue.<\/p>\n<p>Now to make sure the Codeship test pipeline is set.<\/p>\n<p><a href=\"http:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2016\/09\/test_pipeline.png\"><img decoding=\"async\" class=\"aligncenter size-full wp-image-14653\" src=\"http:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2016\/09\/test_pipeline.png\" alt=\"test_pipeline\" width=\"504\" height=\"299\" srcset=\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2016\/09\/test_pipeline.png 504w, https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2016\/09\/test_pipeline-300x178.png 300w\" sizes=\"(max-width: 504px) 100vw, 504px\" \/><\/a><\/p>\n<p>Here I do a migration and seed. Typically I would leave the seed out and let each Behat feature set up the state of the application the way it needs it. In this case, I\u2019ll keep it simple. Note that I\u2019m running Behat with the Codeship profile <code>codeship_non_sauce<\/code>.<\/p>\n<p>During the setup in <code>Codeship<\/code>, we need to put this <code>.env.codeship<\/code> file in place. That happens in the <code>ci\/setup.sh<\/code> script:<\/p>\n<p><a href=\"http:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2016\/09\/copy_env_codeship.png\"><img decoding=\"async\" class=\"aligncenter size-full wp-image-14654\" src=\"http:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2016\/09\/copy_env_codeship.png\" alt=\"copy_env_codeship\" width=\"733\" height=\"125\" srcset=\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2016\/09\/copy_env_codeship.png 733w, https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2016\/09\/copy_env_codeship-300x51.png 300w\" sizes=\"(max-width: 733px) 100vw, 733px\" \/><\/a><\/p>\n<p>Now we push to GitHub and\u2026<\/p>\n<p><a href=\"http:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2016\/09\/codeship_psasing.png\"><img decoding=\"async\" class=\"aligncenter size-full wp-image-14655\" src=\"http:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2016\/09\/codeship_psasing.png\" alt=\"codeship_psasing\" width=\"797\" height=\"719\" srcset=\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2016\/09\/codeship_psasing.png 797w, https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2016\/09\/codeship_psasing-300x271.png 300w, https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2016\/09\/codeship_psasing-768x693.png 768w\" sizes=\"(max-width: 797px) 100vw, 797px\" \/><\/a><\/p>\n<h3>What happens when the tests don\u2019t pass on Codeship?<\/h3>\n<p>Let me give you a real example of a difficult problem I wrestled with. I had this setting in my blade file:<\/p>\n<pre class=\" brush:php\">&lt;script src=\"{{ asset(\"\/js\/app.js\", true) }}\"&gt;&lt;\/script&gt;<\/pre>\n<p>So locally at <code>https:\/\/codeship-behat.dev<\/code>, this worked great. But on Codeship, which runs at <code>http:\/\/localhost:8080<\/code> (<em>i.e.<\/em>, not using https), I kept getting a fail. At that point, I could put Saucelabs into the mix to watch the test run, but this was still not enough. That\u2019s when SauceConnect comes into play, and I can interact with the Codeship server!<\/p>\n<p>It was only here that I could open the Chrome console and really see the error message about not being able to connect to <code>https:\/\/localhost:8080<\/code>.<\/p>\n<p>We\u2019ll continue this in my next article, so stay tuned for it here on Codeship in a couple weeks. For now, let me leave you with a few links to tide you over.<\/p>\n<h2>Resources<\/h2>\n<p><a href=\"https:\/\/github.com\/laracasts\/Behat-Laravel-Extension\">Behat-Laravel-Extension<\/a><\/p>\n<p><a href=\"https:\/\/alfrednutile.info\/posts\/181\">Windows and Selenium<\/a><\/p>\n<p><a href=\"https:\/\/wiki.saucelabs.com\/display\/DOCS\/Setting+Up+Sauce+Connect\">SauceConnect<\/a><\/p>\n<p><a href=\"https:\/\/www.npmjs.com\/package\/selenium-standalone\">Selenium Standalone Install<\/a><\/p>\n<p><a href=\"https:\/\/github.com\/codeship\/scripts\/blob\/master\/packages\/selenium_server.sh\">Codeship Selenium Install Script<\/a><\/p>\n<div class=\"attribution\">\n<table>\n<tbody>\n<tr>\n<td><span class=\"reference\">Reference: <\/span><\/td>\n<td><a href=\"https:\/\/blog.codeship.com\/laravel-and-behat-using-selenium-and-headless-chrome\/\">Laravel and Behat Using Selenium and Headless Chrome<\/a> from our <a href=\"http:\/\/www.webcodegeeks.com\/join-us\/wcg\/\">WCG partner<\/a> Florian Motlik at the <a href=\"http:\/\/blog.codeship.com\/\">Codeship Blog<\/a> blog.<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Let\u2019s take a look at using Codeship for Selenium and Headless Chrome testing, which is key for interacting with JavaScript features on your site. I also want to show you how to troubleshoot those rare moments when there\u2019s an issue on the CI but not on your local build, by using Codeship\u2019s SSH feature and &hellip;<\/p>\n","protected":false},"author":81,"featured_media":14656,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[8],"tags":[392,385,339],"class_list":["post-14625","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-web-development","tag-behat","tag-laravel","tag-selenium"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v26.5 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Laravel and Behat Using Selenium and Headless Chrome - Web Code Geeks - 2026<\/title>\n<meta name=\"description\" content=\"Let\u2019s take a look at using Codeship for Selenium and Headless Chrome testing, which is key for interacting with JavaScript features on your site. I also\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.webcodegeeks.com\/web-development\/laravel-behat-using-selenium-headless-chrome\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Laravel and Behat Using Selenium and Headless Chrome - Web Code Geeks - 2026\" \/>\n<meta property=\"og:description\" content=\"Let\u2019s take a look at using Codeship for Selenium and Headless Chrome testing, which is key for interacting with JavaScript features on your site. I also\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.webcodegeeks.com\/web-development\/laravel-behat-using-selenium-headless-chrome\/\" \/>\n<meta property=\"og:site_name\" content=\"Web Code Geeks\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/webcodegeeks\" \/>\n<meta property=\"article:author\" content=\"https:\/\/www.facebook.com\/flomotlik\" \/>\n<meta property=\"article:published_time\" content=\"2016-09-08T14:19:42+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2016\/09\/selenium-logo.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"150\" \/>\n\t<meta property=\"og:image:height\" content=\"150\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Florian Motlik\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@http:\/\/twitter.com\/flomotlik\" \/>\n<meta name=\"twitter:site\" content=\"@webcodegeeks\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Florian Motlik\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"10 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/www.webcodegeeks.com\/web-development\/laravel-behat-using-selenium-headless-chrome\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/web-development\/laravel-behat-using-selenium-headless-chrome\/\"},\"author\":{\"name\":\"Florian Motlik\",\"@id\":\"https:\/\/www.webcodegeeks.com\/#\/schema\/person\/c2ce85ee77a6cb5f80143c1f88bb8d10\"},\"headline\":\"Laravel and Behat Using Selenium and Headless Chrome\",\"datePublished\":\"2016-09-08T14:19:42+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/web-development\/laravel-behat-using-selenium-headless-chrome\/\"},\"wordCount\":1368,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/#organization\"},\"image\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/web-development\/laravel-behat-using-selenium-headless-chrome\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2016\/09\/selenium-logo.jpg\",\"keywords\":[\"Behat\",\"Laravel\",\"Selenium\"],\"articleSection\":[\"Web Dev\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/www.webcodegeeks.com\/web-development\/laravel-behat-using-selenium-headless-chrome\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.webcodegeeks.com\/web-development\/laravel-behat-using-selenium-headless-chrome\/\",\"url\":\"https:\/\/www.webcodegeeks.com\/web-development\/laravel-behat-using-selenium-headless-chrome\/\",\"name\":\"Laravel and Behat Using Selenium and Headless Chrome - Web Code Geeks - 2026\",\"isPartOf\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/web-development\/laravel-behat-using-selenium-headless-chrome\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/web-development\/laravel-behat-using-selenium-headless-chrome\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2016\/09\/selenium-logo.jpg\",\"datePublished\":\"2016-09-08T14:19:42+00:00\",\"description\":\"Let\u2019s take a look at using Codeship for Selenium and Headless Chrome testing, which is key for interacting with JavaScript features on your site. I also\",\"breadcrumb\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/web-development\/laravel-behat-using-selenium-headless-chrome\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.webcodegeeks.com\/web-development\/laravel-behat-using-selenium-headless-chrome\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.webcodegeeks.com\/web-development\/laravel-behat-using-selenium-headless-chrome\/#primaryimage\",\"url\":\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2016\/09\/selenium-logo.jpg\",\"contentUrl\":\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2016\/09\/selenium-logo.jpg\",\"width\":150,\"height\":150},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.webcodegeeks.com\/web-development\/laravel-behat-using-selenium-headless-chrome\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.webcodegeeks.com\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Web Dev\",\"item\":\"https:\/\/www.webcodegeeks.com\/category\/web-development\/\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"Laravel and Behat Using Selenium and Headless Chrome\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.webcodegeeks.com\/#website\",\"url\":\"https:\/\/www.webcodegeeks.com\/\",\"name\":\"Web Code Geeks\",\"description\":\"Web Developers Resource Center\",\"publisher\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.webcodegeeks.com\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/www.webcodegeeks.com\/#organization\",\"name\":\"Exelixis Media P.C.\",\"url\":\"https:\/\/www.webcodegeeks.com\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.webcodegeeks.com\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2022\/06\/exelixis-logo.png\",\"contentUrl\":\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2022\/06\/exelixis-logo.png\",\"width\":864,\"height\":246,\"caption\":\"Exelixis Media P.C.\"},\"image\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/www.facebook.com\/webcodegeeks\",\"https:\/\/x.com\/webcodegeeks\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.webcodegeeks.com\/#\/schema\/person\/c2ce85ee77a6cb5f80143c1f88bb8d10\",\"name\":\"Florian Motlik\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.webcodegeeks.com\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/d325d3a5660b2d2b2b433ebbe06cac2210d8d0e5083c7998b4ca07cc24d7cf3e?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/d325d3a5660b2d2b2b433ebbe06cac2210d8d0e5083c7998b4ca07cc24d7cf3e?s=96&d=mm&r=g\",\"caption\":\"Florian Motlik\"},\"sameAs\":[\"http:\/\/blog.codeship.com\/\",\"https:\/\/www.facebook.com\/flomotlik\",\"http:\/\/at.linkedin.com\/in\/florianmotlik\",\"https:\/\/x.com\/http:\/\/twitter.com\/flomotlik\"],\"url\":\"https:\/\/www.webcodegeeks.com\/author\/florian-motlik\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Laravel and Behat Using Selenium and Headless Chrome - Web Code Geeks - 2026","description":"Let\u2019s take a look at using Codeship for Selenium and Headless Chrome testing, which is key for interacting with JavaScript features on your site. I also","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.webcodegeeks.com\/web-development\/laravel-behat-using-selenium-headless-chrome\/","og_locale":"en_US","og_type":"article","og_title":"Laravel and Behat Using Selenium and Headless Chrome - Web Code Geeks - 2026","og_description":"Let\u2019s take a look at using Codeship for Selenium and Headless Chrome testing, which is key for interacting with JavaScript features on your site. I also","og_url":"https:\/\/www.webcodegeeks.com\/web-development\/laravel-behat-using-selenium-headless-chrome\/","og_site_name":"Web Code Geeks","article_publisher":"https:\/\/www.facebook.com\/webcodegeeks","article_author":"https:\/\/www.facebook.com\/flomotlik","article_published_time":"2016-09-08T14:19:42+00:00","og_image":[{"width":150,"height":150,"url":"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2016\/09\/selenium-logo.jpg","type":"image\/jpeg"}],"author":"Florian Motlik","twitter_card":"summary_large_image","twitter_creator":"@http:\/\/twitter.com\/flomotlik","twitter_site":"@webcodegeeks","twitter_misc":{"Written by":"Florian Motlik","Est. reading time":"10 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.webcodegeeks.com\/web-development\/laravel-behat-using-selenium-headless-chrome\/#article","isPartOf":{"@id":"https:\/\/www.webcodegeeks.com\/web-development\/laravel-behat-using-selenium-headless-chrome\/"},"author":{"name":"Florian Motlik","@id":"https:\/\/www.webcodegeeks.com\/#\/schema\/person\/c2ce85ee77a6cb5f80143c1f88bb8d10"},"headline":"Laravel and Behat Using Selenium and Headless Chrome","datePublished":"2016-09-08T14:19:42+00:00","mainEntityOfPage":{"@id":"https:\/\/www.webcodegeeks.com\/web-development\/laravel-behat-using-selenium-headless-chrome\/"},"wordCount":1368,"commentCount":0,"publisher":{"@id":"https:\/\/www.webcodegeeks.com\/#organization"},"image":{"@id":"https:\/\/www.webcodegeeks.com\/web-development\/laravel-behat-using-selenium-headless-chrome\/#primaryimage"},"thumbnailUrl":"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2016\/09\/selenium-logo.jpg","keywords":["Behat","Laravel","Selenium"],"articleSection":["Web Dev"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.webcodegeeks.com\/web-development\/laravel-behat-using-selenium-headless-chrome\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.webcodegeeks.com\/web-development\/laravel-behat-using-selenium-headless-chrome\/","url":"https:\/\/www.webcodegeeks.com\/web-development\/laravel-behat-using-selenium-headless-chrome\/","name":"Laravel and Behat Using Selenium and Headless Chrome - Web Code Geeks - 2026","isPartOf":{"@id":"https:\/\/www.webcodegeeks.com\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.webcodegeeks.com\/web-development\/laravel-behat-using-selenium-headless-chrome\/#primaryimage"},"image":{"@id":"https:\/\/www.webcodegeeks.com\/web-development\/laravel-behat-using-selenium-headless-chrome\/#primaryimage"},"thumbnailUrl":"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2016\/09\/selenium-logo.jpg","datePublished":"2016-09-08T14:19:42+00:00","description":"Let\u2019s take a look at using Codeship for Selenium and Headless Chrome testing, which is key for interacting with JavaScript features on your site. I also","breadcrumb":{"@id":"https:\/\/www.webcodegeeks.com\/web-development\/laravel-behat-using-selenium-headless-chrome\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.webcodegeeks.com\/web-development\/laravel-behat-using-selenium-headless-chrome\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.webcodegeeks.com\/web-development\/laravel-behat-using-selenium-headless-chrome\/#primaryimage","url":"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2016\/09\/selenium-logo.jpg","contentUrl":"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2016\/09\/selenium-logo.jpg","width":150,"height":150},{"@type":"BreadcrumbList","@id":"https:\/\/www.webcodegeeks.com\/web-development\/laravel-behat-using-selenium-headless-chrome\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.webcodegeeks.com\/"},{"@type":"ListItem","position":2,"name":"Web Dev","item":"https:\/\/www.webcodegeeks.com\/category\/web-development\/"},{"@type":"ListItem","position":3,"name":"Laravel and Behat Using Selenium and Headless Chrome"}]},{"@type":"WebSite","@id":"https:\/\/www.webcodegeeks.com\/#website","url":"https:\/\/www.webcodegeeks.com\/","name":"Web Code Geeks","description":"Web Developers Resource Center","publisher":{"@id":"https:\/\/www.webcodegeeks.com\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.webcodegeeks.com\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/www.webcodegeeks.com\/#organization","name":"Exelixis Media P.C.","url":"https:\/\/www.webcodegeeks.com\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.webcodegeeks.com\/#\/schema\/logo\/image\/","url":"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2022\/06\/exelixis-logo.png","contentUrl":"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2022\/06\/exelixis-logo.png","width":864,"height":246,"caption":"Exelixis Media P.C."},"image":{"@id":"https:\/\/www.webcodegeeks.com\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/webcodegeeks","https:\/\/x.com\/webcodegeeks"]},{"@type":"Person","@id":"https:\/\/www.webcodegeeks.com\/#\/schema\/person\/c2ce85ee77a6cb5f80143c1f88bb8d10","name":"Florian Motlik","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.webcodegeeks.com\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/d325d3a5660b2d2b2b433ebbe06cac2210d8d0e5083c7998b4ca07cc24d7cf3e?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/d325d3a5660b2d2b2b433ebbe06cac2210d8d0e5083c7998b4ca07cc24d7cf3e?s=96&d=mm&r=g","caption":"Florian Motlik"},"sameAs":["http:\/\/blog.codeship.com\/","https:\/\/www.facebook.com\/flomotlik","http:\/\/at.linkedin.com\/in\/florianmotlik","https:\/\/x.com\/http:\/\/twitter.com\/flomotlik"],"url":"https:\/\/www.webcodegeeks.com\/author\/florian-motlik\/"}]}},"_links":{"self":[{"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/posts\/14625","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/users\/81"}],"replies":[{"embeddable":true,"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/comments?post=14625"}],"version-history":[{"count":0,"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/posts\/14625\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/media\/14656"}],"wp:attachment":[{"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/media?parent=14625"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/categories?post=14625"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/tags?post=14625"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}