Running Drupal's FunctionalJavascript tests on DDEV

This is part two of a series on running Drupal’s testing suites on DDEV. We left off last time with trying to execute a FunctionalJavscript test and having every test case skipped because no browser emulator was available. In this post, we will run through getting set up to execute Drupal’s FunctionalJavascript tests inside of your DDEV web container.

Thumbnail

Drupal 8.3 saw the ability to write tests that interacted with JavaScript when PhantomJS was added to Drupal’s testing capabilities. If you are not familiar with PhantomJS it is a headless web browser that was built on QtWebKit. And then in early 2018, a headless version of Chrome became available, essentially pushing PhantomJS into deprecated status. With Drupal 8.5 we now have the ability to execute WebDriver tests that run on headless Chrome. Honestly, this is a great improvement. The WebDriver API allows for using any client which respects its API (ie: FireFox) and not one-off integrations as was required for PhantomJS.

With JavascriptTestBase deprecated in favor of WebDriverTestBase, you only need a WebDriver client to run Drupal core’s FunctionalJavascript tests. You may need PhantomJS to run some contributed project tests (like Drupal Commerce.) With that said, next we will get Chromedriver up and running so we can run some tests!

If you’re writing a JavaScript enabled test as of Drupal 8.5 and extending JavascriptTestBase you will be using the new WebDriver base class. Some core or contrib tests might extend LegacyJavascriptTestBase and still use PhantomJS. One reason: PhantomJS returned response status codes and WebDriver does not — so tests may have not been ported yet to support the new testing API.

To keep things simple, we are going to use a Chromedriver Docker image provided by DrupalCi — the one used when executing tests on Drupal.org. To do this, we need to define a custom service for DDEV. It is petty simple: we just need to add a file called docker-compose.chromedriver.yml to our project's .ddev directory.

version: '3.6'
services:
  chromedriver:
    container_name: ddev-${DDEV_SITENAME}-chromedriver
    image: drupalci/chromedriver:production
    labels:
    # These labels ensure this service is discoverable by ddev
      com.ddev.site-name: ${DDEV_SITENAME}
      com.ddev.approot: $DDEV_APPROOT
      com.ddev.app-url: $DDEV_URL
  # This links the Chromedriver service to the web service defined
  # in the main docker-compose.yml, allowing applications running
  # in the web service to access the driver at `chromedriver`.
  web:
    links:
      - chromedriver:$DDEV_HOSTNAME

This file goes right into the project’s .ddev directory, as shown.

Thumbnail

Now, restart your project and bring the new service online. The Docker image will be pulled down and the container started, managed by DDEV.

Thumbnail

 

Now that we have Chromedriver running, we need to configure phpunit.xml once more. We need to change the value of SIMPLETEST_BASE_URL and add an environment variable called MINK_DRIVER_ARGS_WEBDRIVER. Previously we defined SIMPLETEST_BASE_URL as http://localhost. There is just one problem: Chrome is going to try and visit localhost and not receive a website! Luckily, via Docker’s network, it can reach the web container through the hostname web. So we will need to change that value. The second environment variable passes WebDriver parameters to our service so let it know how it should be executed.

<php>
  <ini name="error_reporting" value="32767"/>
  <ini name="memory_limit" value="-1"/>
  <!-- Changed to http://web for Chromedriver access -->
  <env name="SIMPLETEST_BASE_URL" value="http://web"/>
  <env name="SIMPLETEST_DB" value="mysql://db:[email protected]/db"/>
  <env name="BROWSERTEST_OUTPUT_DIRECTORY" value=""/>
  <!-- Parameters pass to Chromedriver. -->
  <env name="MINK_DRIVER_ARGS_WEBDRIVER" value='["chrome", {"browserName":"chrome","chromeOptions":{"args":["--disable-gpu","--headless", "--no-sandbox"]}}, "http://chromedriver:9515"]'/>
</php>

You can keep SIMPLETEST_BASE_URL as http://web for running your Functional tests, as well. Inside of the web containerhttp://web resolves as localhost.

For me, Chromedriver kept crashing until I added the --no-sandbox flag. DrupalCi does not pass this, so I am not sure the difference. But, I kept it for those who may have the same problem.

Time to give it a run! Open a terminal and let’s run the Big Pipe module's FunctionalJavascript tests.

ddev ssh
../vendor/bin/phpunit -c core core/modules/big_pipe/tests/src/FunctionalJavascript

And you should see test passes!

Thumbnail

If the tests come back as skipped, communication with the Chromedriver had failed. If this happens, run this command and run the tests again. This will display log output from Chrome.

docker exec -it {YOUR_PROJECT_NAME}-ddev-chromedriver tail -f /tmp/chromedriver.log

Nightwatch.js

All right! We have now run all of Drupal's PHPUnit test suites. In my next post we will explore Drupal's newest testing framework: Nightwatch.js.