Update to Drupal 8.1.5. For more information, see https://www.drupal.org/project/drupal/releases/8.1.5

This commit is contained in:
Pantheon Automation 2016-07-07 09:44:38 -07:00 committed by Greg Anderson
parent 13b6ca7cc2
commit 38ba7c357d
342 changed files with 7814 additions and 1534 deletions

View file

@ -0,0 +1,31 @@
<?php
namespace Drupal\FunctionalJavascriptTests;
use Drupal\Tests\WebAssert;
/**
* Defines a class with methods for asserting presence of elements during tests.
*/
class JSWebAssert extends WebAssert {
/**
* Waits for AJAX request to be completed.
*
* @param int $timeout
* (Optional) Timeout in milliseconds, defaults to 10000.
* @param string $message
* (optional) A message for exception.
*
* @throws \RuntimeException
* When the request is not completed. If left blank, a default message will
* be displayed.
*/
public function assertWaitOnAjaxRequest($timeout = 10000, $message = 'Unable to complete AJAX request.') {
$result = $this->session->wait($timeout, '(typeof(jQuery)=="undefined" || (0 === jQuery.active && 0 === jQuery(\':animated\').length))');
if (!$result) {
throw new \RuntimeException($message);
}
}
}

View file

@ -33,6 +33,24 @@ abstract class JavascriptTestBase extends BrowserTestBase {
return parent::initMink();
}
/**
* {@inheritdoc}
*/
protected function tearDown() {
// Wait for all requests to finish. It is possible that an AJAX request is
// still on-going.
$result = $this->getSession()->wait(5000, '(typeof(jQuery)=="undefined" || (0 === jQuery.active && 0 === jQuery(\':animated\').length))');
if (!$result) {
// If the wait is unsuccessful, there may still be an AJAX request in
// progress. If we tear down now, then this AJAX request may fail with
// missing database tables, because tear down will have removed them. Rather
// than allow it to fail, throw an explicit exception now explaining what
// the problem is.
throw new \RuntimeException('Unfinished AJAX requests whilst tearing down a test');
}
parent::tearDown();
}
/**
* Asserts that the element with the given CSS selector is visible.
*
@ -84,4 +102,11 @@ abstract class JavascriptTestBase extends BrowserTestBase {
$this->assertTrue($result, $message);
}
/**
* {@inheritdoc}
*/
public function assertSession($name = NULL) {
return new JSWebAssert($this->getSession($name), $this->baseUrl);
}
}

View file

@ -0,0 +1,427 @@
<?php
namespace Drupal\FunctionalTests;
use Drupal\KernelTests\AssertLegacyTrait as BaseAssertLegacyTrait;
/**
* Provides convenience methods for assertions in browser tests.
*
* @deprecated Scheduled for removal in Drupal 9.0.0. Use the methods on
* \Drupal\Tests\WebAssert instead, for example
* @code
* $this->assertSession()->statusCodeEquals(200);
* @endcode
*/
trait AssertLegacyTrait {
use BaseAssertLegacyTrait;
/**
* Asserts that the element with the given CSS selector is present.
*
* @param string $css_selector
* The CSS selector identifying the element to check.
*
* @deprecated Scheduled for removal in Drupal 9.0.0.
* Use $this->assertSession()->elementExists() instead.
*/
protected function assertElementPresent($css_selector) {
$this->assertSession()->elementExists('css', $css_selector);
}
/**
* Asserts that the element with the given CSS selector is not present.
*
* @param string $css_selector
* The CSS selector identifying the element to check.
*
* @deprecated Scheduled for removal in Drupal 9.0.0.
* Use $this->assertSession()->elementNotExists() instead.
*/
protected function assertElementNotPresent($css_selector) {
$this->assertSession()->elementNotExists('css', $css_selector);
}
/**
* Passes if the page (with HTML stripped) contains the text.
*
* Note that stripping HTML tags also removes their attributes, such as
* the values of text fields.
*
* @param string $text
* Plain text to look for.
*
* @deprecated Scheduled for removal in Drupal 9.0.0.
* Use $this->assertSession()->pageTextContains() or
* $this->assertSession()->responseContains() instead.
*/
protected function assertText($text) {
$content_type = $this->getSession()->getResponseHeader('Content-type');
// In case of a Non-HTML response (example: XML) check the original
// response.
if (strpos($content_type, 'html') === FALSE) {
$this->assertSession()->responseContains($text);
}
else {
$this->assertSession()->pageTextContains($text);
}
}
/**
* Passes if the page (with HTML stripped) does not contains the text.
*
* Note that stripping HTML tags also removes their attributes, such as
* the values of text fields.
*
* @param string $text
* Plain text to look for.
*
* @deprecated Scheduled for removal in Drupal 9.0.0.
* Use $this->assertSession()->pageTextNotContains() or
* $this->assertSession()->responseNotContains() instead.
*/
protected function assertNoText($text) {
$content_type = $this->getSession()->getResponseHeader('Content-type');
// In case of a Non-HTML response (example: XML) check the original
// response.
if (strpos($content_type, 'html') === FALSE) {
$this->assertSession()->responseNotContains($text);
}
else {
$this->assertSession()->pageTextNotContains($text);
}
}
/**
* Asserts the page responds with the specified response code.
*
* @param int $code
* Response code. For example 200 is a successful page request. For a list
* of all codes see http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html.
*
* @deprecated Scheduled for removal in Drupal 9.0.0.
* Use $this->assertSession()->statusCodeEquals() instead.
*/
protected function assertResponse($code) {
$this->assertSession()->statusCodeEquals($code);
}
/**
* Asserts that a field exists with the given name and value.
*
* @param string $name
* Name of field to assert.
* @param string $value
* (optional) Value of the field to assert. You may pass in NULL (default)
* to skip checking the actual value, while still checking that the field
* exists.
*
* @deprecated Scheduled for removal in Drupal 9.0.0.
* Use $this->assertSession()->fieldExists() or
* $this->assertSession()->fieldValueEquals() instead.
*/
protected function assertFieldByName($name, $value = NULL) {
$this->assertSession()->fieldExists($name);
if ($value !== NULL) {
$this->assertSession()->fieldValueEquals($name, (string) $value);
}
}
/**
* Asserts that a field exists with the given ID and value.
*
* @param string $id
* ID of field to assert.
* @param string|\Drupal\Component\Render\MarkupInterface $value
* (optional) Value for the field to assert. You may pass in NULL to skip
* checking the value, while still checking that the field exists.
* However, the default value ('') asserts that the field value is an empty
* string.
*
* @deprecated Scheduled for removal in Drupal 9.0.0.
* Use $this->assertSession()->fieldExists() or
* $this->assertSession()->fieldValueEquals() instead.
*/
protected function assertFieldById($id, $value = NULL) {
$this->assertFieldByName($id, $value);
}
/**
* Asserts that a field exists with the given name or ID.
*
* @param string $field
* Name or ID of field to assert.
*
* @deprecated Scheduled for removal in Drupal 9.0.0.
* Use $this->assertSession()->fieldExists() instead.
*/
protected function assertField($field) {
$this->assertSession()->fieldExists($field);
}
/**
* Asserts that a field exists with the given name or ID does NOT exist.
*
* @param string $field
* Name or ID of field to assert.
*
* @deprecated Scheduled for removal in Drupal 9.0.0.
* Use $this->assertSession()->fieldNotExists() instead.
*/
protected function assertNoField($field) {
$this->assertSession()->fieldNotExists($field);
}
/**
* Passes if the raw text IS found on the loaded page, fail otherwise.
*
* Raw text refers to the raw HTML that the page generated.
*
* @param string $raw
* Raw (HTML) string to look for.
*
* @deprecated Scheduled for removal in Drupal 9.0.0.
* Use $this->assertSession()->responseContains() instead.
*/
protected function assertRaw($raw) {
$this->assertSession()->responseContains($raw);
}
/**
* Passes if the raw text IS not found on the loaded page, fail otherwise.
*
* Raw text refers to the raw HTML that the page generated.
*
* @param string $raw
* Raw (HTML) string to look for.
*
* @deprecated Scheduled for removal in Drupal 9.0.0.
* Use $this->assertSession()->responseNotContains() instead.
*/
protected function assertNoRaw($raw) {
$this->assertSession()->responseNotContains($raw);
}
/**
* Pass if the page title is the given string.
*
* @param string $expected_title
* The string the page title should be.
*
* @deprecated Scheduled for removal in Drupal 9.0.0.
* Use $this->assertSession()->titleEquals() instead.
*/
protected function assertTitle($expected_title) {
// Cast MarkupInterface to string.
$expected_title = (string) $expected_title;
return $this->assertSession()->titleEquals($expected_title);
}
/**
* Passes if a link with the specified label is found.
*
* An optional link index may be passed.
*
* @param string|\Drupal\Component\Render\MarkupInterface $label
* Text between the anchor tags.
* @param int $index
* Link position counting from zero.
*
* @deprecated Scheduled for removal in Drupal 9.0.0.
* Use $this->assertSession()->linkExists() instead.
*/
protected function assertLink($label, $index = 0) {
return $this->assertSession()->linkExists($label, $index);
}
/**
* Passes if a link with the specified label is not found.
*
* @param string|\Drupal\Component\Render\MarkupInterface $label
* Text between the anchor tags.
*
* @deprecated Scheduled for removal in Drupal 9.0.0.
* Use $this->assertSession()->linkNotExists() instead.
*/
protected function assertNoLink($label) {
return $this->assertSession()->linkNotExists($label);
}
/**
* Passes if a link containing a given href (part) is found.
*
* @param string $href
* The full or partial value of the 'href' attribute of the anchor tag.
* @param int $index
* Link position counting from zero.
*
* @deprecated Scheduled for removal in Drupal 9.0.0.
* Use $this->assertSession()->linkByHref() instead.
*/
protected function assertLinkByHref($href, $index = 0) {
$this->assertSession()->linkByHrefExists($href, $index);
}
/**
* Passes if a link containing a given href (part) is not found.
*
* @param string $href
* The full or partial value of the 'href' attribute of the anchor tag.
*
* @deprecated Scheduled for removal in Drupal 9.0.0.
* Use $this->assertSession()->linkByHrefNotExists() instead.
*/
protected function assertNoLinkByHref($href) {
$this->assertSession()->linkByHrefNotExists($href);
}
/**
* Asserts that a field does not exist with the given ID and value.
*
* @param string $id
* ID of field to assert.
* @param string $value
* (optional) Value for the field, to assert that the field's value on the
* page doesn't match it. You may pass in NULL to skip checking the value,
* while still checking that the field doesn't exist. However, the default
* value ('') asserts that the field value is not an empty string.
*
* @deprecated Scheduled for removal in Drupal 9.0.0.
* Use $this->assertSession()->fieldNotExists() or
* $this->assertSession()->fieldValueNotEquals() instead.
*/
protected function assertNoFieldById($id, $value = '') {
if ($this->getSession()->getPage()->findField($id)) {
$this->assertSession()->fieldValueNotEquals($id, (string) $value);
}
else {
$this->assertSession()->fieldNotExists($id);
}
}
/**
* Passes if the internal browser's URL matches the given path.
*
* @param \Drupal\Core\Url|string $path
* The expected system path or URL.
*
* @deprecated Scheduled for removal in Drupal 9.0.0.
* Use $this->assertSession()->addressEquals() instead.
*/
protected function assertUrl($path) {
$this->assertSession()->addressEquals($path);
}
/**
* Asserts that a select option in the current page exists.
*
* @param string $id
* ID of select field to assert.
* @param string $option
* Option to assert.
*
* @deprecated Scheduled for removal in Drupal 9.0.0.
* Use $this->assertSession()->optionExists() instead.
*/
protected function assertOption($id, $option) {
return $this->assertSession()->optionExists($id, $option);
}
/**
* Asserts that a select option does NOT exist in the current page.
*
* @param string $id
* ID of select field to assert.
* @param string $option
* Option to assert.
*
* @deprecated Scheduled for removal in Drupal 9.0.0.
* Use $this->assertSession()->optionNotExists() instead.
*/
protected function assertNoOption($id, $option) {
return $this->assertSession()->optionNotExists($id, $option);
}
/**
* Passes if the raw text IS found escaped on the loaded page, fail otherwise.
*
* Raw text refers to the raw HTML that the page generated.
*
* @param string $raw
* Raw (HTML) string to look for.
*
* @deprecated Scheduled for removal in Drupal 9.0.0.
* Use $this->assertSession()->assertEscaped() instead.
*/
protected function assertEscaped($raw) {
$this->assertSession()->assertEscaped($raw);
}
/**
* Passes if the raw text is not found escaped on the loaded page.
*
* Raw text refers to the raw HTML that the page generated.
*
* @param string $raw
* Raw (HTML) string to look for.
*
* @deprecated Scheduled for removal in Drupal 9.0.0.
* Use $this->assertSession()->assertNoEscaped() instead.
*/
protected function assertNoEscaped($raw) {
$this->assertSession()->assertNoEscaped($raw);
}
/**
* Asserts whether an expected cache tag was present in the last response.
*
* @param string $expected_cache_tag
* The expected cache tag.
*
* @deprecated Scheduled for removal in Drupal 9.0.0.
* Use $this->assertSession()->responseHeaderContains() instead.
*/
protected function assertCacheTag($expected_cache_tag) {
$this->assertSession()->responseHeaderContains('X-Drupal-Cache-Tags', $expected_cache_tag);
}
/**
* Returns WebAssert object.
*
* @param string $name
* (optional) Name of the session. Defaults to the active session.
*
* @return \Drupal\Tests\WebAssert
* A new web-assert option for asserting the presence of elements with.
*/
abstract public function assertSession($name = NULL);
/**
* Builds an XPath query.
*
* Builds an XPath query by replacing placeholders in the query by the value
* of the arguments.
*
* XPath 1.0 (the version supported by libxml2, the underlying XML library
* used by PHP) doesn't support any form of quotation. This function
* simplifies the building of XPath expression.
*
* @param string $xpath
* An XPath query, possibly with placeholders in the form ':name'.
* @param array $args
* 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 string
* An XPath query with arguments replaced.
*
* @deprecated Scheduled for removal in Drupal 9.0.0.
* Use $this->assertSession()->buildXPathQuery() instead.
*/
protected function buildXPathQuery($xpath, array $args = array()) {
return $this->assertSession()->buildXPathQuery($xpath, $args);
}
}

View file

@ -188,7 +188,8 @@ class ConfigDependencyTest extends EntityKernelTestBase {
/** @var \Drupal\Core\Config\ConfigManagerInterface $config_manager */
$config_manager = \Drupal::service('config.manager');
/** @var \Drupal\Core\Config\Entity\ConfigEntityStorage $storage */
$storage = $this->container->get('entity.manager')->getStorage('config_test');
$storage = $this->container->get('entity.manager')
->getStorage('config_test');
// Test dependencies between modules.
$entity1 = $storage->create(
array(
@ -221,14 +222,42 @@ class ConfigDependencyTest extends EntityKernelTestBase {
$config_manager->uninstall('module', 'node');
$this->assertFalse($storage->load('entity1'), 'Entity 1 deleted');
$this->assertFalse($storage->load('entity2'), 'Entity 2 deleted');
}
// Set a more complicated test where dependencies will be fixed.
\Drupal::state()->set('config_test.fix_dependencies', array($entity1->getConfigDependencyName()));
\Drupal::state()->set('config_test.on_dependency_removal_called', []);
// Entity1 will be deleted because it depends on node.
$entity1 = $storage->create(
/**
* Data provider for self::testConfigEntityUninstallComplex().
*/
public function providerConfigEntityUninstallComplex() {
// Ensure that alphabetical order has no influence on dependency fixing and
// removal.
return [
[['a', 'b', 'c', 'd']],
[['d', 'c', 'b', 'a']],
[['c', 'd', 'a', 'b']],
];
}
/**
* Tests complex configuration entity dependency handling during uninstall.
*
* Configuration entities can be deleted or updated during module uninstall
* because they have dependencies on the module.
*
* @param array $entity_id_suffixes
* The suffixes to add to the 4 entities created by the test.
*
* @dataProvider providerConfigEntityUninstallComplex
*/
public function testConfigEntityUninstallComplex(array $entity_id_suffixes) {
/** @var \Drupal\Core\Config\ConfigManagerInterface $config_manager */
$config_manager = \Drupal::service('config.manager');
/** @var \Drupal\Core\Config\Entity\ConfigEntityStorage $storage */
$storage = $this->container->get('entity.manager')
->getStorage('config_test');
// Entity 1 will be deleted because it depends on node.
$entity_1 = $storage->create(
array(
'id' => 'entity1',
'id' => 'entity_' . $entity_id_suffixes[0],
'dependencies' => array(
'enforced' => array(
'module' => array('node', 'config_test')
@ -236,77 +265,85 @@ class ConfigDependencyTest extends EntityKernelTestBase {
),
)
);
$entity1->save();
$entity_1->save();
// Entity2 has a dependency on Entity1 but it can be fixed because
// Entity 2 has a dependency on entity 1 but it can be fixed because
// \Drupal\config_test\Entity::onDependencyRemoval() will remove the
// dependency before config entities are deleted.
$entity2 = $storage->create(
$entity_2 = $storage->create(
array(
'id' => 'entity2',
'id' => 'entity_' . $entity_id_suffixes[1],
'dependencies' => array(
'enforced' => array(
'config' => array($entity1->getConfigDependencyName()),
'config' => array($entity_1->getConfigDependencyName()),
),
),
)
);
$entity2->save();
$entity_2->save();
// Entity3 will be unchanged because it is dependent on Entity2 which can
// Entity 3 will be unchanged because it is dependent on entity 2 which can
// be fixed. The ConfigEntityInterface::onDependencyRemoval() method will
// not be called for this entity.
$entity3 = $storage->create(
$entity_3 = $storage->create(
array(
'id' => 'entity3',
'id' => 'entity_' . $entity_id_suffixes[2],
'dependencies' => array(
'enforced' => array(
'config' => array($entity2->getConfigDependencyName()),
'config' => array($entity_2->getConfigDependencyName()),
),
),
)
);
$entity3->save();
$entity_3->save();
// Entity4's config dependency will be fixed but it will still be deleted
// Entity 4's config dependency will be fixed but it will still be deleted
// because it also depends on the node module.
$entity4 = $storage->create(
$entity_4 = $storage->create(
array(
'id' => 'entity4',
'id' => 'entity_' . $entity_id_suffixes[3],
'dependencies' => array(
'enforced' => array(
'config' => array($entity1->getConfigDependencyName()),
'config' => array($entity_1->getConfigDependencyName()),
'module' => array('node', 'config_test')
),
),
)
);
$entity4->save();
$entity_4->save();
// Set a more complicated test where dependencies will be fixed.
\Drupal::state()->set('config_test.fix_dependencies', array($entity_1->getConfigDependencyName()));
\Drupal::state()->set('config_test.on_dependency_removal_called', []);
// Do a dry run using
// \Drupal\Core\Config\ConfigManager::getConfigEntitiesToChangeOnDependencyRemoval().
$config_entities = $config_manager->getConfigEntitiesToChangeOnDependencyRemoval('module', ['node']);
$this->assertEqual($entity1->uuid(), $config_entities['delete'][0]->uuid(), 'Entity 1 will be deleted.');
$this->assertEqual($entity2->uuid(), reset($config_entities['update'])->uuid(), 'Entity 2 will be updated.');
$this->assertEqual($entity3->uuid(), reset($config_entities['unchanged'])->uuid(), 'Entity 3 is not changed.');
$this->assertEqual($entity4->uuid(), $config_entities['delete'][1]->uuid(), 'Entity 4 will be deleted.');
$this->assertEqual($entity_1->uuid(), $config_entities['delete'][1]->uuid(), 'Entity 1 will be deleted.');
$this->assertEqual($entity_2->uuid(), reset($config_entities['update'])->uuid(), 'Entity 2 will be updated.');
$this->assertEqual($entity_3->uuid(), reset($config_entities['unchanged'])->uuid(), 'Entity 3 is not changed.');
$this->assertEqual($entity_4->uuid(), $config_entities['delete'][0]->uuid(), 'Entity 4 will be deleted.');
$called = \Drupal::state()->get('config_test.on_dependency_removal_called', []);
$this->assertFalse(in_array($entity3->id(), $called), 'ConfigEntityInterface::onDependencyRemoval() is not called for entity 3.');
$this->assertIdentical(['entity1', 'entity2', 'entity4'], $called, 'The most dependent entites have ConfigEntityInterface::onDependencyRemoval() called first.');
$this->assertFalse(in_array($entity_3->id(), $called), 'ConfigEntityInterface::onDependencyRemoval() is not called for entity 3.');
$this->assertIdentical([$entity_1->id(), $entity_4->id(), $entity_2->id()], $called, 'The most dependent entites have ConfigEntityInterface::onDependencyRemoval() called first.');
// Perform a module rebuild so we can know where the node module is located
// and uninstall it.
// @todo Remove as part of https://www.drupal.org/node/2186491
system_rebuild_module_data();
// Perform the uninstall.
$config_manager->uninstall('module', 'node');
// Test that expected actions have been performed.
$this->assertFalse($storage->load('entity1'), 'Entity 1 deleted');
$entity2 = $storage->load('entity2');
$this->assertTrue($entity2, 'Entity 2 not deleted');
$this->assertEqual($entity2->calculateDependencies()->getDependencies()['config'], array(), 'Entity 2 dependencies updated to remove dependency on Entity1.');
$entity3 = $storage->load('entity3');
$this->assertTrue($entity3, 'Entity 3 not deleted');
$this->assertEqual($entity3->calculateDependencies()->getDependencies()['config'], [$entity2->getConfigDependencyName()], 'Entity 3 still depends on Entity 2.');
$this->assertFalse($storage->load('entity4'), 'Entity 4 deleted');
$this->assertFalse($storage->load($entity_1->id()), 'Entity 1 deleted');
$entity_2 = $storage->load($entity_2->id());
$this->assertTrue($entity_2, 'Entity 2 not deleted');
$this->assertEqual($entity_2->calculateDependencies()->getDependencies()['config'], array(), 'Entity 2 dependencies updated to remove dependency on entity 1.');
$entity_3 = $storage->load($entity_3->id());
$this->assertTrue($entity_3, 'Entity 3 not deleted');
$this->assertEqual($entity_3->calculateDependencies()->getDependencies()['config'], [$entity_2->getConfigDependencyName()], 'Entity 3 still depends on entity 2.');
$this->assertFalse($storage->load($entity_4->id()), 'Entity 4 deleted');
}
/**
@ -456,8 +493,8 @@ class ConfigDependencyTest extends EntityKernelTestBase {
$entity3->save();
$config_entities = $config_manager->getConfigEntitiesToChangeOnDependencyRemoval('content', [$content_entity->getConfigDependencyName()]);
$this->assertEqual($entity1->uuid(), $config_entities['delete'][0]->uuid(), 'Entity 1 will be deleted.');
$this->assertEqual($entity2->uuid(), $config_entities['delete'][1]->uuid(), 'Entity 2 will be deleted.');
$this->assertEqual($entity1->uuid(), $config_entities['delete'][1]->uuid(), 'Entity 1 will be deleted.');
$this->assertEqual($entity2->uuid(), $config_entities['delete'][0]->uuid(), 'Entity 2 will be deleted.');
$this->assertTrue(empty($config_entities['update']), 'No dependencies of the content entity will be updated.');
$this->assertTrue(empty($config_entities['unchanged']), 'No dependencies of the content entity will be unchanged.');
}

View file

@ -97,6 +97,8 @@ class ConnectionTest extends DatabaseTestBase {
// Set up identical replica and confirm connection options are identical.
Database::addConnectionInfo('default', 'replica', $connection_info['default']);
$db2 = Database::getConnection('replica', 'default');
// Getting a driver class ensures the namespace option is set.
$this->assertEquals($db->getDriverClass('select'), $db2->getDriverClass('select'));
$connectionOptions2 = $db2->getConnectionOptions();
// Get a fresh copy of the default connection options.

View file

@ -375,4 +375,50 @@ class SelectComplexTest extends DatabaseTestBase {
$this->assertTrue($exception, 'Exception was thrown');
}
/**
* Test that join conditions can use Condition objects.
*/
public function testJoinConditionObject() {
// Same test as testDefaultJoin, but with a Condition object.
$query = db_select('test_task', 't');
$join_cond = db_and()->where('t.pid = p.id');
$people_alias = $query->join('test', 'p', $join_cond);
$name_field = $query->addField($people_alias, 'name', 'name');
$query->addField('t', 'task', 'task');
$priority_field = $query->addField('t', 'priority', 'priority');
$query->orderBy($priority_field);
$result = $query->execute();
$num_records = 0;
$last_priority = 0;
foreach ($result as $record) {
$num_records++;
$this->assertTrue($record->$priority_field >= $last_priority, 'Results returned in correct order.');
$this->assertNotEqual($record->$name_field, 'Ringo', 'Taskless person not selected.');
$last_priority = $record->$priority_field;
}
$this->assertEqual($num_records, 7, 'Returned the correct number of rows.');
// Test a condition object that creates placeholders.
$t1_name = 'John';
$t2_name = 'George';
$join_cond = db_and()
->condition('t1.name', $t1_name)
->condition('t2.name', $t2_name);
$query = db_select('test', 't1');
$query->innerJoin('test', 't2', $join_cond);
$query->addField('t1', 'name', 't1_name');
$query->addField('t2', 'name', 't2_name');
$num_records = $query->countQuery()->execute()->fetchField();
$this->assertEqual($num_records, 1, 'Query expected to return 1 row. Actual: ' . $num_records);
if ($num_records == 1) {
$record = $query->execute()->fetchObject();
$this->assertEqual($record->t1_name, $t1_name, 'Query expected to retrieve name ' . $t1_name . ' from table t1. Actual: ' . $record->t1_name);
$this->assertEqual($record->t2_name, $t2_name, 'Query expected to retrieve name ' . $t2_name . ' from table t2. Actual: ' . $record->t2_name);
}
}
}

View file

@ -0,0 +1,184 @@
<?php
namespace Drupal\KernelTests\Core\DrupalKernel;
use Drupal\Core\DrupalKernel;
use Drupal\KernelTests\KernelTestBase;
use Symfony\Component\HttpFoundation\Request;
/**
* Tests DIC compilation to disk.
*
* @group DrupalKernel
*/
class DrupalKernelTest extends KernelTestBase {
/**
* {@inheritdoc}
*/
protected function setUp() {
// DrupalKernel relies on global $config_directories and requires those
// directories to exist. Therefore, create the directories, but do not
// invoke KernelTestBase::setUp(), since that would set up further
// environment aspects, which would distort this test, because it tests
// the DrupalKernel (re-)building itself.
$this->root = static::getDrupalRoot();
$this->bootEnvironment();
}
/**
* Build a kernel for testings.
*
* Because the bootstrap is in DrupalKernel::boot and that involved loading
* settings from the filesystem we need to go to extra lengths to build a kernel
* for testing.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* A request object to use in booting the kernel.
* @param array $modules_enabled
* A list of modules to enable on the kernel.
*
* @return \Drupal\Core\DrupalKernel
* New kernel for testing.
*/
protected function getTestKernel(Request $request, array $modules_enabled = NULL) {
// Manually create kernel to avoid replacing settings.
$class_loader = require $this->root . '/autoload.php';
$kernel = DrupalKernel::createFromRequest($request, $class_loader, 'testing');
$this->setSetting('container_yamls', []);
$this->setSetting('hash_salt', $this->databasePrefix);
if (isset($modules_enabled)) {
$kernel->updateModules($modules_enabled);
}
$kernel->boot();
return $kernel;
}
/**
* Tests DIC compilation.
*/
public function testCompileDIC() {
// @todo: write a memory based storage backend for testing.
$modules_enabled = array(
'system' => 'system',
'user' => 'user',
);
$request = Request::createFromGlobals();
$this->getTestKernel($request, $modules_enabled);
// Instantiate it a second time and we should get the compiled Container
// class.
$kernel = $this->getTestKernel($request);
$container = $kernel->getContainer();
$refClass = new \ReflectionClass($container);
$is_compiled_container = !$refClass->isSubclassOf('Symfony\Component\DependencyInjection\ContainerBuilder');
$this->assertTrue($is_compiled_container);
// Verify that the list of modules is the same for the initial and the
// compiled container.
$module_list = array_keys($container->get('module_handler')->getModuleList());
$this->assertEqual(array_values($modules_enabled), $module_list);
// Get the container another time, simulating a "production" environment.
$container = $this->getTestKernel($request, NULL)
->getContainer();
$refClass = new \ReflectionClass($container);
$is_compiled_container = !$refClass->isSubclassOf('Symfony\Component\DependencyInjection\ContainerBuilder');
$this->assertTrue($is_compiled_container);
// Verify that the list of modules is the same for the initial and the
// compiled container.
$module_list = array_keys($container->get('module_handler')->getModuleList());
$this->assertEqual(array_values($modules_enabled), $module_list);
// Test that our synthetic services are there.
$class_loader = $container->get('class_loader');
$refClass = new \ReflectionClass($class_loader);
$this->assertTrue($refClass->hasMethod('loadClass'), 'Container has a class loader');
// We make this assertion here purely to show that the new container below
// is functioning correctly, i.e. we get a brand new ContainerBuilder
// which has the required new services, after changing the list of enabled
// modules.
$this->assertFalse($container->has('service_provider_test_class'));
// Add another module so that we can test that the new module's bundle is
// registered to the new container.
$modules_enabled['service_provider_test'] = 'service_provider_test';
$this->getTestKernel($request, $modules_enabled);
// Instantiate it a second time and we should not get a ContainerBuilder
// class because we are loading the container definition from cache.
$kernel = $this->getTestKernel($request, $modules_enabled);
$container = $kernel->getContainer();
$refClass = new \ReflectionClass($container);
$is_container_builder = $refClass->isSubclassOf('Symfony\Component\DependencyInjection\ContainerBuilder');
$this->assertFalse($is_container_builder, 'Container is not a builder');
// Assert that the new module's bundle was registered to the new container.
$this->assertTrue($container->has('service_provider_test_class'), 'Container has test service');
// Test that our synthetic services are there.
$class_loader = $container->get('class_loader');
$refClass = new \ReflectionClass($class_loader);
$this->assertTrue($refClass->hasMethod('loadClass'), 'Container has a class loader');
// Check that the location of the new module is registered.
$modules = $container->getParameter('container.modules');
$this->assertEqual($modules['service_provider_test'], array(
'type' => 'module',
'pathname' => drupal_get_filename('module', 'service_provider_test'),
'filename' => NULL,
));
}
/**
* Tests repeated loading of compiled DIC with different environment.
*/
public function testRepeatedBootWithDifferentEnvironment() {
$request = Request::createFromGlobals();
$class_loader = require $this->root . '/autoload.php';
$environments = [
'testing1',
'testing1',
'testing2',
'testing2',
];
foreach ($environments as $environment) {
$kernel = DrupalKernel::createFromRequest($request, $class_loader, $environment);
$this->setSetting('container_yamls', []);
$this->setSetting('hash_salt', $this->databasePrefix);
$kernel->boot();
}
$this->pass('Repeatedly loaded compiled DIC with different environment');
}
/**
* Tests setting of site path after kernel boot.
*/
public function testPreventChangeOfSitePath() {
// @todo: write a memory based storage backend for testing.
$modules_enabled = array(
'system' => 'system',
'user' => 'user',
);
$request = Request::createFromGlobals();
$kernel = $this->getTestKernel($request, $modules_enabled);
$pass = FALSE;
try {
$kernel->setSitePath('/dev/null');
}
catch (\LogicException $e) {
$pass = TRUE;
}
$this->assertTrue($pass, 'Throws LogicException if DrupalKernel::setSitePath() is called after boot');
}
}

View file

@ -146,7 +146,7 @@ class EntityAutocompleteTest extends EntityKernelTestBase {
* The label of the entity to query by.
*
* @return mixed
* The JSON value encoded in its appropriate PHP type.
* The JSON value encoded in its appropriate PHP type.
*/
protected function getAutocompleteResult($input) {
$request = Request::create('entity_reference_autocomplete/' . $this->entityType . '/default');

View file

@ -512,6 +512,89 @@ class EntityQueryTest extends EntityKernelTestBase {
$this->assertResult(6, 14);
}
/**
* Test queries with delta conditions.
*/
public function testDelta() {
$figures = $this->figures;
// Test numeric delta value in field condition.
$this->queryResults = $this->factory->get('entity_test_mulrev')
->condition("$figures.0.color", 'red')
->sort('id')
->execute();
// As unit 0 at delta 0 was the red triangle bit 0 needs to be set.
$this->assertResult(1, 3, 5, 7, 9, 11, 13, 15);
$this->queryResults = $this->factory->get('entity_test_mulrev')
->condition("$figures.1.color", 'red')
->sort('id')
->execute();
// Delta 1 is not red.
$this->assertResult();
// Test on two different deltas.
$query = $this->factory->get('entity_test_mulrev');
$or = $query->andConditionGroup()
->condition("$figures.0.color", 'red')
->condition("$figures.1.color", 'blue');
$this->queryResults = $query
->condition($or)
->sort('id')
->execute();
$this->assertResult(3, 7, 11, 15);
// Test the delta range condition.
$this->queryResults = $this->factory->get('entity_test_mulrev')
->condition("$figures.%delta.color", array('blue', 'red'), 'IN')
->condition("$figures.%delta", array(0, 1), 'IN')
->sort('id')
->execute();
// Figure delta 0 or 1 can be blue or red, this matches a lot of entities.
$this->assertResult(1, 2, 3, 5, 6, 7, 9, 10, 11, 13, 14, 15);
// Test the delta range condition without conditions on the value.
$this->queryResults = $this->factory->get('entity_test_mulrev')
->condition("$figures.%delta", 1)
->sort('id')
->execute();
// Entity needs to have atleast two figures.
$this->assertResult(3, 7, 11, 15);
// Numeric delta on single value base field should return results only if
// the first item is being targeted.
$this->queryResults = $this->factory->get('entity_test_mulrev')
->condition("id.0.value", array(1, 3, 5), 'IN')
->sort('id')
->execute();
$this->assertResult(1, 3, 5);
$this->queryResults = $this->factory->get('entity_test_mulrev')
->condition("id.1.value", array(1, 3, 5), 'IN')
->sort('id')
->execute();
$this->assertResult();
// Delta range condition on single value base field should return results
// only if just the field value is targeted.
$this->queryResults = $this->factory->get('entity_test_mulrev')
->condition("id.%delta.value", array(1, 3, 5), 'IN')
->sort('id')
->execute();
$this->assertResult(1, 3, 5);
$this->queryResults = $this->factory->get('entity_test_mulrev')
->condition("id.%delta.value", array(1, 3, 5), 'IN')
->condition("id.%delta", 0, '=')
->sort('id')
->execute();
$this->assertResult(1, 3, 5);
$this->queryResults = $this->factory->get('entity_test_mulrev')
->condition("id.%delta.value", array(1, 3, 5), 'IN')
->condition("id.%delta", 1, '=')
->sort('id')
->execute();
$this->assertResult();
}
protected function assertResult() {
$assert = array();
$expected = func_get_args();

View file

@ -1179,7 +1179,7 @@ abstract class KernelTestBase extends \PHPUnit_Framework_TestCase implements Ser
'kernel',
// @see \Drupal\simpletest\TestBase::prepareEnvironment()
'generatedTestFiles',
// @see \Drupal\simpletest\KernelTestBase::containerBuild()
// Properties from the old KernelTestBase class that has been removed.
'keyValueFactory',
);
if (in_array($name, $denied) || strpos($name, 'original') === 0) {
@ -1209,6 +1209,9 @@ abstract class KernelTestBase extends \PHPUnit_Framework_TestCase implements Ser
* {@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);

View file

@ -6,6 +6,8 @@ use Behat\Mink\Driver\GoutteDriver;
use Behat\Mink\Element\Element;
use Behat\Mink\Mink;
use Behat\Mink\Session;
use Drupal\Component\FileCache\FileCacheFactory;
use Drupal\Component\Serialization\Yaml;
use Drupal\Component\Utility\Html;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Component\Utility\UrlHelper;
@ -20,9 +22,13 @@ use Drupal\Core\StreamWrapper\StreamWrapperInterface;
use Drupal\Core\Test\TestRunnerKernel;
use Drupal\Core\Url;
use Drupal\Core\Test\TestDatabase;
use Drupal\FunctionalTests\AssertLegacyTrait;
use Drupal\simpletest\AssertHelperTrait;
use Drupal\simpletest\ContentTypeCreationTrait;
use Drupal\simpletest\BlockCreationTrait;
use Drupal\simpletest\NodeCreationTrait;
use Drupal\user\Entity\Role;
use Drupal\user\Entity\User;
use Drupal\user\UserInterface;
use Symfony\Component\CssSelector\CssSelectorConverter;
use Symfony\Component\HttpFoundation\Request;
@ -31,13 +37,25 @@ use Symfony\Component\HttpFoundation\Request;
*
* Tests extending BrowserTestBase must exist in the
* Drupal\Tests\yourmodule\Functional namespace and live in the
* modules/yourmodule/Tests/Functional directory.
* modules/yourmodule/tests/src/Functional directory.
*
* @ingroup testing
*/
abstract class BrowserTestBase extends \PHPUnit_Framework_TestCase {
use AssertHelperTrait;
use BlockCreationTrait {
placeBlock as drupalPlaceBlock;
}
use AssertLegacyTrait;
use RandomGeneratorTrait;
use SessionTestTrait;
use NodeCreationTrait {
getNodeByTitle as drupalGetNodeByTitle;
createNode as drupalCreateNode;
}
use ContentTypeCreationTrait {
createContentType as drupalCreateContentType;
}
/**
* Class loader.
@ -273,6 +291,13 @@ abstract class BrowserTestBase extends \PHPUnit_Framework_TestCase {
*/
protected $baseUrl;
/**
* The original array of shutdown function callbacks.
*
* @var array
*/
protected $originalShutdownCallbacks = [];
/**
* Initializes Mink sessions.
*/
@ -505,6 +530,12 @@ abstract class BrowserTestBase extends \PHPUnit_Framework_TestCase {
if ($this->mink) {
$this->mink->stopSessions();
}
// Restore original shutdown callbacks.
if (function_exists('drupal_register_shutdown_function')) {
$callbacks = &drupal_register_shutdown_function();
$callbacks = $this->originalShutdownCallbacks;
}
}
/**
@ -530,7 +561,7 @@ abstract class BrowserTestBase extends \PHPUnit_Framework_TestCase {
* A new web-assert option for asserting the presence of elements with.
*/
public function assertSession($name = NULL) {
return new WebAssert($this->getSession($name));
return new WebAssert($this->getSession($name), $this->baseUrl);
}
/**
@ -546,6 +577,46 @@ abstract class BrowserTestBase extends \PHPUnit_Framework_TestCase {
$session->setCookie('SIMPLETEST_USER_AGENT', drupal_generate_test_ua($this->databasePrefix));
}
/**
* Builds an a absolute URL from a system path or a URL object.
*
* @param string|\Drupal\Core\Url $path
* A system path or a URL.
* @param array $options
* Options to be passed to Url::fromUri().
*
* @return string
* An absolute URL stsring.
*/
protected function buildUrl($path, array $options = array()) {
if ($path instanceof Url) {
$url_options = $path->getOptions();
$options = $url_options + $options;
$path->setOptions($options);
return $path->setAbsolute()->toString();
}
// The URL generator service is not necessarily available yet; e.g., in
// interactive installer tests.
elseif ($this->container->has('url_generator')) {
$force_internal = isset($options['external']) && $options['external'] == FALSE;
if (!$force_internal && UrlHelper::isExternal($path)) {
return Url::fromUri($path, $options)->toString();
}
else {
$uri = $path === '<front>' ? 'base:/' : 'base:/' . $path;
// Path processing is needed for language prefixing. Skip it when a
// path that may look like an external URL is being used as internal.
$options['path_processing'] = !$force_internal;
return Url::fromUri($uri, $options)
->setAbsolute()
->toString();
}
}
else {
return $this->getAbsoluteUrl($path);
}
}
/**
* Retrieves a Drupal path or an absolute path.
*
@ -559,28 +630,8 @@ abstract class BrowserTestBase extends \PHPUnit_Framework_TestCase {
*/
protected function drupalGet($path, array $options = array()) {
$options['absolute'] = TRUE;
$url = $this->buildUrl($path, $options);
if ($path instanceof Url) {
$url_options = $path->getOptions();
$options = $url_options + $options;
$path->setOptions($options);
$url = $path->setAbsolute()->toString();
}
// The URL generator service is not necessarily available yet; e.g., in
// interactive installer tests.
elseif ($this->container->has('url_generator')) {
if (UrlHelper::isExternal($path)) {
$url = Url::fromUri($path, $options)->toString();
}
else {
// This is needed for language prefixing.
$options['path_processing'] = TRUE;
$url = Url::fromUri('base:/' . $path, $options)->toString();
}
}
else {
$url = $this->getAbsoluteUrl($path);
}
$session = $this->getSession();
$this->prepareRequest();
@ -874,6 +925,16 @@ abstract class BrowserTestBase extends \PHPUnit_Framework_TestCase {
// Edit the form values.
foreach ($edit as $name => $value) {
$field = $assert_session->fieldExists($name, $form);
// Provide support for the values '1' and '0' for checkboxes instead of
// TRUE and FALSE.
// @todo Get rid of supporting 1/0 by converting all tests cases using
// this to boolean values.
$field_type = $field->getAttribute('type');
if ($field_type === 'checkbox') {
$value = (bool) $value;
}
$field->setValue($value);
}
@ -893,6 +954,90 @@ abstract class BrowserTestBase extends \PHPUnit_Framework_TestCase {
}
}
/**
* Executes a form submission.
*
* It will be done as usual POST request with Mink.
*
* @param \Drupal\Core\Url|string $path
* Location of the post form. Either a Drupal path or an absolute path or
* NULL to post to the current page. For multi-stage forms you can set the
* path to NULL and have it post to the last received page. Example:
*
* @code
* // First step in form.
* $edit = array(...);
* $this->drupalPostForm('some_url', $edit, t('Save'));
*
* // Second step in form.
* $edit = array(...);
* $this->drupalPostForm(NULL, $edit, t('Save'));
* @endcode
* @param array $edit
* Field data in an associative array. Changes the current input fields
* (where possible) to the values indicated.
*
* When working with form tests, the keys for an $edit element should match
* the 'name' parameter of the HTML of the form. For example, the 'body'
* field for a node has the following HTML:
* @code
* <textarea id="edit-body-und-0-value" class="text-full form-textarea
* resize-vertical" placeholder="" cols="60" rows="9"
* name="body[0][value]"></textarea>
* @endcode
* When testing this field using an $edit parameter, the code becomes:
* @code
* $edit["body[0][value]"] = 'My test value';
* @endcode
*
* A checkbox can be set to TRUE to be checked and should be set to FALSE to
* be unchecked. Multiple select fields can be tested using 'name[]' and
* setting each of the desired values in an array:
* @code
* $edit = array();
* $edit['name[]'] = array('value1', 'value2');
* @endcode
* @param string $submit
* Value of the submit button whose click is to be emulated. For example,
* t('Save'). The processing of the request depends on this value. For
* example, a form may have one button with the value t('Save') and another
* button with the value t('Delete'), and execute different code depending
* on which one is clicked.
*
* This function can also be called to emulate an Ajax submission. In this
* case, this value needs to be an array with the following keys:
* - path: A path to submit the form values to for Ajax-specific processing.
* - triggering_element: If the value for the 'path' key is a generic Ajax
* processing path, this needs to be set to the name of the element. If
* the name doesn't identify the element uniquely, then this should
* instead be an array with a single key/value pair, corresponding to the
* element name and value. The \Drupal\Core\Form\FormAjaxResponseBuilder
* uses this to find the #ajax information for the element, including
* which specific callback to use for processing the request.
*
* This can also be set to NULL in order to emulate an Internet Explorer
* submission of a form with a single text field, and pressing ENTER in that
* textfield: under these conditions, no button information is added to the
* POST data.
* @param array $options
* Options to be forwarded to the url generator.
*/
protected function drupalPostForm($path, array $edit, $submit, array $options = array()) {
if (is_object($submit)) {
// Cast MarkupInterface objects to string.
$submit = (string) $submit;
}
if (is_array($edit)) {
$edit = $this->castSafeStrings($edit);
}
if (isset($path)) {
$this->drupalGet($path, $options);
}
$this->submitForm($edit, $submit);
}
/**
* Helper function to get the options of select field.
*
@ -951,6 +1096,10 @@ abstract class BrowserTestBase extends \PHPUnit_Framework_TestCase {
'value' => $this->publicFilesDirectory,
'required' => TRUE,
);
$settings['settings']['file_private_path'] = (object) [
'value' => $this->privateFilesDirectory,
'required' => TRUE,
];
$this->writeSettings($settings);
// Allow for test-specific overrides.
$settings_testing_file = DRUPAL_ROOT . '/' . $this->originalSiteDirectory . '/settings.testing.php';
@ -961,11 +1110,14 @@ abstract class BrowserTestBase extends \PHPUnit_Framework_TestCase {
// testing specific overrides.
file_put_contents($directory . '/settings.php', "\n\$test_class = '" . get_class($this) . "';\n" . 'include DRUPAL_ROOT . \'/\' . $site_path . \'/settings.testing.php\';' . "\n", FILE_APPEND);
}
$settings_services_file = DRUPAL_ROOT . '/' . $this->originalSiteDirectory . '/testing.services.yml';
if (file_exists($settings_services_file)) {
// Copy the testing-specific service overrides in place.
copy($settings_services_file, $directory . '/services.yml');
if (!file_exists($settings_services_file)) {
// Otherwise, use the default services as a starting point for overrides.
$settings_services_file = DRUPAL_ROOT . '/sites/default/default.services.yml';
}
// Copy the testing-specific service overrides in place.
copy($settings_services_file, $directory . '/services.yml');
// Since Drupal is bootstrapped already, install_begin_request() will not
// bootstrap into DRUPAL_BOOTSTRAP_CONFIGURATION (again). Hence, we have to
@ -990,6 +1142,10 @@ abstract class BrowserTestBase extends \PHPUnit_Framework_TestCase {
// using File API; a potential error must trigger a PHP warning.
chmod($directory, 0777);
// During tests, cacheable responses should get the debugging cacheability
// headers by default.
$this->setContainerParameter('http.response.debug_cacheability_headers', TRUE);
$request = \Drupal::request();
$this->kernel = DrupalKernel::createFromRequest($request, $this->classLoader, 'prod', TRUE);
$this->kernel->prepareLegacyRequest($request);
@ -1000,13 +1156,13 @@ abstract class BrowserTestBase extends \PHPUnit_Framework_TestCase {
$config = $container->get('config.factory');
// Manually create and configure private and temporary files directories.
// While these could be preset/enforced in settings.php like the public
// files directory above, some tests expect them to be configurable in the
// UI. If declared in settings.php, they would no longer be configurable.
file_prepare_directory($this->privateFilesDirectory, FILE_CREATE_DIRECTORY);
file_prepare_directory($this->tempFilesDirectory, FILE_CREATE_DIRECTORY);
// While the temporary files path could be preset/enforced in settings.php
// like the public files directory above, some tests expect it to be
// configurable in the UI. If declared in settings.php, it would no longer
// be configurable.
$config->getEditable('system.file')
->set('path.private', $this->privateFilesDirectory)
->set('path.temporary', $this->tempFilesDirectory)
->save();
@ -1241,6 +1397,14 @@ abstract class BrowserTestBase extends \PHPUnit_Framework_TestCase {
));
drupal_set_time_limit($this->timeLimit);
// Save and clean the shutdown callbacks array because it is static cached
// and will be changed by the test run. Otherwise it will contain callbacks
// from both environments and the testing environment will try to call the
// handlers defined by the original one.
$callbacks = &drupal_register_shutdown_function();
$this->originalShutdownCallbacks = $callbacks;
$callbacks = [];
}
/**
@ -1383,6 +1547,7 @@ abstract class BrowserTestBase extends \PHPUnit_Framework_TestCase {
*/
protected function refreshVariables() {
// Clear the tag cache.
$this->container->get('cache_tags.invalidator')->resetChecksums();
// @todo Replace drupal_static() usage within classes and provide a
// proper interface for invoking reset() on a cache backend:
// https://www.drupal.org/node/2311945.
@ -1402,13 +1567,13 @@ abstract class BrowserTestBase extends \PHPUnit_Framework_TestCase {
/**
* Returns whether a given user account is logged in.
*
* @param \Drupal\user\UserInterface $account
* @param \Drupal\Core\Session\AccountInterface $account
* The user account object to check.
*
* @return bool
* Return TRUE if the user is logged in, FALSE otherwise.
*/
protected function drupalUserIsLoggedIn(UserInterface $account) {
protected function drupalUserIsLoggedIn(AccountInterface $account) {
$logged_in = FALSE;
if (isset($account->sessionId)) {
@ -1419,30 +1584,6 @@ abstract class BrowserTestBase extends \PHPUnit_Framework_TestCase {
return $logged_in;
}
/**
* Asserts that the element with the given CSS selector is present.
*
* @param string $css_selector
* The CSS selector identifying the element to check.
* @param string $message
* Optional message to show alongside the assertion.
*/
protected function assertElementPresent($css_selector, $message = '') {
$this->assertNotEmpty($this->getSession()->getDriver()->find($this->cssSelectToXpath($css_selector)), $message);
}
/**
* Asserts that the element with the given CSS selector is not present.
*
* @param string $css_selector
* The CSS selector identifying the element to check.
* @param string $message
* Optional message to show alongside the assertion.
*/
protected function assertElementNotPresent($css_selector, $message = '') {
$this->assertEmpty($this->getSession()->getDriver()->find($this->cssSelectToXpath($css_selector)), $message);
}
/**
* Clicks the element with the given CSS selector.
*
@ -1529,4 +1670,134 @@ abstract class BrowserTestBase extends \PHPUnit_Framework_TestCase {
return (new CssSelectorConverter($html))->toXPath($selector, $prefix);
}
/**
* Searches elements using a CSS selector in the raw content.
*
* The search is relative to the root element (HTML tag normally) of the page.
*
* @param string $selector
* CSS selector to use in the search.
*
* @return \Behat\Mink\Element\NodeElement[]
* The list of elements on the page that match the selector.
*/
protected function cssSelect($selector) {
return $this->getSession()->getPage()->findAll('css', $selector);
}
/**
* Follows a link by complete name.
*
* Will click the first link found with this link text.
*
* If the link is discovered and clicked, the test passes. Fail otherwise.
*
* @param string|\Drupal\Component\Render\MarkupInterface $label
* Text between the anchor tags.
*/
protected function clickLink($label) {
$label = (string) $label;
$this->getSession()->getPage()->clickLink($label);
}
/**
* Retrieves the plain-text content from the current page.
*/
protected function getTextContent() {
return $this->getSession()->getPage()->getContent();
}
/**
* 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);
}
/**
* 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);
}
/**
* Get the current URL from the browser.
*
* @return string
* The current URL.
*/
protected function getUrl() {
return $this->getSession()->getCurrentUrl();
}
/**
* {@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);
}
/**
* Changes parameters in the services.yml file.
*
* @param string $name
* The name of the parameter.
* @param mixed $value
* The value of the parameter.
*/
protected function setContainerParameter($name, $value) {
$filename = $this->siteDirectory . '/services.yml';
chmod($filename, 0666);
$services = Yaml::decode(file_get_contents($filename));
$services['parameters'][$name] = $value;
file_put_contents($filename, Yaml::encode($services));
// Ensure that the cache is deleted for the yaml file loader.
$file_cache = FileCacheFactory::get('container_yaml_loader');
$file_cache->delete($filename);
}
}

View file

@ -222,6 +222,17 @@ class ContainerTest extends \PHPUnit_Framework_TestCase {
$this->assertTrue($this->container->has('another.service'));
}
/**
* Tests that Container::has() for aliased services works properly.
*
* @covers ::has
*/
public function testHasForAliasedService() {
$service = $this->container->has('service.provider');
$aliased_service = $this->container->has('service.provider_alias');
$this->assertSame($service, $aliased_service);
}
/**
* Tests that Container::get() for circular dependencies works properly.
* @expectedException \Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException

View file

@ -493,6 +493,60 @@ namespace Drupal\Tests\Component\DependencyInjection\Dumper {
$this->dumper->getArray();
}
/**
* Tests that references to aliases work correctly.
*
* @covers ::getReferenceCall
*
* @dataProvider publicPrivateDataProvider
*/
public function testGetServiceDefinitionWithReferenceToAlias($public) {
$bar_definition = new Definition('\stdClass');
$bar_definition_php_array = array(
'class' => '\stdClass',
);
if (!$public) {
$bar_definition->setPublic(FALSE);
$bar_definition_php_array['public'] = FALSE;
}
$bar_definition_php_array['arguments_count'] = 0;
$services['bar'] = $bar_definition;
$aliases['bar.alias'] = 'bar';
$foo = new Definition('\stdClass');
$foo->addArgument(new Reference('bar.alias'));
$services['foo'] = $foo;
$this->containerBuilder->getAliases()->willReturn($aliases);
$this->containerBuilder->getDefinitions()->willReturn($services);
$this->containerBuilder->getDefinition('bar')->willReturn($bar_definition);
$dump = $this->dumper->getArray();
if ($public) {
$service_definition = $this->getServiceCall('bar');
}
else {
$service_definition = $this->getPrivateServiceCall('bar', $bar_definition_php_array, TRUE);
}
$data = array(
'class' => '\stdClass',
'arguments' => $this->getCollection(array(
$service_definition,
)),
'arguments_count' => 1,
);
$this->assertEquals($this->serializeDefinition($data), $dump['services']['foo'], 'Expected definition matches dump.');
}
public function publicPrivateDataProvider() {
return array(
array(TRUE),
array(FALSE),
);
}
/**
* Tests that getDecoratedService() is unsupported.
*

View file

@ -35,11 +35,10 @@ class ImageTest extends UnitTestCase {
*
* @return array
* Keyed array containing:
* - 'input' - Array which contains input for
* Image::scaleDimensions().
* - 'output' - Array which contains expected output after passing
* through Image::scaleDimensions. Also contains a boolean
* 'return_value' which should match the expected return value.
* - 'input' - Array which contains input for Image::scaleDimensions().
* - 'output' - Array which contains expected output after passing
* through Image::scaleDimensions. Also contains a boolean
* 'return_value' which should match the expected return value.
*
* @see testScaleDimensions()
*/

View file

@ -474,11 +474,11 @@ class UnicodeTest extends UnitTestCase {
/**
* Provides data for self::testValidateUtf8().
*
* Invalid UTF-8 examples sourced from http://stackoverflow.com/a/11709412/109119.
*
* @return array
* An array of arrays, each containing the parameters for
* self::testValidateUtf8().
*
* Invalid UTF-8 examples sourced from http://stackoverflow.com/a/11709412/109119.
*/
public function providerTestValidateUtf8() {
return array(

View file

@ -77,7 +77,7 @@ class UuidTest extends UnitTestCase {
* Dataprovider for UUID instance tests.
*
* @return array
* An array of arrays containing
* An array of arrays containing
* - The Uuid to check against.
* - (bool) Whether or not the Uuid is valid.
* - Failure message.

View file

@ -48,6 +48,7 @@ class ComposerIntegrationTest extends UnitTestCase {
$this->root . '/core/lib/Drupal/Component/Assertion',
$this->root . '/core/lib/Drupal/Component/Bridge',
$this->root . '/core/lib/Drupal/Component/Datetime',
$this->root . '/core/lib/Drupal/Component/DependencyInjection',
$this->root . '/core/lib/Drupal/Component/Diff',
$this->root . '/core/lib/Drupal/Component/Discovery',
$this->root . '/core/lib/Drupal/Component/EventDispatcher',

View file

@ -33,6 +33,21 @@ class ChainedFastBackendTest extends UnitTestCase {
*/
protected $bin;
/**
* Tests that chained fast backend cannot be constructed with two instances of
* the same service.
*/
public function testConsistentAndFastBackendCannotBeTheSameService() {
// ToDo: It should throw a proper exception. See https://www.drupal.org/node/2751847.
$this->setExpectedException(\PHPUnit_Framework_Error::class, 'Consistent cache backend and fast cache backend cannot use the same service.');
$cache = $this->getMock('Drupal\Core\Cache\CacheBackendInterface');
$chained_fast_backend = new ChainedFastBackend(
$cache,
$cache,
'foo'
);
}
/**
* Tests a get() on the fast backend, with no hit on the consistent backend.
*/

View file

@ -31,15 +31,18 @@ class QueryArgsCacheContextTest extends UnitTestCase {
*/
public function providerTestGetContext() {
return [
[[], NULL, NULL],
[[], 'foo', NULL],
[[], NULL, ''],
[[], 'foo', ''],
// Non-empty query arguments.
[['llama' => 'rocks', 'alpaca' => '', 'panda' => 'drools', 'z' => '0'], NULL, 'alpaca=&llama=rocks&panda=drools&z=0'],
[['llama' => 'rocks', 'alpaca' => '', 'panda' => 'drools', 'z' => '0'], 'llama', 'rocks'],
[['llama' => 'rocks', 'alpaca' => '', 'panda' => 'drools', 'z' => '0'], 'alpaca', '?valueless?'],
[['llama' => 'rocks', 'alpaca' => '', 'panda' => 'drools', 'z' => '0'], 'panda', 'drools'],
[['llama' => 'rocks', 'alpaca' => '', 'panda' => 'drools', 'z' => '0'], 'z', '0'],
[['llama' => 'rocks', 'alpaca' => '', 'panda' => 'drools', 'z' => '0'], 'chicken', NULL],
[['llama' => 'rocks', 'alpaca' => '', 'panda' => 'drools', 'z' => '0'], 'chicken', ''],
[['llama' => ['rocks', 'kitty']], 'llama', '0=rocks&1=kitty'],
[['llama' => ['rocks' => 'fuzzball', 'monkey' => 'patch']], 'llama', 'rocks=fuzzball&monkey=patch'],
[['llama' => ['rocks' => ['nested', 'bonobo']]], 'llama', 'rocks%5B0%5D=nested&rocks%5B1%5D=bonobo'],
];
}

View file

@ -0,0 +1,120 @@
<?php
namespace Drupal\Tests\Core\Config;
use Drupal\Core\Config\Entity\ConfigDependencyManager;
use Drupal\Tests\UnitTestCase;
/**
* Tests the ConfigDependencyManager class.
*
* @group Config
*
* @coversDefaultClass \Drupal\Core\Config\Entity\ConfigDependencyManager
*/
class ConfigDependencyManagerTest extends UnitTestCase {
/**
* @dataProvider providerTestSortAll
*/
public function testSortAll(array $data, array $expected_order) {
$dependency_manager = new ConfigDependencyManager();
$dependency_manager->setData($data);
$this->assertEquals($expected_order, $dependency_manager->sortAll());
}
public function providerTestSortAll() {
$datasets[] = [
[
'provider.entity_b' => [],
'provider.entity_a' => [],
],
['provider.entity_a', 'provider.entity_b'],
];
$datasets[] = [
[
'provider.entity_a' => [],
'provider.entity_b' => [],
],
['provider.entity_a', 'provider.entity_b'],
];
$datasets[] = [
[
'provider.entity_b' => ['dependencies' => ['config' => ['provider.entity_a']]],
'provider.entity_a' => [],
],
['provider.entity_a', 'provider.entity_b'],
];
$datasets[] = [
[
'provider.entity_a' => [],
'provider.entity_b' => ['dependencies' => ['config' => ['provider.entity_a']]],
],
['provider.entity_a', 'provider.entity_b'],
];
$datasets[] = [
[
'provider.entity_b' => [],
'provider.entity_a' => ['dependencies' => ['config' => ['provider.entity_b']]],
],
['provider.entity_b', 'provider.entity_a'],
];
$datasets[] = [
[
'provider.entity_a' => ['dependencies' => ['config' => ['provider.entity_b']]],
'provider.entity_b' => [],
],
['provider.entity_b', 'provider.entity_a'],
];
$datasets[] = [
[
'provider.entity_a' => ['dependencies' => ['config' => ['provider.entity_b']]],
'provider.entity_b' => [],
'block.block.a' => [],
'block.block.b' => [],
],
['block.block.a', 'provider.entity_b', 'block.block.b', 'provider.entity_a'],
];
$datasets[] = [
[
'provider.entity_b' => [],
'block.block.b' => [],
'block.block.a' => [],
'provider.entity_a' => ['dependencies' => ['config' => ['provider.entity_b']]],
],
['block.block.a', 'provider.entity_b', 'block.block.b', 'provider.entity_a'],
];
$datasets[] = [
[
'provider.entity_b' => [],
'block.block.b' => [],
'block.block.a' => [],
'provider.entity_a' => ['dependencies' => ['config' => ['provider.entity_b']]],
'provider.entity_c' => ['dependencies' => ['config' => ['block.block.a']]],
],
['block.block.a', 'block.block.b', 'provider.entity_b', 'provider.entity_a', 'provider.entity_c'],
];
$datasets[] = [
[
'provider.entity_b' => ['dependencies' => ['module' => ['system']]],
'block.block.b' => [],
'block.block.a' => ['dependencies' => ['module' => ['system']]],
'provider.entity_a' => ['dependencies' => ['config' => ['provider.entity_c']]],
'provider.entity_c' => ['dependencies' => ['config' => ['block.block.a']]],
],
['block.block.b', 'block.block.a', 'provider.entity_c', 'provider.entity_a', 'provider.entity_b'],
];
return $datasets;
}
}

View file

@ -158,8 +158,8 @@ class StorageComparerTest extends UnitTestCase {
$this->storageComparer->createChangelist();
$expected = array(
'field.storage.node.body',
'views.view.test_view',
'field.field.node.article.body',
'views.view.test_view',
);
$this->assertEquals($expected, $this->storageComparer->getChangelist('create'));
$this->assertEmpty($this->storageComparer->getChangelist('delete'));
@ -196,8 +196,8 @@ class StorageComparerTest extends UnitTestCase {
$this->storageComparer->createChangelist();
$expected = array(
'field.field.node.article.body',
'views.view.test_view',
'field.field.node.article.body',
'field.storage.node.body',
);
$this->assertEquals($expected, $this->storageComparer->getChangelist('delete'));
@ -236,8 +236,8 @@ class StorageComparerTest extends UnitTestCase {
$this->storageComparer->createChangelist();
$expected = array(
'field.storage.node.body',
'system.site',
'field.field.node.article.body',
'system.site',
);
$this->assertEquals($expected, $this->storageComparer->getChangelist('update'));
$this->assertEmpty($this->storageComparer->getChangelist('create'));

View file

@ -150,19 +150,16 @@ class ConnectionTest extends UnitTestCase {
return array(
array(
'nonexistent_class',
'stub',
'\\',
'nonexistent_class',
),
array(
'Drupal\\Core\\Database\\Driver\\mysql\\Select',
'mysql',
'Drupal\Tests\Core\Database\Stub\Select',
NULL,
'Select',
),
array(
'Drupal\\Tests\\Core\\Database\\Stub\\Driver\\Schema',
'stub',
'Drupal\\Tests\\Core\\Database\\Stub\\Driver',
'Schema',
),
@ -174,11 +171,10 @@ class ConnectionTest extends UnitTestCase {
*
* @dataProvider providerGetDriverClass
*/
public function testGetDriverClass($expected, $driver, $namespace, $class) {
public function testGetDriverClass($expected, $namespace, $class) {
$mock_pdo = $this->getMock('Drupal\Tests\Core\Database\Stub\StubPDO');
$connection = new StubConnection($mock_pdo, array('namespace' => $namespace));
// Set the driver using our stub class' public property.
$connection->driver = $driver;
$this->assertEquals($expected, $connection->getDriverClass($class));
}

View file

@ -0,0 +1,9 @@
<?php
namespace Drupal\Tests\Core\Database\Stub;
use Drupal\Core\Database\Driver\mysql\Select as QuerySelect;
class Select extends QuerySelect {
}

View file

@ -2,377 +2,501 @@
namespace Drupal\Tests\Core\Entity;
use Drupal\Core\Config\Entity\ConfigEntityInterface;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Language\Language;
use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\Entity\Entity;
use Drupal\Core\Entity\EntityMalformedException;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Entity\Exception\UndefinedLinkTemplateException;
use Drupal\Core\Entity\RevisionableInterface;
use Drupal\Core\Url;
use Drupal\Tests\UnitTestCase;
/**
* Tests URL handling of the \Drupal\Core\Entity\Entity class.
*
* @coversDefaultClass \Drupal\Core\Entity\Entity
*
* @group Entity
*/
class EntityUrlTest extends UnitTestCase {
/**
* The mocked entity manager.
* The entity manager mock used in this test.
*
* @var \Drupal\Core\Entity\EntityManagerInterface|\PHPUnit_Framework_MockObject_MockObject
* @var \Prophecy\Prophecy\ProphecyInterface|\Drupal\Core\Entity\EntityManagerInterface
*/
protected $entityManager;
/**
* The mocked URL generator.
* The ID of the entity type used in this test.
*
* @var \Drupal\Core\Routing\UrlGeneratorInterface|\PHPUnit_Framework_MockObject_MockObject
* @var string
*/
protected $urlGenerator;
protected $entityTypeId = 'test_entity';
/**
* {@inheritdoc}
* The entity type mock used in this test.
*
* @var \Prophecy\Prophecy\ProphecyInterface|\Drupal\Core\Entity\EntityTypeInterface
*/
protected function setUp() {
parent::setUp();
protected $entityType;
$this->entityManager = $this->getMock('Drupal\Core\Entity\EntityManagerInterface');
$this->urlGenerator = $this->getMock('Drupal\Core\Routing\UrlGeneratorInterface');
/**
* The ID of the entity used in this test.
*
* @var int
*/
protected $entityId = 1;
$container = new ContainerBuilder();
$container->set('entity.manager', $this->entityManager);
$container->set('url_generator', $this->urlGenerator);
\Drupal::setContainer($container);
/**
* The revision ID of the entity used in this test.
*
* @var int
*/
protected $revisionId = 2;
/**
* The language code of the entity used in this test.
*
* @var string
*/
protected $langcode = 'en';
/**
* Indicator for default revisions.
*
* @var true
*/
const DEFAULT_REVISION = TRUE;
/**
* Indicator for non-default revisions.
*
* @var false
*/
const NON_DEFAULT_REVISION = FALSE;
/**
* Tests the toUrl() method without an entity ID.
*
* @covers ::toUrl
*/
public function testToUrlNoId() {
$entity = $this->getEntity(Entity::class, []);
$this->setExpectedException(EntityMalformedException::class, 'The "' . $this->entityTypeId . '" entity cannot have a URI as it does not have an ID');
$entity->toUrl();
}
/**
* Tests the toUrl() method with simple link templates.
*
* @param string $link_template
* The link template to test.
* @param string $expected_route_name
* The expected route name of the generated URL.
*
* @dataProvider providerTestToUrlLinkTemplates
*
* @covers ::toUrl
* @covers ::linkTemplates
* @covers ::urlRouteParameters
*/
public function testToUrlLinkTemplates($link_template, $expected_route_name) {
$values = ['id' => $this->entityId, 'langcode' => $this->langcode];
$entity = $this->getEntity(Entity::class, $values);
$this->registerLinkTemplate($link_template);
/** @var \Drupal\Core\Url $url */
$url = $entity->toUrl($link_template);
// The entity ID is the sole route parameter for the link templates tested
// here.
$this->assertUrl($expected_route_name, ['test_entity' => $this->entityId], $entity, TRUE, $url);
}
/**
* Provides data for testToUrlLinkTemplates().
*
* @return array
* An array of test cases for testToUrlLinkTemplates().
*/
public function providerTestToUrlLinkTemplates() {
$test_cases = [];
$test_cases['canonical'] = ['canonical', 'entity.test_entity.canonical'];
$test_cases['version-history'] = ['version-history', 'entity.test_entity.version_history'];
$test_cases['edit-form'] = ['edit-form', 'entity.test_entity.edit_form'];
$test_cases['delete-form'] = ['delete-form', 'entity.test_entity.delete_form'];
$test_cases['revision'] = ['revision', 'entity.test_entity.revision'];
return $test_cases;
}
/**
* Tests the toUrl() method with the 'revision' link template.
*
* @param bool $is_default_revision
* Whether or not the mock entity should be the default revision.
* @param string $link_template
* The link template to test.
* @param string $expected_route_name
* The expected route name of the generated URL.
* @param array $expected_route_parameters
* The expected route parameters of the generated URL.
*
* @dataProvider providerTestToUrlLinkTemplateRevision
*
* @covers ::toUrl
* @covers ::linkTemplates
* @covers ::urlRouteParameters
*/
public function testToUrlLinkTemplateRevision($is_default_revision, $link_template, $expected_route_name, array $expected_route_parameters) {
$values = ['id' => $this->entityId, 'langcode' => $this->langcode];
$entity = $this->getEntity(RevisionableEntity::class, $values);
$entity->method('getRevisionId')->willReturn($this->revisionId);
$entity->method('isDefaultRevision')->willReturn($is_default_revision);
$this->registerLinkTemplate($link_template);
// Even though this is tested with both the 'canonical' and the 'revision'
// template registered with the entity, we always ask for the 'revision'
// link template, to test that it falls back to the 'canonical' link
// template in case of the default revision.
/** @var \Drupal\Core\Url $url */
$url = $entity->toUrl('revision');
$this->assertUrl($expected_route_name, $expected_route_parameters, $entity, TRUE, $url);
}
/**
* Provides data for testToUrlLinkTemplateRevision().
*
* @return array
* An array of test cases for testToUrlLinkTemplateRevision().
*/
public function providerTestToUrlLinkTemplateRevision() {
$test_cases = [];
$route_parameters = ['test_entity' => $this->entityId];
$test_cases['default_revision'] = [static::DEFAULT_REVISION, 'canonical', 'entity.test_entity.canonical', $route_parameters];
// Add the revision ID to the expected route parameters.
$route_parameters['test_entity_revision'] = $this->revisionId;
$test_cases['non_default_revision'] = [static::NON_DEFAULT_REVISION, 'revision', 'entity.test_entity.revision', $route_parameters];
return $test_cases;
}
/**
* Tests the toUrl() method with the 'collection' link template.
*
* @covers ::toUrl
* @covers ::linkTemplates
* @covers ::urlRouteParameters
*/
public function testToUrlLinkTemplateCollection() {
$entity = $this->getEntity(Entity::class, ['id' => $this->entityId]);
$link_template = 'collection';
$this->registerLinkTemplate($link_template);
/** @var \Drupal\Core\Url $url */
$url = $entity->toUrl($link_template);
$this->assertUrl('entity.test_entity.collection', [], $entity, FALSE, $url);
}
/**
* Tests the toUrl() method with neither link templates nor a URI callback.
*
* @param array $bundle_info
* An array of bundle info to register.
* @param string $uri_callback
* The entity type URI callback to register.
*
* @dataProvider providerTestToUrlUriCallbackUndefined
*
* @covers ::toUrl
* @covers ::linkTemplates
*/
public function testToUrlUriCallbackUndefined(array $bundle_info, $uri_callback) {
$entity = $this->getEntity(Entity::class, ['id' => $this->entityId]);
$this->registerBundleInfo($bundle_info);
$this->entityType->getUriCallback()->willReturn($uri_callback);
$link_template = 'canonical';
$this->setExpectedException(UndefinedLinkTemplateException::class, "No link template '$link_template' found for the '$this->entityTypeId' entity type");
$entity->toUrl($link_template);
}
/**
* Provides data for testToUrlUriCallbackUndefined().
*
* @return array
* An array of test cases for testToUrlUriCallbackUndefined().
*/
public function providerTestToUrlUriCallbackUndefined() {
$test_cases = [];
$test_cases['no_callback'] = [[], NULL];
$test_cases['uri_callback'] = [[], 'not_a_callable'];
$test_cases['bundle_uri_callback'] = [['uri_callback' => 'not_a_callable'], NULL];
return $test_cases;
}
/**
* Tests the toUrl() method with a URI callback.
*
* @param array $bundle_info
* An array of bundle info to register.
* @param string $uri_callback
* The entity type URI callback to register.
*
* @covers ::toUrl
* @covers ::linkTemplates
*
* @dataProvider providerTestToUrlUriCallback
*/
public function testToUrlUriCallback(array $bundle_info, $uri_callback) {
$entity = $this->getEntity(Entity::class, ['id' => $this->entityId, 'langcode' => $this->langcode]);
$this->registerBundleInfo($bundle_info);
$this->entityType->getUriCallback()->willReturn($uri_callback);
/** @var \Drupal\Core\Url $url */
$url = $entity->toUrl('canonical');
$this->assertUrl('<none>', [], $entity, TRUE, $url);
}
/**
* Provides data for testToUrlUriCallback().
*
* @return array
* An array of test cases for testToUrlUriCallback().
*/
public function providerTestToUrlUriCallback() {
$test_cases = [];
$uri_callback = function () { return Url::fromRoute('<none>'); };
$test_cases['uri_callback'] = [[], $uri_callback];
$test_cases['bundle_uri_callback'] = [['uri_callback' => $uri_callback], NULL];
return $test_cases;
}
/**
* Tests the urlInfo() method.
*
* Note that urlInfo() is a deprecated alias for toUrl().
* See testToUrl().
* @param string $rel
* The link relation to test.
* @param array $options
* An array of URL options to test with.
*
* @covers ::urlInfo
*
* @dataProvider providerTestToUrl
* @dataProvider providerTestUrlInfo
*/
public function testUrlInfo($entity_class, $link_template, $expected, $langcode = NULL) {
/** @var \Drupal\Core\Entity\EntityInterface $entity */
$entity = $this->getMockForAbstractClass($entity_class, array(array('id' => 'test_entity_id'), 'test_entity_type'));
$uri = $this->getTestUrlInfo($entity, $link_template, [], $langcode);
public function testUrlInfo($rel, $options) {
$entity = $this->getEntity(Entity::class, [], ['toUrl']);
$entity->expects($this->once())
->method('toUrl')
->with($rel, $options);
$this->assertSame($expected, $uri->getRouteName());
$this->assertSame($entity, $uri->getOption('entity'));
if ($langcode) {
$this->assertEquals($langcode, $uri->getOption('language')->getId());
}
else {
if ($entity instanceof ConfigEntityInterface) {
// Config entities do not provide a language with their URIs.
$this->assertEquals(NULL, $uri->getOption('language'));
}
else {
$this->assertEquals(LanguageInterface::LANGCODE_NOT_SPECIFIED, $uri->getOption('language')->getId());
}
}
}
/**
* Tests the toUrl() method.
*
* @covers ::toUrl
*
* @dataProvider providerTestToUrl
*/
public function testToUrl($entity_class, $link_template, $expected, $langcode = NULL) {
/** @var \Drupal\Core\Entity\EntityInterface $entity */
$entity = $this->getMockForAbstractClass($entity_class, array(array('id' => 'test_entity_id'), 'test_entity_type'));
$uri = $this->getTestToUrl($entity, $link_template, [], $langcode);
$this->assertSame($expected, $uri->getRouteName());
$this->assertSame($entity, $uri->getOption('entity'));
if ($langcode) {
$this->assertEquals($langcode, $uri->getOption('language')->getId());
}
else {
if ($entity instanceof ConfigEntityInterface) {
// Config entities do not provide a language with their URIs.
$this->assertEquals(NULL, $uri->getOption('language'));
}
else {
$this->assertEquals(LanguageInterface::LANGCODE_NOT_SPECIFIED, $uri->getOption('language')->getId());
}
}
$entity->urlInfo($rel, $options);
}
/**
* Tests for Entity::toUrl() exercising different language options.
* Provides data for testUrlInfo().
*
* @covers ::toUrl
* @return array
* An array of test cases for testUrlInfo().
*/
public function testToUrlWithSpecificLanguageInOptions() {
/** @var \Drupal\Core\Entity\EntityInterface $entity */
$entity = $this->getMockForAbstractClass('Drupal\Core\Entity\Entity', array(array('id' => 'test_entity_id'), 'test_entity_type'));
public function providerTestUrlInfo() {
$test_cases = [];
// Ensure that a specified language overrides the current translation
// language.
$uri = $this->getTestToUrl($entity, 'edit-form', [], 'en');
$this->assertEquals('en', $uri->getOption('language')->getId());
$test_cases['default'] = ['canonical', []];
$test_cases['with_option'] = ['canonical', ['absolute' => TRUE]];
$test_cases['revision'] = ['revision', []];
$uri = $this->getTestToUrl($entity, 'edit-form', ['language' => new Language(['id' => 'fr'])], 'en');
$this->assertEquals('fr', $uri->getOption('language')->getId());
return $test_cases;
}
/**
* Provides test data for testUrlInfo().
* Tests the url() method without an entity ID.
*
* @param string $rel
* The link relation to test.
*
* @covers ::url
* @covers ::hasLinkTemplate
* @covers ::linkTemplates
*
* @dataProvider providerTestUrl
*/
public function providerTestToUrl() {
return array(
array('Drupal\Core\Entity\Entity', 'edit-form', 'entity.test_entity_type.edit_form', NULL),
// Specify a langcode.
array('Drupal\Core\Entity\Entity', 'edit-form', 'entity.test_entity_type.edit_form', 'es'),
array('Drupal\Core\Entity\Entity', 'edit-form', 'entity.test_entity_type.edit_form', 'en'),
array('Drupal\Core\Config\Entity\ConfigEntityBase', 'edit-form', 'entity.test_entity_type.edit_form', NULL),
// Test that overriding the default $rel parameter works.
array('Drupal\Core\Config\Entity\ConfigEntityBase', FALSE, 'entity.test_entity_type.edit_form', NULL),
);
public function testUrlEmpty($rel) {
$entity = $this->getEntity(Entity::class, []);
$this->assertEquals('', $entity->url($rel));
}
/**
* Tests the toUrl() method with an invalid link template.
* Provides data for testUrlEmpty().
*
* @covers ::toUrl
*
* @expectedException \Drupal\Core\Entity\Exception\UndefinedLinkTemplateException
*
* @expectedExceptionMessage No link template 'canonical' found for the 'test_entity_type' entity type
*
* @dataProvider providerTestToUrlForInvalidLinkTemplate
* @return array
* An array of test cases for testUrlEmpty().
*/
public function testToUrlForInvalidLinkTemplate($entity_class, $link_template) {
/** @var \Drupal\Core\Entity\EntityInterface $entity */
$entity = $this->getMockForAbstractClass($entity_class, array(array('id' => 'test_entity_id'), 'test_entity_type'));
$uri = $this->getTestToUrl($entity, $link_template);
public function providerTestUrlEmpty() {
$test_cases = [];
$this->assertEmpty($uri);
}
$test_cases['default'] = ['canonical', []];
$test_cases['revision'] = ['revision', []];
/**
* Provides test data for testUrlInfoForInvalidLinkTemplate().
*/
public function providerTestToUrlForInvalidLinkTemplate() {
return array(
array('Drupal\Core\Entity\Entity', 'canonical'),
array('Drupal\Core\Entity\Entity', FALSE),
array('Drupal\Core\Config\Entity\ConfigEntityBase', 'canonical'),
);
}
/**
* Creates a \Drupal\Core\Url object based on the entity and link template.
*
* Method urlInfo() is deprecated and replaced with toUrl().
* See also getTestToUrl().
*
* @param \Drupal\Core\Entity\EntityInterface $entity
* The test entity.
* @param string $link_template
* The link template.
* @param string $langcode
* The langcode.
*
* @return \Drupal\Core\Url
* The URL for this entity's link template.
*/
protected function getTestUrlInfo(EntityInterface $entity, $link_template, array $options = [], $langcode = NULL) {
$entity_type = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
$entity_type->expects($this->any())
->method('getLinkTemplates')
->will($this->returnValue(array(
'edit-form' => 'test_entity_type.edit',
)));
if ($langcode) {
$entity->langcode = $langcode;
}
$this->entityManager
->expects($this->any())
->method('getDefinition')
->with('test_entity_type')
->will($this->returnValue($entity_type));
// If no link template is given, call without a value to test the default.
if ($link_template) {
$uri = $entity->urlInfo($link_template, $options);
}
else {
if ($entity instanceof ConfigEntityInterface) {
$uri = $entity->urlInfo('edit-form', $options);
}
else {
$uri = $entity->urlInfo('canonical', $options);
}
}
return $uri;
}
/**
* Creates a \Drupal\Core\Url object based on the entity and link template.
*
* @param \Drupal\Core\Entity\EntityInterface $entity
* The test entity.
* @param string $link_template
* The link template.
* @param string $langcode
* The langcode.
*
* @return \Drupal\Core\Url
* The URL for this entity's link template.
*/
protected function getTestToUrl(EntityInterface $entity, $link_template, array $options = [], $langcode = NULL) {
$entity_type = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
$entity_type->expects($this->any())
->method('getLinkTemplates')
->will($this->returnValue(array(
'edit-form' => 'test_entity_type.edit',
)));
if ($langcode) {
$entity->langcode = $langcode;
}
$this->entityManager
->expects($this->any())
->method('getDefinition')
->with('test_entity_type')
->will($this->returnValue($entity_type));
// If no link template is given, call without a value to test the default.
if ($link_template) {
$uri = $entity->toUrl($link_template, $options);
}
else {
if ($entity instanceof ConfigEntityInterface) {
$uri = $entity->toUrl('edit-form', $options);
}
else {
$uri = $entity->toUrl('canonical', $options);
}
}
return $uri;
}
/**
* Tests the toUrl() method when an entity is still "new".
*
* @see \Drupal\Core\Entity\EntityInterface::isNew()
*
* @covers ::toUrl
*
* @expectedException \Drupal\Core\Entity\EntityMalformedException
*/
public function testToUrlForNewEntity() {
$entity = $this->getMockForAbstractClass('Drupal\Core\Entity\Entity', array(array(), 'test_entity_type'));
$entity->toUrl();
return $test_cases;
}
/**
* Tests the url() method.
*
* @covers ::url
*/
public function testUrl() {
$entity_type = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
$entity_type->expects($this->any())
->method('getLinkTemplates')
->will($this->returnValue(array(
'canonical' => 'test_entity_type.view',
)));
$this->entityManager
->expects($this->any())
->method('getDefinition')
->with('test_entity_type')
->will($this->returnValue($entity_type));
$invalid_entity = $this->getMockForAbstractClass('Drupal\Core\Entity\Entity', array(array(), 'test_entity_type'));
$this->assertSame('', $invalid_entity->url());
$no_link_entity = $this->getMockForAbstractClass('Drupal\Core\Entity\Entity', array(array('id' => 'test_entity_id'), 'test_entity_type'));
$this->assertSame('', $no_link_entity->url('banana'));
$valid_entity = $this->getMockForAbstractClass('Drupal\Core\Entity\Entity', array(array('id' => 'test_entity_id'), 'test_entity_type'));
$language = new Language(array('id' => LanguageInterface::LANGCODE_NOT_SPECIFIED));
$this->urlGenerator->expects($this->any())
->method('generateFromRoute')
// Sadly returnValueMap() uses ===, see \PHPUnit_Framework_MockObject_Stub_ReturnValueMap::invoke
// so the $language object can't be compared directly.
->willReturnCallback(function ($route_name, $route_parameters, $options) use ($language) {
if ($route_name === 'entity.test_entity_type.canonical' && $route_parameters === array('test_entity_type' => 'test_entity_id') && array_keys($options) === ['entity_type', 'entity', 'language'] && $options['language'] == $language) {
return '/entity/test_entity_type/test_entity_id';
}
if ($route_name === 'entity.test_entity_type.canonical' && $route_parameters === array('test_entity_type' => 'test_entity_id') && array_keys($options) === ['absolute', 'entity_type', 'entity', 'language'] && $options['language'] == $language) {
return 'http://drupal/entity/test_entity_type/test_entity_id';
}
});
$this->assertSame('/entity/test_entity_type/test_entity_id', $valid_entity->url());
$this->assertSame('http://drupal/entity/test_entity_type/test_entity_id', $valid_entity->url('canonical', array('absolute' => TRUE)));
}
/**
* Tests the retrieval of link templates.
* @param string $rel
* The link relation to test.
* @param array $options
* An array of URL options to call url() with.
* @param array $default_options
* An array of URL options that toUrl() should generate.
* @param array $expected_options
* An array of combined URL options that should be set on the final URL.
*
* @covers ::url
* @covers ::hasLinkTemplate
* @covers ::linkTemplates
*
* @dataProvider providerTestLinkTemplates
* @dataProvider providerTestUrl
*/
public function testLinkTemplates($override_templates, $expected) {
$entity_type = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
$entity_type->expects($this->any())
->method('getLinkTemplates')
->will($this->returnValue(array(
'canonical' => 'test_entity_type.view',
)));
public function testUrl($rel, $options, $default_options, $expected_options) {
$entity = $this->getEntity(Entity::class, ['id' => $this->entityId], ['toUrl']);
$this->registerLinkTemplate($rel);
$this->entityManager
->expects($this->any())
->method('getDefinition')
->with('test_entity_type')
->will($this->returnValue($entity_type));
$uri = $this->prophesize(Url::class);
$uri->getOptions()->willReturn($default_options);
$uri->setOptions($expected_options)->shouldBeCalled();
$entity = $this->getMockForAbstractClass('Drupal\Core\Entity\Entity', array(array('id' => 'test_entity_id'), 'test_entity_type'), '', TRUE, TRUE, TRUE, array('linkTemplates'));
$entity->expects($this->any())
->method('linkTemplates')
->will($this->returnCallback(function () use ($entity_type, $override_templates) {
$templates = $entity_type->getLinkTemplates();
if ($override_templates) {
$templates['bananas'] = 'test_entity_type.bananas';
}
return $templates;
}));
$this->assertSame($expected['canonical'], $entity->hasLinkTemplate('canonical'));
$this->assertSame($expected['bananas'], $entity->hasLinkTemplate('bananas'));
$url_string = "/test-entity/{$this->entityId}/$rel";
$uri->toString()->willReturn($url_string);
$entity->expects($this->once())
->method('toUrl')
->with($rel)
->willReturn($uri->reveal());
$this->assertEquals($url_string, $entity->url($rel, $options));
}
/**
* Provides test data for testLinkTemplates().
* Provides data for testUrl().
*
* @return array
* An array of test cases for testUrl().
*/
public function providerTestLinkTemplates() {
return array(
array(FALSE, array(
'canonical' => TRUE,
'bananas' => FALSE,
)),
array(TRUE, array(
'canonical' => TRUE,
'bananas' => TRUE,
)),
);
public function providerTestUrl() {
$test_cases = [];
$test_cases['default'] = ['canonical', [], [], []];
$test_cases['revision'] = ['revision', [], [], []];
$test_cases['option'] = ['canonical', ['absolute' => TRUE], [], ['absolute' => TRUE]];
$test_cases['default_option'] = ['canonical', [], ['absolute' => TRUE], ['absolute' => TRUE]];
$test_cases['option_merge'] = ['canonical', ['absolute' => TRUE], ['entity_type' => $this->entityTypeId], ['absolute' => TRUE, 'entity_type' => $this->entityTypeId]];
$test_cases['option_override'] = ['canonical', ['absolute' => TRUE], ['absolute' => FALSE], ['absolute' => TRUE]];
return $test_cases;
}
/**
* Returns a mock entity for testing.
*
* @param string $class
* The class name to mock. Should be \Drupal\Core\Entity\Entity or a
* subclass.
* @param array $values
* An array of entity values to construct the mock entity with.
* @param array $methods
* (optional) An array of additional methods to mock on the entity object.
* The getEntityType() and entityManager() methods are always mocked.
*
* @return \Drupal\Core\Entity\Entity|\PHPUnit_Framework_MockObject_MockObject
*/
protected function getEntity($class, array $values, array $methods = []) {
$methods = array_merge($methods, ['getEntityType', 'entityManager']);
// Prophecy does not allow prophesizing abstract classes while actually
// calling their code. We use Prophecy below because that allows us to
// add method prophecies later while still revealing the prophecy now.
$entity = $this->getMockBuilder($class)
->setConstructorArgs([$values, $this->entityTypeId])
->setMethods($methods)
->getMockForAbstractClass();
$this->entityType = $this->prophesize(EntityTypeInterface::class);
$this->entityType->getLinkTemplates()->willReturn([]);
$this->entityType->getKey('langcode')->willReturn(FALSE);
$entity->method('getEntityType')->willReturn($this->entityType->reveal());
$this->entityManager = $this->prophesize(EntityManagerInterface::class);
$entity->method('entityManager')->willReturn($this->entityManager->reveal());
return $entity;
}
/**
* Asserts that a given URL object matches the expectations.
*
* @param string $expected_route_name
* The expected route name of the generated URL.
* @param array $expected_route_parameters
* The expected route parameters of the generated URL.
* @param \Drupal\Core\Entity\Entity|\PHPUnit_Framework_MockObject_MockObject $entity
* The entity that is expected to be set as a URL option.
* @param bool $has_language
* Whether or not the URL is expected to have a language option.
* @param \Drupal\Core\Url $url
* The URL option to make the assertions on.
*/
protected function assertUrl($expected_route_name, array $expected_route_parameters, $entity, $has_language, Url $url) {
$this->assertEquals($expected_route_name, $url->getRouteName());
$this->assertEquals($expected_route_parameters, $url->getRouteParameters());
$this->assertEquals($this->entityTypeId, $url->getOption('entity_type'));
$this->assertEquals($entity, $url->getOption('entity'));
if ($has_language) {
$this->assertEquals($this->langcode, $url->getOption('language')->getId());
}
else {
$this->assertNull($url->getOption('language'));
}
}
/**
* Registers a link template for the mock entity.
*
* @param string $link_template
* The link template to register.
*/
protected function registerLinkTemplate($link_template) {
$link_templates = [
// The path is actually never used because we never invoke the URL
// generator but perform assertions on the URL object directly.
$link_template => "/test-entity/{test_entity}/$link_template",
];
$this->entityType->getLinkTemplates()->willReturn($link_templates);
}
/**
* Registers bundle information for the mock entity type.
*
* @param array $bundle_info
* The bundle information to register.
*/
protected function registerBundleInfo($bundle_info) {
$this->entityManager
->getBundleInfo($this->entityTypeId)
->willReturn([$this->entityTypeId => $bundle_info])
;
}
}
abstract class RevisionableEntity extends Entity implements RevisionableInterface {}

View file

@ -820,6 +820,16 @@ class SqlContentEntityStorageSchemaTest extends UnitTestCase {
'length' => 32,
'not null' => FALSE,
),
'area' => array(
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
),
'depth' => array(
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
),
),
'foreign keys' => array(
'color' => array(
@ -829,8 +839,14 @@ class SqlContentEntityStorageSchemaTest extends UnitTestCase {
),
),
),
'unique keys' => array(),
'indexes' => array(),
'unique keys' => array(
'area' => array('area'),
'shape' => array(array('shape', 10)),
),
'indexes' => array(
'depth' => array('depth'),
'color' => array(array('color', 3)),
),
));
$field_storage = $this->storageDefinitions[$field_name];
@ -905,11 +921,27 @@ class SqlContentEntityStorageSchemaTest extends UnitTestCase {
'length' => 32,
'not null' => FALSE,
),
$field_name . '_area' => array(
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
),
$field_name . '_depth' => array(
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
),
),
'primary key' => array('entity_id', 'deleted', 'delta', 'langcode'),
'indexes' => array(
'bundle' => array('bundle'),
'revision_id' => array('revision_id'),
$field_name . '_depth' => array($field_name . '_depth'),
$field_name . '_color' => array(array($field_name . '_color', 3)),
),
'unique keys' => array(
$field_name . '_area' => array($field_name . '_area'),
$field_name . '_shape' => array(array($field_name . '_shape', 10)),
),
'foreign keys' => array(
$field_name . '_color' => array(

View file

@ -9,6 +9,7 @@ use Drupal\Tests\UnitTestCase;
/**
* Tests the image class.
*
* @requires extension gd
* @group Image
*/
class ImageTest extends UnitTestCase {

View file

@ -22,7 +22,7 @@ use Symfony\Component\HttpFoundation\RequestStack;
/**
* Base class for the actual unit tests testing \Drupal\Core\Render\Renderer.
*/
class RendererTestBase extends UnitTestCase {
abstract class RendererTestBase extends UnitTestCase {
/**
* The tested renderer.

View file

@ -300,7 +300,7 @@ class UrlGeneratorTest extends UnitTestCase {
// No cacheability to test; UrlGenerator::generate() doesn't support
// collecting cacheability metadata.
$this->routeProcessorManager->expects($this->exactly(7))
$this->routeProcessorManager->expects($this->any())
->method('processOutbound')
->with($this->anything());
@ -316,6 +316,10 @@ class UrlGeneratorTest extends UnitTestCase {
$path = $this->generator->getPathFromRoute('test_2', array('narf' => '5'));
$this->assertEquals('test/two/5', $path);
// Specify a query parameter with NULL.
$options = ['query' => ['page' => NULL], 'fragment' => 'bottom'];
$this->assertGenerateFromRoute('test_2', ['narf' => 5], $options, '/goodbye/cruel/world?page#bottom', (new BubbleableMetadata())->setCacheMaxAge(Cache::PERMANENT));
}
/**

View file

@ -82,6 +82,8 @@ class UnroutedUrlTest extends UnitTestCase {
// [$uri, $is_external]
// An external URI.
['https://www.drupal.org', TRUE],
// A protocol-relative URL.
['//www.drupal.org', TRUE],
// An internal, unrouted, base-relative URI.
['base:robots.txt', FALSE],
// Base-relative URIs with special characters.
@ -114,7 +116,6 @@ class UnroutedUrlTest extends UnitTestCase {
// Schemeless paths.
['test'],
['/test'],
['//test'],
// Schemeless path with a query string.
['foo?bar'],
// Only a query string.

View file

@ -335,6 +335,18 @@ class UrlTest extends UnitTestCase {
$this->assertEquals('http://example.com/test', $url->getUri());
}
/**
* Tests the getUri() and isExternal() methods for protocol-relative URLs.
*
* @covers ::getUri
* @covers ::isExternal
*/
public function testGetUriForProtocolRelativeUrl() {
$url = Url::fromUri('//example.com/test');
$this->assertEquals('//example.com/test', $url->getUri());
$this->assertTrue($url->isExternal());
}
/**
* Tests the getInternalPath method().
*

View file

@ -69,7 +69,9 @@ class LinkGeneratorTest extends UnitTestCase {
protected function setUp() {
parent::setUp();
$this->urlGenerator = $this->getMock('\Drupal\Core\Routing\UrlGenerator', array(), array(), '', FALSE);
$this->urlGenerator = $this->getMockBuilder('\Drupal\Core\Routing\UrlGenerator')
->disableOriginalConstructor()
->getMock();
$this->moduleHandler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface');
$this->renderer = $this->getMock('\Drupal\Core\Render\RendererInterface');
$this->linkGenerator = new LinkGenerator($this->urlGenerator, $this->moduleHandler, $this->renderer);
@ -487,6 +489,33 @@ class LinkGeneratorTest extends UnitTestCase {
$this->assertInstanceOf('\Drupal\Core\Render\BubbleableMetadata', $generated_link);
}
/**
* Tests altering the URL object using hook_link_alter().
*
* @covers ::generate
*/
public function testGenerateWithAlterHook() {
$options = ['query' => [], 'language' => NULL, 'set_active_class' => FALSE, 'absolute' => FALSE];
$this->urlGenerator->expects($this->any())
->method('generateFromRoute')
->will($this->returnValueMap([
['test_route_1', [], $options, TRUE, (new GeneratedUrl())->setGeneratedUrl('/test-route-1')],
['test_route_2', [], $options, TRUE, (new GeneratedUrl())->setGeneratedUrl('/test-route-2')],
]));
$url = new Url('test_route_2');
$url->setUrlGenerator($this->urlGenerator);
$this->moduleHandler->expects($this->atLeastOnce())
->method('alter')
->willReturnCallback(function ($hook, &$options) {
$options['url'] = (new Url('test_route_1'))->setUrlGenerator($this->urlGenerator);
});
$expected_link_markup = '<a href="/test-route-1">Test</a>';
$this->assertEquals($expected_link_markup, (string) $this->linkGenerator->generate('Test', $url)->getGeneratedLink());
}
/**
* Checks that a link with certain properties exists in a given HTML snippet.
*

View file

@ -0,0 +1,134 @@
<?php
namespace Drupal\Tests\TestSuites;
use org\bovigo\vfs\vfsStream;
/**
* @coversDefaultClass \Drupal\Tests\TestSuites\TestSuiteBase
*
* @group TestSuite
*/
class TestSuiteBaseTest extends \PHPUnit_Framework_TestCase {
/**
* Helper method to set up the file system.
*
* @return array[]
* A Drupal filesystem suitable for use with vfsStream.
*/
protected function getFilesystem() {
return [
'core' => [
'modules' => [],
'profiles' => [],
'tests' => [
'Drupal' => [
'NotUnitTests' => [
'CoreNotUnitTest.php' => '<?php',
],
'Tests' => [
'CoreUnitTest.php' => '<?php',
],
],
],
],
];
}
/**
* @return array[]
* Test data for testAddTestsBySuiteNamespaceCore(). An array of arrays:
* - A filesystem array for vfsStream.
* - The sub-namespace of the test suite.
* - The array of tests expected to be discovered.
*/
public function provideCoreTests() {
$filesystem = $this->getFilesystem();
return [
'unit-tests' => [
$filesystem,
'Unit',
[
'Drupal\Tests\CoreUnitTest' => 'vfs://root/core/tests/Drupal/Tests/CoreUnitTest.php',
],
],
'not-unit-tests' => [
$filesystem,
'NotUnit',
[
'Drupal\NotUnitTests\CoreNotUnitTest' => 'vfs://root/core/tests/Drupal/NotUnitTests/CoreNotUnitTest.php',
],
],
];
}
/**
* Tests for special case behavior of unit test suite namespaces in core.
*
* @covers ::addTestsBySuiteNamespace
*
* @dataProvider provideCoreTests
*/
public function testAddTestsBySuiteNamespaceCore($filesystem, $suite_namespace, $expected_tests) {
// Set up the file system.
$vfs = vfsStream::setup('root');
vfsStream::create($filesystem, $vfs);
// Make a stub suite base to test.
$stub = new StubTestSuiteBase('test_me');
// Access addTestsBySuiteNamespace().
$ref_add_tests = new \ReflectionMethod($stub, 'addTestsBySuiteNamespace');
$ref_add_tests->setAccessible(TRUE);
// Invoke addTestsBySuiteNamespace().
$ref_add_tests->invokeArgs($stub, [vfsStream::url('root'), $suite_namespace]);
// Determine if we loaded the expected test files.
$this->assertNotEmpty($stub->testFiles);
$this->assertEmpty(array_diff_assoc($expected_tests, $stub->testFiles));
}
}
/**
* Stub subclass of TestSuiteBase.
*
* We use this class to alter the behavior of TestSuiteBase so it can be
* testable.
*/
class StubTestSuiteBase extends TestSuiteBase {
/**
* Test files discovered by addTestsBySuiteNamespace().
*
* @var string[]
*/
public $testFiles = [];
/**
* {@inheritdoc}
*/
protected function findExtensionDirectories($root) {
// We have to stub findExtensionDirectories() because we can't inject a
// vfsStream filesystem into drupal_phpunit_find_extension_directories(),
// which uses \SplFileInfo->getRealPath(). getRealPath() resolves
// stream-based paths to an empty string. See
// https://github.com/mikey179/vfsStream/wiki/Known-Issues
return [];
}
/**
* {@inheritdoc}
*/
public function addTestFiles($filenames) {
// We stub addTestFiles() because the parent implementation can't deal with
// vfsStream-based filesystems due to an error in
// stream_resolve_include_path(). See
// https://github.com/mikey179/vfsStream/issues/5 Here we just store the
// test file being added in $this->testFiles.
$this->testFiles = array_merge($this->testFiles, $filenames);
}
}

View file

@ -2,15 +2,58 @@
namespace Drupal\Tests;
use Behat\Mink\Exception\ExpectationException;
use Behat\Mink\WebAssert as MinkWebAssert;
use Behat\Mink\Element\TraversableElement;
use Behat\Mink\Exception\ElementNotFoundException;
use Behat\Mink\Session;
use Drupal\Component\Utility\Html;
use Drupal\Core\Url;
/**
* Defines a class with methods for asserting presence of elements during tests.
*/
class WebAssert extends MinkWebAssert {
/**
* The absolute URL of the site under test.
*
* @var string
*/
protected $baseUrl = '';
/**
* Constructor.
*
* @param \Behat\Mink\Session $session
* The Behat session object;
* @param string $base_url
* The base URL of the site under test.
*/
public function __construct(Session $session, $base_url = '') {
parent::__construct($session);
$this->baseUrl = $base_url;
}
/**
* {@inheritdoc}
*/
protected function cleanUrl($url) {
if ($url instanceof Url) {
$url = $url->setAbsolute()->toString();
}
// Strip the base URL from the beginning for absolute URLs.
if ($this->baseUrl !== '' && strpos($url, $this->baseUrl) === 0) {
$url = substr($url, strlen($this->baseUrl));
}
// Make sure there is a forward slash at the beginning of relative URLs for
// consistency.
if (parse_url($url, PHP_URL_HOST) === NULL && strpos($url, '/') !== 0) {
$url = "/$url";
}
return parent::cleanUrl($url);
}
/**
* Checks that specific button exists on the current page.
*
@ -36,6 +79,24 @@ class WebAssert extends MinkWebAssert {
return $node;
}
/**
* Checks that the specific button does NOT exist on the current page.
*
* @param string $button
* One of id|name|label|value for the button.
* @param \Behat\Mink\Element\TraversableElement $container
* (optional) The document to check against. Defaults to the current page.
*
* @throws \Behat\Mink\Exception\ExpectationException
* When the button exists.
*/
public function buttonNotExists($button, TraversableElement $container = NULL) {
$container = $container ?: $this->session->getPage();
$node = $container->findButton($button);
$this->assert(NULL === $node, sprintf('A button "%s" appears on this page, but it should not.', $button));
}
/**
* Checks that specific select field exists on the current page.
*
@ -64,4 +125,301 @@ class WebAssert extends MinkWebAssert {
return $node;
}
/**
* Checks that specific option in a select field exists on the current page.
*
* @param string $select
* One of id|name|label|value for the select field.
* @param string $option
* The option value.
* @param \Behat\Mink\Element\TraversableElement $container
* (optional) The document to check against. Defaults to the current page.
*
* @return \Behat\Mink\Element\NodeElement
* The matching option element
*
* @throws \Behat\Mink\Exception\ElementNotFoundException
* When the element doesn't exist.
*/
public function optionExists($select, $option, TraversableElement $container = NULL) {
$container = $container ?: $this->session->getPage();
$select_field = $container->find('named', array(
'select',
$this->session->getSelectorsHandler()->xpathLiteral($select),
));
if ($select_field === NULL) {
throw new ElementNotFoundException($this->session, 'select', 'id|name|label|value', $select);
}
$option_field = $select_field->find('named', array('option', $option));
if ($option_field === NULL) {
throw new ElementNotFoundException($this->session, 'select', 'id|name|label|value', $option);
}
return $option_field;
}
/**
* Checks that an option in a select field does NOT exist on the current page.
*
* @param string $select
* One of id|name|label|value for the select field.
* @param string $option
* The option value that shoulkd not exist.
* @param \Behat\Mink\Element\TraversableElement $container
* (optional) The document to check against. Defaults to the current page.
*
* @throws \Behat\Mink\Exception\ElementNotFoundException
* When the select element doesn't exist.
*/
public function optionNotExists($select, $option, TraversableElement $container = NULL) {
$container = $container ?: $this->session->getPage();
$select_field = $container->find('named', array(
'select',
$this->session->getSelectorsHandler()->xpathLiteral($select),
));
if ($select_field === NULL) {
throw new ElementNotFoundException($this->session, 'select', 'id|name|label|value', $select);
}
$option_field = $select_field->find('named', array('option', $option));
$this->assert($option_field === NULL, sprintf('An option "%s" exists in select "%s", but it should not.', $option, $select));
}
/**
* Pass if the page title is the given string.
*
* @param string $expected_title
* The string the page title should be.
*
* @throws \Behat\Mink\Exception\ExpectationException
* Thrown when element doesn't exist, or the title is a different one.
*/
public function titleEquals($expected_title) {
$title_element = $this->session->getPage()->find('css', 'title');
if (!$title_element) {
throw new ExpectationException('No title element found on the page', $this->session);
}
$actual_title = $title_element->getText();
$this->assert($expected_title === $actual_title, 'Title found');
}
/**
* Passes if a link with the specified label is found.
*
* An optional link index may be passed.
*
* @param string $label
* Text between the anchor tags.
* @param int $index
* Link position counting from zero.
* @param string $message
* (optional) A message to display with the assertion. Do not translate
* messages: use strtr() to embed variables in the message text, not
* t(). If left blank, a default message will be displayed.
*
* @throws \Behat\Mink\Exception\ExpectationException
* Thrown when element doesn't exist, or the link label is a different one.
*/
public function linkExists($label, $index = 0, $message = '') {
$message = ($message ? $message : strtr('Link with label %label found.', ['%label' => $label]));
$links = $this->session->getPage()->findAll('named', ['link', $label]);
$this->assert(!empty($links[$index]), $message);
}
/**
* Passes if a link with the specified label is not found.
*
* An optional link index may be passed.
*
* @param string $label
* Text between the anchor tags.
* @param string $message
* (optional) A message to display with the assertion. Do not translate
* messages: use strtr() to embed variables in the message text, not
* t(). If left blank, a default message will be displayed.
*
* @throws \Behat\Mink\Exception\ExpectationException
* Thrown when element doesn't exist, or the link label is a different one.
*/
public function linkNotExists($label, $message = '') {
$message = ($message ? $message : strtr('Link with label %label found.', ['%label' => $label]));
$links = $this->session->getPage()->findAll('named', ['link', $label]);
$this->assert(empty($links), $message);
}
/**
* Passes if a link containing a given href (part) is found.
*
* @param string $href
* The full or partial value of the 'href' attribute of the anchor tag.
* @param int $index
* Link position counting from zero.
* @param string $message
* (optional) A message to display with the assertion. Do not translate
* messages: use \Drupal\Component\Utility\SafeMarkup::format() to embed
* variables in the message text, not t(). If left blank, a default message
* will be displayed.
*
* @throws \Behat\Mink\Exception\ExpectationException
* Thrown when element doesn't exist, or the link label is a different one.
*/
public function linkByHrefExists($href, $index = 0, $message = '') {
$xpath = $this->buildXPathQuery('//a[contains(@href, :href)]', [':href' => $href]);
$message = ($message ? $message : strtr('Link containing href %href found.', ['%href' => $href]));
$links = $this->session->getPage()->findAll('xpath', $xpath);
$this->assert(!empty($links[$index]), $message);
}
/**
* Passes if a link containing a given href (part) is not found.
*
* @param string $href
* The full or partial value of the 'href' attribute of the anchor tag.
* @param string $message
* (optional) A message to display with the assertion. Do not translate
* messages: use \Drupal\Component\Utility\SafeMarkup::format() to embed
* variables in the message text, not t(). If left blank, a default message
* will be displayed.
*
* @throws \Behat\Mink\Exception\ExpectationException
* Thrown when element doesn't exist, or the link label is a different one.
*/
public function linkByHrefNotExists($href, $message = '') {
$xpath = $this->buildXPathQuery('//a[contains(@href, :href)]', [':href' => $href]);
$message = ($message ? $message : strtr('Link containing href %href found.', ['%href' => $href]));
$links = $this->session->getPage()->findAll('xpath', $xpath);
$this->assert(empty($links), $message);
}
/**
* Builds an XPath query.
*
* Builds an XPath query by replacing placeholders in the query by the value
* of the arguments.
*
* XPath 1.0 (the version supported by libxml2, the underlying XML library
* used by PHP) doesn't support any form of quotation. This function
* simplifies the building of XPath expression.
*
* @param string $xpath
* An XPath query, possibly with placeholders in the form ':name'.
* @param array $args
* 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 string
* An XPath query with arguments replaced.
*/
public function buildXPathQuery($xpath, array $args = array()) {
// Replace placeholders.
foreach ($args as $placeholder => $value) {
if (is_object($value)) {
throw new \InvalidArgumentException('Just pass in scalar values.');
}
// XPath 1.0 doesn't support a way to escape single or double quotes in a
// string literal. We split double quotes out of the string, and encode
// them separately.
if (is_string($value)) {
// Explode the text at the quote characters.
$parts = explode('"', $value);
// Quote the parts.
foreach ($parts as &$part) {
$part = '"' . $part . '"';
}
// Return the string.
$value = count($parts) > 1 ? 'concat(' . implode(', \'"\', ', $parts) . ')' : $parts[0];
}
// Use preg_replace_callback() instead of preg_replace() to prevent the
// regular expression engine from trying to substitute backreferences.
$replacement = function ($matches) use ($value) {
return $value;
};
$xpath = preg_replace_callback('/' . preg_quote($placeholder) . '\b/', $replacement, $xpath);
}
return $xpath;
}
/**
* Passes if the raw text IS NOT found escaped on the loaded page.
*
* Raw text refers to the raw HTML that the page generated.
*
* @param string $raw
* Raw (HTML) string to look for.
*/
public function assertNoEscaped($raw) {
$this->responseNotContains(Html::escape($raw));
}
/**
* Passes if the raw text IS found escaped on the loaded page.
*
* Raw text refers to the raw HTML that the page generated.
*
* @param string $raw
* Raw (HTML) string to look for.
*/
public function assertEscaped($raw) {
$this->responseContains(Html::escape($raw));
}
/**
* Asserts a condition.
*
* The parent method is overridden because it is a private method.
*
* @param bool $condition
* The condition.
* @param string $message
* The success message.
*
* @throws \Behat\Mink\Exception\ExpectationException
* When the condition is not fulfilled.
*/
public function assert($condition, $message) {
if ($condition) {
return;
}
throw new ExpectationException($message, $this->session->getDriver());
}
/**
* Checks that a given form field element is disabled.
*
* @param string $field
* One of id|name|label|value for the field.
* @param \Behat\Mink\Element\TraversableElement $container
* (optional) The document to check against. Defaults to the current page.
*
* @return \Behat\Mink\Element\NodeElement
* The matching element.
*
* @throws \Behat\Mink\Exception\ElementNotFoundException
* @throws \Behat\Mink\Exception\ExpectationException
*/
public function fieldDisabled($field, TraversableElement $container = NULL) {
$container = $container ?: $this->session->getPage();
$node = $container->findField($field);
if ($node === NULL) {
throw new ElementNotFoundException($this->session->getDriver(), 'field', 'id|name|label|value', $field);
}
if (!$node->hasAttribute('disabled')) {
throw new ExpectationException("Field $field is disabled", $this->session->getDriver());
}
return $node;
}
}