Skip to content
Snippets Groups Projects
Unverified Commit 4babf62c authored by Alex Pott's avatar Alex Pott
Browse files

Issue #3077785 by longwave: Refactor away DrupalMinkClient after Symfony 4.2+

parent d02bccbf
Branches
Tags
8 merge requests!7452Issue #1797438. HTML5 validation is preventing form submit and not fully...,!1012Issue #3226887: Hreflang on non-canonical content pages,!789Issue #3210310: Adjust Database API to remove deprecated Drupal 9 code in Drupal 10,!596Issue #3046532: deleting an entity reference field, used in a contextual view, makes the whole site unrecoverable,!496Issue #2463967: Use .user.ini file for PHP settings,!144Issue #2666286: Clean up menu_ui to conform to Drupal coding standards,!16Draft: Resolve #2081585 "History storage",!13Resolve #2903456
......@@ -9,7 +9,6 @@
use Drupal\Component\FileSystem\FileSystem as DrupalFilesystem;
use Drupal\Tests\Traits\PHPUnit8Warnings;
use PHPUnit\Framework\TestCase;
use Symfony\Component\BrowserKit\Client as SymfonyClient;
use Symfony\Component\Filesystem\Filesystem as SymfonyFilesystem;
use Symfony\Component\Finder\Finder;
use Symfony\Component\Lock\Factory;
......@@ -218,14 +217,7 @@ protected function getWorkingPath($working_dir = NULL) {
* @return \Behat\Mink\Session
*/
protected function initMink() {
// If the Symfony BrowserKit client can followMetaRefresh(), we should use
// the Goutte descendent instead of ours.
if (method_exists(SymfonyClient::class, 'followMetaRefresh')) {
$client = new Client();
}
else {
$client = new DrupalMinkClient();
}
$client = new Client();
$client->followMetaRefresh(TRUE);
$driver = new GoutteDriver($client);
$session = new Session($driver);
......
<?php
namespace Drupal\BuildTests\Framework;
use Behat\Mink\Driver\Goutte\Client;
use Symfony\Component\BrowserKit\Client as SymfonyClient;
/**
* Extend the Mink client for Drupal use-cases.
*
* This is adapted from https://github.com/symfony/symfony/pull/27118.
*
* @todo Update this client when Drupal starts using Symfony 4.2.0+.
* https://www.drupal.org/project/drupal/issues/3077785
*/
class DrupalMinkClient extends Client {
/**
* Whether to follow meta redirects or not.
*
* @var bool
*
* @see \Drupal\BuildTests\Framework\DrupalMinkClient::followMetaRefresh()
*/
protected $followMetaRefresh;
/**
* Sets whether to automatically follow meta refresh redirects or not.
*
* @param bool $followMetaRefresh
* (optional) Whether to follow meta redirects. Defaults to TRUE.
*/
public function followMetaRefresh(bool $followMetaRefresh = TRUE) {
$this->followMetaRefresh = $followMetaRefresh;
}
/**
* Glean the meta refresh URL from the current page content.
*
* @return string|null
* Either the redirect URL that was found, or NULL if none was found.
*/
private function getMetaRefreshUrl() {
$metaRefresh = $this->getCrawler()->filter('meta[http-equiv="Refresh"], meta[http-equiv="refresh"]');
foreach ($metaRefresh->extract(['content']) as $content) {
if (preg_match('/^\s*0\s*;\s*URL\s*=\s*(?|\'([^\']++)|"([^"]++)|([^\'"].*))/i', $content, $m)) {
return str_replace("\t\r\n", '', rtrim($m[1]));
}
}
return NULL;
}
/**
* {@inheritdoc}
*/
public function request($method, $uri, array $parameters = [], array $files = [], array $server = [], $content = NULL, $changeHistory = TRUE) {
$this->crawler = parent::request($method, $uri, $parameters, $files, $server, $content, $changeHistory);
// Check for meta refresh redirect and follow it.
if ($this->followMetaRefresh && NULL !== $redirect = $this->getMetaRefreshUrl()) {
$this->redirect = $redirect;
// $this->redirects is private on the BrowserKit client, so we have to use
// reflection to manage the redirects stack.
$ref_redirects = new \ReflectionProperty(SymfonyClient::class, 'redirects');
$ref_redirects->setAccessible(TRUE);
$redirects = $ref_redirects->getValue($this);
$redirects[serialize($this->history->current())] = TRUE;
$ref_redirects->setValue($this, $redirects);
$this->crawler = $this->followRedirect();
}
return $this->crawler;
}
}
<?php
namespace Drupal\BuildTests\Framework\Tests;
use Drupal\BuildTests\Framework\DrupalMinkClient;
use PHPUnit\Framework\TestCase;
use Symfony\Component\BrowserKit\Response;
/**
* Test \Drupal\BuildTests\Framework\DrupalMinkClient.
*
* This test is adapted from \Symfony\Component\BrowserKit\Tests\ClientTest.
*
* @coversDefaultClass \Drupal\BuildTests\Framework\DrupalMinkClient
*
* @group Build
*/
class DrupalMinkClientTest extends TestCase {
/**
* @dataProvider getTestsForMetaRefresh
* @covers ::getMetaRefreshUrl
*/
public function testFollowMetaRefresh(string $content, string $expectedEndingUrl, bool $followMetaRefresh = TRUE) {
$client = new TestClient();
$client->followMetaRefresh($followMetaRefresh);
$client->setNextResponse(new Response($content));
$client->request('GET', 'http://www.example.com/foo/foobar');
$this->assertEquals($expectedEndingUrl, $client->getRequest()->getUri());
}
public function getTestsForMetaRefresh() {
return [
['<html><head><meta http-equiv="Refresh" content="4" /><meta http-equiv="refresh" content="0; URL=http://www.example.com/redirected"/></head></html>', 'http://www.example.com/redirected'],
['<html><head><meta http-equiv="refresh" content="0;URL=http://www.example.com/redirected"/></head></html>', 'http://www.example.com/redirected'],
['<html><head><meta http-equiv="refresh" content="0;URL=\'http://www.example.com/redirected\'"/></head></html>', 'http://www.example.com/redirected'],
['<html><head><meta http-equiv="refresh" content=\'0;URL="http://www.example.com/redirected"\'/></head></html>', 'http://www.example.com/redirected'],
['<html><head><meta http-equiv="refresh" content="0; URL = http://www.example.com/redirected"/></head></html>', 'http://www.example.com/redirected'],
['<html><head><meta http-equiv="refresh" content="0;URL= http://www.example.com/redirected "/></head></html>', 'http://www.example.com/redirected'],
['<html><head><meta http-equiv="refresh" content="0;url=http://www.example.com/redirected "/></head></html>', 'http://www.example.com/redirected'],
['<html><head><noscript><meta http-equiv="refresh" content="0;URL=http://www.example.com/redirected"/></noscript></head></head></html>', 'http://www.example.com/redirected'],
// Non-zero timeout should not result in a redirect.
['<html><head><meta http-equiv="refresh" content="4; URL=http://www.example.com/redirected"/></head></html>', 'http://www.example.com/foo/foobar'],
['<html><body></body></html>', 'http://www.example.com/foo/foobar'],
// HTML 5 allows the meta tag to be placed in head or body.
['<html><body><meta http-equiv="refresh" content="0;url=http://www.example.com/redirected"/></body></html>', 'http://www.example.com/redirected'],
// Valid meta refresh should not be followed if disabled.
['<html><head><meta http-equiv="refresh" content="0;URL=http://www.example.com/redirected"/></head></html>', 'http://www.example.com/foo/foobar', FALSE],
'drupal-1' => ['<html><head><meta http-equiv="Refresh" content="0; URL=/update.php/start?id=2&op=do_nojs" /></body></html>', 'http://www.example.com/update.php/start?id=2&op=do_nojs'],
'drupal-2' => ['<html><head><noscript><meta http-equiv="Refresh" content="0; URL=/update.php/start?id=2&op=do_nojs" /></noscript></body></html>', 'http://www.example.com/update.php/start?id=2&op=do_nojs'],
];
}
/**
* @covers ::request
*/
public function testBackForwardMetaRefresh() {
$client = new TestClient();
$client->followMetaRefresh();
// First request.
$client->request('GET', 'http://www.example.com/first-page');
$content = '<html><head><meta http-equiv="Refresh" content="0; URL=/refreshed" /></body></html>';
$client->setNextResponse(new Response($content, 200));
$client->request('GET', 'http://www.example.com/refresh-from-here');
$this->assertEquals('http://www.example.com/refreshed', $client->getRequest()->getUri());
$client->back();
$this->assertEquals('http://www.example.com/first-page', $client->getRequest()->getUri());
$client->forward();
$this->assertEquals('http://www.example.com/refreshed', $client->getRequest()->getUri());
}
}
/**
* Special client that can return a given response on the first doRequest().
*/
class TestClient extends DrupalMinkClient {
protected $nextResponse = NULL;
public function setNextResponse(Response $response) {
$this->nextResponse = $response;
}
protected function doRequest($request) {
if (NULL === $this->nextResponse) {
return new Response();
}
$response = $this->nextResponse;
$this->nextResponse = NULL;
return $response;
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment