Skip to main content

Article

Drupal Quality Checks

Where Drupal Quality Checks Should Live: Core, QA, or Watchtower?

As our Drupal normalization tooling grows, an important question is not only what should we check?, but also where should each check live?

We currently use three complementary projects:

  • Core — Python-based tooling that inspects local Drupal repositories and verifies project standards.
  • QA — a Drupal module exposing Drush commands that inspect Drupal configuration and runtime state from inside the application.
  • Watchtower — a centralized Symfony application that monitors deployed sites, records audit results, and tracks changes over time.

The key architectural distinction is the source of truth for each check.

Repository, Drupal, or Browser?

A useful rule is:

Core asks: Is the repository built according to our standards?

QA asks: Is Drupal configured according to our standards?

Watchtower asks: Does the deployed site actually behave correctly?

This distinction becomes clearer with some practical examples.

Favicon validation

A favicon can technically exist in the repository while still failing in production.

Core may verify that expected favicon assets exist, but the most meaningful validation belongs in Watchtower.

Watchtower should:

  1. Inspect the HTML for <link rel="icon"> and related declarations.
  2. Resolve the favicon URL.
  3. Request the resource.
  4. Verify that it returns a successful HTTP response.
  5. Verify that the response has an appropriate image MIME type.

Checking /favicon.ico can also be useful as a compatibility fallback.

A missing favicon often appears as a failed browser network request, but the failed request itself should be considered evidence rather than the primary criterion.

Browser console errors

JavaScript errors are fundamentally browser-runtime issues, so they belong in Watchtower.

Using a browser automation engine such as Playwright, Watchtower could open representative pages and collect:

  • uncaught JavaScript exceptions;
  • console.error messages;
  • failed network requests;
  • CSP errors;
  • CORS errors;
  • mixed-content warnings.

These findings should remain separate rather than being combined into one generic "browser error" status.

For example:

Browser Audit ├── JavaScript exceptions ├── Console errors ├── Failed network requests ├── CSP violations ├── CORS problems └── Mixed-content errors

This could eventually become a general-purpose frontend audit layer inside Watchtower.

Drupal 404 and 403 pages

Error-page testing illustrates well why QA and Watchtower should complement each other.

QA

The Drupal QA module should inspect Drupal configuration and verify values such as:

system.site:page.403 system.site:page.404

It can determine whether custom error pages have been configured and whether their referenced paths appear valid.

Watchtower

Watchtower should verify actual external behavior.

For a 404 test it can request a path guaranteed not to exist:

/watchtower-this-page-must-not-exist

The audit should verify:

  • HTTP status is actually 404;
  • the page renders successfully;
  • the page is not blank;
  • the normal site theme/layout is present;
  • no PHP exception or debug output is exposed.

A particularly important check is ensuring that a custom 404 page does not return HTTP 200 OK.

Testing 403 pages

Testing 403 responses is less universal because Drupal projects do not necessarily expose the same anonymous forbidden URL.

The test should therefore be configurable per project:

error_pages:  404:    test_path: "/watchtower-this-page-must-not-exist"  403:    test_path: "/known-protected-path"

QA can verify that Drupal has a custom 403 page configured, while Watchtower verifies that visiting a protected resource actually produces the expected response.

Proposed responsibility model

The resulting architecture becomes:

                     Quality Criterion                            │         ┌──────────────────┼──────────────────┐         │                  │                  │    Repository          Drupal state      External behavior         │                  │                  │       CORE                 QA             WATCHTOWER         │                  │                  │ expected files      Drupal config       favicon loads project layout      module state        JS errors dependencies        error pages         failed resources conventions         internal checks     HTTP behavior                                         rendered pages

Some checks intentionally exist at more than one layer.

For example:

Favicon CORE └── favicon asset exists WATCHTOWER └── favicon actually loads

Or:

404 page QA └── Drupal custom 404 configured WATCHTOWER ├── unknown URL returns HTTP 404 └── custom page renders correctly

This duplication is useful because the tools answer different questions.

A repository can be correctly configured while production is broken.

Likewise, production may currently work even though the repository does not meet the project standard.

A possible Watchtower frontend category

These checks could form a dedicated Watchtower section:

Frontend ├── Favicon │   ├── declared │   ├── reachable │   └── valid content type │ ├── Browser Console │   ├── JS exceptions │   ├── console errors │   └── failed resources │ └── Error Handling    ├── 404 status correct    ├── custom 404 rendered    ├── 403 status correct    └── custom 403 rendered

The same layer could later include broken images, canonical URLs, redirects, cookie consent behavior, accessibility checks, security-header browser errors, and Core Web Vitals.

The important principle is to keep QA Drupal-aware and Watchtower browser-aware, while Core remains focused on the repository itself.