2016-04-20 09:56:34 -07:00
< ? php
namespace Drupal\Tests ;
use Behat\Mink\Driver\GoutteDriver ;
use Behat\Mink\Element\Element ;
use Behat\Mink\Mink ;
2017-04-13 15:53:35 +01:00
use Behat\Mink\Selector\SelectorsHandler ;
2016-04-20 09:56:34 -07:00
use Behat\Mink\Session ;
2016-08-03 13:22:33 -07:00
use Drupal\Component\Serialization\Json ;
2016-04-20 09:56:34 -07:00
use Drupal\Core\Database\Database ;
2017-04-13 15:53:35 +01:00
use Drupal\Core\Test\FunctionalTestSetupTrait ;
use Drupal\Core\Test\TestSetupTrait ;
use Drupal\Core\Utility\Error ;
2016-07-07 09:44:38 -07:00
use Drupal\FunctionalTests\AssertLegacyTrait ;
2018-11-23 12:29:20 +00:00
use Drupal\Tests\block\Traits\BlockCreationTrait ;
use Drupal\Tests\node\Traits\ContentTypeCreationTrait ;
use Drupal\Tests\node\Traits\NodeCreationTrait ;
use Drupal\Tests\user\Traits\UserCreationTrait ;
use GuzzleHttp\Cookie\CookieJar ;
use PHPUnit\Framework\TestCase ;
2017-04-13 15:53:35 +01:00
use Psr\Http\Message\RequestInterface ;
use Psr\Http\Message\ResponseInterface ;
2018-11-23 12:29:20 +00:00
use Symfony\Component\CssSelector\CssSelectorConverter ;
2016-04-20 09:56:34 -07:00
/**
* Provides a test case for functional Drupal tests .
*
* Tests extending BrowserTestBase must exist in the
* Drupal\Tests\yourmodule\Functional namespace and live in the
2016-07-07 09:44:38 -07:00
* modules / yourmodule / tests / src / Functional directory .
2016-04-20 09:56:34 -07:00
*
2018-11-23 12:29:20 +00:00
* Tests extending this base class should only translate text when testing
* translation functionality . For example , avoid wrapping test text with t ()
* or TranslatableMarkup () .
*
2016-04-20 09:56:34 -07:00
* @ ingroup testing
*/
2018-11-23 12:29:20 +00:00
abstract class BrowserTestBase extends TestCase {
2017-04-13 15:53:35 +01:00
use FunctionalTestSetupTrait ;
2018-11-23 12:29:20 +00:00
use UiHelperTrait {
FunctionalTestSetupTrait :: refreshVariables insteadof UiHelperTrait ;
}
2017-04-13 15:53:35 +01:00
use TestSetupTrait ;
2016-07-07 09:44:38 -07:00
use BlockCreationTrait {
placeBlock as drupalPlaceBlock ;
}
use AssertLegacyTrait ;
2016-04-20 09:56:34 -07:00
use RandomGeneratorTrait ;
2016-07-07 09:44:38 -07:00
use NodeCreationTrait {
getNodeByTitle as drupalGetNodeByTitle ;
createNode as drupalCreateNode ;
}
use ContentTypeCreationTrait {
createContentType as drupalCreateContentType ;
}
2016-10-06 15:16:20 -07:00
use ConfigTestTrait ;
2018-11-23 12:29:20 +00:00
use TestRequirementsTrait ;
2016-08-03 13:22:33 -07:00
use UserCreationTrait {
createRole as drupalCreateRole ;
createUser as drupalCreateUser ;
}
2017-04-13 15:53:35 +01:00
use XdebugRequestTrait ;
2018-11-23 12:29:20 +00:00
use PhpunitCompatibilityTrait ;
2016-04-20 09:56:34 -07:00
/**
* The database prefix of this test run .
*
* @ var string
*/
protected $databasePrefix ;
/**
* Time limit in seconds for the test .
*
* @ var int
*/
protected $timeLimit = 500 ;
/**
* The translation file directory for the test environment .
*
* This is set in BrowserTestBase :: prepareEnvironment () .
*
* @ var string
*/
protected $translationFilesDirectory ;
/**
* The config importer that can be used in a test .
*
* @ var \Drupal\Core\Config\ConfigImporter
*/
protected $configImporter ;
2016-10-06 15:16:20 -07:00
/**
* Modules to enable .
*
* The test runner will merge the $modules lists from this class , the class
* it extends , and so on up the class hierarchy . It is not necessary to
* include modules in your list that a parent class has already declared .
*
* @ var string []
*
* @ see \Drupal\Tests\BrowserTestBase :: installDrupal ()
*/
2017-01-04 16:50:53 -08:00
protected static $modules = [];
2016-10-06 15:16:20 -07:00
2016-04-20 09:56:34 -07:00
/**
* The profile to install as a basis for testing .
*
* @ var string
*/
protected $profile = 'testing' ;
/**
* An array of custom translations suitable for drupal_rewrite_settings () .
*
* @ var array
*/
protected $customTranslations ;
/*
* Mink class for the default driver to use .
*
2018-11-23 12:29:20 +00:00
* Should be a fully - qualified class name that implements
2016-04-20 09:56:34 -07:00
* Behat\Mink\Driver\DriverInterface .
*
* Value can be overridden using the environment variable MINK_DRIVER_CLASS .
*
2018-11-23 12:29:20 +00:00
* @ var string
2016-04-20 09:56:34 -07:00
*/
protected $minkDefaultDriverClass = GoutteDriver :: class ;
/*
* Mink default driver params .
*
* If it ' s an array its contents are used as constructor params when default
* Mink driver class is instantiated .
*
* Can be overridden using the environment variable MINK_DRIVER_ARGS . In this
* case that variable should be a JSON array , for example :
* '["firefox", null, "http://localhost:4444/wd/hub"]' .
*
*
* @ var array
*/
protected $minkDefaultDriverArgs ;
/**
* Mink session manager .
*
2016-12-07 12:19:38 -08:00
* This will not be initialized if there was an error during the test setup .
*
* @ var \Behat\Mink\Mink | null
2016-04-20 09:56:34 -07:00
*/
protected $mink ;
/**
* { @ inheritdoc }
*
* Browser tests are run in separate processes to prevent collisions between
* code that may be loaded by tests .
*/
protected $runTestInSeparateProcess = TRUE ;
/**
* { @ inheritdoc }
*/
protected $preserveGlobalState = FALSE ;
/**
2018-11-23 12:29:20 +00:00
* The base URL .
2016-04-20 09:56:34 -07:00
*
* @ var string
*/
2018-11-23 12:29:20 +00:00
protected $baseUrl ;
2016-04-20 09:56:34 -07:00
/**
2018-11-23 12:29:20 +00:00
* The original array of shutdown function callbacks .
2016-04-20 09:56:34 -07:00
*
2018-11-23 12:29:20 +00:00
* @ var array
2016-04-20 09:56:34 -07:00
*/
2018-11-23 12:29:20 +00:00
protected $originalShutdownCallbacks = [];
2016-04-20 09:56:34 -07:00
/**
2018-11-23 12:29:20 +00:00
* The app root .
2016-04-20 09:56:34 -07:00
*
* @ var string
*/
2018-11-23 12:29:20 +00:00
protected $root ;
2016-04-20 09:56:34 -07:00
/**
2018-11-23 12:29:20 +00:00
* The original container .
2016-04-20 09:56:34 -07:00
*
2018-11-23 12:29:20 +00:00
* Move this to \Drupal\Core\Test\FunctionalTestSetupTrait once TestBase no
* longer provides the same value .
2016-07-07 09:44:38 -07:00
*
2018-11-23 12:29:20 +00:00
* @ var \Symfony\Component\DependencyInjection\ContainerInterface
2016-07-07 09:44:38 -07:00
*/
2018-11-23 12:29:20 +00:00
protected $originalContainer ;
2016-07-07 09:44:38 -07:00
2017-07-03 16:47:07 +01:00
/**
2018-11-23 12:29:20 +00:00
* { @ inheritdoc }
2017-07-03 16:47:07 +01:00
*/
2018-11-23 12:29:20 +00:00
public function __construct ( $name = NULL , array $data = [], $dataName = '' ) {
parent :: __construct ( $name , $data , $dataName );
2017-07-03 16:47:07 +01:00
2018-11-23 12:29:20 +00:00
$this -> root = dirname ( dirname ( substr ( __DIR__ , 0 , - strlen ( __NAMESPACE__ ))));
}
2017-07-03 16:47:07 +01:00
2016-04-20 09:56:34 -07:00
/**
* Initializes Mink sessions .
*/
protected function initMink () {
$driver = $this -> getDefaultDriverInstance ();
if ( $driver instanceof GoutteDriver ) {
2017-02-02 16:28:38 -08:00
// Turn off curl timeout. Having a timeout is not a problem in a normal
2017-04-13 15:53:35 +01:00
// test running, but it is a problem when debugging. Also, disable SSL
// peer verification so that testing under HTTPS always works.
2017-02-02 16:28:38 -08:00
/** @var \GuzzleHttp\Client $client */
2017-04-13 15:53:35 +01:00
$client = $this -> container -> get ( 'http_client_factory' ) -> fromOptions ([
'timeout' => NULL ,
'verify' => FALSE ,
]);
// Inject a Guzzle middleware to generate debug output for every request
// performed in the test.
$handler_stack = $client -> getConfig ( 'handler' );
$handler_stack -> push ( $this -> getResponseLogHandler ());
2017-02-02 16:28:38 -08:00
$driver -> getClient () -> setClient ( $client );
2016-04-20 09:56:34 -07:00
}
2017-04-13 15:53:35 +01:00
$selectors_handler = new SelectorsHandler ([
2018-11-23 12:29:20 +00:00
'hidden_field_selector' => new HiddenFieldSelector (),
2017-04-13 15:53:35 +01:00
]);
$session = new Session ( $driver , $selectors_handler );
2016-04-20 09:56:34 -07:00
$this -> mink = new Mink ();
$this -> mink -> registerSession ( 'default' , $session );
$this -> mink -> setDefaultSessionName ( 'default' );
$this -> registerSessions ();
2018-11-23 12:29:20 +00:00
$this -> initFrontPage ();
2016-04-20 09:56:34 -07:00
2019-01-24 08:00:03 +00:00
// Copies cookies from the current environment, for example, XDEBUG_SESSION
// in order to support Xdebug.
// @see BrowserTestBase::initFrontPage()
$cookies = $this -> extractCookiesFromRequest ( \Drupal :: request ());
foreach ( $cookies as $cookie_name => $values ) {
foreach ( $values as $value ) {
$session -> setCookie ( $cookie_name , $value );
}
}
2016-04-20 09:56:34 -07:00
return $session ;
}
2018-11-23 12:29:20 +00:00
/**
* Visits the front page when initializing Mink .
*
* According to the W3C WebDriver specification a cookie can only be set if
* the cookie domain is equal to the domain of the active document . When the
* browser starts up the active document is not our domain but 'about:blank'
* or similar . To be able to set our User - Agent and Xdebug cookies at the
* start of the test we now do a request to the front page so the active
* document matches the domain .
*
* @ see https :// w3c . github . io / webdriver / webdriver - spec . html #add-cookie
* @ see https :// www . w3 . org / Bugs / Public / show_bug . cgi ? id = 20975
*/
protected function initFrontPage () {
$session = $this -> getSession ();
$session -> visit ( $this -> baseUrl );
}
2016-04-20 09:56:34 -07:00
/**
* Gets an instance of the default Mink driver .
*
* @ return Behat\Mink\Driver\DriverInterface
* Instance of default Mink driver .
*
* @ throws \InvalidArgumentException
* When provided default Mink driver class can ' t be instantiated .
*/
protected function getDefaultDriverInstance () {
2018-11-23 12:29:20 +00:00
// Get default driver params from environment if available.
if ( $arg_json = $this -> getMinkDriverArgs ()) {
$this -> minkDefaultDriverArgs = json_decode ( $arg_json , TRUE );
2016-04-20 09:56:34 -07:00
}
2018-11-23 12:29:20 +00:00
// Get and check default driver class from environment if available.
2016-04-20 09:56:34 -07:00
if ( $minkDriverClass = getenv ( 'MINK_DRIVER_CLASS' )) {
if ( class_exists ( $minkDriverClass )) {
$this -> minkDefaultDriverClass = $minkDriverClass ;
}
else {
throw new \InvalidArgumentException ( " Can't instantiate provided $minkDriverClass class by environment as default driver class. " );
}
}
if ( is_array ( $this -> minkDefaultDriverArgs )) {
2016-10-06 15:16:20 -07:00
// Use ReflectionClass to instantiate class with received params.
2016-04-20 09:56:34 -07:00
$reflector = new \ReflectionClass ( $this -> minkDefaultDriverClass );
$driver = $reflector -> newInstanceArgs ( $this -> minkDefaultDriverArgs );
}
else {
2016-05-04 14:35:41 -07:00
$driver = new $this -> minkDefaultDriverClass ();
2016-04-20 09:56:34 -07:00
}
return $driver ;
}
2018-11-23 12:29:20 +00:00
/**
* Get the Mink driver args from an environment variable , if it is set . Can
* be overridden in a derived class so it is possible to use a different
* value for a subset of tests , e . g . the JavaScript tests .
*
* @ return string | false
* The JSON - encoded argument string . False if it is not set .
*/
protected function getMinkDriverArgs () {
return getenv ( 'MINK_DRIVER_ARGS' );
}
2017-04-13 15:53:35 +01:00
/**
* Provides a Guzzle middleware handler to log every response received .
*
* @ return callable
* The callable handler that will do the logging .
*/
protected function getResponseLogHandler () {
return function ( callable $handler ) {
return function ( RequestInterface $request , array $options ) use ( $handler ) {
return $handler ( $request , $options )
-> then ( function ( ResponseInterface $response ) use ( $request ) {
if ( $this -> htmlOutputEnabled ) {
$caller = $this -> getTestMethodCaller ();
$html_output = 'Called from ' . $caller [ 'function' ] . ' line ' . $caller [ 'line' ];
$html_output .= '<hr />' . $request -> getMethod () . ' request to: ' . $request -> getUri ();
// On redirect responses (status code starting with '3') we need
// to remove the meta tag that would do a browser refresh. We
// don't want to redirect developers away when they look at the
// debug output file in their browser.
$body = $response -> getBody ();
$status_code = ( string ) $response -> getStatusCode ();
if ( $status_code [ 0 ] === '3' ) {
$body = preg_replace ( '#<meta http-equiv="refresh" content=.+/>#' , '' , $body , 1 );
}
$html_output .= '<hr />' . $body ;
$html_output .= $this -> formatHtmlOutputHeaders ( $response -> getHeaders ());
$this -> htmlOutput ( $html_output );
}
return $response ;
});
};
};
}
2016-04-20 09:56:34 -07:00
/**
* Registers additional Mink sessions .
*
* Tests wishing to use a different driver or change the default driver should
* override this method .
*
* @ code
* // Register a new session that uses the MinkPonyDriver.
* $pony = new MinkPonyDriver ();
* $session = new Session ( $pony );
* $this -> mink -> registerSession ( 'pony' , $session );
* @ endcode
*/
protected function registerSessions () {}
/**
* { @ inheritdoc }
*/
protected function setUp () {
2018-11-23 12:29:20 +00:00
// Installing Drupal creates 1000s of objects. Garbage collection of these
// objects is expensive. This appears to be causing random segmentation
// faults in PHP 5.x due to https://bugs.php.net/bug.php?id=72286. Once
// Drupal is installed is rebuilt, garbage collection is re-enabled.
$disable_gc = version_compare ( PHP_VERSION , '7' , '<' ) && gc_enabled ();
if ( $disable_gc ) {
gc_collect_cycles ();
gc_disable ();
2016-04-20 09:56:34 -07:00
}
2018-11-23 12:29:20 +00:00
parent :: setUp ();
2016-04-20 09:56:34 -07:00
2018-11-23 12:29:20 +00:00
$this -> setupBaseUrl ();
2016-04-20 09:56:34 -07:00
// Install Drupal test site.
$this -> prepareEnvironment ();
$this -> installDrupal ();
// Setup Mink.
2018-11-23 12:29:20 +00:00
$this -> initMink ();
2016-04-20 09:56:34 -07:00
2018-11-23 12:29:20 +00:00
// Set up the browser test output file.
$this -> initBrowserOutputFile ();
// If garbage collection was disabled prior to rebuilding container,
// re-enable it.
if ( $disable_gc ) {
gc_enable ();
2016-04-20 09:56:34 -07:00
}
2018-11-23 12:29:20 +00:00
// Ensure that the test is not marked as risky because of no assertions. In
// PHPUnit 6 tests that only make assertions using $this->assertSession()
// can be marked as risky.
$this -> addToAssertionCount ( 1 );
2016-04-20 09:56:34 -07:00
}
/**
* Ensures test files are deletable within file_unmanaged_delete_recursive () .
*
* Some tests chmod generated files to be read only . During
* BrowserTestBase :: cleanupEnvironment () and other cleanup operations ,
* these files need to get deleted too .
*
* @ param string $path
* The file path .
*/
public static function filePreDeleteCallback ( $path ) {
2016-11-02 11:43:31 -07:00
// When the webserver runs with the same system user as phpunit, we can
// make read-only files writable again. If not, chmod will fail while the
// file deletion still works if file permissions have been configured
// correctly. Thus, we ignore any problems while running chmod.
@ chmod ( $path , 0700 );
2016-04-20 09:56:34 -07:00
}
/**
* Clean up the Simpletest environment .
*/
protected function cleanupEnvironment () {
// Remove all prefixed tables.
$original_connection_info = Database :: getConnectionInfo ( 'simpletest_original_default' );
$original_prefix = $original_connection_info [ 'default' ][ 'prefix' ][ 'default' ];
$test_connection_info = Database :: getConnectionInfo ( 'default' );
$test_prefix = $test_connection_info [ 'default' ][ 'prefix' ][ 'default' ];
if ( $original_prefix != $test_prefix ) {
$tables = Database :: getConnection () -> schema () -> findTables ( '%' );
foreach ( $tables as $table ) {
if ( Database :: getConnection () -> schema () -> dropTable ( $table )) {
unset ( $tables [ $table ]);
}
}
}
// Delete test site directory.
2017-04-13 15:53:35 +01:00
file_unmanaged_delete_recursive ( $this -> siteDirectory , [ $this , 'filePreDeleteCallback' ]);
2016-04-20 09:56:34 -07:00
}
/**
* { @ inheritdoc }
*/
protected function tearDown () {
parent :: tearDown ();
// Destroy the testing kernel.
if ( isset ( $this -> kernel )) {
$this -> cleanupEnvironment ();
$this -> kernel -> shutdown ();
}
// Ensure that internal logged in variable is reset.
$this -> loggedInUser = FALSE ;
if ( $this -> mink ) {
$this -> mink -> stopSessions ();
}
2016-07-07 09:44:38 -07:00
// Restore original shutdown callbacks.
if ( function_exists ( 'drupal_register_shutdown_function' )) {
$callbacks = & drupal_register_shutdown_function ();
$callbacks = $this -> originalShutdownCallbacks ;
}
2016-04-20 09:56:34 -07:00
}
/**
* Returns Mink session .
*
* @ param string $name
* ( optional ) Name of the session . Defaults to the active session .
*
* @ return \Behat\Mink\Session
* The active Mink session object .
*/
public function getSession ( $name = NULL ) {
return $this -> mink -> getSession ( $name );
}
/**
2018-11-23 12:29:20 +00:00
* Get session cookies from current session .
2016-04-20 09:56:34 -07:00
*
2018-11-23 12:29:20 +00:00
* @ return \GuzzleHttp\Cookie\CookieJar
* A cookie jar with the current session .
2016-04-20 09:56:34 -07:00
*/
2018-11-23 12:29:20 +00:00
protected function getSessionCookies () {
$domain = parse_url ( $this -> getUrl (), PHP_URL_HOST );
$session_id = $this -> getSession () -> getCookie ( $this -> getSessionName ());
$cookies = CookieJar :: fromArray ([ $this -> getSessionName () => $session_id ], $domain );
2016-04-20 09:56:34 -07:00
2018-11-23 12:29:20 +00:00
return $cookies ;
2016-04-20 09:56:34 -07:00
}
/**
2018-11-23 12:29:20 +00:00
* Obtain the HTTP client for the system under test .
2016-04-20 09:56:34 -07:00
*
2018-11-23 12:29:20 +00:00
* Use this method for arbitrary HTTP requests to the site under test . For
* most tests , you should not get the HTTP client and instead use navigation
* methods such as drupalGet () and clickLink () in order to benefit from
* assertions .
2016-04-20 09:56:34 -07:00
*
2018-11-23 12:29:20 +00:00
* Subclasses which substitute a different Mink driver should override this
* method and provide a Guzzle client if the Mink driver provides one .
2016-07-07 09:44:38 -07:00
*
2018-11-23 12:29:20 +00:00
* @ return \GuzzleHttp\ClientInterface
* The client with BrowserTestBase configuration .
2016-04-20 09:56:34 -07:00
*
2018-11-23 12:29:20 +00:00
* @ throws \RuntimeException
* If the Mink driver does not support a Guzzle HTTP client , throw an
* exception .
2016-04-20 09:56:34 -07:00
*/
2018-11-23 12:29:20 +00:00
protected function getHttpClient () {
/* @var $mink_driver \Behat\Mink\Driver\DriverInterface */
$mink_driver = $this -> getSession () -> getDriver ();
if ( $mink_driver instanceof GoutteDriver ) {
return $mink_driver -> getClient () -> getClient ();
2016-04-20 09:56:34 -07:00
}
2018-11-23 12:29:20 +00:00
throw new \RuntimeException ( 'The Mink client type ' . get_class ( $mink_driver ) . ' does not support getHttpClient().' );
2016-07-07 09:44:38 -07:00
}
2016-04-20 09:56:34 -07:00
/**
* Helper function to get the options of select field .
*
* @ param \Behat\Mink\Element\NodeElement | string $select
* Name , ID , or Label of select field to assert .
* @ param \Behat\Mink\Element\Element $container
* ( optional ) Container element to check against . Defaults to current page .
*
* @ return array
* Associative array of option keys and values .
*/
protected function getOptions ( $select , Element $container = NULL ) {
if ( is_string ( $select )) {
$select = $this -> assertSession () -> selectExists ( $select , $container );
}
$options = [];
/* @var \Behat\Mink\Element\NodeElement $option */
foreach ( $select -> findAll ( 'xpath' , '//option' ) as $option ) {
$label = $option -> getText ();
$value = $option -> getAttribute ( 'value' ) ? : $label ;
$options [ $value ] = $label ;
}
return $options ;
}
/**
* Installs Drupal into the Simpletest site .
*/
public function installDrupal () {
2017-04-13 15:53:35 +01:00
$this -> initUserSession ();
$this -> prepareSettings ();
$this -> doInstall ();
$this -> initSettings ();
$container = $this -> initKernel ( \Drupal :: request ());
$this -> initConfig ( $container );
$this -> installModulesFromClassProperty ( $container );
$this -> rebuildAll ();
2016-04-20 09:56:34 -07:00
}
/**
* Prevents serializing any properties .
*
* Browser tests are run in a separate process . To do this PHPUnit creates a
* script to run the test . If it fails , the test result object will contain a
* stack trace which includes the test object . It will attempt to serialize
* it . Returning an empty array prevents it from serializing anything it
* should not .
*
* @ return array
* An empty array .
*
* @ see vendor / phpunit / phpunit / src / Util / PHP / Template / TestCaseMethod . tpl . dist
*/
public function __sleep () {
return [];
}
2016-06-02 15:56:09 -07:00
/**
* Translates a CSS expression to its XPath equivalent .
*
* The search is relative to the root element ( HTML tag normally ) of the page .
*
* @ param string $selector
* CSS selector to use in the search .
* @ param bool $html
* ( optional ) Enables HTML support . Disable it for XML documents .
* @ param string $prefix
* ( optional ) The prefix for the XPath expression .
*
* @ return string
* The equivalent XPath of a CSS expression .
*/
protected function cssSelectToXpath ( $selector , $html = TRUE , $prefix = 'descendant-or-self::' ) {
return ( new CssSelectorConverter ( $html )) -> toXPath ( $selector , $prefix );
}
2016-07-07 09:44:38 -07:00
/**
* Performs an xpath search on the contents of the internal browser .
*
* The search is relative to the root element ( HTML tag normally ) of the page .
*
* @ param string $xpath
* The xpath string to use in the search .
* @ param array $arguments
* An array of arguments with keys in the form ':name' matching the
* placeholders in the query . The values may be either strings or numeric
* values .
*
* @ return \Behat\Mink\Element\NodeElement []
* The list of elements matching the xpath expression .
*/
protected function xpath ( $xpath , array $arguments = []) {
$xpath = $this -> assertSession () -> buildXPathQuery ( $xpath , $arguments );
return $this -> getSession () -> getPage () -> findAll ( 'xpath' , $xpath );
}
/**
* Configuration accessor for tests . Returns non - overridden configuration .
*
* @ param string $name
* Configuration name .
*
* @ return \Drupal\Core\Config\Config
* The configuration object with original configuration data .
*/
protected function config ( $name ) {
return $this -> container -> get ( 'config.factory' ) -> getEditable ( $name );
}
2017-04-13 15:53:35 +01:00
/**
* Returns all response headers .
*
* @ return array
* The HTTP headers values .
*
* @ deprecated Scheduled for removal in Drupal 9.0 . 0.
* Use $this -> getSession () -> getResponseHeaders () instead .
*/
protected function drupalGetHeaders () {
return $this -> getSession () -> getResponseHeaders ();
}
2016-07-07 09:44:38 -07:00
/**
* Gets the value of an HTTP response header .
*
* If multiple requests were required to retrieve the page , only the headers
* from the last request will be checked by default .
*
* @ param string $name
* The name of the header to retrieve . Names are case - insensitive ( see RFC
* 2616 section 4.2 ) .
*
* @ return string | null
* The HTTP header value or NULL if not found .
*/
protected function drupalGetHeader ( $name ) {
return $this -> getSession () -> getResponseHeader ( $name );
}
2016-08-03 13:22:33 -07:00
/**
* Gets the JavaScript drupalSettings variable for the currently - loaded page .
*
* @ return array
* The JSON decoded drupalSettings value from the current page .
*/
protected function getDrupalSettings () {
2019-01-24 08:00:03 +00:00
$html = $this -> getSession () -> getPage () -> getContent ();
2016-08-03 13:22:33 -07:00
if ( preg_match ( '@<script type="application/json" data-drupal-selector="drupal-settings-json">([^<]*)</script>@' , $html , $matches )) {
return Json :: decode ( $matches [ 1 ]);
}
return [];
}
2016-07-07 09:44:38 -07:00
/**
* { @ inheritdoc }
*/
public static function assertEquals ( $expected , $actual , $message = '' , $delta = 0.0 , $maxDepth = 10 , $canonicalize = FALSE , $ignoreCase = FALSE ) {
// Cast objects implementing MarkupInterface to string instead of
// relying on PHP casting them to string depending on what they are being
// comparing with.
$expected = static :: castSafeStrings ( $expected );
$actual = static :: castSafeStrings ( $actual );
parent :: assertEquals ( $expected , $actual , $message , $delta , $maxDepth , $canonicalize , $ignoreCase );
}
/**
2017-04-13 15:53:35 +01:00
* Retrieves the current calling line in the class under test .
2016-07-07 09:44:38 -07:00
*
2017-04-13 15:53:35 +01:00
* @ return array
* An associative array with keys 'file' , 'line' and 'function' .
*/
protected function getTestMethodCaller () {
$backtrace = debug_backtrace ();
// Find the test class that has the test method.
while ( $caller = Error :: getLastCaller ( $backtrace )) {
if ( isset ( $caller [ 'class' ]) && $caller [ 'class' ] === get_class ( $this )) {
break ;
2016-10-06 15:16:20 -07:00
}
2017-04-13 15:53:35 +01:00
// If the test method is implemented by a test class's parent then the
// class name of $this will not be part of the backtrace.
// In that case we process the backtrace until the caller is not a
// subclass of $this and return the previous caller.
if ( isset ( $last_caller ) && ( ! isset ( $caller [ 'class' ]) || ! is_subclass_of ( $this , $caller [ 'class' ]))) {
// Return the last caller since that has to be the test class.
$caller = $last_caller ;
break ;
}
// Otherwise we have not reached our test class yet: save the last caller
// and remove an element from to backtrace to process the next call.
$last_caller = $caller ;
array_shift ( $backtrace );
2016-10-06 15:16:20 -07:00
}
2017-04-13 15:53:35 +01:00
return $caller ;
2016-10-06 15:16:20 -07:00
}
2017-07-03 16:47:07 +01:00
/**
2018-11-23 12:29:20 +00:00
* Transforms a nested array into a flat array suitable for drupalPostForm () .
2017-07-03 16:47:07 +01:00
*
2018-11-23 12:29:20 +00:00
* @ param array $values
* A multi - dimensional form values array to convert .
2017-07-03 16:47:07 +01:00
*
2018-11-23 12:29:20 +00:00
* @ return array
* The flattened $edit array suitable for BrowserTestBase :: drupalPostForm () .
*/
protected function translatePostValues ( array $values ) {
$edit = [];
// The easiest and most straightforward way to translate values suitable for
// BrowserTestBase::drupalPostForm() is to actually build the POST data
// string and convert the resulting key/value pairs back into a flat array.
$query = http_build_query ( $values );
foreach ( explode ( '&' , $query ) as $item ) {
list ( $key , $value ) = explode ( '=' , $item );
$edit [ urldecode ( $key )] = urldecode ( $value );
}
return $edit ;
2017-07-03 16:47:07 +01:00
}
2016-04-20 09:56:34 -07:00
}