Update to Drupal 8.0.0-beta15. For more information, see: https://www.drupal.org/node/2563023

This commit is contained in:
Pantheon Automation 2015-09-04 13:20:09 -07:00 committed by Greg Anderson
parent 2720a9ec4b
commit f3791f1da3
1898 changed files with 54300 additions and 11481 deletions

View file

@ -48,6 +48,9 @@ class FormValuesTest extends AjaxTestBase {
// Verify that AJAX elements with invalid callbacks return error code 500.
// Ensure the test error log is empty before these tests.
$this->assertNoErrorsLogged();
// We don't need to check for the X-Drupal-Ajax-Token header with these
// invalid requests.
$this->assertAjaxHeader = FALSE;
foreach (array('null', 'empty', 'nonexistent') as $key) {
$element_name = 'select_' . $key . '_callback';
$edit = array(
@ -56,6 +59,8 @@ class FormValuesTest extends AjaxTestBase {
$commands = $this->drupalPostAjaxForm('ajax_forms_test_get_form', $edit, $element_name);
$this->assertResponse(500);
}
// Switch this back to the default.
$this->assertAjaxHeader = TRUE;
// The exceptions are expected. Do not interpret them as a test failure.
// Not using File API; a potential error must trigger a PHP warning.
unlink(\Drupal::root() . '/' . $this->siteDirectory . '/error.log');

View file

@ -165,7 +165,7 @@ class FrameworkTest extends AjaxTestBase {
// the first AJAX command.
$this->assertIdentical($new_settings[$expected['setting_name']], $expected['setting_value'], format_string('Page now has the %setting.', array('%setting' => $expected['setting_name'])));
$expected_command = new SettingsCommand(array($expected['setting_name'] => $expected['setting_value']), TRUE);
$this->assertCommand(array_slice($commands, 0, 1), $expected_command->render(), format_string('The settings command was first.'));
$this->assertCommand(array_slice($commands, 0, 1), $expected_command->render(), 'The settings command was first.');
// Verify the expected CSS file was added, both to drupalSettings, and as
// the second AJAX command for inclusion into the HTML.

View file

@ -60,7 +60,7 @@ class MultiFormTest extends AjaxTestBase {
$field_name = 'field_ajax_test';
$form_xpath = '//form[starts-with(@id, "node-page-form")]';
$field_xpath = '//div[contains(@class, "field-name-field-ajax-test")]';
$field_xpath = '//div[contains(@class, "field--name-field-ajax-test")]';
$button_name = $field_name . '_add_more';
$button_value = t('Add another item');
$button_xpath_suffix = '//input[@name="' . $button_name . '"]';

View file

@ -61,4 +61,19 @@ class PageTest extends WebTestBase {
$this->assertText('Redirection successful.', 'Redirection after batch execution is correct.');
}
/**
* Tests that the progress messages are correct.
*/
function testBatchProgressMessages() {
// Go to the initial step only.
$this->maximumMetaRefreshCount = 0;
$this->drupalGet('batch-test/test-title');
$this->assertRaw('<div class="progress__description">Initializing.<br />&nbsp;</div>', 'Initial progress message appears correctly.');
$this->assertNoRaw('&amp;nbsp;', 'Initial progress message is not double escaped.');
// Now also go to the next step.
$this->maximumMetaRefreshCount = 1;
$this->drupalGet('batch-test/test-title');
$this->assertRaw('<div class="progress__description">Completed 1 of 1.</div>', 'Progress message for second step appears correctly.');
}
}

View file

@ -7,6 +7,7 @@
namespace Drupal\system\Tests\Batch;
use Drupal\Core\Url;
use Drupal\simpletest\WebTestBase;
/**
@ -21,7 +22,7 @@ class ProcessingTest extends WebTestBase {
*
* @var array
*/
public static $modules = array('batch_test');
public static $modules = array('batch_test', 'test_page_test');
/**
* Tests batches triggered outside of form submission.
@ -34,6 +35,18 @@ class ProcessingTest extends WebTestBase {
$this->assertText('Redirection successful.', 'Redirection after batch execution is correct.');
}
/**
* Tests batches that redirect in the batch finished callback.
*/
function testBatchRedirectFinishedCallback() {
// Displaying the page triggers batch 1.
$this->drupalGet('batch-test/finish-redirect');
$this->assertBatchMessages($this->_resultMessages('batch_1'), 'Batch for step 2 performed successfully.');
$this->assertEqual(batch_test_stack(), $this->_resultStack('batch_1'), 'Execution order was correct.');
$this->assertText('Test page text.', 'Custom redirection after batch execution displays the correct page.');
$this->assertUrl(Url::fromRoute('test_page_test.test_page'));
}
/**
* Tests batches defined in a form submit handler.
*/
@ -244,28 +257,28 @@ class ProcessingTest extends WebTestBase {
switch ($id) {
case 'batch_0':
$messages[] = 'results for batch 0<br>none';
$messages[] = 'results for batch 0<div class="item-list"><ul><li>none</li></ul></div>';
break;
case 'batch_1':
$messages[] = 'results for batch 1<br>op 1: processed 10 elements';
$messages[] = 'results for batch 1<div class="item-list"><ul><li>op 1: processed 10 elements</li></ul></div>';
break;
case 'batch_2':
$messages[] = 'results for batch 2<br>op 2: processed 10 elements';
$messages[] = 'results for batch 2<div class="item-list"><ul><li>op 2: processed 10 elements</li></ul></div>';
break;
case 'batch_3':
$messages[] = 'results for batch 3<br>op 1: processed 10 elements<br>op 2: processed 10 elements';
$messages[] = 'results for batch 3<div class="item-list"><ul><li>op 1: processed 10 elements</li><li>op 2: processed 10 elements</li></ul></div>';
break;
case 'batch_4':
$messages[] = 'results for batch 4<br>op 1: processed 10 elements';
$messages[] = 'results for batch 4<div class="item-list"><ul><li>op 1: processed 10 elements</li></ul></div>';
$messages = array_merge($messages, $this->_resultMessages('batch_2'));
break;
case 'batch_5':
$messages[] = 'results for batch 5<br>op 5: processed 10 elements';
$messages[] = 'results for batch 5<div class="item-list"><ul><li>op 5: processed 10 elements</li></ul></div>';
break;
case 'chained':

View file

@ -24,23 +24,30 @@ class DrupalSetMessageTest extends WebTestBase {
public static $modules = array('system_test');
/**
* Tests setting messages and removing one before it is displayed.
* Tests drupal_set_message().
*/
function testSetRemoveMessages() {
function testDrupalSetMessage() {
// The page at system-test/drupal-set-message sets two messages and then
// removes the first before it is displayed.
$this->drupalGet('system-test/drupal-set-message');
$this->assertNoText('First message (removed).');
$this->assertText('Second message (not removed).');
}
$this->assertRaw(t('Second message with <em>markup!</em> (not removed).'));
/**
* Tests setting duplicated messages.
*/
function testDuplicatedMessages() {
$this->drupalGet('system-test/drupal-set-message');
// Ensure duplicate messages are handled as expected.
$this->assertUniqueText('Non Duplicated message');
$this->assertNoUniqueText('Duplicated message');
// Ensure SafeString objects are rendered as expected.
$this->assertRaw('SafeString with <em>markup!</em>');
$this->assertUniqueText('SafeString with markup!');
$this->assertRaw('SafeString2 with <em>markup!</em>');
// Ensure when the same message is of different types it is not duplicated.
$this->assertUniqueText('Non duplicate SafeString / string.');
$this->assertNoUniqueText('Duplicate SafeString / string.');
// Ensure that strings that are not marked as safe are escaped.
$this->assertEscaped('<em>This<span>markup will be</span> escaped</em>.');
}
}

View file

@ -185,9 +185,9 @@ class AttachedAssetsTest extends KernelTestBase {
$rendered_footer_js = \Drupal::service('asset.js.collection_renderer')->render($footer_js);
$this->assertTrue(
count($rendered_footer_js) == 2
&& substr($rendered_footer_js[0]['#value'], 0, 20) === 'var drupalSettings ='
&& $rendered_footer_js[0]['#attributes']['data-drupal-selector'] === 'drupal-settings-json'
&& substr($rendered_footer_js[1]['#attributes']['src'], 0, 7) === 'http://',
'There are 2 JavaScript assets in the footer: one with drupalSettings, one with the sole aggregated JavaScript asset.'
'There are 2 JavaScript assets in the footer: one with drupal settings, one with the sole aggregated JavaScript asset.'
);
}
@ -206,9 +206,9 @@ class AttachedAssetsTest extends KernelTestBase {
$rendered_js = $this->renderer->renderPlain($js_render_array);
// Parse the generated drupalSettings <script> back to a PHP representation.
$startToken = 'drupalSettings = ';
$startToken = '{';
$endToken = '}';
$start = strpos($rendered_js, $startToken) + strlen($startToken);
$start = strpos($rendered_js, $startToken);
$end = strrpos($rendered_js, $endToken);
$json = Unicode::substr($rendered_js, $start, $end - $start + 1);
$parsed_settings = Json::decode($json);

View file

@ -7,7 +7,7 @@
namespace Drupal\system\Tests\Common;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Component\Utility\Html;
use Drupal\Core\Url;
use Drupal\simpletest\KernelTestBase;
@ -46,12 +46,12 @@ class RenderElementTypesTest extends KernelTestBase {
$actual_html = (string) \Drupal::service('renderer')->renderRoot($elements);
$out = '<table><tr>';
$out .= '<td valign="top"><pre>' . SafeMarkup::checkPlain($expected_html) . '</pre></td>';
$out .= '<td valign="top"><pre>' . SafeMarkup::checkPlain($actual_html) . '</pre></td>';
$out .= '<td valign="top"><pre>' . Html::escape($expected_html) . '</pre></td>';
$out .= '<td valign="top"><pre>' . Html::escape($actual_html) . '</pre></td>';
$out .= '</tr></table>';
$this->verbose($out);
$this->assertIdentical($actual_html, $expected_html, SafeMarkup::checkPlain($message));
$this->assertIdentical($actual_html, $expected_html, Html::escape($message));
}
/**
@ -91,8 +91,6 @@ class RenderElementTypesTest extends KernelTestBase {
'#type' => 'html_tag',
'#tag' => 'meta',
'#value' => 'ignored',
'#value_prefix' => 'ignored',
'#value_suffix' => 'ignored',
'#attributes' => array(
'name' => 'description',
'content' => 'Drupal test',
@ -104,12 +102,10 @@ class RenderElementTypesTest extends KernelTestBase {
'#type' => 'html_tag',
'#tag' => 'section',
'#value' => 'value',
'#value_prefix' => 'value_prefix|',
'#value_suffix' => '|value_suffix',
'#attributes' => array(
'class' => array('unicorns'),
),
), '<section class="unicorns">value_prefix|value|value_suffix</section>' . "\n", "#type 'html_tag', non-void element renders properly");
), '<section class="unicorns">value</section>' . "\n", "#type 'html_tag', non-void element renders properly");
// Test empty void element tag.
$this->assertElements(array(

View file

@ -7,6 +7,8 @@
namespace Drupal\system\Tests\Common;
use Drupal\Component\Serialization\Json;
use Drupal\Core\EventSubscriber\MainContentViewSubscriber;
use Drupal\Core\Url;
use Drupal\simpletest\WebTestBase;
@ -24,6 +26,24 @@ class RenderWebTest extends WebTestBase {
*/
public static $modules = array('common_test');
/**
* Asserts the cache context for the wrapper format is always present.
*/
function testWrapperFormatCacheContext() {
$this->drupalGet('common-test/type-link-active-class');
$this->assertIdentical(0, strpos($this->getRawContent(), "<!DOCTYPE html>\n<html"));
$this->assertIdentical('text/html; charset=UTF-8', $this->drupalGetHeader('Content-Type'));
$this->assertTitle('Test active link class | Drupal');
$this->assertCacheContext('url.query_args:' . MainContentViewSubscriber::WRAPPER_FORMAT);
$this->drupalGet('common-test/type-link-active-class', ['query' => [MainContentViewSubscriber::WRAPPER_FORMAT => 'json']]);
$this->assertIdentical('application/json', $this->drupalGetHeader('Content-Type'));
$json = Json::decode($this->getRawContent());
$this->assertEqual(['content', 'title'], array_keys($json));
$this->assertIdentical('Test active link class', $json['title']);
$this->assertCacheContext('url.query_args:' . MainContentViewSubscriber::WRAPPER_FORMAT);
}
/**
* Tests rendering form elements without passing through
* \Drupal::formBuilder()->doBuildForm().

View file

@ -44,7 +44,7 @@ class SimpleTestErrorCollectorTest extends WebTestBase {
if (count($this->collectedErrors) == 3) {
$this->assertError($this->collectedErrors[0], 'Notice', 'Drupal\error_test\Controller\ErrorTestController->generateWarnings()', 'ErrorTestController.php', 'Undefined variable: bananas');
$this->assertError($this->collectedErrors[1], 'Warning', 'Drupal\error_test\Controller\ErrorTestController->generateWarnings()', 'ErrorTestController.php', 'Division by zero');
$this->assertError($this->collectedErrors[2], 'User warning', 'Drupal\error_test\Controller\ErrorTestController->generateWarnings()', 'ErrorTestController.php', 'Drupal is awesome');
$this->assertError($this->collectedErrors[2], 'User warning', 'Drupal\error_test\Controller\ErrorTestController->generateWarnings()', 'ErrorTestController.php', 'Drupal &amp; awesome');
}
else {
// Give back the errors to the log report.

View file

@ -7,7 +7,7 @@
namespace Drupal\system\Tests\Common;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Component\Utility\Html;
use Drupal\simpletest\KernelTestBase;
use Symfony\Component\HttpFoundation\Request;
@ -38,7 +38,7 @@ class TableSortExtenderUnitTest extends KernelTestBase {
$request->query->replace(array());
\Drupal::getContainer()->get('request_stack')->push($request);
$ts = tablesort_init($headers);
$this->verbose(strtr('$ts: <pre>!ts</pre>', array('!ts' => SafeMarkup::checkPlain(var_export($ts, TRUE)))));
$this->verbose(strtr('$ts: <pre>!ts</pre>', array('!ts' => Html::escape(var_export($ts, TRUE)))));
$this->assertEqual($ts, $expected_ts, 'Simple table headers sorted correctly.');
// Test with simple table headers plus $_GET parameters that should _not_
@ -51,7 +51,7 @@ class TableSortExtenderUnitTest extends KernelTestBase {
));
\Drupal::getContainer()->get('request_stack')->push($request);
$ts = tablesort_init($headers);
$this->verbose(strtr('$ts: <pre>!ts</pre>', array('!ts' => SafeMarkup::checkPlain(var_export($ts, TRUE)))));
$this->verbose(strtr('$ts: <pre>!ts</pre>', array('!ts' => Html::escape(var_export($ts, TRUE)))));
$this->assertEqual($ts, $expected_ts, 'Simple table headers plus non-overriding $_GET parameters sorted correctly.');
// Test with simple table headers plus $_GET parameters that _should_
@ -67,7 +67,7 @@ class TableSortExtenderUnitTest extends KernelTestBase {
$expected_ts['sort'] = 'desc';
$expected_ts['query'] = array('alpha' => 'beta');
$ts = tablesort_init($headers);
$this->verbose(strtr('$ts: <pre>!ts</pre>', array('!ts' => SafeMarkup::checkPlain(var_export($ts, TRUE)))));
$this->verbose(strtr('$ts: <pre>!ts</pre>', array('!ts' => Html::escape(var_export($ts, TRUE)))));
$this->assertEqual($ts, $expected_ts, 'Simple table headers plus $_GET parameters sorted correctly.');
// Test complex table headers.
@ -99,7 +99,7 @@ class TableSortExtenderUnitTest extends KernelTestBase {
'sort' => 'desc',
'query' => array(),
);
$this->verbose(strtr('$ts: <pre>!ts</pre>', array('!ts' => SafeMarkup::checkPlain(var_export($ts, TRUE)))));
$this->verbose(strtr('$ts: <pre>!ts</pre>', array('!ts' => Html::escape(var_export($ts, TRUE)))));
$this->assertEqual($ts, $expected_ts, 'Complex table headers sorted correctly.');
// Test complex table headers plus $_GET parameters that should _not_
@ -118,7 +118,7 @@ class TableSortExtenderUnitTest extends KernelTestBase {
'sort' => 'asc',
'query' => array(),
);
$this->verbose(strtr('$ts: <pre>!ts</pre>', array('!ts' => SafeMarkup::checkPlain(var_export($ts, TRUE)))));
$this->verbose(strtr('$ts: <pre>!ts</pre>', array('!ts' => Html::escape(var_export($ts, TRUE)))));
$this->assertEqual($ts, $expected_ts, 'Complex table headers plus non-overriding $_GET parameters sorted correctly.');
// Test complex table headers plus $_GET parameters that _should_
@ -139,7 +139,7 @@ class TableSortExtenderUnitTest extends KernelTestBase {
'query' => array('alpha' => 'beta'),
);
$ts = tablesort_init($headers);
$this->verbose(strtr('$ts: <pre>!ts</pre>', array('!ts' => SafeMarkup::checkPlain(var_export($ts, TRUE)))));
$this->verbose(strtr('$ts: <pre>!ts</pre>', array('!ts' => Html::escape(var_export($ts, TRUE)))));
$this->assertEqual($ts, $expected_ts, 'Complex table headers plus $_GET parameters sorted correctly.');
}
}

View file

@ -33,14 +33,14 @@ class UrlTest extends WebTestBase {
// Test \Drupal::l().
$text = $this->randomMachineName();
$path = "<SCRIPT>alert('XSS')</SCRIPT>";
$encoded_path = "3CSCRIPT%3Ealert%28%27XSS%27%29%3C/SCRIPT%3E";
$link = \Drupal::l($text, Url::fromUserInput('/' . $path));
$sanitized_path = check_url(Url::fromUri('base:' . $path)->toString());
$this->assertTrue(strpos($link, $sanitized_path) !== FALSE, format_string('XSS attack @path was filtered by _l().', array('@path' => $path)));
$this->assertTrue(strpos($link, $encoded_path) !== FALSE && strpos($link, $path) === FALSE, format_string('XSS attack @path was filtered by _l().', array('@path' => $path)));
// Test \Drupal\Core\Url.
$link = Url::fromUri('base:' . $path)->toString();
$sanitized_path = check_url(Url::fromUri('base:' . $path)->toString());
$this->assertTrue(strpos($link, $sanitized_path) !== FALSE, format_string('XSS attack @path was filtered by #theme', ['@path' => $path]));
$this->assertTrue(strpos($link, $encoded_path) !== FALSE && strpos($link, $path) === FALSE, format_string('XSS attack @path was filtered by #theme', ['@path' => $path]));
}
/**

View file

@ -56,6 +56,8 @@ class XssUnitTest extends KernelTestBase {
$expected_plain = 'http://www.example.com/?x=1&y=2';
$expected_html = 'http://www.example.com/?x=1&amp;y=2';
$this->assertIdentical(check_url($url), $expected_html, 'check_url() filters a URL and encodes it for HTML.');
$this->assertIdentical(UrlHelper::stripDangerousProtocols($url), $expected_plain, '\Drupal\Component\Utility\Url::stripDangerousProtocols() filters a URL and returns plain text.');
$this->assertIdentical(UrlHelper::filterBadProtocol($url), $expected_html, '\Drupal\Component\Utility\UrlHelper::filterBadProtocol() filters a URL and encodes it for HTML.');
$this->assertIdentical(UrlHelper::stripDangerousProtocols($url), $expected_plain, '\Drupal\Component\Utility\UrlHelper::stripDangerousProtocols() filters a URL and returns plain text.');
}
}

View file

@ -8,6 +8,7 @@
namespace Drupal\system\Tests\Database;
use Drupal\Core\Database\Database;
use Drupal\Core\Database\SchemaException;
use Drupal\Core\Database\SchemaObjectDoesNotExistException;
use Drupal\Core\Database\SchemaObjectExistsException;
use Drupal\simpletest\KernelTestBase;
@ -99,7 +100,7 @@ class SchemaTest extends KernelTestBase {
$index_exists = Database::getConnection()->schema()->indexExists('test_table', 'test_field');
$this->assertIdentical($index_exists, FALSE, 'Fake index does not exists');
// Add index.
db_add_index('test_table', 'test_field', array('test_field'));
db_add_index('test_table', 'test_field', array('test_field'), $table_specification);
// Test for created index and test for the boolean result of indexExists().
$index_exists = Database::getConnection()->schema()->indexExists('test_table', 'test_field');
$this->assertIdentical($index_exists, TRUE, 'Index created.');
@ -295,6 +296,43 @@ class SchemaTest extends KernelTestBase {
);
db_create_table('test_table_index_length', $table_specification);
$schema_object = Database::getConnection()->schema();
// Ensure expected exception thrown when adding index with missing info.
$expected_exception_message = "MySQL needs the 'test_field_text' field specification in order to normalize the 'test_regular' index";
$missing_field_spec = $table_specification;
unset($missing_field_spec['fields']['test_field_text']);
try {
$schema_object->addIndex('test_table_index_length', 'test_separate', [['test_field_text', 200]], $missing_field_spec);
$this->fail('SchemaException not thrown when adding index with missing information.');
}
catch (SchemaException $e) {
$this->assertEqual($expected_exception_message, $e->getMessage());
}
// Add a separate index.
$schema_object->addIndex('test_table_index_length', 'test_separate', [['test_field_text', 200]], $table_specification);
$table_specification_with_new_index = $table_specification;
$table_specification_with_new_index['indexes']['test_separate'] = [['test_field_text', 200]];
// Ensure that the exceptions of addIndex are thrown as expected.
try {
$schema_object->addIndex('test_table_index_length', 'test_separate', [['test_field_text', 200]], $table_specification);
$this->fail('\Drupal\Core\Database\SchemaObjectExistsException exception missed.');
}
catch (SchemaObjectExistsException $e) {
$this->pass('\Drupal\Core\Database\SchemaObjectExistsException thrown when index already exists.');
}
try {
$schema_object->addIndex('test_table_non_existing', 'test_separate', [['test_field_text', 200]], $table_specification);
$this->fail('\Drupal\Core\Database\SchemaObjectDoesNotExistException exception missed.');
}
catch (SchemaObjectDoesNotExistException $e) {
$this->pass('\Drupal\Core\Database\SchemaObjectDoesNotExistException thrown when index already exists.');
}
// Get index information.
$results = db_query('SHOW INDEX FROM {test_table_index_length}');
$expected_lengths = array(
@ -316,11 +354,14 @@ class SchemaTest extends KernelTestBase {
'test_field_string_ascii_long' => 200,
'test_field_string_short' => NULL,
),
'test_separate' => array(
'test_field_text' => 191,
),
);
// Count the number of columns defined in the indexes.
$column_count = 0;
foreach ($table_specification['indexes'] as $index) {
foreach ($table_specification_with_new_index['indexes'] as $index) {
foreach ($index as $field) {
$column_count++;
}
@ -657,4 +698,63 @@ class SchemaTest extends KernelTestBase {
// Clean-up.
db_drop_table($table_name);
}
/**
* Tests the findTables() method.
*/
public function testFindTables() {
// We will be testing with three tables, two of them using the default
// prefix and the third one with an individually specified prefix.
// Set up a new connection with different connection info.
$connection_info = Database::getConnectionInfo();
// Add per-table prefix to the second table.
$new_connection_info = $connection_info['default'];
$new_connection_info['prefix']['test_2_table'] = $new_connection_info['prefix']['default'] . '_shared_';
Database::addConnectionInfo('test', 'default', $new_connection_info);
Database::setActiveConnection('test');
// Create the tables.
$table_specification = [
'description' => 'Test table.',
'fields' => [
'id' => [
'type' => 'int',
'default' => NULL,
],
],
];
Database::getConnection()->schema()->createTable('test_1_table', $table_specification);
Database::getConnection()->schema()->createTable('test_2_table', $table_specification);
Database::getConnection()->schema()->createTable('the_third_table', $table_specification);
// Check the "all tables" syntax.
$tables = Database::getConnection()->schema()->findTables('%');
sort($tables);
$expected = [
// The 'config' table is added by
// \Drupal\simpletest\KernelTestBase::containerBuild().
'config',
'test_1_table',
// This table uses a per-table prefix, yet it is returned as un-prefixed.
'test_2_table',
'the_third_table',
];
$this->assertEqual($tables, $expected, 'All tables were found.');
// Check the restrictive syntax.
$tables = Database::getConnection()->schema()->findTables('test_%');
sort($tables);
$expected = [
'test_1_table',
'test_2_table',
];
$this->assertEqual($tables, $expected, 'Two tables were found.');
// Go back to the initial connection.
Database::setActiveConnection('default');
}
}

View file

@ -7,6 +7,7 @@
namespace Drupal\system\Tests\Database;
use Drupal\Core\Database\InvalidQueryException;
use Drupal\Core\Database\Database;
/**
* Tests the Select query builder.
@ -57,10 +58,47 @@ class SelectTest extends DatabaseTestBase {
$records = $result->fetchAll();
$query = (string) $query;
$expected = "/* Testing query comments SELECT nid FROM {node}; -- */";
$expected = "/* Testing query comments * / SELECT nid FROM {node}; -- */ SELECT test.name AS name, test.age AS age\nFROM \n{test} test";
$this->assertEqual(count($records), 4, 'Returned the correct number of rows.');
$this->assertNotIdentical(FALSE, strpos($query, $expected), 'The flattened query contains the sanitised comment string.');
$connection = Database::getConnection();
foreach ($this->makeCommentsProvider() as $test_set) {
list($expected, $comments) = $test_set;
$this->assertEqual($expected, $connection->makeComment($comments));
}
}
/**
* Provides expected and input values for testVulnerableComment().
*/
function makeCommentsProvider() {
return [
[
'/* */ ',
[''],
],
// Try and close the comment early.
[
'/* Exploit * / DROP TABLE node; -- */ ',
['Exploit */ DROP TABLE node; --'],
],
// Variations on comment closing.
[
'/* Exploit * / * / DROP TABLE node; -- */ ',
['Exploit */*/ DROP TABLE node; --'],
],
[
'/* Exploit * * // DROP TABLE node; -- */ ',
['Exploit **// DROP TABLE node; --'],
],
// Try closing the comment in the second string which is appended.
[
'/* Exploit * / DROP TABLE node; --; Another try * / DROP TABLE node; -- */ ',
['Exploit */ DROP TABLE node; --', 'Another try */ DROP TABLE node; --'],
],
];
}
/**

View file

@ -0,0 +1,61 @@
<?php
/**
* @file
* Contains \Drupal\system\Tests\Database\UpsertTest.
*/
namespace Drupal\system\Tests\Database;
use Drupal\Core\Database\Database;
/**
* Tests the Upsert query builder.
*
* @group Database
*/
class UpsertTest extends DatabaseTestBase {
/**
* Confirms that we can upsert (update-or-insert) records successfully.
*/
public function testUpsert() {
$connection = Database::getConnection();
$num_records_before = $connection->query('SELECT COUNT(*) FROM {test_people}')->fetchField();
$upsert = $connection->upsert('test_people')
->key('job')
->fields(['job', 'age', 'name']);
// Add a new row.
$upsert->values([
'job' => 'Presenter',
'age' => 31,
'name' => 'Tiffany',
]);
// Update an existing row.
$upsert->values([
'job' => 'Speaker',
// The initial age was 30.
'age' => 32,
'name' => 'Meredith',
]);
$upsert->execute();
$num_records_after = $connection->query('SELECT COUNT(*) FROM {test_people}')->fetchField();
$this->assertEqual($num_records_before + 1, $num_records_after, 'Rows were inserted and updated properly.');
$person = $connection->query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Presenter'))->fetch();
$this->assertEqual($person->job, 'Presenter', 'Job set correctly.');
$this->assertEqual($person->age, 31, 'Age set correctly.');
$this->assertEqual($person->name, 'Tiffany', 'Name set correctly.');
$person = $connection->query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Speaker'))->fetch();
$this->assertEqual($person->job, 'Speaker', 'Job was not changed.');
$this->assertEqual($person->age, 32, 'Age updated correctly.');
$this->assertEqual($person->name, 'Meredith', 'Name was not changed.');
}
}

View file

@ -55,13 +55,11 @@ class DrupalKernelTest extends KernelTestBase {
* A request object to use in booting the kernel.
* @param array $modules_enabled
* A list of modules to enable on the kernel.
* @param bool $read_only
* Build the kernel in a read only state.
*
* @return \Drupal\Core\DrupalKernel
* New kernel for testing.
*/
protected function getTestKernel(Request $request, array $modules_enabled = NULL, $read_only = FALSE) {
protected function getTestKernel(Request $request, array $modules_enabled = NULL) {
// Manually create kernel to avoid replacing settings.
$class_loader = require DRUPAL_ROOT . '/autoload.php';
$kernel = DrupalKernel::createFromRequest($request, $class_loader, 'testing');
@ -72,11 +70,6 @@ class DrupalKernelTest extends KernelTestBase {
}
$kernel->boot();
if ($read_only) {
$php_storage = Settings::get('php_storage');
$php_storage['service_container']['class'] = 'Drupal\Component\PhpStorage\FileReadOnlyStorage';
$this->settingsSet('php_storage', $php_storage);
}
return $kernel;
}
@ -98,24 +91,19 @@ class DrupalKernelTest extends KernelTestBase {
$kernel = $this->getTestKernel($request);
$container = $kernel->getContainer();
$refClass = new \ReflectionClass($container);
$is_compiled_container =
$refClass->getParentClass()->getName() == 'Drupal\Core\DependencyInjection\Container' &&
!$refClass->isSubclassOf('Symfony\Component\DependencyInjection\ContainerBuilder');
$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);
// Now use the read-only storage implementation, simulating a "production"
// environment.
$container = $this->getTestKernel($request, NULL, TRUE)
// Get the container another time, simulating a "production" environment.
$container = $this->getTestKernel($request, NULL)
->getContainer();
$refClass = new \ReflectionClass($container);
$is_compiled_container =
$refClass->getParentClass()->getName() == 'Drupal\Core\DependencyInjection\Container' &&
!$refClass->isSubclassOf('Symfony\Component\DependencyInjection\ContainerBuilder');
$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
@ -137,16 +125,16 @@ class DrupalKernelTest extends KernelTestBase {
// 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, TRUE);
$this->getTestKernel($request, $modules_enabled);
// Instantiate it a second time and we should still get a ContainerBuilder
// class because we are using the read-only PHP storage.
$kernel = $this->getTestKernel($request, $modules_enabled, TRUE);
// 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->assertTrue($is_container_builder, 'Container is a builder');
$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');

View file

@ -9,7 +9,7 @@ namespace Drupal\system\Tests\Entity;
use Drupal\Component\Serialization\Json;
use Drupal\Component\Utility\Crypt;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Component\Utility\Html;
use Drupal\Component\Utility\Tags;
use Drupal\Core\Site\Settings;
use Drupal\system\Controller\EntityAutocompleteController;
@ -65,8 +65,8 @@ class EntityAutocompleteTest extends EntityUnitTestBase {
// We should get both entities in a JSON encoded string.
$input = '10/';
$data = $this->getAutocompleteResult($input);
$this->assertIdentical($data[0]['label'], SafeMarkup::checkPlain($entity_1->name->value), 'Autocomplete returned the first matching entity');
$this->assertIdentical($data[1]['label'], SafeMarkup::checkPlain($entity_2->name->value), 'Autocomplete returned the second matching entity');
$this->assertIdentical($data[0]['label'], Html::escape($entity_1->name->value), 'Autocomplete returned the first matching entity');
$this->assertIdentical($data[1]['label'], Html::escape($entity_2->name->value), 'Autocomplete returned the second matching entity');
// Try to autocomplete a entity label that matches the first entity.
// We should only get the first entity in a JSON encoded string.
@ -74,7 +74,7 @@ class EntityAutocompleteTest extends EntityUnitTestBase {
$data = $this->getAutocompleteResult($input);
$target = array(
'value' => $entity_1->name->value . ' (1)',
'label' => SafeMarkup::checkPlain($entity_1->name->value),
'label' => Html::escape($entity_1->name->value),
);
$this->assertIdentical(reset($data), $target, 'Autocomplete returns only the expected matching entity.');
@ -82,7 +82,7 @@ class EntityAutocompleteTest extends EntityUnitTestBase {
// the first entity is already typed in the autocomplete (tags) widget.
$input = $entity_1->name->value . ' (1), 10/17';
$data = $this->getAutocompleteResult($input);
$this->assertIdentical($data[0]['label'], SafeMarkup::checkPlain($entity_2->name->value), 'Autocomplete returned the second matching entity');
$this->assertIdentical($data[0]['label'], Html::escape($entity_2->name->value), 'Autocomplete returned the second matching entity');
// Try to autocomplete a entity label with both a comma and a slash.
$input = '"label with, and / t';
@ -92,7 +92,7 @@ class EntityAutocompleteTest extends EntityUnitTestBase {
$n = Tags::encode($n);
$target = array(
'value' => $n,
'label' => SafeMarkup::checkPlain($entity_3->name->value),
'label' => Html::escape($entity_3->name->value),
);
$this->assertIdentical(reset($data), $target, 'Autocomplete returns an entity label containing a comma and a slash.');
}

View file

@ -9,6 +9,7 @@ namespace Drupal\system\Tests\Entity;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\EventSubscriber\MainContentViewSubscriber;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\Url;
@ -337,6 +338,7 @@ abstract class EntityCacheTagsTestBase extends PageCacheTagsTestBase {
// The default cache contexts for rendered entities.
$default_cache_contexts = ['languages:' . LanguageInterface::TYPE_INTERFACE, 'theme', 'user.permissions'];
$entity_cache_contexts = $default_cache_contexts;
$page_cache_contexts = Cache::mergeContexts($default_cache_contexts, ['url.query_args:' . MainContentViewSubscriber::WRAPPER_FORMAT]);
// Cache tags present on every rendered page.
// 'user.permissions' is a required cache context, and responses that vary
@ -428,7 +430,7 @@ abstract class EntityCacheTagsTestBase extends PageCacheTagsTestBase {
$this->verifyPageCache($empty_entity_listing_url, 'HIT', $empty_entity_listing_cache_tags);
// Verify the entity type's list cache contexts are present.
$contexts_in_header = $this->drupalGetHeader('X-Drupal-Cache-Contexts');
$this->assertEqual(Cache::mergeContexts($default_cache_contexts, $this->getAdditionalCacheContextsForEntityListing()), empty($contexts_in_header) ? [] : explode(' ', $contexts_in_header));
$this->assertEqual(Cache::mergeContexts($page_cache_contexts, $this->getAdditionalCacheContextsForEntityListing()), empty($contexts_in_header) ? [] : explode(' ', $contexts_in_header));
$this->pass("Test listing containing referenced entity.", 'Debug');
@ -438,7 +440,7 @@ abstract class EntityCacheTagsTestBase extends PageCacheTagsTestBase {
$this->verifyPageCache($nonempty_entity_listing_url, 'HIT', $nonempty_entity_listing_cache_tags);
// Verify the entity type's list cache contexts are present.
$contexts_in_header = $this->drupalGetHeader('X-Drupal-Cache-Contexts');
$this->assertEqual(Cache::mergeContexts($default_cache_contexts, $this->getAdditionalCacheContextsForEntityListing()), empty($contexts_in_header) ? [] : explode(' ', $contexts_in_header));
$this->assertEqual(Cache::mergeContexts($page_cache_contexts, $this->getAdditionalCacheContextsForEntityListing()), empty($contexts_in_header) ? [] : explode(' ', $contexts_in_header));
// Verify that after modifying the referenced entity, there is a cache miss
@ -509,13 +511,12 @@ abstract class EntityCacheTagsTestBase extends PageCacheTagsTestBase {
}
$bundle_entity_type = $this->entity->getEntityType()->getBundleEntityType();
if ($bundle_entity_type !== 'bundle') {
if ($bundle_entity_type_id = $this->entity->getEntityType()->getBundleEntityType()) {
// Verify that after modifying the corresponding bundle entity, there is a
// cache miss for both the referencing entity, and the listing of
// referencing entities, but not for any other routes.
$this->pass("Test modification of referenced entity's bundle entity.", 'Debug');
$bundle_entity = entity_load($bundle_entity_type, $this->entity->bundle());
$bundle_entity = entity_load($bundle_entity_type_id, $this->entity->bundle());
$bundle_entity->save();
$this->verifyPageCache($referencing_entity_url, 'MISS');
$this->verifyPageCache($listing_url, 'MISS');

View file

@ -125,6 +125,17 @@ trait EntityDefinitionTestTrait {
$this->addBaseField('text');
}
/**
* Promotes a field to an entity key.
*/
protected function makeBaseFieldEntityKey() {
$entity_type = clone $this->entityManager->getDefinition('entity_test_update');
$entity_keys = $entity_type->getKeys();
$entity_keys['new_base_field'] = 'new_base_field';
$entity_type->set('entity_keys', $entity_keys);
$this->state->set('entity_test_update.entity_type', $entity_type);
}
/**
* Removes the new base field from the 'entity_test_update' entity type.
*/

View file

@ -7,16 +7,17 @@
namespace Drupal\system\Tests\Entity;
use Drupal\Component\Plugin\Exception\PluginNotFoundException;
use Drupal\Core\Database\Database;
use Drupal\Core\Database\DatabaseExceptionWrapper;
use Drupal\Core\Database\IntegrityConstraintViolationException;
use Drupal\Core\Entity\EntityDefinitionUpdateManagerInterface;
use Drupal\Core\Entity\ContentEntityType;
use Drupal\Core\Entity\EntityStorageException;
use Drupal\Core\Entity\EntityTypeEvents;
use Drupal\Core\Entity\Exception\FieldStorageDefinitionUpdateForbiddenException;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\Field\FieldStorageDefinitionEvents;
use Drupal\Core\Language\LanguageInterface;
use Drupal\entity_test\FieldStorageDefinition;
/**
* Tests EntityDefinitionUpdateManager functionality.
@ -508,6 +509,18 @@ class EntityDefinitionUpdateTest extends EntityUnitTestBase {
// Run the update and ensure the index is deleted.
$this->entityDefinitionUpdateManager->applyUpdates();
$this->assertFalse($this->database->schema()->indexExists('entity_test_update', 'entity_test_update__new_index'), 'Index deleted.');
// Test that composite indexes are handled correctly when dropping and
// re-creating one of their columns.
$this->addEntityIndex();
$this->entityDefinitionUpdateManager->applyUpdates();
$storage_definition = $this->entityDefinitionUpdateManager->getFieldStorageDefinition('name', 'entity_test_update');
$this->entityDefinitionUpdateManager->updateFieldStorageDefinition($storage_definition);
$this->assertTrue($this->database->schema()->indexExists('entity_test_update', 'entity_test_update__new_index'), 'Index created.');
$this->entityDefinitionUpdateManager->uninstallFieldStorageDefinition($storage_definition);
$this->assertFalse($this->database->schema()->indexExists('entity_test_update', 'entity_test_update__new_index'), 'Index deleted.');
$this->entityDefinitionUpdateManager->installFieldStorageDefinition('name', 'entity_test_update', 'entity_test', $storage_definition);
$this->assertTrue($this->database->schema()->indexExists('entity_test_update', 'entity_test_update__new_index'), 'Index created again.');
}
/**
@ -609,21 +622,178 @@ class EntityDefinitionUpdateTest extends EntityUnitTestBase {
* Tests ::applyEntityUpdate() and ::applyFieldUpdate().
*/
public function testSingleActionCalls() {
// Ensure that the methods return FALSE when called with bogus information.
$this->assertFalse($this->entityDefinitionUpdateManager->applyEntityUpdate(EntityDefinitionUpdateManagerInterface::DEFINITION_CREATED, 'foo'), 'Calling applyEntityUpdate() with a non-existent entity returns FALSE.');
$this->assertFalse($this->entityDefinitionUpdateManager->applyFieldUpdate(EntityDefinitionUpdateManagerInterface::DEFINITION_CREATED, 'foo', 'bar'), 'Calling applyFieldUpdate() with a non-existent entity returns FALSE.');
$this->assertFalse($this->entityDefinitionUpdateManager->applyFieldUpdate(EntityDefinitionUpdateManagerInterface::DEFINITION_CREATED, 'entity_test_update', 'bar'), 'Calling applyFieldUpdate() with a non-existent field returns FALSE.');
$this->assertFalse($this->entityDefinitionUpdateManager->applyEntityUpdate(EntityDefinitionUpdateManagerInterface::DEFINITION_CREATED, 'entity_test_update'), 'Calling applyEntityUpdate() with an $op that is not applicable to the entity type returns FALSE.');
$this->assertFalse($this->entityDefinitionUpdateManager->applyFieldUpdate(EntityDefinitionUpdateManagerInterface::DEFINITION_DELETED, 'entity_test_update', 'new_base_field'), 'Calling applyFieldUpdate() with an $op that is not applicable to the field returns FALSE.');
$db_schema = $this->database->schema();
// Ensure that a non-existing entity type cannot be installed.
$message = 'A non-existing entity type cannot be installed';
try {
$this->entityDefinitionUpdateManager->installEntityType(new ContentEntityType(['id' => 'foo']));
$this->fail($message);
}
catch (PluginNotFoundException $e) {
$this->pass($message);
}
// Ensure that a field cannot be installed on non-existing entity type.
$message = 'A field cannot be installed on a non-existing entity type';
try {
$storage_definition = BaseFieldDefinition::create('string')
->setLabel(t('A new revisionable base field'))
->setRevisionable(TRUE);
$this->entityDefinitionUpdateManager->installFieldStorageDefinition('bar', 'foo', 'entity_test', $storage_definition);
$this->fail($message);
}
catch (PluginNotFoundException $e) {
$this->pass($message);
}
// Ensure that a non-existing field cannot be installed.
$storage_definition = BaseFieldDefinition::create('string')
->setLabel(t('A new revisionable base field'))
->setRevisionable(TRUE);
$this->entityDefinitionUpdateManager->installFieldStorageDefinition('bar', 'entity_test_update', 'entity_test', $storage_definition);
$this->assertFalse($db_schema->fieldExists('entity_test_update', 'bar'), "A non-existing field cannot be installed.");
// Ensure that installing an existing entity type is a no-op.
$entity_type = $this->entityDefinitionUpdateManager->getEntityType('entity_test_update');
$this->entityDefinitionUpdateManager->installEntityType($entity_type);
$this->assertTrue($db_schema->tableExists('entity_test_update'), 'Installing an existing entity type is a no-op');
// Create a new base field.
$this->addRevisionableBaseField();
$this->assertTrue($this->entityDefinitionUpdateManager->applyFieldUpdate(EntityDefinitionUpdateManagerInterface::DEFINITION_CREATED, 'entity_test_update', 'new_base_field'), 'Calling applyFieldUpdate() correctly returns TRUE.');
$this->assertTrue($this->database->schema()->fieldExists('entity_test_update', 'new_base_field'), "New field 'new_base_field' has been created on the 'entity_test_update' table.");
$storage_definition = BaseFieldDefinition::create('string')
->setLabel(t('A new revisionable base field'))
->setRevisionable(TRUE);
$this->assertFalse($db_schema->fieldExists('entity_test_update', 'new_base_field'), "New field 'new_base_field' does not exist before applying the update.");
$this->entityDefinitionUpdateManager->installFieldStorageDefinition('new_base_field', 'entity_test_update', 'entity_test', $storage_definition);
$this->assertTrue($db_schema->fieldExists('entity_test_update', 'new_base_field'), "New field 'new_base_field' has been created on the 'entity_test_update' table.");
// Ensure that installing an existing entity type is a no-op.
$this->entityDefinitionUpdateManager->installFieldStorageDefinition('new_base_field', 'entity_test_update', 'entity_test', $storage_definition);
$this->assertTrue($db_schema->fieldExists('entity_test_update', 'new_base_field'), 'Installing an existing entity type is a no-op');
// Update an existing field schema.
$this->modifyBaseField();
$storage_definition = BaseFieldDefinition::create('text')
->setName('new_base_field')
->setTargetEntityTypeId('entity_test_update')
->setLabel(t('A new revisionable base field'))
->setRevisionable(TRUE);
$this->entityDefinitionUpdateManager->updateFieldStorageDefinition($storage_definition);
$this->assertFalse($db_schema->fieldExists('entity_test_update', 'new_base_field'), "Previous schema for 'new_base_field' no longer exists.");
$this->assertTrue(
$db_schema->fieldExists('entity_test_update', 'new_base_field__value') && $db_schema->fieldExists('entity_test_update', 'new_base_field__format'),
"New schema for 'new_base_field' has been created."
);
// Drop an existing field schema.
$this->entityDefinitionUpdateManager->uninstallFieldStorageDefinition($storage_definition);
$this->assertFalse(
$db_schema->fieldExists('entity_test_update', 'new_base_field__value') || $db_schema->fieldExists('entity_test_update', 'new_base_field__format'),
"The schema for 'new_base_field' has been dropped."
);
// Make the entity type revisionable.
$this->updateEntityTypeToRevisionable();
$this->assertTrue($this->entityDefinitionUpdateManager->applyEntityUpdate(EntityDefinitionUpdateManagerInterface::DEFINITION_UPDATED, 'entity_test_update'), 'Calling applyEntityUpdate() correctly returns TRUE.');
$this->assertTrue($this->database->schema()->tableExists('entity_test_update_revision'), "The 'entity_test_update_revision' table has been created.");
$this->assertFalse($db_schema->tableExists('entity_test_update_revision'), "The 'entity_test_update_revision' does not exist before applying the update.");
$entity_type = $this->entityDefinitionUpdateManager->getEntityType('entity_test_update');
$keys = $entity_type->getKeys();
$keys['revision'] = 'revision_id';
$entity_type->set('entity_keys', $keys);
$this->entityDefinitionUpdateManager->updateEntityType($entity_type);
$this->assertTrue($db_schema->tableExists('entity_test_update_revision'), "The 'entity_test_update_revision' table has been created.");
}
/**
* Ensures that a new field and index on a shared table are created.
*
* @see Drupal\Core\Entity\Sql\SqlContentEntityStorageSchema::createSharedTableSchema
*/
public function testCreateFieldAndIndexOnSharedTable() {
$this->addBaseField();
$this->addBaseFieldIndex();
$this->entityDefinitionUpdateManager->applyUpdates();
$this->assertTrue($this->database->schema()->fieldExists('entity_test_update', 'new_base_field'), "New field 'new_base_field' has been created on the 'entity_test_update' table.");
$this->assertTrue($this->database->schema()->indexExists('entity_test_update', 'entity_test_update_field__new_base_field'), "New index 'entity_test_update_field__new_base_field' has been created on the 'entity_test_update' table.");
// Check index size in for MySQL.
if (Database::getConnection()->driver() == 'mysql') {
$result = Database::getConnection()->query('SHOW INDEX FROM {entity_test_update} WHERE key_name = \'entity_test_update_field__new_base_field\' and column_name = \'new_base_field\'')->fetchObject();
$this->assertEqual(191, $result->Sub_part, 'The index length has been restricted to 191 characters for UTF8MB4 compatibility.');
}
}
/**
* Ensures that a new entity level index is created when data exists.
*
* @see Drupal\Core\Entity\Sql\SqlContentEntityStorageSchema::onEntityTypeUpdate
*/
public function testCreateIndexUsingEntityStorageSchemaWithData() {
// Save an entity.
$name = $this->randomString();
$storage = $this->entityManager->getStorage('entity_test_update');
$entity = $storage->create(array('name' => $name));
$entity->save();
// Create an index.
$indexes = array(
'entity_test_update__type_index' => array('type'),
);
$this->state->set('entity_test_update.additional_entity_indexes', $indexes);
$this->entityDefinitionUpdateManager->applyUpdates();
$this->assertTrue($this->database->schema()->indexExists('entity_test_update', 'entity_test_update__type_index'), "New index 'entity_test_update__type_index' has been created on the 'entity_test_update' table.");
// Check index size in for MySQL.
if (Database::getConnection()->driver() == 'mysql') {
$result = Database::getConnection()->query('SHOW INDEX FROM {entity_test_update} WHERE key_name = \'entity_test_update__type_index\' and column_name = \'type\'')->fetchObject();
$this->assertEqual(191, $result->Sub_part, 'The index length has been restricted to 191 characters for UTF8MB4 compatibility.');
}
}
/**
* Tests updating a base field when it has existing data.
*/
public function testBaseFieldEntityKeyUpdateWithExistingData() {
// Add the base field and run the update.
$this->addBaseField();
$this->entityDefinitionUpdateManager->applyUpdates();
// Save an entity with the base field populated.
$this->entityManager->getStorage('entity_test_update')->create(['new_base_field' => $this->randomString()])->save();
// Save an entity with the base field not populated.
/** @var \Drupal\entity_test\Entity\EntityTestUpdate $entity */
$entity = $this->entityManager->getStorage('entity_test_update')->create();
$entity->save();
// Promote the base field to an entity key. This will trigger the addition
// of a NOT NULL constraint.
$this->makeBaseFieldEntityKey();
// Try to apply the update and verify they fail since we have a NULL value.
$message = 'An error occurs when trying to enabling NOT NULL constraints with NULL data.';
try {
$this->entityDefinitionUpdateManager->applyUpdates();
$this->fail($message);
}
catch (EntityStorageException $e) {
$this->pass($message);
}
// Check that the update is correctly applied when no NULL data is left.
$entity->set('new_base_field', $this->randomString());
$entity->save();
$this->entityDefinitionUpdateManager->applyUpdates();
$this->pass('The update is correctly performed when no NULL data exists.');
// Check that the update actually applied a NOT NULL constraint.
$entity->set('new_base_field', NULL);
$message = 'The NOT NULL constraint was correctly applied.';
try {
$entity->save();
$this->fail($message);
}
catch (EntityStorageException $e) {
$this->pass($message);
}
}
}

View file

@ -677,6 +677,7 @@ class EntityFieldTest extends EntityUnitTestBase {
$node = entity_create('node', array(
'type' => 'page',
'uid' => $user->id(),
'title' => $this->randomString(),
));
$reference->setValue($node);
$violations = $reference->validate();
@ -699,6 +700,7 @@ class EntityFieldTest extends EntityUnitTestBase {
$node = entity_create('node', array(
'type' => 'article',
'uid' => $user->id(),
'title' => $this->randomString(),
));
$node->save();
$reference->setValue($node);

View file

@ -222,7 +222,7 @@ class EntityQueryTest extends EntityUnitTestBase {
$query = $this->factory->get('entity_test_mulrev');
$group_blue = $query->andConditionGroup()->condition("$figures.color", array('blue'), 'IN');
$group_red = $query->andConditionGroup()->condition("$figures.color", array('red'), 'IN');
$query
$this->queryResults = $query
->condition($group_blue)
->condition($group_red)
->sort('id')
@ -554,7 +554,7 @@ class EntityQueryTest extends EntityUnitTestBase {
}
}
}
$this->assertTrue($ok, format_string("$i is after all entities in bundle2"));
$this->assertTrue($ok, "$i is after all entities in bundle2");
}
}

View file

@ -8,7 +8,7 @@
namespace Drupal\system\Tests\Entity\EntityReferenceSelection;
use Drupal\comment\Tests\CommentTestTrait;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Component\Utility\Html;
use Drupal\Core\Language\LanguageInterface;
use Drupal\comment\CommentInterface;
use Drupal\simpletest\WebTestBase;
@ -110,7 +110,7 @@ class EntityReferenceSelectionAccessTest extends WebTestBase {
$node = entity_create('node', $values);
$node->save();
$nodes[$key] = $node;
$node_labels[$key] = SafeMarkup::checkPlain($node->label());
$node_labels[$key] = Html::escape($node->label());
}
// Test as a non-admin.
@ -241,7 +241,7 @@ class EntityReferenceSelectionAccessTest extends WebTestBase {
$account = $values;
}
$users[$key] = $account;
$user_labels[$key] = SafeMarkup::checkPlain($account->getUsername());
$user_labels[$key] = Html::escape($account->getUsername());
}
// Test as a non-admin.
@ -416,7 +416,7 @@ class EntityReferenceSelectionAccessTest extends WebTestBase {
$comment = entity_create('comment', $values);
$comment->save();
$comments[$key] = $comment;
$comment_labels[$key] = SafeMarkup::checkPlain($comment->label());
$comment_labels[$key] = Html::escape($comment->label());
}
// Test as a non-admin.

View file

@ -7,7 +7,7 @@
namespace Drupal\system\Tests\Entity\EntityReferenceSelection;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Component\Utility\Html;
use Drupal\node\Entity\Node;
use Drupal\node\Entity\NodeType;
use Drupal\system\Tests\Entity\EntityUnitTestBase;
@ -93,7 +93,7 @@ class EntityReferenceSelectionSortTest extends EntityUnitTestBase {
$node = Node::create($values);
$node->save();
$nodes[$key] = $node;
$node_labels[$key] = SafeMarkup::checkPlain($node->label());
$node_labels[$key] = Html::escape($node->label());
}
$selection_options = array(

View file

@ -0,0 +1,60 @@
<?php
/**
* @file
* Contains \Drupal\system\Tests\Entity\EntityRevisionTranslationTest.
*/
namespace Drupal\system\Tests\Entity;
use Drupal\entity_test\Entity\EntityTestMulRev;
use Drupal\language\Entity\ConfigurableLanguage;
/**
* Tests proper revision propagation of entities.
*
* @group Entity
*/
class EntityRevisionTranslationTest extends EntityUnitTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['language'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
// Enable an additional language.
ConfigurableLanguage::createFromLangcode('de')->save();
$this->installEntitySchema('entity_test_mulrev');
}
/**
* Tests if the translation object has the right revision id after new revision.
*/
public function testNewRevisionAfterTranslation() {
$user = $this->createUser();
// Create a test entity.
$entity = EntityTestMulRev::create([
'name' => $this->randomString(),
'user_id' => $user->id(),
'language' => 'en',
]);
$entity->save();
$old_rev_id = $entity->getRevisionId();
$translation = $entity->addTranslation('de');
$translation->setNewRevision();
$translation->save();
$this->assertTrue($translation->getRevisionId() > $old_rev_id, 'The saved translation in new revision has a newer revision id.');
$this->assertTrue($this->reloadEntity($entity)->getRevisionId() > $old_rev_id, 'The entity from the storage has a newer revision id.');
}
}

View file

@ -106,6 +106,7 @@ class EntityTranslationFormTest extends WebTestBase {
// Create a body translation and check the form language.
$langcode2 = $this->langcodes[1];
$node->getTranslation($langcode2)->title->value = $this->randomString();
$node->getTranslation($langcode2)->body->value = $this->randomMachineName(16);
$node->getTranslation($langcode2)->setOwnerId($web_user->id());
$node->save();

View file

@ -39,6 +39,7 @@ class EntityViewControllerTest extends WebTestBase {
$this->entities[] = $entity_test;
}
$this->drupalLogin($this->drupalCreateUser(['view test entity']));
}
/**
@ -46,12 +47,9 @@ class EntityViewControllerTest extends WebTestBase {
*/
function testEntityViewController() {
$get_label_markup = function($label) {
return '<h1><div class="field field-entity-test--name field-name-name field-type-string field-label-hidden">
<div class="field-items">
<div class="field-item">' . $label . '</div>
</div>
</div>
</h1>';
return '<h1>
<div class="field field--name-name field--type-string field--label-hidden field__item">' . $label . '</div>
</h1>';
};
foreach ($this->entities as $entity) {

View file

@ -81,12 +81,11 @@ abstract class EntityWithUriCacheTagsTestBase extends EntityCacheTagsTestBase {
$this->verifyPageCache($entity_url, 'HIT');
$bundle_entity_type = $this->entity->getEntityType()->getBundleEntityType();
if ($bundle_entity_type !== 'bundle') {
if ($bundle_entity_type_id = $this->entity->getEntityType()->getBundleEntityType()) {
// Verify that after modifying the corresponding bundle entity, there is a
// cache miss.
$this->pass("Test modification of entity's bundle entity.", 'Debug');
$bundle_entity = entity_load($bundle_entity_type, $this->entity->bundle());
$bundle_entity = entity_load($bundle_entity_type_id, $this->entity->bundle());
$bundle_entity->save();
$this->verifyPageCache($entity_url, 'MISS');

View file

@ -0,0 +1,25 @@
<?php
/**
* @file
* Contains \Drupal\system\Tests\Entity\Update\SqlContentEntityStorageSchemaIndexFilledTest.
*/
namespace Drupal\system\Tests\Entity\Update;
/**
* Runs SqlContentEntityStorageSchemaIndexTest with a dump filled with content.
*
* @group Entity
*/
class SqlContentEntityStorageSchemaIndexFilledTest extends SqlContentEntityStorageSchemaIndexTest {
/**
* {@inheritdoc}
*/
protected function setDatabaseDumpFiles() {
parent::setDatabaseDumpFiles();
$this->databaseDumpFiles[0] = __DIR__ . '/../../../../tests/fixtures/update/drupal-8.filled.standard.php.gz';
}
}

View file

@ -19,25 +19,16 @@ class SqlContentEntityStorageSchemaIndexTest extends UpdatePathTestBase {
/**
* {@inheritdoc}
*/
protected static $modules = ['update_order_test'];
/**
* {@inheritdoc}
*/
public function setUp() {
protected function setDatabaseDumpFiles() {
$this->databaseDumpFiles = [
__DIR__ . '/../../../../tests/fixtures/update/drupal-8.bare.standard.php.gz',
];
parent::setUp();
}
/**
* Tests entity and field schema database updates and execution order.
*/
public function testIndex() {
// Enable the hook implementations in the update_order_test module.
\Drupal::state()->set('update_order_test', TRUE);
// The initial Drupal 8 database dump before any updates does not include
// the entity ID in the entity field data table indices that were added in
// https://www.drupal.org/node/2261669.
@ -45,38 +36,12 @@ class SqlContentEntityStorageSchemaIndexTest extends UpdatePathTestBase {
$this->assertFalse(db_index_exists('node_field_data', 'node__id__default_langcode__langcode'), 'Index node__id__default_langcode__langcode does not exist prior to running updates.');
$this->assertFalse(db_index_exists('users_field_data', 'user__id__default_langcode__langcode'), 'Index users__id__default_langcode__langcode does not exist prior to running updates.');
// Running database updates should automatically update the entity schemata
// to add the indices from https://www.drupal.org/node/2261669.
// Running database updates should update the entity schemata to add the
// indices from https://www.drupal.org/node/2261669.
$this->runUpdates();
$this->assertFalse(db_index_exists('node_field_data', 'node__default_langcode'), 'Index node__default_langcode properly removed.');
$this->assertTrue(db_index_exists('node_field_data', 'node__id__default_langcode__langcode'), 'Index node__id__default_langcode__langcode properly created on the node_field_data table.');
$this->assertTrue(db_index_exists('users_field_data', 'user__id__default_langcode__langcode'), 'Index users__id__default_langcode__langcode properly created on the user_field_data table.');
// Ensure that hook_update_N() implementations were in the expected order
// relative to the entity and field updates. The expected order is:
// 1. Initial Drupal 8.0.0-beta12 installation with no indices.
// 2. update_order_test_update_8001() is invoked.
// 3. update_order_test_update_8002() is invoked.
// 4. update_order_test_update_8002() explicitly applies the updates for
// the update_order_test_field storage. See update_order_test.module.
// 5. update_order_test_update_8002() explicitly applies the updates for
// the node entity type indices listed above.
// 6. The remaining entity schema updates are applied automatically after
// all update hook implementations have run, which applies the user
// index update.
$this->assertTrue(\Drupal::state()->get('update_order_test_update_8001', FALSE), 'Index node__default_langcode still existed during update_order_test_update_8001(), indicating that it ran before the entity type updates.');
// Node updates were run during update_order_test_update_8002().
$this->assertFalse(\Drupal::state()->get('update_order_test_update_8002_node__default_langcode', TRUE), 'The node__default_langcode index was removed during update_order_test_update_8002().');
$this->assertTrue(\Drupal::state()->get('update_order_test_update_8002_node__id__default_langcode__langcode', FALSE), 'The node__id__default_langcode__langcode index was created during update_order_test_update_8002().');
// Ensure that the base field created by update_order_test_update_8002() is
// created when we expect.
$this->assertFalse(\Drupal::state()->get('update_order_test_update_8002_update_order_test_before', TRUE), 'The update_order_test field was not been created on Node before update_order_test_update_8002().');
$this->assertTrue(\Drupal::state()->get('update_order_test_update_8002_update_order_test_after', FALSE), 'The update_order_test field was created on Node by update_order_test_update_8002().');
// User update were not run during update_order_test_update_8002().
$this->assertFalse(\Drupal::state()->get('update_order_test_update_8002_user__id__default_langcode__langcode', TRUE));
}
}

View file

@ -0,0 +1,204 @@
<?php
/**
* @file
* Contains \Drupal\system\Tests\Entity\Update\UpdateApiEntityDefinitionUpdateTest.
*/
namespace Drupal\system\Tests\Entity\Update;
use Drupal\Core\Entity\Exception\FieldStorageDefinitionUpdateForbiddenException;
use Drupal\entity_test\Entity\EntityTest;
use Drupal\simpletest\WebTestBase;
use Drupal\system\Tests\Update\DbUpdatesTrait;
/**
* Tests performing entity updates through the Update API.
*
* @group Entity
*/
class UpdateApiEntityDefinitionUpdateTest extends WebTestBase {
use DbUpdatesTrait;
/**
* {@inheritdoc}
*/
protected static $modules = ['entity_test'];
/**
* The entity manager.
*
* @var \Drupal\Core\Entity\EntityManagerInterface
*/
protected $entityManager;
/**
* The entity definition update manager.
*
* @var \Drupal\Core\Entity\EntityDefinitionUpdateManagerInterface
*/
protected $updatesManager;
/**
* {@inheritdoc}
*/
public function setUp() {
parent::setUp();
$this->entityManager = $this->container->get('entity.manager');
$this->updatesManager = $this->container->get('entity.definition_update_manager');
$admin = $this->drupalCreateUser([], FALSE, TRUE);
$this->drupalLogin($admin);
}
/**
* Tests that individual updates applied sequentially work as expected.
*/
public function testSingleUpdates() {
// Create a test entity.
$user_ids = [mt_rand(), mt_rand()];
$entity = EntityTest::create(['name' => $this->randomString(), 'user_id' => $user_ids]);
$entity->save();
// Check that only a single value is stored for 'user_id'.
$entity = $this->reloadEntity($entity);
$this->assertEqual(count($entity->user_id), 1);
$this->assertEqual($entity->user_id->target_id, $user_ids[0]);
// Make 'user_id' multiple by running updates.
$this->enableUpdates('entity_test', 'entity_definition_updates', 8001);
$this->runUpdates();
// Check that data was correctly migrated.
$entity = $this->reloadEntity($entity);
$this->assertEqual(count($entity->user_id), 1);
$this->assertEqual($entity->user_id->target_id, $user_ids[0]);
// Store multiple data and check it is correctly stored.
$entity->user_id = $user_ids;
$entity->save();
$entity = $this->reloadEntity($entity);
$this->assertEqual(count($entity->user_id), 2);
$this->assertEqual($entity->user_id[0]->target_id, $user_ids[0]);
$this->assertEqual($entity->user_id[1]->target_id, $user_ids[1]);
// Make 'user_id' single again by running updates.
$this->enableUpdates('entity_test', 'entity_definition_updates', 8002);
$this->runUpdates();
// Check that data was correctly migrated/dropped.
$entity = $this->reloadEntity($entity);
$this->assertEqual(count($entity->user_id), 1);
$this->assertEqual($entity->user_id->target_id, $user_ids[0]);
}
/**
* Tests that multiple updates applied in bulk work as expected.
*/
public function testMultipleUpdates() {
// Create a test entity.
$user_ids = [mt_rand(), mt_rand()];
$entity = EntityTest::create(['name' => $this->randomString(), 'user_id' => $user_ids]);
$entity->save();
// Check that only a single value is stored for 'user_id'.
$entity = $this->reloadEntity($entity);
$this->assertEqual(count($entity->user_id), 1);
$this->assertEqual($entity->user_id->target_id, $user_ids[0]);
// Make 'user_id' multiple and then single again by running updates.
$this->enableUpdates('entity_test', 'entity_definition_updates', 8002);
$this->runUpdates();
// Check that data was correctly migrated back and forth.
$entity = $this->reloadEntity($entity);
$this->assertEqual(count($entity->user_id), 1);
$this->assertEqual($entity->user_id->target_id, $user_ids[0]);
// Check that only a single value is stored for 'user_id' again.
$entity->user_id = $user_ids;
$entity->save();
$entity = $this->reloadEntity($entity);
$this->assertEqual(count($entity->user_id), 1);
$this->assertEqual($entity->user_id[0]->target_id, $user_ids[0]);
}
/**
* Tests that entity updates are correctly reported in the status report page.
*/
function testStatusReport() {
// Create a test entity.
$entity = EntityTest::create(['name' => $this->randomString(), 'user_id' => mt_rand()]);
$entity->save();
// Check that the status report initially displays no error.
$this->drupalGet('admin/reports/status');
$this->assertNoRaw('Out of date');
$this->assertNoRaw('Mismatch detected');
// Enable an entity update and check that we have a dedicated status report
// item.
$this->container->get('state')->set('entity_test.remove_name_field', TRUE);
$this->drupalGet('admin/reports/status');
$this->assertNoRaw('Out of date');
$this->assertRaw('Mismatch detected');
// Enable a db update and check that now the entity update status report
// item is no longer displayed. We assume an update function will fix the
// mismatch.
$this->enableUpdates('entity_test', 'status_report', 8001);
$this->drupalGet('admin/reports/status');
$this->assertRaw('Out of date');
$this->assertNoRaw('Mismatch detected');
// Run db updates and check that entity updates were not applied.
$this->runUpdates();
$this->drupalGet('admin/reports/status');
$this->assertNoRaw('Out of date');
$this->assertRaw('Mismatch detected');
// Check that en exception would be triggered when trying to apply them with
// existing data.
$message = 'Entity updates cannot run if entity data exists.';
try {
$this->updatesManager->applyUpdates();
$this->fail($message);
}
catch (FieldStorageDefinitionUpdateForbiddenException $e) {
$this->pass($message);
}
// Check the status report is the same after trying to apply updates.
$this->drupalGet('admin/reports/status');
$this->assertNoRaw('Out of date');
$this->assertRaw('Mismatch detected');
// Delete entity data, enable a new update, run updates again and check that
// entity updates were not applied even when no data exists.
$entity->delete();
$this->enableUpdates('entity_test', 'status_report', 8002);
$this->runUpdates();
$this->drupalGet('admin/reports/status');
$this->assertNoRaw('Out of date');
$this->assertRaw('Mismatch detected');
}
/**
* Reloads the specified entity.
*
* @param \Drupal\entity_test\Entity\EntityTest $entity
* An entity object.
*
* @return \Drupal\entity_test\Entity\EntityTest
* The reloaded entity object.
*/
protected function reloadEntity(EntityTest $entity) {
$this->entityManager->useCaches(FALSE);
$this->entityManager->getStorage('entity_test')->resetCache([$entity->id()]);
return EntityTest::load($entity->id());
}
}

View file

@ -0,0 +1,37 @@
<?php
/**
* @file
* Contains \Drupal\system\Tests\Extension\UpdaterTest.
*/
namespace Drupal\system\Tests\Extension;
use Drupal\simpletest\KernelTestBase;
use Drupal\Core\Updater\Updater;
/**
* Tests InfoParser class and exception.
*
* Files for this test are stored in core/modules/system/tests/fixtures and end
* with .info.txt instead of info.yml in order not not be considered as real
* extensions.
*
* @group Extension
*/
class UpdaterTest extends KernelTestBase {
/**
* Tests project and child project showing correct title.
*
* @see https://drupal.org/node/2409515
*/
public function testGetProjectTitleWithChild() {
// Get the project title from it's directory. If it can't find the title
// it will choose the first project title in the directory.
$directory = \Drupal::root() . '/core/modules/system/tests/modules/module_handler_test_multiple';
$title = Updater::getProjectTitle($directory);
$this->assertEqual('module handler test multiple', $title);
}
}

View file

@ -0,0 +1,40 @@
<?php
/**
* @file
* Contains Drupal\system\Tests\Form\ElementsAccessTest.
*/
namespace Drupal\system\Tests\Form;
use Drupal\simpletest\WebTestBase;
/**
* Tests access control for form elements.
*
* @group Form
*/
class ElementsAccessTest extends WebTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('form_test');
/**
* Ensures that child values are still processed when #access = FALSE.
*/
public function testAccessFalse() {
$this->drupalPostForm('form_test/vertical-tabs-access', NULL, t('Submit'));
$this->assertNoText(t('This checkbox inside a vertical tab does not have its default value.'));
$this->assertNoText(t('This textfield inside a vertical tab does not have its default value.'));
$this->assertNoText(t('This checkbox inside a fieldset does not have its default value.'));
$this->assertNoText(t('This checkbox inside a container does not have its default value.'));
$this->assertNoText(t('This checkbox inside a nested container does not have its default value.'));
$this->assertNoText(t('This checkbox inside a vertical tab whose fieldset access is allowed does not have its default value.'));
$this->assertText(t('The form submitted correctly.'));
}
}

View file

@ -53,16 +53,16 @@ class ElementsLabelsTest extends WebTestBase {
// Exercise various defaults for textboxes and modifications to ensure
// appropriate override and correct behavior.
$elements = $this->xpath('//label[@for="edit-form-textfield-test-title-and-required" and @class="form-required"]/following-sibling::input[@id="edit-form-textfield-test-title-and-required"]');
$elements = $this->xpath('//label[@for="edit-form-textfield-test-title-and-required" and @class="js-form-required form-required"]/following-sibling::input[@id="edit-form-textfield-test-title-and-required"]');
$this->assertTrue(isset($elements[0]), 'Label precedes textfield, with required marker inside label.');
$elements = $this->xpath('//input[@id="edit-form-textfield-test-no-title-required"]/preceding-sibling::label[@for="edit-form-textfield-test-no-title-required" and @class="form-required"]');
$elements = $this->xpath('//input[@id="edit-form-textfield-test-no-title-required"]/preceding-sibling::label[@for="edit-form-textfield-test-no-title-required" and @class="js-form-required form-required"]');
$this->assertTrue(isset($elements[0]), 'Label tag with required marker precedes required textfield with no title.');
$elements = $this->xpath('//input[@id="edit-form-textfield-test-title-invisible"]/preceding-sibling::label[@for="edit-form-textfield-test-title-invisible" and @class="visually-hidden"]');
$this->assertTrue(isset($elements[0]), 'Label preceding field and label class is visually-hidden.');
$elements = $this->xpath('//input[@id="edit-form-textfield-test-title"]/preceding-sibling::span[@class="form-required"]');
$elements = $this->xpath('//input[@id="edit-form-textfield-test-title"]/preceding-sibling::span[@class="js-form-required form-required"]');
$this->assertFalse(isset($elements[0]), 'No required marker on non-required field.');
$elements = $this->xpath('//input[@id="edit-form-textfield-test-title-after"]/following-sibling::label[@for="edit-form-textfield-test-title-after" and @class="option"]');

View file

@ -8,9 +8,11 @@
namespace Drupal\system\Tests\Form;
use Drupal\Component\Serialization\Json;
use Drupal\Component\Utility\Html;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Core\Form\FormState;
use Drupal\Core\Render\Element;
use Drupal\Core\Url;
use Drupal\form_test\Form\FormTestDisabledElementsForm;
use Drupal\simpletest\WebTestBase;
use Drupal\user\RoleInterface;
@ -97,7 +99,7 @@ class FormTest extends WebTestBase {
$elements['file']['empty_values'] = $empty_strings;
// Regular expression to find the expected marker on required elements.
$required_marker_preg = '@<.*?class=".*?form-required.*?">@';
$required_marker_preg = '@<.*?class=".*?js-form-required.*form-required.*?">@';
// Go through all the elements and all the empty values for them.
foreach ($elements as $type => $data) {
foreach ($data['empty_values'] as $key => $empty) {
@ -231,6 +233,67 @@ class FormTest extends WebTestBase {
$this->assertRaw("The form_test_validate_required_form form was submitted successfully.", 'Validation form submitted successfully.');
}
/**
* Tests that input is retained for safe elements even with an invalid token.
*
* Submits a test form containing several types of form elements.
*/
public function testInputWithInvalidToken() {
// We need to be logged in to have CSRF tokens.
$account = $this->createUser();
$this->drupalLogin($account);
// Submit again with required fields set but an invalid form token and
// verify that all the values are retained.
$edit = array(
'textfield' => $this->randomString(),
'checkboxes[bar]' => TRUE,
'select' => 'bar',
'radios' => 'foo',
'form_token' => 'invalid token',
);
$this->drupalPostForm(Url::fromRoute('form_test.validate_required'), $edit, 'Submit');
$this->assertFieldByXpath('//div[contains(@class, "error")]', NULL, 'Error message is displayed with invalid token even when required fields are filled.');
$this->assertText('The form has become outdated. Copy any unsaved work in the form below');
// Verify that input elements retained the posted values.
$this->assertFieldByName('textfield', $edit['textfield']);
$this->assertNoFieldChecked('edit-checkboxes-foo');
$this->assertFieldChecked('edit-checkboxes-bar');
$this->assertOptionSelected('edit-select', 'bar');
$this->assertFieldChecked('edit-radios-foo');
// Check another form that has a textarea input.
$edit = array(
'textfield' => $this->randomString(),
'textarea' => $this->randomString() . "\n",
'form_token' => 'invalid token',
);
$this->drupalPostForm(Url::fromRoute('form_test.required'), $edit, 'Submit');
$this->assertFieldByXpath('//div[contains(@class, "error")]', NULL, 'Error message is displayed with invalid token even when required fields are filled.');
$this->assertText('The form has become outdated. Copy any unsaved work in the form below');
$this->assertFieldByName('textfield', $edit['textfield']);
$this->assertFieldByName('textarea', $edit['textarea']);
// Check another form that has a number input.
$edit = array(
'integer_step' => mt_rand(1, 100),
'form_token' => 'invalid token',
);
$this->drupalPostForm(Url::fromRoute('form_test.number'), $edit, 'Submit');
$this->assertFieldByXpath('//div[contains(@class, "error")]', NULL, 'Error message is displayed with invalid token even when required fields are filled.');
$this->assertText('The form has become outdated. Copy any unsaved work in the form below');
$this->assertFieldByName('integer_step', $edit['integer_step']);
// Check a form with a Url field
$edit = array(
'url' => $this->randomString(),
'form_token' => 'invalid token',
);
$this->drupalPostForm(Url::fromRoute('form_test.url'), $edit, 'Submit');
$this->assertFieldByXpath('//div[contains(@class, "error")]', NULL, 'Error message is displayed with invalid token even when required fields are filled.');
$this->assertText('The form has become outdated. Copy any unsaved work in the form below');
$this->assertFieldByName('url', $edit['url']);
}
/**
* Tests validation for required textfield element without title.
*
@ -533,7 +596,7 @@ class FormTest extends WebTestBase {
// All the elements should be marked as disabled, including the ones below
// the disabled container.
$actual_count = count($disabled_elements);
$expected_count = 41;
$expected_count = 42;
$this->assertEqual($actual_count, $expected_count, SafeMarkup::format('Found @actual elements with disabled property (expected @expected).', array(
'@actual' => count($disabled_elements),
'@expected' => $expected_count,
@ -616,7 +679,7 @@ class FormTest extends WebTestBase {
$path = strtr($path, array('!type' => $type));
// Verify that the element exists.
$element = $this->xpath($path, array(
':name' => SafeMarkup::checkPlain($name),
':name' => Html::escape($name),
':div-class' => $class,
':value' => isset($item['#value']) ? $item['#value'] : '',
));

View file

@ -97,7 +97,7 @@ class RebuildTest extends WebTestBase {
// field items in the field for which we just added an item.
$this->drupalGet('node/add/page');
$this->drupalPostAjaxForm(NULL, array(), array('field_ajax_test_add_more' => t('Add another item')), NULL, array(), array(), 'node-page-form');
$this->assert(count($this->xpath('//div[contains(@class, "field-name-field-ajax-test")]//input[@type="text"]')) == 2, 'AJAX submission succeeded.');
$this->assert(count($this->xpath('//div[contains(@class, "field--name-field-ajax-test")]//input[@type="text"]')) == 2, 'AJAX submission succeeded.');
// Submit the form with the non-Ajax "Save" button, leaving the title field
// blank to trigger a validation error, and ensure that a validation error

View file

@ -7,7 +7,6 @@
namespace Drupal\system\Tests\Form;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Core\Render\Element;
use Drupal\Core\Url;
use Drupal\simpletest\WebTestBase;
@ -292,11 +291,12 @@ class ValidationTest extends WebTestBase {
// Gather the element for checking the jump link section.
$error_links[] = \Drupal::l($message['title'], Url::fromRoute('<none>', [], ['fragment' => 'edit-' . str_replace('_', '-', $message['key']), 'external' => TRUE]));
}
$top_message = \Drupal::translation()->formatPlural(count($error_links), '1 error has been found: !errors', '@count errors have been found: !errors', [
'!errors' => SafeMarkup::set(implode(', ', $error_links))
]);
$top_message = \Drupal::translation()->formatPlural(count($error_links), '1 error has been found:', '@count errors have been found:');
$this->assertRaw($top_message);
$this->assertNoText(t('An illegal choice has been detected. Please contact the site administrator.'));
foreach ($error_links as $error_link) {
$this->assertRaw($error_link);
}
$this->assertNoText('An illegal choice has been detected. Please contact the site administrator.');
}
}

View file

@ -0,0 +1,37 @@
<?php
/**
* @file
* Contains \Drupal\system\Tests\HttpKernel\HeadersResponseCodeRenderTest.
*/
namespace Drupal\system\Tests\HttpKernel;
use Drupal\simpletest\WebTestBase;
/**
* Tests rendering headers and response codes.
*
* @group Routing
*/
class HeadersResponseCodeRenderTest extends WebTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('httpkernel_test');
/**
* Tests the rendering of an array-based header and response code.
*/
public function testHeaderResponseCode() {
$this->drupalGet('/httpkernel-test/teapot');
$this->assertResponse(418);
$this->assertHeader('X-Test-Teapot', 'Teapot Mode Active');
$this->assertHeader('X-Test-Teapot-Replace', 'Teapot replaced');
$this->assertHeader('X-Test-Teapot-No-Replace', 'This value is not replaced,This one is added');
}
}

View file

@ -0,0 +1,46 @@
<?php
/**
* @file
* Contains \Drupal\system\Tests\Installer\InstallerDatabaseErrorMessagesTest.
*/
namespace Drupal\system\Tests\Installer;
use Drupal\Core\Database\Database;
use Drupal\simpletest\InstallerTestBase;
/**
* Tests the installer with database errors.
*
* @group Installer
*/
class InstallerDatabaseErrorMessagesTest extends InstallerTestBase {
/**
* @{inheritdoc}
*/
protected function setUpSettings() {
// We are creating a table here to force an error in the installer because
// it will try and create the drupal_install_test table as this is part of
// the standard database tests performed by the installer in
// Drupal\Core\Database\Install\Tasks.
Database::getConnection('default')->query('CREATE TABLE {drupal_install_test} (id int NULL)');
parent::setUpSettings();
}
/**
* @{inheritdoc}
*/
protected function setUpSite() {
// This step should not appear as we had a failure on the settings screen.
}
/**
* Verifies that the error message in the settings step is correct.
*/
public function testSetUpSettingsErrorMessage() {
$this->assertRaw('<ul><li>Failed to <strong>CREATE</strong> a test table');
}
}

View file

@ -7,6 +7,7 @@
namespace Drupal\system\Tests\Installer;
use Drupal\Core\Database\Database;
use Drupal\simpletest\InstallerTestBase;
use Drupal\user\Entity\User;
@ -45,6 +46,26 @@ class InstallerTranslationTest extends InstallerTestBase {
$this->assertEqual($direction, 'ltr');
}
/**
* @{inheritdoc}
*/
protected function setUpSettings() {
// We are creating a table here to force an error in the installer because
// it will try and create the drupal_install_test table as this is part of
// the standard database tests performed by the installer in
// Drupal\Core\Database\Install\Tasks.
Database::getConnection('default')->query('CREATE TABLE {drupal_install_test} (id int NULL)');
parent::setUpSettings();
// Ensure that the error message translation is working.
$this->assertRaw('Beheben Sie alle Probleme unten, um die Installation fortzusetzen. Informationen zur Konfiguration der Datenbankserver finden Sie in der <a href="https://www.drupal.org/getting-started/install">Installationshandbuch</a>, oder kontaktieren Sie Ihren Hosting-Anbieter.');
$this->assertRaw('<strong>CREATE</strong> ein Test-Tabelle auf Ihrem Datenbankserver mit dem Befehl <em class="placeholder">CREATE TABLE {drupal_install_test} (id int NULL)</em> fehlgeschlagen.');
// Now do it successfully.
Database::getConnection('default')->query('DROP TABLE {drupal_install_test}');
parent::setUpSettings();
}
/**
* Verifies the expected behaviors of the installation result.
*/
@ -127,6 +148,12 @@ msgstr "Save and continue $langcode"
msgid "Anonymous"
msgstr "Anonymous $langcode"
msgid "Resolve all issues below to continue the installation. For help configuring your database server, see the <a href="https://www.drupal.org/getting-started/install">installation handbook</a>, or contact your hosting provider."
msgstr "Beheben Sie alle Probleme unten, um die Installation fortzusetzen. Informationen zur Konfiguration der Datenbankserver finden Sie in der <a href="https://www.drupal.org/getting-started/install">Installationshandbuch</a>, oder kontaktieren Sie Ihren Hosting-Anbieter."
msgid "Failed to <strong>CREATE</strong> a test table on your database server with the command %query. The server reports the following message: %error.<p>Are you sure the configured username has the necessary permissions to create tables in the database?</p>"
msgstr "<strong>CREATE</strong> ein Test-Tabelle auf Ihrem Datenbankserver mit dem Befehl %query fehlgeschlagen."
ENDPO;
}

View file

@ -7,7 +7,7 @@
namespace Drupal\system\Tests\Mail;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Component\Utility\Html;
use Drupal\Component\Utility\Unicode;
use Drupal\Core\Mail\MailFormatHelper;
use Drupal\Core\Site\Settings;
@ -34,7 +34,7 @@ class HtmlToTextTest extends WebTestBase {
str_replace(
array("\n", ' '),
array('\n', '&nbsp;'),
SafeMarkup::checkPlain($text)
Html::escape($text)
) . '"';
}
@ -57,7 +57,7 @@ class HtmlToTextTest extends WebTestBase {
$tested_tags = implode(', ', array_unique($matches[1]));
$message .= ' (' . $tested_tags . ')';
$result = MailFormatHelper::htmlToText($html, $allowed_tags);
$pass = $this->assertEqual($result, $text, SafeMarkup::checkPlain($message));
$pass = $this->assertEqual($result, $text, Html::escape($message));
$verbose = 'html = <pre>' . $this->stringToHtml($html)
. '</pre><br />' . 'result = <pre>' . $this->stringToHtml($result)
. '</pre><br />' . 'expected = <pre>' . $this->stringToHtml($text)

View file

@ -7,7 +7,7 @@
namespace Drupal\system\Tests\Menu;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Component\Utility\Html;
use Drupal\Core\Url;
/**
@ -83,7 +83,7 @@ trait AssertBreadcrumbTrait {
$url = $path;
}
$part = array_shift($parts);
$pass = ($pass && $part['href'] === $url && $part['text'] === SafeMarkup::checkPlain($title));
$pass = ($pass && $part['href'] === $url && $part['text'] === Html::escape($title));
}
}
// No parts must be left, or an expected "Home" will always pass.

View file

@ -7,6 +7,7 @@
namespace Drupal\system\Tests\Menu;
use Drupal\Component\Utility\Html;
use Drupal\Core\Url;
use Drupal\simpletest\WebTestBase;
@ -17,10 +18,21 @@ use Drupal\simpletest\WebTestBase;
*/
class LocalActionTest extends WebTestBase {
/**
* Modules to enable.
*
* @var string[]
*/
public static $modules = ['block', 'menu_test'];
/**
* {@inheritdoc}
*/
public static $modules = array('menu_test');
protected function setUp() {
parent::setUp();
$this->drupalPlaceBlock('local_actions_block');
}
/**
* Tests appearance of local actions.
@ -30,8 +42,8 @@ class LocalActionTest extends WebTestBase {
// Ensure that both menu and route based actions are shown.
$this->assertLocalAction([
[Url::fromRoute('menu_test.local_action4'), 'My dynamic-title action'],
[Url::fromRoute('menu_test.local_action4'), htmlspecialchars("<script>alert('Welcome to the jungle!')</script>", ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8')],
[Url::fromRoute('menu_test.local_action4'), htmlspecialchars("<script>alert('Welcome to the derived jungle!')</script>", ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8')],
[Url::fromRoute('menu_test.local_action4'), Html::escape("<script>alert('Welcome to the jungle!')</script>")],
[Url::fromRoute('menu_test.local_action4'), Html::escape("<script>alert('Welcome to the derived jungle!')</script>")],
[Url::fromRoute('menu_test.local_action2'), 'My hook_menu action'],
[Url::fromRoute('menu_test.local_action3'), 'My YAML discovery action'],
[Url::fromRoute('menu_test.local_action5'), 'Title override'],

View file

@ -7,6 +7,7 @@
namespace Drupal\system\Tests\Menu;
use Drupal\Component\Utility\Html;
use Drupal\Core\Url;
use Drupal\simpletest\WebTestBase;
@ -17,7 +18,28 @@ use Drupal\simpletest\WebTestBase;
*/
class LocalTasksTest extends WebTestBase {
public static $modules = array('menu_test', 'entity_test');
/**
* Modules to enable.
*
* @var string[]
*/
public static $modules = ['block', 'menu_test', 'entity_test'];
/**
* The local tasks block under testing.
*
* @var \Drupal\block\Entity\Block
*/
protected $sut;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->sut = $this->drupalPlaceBlock('local_tasks_block', ['id' => 'tabs_block']);
}
/**
* Asserts local tasks in the page output.
@ -64,6 +86,20 @@ class LocalTasksTest extends WebTestBase {
return $this->assertPattern('@<a [^>]*>' . preg_quote($title, '@') . '</a>@');
}
/**
* Asserts that the local tasks on the specified level are not being printed.
*
* @param int $level
* (optional) The local tasks level to assert; 0 for primary, 1 for
* secondary. Defaults to 0.
*/
protected function assertNoLocalTasks($level = 0) {
$elements = $this->xpath('//*[contains(@class, :class)]//a', array(
':class' => $level == 0 ? 'tabs primary' : 'tabs secondary',
));
$this->assertFalse(count($elements), 'Local tasks not found.');
}
/**
* Tests the plugin based local tasks.
*/
@ -78,9 +114,9 @@ class LocalTasksTest extends WebTestBase {
]);
// Verify that script tags are escaped on output.
$title = htmlspecialchars("Task 1 <script>alert('Welcome to the jungle!')</script>", ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
$title = Html::escape("Task 1 <script>alert('Welcome to the jungle!')</script>");
$this->assertLocalTaskAppers($title);
$title = htmlspecialchars("<script>alert('Welcome to the derived jungle!')</script>", ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
$title = Html::escape("<script>alert('Welcome to the derived jungle!')</script>");
$this->assertLocalTaskAppers($title);
// Verify that local tasks appear as defined in the router.
@ -92,7 +128,7 @@ class LocalTasksTest extends WebTestBase {
['menu_test.local_task_test_tasks_settings_dynamic', []],
]);
$title = htmlspecialchars("<script>alert('Welcome to the jungle!')</script>", ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
$title = Html::escape("<script>alert('Welcome to the jungle!')</script>");
$this->assertLocalTaskAppers($title);
// Ensure the view tab is active.
@ -171,4 +207,52 @@ class LocalTasksTest extends WebTestBase {
$this->assertEqual('upcasting sub2', (string) $result[0]->a, 'The "upcasting sub2" tab is active.');
}
/**
* Tests that local task blocks are configurable to show a specific level.
*/
public function testLocalTaskBlock() {
// Remove the default block and create a new one.
$this->sut->delete();
$this->sut = $this->drupalPlaceBlock('local_tasks_block', [
'id' => 'tabs_block',
'primary' => TRUE,
'secondary' => FALSE,
]);
$this->drupalGet(Url::fromRoute('menu_test.local_task_test_tasks_settings'));
// Verify that local tasks in the first level appear.
$this->assertLocalTasks([
['menu_test.local_task_test_tasks_view', []],
['menu_test.local_task_test_tasks_edit', []],
['menu_test.local_task_test_tasks_settings', []],
]);
// Verify that local tasks in the second level doesn't appear.
$this->assertNoLocalTasks(1);
$this->sut->delete();
$this->sut = $this->drupalPlaceBlock('local_tasks_block', [
'id' => 'tabs_block',
'primary' => FALSE,
'secondary' => TRUE,
]);
$this->drupalGet(Url::fromRoute('menu_test.local_task_test_tasks_settings'));
// Verify that local tasks in the first level doesn't appear.
$this->assertNoLocalTasks(0);
// Verify that local tasks in the second level appear.
$sub_tasks = [
['menu_test.local_task_test_tasks_settings_sub1', []],
['menu_test.local_task_test_tasks_settings_sub2', []],
['menu_test.local_task_test_tasks_settings_sub3', []],
['menu_test.local_task_test_tasks_settings_derived', ['placeholder' => 'derive1']],
['menu_test.local_task_test_tasks_settings_derived', ['placeholder' => 'derive2']],
];
$this->assertLocalTasks($sub_tasks, 1);
}
}

View file

@ -43,6 +43,7 @@ class MenuRouterTest extends WebTestBase {
parent::setUp();
$this->drupalPlaceBlock('system_menu_block:tools');
$this->drupalPlaceBlock('local_tasks_block');
}
/**

View file

@ -23,7 +23,16 @@ class MenuTranslateTest extends WebTestBase {
*
* @var array
*/
public static $modules = array('menu_test');
public static $modules = ['block', 'menu_test'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->drupalPlaceBlock('local_tasks_block');
}
/**
* Tests _menu_translate().

View file

@ -0,0 +1,57 @@
<?php
/**
* @file
* Contains \Drupal\system\Tests\Migrate\MigrateMenuTest.
*/
namespace Drupal\system\Tests\Migrate;
use Drupal\migrate\Entity\Migration;
use Drupal\Core\Database\Database;
use Drupal\migrate_drupal\Tests\d6\MigrateDrupal6TestBase;
use Drupal\system\Entity\Menu;
/**
* Upgrade menus to system.menu.*.yml.
*
* @group migrate_drupal_6
*/
class MigrateMenuTest extends MigrateDrupal6TestBase {
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->executeMigration('menu');
}
/**
* Tests the Drupal 6 menu to Drupal 8 migration.
*/
public function testMenu() {
$navigation_menu = Menu::load('navigation');
$this->assertIdentical('navigation', $navigation_menu->id());
$this->assertIdentical('Navigation', $navigation_menu->label());
$expected = <<<EOT
The navigation menu is provided by Drupal and is the main interactive menu for any site. It is usually the only menu that contains personalized links for authenticated users, and is often not even visible to anonymous users.
EOT;
$this->assertIdentical($expected, $navigation_menu->getDescription());
// Test that we can re-import using the ConfigEntityBase destination.
Database::getConnection('default', 'migrate')
->update('menu_custom')
->fields(array('title' => 'Home Navigation'))
->condition('menu_name', 'navigation')
->execute();
$migration = Migration::load('menu');
db_truncate($migration->getIdMap()->mapTableName())->execute();
$this->executeMigration($migration);
$navigation_menu = Menu::load('navigation');
$this->assertIdentical('Home Navigation', $navigation_menu->label());
}
}

View file

@ -14,7 +14,7 @@ use Drupal\migrate_drupal\Tests\d6\MigrateDrupal6TestBase;
/**
* Upgrade date formats to core.date_format.*.yml.
*
* @group system
* @group migrate_drupal_6
*/
class MigrateDateFormatTest extends MigrateDrupal6TestBase {
@ -23,7 +23,6 @@ class MigrateDateFormatTest extends MigrateDrupal6TestBase {
*/
protected function setUp() {
parent::setUp();
$this->loadDumps(['Variable.php']);
$this->executeMigration('d6_date_formats');
}

View file

@ -12,7 +12,7 @@ use Drupal\migrate_drupal\Tests\d6\MigrateDrupal6TestBase;
/**
* Upgrade cron variable to system.*.yml.
*
* @group system
* @group migrate_drupal_6
*/
class MigrateSystemCronTest extends MigrateDrupal6TestBase {
@ -21,7 +21,6 @@ class MigrateSystemCronTest extends MigrateDrupal6TestBase {
*/
protected function setUp() {
parent::setUp();
$this->loadDumps(['Variable.php']);
$this->executeMigration('d6_system_cron');
}

View file

@ -0,0 +1,37 @@
<?php
/**
* @file
* Contains \Drupal\system\Tests\Migrate\d6\MigrateSystemDateTest.
*/
namespace Drupal\system\Tests\Migrate\d6;
use Drupal\migrate_drupal\Tests\d6\MigrateDrupal6TestBase;
/**
* Upgrade date time variables to system.date config
*
* @group migrate_drupal_6
*/
class MigrateSystemDateTest extends MigrateDrupal6TestBase {
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->executeMigration('d6_system_date');
}
/**
* Tests migration of user variables to system_date.yml.
*/
public function testSystemDate() {
$config = $this->config('system.date');
$this->assertIdentical(4, $config->get('first_day'));
$this->assertIdentical(FALSE, $config->get('timezone.user.configurable'));
$this->assertIdentical("Europe/Paris", $config->get('timezone.default'));
}
}

View file

@ -12,7 +12,7 @@ use Drupal\migrate_drupal\Tests\d6\MigrateDrupal6TestBase;
/**
* Upgrade variables to system.*.yml.
*
* @group system
* @group migrate_drupal_6
*/
class MigrateSystemFileTest extends MigrateDrupal6TestBase {
@ -21,7 +21,6 @@ class MigrateSystemFileTest extends MigrateDrupal6TestBase {
*/
protected function setUp() {
parent::setUp();
$this->loadDumps(['Variable.php']);
$this->executeMigration('d6_system_file');
}

View file

@ -12,7 +12,7 @@ use Drupal\migrate_drupal\Tests\d6\MigrateDrupal6TestBase;
/**
* Upgrade image gd variables to system.*.yml.
*
* @group system
* @group migrate_drupal_6
*/
class MigrateSystemImageGdTest extends MigrateDrupal6TestBase {
@ -21,7 +21,6 @@ class MigrateSystemImageGdTest extends MigrateDrupal6TestBase {
*/
protected function setUp() {
parent::setUp();
$this->loadDumps(['Variable.php']);
$this->executeMigration('d6_system_image_gd');
}

View file

@ -12,7 +12,7 @@ use Drupal\migrate_drupal\Tests\d6\MigrateDrupal6TestBase;
/**
* Upgrade image variables to system.*.yml.
*
* @group system
* @group migrate_drupal_6
*/
class MigrateSystemImageTest extends MigrateDrupal6TestBase {
@ -21,7 +21,6 @@ class MigrateSystemImageTest extends MigrateDrupal6TestBase {
*/
protected function setUp() {
parent::setUp();
$this->loadDumps(['Variable.php']);
$this->executeMigration('d6_system_image');
}

View file

@ -13,7 +13,7 @@ use Drupal\migrate_drupal\Tests\d6\MigrateDrupal6TestBase;
/**
* Upgrade error_level variable to system.logging.yml.
*
* @group system
* @group migrate_drupal_6
*/
class MigrateSystemLoggingTest extends MigrateDrupal6TestBase {
@ -24,7 +24,6 @@ class MigrateSystemLoggingTest extends MigrateDrupal6TestBase {
*/
protected function setUp() {
parent::setUp();
$this->loadDumps(['Variable.php']);
$this->executeMigration('d6_system_logging');
}

View file

@ -12,7 +12,7 @@ use Drupal\migrate_drupal\Tests\d6\MigrateDrupal6TestBase;
/**
* Upgrade maintenance variables to system.*.yml.
*
* @group system
* @group migrate_drupal_6
*/
class MigrateSystemMaintenanceTest extends MigrateDrupal6TestBase {
@ -21,7 +21,6 @@ class MigrateSystemMaintenanceTest extends MigrateDrupal6TestBase {
*/
protected function setUp() {
parent::setUp();
$this->loadDumps(['Variable.php']);
$this->executeMigration('d6_system_maintenance');
}

View file

@ -12,7 +12,7 @@ use Drupal\migrate_drupal\Tests\d6\MigrateDrupal6TestBase;
/**
* Upgrade performance variables to system.*.yml.
*
* @group system
* @group migrate_drupal_6
*/
class MigrateSystemPerformanceTest extends MigrateDrupal6TestBase {
@ -21,7 +21,6 @@ class MigrateSystemPerformanceTest extends MigrateDrupal6TestBase {
*/
protected function setUp() {
parent::setUp();
$this->loadDumps(['Variable.php']);
$this->executeMigration('d6_system_performance');
}

View file

@ -12,7 +12,7 @@ use Drupal\migrate_drupal\Tests\d6\MigrateDrupal6TestBase;
/**
* Upgrade rss variable to system.*.yml.
*
* @group system
* @group migrate_drupal_6
*/
class MigrateSystemRssTest extends MigrateDrupal6TestBase {
@ -21,7 +21,6 @@ class MigrateSystemRssTest extends MigrateDrupal6TestBase {
*/
protected function setUp() {
parent::setUp();
$this->loadDumps(['Variable.php']);
$this->executeMigration('d6_system_rss');
}

View file

@ -12,7 +12,7 @@ use Drupal\migrate_drupal\Tests\d6\MigrateDrupal6TestBase;
/**
* Upgrade site variables to system.*.yml.
*
* @group system
* @group migrate_drupal_6
*/
class MigrateSystemSiteTest extends MigrateDrupal6TestBase {
@ -21,7 +21,6 @@ class MigrateSystemSiteTest extends MigrateDrupal6TestBase {
*/
protected function setUp() {
parent::setUp();
$this->loadDumps(['Variable.php']);
$this->executeMigration('d6_system_site');
}

View file

@ -51,7 +51,7 @@ class UninstallTest extends WebTestBase {
$node_type->setThirdPartySetting('module_test', 'key', 'value');
$node_type->save();
// Add a node to prevent node from being uninstalled.
$node = entity_create('node', array('type' => 'uninstall_blocker'));
$node = entity_create('node', array('type' => 'uninstall_blocker', 'title' => $this->randomString()));
$node->save();
$this->drupalGet('admin/modules/uninstall');

View file

@ -95,6 +95,29 @@ class PagerTest extends WebTestBase {
$this->assertCacheContext('url.query_args');
}
/**
* Test proper functioning of the ellipsis.
*/
public function testPagerEllipsis() {
// Insert 100 extra log messages to get 9 pages.
$logger = $this->container->get('logger.factory')->get('pager_test');
for ($i = 0; $i < 100; $i++) {
$logger->debug($this->randomString());
}
$this->drupalGet('admin/reports/dblog');
$elements = $this->cssSelect(".pager__item--ellipsis:contains('…')");
$this->assertEqual(count($elements), 0, 'No ellipsis has been set.');
// Insert an extra 50 log messages to get 10 pages.
$logger = $this->container->get('logger.factory')->get('pager_test');
for ($i = 0; $i < 50; $i++) {
$logger->debug($this->randomString());
}
$this->drupalGet('admin/reports/dblog');
$elements = $this->cssSelect(".pager__item--ellipsis:contains('…')");
$this->assertEqual(count($elements), 1, 'Found the ellipsis.');
}
/**
* Asserts pager items and links.
*

View file

@ -67,7 +67,9 @@ class UpcastingTest extends WebTestBase {
public function testEntityLanguage() {
$language = ConfigurableLanguage::createFromLangcode('de');
$language->save();
language_negotiation_url_prefixes_save(array('de' => 'de'));
\Drupal::configFactory()->getEditable('language.negotiation')
->set('url.prefixes', array('de' => 'de'))
->save();
// The container must be recreated after adding a new language.
$this->rebuildContainer();

View file

@ -52,7 +52,7 @@ class AliasTest extends PathUnitTestBase {
// Load alias by source path.
$loadedAlias = $aliasStorage->load(array('source' => '/node/1'));
$this->assertEqual($loadedAlias['alias'], '/alias_for_node_1_und', format_string('The last created alias loaded by default.'));
$this->assertEqual($loadedAlias['alias'], '/alias_for_node_1_und', 'The last created alias loaded by default.');
//Update a few aliases
foreach ($aliases as $alias) {

View file

@ -7,7 +7,7 @@
namespace Drupal\system\Tests\Routing;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Component\Utility\Html;
use Drupal\simpletest\KernelTestBase;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
@ -116,7 +116,7 @@ class ExceptionHandlingTest extends KernelTestBase {
// Test both that the backtrace is properly escaped, and that the unescaped
// string is not output at all.
$this->assertTrue(strpos($response->getContent(), SafeMarkup::checkPlain('<script>alert(\'xss\')</script>')) !== FALSE);
$this->assertTrue(strpos($response->getContent(), Html::escape('<script>alert(\'xss\')</script>')) !== FALSE);
$this->assertTrue(strpos($response->getContent(), '<script>alert(\'xss\')</script>') === FALSE);
}
@ -140,7 +140,7 @@ class ExceptionHandlingTest extends KernelTestBase {
// Test message is properly escaped, and that the unescaped string is not
// output at all.
$this->setRawContent($response->getContent());
$this->assertRaw(SafeMarkup::checkPlain('Escaped content: <p> <br> <h3>'));
$this->assertRaw(Html::escape('Escaped content: <p> <br> <h3>'));
$this->assertNoRaw('<p> <br> <h3>');
}

View file

@ -71,6 +71,13 @@ class RouteProviderTest extends KernelTestBase {
*/
protected $pathProcessor;
/**
* The cache tags invalidator.
*
* @var \Drupal\Core\Cache\CacheTagsInvalidatorInterface
*/
protected $cacheTagsInvalidator;
protected function setUp() {
parent::setUp();
$this->fixtures = new RoutingFixtures();
@ -78,6 +85,7 @@ class RouteProviderTest extends KernelTestBase {
$this->currentPath = new CurrentPathStack(new RequestStack());
$this->cache = new MemoryBackend('data');
$this->pathProcessor = \Drupal::service('path_processor_manager');
$this->cacheTagsInvalidator = \Drupal::service('cache_tags.invalidator');
$this->installSchema('system', 'url_alias');
}
@ -107,7 +115,7 @@ class RouteProviderTest extends KernelTestBase {
public function testCandidateOutlines() {
$connection = Database::getConnection();
$provider = new RouteProvider($connection, $this->state, $this->currentPath, $this->cache, $this->pathProcessor, 'test_routes');
$provider = new RouteProvider($connection, $this->state, $this->currentPath, $this->cache, $this->pathProcessor, $this->cacheTagsInvalidator, 'test_routes');
$parts = array('node', '5', 'edit');
@ -130,7 +138,7 @@ class RouteProviderTest extends KernelTestBase {
*/
function testExactPathMatch() {
$connection = Database::getConnection();
$provider = new RouteProvider($connection, $this->state, $this->currentPath, $this->cache, $this->pathProcessor, 'test_routes');
$provider = new RouteProvider($connection, $this->state, $this->currentPath, $this->cache, $this->pathProcessor, $this->cacheTagsInvalidator, 'test_routes');
$this->fixtures->createTables($connection);
@ -154,7 +162,7 @@ class RouteProviderTest extends KernelTestBase {
*/
function testOutlinePathMatch() {
$connection = Database::getConnection();
$provider = new RouteProvider($connection, $this->state, $this->currentPath, $this->cache, $this->pathProcessor, 'test_routes');
$provider = new RouteProvider($connection, $this->state, $this->currentPath, $this->cache, $this->pathProcessor, $this->cacheTagsInvalidator, 'test_routes');
$this->fixtures->createTables($connection);
@ -183,7 +191,7 @@ class RouteProviderTest extends KernelTestBase {
*/
function testOutlinePathMatchTrailingSlash() {
$connection = Database::getConnection();
$provider = new RouteProvider($connection, $this->state, $this->currentPath, $this->cache, $this->pathProcessor, 'test_routes');
$provider = new RouteProvider($connection, $this->state, $this->currentPath, $this->cache, $this->pathProcessor, $this->cacheTagsInvalidator, 'test_routes');
$this->fixtures->createTables($connection);
@ -212,7 +220,7 @@ class RouteProviderTest extends KernelTestBase {
*/
function testOutlinePathMatchDefaults() {
$connection = Database::getConnection();
$provider = new RouteProvider($connection, $this->state, $this->currentPath, $this->cache, $this->pathProcessor, 'test_routes');
$provider = new RouteProvider($connection, $this->state, $this->currentPath, $this->cache, $this->pathProcessor, $this->cacheTagsInvalidator, 'test_routes');
$this->fixtures->createTables($connection);
@ -250,7 +258,7 @@ class RouteProviderTest extends KernelTestBase {
*/
function testOutlinePathMatchDefaultsCollision() {
$connection = Database::getConnection();
$provider = new RouteProvider($connection, $this->state, $this->currentPath, $this->cache, $this->pathProcessor, 'test_routes');
$provider = new RouteProvider($connection, $this->state, $this->currentPath, $this->cache, $this->pathProcessor, $this->cacheTagsInvalidator, 'test_routes');
$this->fixtures->createTables($connection);
@ -289,7 +297,7 @@ class RouteProviderTest extends KernelTestBase {
*/
function testOutlinePathMatchDefaultsCollision2() {
$connection = Database::getConnection();
$provider = new RouteProvider($connection, $this->state, $this->currentPath, $this->cache, $this->pathProcessor, 'test_routes');
$provider = new RouteProvider($connection, $this->state, $this->currentPath, $this->cache, $this->pathProcessor, $this->cacheTagsInvalidator, 'test_routes');
$this->fixtures->createTables($connection);
@ -316,7 +324,46 @@ class RouteProviderTest extends KernelTestBase {
$this->assertEqual(array('narf', 'poink'), array_keys($routes_array), 'Ensure the fitness was taken into account.');
$this->assertNotNull($routes->get('narf'), 'The first matching route was found.');
$this->assertNotNull($routes->get('poink'), 'The second matching route was found.');
$this->assertNull($routes->get('eep'), 'Noin-matching route was not found.');
$this->assertNull($routes->get('eep'), 'Non-matching route was not found.');
}
catch (ResourceNotFoundException $e) {
$this->fail('No matching route found with default argument value.');
}
}
/**
* Confirms that we can find multiple routes that match the request equally.
*/
function testOutlinePathMatchDefaultsCollision3() {
$connection = Database::getConnection();
$provider = new RouteProvider($connection, $this->state, $this->currentPath, $this->cache, $this->pathProcessor, $this->cacheTagsInvalidator, 'test_routes');
$this->fixtures->createTables($connection);
$collection = new RouteCollection();
$collection->add('poink', new Route('/some/{value}/path'));
// Add a second route matching the same path pattern.
$collection->add('poink2', new Route('/some/{object}/path'));
$collection->add('narf', new Route('/some/here/path'));
$collection->add('eep', new Route('/something/completely/different'));
$dumper = new MatcherDumper($connection, $this->state, 'test_routes');
$dumper->addRoutes($collection);
$dumper->dump();
$path = '/some/over-there/path';
$request = Request::create($path, 'GET');
try {
$routes = $provider->getRouteCollectionForRequest($request);
$routes_array = $routes->all();
$this->assertEqual(count($routes), 2, 'The correct number of routes was found.');
$this->assertEqual(array('poink', 'poink2'), array_keys($routes_array), 'Ensure the fitness and name were taken into account in the sort.');
$this->assertNotNull($routes->get('poink'), 'The first matching route was found.');
$this->assertNotNull($routes->get('poink2'), 'The second matching route was found.');
$this->assertNull($routes->get('eep'), 'Non-matching route was not found.');
}
catch (ResourceNotFoundException $e) {
$this->fail('No matching route found with default argument value.');
@ -328,7 +375,7 @@ class RouteProviderTest extends KernelTestBase {
*/
public function testOutlinePathMatchZero() {
$connection = Database::getConnection();
$provider = new RouteProvider($connection, $this->state, $this->currentPath, $this->cache, $this->pathProcessor, 'test_routes');
$provider = new RouteProvider($connection, $this->state, $this->currentPath, $this->cache, $this->pathProcessor, $this->cacheTagsInvalidator, 'test_routes');
$this->fixtures->createTables($connection);
@ -363,7 +410,7 @@ class RouteProviderTest extends KernelTestBase {
*/
function testOutlinePathNoMatch() {
$connection = Database::getConnection();
$provider = new RouteProvider($connection, $this->state, $this->currentPath, $this->cache, $this->pathProcessor, 'test_routes');
$provider = new RouteProvider($connection, $this->state, $this->currentPath, $this->cache, $this->pathProcessor, $this->cacheTagsInvalidator, 'test_routes');
$this->fixtures->createTables($connection);
@ -388,7 +435,7 @@ class RouteProviderTest extends KernelTestBase {
*/
public function testRouteCaching() {
$connection = Database::getConnection();
$provider = new RouteProvider($connection, $this->state, $this->currentPath, $this->cache, $this->pathProcessor, 'test_routes');
$provider = new RouteProvider($connection, $this->state, $this->currentPath, $this->cache, $this->pathProcessor, $this->cacheTagsInvalidator, 'test_routes');
$this->fixtures->createTables($connection);
@ -450,7 +497,7 @@ class RouteProviderTest extends KernelTestBase {
*/
public function testRouteByName() {
$connection = Database::getConnection();
$provider = new RouteProvider($connection, $this->state, $this->currentPath, $this->cache, $this->pathProcessor, 'test_routes');
$provider = new RouteProvider($connection, $this->state, $this->currentPath, $this->cache, $this->pathProcessor, $this->cacheTagsInvalidator, 'test_routes');
$this->fixtures->createTables($connection);
@ -485,7 +532,7 @@ class RouteProviderTest extends KernelTestBase {
*/
public function testGetRoutesByPatternWithLongPatterns() {
$connection = Database::getConnection();
$provider = new RouteProvider($connection, $this->state, $this->currentPath, $this->cache, $this->pathProcessor, 'test_routes');
$provider = new RouteProvider($connection, $this->state, $this->currentPath, $this->cache, $this->pathProcessor, $this->cacheTagsInvalidator, 'test_routes');
$this->fixtures->createTables($connection);
// This pattern has only 3 parts, so we will get candidates, but no routes,
@ -543,7 +590,7 @@ class RouteProviderTest extends KernelTestBase {
*/
public function testGetRoutesPaged() {
$connection = Database::getConnection();
$provider = new RouteProvider($connection, $this->state, $this->currentPath, $this->cache, $this->pathProcessor, 'test_routes');
$provider = new RouteProvider($connection, $this->state, $this->currentPath, $this->cache, $this->pathProcessor, $this->cacheTagsInvalidator, 'test_routes');
$this->fixtures->createTables($connection);
$dumper = new MatcherDumper($connection, $this->state, 'test_routes');

View file

@ -8,6 +8,7 @@
namespace Drupal\system\Tests\Routing;
use Drupal\Core\Cache\Cache;
use Drupal\Core\EventSubscriber\MainContentViewSubscriber;
use Drupal\Core\Language\LanguageInterface;
use Drupal\simpletest\WebTestBase;
use Symfony\Component\HttpFoundation\Request;
@ -32,6 +33,7 @@ class RouterTest extends WebTestBase {
*/
public function testFinishResponseSubscriber() {
$renderer_required_cache_contexts = ['languages:' . LanguageInterface::TYPE_INTERFACE, 'theme', 'user.permissions'];
$expected_cache_contexts = Cache::mergeContexts($renderer_required_cache_contexts, ['url.query_args:' . MainContentViewSubscriber::WRAPPER_FORMAT]);
// Confirm that the router can get to a controller.
$this->drupalGet('router_test/test1');
@ -47,7 +49,7 @@ class RouterTest extends WebTestBase {
$this->assertRaw('test2', 'The correct string was returned because the route was successful.');
// Check expected headers from FinishResponseSubscriber.
$headers = $this->drupalGetHeaders();
$this->assertEqual($headers['x-drupal-cache-contexts'], implode(' ', $renderer_required_cache_contexts));
$this->assertEqual($headers['x-drupal-cache-contexts'], implode(' ', $expected_cache_contexts));
$this->assertEqual($headers['x-drupal-cache-tags'], 'config:user.role.anonymous rendered');
// Confirm that the page wrapping is being added, so we're not getting a
// raw body returned.
@ -197,6 +199,15 @@ class RouterTest extends WebTestBase {
$this->drupalGet('router_test/test14/2');
$this->assertResponse(200);
$this->assertText('Route not matched.');
// Check that very long paths don't cause an error.
$path = 'router_test/test1';
$suffix = '/d/r/u/p/a/l';
for ($i = 0; $i < 10; $i++) {
$path .= $suffix;
$this->drupalGet($path);
$this->assertResponse(404);
}
}
/**

View file

@ -7,14 +7,14 @@
namespace Drupal\system\Tests\ServiceProvider;
use Drupal\simpletest\WebTestBase;
use Drupal\simpletest\KernelTestBase;
/**
* Tests service provider registration to the DIC.
*
* @group ServiceProvider
*/
class ServiceProviderTest extends WebTestBase {
class ServiceProviderTest extends KernelTestBase {
/**
* Modules to enable.
@ -27,13 +27,9 @@ class ServiceProviderTest extends WebTestBase {
* Tests that services provided by module service providers get registered to the DIC.
*/
function testServiceProviderRegistration() {
$this->assertTrue(\Drupal::getContainer()->getDefinition('file.usage')->getClass() == 'Drupal\\service_provider_test\\TestFileUsage', 'Class has been changed');
$definition = $this->container->getDefinition('file.usage');
$this->assertTrue($definition->getClass() == 'Drupal\\service_provider_test\\TestFileUsage', 'Class has been changed');
$this->assertTrue(\Drupal::hasService('service_provider_test_class'), 'The service_provider_test_class service has been registered to the DIC');
// The event subscriber method in the test class calls drupal_set_message with
// a message saying it has fired. This will fire on every page request so it
// should show up on the front page.
$this->drupalGet('');
$this->assertText(t('The service_provider_test event subscriber fired!'), 'The service_provider_test event subscriber fired');
}
/**

View file

@ -0,0 +1,41 @@
<?php
/**
* @file
* Contains \Drupal\system\Tests\ServiceProvider\ServiceProviderWebTest.
*/
namespace Drupal\system\Tests\ServiceProvider;
use Drupal\simpletest\WebTestBase;
/**
* Tests service provider registration to the DIC.
*
* @group ServiceProvider
*/
class ServiceProviderWebTest extends WebTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('file', 'service_provider_test');
/**
* Tests that module service providers get registered to the DIC.
*
* Also tests that services provided by module service providers get
* registered to the DIC.
*/
public function testServiceProviderRegistrationIntegration() {
$this->assertTrue(\Drupal::hasService('service_provider_test_class'), 'The service_provider_test_class service has been registered to the DIC');
// The event subscriber method in the test class calls drupal_set_message()
// with a message saying it has fired. This will fire on every page request
// so it should show up on the front page.
$this->drupalGet('');
$this->assertText(t('The service_provider_test event subscriber fired!'), 'The service_provider_test event subscriber fired');
}
}

View file

@ -22,13 +22,14 @@ class DateTimeTest extends WebTestBase {
*
* @var array
*/
public static $modules = array('node', 'language');
public static $modules = ['block', 'node', 'language'];
protected function setUp() {
parent::setUp();
// Create admin user and log in admin user.
$this->drupalLogin ($this->drupalCreateUser(array('administer site configuration')));
$this->drupalPlaceBlock('local_actions_block');
}
/**
@ -117,6 +118,21 @@ class DateTimeTest extends WebTestBase {
// Make sure the date does not exist in config.
$date_format = entity_load('date_format', $date_format_id);
$this->assertFalse($date_format);
// Add a new date format with an existing format.
$date_format_id = strtolower($this->randomMachineName(8));
$name = ucwords($date_format_id);
$date_format = 'Y';
$edit = array(
'id' => $date_format_id,
'label' => $name,
'date_format_pattern' => $date_format,
);
$this->drupalPostForm('admin/config/regional/date-time/formats/add', $edit, t('Add format'));
$this->assertUrl(\Drupal::url('entity.date_format.collection', [], ['absolute' => TRUE]), [], 'Correct page redirection.');
$this->assertText(t('Custom date format added.'), 'Date format added confirmation message appears.');
$this->assertText($name, 'Custom date format appears in the date format list.');
$this->assertText(t('Delete'), 'Delete link for custom date format appears.');
}
/**

View file

@ -30,32 +30,32 @@ class ErrorHandlerTest extends WebTestBase {
$config = $this->config('system.logging');
$error_notice = array(
'%type' => 'Notice',
'!message' => 'Undefined variable: bananas',
'@message' => 'Undefined variable: bananas',
'%function' => 'Drupal\error_test\Controller\ErrorTestController->generateWarnings()',
'%file' => drupal_get_path('module', 'error_test') . '/error_test.module',
);
$error_warning = array(
'%type' => 'Warning',
'!message' => 'Division by zero',
'@message' => 'Division by zero',
'%function' => 'Drupal\error_test\Controller\ErrorTestController->generateWarnings()',
'%file' => drupal_get_path('module', 'error_test') . '/error_test.module',
);
$error_user_notice = array(
'%type' => 'User warning',
'!message' => 'Drupal is awesome',
'@message' => 'Drupal & awesome',
'%function' => 'Drupal\error_test\Controller\ErrorTestController->generateWarnings()',
'%file' => drupal_get_path('module', 'error_test') . '/error_test.module',
);
$fatal_error = array(
'%type' => 'Recoverable fatal error',
'%function' => 'Drupal\error_test\Controller\ErrorTestController->Drupal\error_test\Controller\{closure}()',
'!message' => 'Argument 1 passed to Drupal\error_test\Controller\ErrorTestController::Drupal\error_test\Controller\{closure}() must be of the type array, string given, called in ' . \Drupal::root() . '/core/modules/system/tests/modules/error_test/src/Controller/ErrorTestController.php on line 66 and defined',
'@message' => 'Argument 1 passed to Drupal\error_test\Controller\ErrorTestController::Drupal\error_test\Controller\{closure}() must be of the type array, string given, called in ' . \Drupal::root() . '/core/modules/system/tests/modules/error_test/src/Controller/ErrorTestController.php on line 66 and defined',
);
if (version_compare(PHP_VERSION, '7.0.0-dev') >= 0) {
// In PHP 7, instead of a recoverable fatal error we get a TypeError.
$fatal_error['%type'] = 'TypeError';
// The error message also changes in PHP 7.
$fatal_error['!message'] = 'Argument 1 passed to Drupal\error_test\Controller\ErrorTestController::Drupal\error_test\Controller\{closure}() must be of the type array, string given, called in ' . \Drupal::root() . '/core/modules/system/tests/modules/error_test/src/Controller/ErrorTestController.php on line 66';
$fatal_error['@message'] = 'Argument 1 passed to Drupal\error_test\Controller\ErrorTestController::Drupal\error_test\Controller\{closure}() must be of the type array, string given, called in ' . \Drupal::root() . '/core/modules/system/tests/modules/error_test/src/Controller/ErrorTestController.php on line 66';
}
// Set error reporting to display verbose notices.
@ -66,6 +66,9 @@ class ErrorHandlerTest extends WebTestBase {
$this->assertErrorMessage($error_warning);
$this->assertErrorMessage($error_user_notice);
$this->assertRaw('<pre class="backtrace">', 'Found pre element with backtrace class.');
// Ensure we are escaping but not double escaping.
$this->assertRaw('&amp;');
$this->assertNoRaw('&amp;amp;');
// Set error reporting to display verbose notices.
$this->config('system.logging')->set('error_level', ERROR_REPORTING_DISPLAY_VERBOSE)->save();
@ -73,6 +76,9 @@ class ErrorHandlerTest extends WebTestBase {
$this->assertResponse(500, 'Received expected HTTP status code.');
$this->assertErrorMessage($fatal_error);
$this->assertRaw('<pre class="backtrace">', 'Found pre element with backtrace class.');
// Ensure we are escaping but not double escaping.
$this->assertRaw('&#039;');
$this->assertNoRaw('&amp;#039;');
// Remove the recoverable fatal error from the assertions, it's wanted here.
// Ensure that we just remove this one recoverable fatal error (in PHP 7 this
@ -111,6 +117,7 @@ class ErrorHandlerTest extends WebTestBase {
$this->assertNoErrorMessage($error_notice);
$this->assertNoErrorMessage($error_warning);
$this->assertNoErrorMessage($error_user_notice);
$this->assertNoMessages();
$this->assertNoRaw('<pre class="backtrace">', 'Did not find pre element with backtrace class.');
}
@ -123,21 +130,21 @@ class ErrorHandlerTest extends WebTestBase {
$error_exception = array(
'%type' => 'Exception',
'!message' => 'Drupal is awesome',
'@message' => 'Drupal & awesome',
'%function' => 'Drupal\error_test\Controller\ErrorTestController->triggerException()',
'%line' => 56,
'%file' => drupal_get_path('module', 'error_test') . '/error_test.module',
);
$error_pdo_exception = array(
'%type' => 'DatabaseExceptionWrapper',
'!message' => 'SELECT * FROM bananas_are_awesome',
'@message' => 'SELECT * FROM bananas_are_awesome',
'%function' => 'Drupal\error_test\Controller\ErrorTestController->triggerPDOException()',
'%line' => 64,
'%file' => drupal_get_path('module', 'error_test') . '/error_test.module',
);
$error_renderer_exception = array(
'%type' => 'Exception',
'!message' => 'This is an exception that occurs during rendering',
'@message' => 'This is an exception that occurs during rendering',
'%function' => 'Drupal\error_test\Controller\ErrorTestController->Drupal\error_test\Controller\{closure}()',
'%line' => 82,
'%file' => drupal_get_path('module', 'error_test') . '/error_test.module',
@ -152,9 +159,9 @@ class ErrorHandlerTest extends WebTestBase {
// We cannot use assertErrorMessage() since the exact error reported
// varies from database to database. Check that the SQL string is displayed.
$this->assertText($error_pdo_exception['%type'], format_string('Found %type in error page.', $error_pdo_exception));
$this->assertText($error_pdo_exception['!message'], format_string('Found !message in error page.', $error_pdo_exception));
$this->assertText($error_pdo_exception['@message'], format_string('Found @message in error page.', $error_pdo_exception));
$error_details = format_string('in %function (line ', $error_pdo_exception);
$this->assertRaw($error_details, format_string("Found '!message' in error page.", array('!message' => $error_details)));
$this->assertRaw($error_details, format_string("Found '@message' in error page.", array('@message' => $error_details)));
$this->drupalGet('error-test/trigger-renderer-exception');
$this->assertTrue(strpos($this->drupalGetHeader(':status'), '500 Service unavailable (with message)'), 'Received expected HTTP status line.');
@ -180,15 +187,26 @@ class ErrorHandlerTest extends WebTestBase {
* Helper function: assert that the error message is found.
*/
function assertErrorMessage(array $error) {
$message = t('%type: !message in %function (line ', $error);
$this->assertRaw($message, format_string('Found error message: !message.', array('!message' => $message)));
$message = t('%type: @message in %function (line ', $error);
$this->assertRaw($message, format_string('Found error message: @message.', array('@message' => $message)));
}
/**
* Helper function: assert that the error message is not found.
*/
function assertNoErrorMessage(array $error) {
$message = t('%type: !message in %function (line ', $error);
$this->assertNoRaw($message, format_string('Did not find error message: !message.', array('!message' => $message)));
$message = t('%type: @message in %function (line ', $error);
$this->assertNoRaw($message, format_string('Did not find error message: @message.', array('@message' => $message)));
}
/**
* Asserts that no messages are printed onto the page.
*
* @return bool
* TRUE, if there are no messages.
*/
protected function assertNoMessages() {
return $this->assertFalse($this->xpath('//div[contains(@class, "messages")]'), 'Ensures that also no messages div exists, which proves that no messages were generated by the error handler, not even an empty one.');
}
}

View file

@ -7,7 +7,7 @@
namespace Drupal\system\Tests\System;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Component\Utility\Html;
use Drupal\Component\Utility\Xss;
use Drupal\simpletest\WebTestBase;
@ -55,9 +55,9 @@ class PageTitleTest extends WebTestBase {
$node = $this->drupalGetNodeByTitle($edit['title[0][value]']);
$this->assertNotNull($node, 'Node created and found in database');
$this->assertText(SafeMarkup::checkPlain($edit['title[0][value]']), 'Check to make sure tags in the node title are converted.');
$this->assertText(Html::escape($edit['title[0][value]']), 'Check to make sure tags in the node title are converted.');
$this->drupalGet("node/" . $node->id());
$this->assertText(SafeMarkup::checkPlain($edit['title[0][value]']), 'Check to make sure tags in the node title are converted.');
$this->assertText(Html::escape($edit['title[0][value]']), 'Check to make sure tags in the node title are converted.');
}
/**
@ -66,7 +66,7 @@ class PageTitleTest extends WebTestBase {
function testTitleXSS() {
// Set some title with JavaScript and HTML chars to escape.
$title = '</title><script type="text/javascript">alert("Title XSS!");</script> & < > " \' ';
$title_filtered = SafeMarkup::checkPlain($title);
$title_filtered = Html::escape($title);
$slogan = '<script type="text/javascript">alert("Slogan XSS!");</script>';
$slogan_filtered = Xss::filterAdmin($slogan);
@ -143,19 +143,10 @@ class PageTitleTest extends WebTestBase {
// controller does not escape them.
$this->drupalGet('test-page-cached-controller');
$this->assertTitle('Cached title | Drupal');
$this->assertRaw(SafeMarkup::checkPlain('<span>Cached title</span>') . '</h1>');
$this->assertRaw(Html::escape('<span>Cached title</span>') . '</h1>');
$this->drupalGet('test-page-cached-controller');
$this->assertTitle('Cached title | Drupal');
$this->assertRaw(SafeMarkup::checkPlain('<span>Cached title</span>') . '</h1>');
// Ensure that titles are cacheable and are escaped normally if the
// controller escapes them use SafeMarkup::checkPlain().
$this->drupalGet('test-page-cached-controller-safe');
$this->assertTitle('<span>Cached title</span> | Drupal');
$this->assertRaw(SafeMarkup::checkPlain('<span>Cached title</span>') . '</h1>');
$this->drupalGet('test-page-cached-controller-safe');
$this->assertTitle('<span>Cached title</span> | Drupal');
$this->assertRaw(SafeMarkup::checkPlain('<span>Cached title</span>') . '</h1>');
$this->assertRaw(Html::escape('<span>Cached title</span>') . '</h1>');
}
}

View file

@ -7,6 +7,7 @@
namespace Drupal\system\Tests\System;
use Drupal\Core\Url;
use Drupal\simpletest\WebTestBase;
/**
@ -30,6 +31,7 @@ class SiteMaintenanceTest extends WebTestBase {
// Configure 'node' as front page.
$this->config('system.site')->set('page.front', '/node')->save();
$this->config('system.performance')->set('js.preprocess', 1)->save();
// Create a user allowed to access site in maintenance mode.
$this->user = $this->drupalCreateUser(array('access site in maintenance mode'));
@ -42,6 +44,10 @@ class SiteMaintenanceTest extends WebTestBase {
* Verify site maintenance mode functionality.
*/
protected function testSiteMaintenance() {
$this->drupalGet(Url::fromRoute('user.page'));
// JS should be aggregated, so drupal.js is not in the page source.
$links = $this->xpath('//script[contains(@src, :href)]', array(':href' => '/core/misc/drupal.js'));
$this->assertFalse(isset($links[0]), 'script /core/misc/drupal.js not in page');
// Turn on maintenance mode.
$edit = array(
'maintenance_mode' => 1,
@ -52,7 +58,10 @@ class SiteMaintenanceTest extends WebTestBase {
$user_message = t('Operating in maintenance mode.');
$offline_message = t('@site is currently under maintenance. We should be back shortly. Thank you for your patience.', array('@site' => $this->config('system.site')->get('name')));
$this->drupalGet('');
$this->drupalGet(Url::fromRoute('user.page'));
// JS should not be aggregated, so drupal.js is expected in the page source.
$links = $this->xpath('//script[contains(@src, :href)]', array(':href' => '/core/misc/drupal.js'));
$this->assertTrue(isset($links[0]), 'script /core/misc/drupal.js in page');
$this->assertRaw($admin_message, 'Found the site maintenance mode message.');
// Logout and verify that offline message is displayed.

View file

@ -30,7 +30,7 @@ class ThemeTest extends WebTestBase {
*
* @var array
*/
public static $modules = array('node', 'block', 'file');
public static $modules = ['node', 'block', 'file'];
protected function setUp() {
parent::setUp();
@ -40,6 +40,7 @@ class ThemeTest extends WebTestBase {
$this->adminUser = $this->drupalCreateUser(array('access administration pages', 'view the administration theme', 'administer themes', 'bypass node access', 'administer blocks'));
$this->drupalLogin($this->adminUser);
$this->node = $this->drupalCreateNode();
$this->drupalPlaceBlock('local_tasks_block');
}
/**

View file

@ -7,7 +7,7 @@
namespace Drupal\system\Tests\System;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Component\Utility\Html;
use Drupal\Component\Utility\Xss;
use Drupal\Core\Render\BubbleableMetadata;
@ -70,7 +70,7 @@ class TokenReplaceUnitTest extends TokenReplaceUnitTestBase {
$source .= '[bogus:token]';
// Replace with with the clear parameter, only the valid token should remain.
$target = SafeMarkup::checkPlain($this->config('system.site')->get('name'));
$target = Html::escape($this->config('system.site')->get('name'));
$result = $this->tokenService->replace($source, array(), array('langcode' => $this->interfaceLanguage->getId(), 'clear' => TRUE));
$this->assertEqual($target, $result, 'Valid tokens replaced while invalid tokens ignored.');
@ -105,7 +105,7 @@ class TokenReplaceUnitTest extends TokenReplaceUnitTestBase {
// Generate and test sanitized tokens.
$tests = array();
$tests['[site:name]'] = SafeMarkup::checkPlain($config->get('name'));
$tests['[site:name]'] = Html::escape($config->get('name'));
$tests['[site:slogan]'] = $safe_slogan;
$tests['[site:mail]'] = $config->get('mail');
$tests['[site:url]'] = \Drupal::url('<front>', [], $url_options);

View file

@ -7,6 +7,7 @@
namespace Drupal\system\Tests\System;
use Drupal\Core\EventSubscriber\MainContentViewSubscriber;
use Drupal\simpletest\WebTestBase;
use Drupal\system\Tests\Cache\AssertPageCacheContextsAndTagsTrait;
@ -35,12 +36,12 @@ class TokenReplaceWebTest extends WebTestBase {
$this->drupalGet('token-test/' . $node->id());
$this->assertText("Tokens: {$node->id()} {$account->id()}");
$this->assertCacheTags(['node:1', 'rendered', 'user:2']);
$this->assertCacheContexts(['languages:language_interface', 'theme', 'user']);
$this->assertCacheContexts(['languages:language_interface', 'theme', 'url.query_args:' . MainContentViewSubscriber::WRAPPER_FORMAT, 'user']);
$this->drupalGet('token-test-without-bubleable-metadata/' . $node->id());
$this->assertText("Tokens: {$node->id()} {$account->id()}");
$this->assertCacheTags(['node:1', 'rendered', 'user:2']);
$this->assertCacheContexts(['languages:language_interface', 'theme', 'user']);
$this->assertCacheContexts(['languages:language_interface', 'theme', 'url.query_args:' . MainContentViewSubscriber::WRAPPER_FORMAT, 'user']);
}
}

View file

@ -89,17 +89,77 @@ class UncaughtExceptionTest extends WebTestBase {
$this->assertText($this->expectedExceptionMessage);
}
/**
* Tests uncaught exception handling with custom exception handler.
*/
public function testUncaughtExceptionCustomExceptionHandler() {
$settings_filename = $this->siteDirectory . '/settings.php';
chmod($settings_filename, 0777);
$settings_php = file_get_contents($settings_filename);
$settings_php .= "\n";
$settings_php .= "set_exception_handler(function() {\n";
$settings_php .= " header('HTTP/1.1 418 I\'m a teapot');\n";
$settings_php .= " print('Oh oh, flying teapots');\n";
$settings_php .= "});\n";
file_put_contents($settings_filename, $settings_php);
\Drupal::state()->set('error_service_test.break_bare_html_renderer', TRUE);
$this->drupalGet('');
$this->assertResponse(418);
$this->assertNoText('The website encountered an unexpected error. Please try again later.');
$this->assertNoText('Oh oh, bananas in the instruments');
$this->assertText('Oh oh, flying teapots');
}
/**
* Tests a missing dependency on a service.
*/
public function testMissingDependency() {
$this->expectedExceptionMessage = 'Argument 1 passed to Drupal\error_service_test\LonelyMonkeyClass::__construct() must be an instance of Drupal\Core\Database\Connection, non';
$this->drupalGet('broken-service-class');
$this->assertResponse(500);
$this->assertRaw('The website encountered an unexpected error.');
$this->assertRaw($this->expectedExceptionMessage);
}
/**
* Tests a missing dependency on a service with a custom error handler.
*/
public function testMissingDependencyCustomErrorHandler() {
$settings_filename = $this->siteDirectory . '/settings.php';
chmod($settings_filename, 0777);
$settings_php = file_get_contents($settings_filename);
$settings_php .= "\n";
$settings_php .= "set_error_handler(function() {\n";
$settings_php .= " header('HTTP/1.1 418 I\'m a teapot');\n";
$settings_php .= " print('Oh oh, flying teapots');\n";
$settings_php .= " exit();\n";
$settings_php .= "});\n";
file_put_contents($settings_filename, $settings_php);
$this->drupalGet('broken-service-class');
$this->assertResponse(418);
$this->assertRaw('Oh oh, flying teapots');
$message = 'Argument 1 passed to Drupal\error_service_test\LonelyMonkeyClass::__construct() must be an instance of Drupal\Core\Database\Connection, non';
$this->assertNoRaw('The website encountered an unexpected error.');
$this->assertNoRaw($message);
$found_exception = FALSE;
foreach ($this->assertions as &$assertion) {
if (strpos($assertion['message'], $message) !== FALSE) {
$found_exception = TRUE;
$this->deleteAssert($assertion['message_id']);
unset($assertion);
}
}
$this->assertTrue($found_exception, 'Ensure that the exception of a missing constructor argument was triggered.');
}
/**
* Tests a container which has an error.
*/
@ -110,17 +170,11 @@ class UncaughtExceptionTest extends WebTestBase {
'required' => TRUE,
];
$this->writeSettings($settings);
// Need to rebuild the container, so the dumped container can be tested
// and not the container builder.
\Drupal::service('kernel')->rebuildContainer();
// Ensure that we don't use the now broken generated container on the test
// process.
\Drupal::setContainer($this->container);
\Drupal::service('kernel')->invalidateContainer();
$this->expectedExceptionMessage = 'Argument 1 passed to Drupal\system\Tests\Bootstrap\ErrorContainer::Drupal\system\Tests\Bootstrap\{closur';
$this->drupalGet('');
$this->assertResponse(500);
$this->assertRaw($this->expectedExceptionMessage);
}
@ -135,17 +189,11 @@ class UncaughtExceptionTest extends WebTestBase {
'required' => TRUE,
];
$this->writeSettings($settings);
// Need to rebuild the container, so the dumped container can be tested
// and not the container builder.
\Drupal::service('kernel')->rebuildContainer();
// Ensure that we don't use the now broken generated container on the test
// process.
\Drupal::setContainer($this->container);
\Drupal::service('kernel')->invalidateContainer();
$this->expectedExceptionMessage = 'Thrown exception during Container::get';
$this->drupalGet('');
$this->assertResponse(500);
$this->assertRaw('The website encountered an unexpected error');
@ -182,9 +230,39 @@ class UncaughtExceptionTest extends WebTestBase {
$this->writeSettings($settings);
$this->drupalGet('');
$this->assertResponse(500);
$this->assertRaw('PDOException');
}
/**
* Tests fallback to PHP error log when an exception is thrown while logging.
*/
public function testLoggerException() {
// Ensure the test error log is empty before these tests.
$this->assertNoErrorsLogged();
$this->expectedExceptionMessage = 'Deforestation';
\Drupal::state()->set('error_service_test.break_logger', TRUE);
$this->drupalGet('');
$this->assertResponse(500);
$this->assertText('The website encountered an unexpected error. Please try again later.');
$this->assertRaw($this->expectedExceptionMessage);
// Find fatal error logged to the simpletest error.log
$errors = file(\Drupal::root() . '/' . $this->siteDirectory . '/error.log');
$this->assertIdentical(count($errors), 1, 'Exactly one line logged to the PHP error log');
$expected_path = \Drupal::root() . '/core/modules/system/tests/modules/error_service_test/src/MonkeysInTheControlRoom.php';
$expected_line = 61;
$expected_entry = "Failed to log error: Exception: Deforestation in Drupal\\error_service_test\\MonkeysInTheControlRoom->handle() (line ${expected_line} of ${expected_path})";
$this->assert(strpos($errors[0], $expected_entry) !== FALSE, 'Original error logged to the PHP error log when an exception is thrown by a logger');
// The exception is expected. Do not interpret it as a test failure. Not
// using File API; a potential error must trigger a PHP warning.
unlink(\Drupal::root() . '/' . $this->siteDirectory . '/error.log');
}
/**
* {@inheritdoc}
*/

View file

@ -8,6 +8,7 @@
namespace Drupal\system\Tests\Theme;
use Drupal\Component\Serialization\Json;
use Drupal\Component\Utility\Html;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Core\Session\UserSession;
use Drupal\Core\Url;
@ -218,13 +219,13 @@ class FunctionsTest extends WebTestBase {
$expected_links = '';
$expected_links .= '<ul id="somelinks">';
$expected_links .= '<li class="a-link"><a href="' . Url::fromUri('base:a/link')->toString() . '">' . SafeMarkup::checkPlain('A <link>') . '</a></li>';
$expected_links .= '<li class="plain-text">' . SafeMarkup::checkPlain('Plain "text"') . '</li>';
$expected_links .= '<li class="html-text"><span class="unescaped">' . SafeMarkup::checkPlain('potentially unsafe text that <should> be escaped') . '</span></li>';
$expected_links .= '<li class="front-page"><a href="' . Url::fromRoute('<front>')->toString() . '">' . SafeMarkup::checkPlain('Front page') . '</a></li>';
$expected_links .= '<li class="router-test"><a href="' . \Drupal::urlGenerator()->generate('router_test.1') . '">' . SafeMarkup::checkPlain('Test route') . '</a></li>';
$expected_links .= '<li class="a-link"><a href="' . Url::fromUri('base:a/link')->toString() . '">' . Html::escape('A <link>') . '</a></li>';
$expected_links .= '<li class="plain-text">' . Html::escape('Plain "text"') . '</li>';
$expected_links .= '<li class="html-text"><span class="unescaped">' . Html::escape('potentially unsafe text that <should> be escaped') . '</span></li>';
$expected_links .= '<li class="front-page"><a href="' . Url::fromRoute('<front>')->toString() . '">' . Html::escape('Front page') . '</a></li>';
$expected_links .= '<li class="router-test"><a href="' . \Drupal::urlGenerator()->generate('router_test.1') . '">' . Html::escape('Test route') . '</a></li>';
$query = array('key' => 'value');
$expected_links .= '<li class="query-test"><a href="' . \Drupal::urlGenerator()->generate('router_test.1', $query) . '">' . SafeMarkup::checkPlain('Query test route') . '</a></li>';
$expected_links .= '<li class="query-test"><a href="' . \Drupal::urlGenerator()->generate('router_test.1', $query) . '">' . Html::escape('Query test route') . '</a></li>';
$expected_links .= '</ul>';
// Verify that passing a string as heading works.
@ -258,13 +259,13 @@ class FunctionsTest extends WebTestBase {
);
$expected_links = '';
$expected_links .= '<ul id="somelinks">';
$expected_links .= '<li class="a-link"><a href="' . Url::fromUri('base:a/link')->toString() . '">' . SafeMarkup::checkPlain('A <link>') . '</a></li>';
$expected_links .= '<li class="plain-text"><span class="a/class">' . SafeMarkup::checkPlain('Plain "text"') . '</span></li>';
$expected_links .= '<li class="html-text"><span class="unescaped">' . SafeMarkup::checkPlain('potentially unsafe text that <should> be escaped') . '</span></li>';
$expected_links .= '<li class="front-page"><a href="' . Url::fromRoute('<front>')->toString() . '">' . SafeMarkup::checkPlain('Front page') . '</a></li>';
$expected_links .= '<li class="router-test"><a href="' . \Drupal::urlGenerator()->generate('router_test.1') . '">' . SafeMarkup::checkPlain('Test route') . '</a></li>';
$expected_links .= '<li class="a-link"><a href="' . Url::fromUri('base:a/link')->toString() . '">' . Html::escape('A <link>') . '</a></li>';
$expected_links .= '<li class="plain-text"><span class="a/class">' . Html::escape('Plain "text"') . '</span></li>';
$expected_links .= '<li class="html-text"><span class="unescaped">' . Html::escape('potentially unsafe text that <should> be escaped') . '</span></li>';
$expected_links .= '<li class="front-page"><a href="' . Url::fromRoute('<front>')->toString() . '">' . Html::escape('Front page') . '</a></li>';
$expected_links .= '<li class="router-test"><a href="' . \Drupal::urlGenerator()->generate('router_test.1') . '">' . Html::escape('Test route') . '</a></li>';
$query = array('key' => 'value');
$expected_links .= '<li class="query-test"><a href="' . \Drupal::urlGenerator()->generate('router_test.1', $query) . '">' . SafeMarkup::checkPlain('Query test route') . '</a></li>';
$expected_links .= '<li class="query-test"><a href="' . \Drupal::urlGenerator()->generate('router_test.1', $query) . '">' . Html::escape('Query test route') . '</a></li>';
$expected_links .= '</ul>';
$expected = $expected_heading . $expected_links;
$this->assertThemeOutput('links', $variables, $expected);
@ -274,14 +275,14 @@ class FunctionsTest extends WebTestBase {
$variables['set_active_class'] = TRUE;
$expected_links = '';
$expected_links .= '<ul id="somelinks">';
$expected_links .= '<li class="a-link"><a href="' . Url::fromUri('base:a/link')->toString() . '">' . SafeMarkup::checkPlain('A <link>') . '</a></li>';
$expected_links .= '<li class="plain-text"><span class="a/class">' . SafeMarkup::checkPlain('Plain "text"') . '</span></li>';
$expected_links .= '<li class="html-text"><span class="unescaped">' . SafeMarkup::checkPlain('potentially unsafe text that <should> be escaped') . '</span></li>';
$expected_links .= '<li data-drupal-link-system-path="&lt;front&gt;" class="front-page"><a href="' . Url::fromRoute('<front>')->toString() . '" data-drupal-link-system-path="&lt;front&gt;">' . SafeMarkup::checkPlain('Front page') . '</a></li>';
$expected_links .= '<li data-drupal-link-system-path="router_test/test1" class="router-test"><a href="' . \Drupal::urlGenerator()->generate('router_test.1') . '" data-drupal-link-system-path="router_test/test1">' . SafeMarkup::checkPlain('Test route') . '</a></li>';
$expected_links .= '<li class="a-link"><a href="' . Url::fromUri('base:a/link')->toString() . '">' . Html::escape('A <link>') . '</a></li>';
$expected_links .= '<li class="plain-text"><span class="a/class">' . Html::escape('Plain "text"') . '</span></li>';
$expected_links .= '<li class="html-text"><span class="unescaped">' . Html::escape('potentially unsafe text that <should> be escaped') . '</span></li>';
$expected_links .= '<li data-drupal-link-system-path="&lt;front&gt;" class="front-page"><a href="' . Url::fromRoute('<front>')->toString() . '" data-drupal-link-system-path="&lt;front&gt;">' . Html::escape('Front page') . '</a></li>';
$expected_links .= '<li data-drupal-link-system-path="router_test/test1" class="router-test"><a href="' . \Drupal::urlGenerator()->generate('router_test.1') . '" data-drupal-link-system-path="router_test/test1">' . Html::escape('Test route') . '</a></li>';
$query = array('key' => 'value');
$encoded_query = SafeMarkup::checkPlain(Json::encode($query));
$expected_links .= '<li data-drupal-link-query="'.$encoded_query.'" data-drupal-link-system-path="router_test/test1" class="query-test"><a href="' . \Drupal::urlGenerator()->generate('router_test.1', $query) . '" data-drupal-link-query="'.$encoded_query.'" data-drupal-link-system-path="router_test/test1">' . SafeMarkup::checkPlain('Query test route') . '</a></li>';
$encoded_query = Html::escape(Json::encode($query));
$expected_links .= '<li data-drupal-link-query="'.$encoded_query.'" data-drupal-link-system-path="router_test/test1" class="query-test"><a href="' . \Drupal::urlGenerator()->generate('router_test.1', $query) . '" data-drupal-link-query="'.$encoded_query.'" data-drupal-link-system-path="router_test/test1">' . Html::escape('Query test route') . '</a></li>';
$expected_links .= '</ul>';
$expected = $expected_heading . $expected_links;
$this->assertThemeOutput('links', $variables, $expected);

View file

@ -99,6 +99,47 @@ class RegistryTest extends KernelTestBase {
'template_preprocess',
'test_basetheme_preprocess_theme_test_template_test',
], $preprocess_functions);
}
/**
* Tests the theme registry with suggestions.
*/
public function testSuggestionPreprocessFunctions() {
$theme_handler = \Drupal::service('theme_handler');
$theme_handler->install(['test_theme']);
$registry_theme = new Registry(\Drupal::root(), \Drupal::cache(), \Drupal::lock(), \Drupal::moduleHandler(), $theme_handler, \Drupal::service('theme.initialization'), 'test_theme');
$registry_theme->setThemeManager(\Drupal::theme());
$suggestions = ['__kitten', '__flamingo'];
$expected_preprocess_functions = [
'template_preprocess',
'theme_test_preprocess_theme_test_preprocess_suggestions',
];
$suggestion = '';
$hook = 'theme_test_preprocess_suggestions';
do {
$hook .= "$suggestion";
$expected_preprocess_functions[] = "test_theme_preprocess_$hook";
$preprocess_functions = $registry_theme->get()[$hook]['preprocess functions'];
$this->assertIdentical($expected_preprocess_functions, $preprocess_functions, "$hook has correct preprocess functions.");
} while ($suggestion = array_shift($suggestions));
$expected_preprocess_functions = [
'template_preprocess',
'theme_test_preprocess_theme_test_preprocess_suggestions',
'test_theme_preprocess_theme_test_preprocess_suggestions',
'test_theme_preprocess_theme_test_preprocess_suggestions__kitten',
];
$preprocess_functions = $registry_theme->get()['theme_test_preprocess_suggestions__kitten__meerkat']['preprocess functions'];
$this->assertIdentical($expected_preprocess_functions, $preprocess_functions, 'Suggestion implemented as a function correctly inherits preprocess functions.');
$preprocess_functions = $registry_theme->get()['theme_test_preprocess_suggestions__kitten__bearcat']['preprocess functions'];
$this->assertIdentical($expected_preprocess_functions, $preprocess_functions, 'Suggestion implemented as a template correctly inherits preprocess functions.');
$this->assertTrue(isset($registry_theme->get()['theme_test_preprocess_suggestions__kitten__meerkat__tarsier__moose']), 'Preprocess function with an unimplemented lower-level suggestion is added to the registry.');
}
/**

View file

@ -71,16 +71,17 @@ class ThemeInfoTest extends WebTestBase {
// should work nevertheless.
$this->drupalGet('theme-test/info/stylesheets');
$this->assertIdentical(1, count($this->xpath("//link[contains(@href, '$base/base-add.css')]")), "$base/base-add.css found");
$this->assertIdentical(0, count($this->xpath("//link[contains(@href, 'base-remove.css')]")), "base-remove.css not found");
$this->assertIdentical(1, count($this->xpath('//style[contains(text(), :text)]', array(':text' => "$base/base-add.css"))), "$base/base-add.css found");
$this->assertIdentical(0, count($this->xpath('//style[contains(text(), :text)]', array(':text' => "base-remove.css"))), "base-remove.css not found");
$this->assertIdentical(1, count($this->xpath("//link[contains(@href, '$sub/sub-add.css')]")), "$sub/sub-add.css found");
$this->assertIdentical(0, count($this->xpath("//link[contains(@href, 'sub-remove.css')]")), "sub-remove.css not found");
$this->assertIdentical(0, count($this->xpath("//link[contains(@href, 'base-add.sub-remove.css')]")), "base-add.sub-remove.css not found");
$this->assertIdentical(1, count($this->xpath('//style[contains(text(), :text)]', array(':text' => "$sub/sub-add.css"))), "$sub/sub-add.css found");
$this->assertIdentical(0, count($this->xpath('//style[contains(text(), :text)]', array(':text' => "sub-remove.css"))), "sub-remove.css not found");
$this->assertIdentical(0, count($this->xpath('//style[contains(text(), :text)]', array(':text' => "base-add.sub-remove.css"))), "base-add.sub-remove.css not found");
// Verify that CSS files with the same name are loaded from both the base theme and subtheme.
$this->assertIdentical(1, count($this->xpath("//link[contains(@href, '$base/samename.css')]")), "$base/samename.css found");
$this->assertIdentical(1, count($this->xpath("//link[contains(@href, '$sub/samename.css')]")), "$sub/samename.css found");
$this->assertIdentical(1, count($this->xpath('//style[contains(text(), :text)]', array(':text' => "$base/samename.css"))), "$base/samename.css found");
$this->assertIdentical(1, count($this->xpath('//style[contains(text(), :text)]', array(':text' => "$sub/samename.css"))), "$sub/samename.css found");
}
/**

View file

@ -13,6 +13,7 @@ use Drupal\test_theme\ThemeClass;
use Symfony\Cmf\Component\Routing\RouteObjectInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Route;
use Drupal\Component\Utility\SafeStringInterface;
/**
* Tests low-level theme functions.
@ -59,12 +60,19 @@ class ThemeTest extends WebTestBase {
* Test that _theme() returns expected data types.
*/
function testThemeDataTypes() {
// theme_test_false is an implemented theme hook so \Drupal::theme() service should
// return a string, even though the theme function itself can return anything.
$foos = array('null' => NULL, 'false' => FALSE, 'integer' => 1, 'string' => 'foo');
// theme_test_false is an implemented theme hook so \Drupal::theme() service
// should return a string or an object that implements SafeStringInterface,
// even though the theme function itself can return anything.
$foos = array('null' => NULL, 'false' => FALSE, 'integer' => 1, 'string' => 'foo', 'empty_string' => '');
foreach ($foos as $type => $example) {
$output = \Drupal::theme()->render('theme_test_foo', array('foo' => $example));
$this->assertTrue(is_string($output), format_string('\Drupal::theme() returns a string for data type !type.', array('!type' => $type)));
$this->assertTrue($output instanceof SafeStringInterface || is_string($output), format_string('\Drupal::theme() returns an object that implements SafeStringInterface or a string for data type !type.', array('!type' => $type)));
if ($output instanceof SafeStringInterface) {
$this->assertIdentical((string) $example, $output->__toString());
}
elseif (is_string($output)) {
$this->assertIdentical($output, '', 'A string will be return when the theme returns an empty string.');
}
}
// suggestionnotimplemented is not an implemented theme hook so \Drupal::theme() service
@ -277,7 +285,7 @@ class ThemeTest extends WebTestBase {
/**
* Tests that region attributes can be manipulated via preprocess functions.
*/
function testRegionClass() {
public function testRegionClass() {
\Drupal::service('module_installer')->install(array('block', 'theme_region_test'));
// Place a block.
@ -287,4 +295,31 @@ class ThemeTest extends WebTestBase {
$this->assertEqual(count($elements), 1, 'New class found.');
}
/**
* Ensures suggestion preprocess functions run for default implementations.
*
* The theme hook used by this test has its base preprocess function in a
* separate file, so this test also ensures that that file is correctly loaded
* when needed.
*/
public function testSuggestionPreprocessForDefaults() {
\Drupal::service('theme_handler')->setDefault('test_theme');
// Test with both an unprimed and primed theme registry.
drupal_theme_rebuild();
for ($i = 0; $i < 2; $i++) {
$this->drupalGet('theme-test/preprocess-suggestions');
$items = $this->cssSelect('.suggestion');
$expected_values = [
'Suggestion',
'Kitten',
'Monkey',
'Kitten',
'Flamingo',
];
foreach ($expected_values as $key => $value) {
$this->assertEqual((string) $value, $items[$key]);
}
}
}
}

View file

@ -7,7 +7,7 @@
namespace Drupal\system\Tests\Theme;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Component\Utility\Html;
use Drupal\Core\Site\Settings;
use Drupal\simpletest\KernelTestBase;
@ -44,7 +44,7 @@ class TwigEnvironmentTest extends KernelTestBase {
'#template' => 'test-with-context <label>{{ unsafe_content }}</label>',
'#context' => array('unsafe_content' => $unsafe_string),
);
$this->assertEqual($renderer->renderRoot($element), 'test-with-context <label>' . SafeMarkup::checkPlain($unsafe_string) . '</label>');
$this->assertEqual($renderer->renderRoot($element), 'test-with-context <label>' . Html::escape($unsafe_string) . '</label>');
// Enable twig_auto_reload and twig_debug.
$settings = Settings::getAll();
@ -83,5 +83,25 @@ class TwigEnvironmentTest extends KernelTestBase {
}
}
/**
* Ensures that cacheFilename() varies by extensions + deployment identifier.
*/
public function testCacheFilename() {
/** @var \Drupal\Core\Template\TwigEnvironment $environment */
// Note: Later we refetch the twig service in order to bypass its internal
// static cache.
$environment = \Drupal::service('twig');
$original_filename = $environment->getCacheFilename('core/modules/system/templates/container.html.twig');
\Drupal::getContainer()->set('twig', NULL);
\Drupal::service('module_installer')->install(['twig_extension_test']);
$environment = \Drupal::service('twig');
$new_extension_filename = $environment->getCacheFilename('core/modules/system/templates/container.html.twig');
\Drupal::getContainer()->set('twig', NULL);
$this->assertNotEqual($new_extension_filename, $original_filename);
}
}

View file

@ -47,6 +47,8 @@ class TwigExtensionTest extends WebTestBase {
$this->drupalGet('twig-extension-test/filter');
$this->assertText('Every plant is not a mineral.', 'Success: String filtered.');
// Test safe_join filter.
$this->assertRaw('&lt;em&gt;will be escaped&lt;/em&gt;<br/><em>will be markup</em><br/><strong>will be rendered</strong>');
}
/**

View file

@ -13,6 +13,7 @@ use Drupal\Core\Config\DatabaseStorage;
use Drupal\Core\Database\Database;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\simpletest\KernelTestBase;
use Drupal\user\Entity\User;
use Symfony\Component\Console\Tester\CommandTester;
use Symfony\Component\DependencyInjection\Reference;
@ -98,15 +99,20 @@ class DbDumpTest extends KernelTestBase {
$this->installEntitySchema('user');
$this->installEntitySchema('file');
$this->installEntitySchema('menu_link_content');
$this->installSchema('system', 'sequences');
// Place some sample config to test for in the export.
$this->data = [
'foo' => $this->randomMachineName(),
'bar' => $this->randomMachineName()
'bar' => $this->randomMachineName(),
];
$storage = new DatabaseStorage(Database::getConnection(), 'config');
$storage->write('test_config', $this->data);
// Create user account with some potential syntax issues.
$account = User::create(['mail' => 'q\'uote$dollar@example.com', 'name' => '$dollar']);
$account->save();
// Create a cache table (this will create 'cache_discovery').
\Drupal::cache('discovery')->set('test', $this->data);
@ -118,13 +124,15 @@ class DbDumpTest extends KernelTestBase {
'block_content_revision',
'cachetags',
'config',
'cache_discovery',
'cache_bootstrap',
'cache_discovery',
'cache_entity',
'file_managed',
'key_value_expire',
'menu_link_content',
'menu_link_content_data',
'semaphore',
'sequences',
'sessions',
'url_alias',
'user__roles',
@ -169,6 +177,12 @@ class DbDumpTest extends KernelTestBase {
// The test data are in the dump (serialized).
$pattern = preg_quote(serialize($this->data));
$this->assertTrue(preg_match('/' . $pattern . '/', $command_tester->getDisplay()), 'Generated data is found in the exported script.');
// Check that the user account name and email address was properly escaped.
$pattern = preg_quote('"q\'uote\$dollar@example.com"');
$this->assertTrue(preg_match('/' . $pattern . '/', $command_tester->getDisplay()), 'The user account email address was properly escaped in the exported script.');
$pattern = preg_quote('\'$dollar\'');
$this->assertTrue(preg_match('/' . $pattern . '/', $command_tester->getDisplay()), 'The user account name was properly escaped in the exported script.');
}
/**

View file

@ -0,0 +1,57 @@
<?php
/**
* @file
* Contains \Drupal\system\Tests\Update\DbUpdatesTrait.
*/
namespace Drupal\system\Tests\Update;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\Url;
/**
* Provides methods to conditionally enable db update functions and run updates.
*/
trait DbUpdatesTrait {
use StringTranslationTrait;
/**
* Enables db updates until the specified index.
*
* @param string $module
* The name of the module defining the update functions.
* @param string $group
* A name identifying the group of update functions to enable.
* @param $index
* The index of the last update function to run.
*/
protected function enableUpdates($module, $group, $index) {
$this->container->get('state')->set($module . '.db_updates.' . $group, $index);
}
/**
* Runs DB updates.
*/
protected function runUpdates() {
$this->drupalGet(Url::fromRoute('system.db_update'));
$this->clickLink($this->t('Continue'));
$this->clickLink($this->t('Apply pending updates'));
}
/**
* Conditionally load Update API functions for the specified group.
*
* @param string $module
* The name of the module defining the update functions.
* @param string $group
* A name identifying the group of update functions to enable.
*/
public static function includeUpdates($module, $group) {
if ($index = \Drupal::state()->get($module . '.db_updates.' . $group)) {
module_load_include('inc', $module, 'update/' . $group . '_' . $index);
}
}
}

View file

@ -0,0 +1,91 @@
<?php
/**
* @file
* Contains \Drupal\system\Tests\Update\LocalActionsAndTasksConvertedIntoBlocksUpdateTest.
*/
namespace Drupal\system\Tests\Update;
use Drupal\node\Entity\Node;
/**
* Tests the upgrade path for local actions/tasks being converted into blocks.
*
* @see https://www.drupal.org/node/507488
*
* @group system
*/
class LocalActionsAndTasksConvertedIntoBlocksUpdateTest extends UpdatePathTestBase {
/**
* {@inheritdoc}
*/
public function setDatabaseDumpFiles() {
$this->databaseDumpFiles = [
__DIR__ . '/../../../../system/tests/fixtures/update/drupal-8.bare.standard.php.gz',
__DIR__ . '/../../../../system/tests/fixtures/update/drupal-8.local-actions-tasks-into-blocks-507488.php',
];
}
/**
* {@inheritdoc}
*/
public function setUp() {
parent::setUp();
/** @var \Drupal\Core\Extension\ThemeHandlerInterface $theme_handler */
$theme_handler = \Drupal::service('theme_handler');
$theme_handler->refreshInfo();
}
/**
* Tests that local actions/tasks are being converted into blocks.
*/
public function testUpdateHookN() {
$this->runUpdates();
/** @var \Drupal\block\BlockInterface $block_storage */
$block_storage = \Drupal::entityManager()->getStorage('block');
/* @var \Drupal\block\BlockInterface[] $help_blocks */
$help_blocks = $block_storage->loadByProperties(['theme' => 'bartik', 'region' => 'help']);
$this->assertRaw('Because your site has custom theme(s) installed, we had to set local actions and tasks blocks into the content region. Please manually review the block configurations and remove the removed variables from your templates.');
// Disable maintenance mode.
// @todo Can be removed once maintenance mode is automatically turned off
// after updates in https://www.drupal.org/node/2435135.
\Drupal::state()->set('system.maintenance_mode', FALSE);
// We finished updating so we can login the user now.
$this->drupalLogin($this->rootUser);
$page = Node::create([
'type' => 'page',
'title' => 'Page node',
]);
$page->save();
// Ensures that blocks inside help region has been moved to content region.
foreach ($help_blocks as $block) {
$new_block = $block_storage->load($block->id());
$this->assertEqual($new_block->getRegion(), 'content');
}
// Local tasks are visible on the node page.
$this->drupalGet('node/' . $page->id());
$this->assertText(t('Edit'));
// Local actions are visible on the content listing page.
$this->drupalGet('admin/content');
$action_link = $this->cssSelect('.action-links');
$this->assertTrue($action_link);
$this->drupalGet('admin/structure/block/list/seven');
/** @var \Drupal\Core\Config\StorageInterface $config_storage */
$config_storage = \Drupal::service('config.storage');
$this->assertTrue($config_storage->exists('block.block.test_theme_local_tasks'), 'Local task block has been created for the custom theme.');
$this->assertTrue($config_storage->exists('block.block.test_theme_local_actions'), 'Local action block has been created for the custom theme.');
}
}

View file

@ -0,0 +1,25 @@
<?php
/**
* @file
* Contains \Drupal\system\Tests\Update\MenuTreeSerializationTitleFilledTest.
*/
namespace Drupal\system\Tests\Update;
/**
* Runs MenuTreeSerializationTitleTest with a dump filled with content.
*
* @group Update
*/
class MenuTreeSerializationTitleFilledTest extends MenuTreeSerializationTitleTest {
/**
* {@inheritdoc}
*/
protected function setDatabaseDumpFiles() {
parent::setDatabaseDumpFiles();
$this->databaseDumpFiles[0] = __DIR__ . '/../../../tests/fixtures/update/drupal-8.filled.standard.php.gz';
}
}

View file

@ -19,11 +19,10 @@ class MenuTreeSerializationTitleTest extends UpdatePathTestBase {
/**
* {@inheritdoc}
*/
public function setUp() {
protected function setDatabaseDumpFiles() {
$this->databaseDumpFiles = [
__DIR__ . '/../../../tests/fixtures/update/drupal-8.bare.standard.php.gz',
];
parent::setUp();
}
/**

View file

@ -0,0 +1,24 @@
<?php
/**
* @file
* Contains \Drupal\system\Tests\Update\RouterIndexOptimizationFilledTest.
*/
namespace Drupal\system\Tests\Update;
/**
* Runs RouterIndexOptimizationTest with a dump filled with content.
*
* @group Update
*/
class RouterIndexOptimizationFilledTest extends RouterIndexOptimizationTest {
/**
* {@inheritdoc}
*/
protected function setDatabaseDumpFiles() {
parent::setDatabaseDumpFiles();
$this->databaseDumpFiles[0] = __DIR__ . '/../../../tests/fixtures/update/drupal-8.filled.standard.php.gz';
}
}

View file

@ -0,0 +1,40 @@
<?php
/**
* @file
* Contains \Drupal\system\Tests\Update\RouterIndexOptimizationTest.
*/
namespace Drupal\system\Tests\Update;
/**
* Tests system_update_8002().
*
* @group Update
*/
class RouterIndexOptimizationTest extends UpdatePathTestBase {
/**
* {@inheritdoc}
*/
protected function setDatabaseDumpFiles() {
$this->databaseDumpFiles = [
__DIR__ . '/../../../tests/fixtures/update/drupal-8.bare.standard.php.gz',
];
}
/**
* Ensures that the system_update_8002() runs as expected.
*/
public function testUpdate() {
$this->runUpdates();
$database = $this->container->get('database');
// Removed index.
$this->assertFalse($database->schema()->indexExists(
'router', 'pattern_outline_fit'
));
// Added index.
$this->assertTrue($database->schema()->indexExists(
'router', 'pattern_outline_parts'
));
}
}

View file

@ -8,17 +8,43 @@
namespace Drupal\system\Tests\Update;
use Drupal\Component\Utility\Crypt;
use Drupal\config\Tests\SchemaCheckTestTrait;
use Drupal\Core\Database\Database;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\Core\Language\Language;
use Drupal\Core\Url;
use Drupal\simpletest\WebTestBase;
use Drupal\user\Entity\User;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\HttpFoundation\Request;
/**
* Provides a base class that loads a database as a starting point.
* Provides a base class for writing an update test.
*
* To write an update test:
* - Write the hook_update_N() implementations that you are testing.
* - Create one or more database dump files, which will set the database to the
* "before updates" state. Normally, these will add some configuration data to
* the database, set up some tables/fields, etc.
* - Create a class that extends this class.
* - In your setUp() method, point the $this->databaseDumpFiles variable to the
* database dump files, and then call parent::setUp() to run the base setUp()
* method in this class.
* - In your test method, call $this->runUpdates() to run the necessary updates,
* and then use test assertions to verify that the result is what you expect.
* - In order to test both with a "bare" database dump as well as with a
* database dump filled with content, extend your update path test class with
* a new test class that overrides the bare database dump. Refer to
* UpdatePathTestBaseFilledTest for an example.
*
* @ingroup update_api
*
* @see hook_update_N()
*/
abstract class UpdatePathTestBase extends WebTestBase {
use SchemaCheckTestTrait;
/**
* Modules to enable after the database is loaded.
*/
@ -27,6 +53,14 @@ abstract class UpdatePathTestBase extends WebTestBase {
/**
* The file path(s) to the dumped database(s) to load into the child site.
*
* The file system/tests/fixtures/update/drupal-8.bare.standard.php.gz is
* normally included first -- this sets up the base database from a bare
* standard Drupal installation.
*
* The file system/tests/fixtures/update/drupal-8.filled.standard.php.gz
* can also be used in case we want to test with a database filled with
* content, and with all core modules enabled.
*
* @var array
*/
protected $databaseDumpFiles = [];
@ -39,14 +73,14 @@ abstract class UpdatePathTestBase extends WebTestBase {
protected $installProfile = 'standard';
/**
* Flag that indicates whether the child site has been upgraded.
* Flag that indicates whether the child site has been updated.
*
* @var bool
*/
protected $upgradedSite = FALSE;
/**
* Array of errors triggered during the upgrade process.
* Array of errors triggered during the update process.
*
* @var array
*/
@ -80,6 +114,15 @@ abstract class UpdatePathTestBase extends WebTestBase {
*/
protected $updateUrl;
/**
* Disable strict config schema checking.
*
* The schema is verified at the end of running the update.
*
* @var bool
*/
protected $strictConfigSchema = FALSE;
/**
* Constructs an UpdatePathTestCase object.
*
@ -90,13 +133,10 @@ abstract class UpdatePathTestBase extends WebTestBase {
function __construct($test_id = NULL) {
parent::__construct($test_id);
$this->zlibInstalled = function_exists('gzopen');
// Set the update url.
$this->updateUrl = Url::fromRoute('system.db_update');
}
/**
* Overrides WebTestBase::setUp() for upgrade testing.
* Overrides WebTestBase::setUp() for update testing.
*
* The main difference in this method is that rather than performing the
* installation via the installer, a database is loaded. Additional work is
@ -104,6 +144,10 @@ abstract class UpdatePathTestBase extends WebTestBase {
* container that would normally be done via the installer.
*/
protected function setUp() {
$this->runDbTasks();
// Allow classes to set database dump files.
$this->setDatabaseDumpFiles();
// We are going to set a missing zlib requirement property for usage
// during the performUpgrade() and tearDown() methods. Also set that the
// tests failed.
@ -112,6 +156,11 @@ abstract class UpdatePathTestBase extends WebTestBase {
return;
}
// Set the update url. This must be set here rather than in
// self::__construct() or the old URL generator will leak additional test
// sites.
$this->updateUrl = Url::fromRoute('system.db_update');
// These methods are called from parent::setUp().
$this->setBatch();
$this->initUserSession();
@ -133,24 +182,21 @@ abstract class UpdatePathTestBase extends WebTestBase {
// Add the config directories to settings.php.
drupal_install_config_directories();
// Install any additional modules.
$this->installModulesFromClassProperty($container);
// Restore the original Simpletest batch.
$this->restoreBatch();
// Rebuild and reset.
$this->rebuildAll();
// Set the container. parent::rebuildAll() would normally do this, but this
// not safe to do here, because the database has not been updated yet.
$this->container = \Drupal::getContainer();
// Replace User 1 with the user created here.
/** @var \Drupal\user\UserInterface $account */
$account = User::load(1);
$account->setPassword($this->rootUser->pass_raw);
$account->setEmail($this->rootUser->getEmail());
$account->setUsername($this->rootUser->getUsername());
$account->save();
$this->replaceUser1();
}
/**
* Set database dump files to be used.
*/
abstract protected function setDatabaseDumpFiles();
/**
* Add settings that are missed since the installer isn't run.
*/
@ -182,7 +228,7 @@ abstract class UpdatePathTestBase extends WebTestBase {
*/
protected function runUpdates() {
if (!$this->zlibInstalled) {
$this->fail('Missing zlib requirement for upgrade tests.');
$this->fail('Missing zlib requirement for update tests.');
return FALSE;
}
// The site might be broken at the time so logging in using the UI might
@ -195,25 +241,76 @@ abstract class UpdatePathTestBase extends WebTestBase {
$this->drupalGet($this->updateUrl);
$this->clickLink(t('Continue'));
$this->doSelectionTest();
// Run the update hooks.
$this->clickLink(t('Apply pending updates'));
// Ensure there are no failed updates.
$this->assertNoRaw('<strong>' . t('Failed:') . '</strong>');
// The config schema can be incorrect while the update functions are being
// executed. But once the update has been completed, it needs to be valid
// again. Assert the schema of all configuration objects now.
$names = $this->container->get('config.storage')->listAll();
/** @var \Drupal\Core\Config\TypedConfigManagerInterface $typed_config */
$typed_config = $this->container->get('config.typed');
foreach ($names as $name) {
$config = $this->config($name);
$this->assertConfigSchema($typed_config, $name, $config->get());
}
// Ensure that the update hooks updated all entity schema.
$this->assertFalse(\Drupal::service('entity.definition_update_manager')->needsUpdates(), 'After all updates ran, entity schema is up to date.');
}
/**
* {@inheritdoc}
* Runs the install database tasks for the driver used by the test runner.
*/
protected function rebuildAll() {
parent::rebuildAll();
protected function runDbTasks() {
// Create a minimal container so that t() works.
// @see install_begin_request()
$container = new ContainerBuilder();
$container->setParameter('language.default_values', Language::$defaultValues);
$container
->register('language.default', 'Drupal\Core\Language\LanguageDefault')
->addArgument('%language.default_values%');
$container
->register('language_manager', 'Drupal\Core\Language\LanguageManager')
->addArgument(new Reference('language.default'));
$container
->register('string_translation', 'Drupal\Core\StringTranslation\TranslationManager')
->addArgument(new Reference('language_manager'));
\Drupal::setContainer($container);
// Remove the notices we get due to the menu link rebuild prior to running
// the system updates for the schema change.
foreach ($this->assertions as $key => $assertion) {
if ($assertion['message_group'] == 'Notice' && basename($assertion['file']) == 'MenuTreeStorage.php' && strpos($assertion['message'], 'unserialize(): Error at offset 0') !== FALSE) {
unset($this->assertions[$key]);
$this->deleteAssert($assertion['message_id']);
$this->results['#exception']--;
}
require_once __DIR__ . '/../../../../../includes/install.inc';
$connection = Database::getConnection();
$errors = db_installer_object($connection->driver())->runTasks();
if (!empty($errors)) {
$this->fail('Failed to run installer database tasks: ' . implode(', ', $errors));
}
}
/**
* Replace User 1 with the user created here.
*/
protected function replaceUser1() {
/** @var \Drupal\user\UserInterface $account */
// @todo: Saving the account before the update is problematic.
// https://www.drupal.org/node/2560237
$account = User::load(1);
$account->setPassword($this->rootUser->pass_raw);
$account->setEmail($this->rootUser->getEmail());
$account->setUsername($this->rootUser->getUsername());
$account->save();
}
/**
* Tests the selection page.
*/
protected function doSelectionTest() {
// No-op. Tests wishing to do test the selection page or the general
// update.php environment before running update.php can override this method
// and implement their required tests.
}
}

View file

@ -0,0 +1,427 @@
<?php
/**
* @file
* Contains \Drupal\system\Tests\Update\UpdatePathTestBaseFilledTest.
*/
namespace Drupal\system\Tests\Update;
use Drupal\node\Entity\Node;
use Drupal\user\Entity\User;
/**
* Runs UpdatePathTestBaseTest with a dump filled with content.
*
* @group Update
*/
class UpdatePathTestBaseFilledTest extends UpdatePathTestBaseTest {
/**
* {@inheritdoc}
*/
protected function setDatabaseDumpFiles() {
parent::setDatabaseDumpFiles();
$this->databaseDumpFiles[0] = __DIR__ . '/../../../tests/fixtures/update/drupal-8.filled.standard.php.gz';
}
/**
* Tests that the content and configuration were properly updated.
*/
public function testUpdatedSite() {
$this->runUpdates();
$spanish = \Drupal::languageManager()->getLanguage('es');
$expected_node_data = array(
[1, 'article', 'en', 'Test Article - New title'],
[2, 'book', 'en', 'Book page'],
[3, 'forum', 'en', 'Forum topic'],
[4, 'page', 'en', 'Test page'],
[8, 'test_content_type', 'en', 'Test title'],
);
foreach ($expected_node_data as $node_data) {
$id = $node_data[0];
$type = $node_data[1];
$langcode = $node_data[2];
$title = $node_data[3];
// Make sure our English nodes still exist.
$node = Node::load($id);
$this->assertEqual($node->language()->getId(), $langcode);
$this->assertEqual($node->getType(), $type);
$this->assertEqual($node->getTitle(), $title);
// Assert that nodes are all published.
$this->assertTrue($node->isPublished());
$this->drupalGet('node/' . $id);
$this->assertText($title);
}
// Make sure the translated node still exists.
$translation = Node::load(8)->getTranslation('es');
$this->assertEqual('Test title Spanish', $translation->getTitle());
// Make sure our alias still works.
$this->drupalGet('test-article');
$this->assertText('Test Article - New title');
$this->assertText('Body');
$this->assertText('Tags');
// Make sure a translated page exists.
$this->drupalGet('node/8', ['language' => $spanish]);
// Check for text of two comments.
$this->assertText('Hola');
$this->assertText('Hello');
// The user entity reference field is access restricted.
$this->assertNoText('Test 12');
// Make sure all other field labels are there.
for ($i = 1; $i <= 23; $i++) {
if ($i != 12) {
$this->assertText('Test ' . $i);
}
}
// Make sure the translated slogan appears.
$this->assertText('drupal Spanish');
// Make sure the custom block appears.
$this->drupalGet('<front>');
// Block title.
$this->assertText('Another block');
// Block body.
$this->assertText('Hello');
// Log in as user 1.
$account = User::load(1);
$account->pass_raw = 'drupal';
$this->drupalLogin($account);
// Make sure we can see the access-restricted entity reference field
// now that we're logged in.
$this->drupalGet('node/8', ['language' => $spanish]);
$this->assertText('Test 12');
$this->assertLink('drupal');
// Make sure the content for node 8 is still in the edit form.
$this->drupalGet('node/8/edit');
$this->assertText('Test title');
$this->assertText('Test body');
$this->assertFieldChecked('edit-field-test-1-value');
$this->assertRaw('2015-08-16');
$this->assertRaw('test@example.com');
$this->assertRaw('drupal.org');
$this->assertText('0.1');
$this->assertText('0.2');
$this->assertRaw('+31612345678');
$this->assertRaw('+31612345679');
$this->assertText('Test Article - New title');
$this->assertText('test.txt');
$this->assertText('druplicon.small');
$this->assertRaw('General discussion');
$this->assertText('Test Article - New title');
$this->assertText('Test 1');
$this->assertRaw('0.01');
$this->drupalPostForm('node/8/edit', [], 'Save and keep published (this translation)');
$this->assertResponse(200);
$this->drupalGet('node/8/edit', ['language' => $spanish]);
$this->assertText('Test title Spanish');
$this->assertText('Test body Spanish');
// Make sure the user page is correct.
$this->drupalGet('user/3');
$this->assertText('usuario_test');
$this->assertRaw('druplicon.small');
$this->assertText('Test file field');
$this->assertLink('test.txt');
// Make sure the user is translated.
$this->drupalGet('user/3/translations');
$this->assertNoText('Not translated');
// Make sure the custom field on the user is still there.
$this->drupalGet('admin/config/people/accounts/fields');
$this->assertText('Test file field');
// Make sure the test view still exists.
$this->drupalGet('admin/structure/views/view/test_view');
$this->assertText('Test view');
// Make sure the book node exists.
$this->drupalGet('admin/structure/book');
$this->clickLink('Test Article - New title');
$this->assertText('Body');
$this->assertText('Tags');
$this->assertRaw('Text format');
// Make sure that users still exist.
$this->drupalGet('admin/people');
$this->assertText('usuario_test');
$this->assertText('drupal');
$this->drupalGet('user/1/edit');
$this->assertRaw('drupal@example.com');
// Make sure the content view works.
$this->drupalGet('admin/content');
$this->assertText('Test title');
// Make sure our custom blocks show up.
$this->drupalGet('admin/structure/block');
$this->assertText('Another block');
$this->assertText('Test block');
$this->drupalGet('admin/structure/block/block-content');
$this->assertText('Another block');
$this->assertText('Test block');
// Make sure our custom visibility conditions are correct.
$this->drupalGet('admin/structure/block/manage/testblock');
$this->assertNoFieldChecked('edit-visibility-language-langcodes-es');
$this->assertFieldChecked('edit-visibility-language-langcodes-en');
$this->assertNoFieldChecked('edit-visibility-node-type-bundles-book');
$this->assertFieldChecked('edit-visibility-node-type-bundles-test-content-type');
// Make sure our block is still translated.
$this->drupalGet('admin/structure/block/manage/testblock/translate/es/edit');
$this->assertRaw('Test block spanish');
// Make sure our custom text format exists.
$this->drupalGet('admin/config/content/formats');
$this->assertText('Test text format');
$this->drupalGet('admin/config/content/formats/manage/test_text_format');
$this->assertResponse('200');
// Make sure our feed still exists.
$this->drupalGet('admin/config/services/aggregator');
$this->assertText('Test feed');
$this->drupalGet('admin/config/services/aggregator/fields');
$this->assertText('field_test');
// Make sure our view appears in the overview.
$this->drupalGet('admin/structure/views');
$this->assertText('test_view');
$this->assertText('Test view');
// Make sure our custom forum exists.
$this->drupalGet('admin/structure/forum');
$this->assertText('Test forum');
// Make sure our custom menu exists.
$this->drupalGet('admin/structure/menu');
$this->assertText('Test menu');
// Make sure our custom menu exists.
$this->drupalGet('admin/structure/menu/manage/test-menu');
$this->clickLink('Admin');
// Make sure the translation for the menu is still correct.
$this->drupalGet('admin/structure/menu/manage/test-menu/translate/es/edit');
$this->assertRaw('Menu test');
// Make sure our custom menu link exists.
$this->drupalGet('admin/structure/menu/item/1/edit');
$this->assertFieldChecked('edit-enabled-value');
// Make sure our comment type exists.
$this->drupalGet('admin/structure/comment');
$this->assertText('Test comment type');
$this->drupalGet('admin/structure/comment/manage/test_comment_type/fields');
$this->assertText('comment_body');
// Make sure our contact form exists.
$this->drupalGet('admin/structure/contact');
$this->assertText('Test contact form');
$this->drupalGet('admin/structure/types');
$this->assertText('Test content type description');
$this->drupalGet('admin/structure/types/manage/test_content_type/fields');
// Make sure fields are the right type.
$this->assertLink('Text (formatted, long, with summary)');
$this->assertLink('Boolean');
$this->assertLink('Comments');
$this->assertLink('Date');
$this->assertLink('Email');
$this->assertLink('Link');
$this->assertLink('List (float)');
$this->assertLink('Telephone number');
$this->assertLink('Entity reference');
$this->assertLink('File');
$this->assertLink('Image');
$this->assertLink('Text (plain, long)');
$this->assertLink('List (text)');
$this->assertLink('Text (formatted, long)');
$this->assertLink('Text (plain)');
$this->assertLink('List (integer)');
$this->assertLink('Number (integer)');
$this->assertLink('Number (float)');
// Make sure our form mode exists.
$this->drupalGet('admin/structure/display-modes/form');
$this->assertText('New form mode');
// Make sure our view mode exists.
$this->drupalGet('admin/structure/display-modes/view');
$this->assertText('New view mode');
$this->drupalGet('admin/structure/display-modes/view/manage/node.new_view_mode');
$this->assertResponse(200);
// Make sure our other language is still there.
$this->drupalGet('admin/config/regional/language');
$this->assertText('Spanish');
// Make sure our custom date format exists.
$this->drupalGet('admin/config/regional/date-time');
$this->assertText('Test date format');
$this->drupalGet('admin/config/regional/date-time/formats/manage/test_date_format');
$this->assertOptionSelected('edit-langcode', 'es');
// Make sure our custom image style exists.
$this->drupalGet('admin/config/media/image-styles/manage/test_image_style');
$this->assertText('Test image style');
$this->assertText('Desaturate');
$this->assertText('Convert PNG');
// Make sure our custom responsive image style exists.
$this->drupalGet('admin/config/media/responsive-image-style/test');
$this->assertResponse(200);
$this->assertText('Test');
// Make sure our custom shortcut exists.
$this->drupalGet('admin/config/user-interface/shortcut');
$this->assertText('Test shortcut');
$this->drupalGet('admin/config/user-interface/shortcut/manage/test/customize');
$this->assertText('All content');
// Make sure our language detection settings are still correct.
$this->drupalGet('admin/config/regional/language/detection');
$this->assertFieldChecked('edit-language-interface-enabled-language-user-admin');
$this->assertFieldChecked('edit-language-interface-enabled-language-url');
$this->assertFieldChecked('edit-language-interface-enabled-language-session');
$this->assertFieldChecked('edit-language-interface-enabled-language-user');
$this->assertFieldChecked('edit-language-interface-enabled-language-browser');
// Make sure strings are still translated.
$this->drupalGet('admin/structure/views/view/content/translate/es/edit');
$this->assertText('Contenido');
$this->drupalPostForm('admin/config/regional/translate', ['string' => 'Full comment'], 'Filter');
$this->assertText('Comentario completo');
// Make sure our custom action is still there.
$this->drupalGet('admin/config/system/actions');
$this->assertText('Test action');
$this->drupalGet('admin/config/system/actions/configure/test_action');
$this->assertText('test_action');
$this->assertRaw('drupal.org');
// Make sure our ban still exists.
$this->drupalGet('admin/config/people/ban');
$this->assertText('8.8.8.8');
// Make sure our vocabulary exists.
$this->drupalGet('admin/structure/taxonomy/manage/test_vocabulary/overview');
// Make sure our terms exist.
$this->assertText('Test root term');
$this->assertText('Test child term');
$this->drupalGet('taxonomy/term/3');
$this->assertResponse('200');
// Make sure the terms are still translated.
$this->drupalGet('taxonomy/term/2/translations');
$this->assertLink('Test root term - Spanish');
// Make sure our contact form exists.
$this->drupalGet('admin/structure/contact');
$this->assertText('Test contact form');
$this->drupalGet('admin/structure/contact/manage/test_contact_form');
$this->assertText('test@example.com');
$this->assertText('Hello');
$this->drupalGet('admin/structure/contact/manage/test_contact_form/translate/es/edit');
$this->assertText('Hola');
$this->assertRaw('Test contact form Spanish');
// Make sure our modules are still enabled.
$expected_enabled_modules = [
'action',
'aggregator',
'ban',
'basic_auth',
'block',
'block_content',
'book',
'breakpoint',
'ckeditor',
'color',
'comment',
'config',
'config_translation',
'contact',
'content_translation',
'contextual',
'datetime',
'dblog',
'editor',
'entity_reference',
'field',
'field_ui',
'file',
'filter',
'hal',
'help',
'history',
'image',
'language',
'link',
'locale',
'menu_ui',
'migrate',
'migrate_drupal',
'node',
'options',
'page_cache',
'path',
'quickedit',
'rdf',
'responsive_image',
'rest',
'search',
'serialization',
'shortcut',
'simpletest',
'statistics',
'syslog',
'system',
'taxonomy',
'telephone',
'text',
'toolbar',
'tour',
'tracker',
'update',
'user',
'views_ui',
'forum',
'menu_link_content',
'views',
'standard',
];
foreach ($expected_enabled_modules as $module) {
$this->assertTrue($this->container->get('module_handler')->moduleExists($module), 'The "' . $module . '" module is still enabled.');
}
// Make sure our themes are still enabled.
$expected_enabled_themes = [
'bartik',
'classy',
'seven',
'stark',
];
foreach ($expected_enabled_themes as $theme) {
$this->assertTrue($this->container->get('theme_handler')->themeExists($theme), 'The "' . $theme . '" is still enabled.');
}
}
/**
* {@inheritdoc}
*/
protected function replaceUser1() {
// Do not replace the user from our dump.
}
}

View file

@ -24,9 +24,11 @@ class UpdatePathTestBaseTest extends UpdatePathTestBase {
/**
* {@inheritdoc}
*/
protected function setUp() {
$this->databaseDumpFiles = [__DIR__ . '/../../../tests/fixtures/update/drupal-8.bare.standard.php.gz'];
parent::setUp();
protected function setDatabaseDumpFiles() {
$this->databaseDumpFiles = [
__DIR__ . '/../../../tests/fixtures/update/drupal-8.bare.standard.php.gz',
__DIR__ . '/../../../tests/fixtures/update/drupal-8.update-test-schema-enabled.php',
];
}
/**
@ -36,6 +38,9 @@ class UpdatePathTestBaseTest extends UpdatePathTestBase {
foreach (['user', 'node', 'system', 'update_test_schema'] as $module) {
$this->assertEqual(drupal_get_installed_schema_version($module), 8000, SafeMarkup::format('Module @module schema is 8000', ['@module' => $module]));
}
// Before accessing the site we need to run updates first or the site might
// be broken.
$this->runUpdates();
$this->assertEqual(\Drupal::config('system.site')->get('name'), 'Site-Install');
$this->drupalGet('<front>');
$this->assertText('Site-Install');

View file

@ -0,0 +1,58 @@
<?php
/**
* @file
* Contains \Drupal\system\Tests\Update\UpdatePathTestJavaScriptTest.php
*/
namespace Drupal\system\Tests\Update;
/**
* Tests the presence of JavaScript at update.php.
*
* @group Update
*/
class UpdatePathTestJavaScriptTest extends UpdatePathTestBase {
/**
* {@inheritdoc}
*/
protected function setDatabaseDumpFiles() {
$this->databaseDumpFiles = [
__DIR__ . '/../../../tests/fixtures/update/drupal-8.bare.standard.php.gz',
];
}
/**
* Test JavaScript loading at update.php.
*
* @see ::doPreUpdateTests
*/
public function testJavaScriptLoading() {
$this->runUpdates();
}
/**
* {@inheritdoc}
*/
protected function doSelectionTest() {
// Ensure that at least one JS script has drupalSettings in there.
$scripts = $this->xpath('//script');
$found = FALSE;
foreach ($scripts as $script) {
if (!isset($script['src'])) {
continue;
}
$src = (string) $script['src'];
$file_content = file_get_contents($src);
if (strpos($file_content, 'window.drupalSettings =') !== FALSE) {
$found = TRUE;
break;
}
}
$this->assertTrue($found, 'Ensure that the drupalSettingsLoader.js was included in the JS files');
}
}

View file

@ -0,0 +1,25 @@
<?php
/**
* @file
* Contains \Drupal\system\Tests\Update\UpdatePathWithBrokenRoutingFilledTest.
*/
namespace Drupal\system\Tests\Update;
/**
* Runs UpdatePathWithBrokenRoutingTest with a dump filled with content.
*
* @group Update
*/
class UpdatePathWithBrokenRoutingFilledTest extends UpdatePathWithBrokenRoutingTest {
/**
* {@inheritdoc}
*/
protected function setDatabaseDumpFiles() {
parent::setDatabaseDumpFiles();
$this->databaseDumpFiles[0] = __DIR__ . '/../../../tests/fixtures/update/drupal-8.filled.standard.php.gz';
}
}

View file

@ -0,0 +1,57 @@
<?php
/**
* @file
* Contains \Drupal\system\Tests\Update\UpdatePathWithBrokenRoutingTest.
*/
namespace Drupal\system\Tests\Update;
/**
* Tests the update path with a broken router.
*
* @group Update
*/
class UpdatePathWithBrokenRoutingTest extends UpdatePathTestBase {
/**
* {@inheritdoc}
*/
protected function setDatabaseDumpFiles() {
$this->databaseDumpFiles = [
__DIR__ . '/../../../tests/fixtures/update/drupal-8.bare.standard.php.gz',
__DIR__ . '/../../../tests/fixtures/update/drupal-8.broken_routing.php',
];
}
/**
* Tests running update.php with some form of broken routing.
*/
public function testWithBrokenRouting() {
// Simulate a broken router, and make sure the front page is
// inaccessible.
\Drupal::state()->set('update_script_test_broken_inbound', TRUE);
\Drupal::service('cache_tags.invalidator')->invalidateTags(['route_match', 'rendered']);
$this->drupalGet('<front>');
$this->assertResponse(500);
// The exceptions are expected. Do not interpret them as a test failure.
// Not using File API; a potential error must trigger a PHP warning.
unlink(\Drupal::root() . '/' . $this->siteDirectory . '/error.log');
foreach ($this->assertions as $key => $assertion) {
if (strpos($assertion['message'], 'core/modules/system/tests/modules/update_script_test/src/PathProcessor/BrokenInboundPathProcessor.php') !== FALSE) {
unset($this->assertions[$key]);
$this->deleteAssert($assertion['message_id']);
}
}
$this->runUpdates();
// Remove the simulation of the broken router, and make sure we can get to
// the front page again.
\Drupal::state()->set('update_script_test_broken_inbound', FALSE);
$this->drupalGet('<front>');
$this->assertResponse(200);
}
}

View file

@ -153,7 +153,7 @@ class UpdateScriptTest extends WebTestBase {
$this->clickLink(t('Continue'));
$this->assertText(t('No pending updates.'));
$this->assertNoLink('Administration pages');
$this->assertNoLinkByHref('update.php', 0);
$this->assertNoLinkByHrefInMainRegion('update.php', 0);
$this->clickLink('Front page');
$this->assertResponse(200);
@ -164,7 +164,7 @@ class UpdateScriptTest extends WebTestBase {
$this->clickLink(t('Continue'));
$this->assertText(t('No pending updates.'));
$this->assertLink('Administration pages');
$this->assertNoLinkByHref('update.php', 1);
$this->assertNoLinkByHrefInMainRegion('update.php', 1);
$this->clickLink('Administration pages');
$this->assertResponse(200);
}
@ -173,37 +173,11 @@ class UpdateScriptTest extends WebTestBase {
* Tests update.php after performing a successful update.
*/
function testSuccessfulUpdateFunctionality() {
$schema_version = drupal_get_installed_schema_version('update_script_test');
$this->assertEqual($schema_version, 8001, 'update_script_test is initially installed with schema version 8001.');
// Set the installed schema version to one less than the current update.
drupal_set_installed_schema_version('update_script_test', $schema_version - 1);
$schema_version = drupal_get_installed_schema_version('update_script_test', TRUE);
$this->assertEqual($schema_version, 8000, 'update_script_test schema version overridden to 8000.');
// Click through update.php with 'administer software updates' permission.
$this->drupalLogin($this->updateUser);
$this->drupalGet($this->updateUrl, array('external' => TRUE));
$this->clickLink(t('Continue'));
$this->clickLink(t('Apply pending updates'));
// Verify that updates were completed successfully.
$this->assertText('Updates were attempted.');
$this->assertLink('site');
$this->assertText('The update_script_test_update_8001() update was executed successfully.');
// Verify that no 7.x updates were run.
$this->assertNoText('The update_script_test_update_7200() update was executed successfully.');
$this->assertNoText('The update_script_test_update_7201() update was executed successfully.');
// Verify that there are no links to different parts of the workflow.
$this->assertNoLink('Administration pages');
$this->assertNoLinkByHref('update.php', 0);
$this->assertNoLink('logged');
// Verify the front page can be visited following the upgrade.
$this->clickLink('Front page');
$this->assertResponse(200);
$initial_maintenance_mode = $this->container->get('state')->get('system.maintenance_mode');
$this->assertFalse($initial_maintenance_mode, 'Site is not in maintenance mode.');
$this->updateScriptTest($initial_maintenance_mode);
$final_maintenance_mode = $this->container->get('state')->get('system.maintenance_mode');
$this->assertEqual($final_maintenance_mode, $initial_maintenance_mode, 'Maintenance mode should not have changed after database updates.');
// Reset the static cache to ensure we have the most current setting.
$schema_version = drupal_get_installed_schema_version('update_script_test', TRUE);
@ -224,11 +198,69 @@ class UpdateScriptTest extends WebTestBase {
$this->assertText('Updates were attempted.');
$this->assertLink('logged');
$this->assertLink('Administration pages');
$this->assertNoLinkByHref('update.php', 1);
$this->assertNoLinkByHrefInMainRegion('update.php', 1);
$this->clickLink('Administration pages');
$this->assertResponse(200);
}
/**
* Tests update.php while in maintenance mode.
*/
function testMaintenanceModeUpdateFunctionality() {
$this->container->get('state')
->set('system.maintenance_mode', TRUE);
$initial_maintenance_mode = $this->container->get('state')
->get('system.maintenance_mode');
$this->assertTrue($initial_maintenance_mode, 'Site is in maintenance mode.');
$this->updateScriptTest($initial_maintenance_mode);
$final_maintenance_mode = $this->container->get('state')
->get('system.maintenance_mode');
$this->assertEqual($final_maintenance_mode, $initial_maintenance_mode, 'Maintenance mode should not have changed after database updates.');
}
/**
* Helper function to run updates via the browser.
*/
protected function updateScriptTest($maintenance_mode) {
$schema_version = drupal_get_installed_schema_version('update_script_test');
$this->assertEqual($schema_version, 8001, 'update_script_test is initially installed with schema version 8001.');
// Set the installed schema version to one less than the current update.
drupal_set_installed_schema_version('update_script_test', $schema_version - 1);
$schema_version = drupal_get_installed_schema_version('update_script_test', TRUE);
$this->assertEqual($schema_version, 8000, 'update_script_test schema version overridden to 8000.');
// Click through update.php with 'administer software updates' permission.
$this->drupalLogin($this->updateUser);
if ($maintenance_mode) {
$this->assertText('Operating in maintenance mode.');
}
else {
$this->assertNoText('Operating in maintenance mode.');
}
$this->drupalGet($this->updateUrl, array('external' => TRUE));
$this->clickLink(t('Continue'));
$this->clickLink(t('Apply pending updates'));
// Verify that updates were completed successfully.
$this->assertText('Updates were attempted.');
$this->assertLink('site');
$this->assertText('The update_script_test_update_8001() update was executed successfully.');
// Verify that no 7.x updates were run.
$this->assertNoText('The update_script_test_update_7200() update was executed successfully.');
$this->assertNoText('The update_script_test_update_7201() update was executed successfully.');
// Verify that there are no links to different parts of the workflow.
$this->assertNoLink('Administration pages');
$this->assertNoLinkByHrefInMainRegion('update.php', 0);
$this->assertNoLink('logged');
// Verify the front page can be visited following the upgrade.
$this->clickLink('Front page');
$this->assertResponse(200);
}
/**
* Returns the Drupal 7 system table schema.
*/