Testing with DDev, Gitlab, and serving Drupal from a subfolder instead of a subdomain

While working on the Easy Breadcrumb module for Drupal, I came across a situation where a test was failling on Gitlab but was passing with DDev: https://www.drupal.org/project/easy_breadcrumb/issues/3615550

The difference that mattered was that Gitlab serves the site from http://localhost/web for testing and locally it gets served from a fully qualified domain name, ie. http://ddev.site.example.com.

Getting the test to fail locally

Using firecow/gitlab-ci-local

To get the test to fail locally, I used the suggestions at https://project.pages.drupalcode.org/gitlab_templates/info/test-locally/ , which suggested firecow/gitlab-ci-local . I did get that working but I ran into a hitch. My work computer is a Mac with an ARM64 chip and gitlab-ci-local tries to install selenium/standalone-chrome:127.0, which doesn't support ARM64. I dug around and figured out that I could use selenium/standalone-chromium:latest instead by adding a few lines in .gitlab-ci.yml:

phpunit:
  services:
    - !reference [.with-database]
    - name: selenium/standalone-chromium:latest
      alias: selenium
      
# The lines below are to filter which tests run. In this case, only tests in the
# "redirect" group.
variables:
  _PHPUNIT_TESTGROUPS: "redirect"

Then, on the command line, to just run PHPUnit and not most of the other steps, I used the --stage flag to just run jobs in the test stage, which by default is just PHPUnit:

$ drupal-ci-local --stage=test

That worked and I was able to reproduce locally. But, it took about a minute to run the test instead of 2 or 3 seconds with DDev.

Using DDEV to serve a Drupal site from a subdirectory instead of a subdomain

This way is not perfect, but it worked well enough for me to use Xdebug when running tests and when browing the site in a browser.

The code below results in the site being served from a subdirectory named easy, ie. http://example.com/easy.

First, cd to root of project, then:

mkdir router
ls -s ./web ./router/easy

Then, in .ddev/config.yaml

docroot: router

Next, in settings.php

if (isset($GLOBALS['request'])) {
 $request = $GLOBALS['request'];
 $script_name = $request->server->get('SCRIPT_NAME');
 if ($script_name === '/easy/index.php' || $script_name === '/index.php') {
   $request->server->set('SCRIPT_NAME', '/easy/index.php');
 }
}

Then, in .ddev/nginx_full/nginx-site.conf, replace the section that starts with 'location /' to be like this instead:

   # 1. CSS/JS caches
   location ~* ^/easy/sites/.*/files/(css|js)/ {
       try_files $uri @rewrite;
       expires max;
       log_not_found off;
   }
   # 2. Image styles
   location ~* ^/easy/sites/.*/files/styles/ {
       try_files $uri @rewrite;
   }
   # 3. Clean URLs
   location /easy {
       try_files $uri $uri/ /easy/index.php?$query_string;
   }
   # 4. Fallback
   location @rewrite {
       rewrite ^/easy/(.*)$ /easy/index.php?$query_string;
   }

To run phpunit tests, pass in SIMPLETEST_BASE_URL.
Example which just runs ones test:

ddev exec SIMPLETEST_BASE_URL=http://localhost/easy vendor/bin/phpunit --bootstrap web/core/tests/bootstrap.php --filter testEasyBreadcrumbFollowRedirects web/modules/custom/easy_breadcrumb