Skip to content

Instantly share code, notes, and snippets.

@mstrelan
Created November 21, 2022 02:11
Show Gist options
  • Save mstrelan/5b996393f5a9931bdbc67412abe6a632 to your computer and use it in GitHub Desktop.
Save mstrelan/5b996393f5a9931bdbc67412abe6a632 to your computer and use it in GitHub Desktop.
Fail tests if pages are not cacheable when they should be
<?php
declare(strict_types=1);
namespace Drupal\Tests\my_profile\Traits;
use Behat\Mink\Exception\UnsupportedDriverActionException;
use Drupal\Core\Url;
/**
* Fails tests if pages are not cacheable when they should be.
*/
trait ExpectsCacheableResponseTrait {
/**
* List of dynamic paths that are not cacheable..
*
* @var string[]
*/
private array $dynamicPaths = [
'/user/login',
'/big_pipe/no-js',
'/batch',
];
/**
* Option to pass to drupalGet to allow uncacheable responses.
*
* @todo use a constant in PHP 8.2
* @see https://php.watch/versions/8.2/constants-in-traits
*/
private string $uncacheableResponseOption = '_expect_uncacheable_response';
/**
* Retrieves a Drupal path without checking cacheability.
*/
protected function drupalGetUncacheablePage(string|Url $path, array $options = [], array $headers = []): string {
return $this->drupalGet($path, [$this->uncacheableResponseOption => TRUE] + $options, $headers);
}
/**
* Detects un-cacheable responses and fails if that is not allowed.
*/
private function detectUncacheableResponse(string|Url $path, array $options = []): void {
if (($options[$this->uncacheableResponseOption] ?? FALSE)) {
return;
}
if ($this->isDynamicPath($path)) {
return;
}
try {
if ($this->getSession()->getResponseHeader('X-Drupal-Dynamic-Cache') === 'UNCACHEABLE') {
$this->fail(sprintf('Found an un-cacheable response at path: %s. If your test visits dynamic pages (cache-lifetime of zero) use ::drupalGetUncacheablePage method instead.', $this->buildUrl($path, $options)));
}
}
catch (UnsupportedDriverActionException $e) {
// Javascript tests don't support reading response headers.
}
}
/**
* Determines if the current path is uncacheable.
*/
private function isDynamicPath(string|Url $path): bool {
$currentPath = $path instanceof Url ? $path->toString() : $path;
$currentUrl = $this->getSession()->getCurrentUrl();
foreach ($this->dynamicPaths as $dynamicPath) {
if (str_contains($currentUrl, $dynamicPath)) {
return TRUE;
}
if (str_contains($currentPath, $dynamicPath)) {
return TRUE;
}
}
return FALSE;
}
}
@mstrelan
Copy link
Author

Usage:

/**
 * {@inheritdoc}
 */
protected function drupalGet($path, array $options = [], array $headers = []): string {
  $content = parent::drupalGet($path, $options, $headers);
  $this->detectUncacheableResponse($path, $options);
  return $content;
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment