From 0b8ffb429316ddf1ee399155dbeff883b3ff1b29 Mon Sep 17 00:00:00 2001 From: Alex Pott <alex.a.pott@googlemail.com> Date: Thu, 5 Jul 2018 16:30:05 +0100 Subject: [PATCH] Issue #2870443 by Tessa Bakker, Lendude, michielnugter, dawehner: Convert web tests to browser tests for editor module --- .../src/Functional}/EditorAdminTest.php | 27 +++++----- .../src/Functional}/EditorLoadingTest.php | 6 +-- .../src/Functional}/EditorSecurityTest.php | 51 ++++++++++++++---- .../EditorUploadImageScaleTest.php | 24 +++++---- .../QuickEditIntegrationLoadingTest.php | 54 +++++++++++++++---- 5 files changed, 116 insertions(+), 46 deletions(-) rename core/modules/editor/{src/Tests => tests/src/Functional}/EditorAdminTest.php (85%) rename core/modules/editor/{src/Tests => tests/src/Functional}/EditorLoadingTest.php (98%) rename core/modules/editor/{src/Tests => tests/src/Functional}/EditorSecurityTest.php (88%) rename core/modules/editor/{src/Tests => tests/src/Functional}/EditorUploadImageScaleTest.php (84%) rename core/modules/editor/{src/Tests => tests/src/Functional}/QuickEditIntegrationLoadingTest.php (69%) diff --git a/core/modules/editor/src/Tests/EditorAdminTest.php b/core/modules/editor/tests/src/Functional/EditorAdminTest.php similarity index 85% rename from core/modules/editor/src/Tests/EditorAdminTest.php rename to core/modules/editor/tests/src/Functional/EditorAdminTest.php index 8e993f3000c5..3c0ab450824d 100644 --- a/core/modules/editor/src/Tests/EditorAdminTest.php +++ b/core/modules/editor/tests/src/Functional/EditorAdminTest.php @@ -1,18 +1,19 @@ <?php -namespace Drupal\editor\Tests; +namespace Drupal\Tests\editor\Functional; +use Drupal\Component\Render\FormattableMarkup; use Drupal\filter\Entity\FilterFormat; use Drupal\node\Entity\Node; use Drupal\node\Entity\NodeType; -use Drupal\simpletest\WebTestBase; +use Drupal\Tests\BrowserTestBase; /** * Tests administration of text editors. * * @group editor */ -class EditorAdminTest extends WebTestBase { +class EditorAdminTest extends BrowserTestBase { /** * Modules to enable. @@ -64,8 +65,8 @@ public function testNoEditorAvailable() { $this->assertTrue(count($select) === 1, 'The Text Editor select exists.'); $this->assertTrue(count($select_is_disabled) === 1, 'The Text Editor select is disabled.'); $this->assertTrue(count($options) === 1, 'The Text Editor select has only one option.'); - $this->assertTrue(((string) $options[0]) === 'None', 'Option 1 in the Text Editor select is "None".'); - $this->assertRaw(t('This option is disabled because no modules that provide a text editor are currently enabled.'), 'Description for select present that tells users to install a text editor module.'); + $this->assertTrue(($options[0]->getText()) === 'None', 'Option 1 in the Text Editor select is "None".'); + $this->assertRaw('This option is disabled because no modules that provide a text editor are currently enabled.', 'Description for select present that tells users to install a text editor module.'); } /** @@ -85,7 +86,7 @@ public function testAddEditorToExistingFormat() { $edit = [ 'editor[editor]' => '', ]; - $this->drupalPostAjaxForm(NULL, $edit, 'editor_configure'); + $this->drupalPostForm(NULL, $edit, 'Configure'); $unicorn_setting = $this->xpath('//input[@name="editor[settings][ponies_too]" and @type="checkbox" and @checked]'); $this->assertTrue(count($unicorn_setting) === 0, "Unicorn Editor's settings form is no longer present."); } @@ -134,7 +135,7 @@ public function testDisableFormatWithEditor() { $this->drupalLogin($account); // The node edit page header. - $text = t('<em>Edit @type</em> @title', ['@type' => $node_type->label(), '@title' => $node->label()]); + $text = (string) new FormattableMarkup('<em>Edit @type</em> @title', ['@type' => $node_type->label(), '@title' => $node->label()]); // Go to node edit form. $this->drupalGet('node/' . $node->id() . '/edit'); @@ -192,17 +193,17 @@ protected function selectUnicornEditor() { $this->assertTrue(count($select) === 1, 'The Text Editor select exists.'); $this->assertTrue(count($select_is_disabled) === 0, 'The Text Editor select is not disabled.'); $this->assertTrue(count($options) === 2, 'The Text Editor select has two options.'); - $this->assertTrue(((string) $options[0]) === 'None', 'Option 1 in the Text Editor select is "None".'); - $this->assertTrue(((string) $options[1]) === 'Unicorn Editor', 'Option 2 in the Text Editor select is "Unicorn Editor".'); - $this->assertTrue(((string) $options[0]['selected']) === 'selected', 'Option 1 ("None") is selected.'); + $this->assertTrue(($options[0]->getText()) === 'None', 'Option 1 in the Text Editor select is "None".'); + $this->assertTrue(($options[1]->getText()) === 'Unicorn Editor', 'Option 2 in the Text Editor select is "Unicorn Editor".'); + $this->assertTrue($options[0]->hasAttribute('selected'), 'Option 1 ("None") is selected.'); // Ensure the none option is selected. - $this->assertNoRaw(t('This option is disabled because no modules that provide a text editor are currently enabled.'), 'Description for select absent that tells users to install a text editor module.'); + $this->assertNoRaw('This option is disabled because no modules that provide a text editor are currently enabled.', 'Description for select absent that tells users to install a text editor module.'); // Select the "Unicorn Editor" editor and click the "Configure" button. $edit = [ 'editor[editor]' => 'unicorn', ]; - $this->drupalPostAjaxForm(NULL, $edit, 'editor_configure'); + $this->drupalPostForm(NULL, $edit, 'Configure'); $unicorn_setting = $this->xpath('//input[@name="editor[settings][ponies_too]" and @type="checkbox" and @checked]'); $this->assertTrue(count($unicorn_setting), "Unicorn Editor's settings form is present."); @@ -229,7 +230,7 @@ protected function verifyUnicornEditorConfiguration($format_id, $ponies_too = TR $this->assertTrue(count($select) === 1, 'The Text Editor select exists.'); $this->assertTrue(count($select_is_disabled) === 0, 'The Text Editor select is not disabled.'); $this->assertTrue(count($options) === 2, 'The Text Editor select has two options.'); - $this->assertTrue(((string) $options[1]['selected']) === 'selected', 'Option 2 ("Unicorn Editor") is selected.'); + $this->assertTrue($options[1]->hasAttribute('selected'), 'Option 2 ("Unicorn Editor") is selected.'); } } diff --git a/core/modules/editor/src/Tests/EditorLoadingTest.php b/core/modules/editor/tests/src/Functional/EditorLoadingTest.php similarity index 98% rename from core/modules/editor/src/Tests/EditorLoadingTest.php rename to core/modules/editor/tests/src/Functional/EditorLoadingTest.php index dca641ae0cd3..d0242140a3f6 100644 --- a/core/modules/editor/src/Tests/EditorLoadingTest.php +++ b/core/modules/editor/tests/src/Functional/EditorLoadingTest.php @@ -1,19 +1,19 @@ <?php -namespace Drupal\editor\Tests; +namespace Drupal\Tests\editor\Functional; use Drupal\editor\Entity\Editor; use Drupal\field\Entity\FieldConfig; use Drupal\field\Entity\FieldStorageConfig; -use Drupal\simpletest\WebTestBase; use Drupal\filter\Entity\FilterFormat; +use Drupal\Tests\BrowserTestBase; /** * Tests loading of text editors. * * @group editor */ -class EditorLoadingTest extends WebTestBase { +class EditorLoadingTest extends BrowserTestBase { /** * Modules to enable. diff --git a/core/modules/editor/src/Tests/EditorSecurityTest.php b/core/modules/editor/tests/src/Functional/EditorSecurityTest.php similarity index 88% rename from core/modules/editor/src/Tests/EditorSecurityTest.php rename to core/modules/editor/tests/src/Functional/EditorSecurityTest.php index a5ea925af6e4..904f8f0ffe62 100644 --- a/core/modules/editor/src/Tests/EditorSecurityTest.php +++ b/core/modules/editor/tests/src/Functional/EditorSecurityTest.php @@ -1,18 +1,19 @@ <?php -namespace Drupal\editor\Tests; +namespace Drupal\Tests\editor\Functional; use Drupal\Component\Serialization\Json; use Drupal\editor\Entity\Editor; -use Drupal\simpletest\WebTestBase; use Drupal\filter\Entity\FilterFormat; +use Drupal\Tests\BrowserTestBase; +use GuzzleHttp\Cookie\CookieJar; /** * Tests XSS protection for content creators when using text editors. * * @group editor */ -class EditorSecurityTest extends WebTestBase { +class EditorSecurityTest extends BrowserTestBase { /** * The sample content to use in all tests. @@ -283,7 +284,7 @@ public function testInitialSecurity() { $this->drupalLogin($account); $this->drupalGet('node/' . $case['node_id'] . '/edit'); $dom_node = $this->xpath('//textarea[@id="edit-body-0-value"]'); - $this->assertIdentical($case['value'], (string) $dom_node[0], 'The value was correctly filtered for XSS attack vectors.'); + $this->assertIdentical($case['value'], $dom_node[0]->getText(), 'The value was correctly filtered for XSS attack vectors.'); } } } @@ -389,13 +390,15 @@ public function testSwitchingSecurity() { // - switch to every other text format/editor // - assert the XSS-filtered values that we get from the server $this->drupalLogin($this->privilegedUser); + $cookies = $this->getCookies(); + foreach ($expected as $case) { $this->drupalGet('node/' . $case['node_id'] . '/edit'); // Verify data- attributes. $dom_node = $this->xpath('//textarea[@id="edit-body-0-value"]'); - $this->assertIdentical(self::$sampleContent, (string) $dom_node[0]['data-editor-value-original'], 'The data-editor-value-original attribute is correctly set.'); - $this->assertIdentical('false', (string) $dom_node[0]['data-editor-value-is-changed'], 'The data-editor-value-is-changed attribute is correctly set.'); + $this->assertIdentical(self::$sampleContent, $dom_node[0]->getAttribute('data-editor-value-original'), 'The data-editor-value-original attribute is correctly set.'); + $this->assertIdentical('false', (string) $dom_node[0]->getAttribute('data-editor-value-is-changed'), 'The data-editor-value-is-changed attribute is correctly set.'); // Switch to every other text format/editor and verify the results. foreach ($case['switch_to'] as $format => $expected_filtered_value) { @@ -404,13 +407,25 @@ public function testSwitchingSecurity() { '%original_format' => $case['format'], '%format' => $format, ])); + $post = [ 'value' => self::$sampleContent, 'original_format_id' => $case['format'], ]; - $response = $this->drupalPostWithFormat('editor/filter_xss/' . $format, 'json', $post); - $this->assertResponse(200); - $json = Json::decode($response); + $client = $this->getHttpClient(); + $response = $client->post($this->buildUrl('/editor/filter_xss/' . $format), [ + 'body' => http_build_query($post), + 'cookies' => $cookies, + 'headers' => [ + 'Accept' => 'application/json', + 'Content-Type' => 'application/x-www-form-urlencoded', + ], + 'http_errors' => FALSE, + ]); + + $this->assertEquals(200, $response->getStatusCode()); + + $json = Json::decode($response->getBody()); $this->assertIdentical($json, $expected_filtered_value, 'The value was correctly filtered for XSS attack vectors.'); } } @@ -424,7 +439,7 @@ public function testEditorXssFilterOverride() { $this->drupalLogin($this->normalUser); $this->drupalGet('node/2/edit'); $dom_node = $this->xpath('//textarea[@id="edit-body-0-value"]'); - $this->assertIdentical(self::$sampleContentSecured, (string) $dom_node[0], 'The value was filtered by the Standard text editor XSS filter.'); + $this->assertIdentical(self::$sampleContentSecured, $dom_node[0]->getText(), 'The value was filtered by the Standard text editor XSS filter.'); // Enable editor_test.module's hook_editor_xss_filter_alter() implementation // to alter the text editor XSS filter class being used. @@ -433,7 +448,21 @@ public function testEditorXssFilterOverride() { // First: the Insecure text editor XSS filter. $this->drupalGet('node/2/edit'); $dom_node = $this->xpath('//textarea[@id="edit-body-0-value"]'); - $this->assertIdentical(self::$sampleContent, (string) $dom_node[0], 'The value was filtered by the Insecure text editor XSS filter.'); + $this->assertIdentical(self::$sampleContent, $dom_node[0]->getText(), 'The value was filtered by the Insecure text editor XSS filter.'); + } + + /** + * Get session cookies from current session. + * + * @return \GuzzleHttp\Cookie\CookieJar + * A cookie jar with the current session. + */ + protected function getCookies() { + $domain = parse_url($this->getUrl(), PHP_URL_HOST); + $session_id = $this->getSession()->getCookie($this->getSessionName()); + $cookies = CookieJar::fromArray([$this->getSessionName() => $session_id], $domain); + + return $cookies; } } diff --git a/core/modules/editor/src/Tests/EditorUploadImageScaleTest.php b/core/modules/editor/tests/src/Functional/EditorUploadImageScaleTest.php similarity index 84% rename from core/modules/editor/src/Tests/EditorUploadImageScaleTest.php rename to core/modules/editor/tests/src/Functional/EditorUploadImageScaleTest.php index a4a22339dedb..46f9a6e4589d 100644 --- a/core/modules/editor/src/Tests/EditorUploadImageScaleTest.php +++ b/core/modules/editor/tests/src/Functional/EditorUploadImageScaleTest.php @@ -1,17 +1,21 @@ <?php -namespace Drupal\editor\Tests; +namespace Drupal\Tests\editor\Functional; +use Drupal\Component\Render\FormattableMarkup; use Drupal\editor\Entity\Editor; use Drupal\filter\Entity\FilterFormat; -use Drupal\simpletest\WebTestBase; +use Drupal\Tests\BrowserTestBase; +use Drupal\Tests\TestFileCreationTrait; /** * Tests scaling of inline images. * * @group editor */ -class EditorUploadImageScaleTest extends WebTestBase { +class EditorUploadImageScaleTest extends BrowserTestBase { + + use TestFileCreationTrait; /** * Modules to enable. @@ -66,7 +70,7 @@ protected function setUp() { */ public function testEditorUploadImageScale() { // Generate testing images. - $testing_image_list = $this->drupalGetTestFiles('image'); + $testing_image_list = $this->getTestFiles('image'); // Case 1: no max dimensions set: uploaded image not scaled. $test_image = $testing_image_list[0]; @@ -78,7 +82,7 @@ public function testEditorUploadImageScale() { list($uploaded_image_file_width, $uploaded_image_file_height) = $this->uploadImage($test_image->uri); $this->assertEqual($uploaded_image_file_width, $image_file_width); $this->assertEqual($uploaded_image_file_height, $image_file_height); - $this->assertNoRaw(t('The image was resized to fit within the maximum allowed dimensions of %dimensions pixels.', ['%dimensions' => $max_width . 'x' . $max_height])); + $this->assertNoRaw((string) new FormattableMarkup('The image was resized to fit within the maximum allowed dimensions of %dimensions pixels.', ['%dimensions' => $max_width . 'x' . $max_height])); // Case 2: max width smaller than uploaded image: image scaled down. $test_image = $testing_image_list[1]; @@ -90,7 +94,7 @@ public function testEditorUploadImageScale() { list($uploaded_image_file_width, $uploaded_image_file_height) = $this->uploadImage($test_image->uri); $this->assertEqual($uploaded_image_file_width, $max_width); $this->assertEqual($uploaded_image_file_height, $uploaded_image_file_height * ($uploaded_image_file_width / $max_width)); - $this->assertRaw(t('The image was resized to fit within the maximum allowed dimensions of %dimensions pixels.', ['%dimensions' => $max_width . 'x' . $max_height])); + $this->assertRaw((string) new FormattableMarkup('The image was resized to fit within the maximum allowed dimensions of %dimensions pixels.', ['%dimensions' => $max_width . 'x' . $max_height])); // Case 3: max height smaller than uploaded image: image scaled down. $test_image = $testing_image_list[2]; @@ -102,7 +106,7 @@ public function testEditorUploadImageScale() { list($uploaded_image_file_width, $uploaded_image_file_height) = $this->uploadImage($test_image->uri); $this->assertEqual($uploaded_image_file_width, $uploaded_image_file_width * ($uploaded_image_file_height / $max_height)); $this->assertEqual($uploaded_image_file_height, $max_height); - $this->assertRaw(t('The image was resized to fit within the maximum allowed dimensions of %dimensions pixels.', ['%dimensions' => $max_width . 'x' . $max_height])); + $this->assertRaw((string) new FormattableMarkup('The image was resized to fit within the maximum allowed dimensions of %dimensions pixels.', ['%dimensions' => $max_width . 'x' . $max_height])); // Case 4: max dimensions greater than uploaded image: image not scaled. $test_image = $testing_image_list[3]; @@ -114,7 +118,7 @@ public function testEditorUploadImageScale() { list($uploaded_image_file_width, $uploaded_image_file_height) = $this->uploadImage($test_image->uri); $this->assertEqual($uploaded_image_file_width, $image_file_width); $this->assertEqual($uploaded_image_file_height, $image_file_height); - $this->assertNoRaw(t('The image was resized to fit within the maximum allowed dimensions of %dimensions pixels.', ['%dimensions' => $max_width . 'x' . $max_height])); + $this->assertNoRaw((string) new FormattableMarkup('The image was resized to fit within the maximum allowed dimensions of %dimensions pixels.', ['%dimensions' => $max_width . 'x' . $max_height])); // Case 5: only max width dimension was provided and it was smaller than // uploaded image: image scaled down. @@ -127,7 +131,7 @@ public function testEditorUploadImageScale() { list($uploaded_image_file_width, $uploaded_image_file_height) = $this->uploadImage($test_image->uri); $this->assertEqual($uploaded_image_file_width, $max_width); $this->assertEqual($uploaded_image_file_height, $uploaded_image_file_height * ($uploaded_image_file_width / $max_width)); - $this->assertRaw(t('The image was resized to fit within the maximum allowed width of %width pixels.', ['%width' => $max_width])); + $this->assertRaw((string) new FormattableMarkup('The image was resized to fit within the maximum allowed width of %width pixels.', ['%width' => $max_width])); // Case 6: only max height dimension was provided and it was smaller than // uploaded image: image scaled down. @@ -140,7 +144,7 @@ public function testEditorUploadImageScale() { list($uploaded_image_file_width, $uploaded_image_file_height) = $this->uploadImage($test_image->uri); $this->assertEqual($uploaded_image_file_width, $uploaded_image_file_width * ($uploaded_image_file_height / $max_height)); $this->assertEqual($uploaded_image_file_height, $max_height); - $this->assertRaw(t('The image was resized to fit within the maximum allowed height of %height pixels.', ['%height' => $max_height])); + $this->assertRaw((string) new FormattableMarkup('The image was resized to fit within the maximum allowed height of %height pixels.', ['%height' => $max_height])); } /** diff --git a/core/modules/editor/src/Tests/QuickEditIntegrationLoadingTest.php b/core/modules/editor/tests/src/Functional/QuickEditIntegrationLoadingTest.php similarity index 69% rename from core/modules/editor/src/Tests/QuickEditIntegrationLoadingTest.php rename to core/modules/editor/tests/src/Functional/QuickEditIntegrationLoadingTest.php index 5496378eaa3f..0df19ec326fa 100644 --- a/core/modules/editor/src/Tests/QuickEditIntegrationLoadingTest.php +++ b/core/modules/editor/tests/src/Functional/QuickEditIntegrationLoadingTest.php @@ -1,18 +1,19 @@ <?php -namespace Drupal\editor\Tests; +namespace Drupal\Tests\editor\Functional; use Drupal\Component\Serialization\Json; use Drupal\Core\EventSubscriber\MainContentViewSubscriber; -use Drupal\simpletest\WebTestBase; use Drupal\filter\Entity\FilterFormat; +use Drupal\Tests\BrowserTestBase; +use GuzzleHttp\Cookie\CookieJar; /** * Tests Quick Edit module integration endpoints. * * @group editor */ -class QuickEditIntegrationLoadingTest extends WebTestBase { +class QuickEditIntegrationLoadingTest extends BrowserTestBase { /** * Modules to enable. @@ -84,17 +85,30 @@ public function testUsersWithoutPermission() { // Ensure the text is transformed. $this->assertRaw('<p>Do you also love Drupal?</p><figure role="group" class="caption caption-img"><img src="druplicon.png" /><figcaption>Druplicon</figcaption></figure>'); + $client = $this->getHttpClient(); + // Retrieving the untransformed text should result in an 403 response and // return a different error message depending of the missing permission. - $response = $this->drupalPost('editor/' . 'node/1/body/en/full', '', [], ['query' => [MainContentViewSubscriber::WRAPPER_FORMAT => 'drupal_ajax']]); - $this->assertResponse(403); + $response = $client->post($this->buildUrl('editor/node/1/body/en/full'), [ + 'query' => http_build_query([MainContentViewSubscriber::WRAPPER_FORMAT => 'drupal_ajax']), + 'cookies' => $this->getCookies(), + 'headers' => [ + 'Accept' => 'application/json', + 'Content-Type' => 'application/x-www-form-urlencoded', + ], + 'http_errors' => FALSE, + ]); + + $this->assertEquals(403, $response->getStatusCode()); if (!$user->hasPermission('access in-place editing')) { $message = "The 'access in-place editing' permission is required."; } else { $message = ''; } - $this->assertIdentical(Json::encode(['message' => $message]), $response); + + $body = Json::decode($response->getBody()); + $this->assertIdentical($message, $body['message']); } } @@ -108,13 +122,35 @@ public function testUserWithPermission() { // Ensure the text is transformed. $this->assertRaw('<p>Do you also love Drupal?</p><figure role="group" class="caption caption-img"><img src="druplicon.png" /><figcaption>Druplicon</figcaption></figure>'); + $client = $this->getHttpClient(); + $response = $client->post($this->buildUrl('editor/node/1/body/en/full'), [ + 'query' => http_build_query([MainContentViewSubscriber::WRAPPER_FORMAT => 'drupal_ajax']), + 'cookies' => $this->getCookies(), + 'headers' => [ + 'Accept' => 'application/json', + 'Content-Type' => 'application/x-www-form-urlencoded', + ], + 'http_errors' => FALSE, + ]); - $response = $this->drupalPost('editor/' . 'node/1/body/en/full', '', [], ['query' => [MainContentViewSubscriber::WRAPPER_FORMAT => 'drupal_ajax']]); - $this->assertResponse(200); - $ajax_commands = Json::decode($response); + $this->assertEquals(200, $response->getStatusCode()); + $ajax_commands = Json::decode($response->getBody()); $this->assertIdentical(1, count($ajax_commands), 'The untransformed text POST request results in one AJAX command.'); $this->assertIdentical('editorGetUntransformedText', $ajax_commands[0]['command'], 'The first AJAX command is an editorGetUntransformedText command.'); $this->assertIdentical('<p>Do you also love Drupal?</p><img src="druplicon.png" data-caption="Druplicon" />', $ajax_commands[0]['data'], 'The editorGetUntransformedText command contains the expected data.'); } + /** + * Get session cookies from current session. + * + * @return \GuzzleHttp\Cookie\CookieJar + */ + protected function getCookies() { + $domain = parse_url($this->getUrl(), PHP_URL_HOST); + $session_id = $this->getSession()->getCookie($this->getSessionName()); + $cookies = CookieJar::fromArray([$this->getSessionName() => $session_id], $domain); + + return $cookies; + } + } -- GitLab