diff --git a/core/modules/quickedit/src/Tests/QuickEditAutocompleteTermTest.php b/core/modules/quickedit/src/Tests/QuickEditAutocompleteTermTest.php deleted file mode 100644 index 97ccc77710..0000000000 --- a/core/modules/quickedit/src/Tests/QuickEditAutocompleteTermTest.php +++ /dev/null @@ -1,215 +0,0 @@ -drupalCreateContentType([ - 'type' => 'article', - ]); - // Create the vocabulary for the tag field. - $this->vocabulary = Vocabulary::create([ - 'name' => 'quickedit testing tags', - 'vid' => 'quickedit_testing_tags', - ]); - $this->vocabulary->save(); - $this->fieldName = 'field_' . $this->vocabulary->id(); - - $handler_settings = [ - 'target_bundles' => [ - $this->vocabulary->id() => $this->vocabulary->id(), - ], - 'auto_create' => TRUE, - ]; - $this->createEntityReferenceField('node', 'article', $this->fieldName, 'Tags', 'taxonomy_term', 'default', $handler_settings, FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED); - - entity_get_form_display('node', 'article', 'default') - ->setComponent($this->fieldName, [ - 'type' => 'entity_reference_autocomplete_tags', - 'weight' => -4, - ]) - ->save(); - - entity_get_display('node', 'article', 'default') - ->setComponent($this->fieldName, [ - 'type' => 'entity_reference_label', - 'weight' => 10, - ]) - ->save(); - entity_get_display('node', 'article', 'teaser') - ->setComponent($this->fieldName, [ - 'type' => 'entity_reference_label', - 'weight' => 10, - ]) - ->save(); - - $this->term1 = $this->createTerm(); - $this->term2 = $this->createTerm(); - - $node = []; - $node['type'] = 'article'; - $node[$this->fieldName][]['target_id'] = $this->term1->id(); - $node[$this->fieldName][]['target_id'] = $this->term2->id(); - $this->node = $this->drupalCreateNode($node); - - $this->editorUser = $this->drupalCreateUser(['access content', 'create article content', 'edit any article content', 'access in-place editing']); - } - - /** - * Tests Quick Edit autocomplete term behavior. - */ - public function testAutocompleteQuickEdit() { - $this->drupalLogin($this->editorUser); - - $quickedit_uri = 'quickedit/form/node/' . $this->node->id() . '/' . $this->fieldName . '/' . $this->node->language()->getId() . '/full'; - $post = ['nocssjs' => 'true'] + $this->getAjaxPageStatePostData(); - $response = $this->drupalPost($quickedit_uri, '', $post, ['query' => [MainContentViewSubscriber::WRAPPER_FORMAT => 'drupal_ajax']]); - $ajax_commands = Json::decode($response); - - // Prepare form values for submission. drupalPostAJAX() is not suitable for - // handling pages with JSON responses, so we need our own solution here. - $form_tokens_found = preg_match('/\sname="form_token" value="([^"]+)"/', $ajax_commands[0]['data'], $token_match) && preg_match('/\sname="form_build_id" value="([^"]+)"/', $ajax_commands[0]['data'], $build_id_match); - $this->assertTrue($form_tokens_found, 'Form tokens found in output.'); - - if ($form_tokens_found) { - $post = [ - 'form_id' => 'quickedit_field_form', - 'form_token' => $token_match[1], - 'form_build_id' => $build_id_match[1], - $this->fieldName . '[target_id]' => implode(', ', [$this->term1->getName(), 'new term', $this->term2->getName()]), - 'op' => t('Save'), - ]; - - // Submit field form and check response. Should render back all the terms. - $response = $this->drupalPost($quickedit_uri, '', $post, ['query' => [MainContentViewSubscriber::WRAPPER_FORMAT => 'drupal_ajax']]); - $this->assertResponse(200); - $ajax_commands = Json::decode($response); - $this->setRawContent($ajax_commands[0]['data']); - $this->assertLink($this->term1->getName()); - $this->assertLink($this->term2->getName()); - $this->assertText('new term'); - $this->assertNoLink('new term'); - - // Load the form again, which should now get it back from - // PrivateTempStore. - $quickedit_uri = 'quickedit/form/node/' . $this->node->id() . '/' . $this->fieldName . '/' . $this->node->language()->getId() . '/full'; - $post = ['nocssjs' => 'true'] + $this->getAjaxPageStatePostData(); - $response = $this->drupalPost($quickedit_uri, '', $post, ['query' => [MainContentViewSubscriber::WRAPPER_FORMAT => 'drupal_ajax']]); - $ajax_commands = Json::decode($response); - - // The AjaxResponse's first command is an InsertCommand which contains - // the form to edit the taxonomy term field, it should contain all three - // taxonomy terms, including the one that has just been newly created and - // which is not yet stored. - $this->setRawContent($ajax_commands[0]['data']); - $expected = [ - $this->term1->getName() . ' (' . $this->term1->id() . ')', - 'new term', - $this->term2->getName() . ' (' . $this->term2->id() . ')', - ]; - $this->assertFieldByName($this->fieldName . '[target_id]', implode(', ', $expected)); - - // Save the entity. - $post = ['nocssjs' => 'true']; - $response = $this->drupalPostWithFormat('quickedit/entity/node/' . $this->node->id(), 'json', $post); - $this->assertResponse(200); - - // The full node display should now link to all entities, with the new - // one created in the database as well. - $this->drupalGet('node/' . $this->node->id()); - $this->assertLink($this->term1->getName()); - $this->assertLink($this->term2->getName()); - $this->assertLink('new term'); - } - } - - /** - * Returns a new term with random name and description in $this->vocabulary. - * - * @return \Drupal\taxonomy\TermInterface - * The created taxonomy term. - */ - protected function createTerm() { - $filter_formats = filter_formats(); - $format = array_pop($filter_formats); - $term = Term::create([ - 'name' => $this->randomMachineName(), - 'description' => $this->randomMachineName(), - // Use the first available text format. - 'format' => $format->id(), - 'vid' => $this->vocabulary->id(), - 'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED, - ]); - $term->save(); - return $term; - } - -} diff --git a/core/modules/quickedit/src/Tests/QuickEditLoadingTest.php b/core/modules/quickedit/src/Tests/QuickEditLoadingTest.php deleted file mode 100644 index 1545138082..0000000000 --- a/core/modules/quickedit/src/Tests/QuickEditLoadingTest.php +++ /dev/null @@ -1,593 +0,0 @@ - 'filtered_html', - 'name' => 'Filtered HTML', - 'weight' => 0, - 'filters' => [], - ]); - $filtered_html_format->save(); - - // Create a node type. - $this->drupalCreateContentType([ - 'type' => 'article', - 'name' => 'Article', - ]); - - // Set the node type to initially not have revisions. - // Testing with revisions will be done later. - $node_type = NodeType::load('article'); - $node_type->setNewRevision(FALSE); - $node_type->save(); - - // Create one node of the above node type using the above text format. - $this->testNode = $this->drupalCreateNode([ - 'type' => 'article', - 'body' => [ - 0 => [ - 'value' => '

How are you?

', - 'format' => 'filtered_html', - ], - ], - 'revision_log' => $this->randomString(), - ]); - - // Create 2 users, the only difference being the ability to use in-place - // editing - $basic_permissions = ['access content', 'create article content', 'edit any article content', 'use text format filtered_html', 'access contextual links']; - $this->authorUser = $this->drupalCreateUser($basic_permissions); - $this->editorUser = $this->drupalCreateUser(array_merge($basic_permissions, ['access in-place editing'])); - } - - /** - * Test the loading of Quick Edit when a user doesn't have access to it. - */ - public function testUserWithoutPermission() { - $this->drupalLogin($this->authorUser); - $this->drupalGet('node/1'); - - // Library and in-place editors. - $this->assertNoRaw('core/modules/quickedit/js/quickedit.js', 'Quick Edit library not loaded.'); - $this->assertNoRaw('core/modules/quickedit/js/editors/formEditor.js', "'form' in-place editor not loaded."); - - // HTML annotation and title class does not exist for users without - // permission to in-place edit. - $this->assertNoRaw('data-quickedit-entity-id="node/1"'); - $this->assertNoRaw('data-quickedit-field-id="node/1/body/en/full"'); - $this->assertNoFieldByXPath('//h1[contains(@class, "js-quickedit-page-title")]'); - - // Retrieving the metadata should result in an empty 403 response. - $post = ['fields[0]' => 'node/1/body/en/full']; - $response = $this->drupalPostWithFormat(Url::fromRoute('quickedit.metadata'), 'json', $post); - $this->assertIdentical(Json::encode(['message' => "The 'access in-place editing' permission is required."]), $response); - $this->assertResponse(403); - - // Quick Edit's JavaScript would never hit these endpoints if the metadata - // was empty as above, but we need to make sure that malicious users aren't - // able to use any of the other endpoints either. - $post = ['editors[0]' => 'form'] + $this->getAjaxPageStatePostData(); - $response = $this->drupalPost('quickedit/attachments', '', $post, ['query' => [MainContentViewSubscriber::WRAPPER_FORMAT => 'drupal_ajax']]); - $message = Json::encode(['message' => "The 'access in-place editing' permission is required."]); - $this->assertIdentical($message, $response); - $this->assertResponse(403); - $post = ['nocssjs' => 'true'] + $this->getAjaxPageStatePostData(); - $response = $this->drupalPost('quickedit/form/' . 'node/1/body/en/full', '', $post, ['query' => [MainContentViewSubscriber::WRAPPER_FORMAT => 'drupal_ajax']]); - $this->assertIdentical($message, $response); - $this->assertResponse(403); - $edit = []; - $edit['form_id'] = 'quickedit_field_form'; - $edit['form_token'] = 'xIOzMjuc-PULKsRn_KxFn7xzNk5Bx7XKXLfQfw1qOnA'; - $edit['form_build_id'] = 'form-kVmovBpyX-SJfTT5kY0pjTV35TV-znor--a64dEnMR8'; - $edit['body[0][summary]'] = ''; - $edit['body[0][value]'] = '

Malicious content.

'; - $edit['body[0][format]'] = 'filtered_html'; - $edit['op'] = t('Save'); - $response = $this->drupalPost('quickedit/form/' . 'node/1/body/en/full', '', $edit, ['query' => [MainContentViewSubscriber::WRAPPER_FORMAT => 'drupal_ajax']]); - $this->assertIdentical($message, $response); - $this->assertResponse(403); - $post = ['nocssjs' => 'true']; - $response = $this->drupalPostWithFormat('quickedit/entity/' . 'node/1', 'json', $post); - $this->assertIdentical(Json::encode(['message' => "The 'access in-place editing' permission is required."]), $response); - $this->assertResponse(403); - } - - /** - * Tests the loading of Quick Edit when a user does have access to it. - * - * Also ensures lazy loading of in-place editors works. - */ - public function testUserWithPermission() { - $this->drupalLogin($this->editorUser); - $this->drupalGet('node/1'); - - // Library and in-place editors. - $settings = $this->getDrupalSettings(); - $libraries = explode(',', $settings['ajaxPageState']['libraries']); - $this->assertTrue(in_array('quickedit/quickedit', $libraries), 'Quick Edit library loaded.'); - $this->assertFalse(in_array('quickedit/quickedit.inPlaceEditor.form', $libraries), "'form' in-place editor not loaded."); - - // HTML annotation and title class must always exist (to not break the - // render cache). - $this->assertRaw('data-quickedit-entity-id="node/1"'); - $this->assertRaw('data-quickedit-field-id="node/1/body/en/full"'); - $this->assertFieldByXPath('//h1[contains(@class, "js-quickedit-page-title")]'); - - // There should be only one revision so far. - $node = Node::load(1); - $vids = \Drupal::entityManager()->getStorage('node')->revisionIds($node); - $this->assertIdentical(1, count($vids), 'The node has only one revision.'); - $original_log = $node->revision_log->value; - - // Retrieving the metadata should result in a 200 JSON response. - $htmlPageDrupalSettings = $this->drupalSettings; - $post = ['fields[0]' => 'node/1/body/en/full']; - $response = $this->drupalPostWithFormat('quickedit/metadata', 'json', $post); - $this->assertResponse(200); - $expected = [ - 'node/1/body/en/full' => [ - 'label' => 'Body', - 'access' => TRUE, - 'editor' => 'form', - ], - ]; - $this->assertIdentical(Json::decode($response), $expected, 'The metadata HTTP request answers with the correct JSON response.'); - // Restore drupalSettings to build the next requests; simpletest wipes them - // after a JSON response. - $this->drupalSettings = $htmlPageDrupalSettings; - - // Retrieving the attachments should result in a 200 response, containing: - // 1. a settings command with useless metadata: AjaxController is dumb - // 2. an insert command that loads the required in-place editors - $post = ['editors[0]' => 'form'] + $this->getAjaxPageStatePostData(); - $response = $this->drupalPost('quickedit/attachments', '', $post, ['query' => [MainContentViewSubscriber::WRAPPER_FORMAT => 'drupal_ajax']]); - $ajax_commands = Json::decode($response); - $this->assertIdentical(2, count($ajax_commands), 'The attachments HTTP request results in two AJAX commands.'); - // First command: settings. - $this->assertIdentical('settings', $ajax_commands[0]['command'], 'The first AJAX command is a settings command.'); - // Second command: insert libraries into DOM. - $this->assertIdentical('insert', $ajax_commands[1]['command'], 'The second AJAX command is an append command.'); - $this->assertTrue(in_array('quickedit/quickedit.inPlaceEditor.form', explode(',', $ajax_commands[0]['settings']['ajaxPageState']['libraries'])), 'The quickedit.inPlaceEditor.form library is loaded.'); - - // Retrieving the form for this field should result in a 200 response, - // containing only a quickeditFieldForm command. - $post = ['nocssjs' => 'true', 'reset' => 'true'] + $this->getAjaxPageStatePostData(); - $response = $this->drupalPost('quickedit/form/' . 'node/1/body/en/full', '', $post, ['query' => [MainContentViewSubscriber::WRAPPER_FORMAT => 'drupal_ajax']]); - $this->assertResponse(200); - $ajax_commands = Json::decode($response); - $this->assertIdentical(1, count($ajax_commands), 'The field form HTTP request results in one AJAX command.'); - $this->assertIdentical('quickeditFieldForm', $ajax_commands[0]['command'], 'The first AJAX command is a quickeditFieldForm command.'); - $this->assertIdentical('
assertTrue($form_tokens_found, 'Form tokens found in output.'); - - if ($form_tokens_found) { - $edit = [ - 'body[0][summary]' => '', - 'body[0][value]' => '

Fine thanks.

', - 'body[0][format]' => 'filtered_html', - 'op' => t('Save'), - ]; - $post = [ - 'form_id' => 'quickedit_field_form', - 'form_token' => $token_match[1], - 'form_build_id' => $build_id_match[1], - ]; - $post += $edit + $this->getAjaxPageStatePostData(); - - // Submit field form and check response. This should store the updated - // entity in PrivateTempStore on the server. - $response = $this->drupalPost('quickedit/form/' . 'node/1/body/en/full', '', $post, ['query' => [MainContentViewSubscriber::WRAPPER_FORMAT => 'drupal_ajax']]); - $this->assertResponse(200); - $ajax_commands = Json::decode($response); - $this->assertIdentical(1, count($ajax_commands), 'The field form HTTP request results in one AJAX command.'); - $this->assertIdentical('quickeditFieldFormSaved', $ajax_commands[0]['command'], 'The first AJAX command is a quickeditFieldFormSaved command.'); - $this->assertTrue(strpos($ajax_commands[0]['data'], 'Fine thanks.'), 'Form value saved and printed back.'); - $this->assertIdentical($ajax_commands[0]['other_view_modes'], [], 'Field was not rendered in any other view mode.'); - - // Ensure the text on the original node did not change yet. - $this->drupalGet('node/1'); - $this->assertText('How are you?'); - - // Save the entity by moving the PrivateTempStore values to entity storage. - $post = ['nocssjs' => 'true']; - $response = $this->drupalPostWithFormat('quickedit/entity/' . 'node/1', 'json', $post); - $this->assertResponse(200); - $ajax_commands = Json::decode($response); - $this->assertIdentical(1, count($ajax_commands), 'The entity submission HTTP request results in one AJAX command.'); - $this->assertIdentical('quickeditEntitySaved', $ajax_commands[0]['command'], 'The first AJAX command is a quickeditEntitySaved command.'); - $this->assertIdentical($ajax_commands[0]['data']['entity_type'], 'node', 'Saved entity is of type node.'); - $this->assertIdentical($ajax_commands[0]['data']['entity_id'], '1', 'Entity id is 1.'); - - // Ensure the text on the original node did change. - $this->drupalGet('node/1'); - $this->assertText('Fine thanks.'); - - // Ensure no new revision was created and the log message is unchanged. - $node = Node::load(1); - $vids = \Drupal::entityManager()->getStorage('node')->revisionIds($node); - $this->assertIdentical(1, count($vids), 'The node has only one revision.'); - $this->assertIdentical($original_log, $node->revision_log->value, 'The revision log message is unchanged.'); - - // Now configure this node type to create new revisions automatically, - // then again retrieve the field form, fill it, submit it (so it ends up - // in PrivateTempStore) and then save the entity. Now there should be two - // revisions. - $node_type = NodeType::load('article'); - $node_type->setNewRevision(TRUE); - $node_type->save(); - - // Retrieve field form. - $post = ['nocssjs' => 'true', 'reset' => 'true']; - $response = $this->drupalPost('quickedit/form/' . 'node/1/body/en/full', '', $post, ['query' => [MainContentViewSubscriber::WRAPPER_FORMAT => 'drupal_ajax']]); - $this->assertResponse(200); - $ajax_commands = Json::decode($response); - $this->assertIdentical(1, count($ajax_commands), 'The field form HTTP request results in one AJAX command.'); - $this->assertIdentical('quickeditFieldForm', $ajax_commands[0]['command'], 'The first AJAX command is a quickeditFieldForm command.'); - $this->assertIdentical(' 'quickedit_field_form', - 'form_token' => $token_match[1], - 'form_build_id' => $build_id_match[1], - ]; - $post += $edit + $this->getAjaxPageStatePostData(); - $response = $this->drupalPost('quickedit/form/' . 'node/1/body/en/full', '', $post, ['query' => [MainContentViewSubscriber::WRAPPER_FORMAT => 'drupal_ajax']]); - $this->assertResponse(200); - $ajax_commands = Json::decode($response); - $this->assertIdentical(1, count($ajax_commands), 'The field form HTTP request results in one AJAX command.'); - $this->assertIdentical('quickeditFieldFormSaved', $ajax_commands[0]['command'], 'The first AJAX command is an quickeditFieldFormSaved command.'); - $this->assertTrue(strpos($ajax_commands[0]['data'], 'kthxbye'), 'Form value saved and printed back.'); - - // Save the entity. - $post = ['nocssjs' => 'true']; - $response = $this->drupalPostWithFormat('quickedit/entity/' . 'node/1', 'json', $post); - $this->assertResponse(200); - $ajax_commands = Json::decode($response); - $this->assertIdentical(1, count($ajax_commands)); - $this->assertIdentical('quickeditEntitySaved', $ajax_commands[0]['command'], 'The first AJAX command is an quickeditEntitySaved command.'); - $this->assertEqual($ajax_commands[0]['data'], ['entity_type' => 'node', 'entity_id' => 1], 'Updated entity type and ID returned'); - - // Test that a revision was created with the correct log message. - $vids = \Drupal::entityManager()->getStorage('node')->revisionIds(Node::load(1)); - $this->assertIdentical(2, count($vids), 'The node has two revisions.'); - $revision = node_revision_load($vids[0]); - $this->assertIdentical($original_log, $revision->revision_log->value, 'The first revision log message is unchanged.'); - $revision = node_revision_load($vids[1]); - $this->assertIdentical('Updated the Body field through in-place editing.', $revision->revision_log->value, 'The second revision log message was correctly generated by Quick Edit module.'); - } - } - - /** - * Test quickedit does not appear for entities with pending revisions. - */ - public function testWithPendingRevision() { - $this->drupalLogin($this->editorUser); - - // Verify that the preview is loaded correctly. - $this->drupalPostForm('node/add/article', ['title[0][value]' => 'foo'], 'Preview'); - $this->assertResponse(200); - // Verify that quickedit is not active on preview. - $this->assertNoRaw('data-quickedit-entity-id="node/' . $this->testNode->id() . '"'); - $this->assertNoRaw('data-quickedit-field-id="node/' . $this->testNode->id() . '/title/' . $this->testNode->language()->getId() . '/full"'); - - $this->drupalGet('node/' . $this->testNode->id()); - $this->assertRaw('data-quickedit-entity-id="node/' . $this->testNode->id() . '"'); - $this->assertRaw('data-quickedit-field-id="node/' . $this->testNode->id() . '/title/' . $this->testNode->language()->getId() . '/full"'); - - $this->testNode->title = 'Updated node'; - $this->testNode->setNewRevision(TRUE); - $this->testNode->isDefaultRevision(FALSE); - $this->testNode->save(); - - $this->drupalGet('node/' . $this->testNode->id()); - $this->assertResponse(200); - $this->assertNoRaw('data-quickedit-entity-id="node/' . $this->testNode->id() . '"'); - $this->assertNoRaw('data-quickedit-field-id="node/' . $this->testNode->id() . '/title/' . $this->testNode->language()->getId() . '/full"'); - } - - /** - * Tests the loading of Quick Edit for the title base field. - */ - public function testTitleBaseField() { - $this->drupalLogin($this->editorUser); - $this->drupalGet('node/1'); - - // Ensure that the full page title is actually in-place editable - $node = Node::load(1); - $elements = $this->xpath('//h1/span[@data-quickedit-field-id="node/1/title/en/full" and normalize-space(text())=:title]', [':title' => $node->label()]); - $this->assertTrue(!empty($elements), 'Title with data-quickedit-field-id attribute found.'); - - // Retrieving the metadata should result in a 200 JSON response. - $htmlPageDrupalSettings = $this->drupalSettings; - $post = ['fields[0]' => 'node/1/title/en/full']; - $response = $this->drupalPostWithFormat('quickedit/metadata', 'json', $post); - $this->assertResponse(200); - $expected = [ - 'node/1/title/en/full' => [ - 'label' => 'Title', - 'access' => TRUE, - 'editor' => 'plain_text', - ], - ]; - $this->assertIdentical(Json::decode($response), $expected, 'The metadata HTTP request answers with the correct JSON response.'); - // Restore drupalSettings to build the next requests; simpletest wipes them - // after a JSON response. - $this->drupalSettings = $htmlPageDrupalSettings; - - // Retrieving the form for this field should result in a 200 response, - // containing only a quickeditFieldForm command. - $post = ['nocssjs' => 'true', 'reset' => 'true'] + $this->getAjaxPageStatePostData(); - $response = $this->drupalPost('quickedit/form/' . 'node/1/title/en/full', '', $post, ['query' => [MainContentViewSubscriber::WRAPPER_FORMAT => 'drupal_ajax']]); - $this->assertResponse(200); - $ajax_commands = Json::decode($response); - $this->assertIdentical(1, count($ajax_commands), 'The field form HTTP request results in one AJAX command.'); - $this->assertIdentical('quickeditFieldForm', $ajax_commands[0]['command'], 'The first AJAX command is a quickeditFieldForm command.'); - $this->assertIdentical('assertTrue($form_tokens_found, 'Form tokens found in output.'); - - if ($form_tokens_found) { - $edit = [ - 'title[0][value]' => 'Obligatory question', - 'op' => t('Save'), - ]; - $post = [ - 'form_id' => 'quickedit_field_form', - 'form_token' => $token_match[1], - 'form_build_id' => $build_id_match[1], - ]; - $post += $edit + $this->getAjaxPageStatePostData(); - - // Submit field form and check response. This should store the - // updated entity in PrivateTempStore on the server. - $response = $this->drupalPost('quickedit/form/' . 'node/1/title/en/full', '', $post, ['query' => [MainContentViewSubscriber::WRAPPER_FORMAT => 'drupal_ajax']]); - $this->assertResponse(200); - $ajax_commands = Json::decode($response); - $this->assertIdentical(1, count($ajax_commands), 'The field form HTTP request results in one AJAX command.'); - $this->assertIdentical('quickeditFieldFormSaved', $ajax_commands[0]['command'], 'The first AJAX command is a quickeditFieldFormSaved command.'); - $this->assertTrue(strpos($ajax_commands[0]['data'], 'Obligatory question'), 'Form value saved and printed back.'); - - // Ensure the text on the original node did not change yet. - $this->drupalGet('node/1'); - $this->assertNoText('Obligatory question'); - - // Save the entity by moving the PrivateTempStore values to entity storage. - $post = ['nocssjs' => 'true']; - $response = $this->drupalPostWithFormat('quickedit/entity/' . 'node/1', 'json', $post); - $this->assertResponse(200); - $ajax_commands = Json::decode($response); - $this->assertIdentical(1, count($ajax_commands), 'The entity submission HTTP request results in one AJAX command.'); - $this->assertIdentical('quickeditEntitySaved', $ajax_commands[0]['command'], 'The first AJAX command is n quickeditEntitySaved command.'); - $this->assertIdentical($ajax_commands[0]['data']['entity_type'], 'node', 'Saved entity is of type node.'); - $this->assertIdentical($ajax_commands[0]['data']['entity_id'], '1', 'Entity id is 1.'); - - // Ensure the text on the original node did change. - $this->drupalGet('node/1'); - $this->assertText('Obligatory question'); - } - } - - /** - * Tests that Quick Edit works with custom render pipelines. - */ - public function testCustomPipeline() { - \Drupal::service('module_installer')->install(['quickedit_test']); - - $custom_render_url = 'quickedit/form/node/1/body/en/quickedit_test-custom-render-data'; - $this->drupalLogin($this->editorUser); - - // Request editing to render results with the custom render pipeline. - $post = ['nocssjs' => 'true'] + $this->getAjaxPageStatePostData(); - $response = $this->drupalPost($custom_render_url, '', $post, ['query' => [MainContentViewSubscriber::WRAPPER_FORMAT => 'drupal_ajax']]); - $ajax_commands = Json::decode($response); - - // Prepare form values for submission. drupalPostAJAX() is not suitable for - // handling pages with JSON responses, so we need our own solution here. - $form_tokens_found = preg_match('/\sname="form_token" value="([^"]+)"/', $ajax_commands[0]['data'], $token_match) && preg_match('/\sname="form_build_id" value="([^"]+)"/', $ajax_commands[0]['data'], $build_id_match); - $this->assertTrue($form_tokens_found, 'Form tokens found in output.'); - - if ($form_tokens_found) { - $post = [ - 'form_id' => 'quickedit_field_form', - 'form_token' => $token_match[1], - 'form_build_id' => $build_id_match[1], - 'body[0][summary]' => '', - 'body[0][value]' => '

Fine thanks.

', - 'body[0][format]' => 'filtered_html', - 'op' => t('Save'), - ]; - // Assume there is another field on this page, which doesn't use a custom - // render pipeline, but the default one, and it uses the "full" view mode. - $post += ['other_view_modes[]' => 'full']; - - // Submit field form and check response. Should render with the custom - // render pipeline. - $response = $this->drupalPost($custom_render_url, '', $post, ['query' => [MainContentViewSubscriber::WRAPPER_FORMAT => 'drupal_ajax']]); - $this->assertResponse(200); - $ajax_commands = Json::decode($response); - $this->assertIdentical(1, count($ajax_commands), 'The field form HTTP request results in one AJAX command.'); - $this->assertIdentical('quickeditFieldFormSaved', $ajax_commands[0]['command'], 'The first AJAX command is a quickeditFieldFormSaved command.'); - $this->assertTrue(strpos($ajax_commands[0]['data'], 'Fine thanks.'), 'Form value saved and printed back.'); - $this->assertTrue(strpos($ajax_commands[0]['data'], '
') !== FALSE, 'Custom render pipeline used to render the value.'); - $this->assertIdentical(array_keys($ajax_commands[0]['other_view_modes']), ['full'], 'Field was also rendered in the "full" view mode.'); - $this->assertTrue(strpos($ajax_commands[0]['other_view_modes']['full'], 'Fine thanks.'), '"full" version of field contains the form value.'); - } - } - - /** - * Tests Quick Edit on a node that was concurrently edited on the full node - * form. - */ - public function testConcurrentEdit() { - $this->drupalLogin($this->editorUser); - - $post = ['nocssjs' => 'true'] + $this->getAjaxPageStatePostData(); - $response = $this->drupalPost('quickedit/form/' . 'node/1/body/en/full', '', $post, ['query' => [MainContentViewSubscriber::WRAPPER_FORMAT => 'drupal_ajax']]); - $this->assertResponse(200); - $ajax_commands = Json::decode($response); - - // Prepare form values for submission. drupalPostAJAX() is not suitable for - // handling pages with JSON responses, so we need our own solution here. - $form_tokens_found = preg_match('/\sname="form_token" value="([^"]+)"/', $ajax_commands[0]['data'], $token_match) && preg_match('/\sname="form_build_id" value="([^"]+)"/', $ajax_commands[0]['data'], $build_id_match); - $this->assertTrue($form_tokens_found, 'Form tokens found in output.'); - - if ($form_tokens_found) { - $post = [ - 'nocssjs' => 'true', - 'form_id' => 'quickedit_field_form', - 'form_token' => $token_match[1], - 'form_build_id' => $build_id_match[1], - 'body[0][summary]' => '', - 'body[0][value]' => '

Fine thanks.

', - 'body[0][format]' => 'filtered_html', - 'op' => t('Save'), - ]; - - // Save the node on the regular node edit form. - $this->drupalPostForm('node/1/edit', [], t('Save')); - // Ensure different save timestamps for field editing. - sleep(2); - - // Submit field form and check response. Should throw a validation error - // because the node was changed in the meantime. - $response = $this->drupalPost('quickedit/form/' . 'node/1/body/en/full', '', $post, ['query' => [MainContentViewSubscriber::WRAPPER_FORMAT => 'drupal_ajax']]); - $this->assertResponse(200); - $ajax_commands = Json::decode($response); - $this->assertIdentical(2, count($ajax_commands), 'The field form HTTP request results in two AJAX commands.'); - $this->assertIdentical('quickeditFieldFormValidationErrors', $ajax_commands[1]['command'], 'The second AJAX command is a quickeditFieldFormValidationErrors command.'); - $this->assertTrue(strpos($ajax_commands[1]['data'], 'The content has either been modified by another user, or you have already submitted modifications. As a result, your changes cannot be saved.'), 'Error message returned to user.'); - } - } - - /** - * Tests that Quick Edit's data- attributes are present for content blocks. - */ - public function testContentBlock() { - \Drupal::service('module_installer')->install(['block_content']); - - // Create and place a content_block block. - $block = BlockContent::create([ - 'info' => $this->randomMachineName(), - 'type' => 'basic', - 'langcode' => 'en', - ]); - $block->save(); - $this->drupalPlaceBlock('block_content:' . $block->uuid()); - - // Check that the data- attribute is present. - $this->drupalLogin($this->editorUser); - $this->drupalGet(''); - $this->assertRaw('data-quickedit-entity-id="block_content/1"'); - } - - /** - * Tests that Quick Edit can handle an image field. - */ - public function testImageField() { - // Add an image field to the content type. - FieldStorageConfig::create([ - 'field_name' => 'field_image', - 'type' => 'image', - 'entity_type' => 'node', - ])->save(); - FieldConfig::create([ - 'field_name' => 'field_image', - 'field_type' => 'image', - 'label' => t('Image'), - 'entity_type' => 'node', - 'bundle' => 'article', - ])->save(); - entity_get_form_display('node', 'article', 'default') - ->setComponent('field_image', [ - 'type' => 'image_image', - ]) - ->save(); - - // Add an image to the node. - $this->drupalLogin($this->editorUser); - $image = $this->drupalGetTestFiles('image')[0]; - $this->drupalPostForm('node/1/edit', [ - 'files[field_image_0]' => $image->uri, - ], t('Upload')); - $this->drupalPostForm(NULL, [ - 'field_image[0][alt]' => 'Vivamus aliquet elit', - ], t('Save')); - - // The image field form should load normally. - $response = $this->drupalPost('quickedit/form/node/1/field_image/en/full', '', ['nocssjs' => 'true'] + $this->getAjaxPageStatePostData(), ['query' => [MainContentViewSubscriber::WRAPPER_FORMAT => 'drupal_ajax']]); - $this->assertResponse(200); - $ajax_commands = Json::decode($response); - $this->assertIdentical('drupalCreateContentType([ + 'type' => 'article', + 'name' => 'Article', + ]); + $node = $this->createNode(['type' => 'article']); + $editor_user = $this->drupalCreateUser([ + 'access content', + 'create article content', + 'edit any article content', + 'access in-place editing' + ]); + $this->drupalLogin($editor_user); + + $custom_render_url = $this->buildUrl('quickedit/form/node/' . $node->id() . '/body/en/quickedit_test-custom-render-data'); + + $client = $this->getHttpClient(); + $post = ['nocssjs' => 'true']; + $response = $client->post($custom_render_url, [ + 'body' => http_build_query($post), + 'query' => [MainContentViewSubscriber::WRAPPER_FORMAT => 'drupal_ajax'], + 'cookies' => $this->getSessionCookies(), + 'headers' => [ + 'Accept' => 'application/json', + 'Content-Type' => 'application/x-www-form-urlencoded', + ], + 'http_errors' => FALSE, + ]); + + $this->assertEquals(200, $response->getStatusCode()); + + $ajax_commands = Json::decode($response->getBody()); + // Request editing to render results with the custom render pipeline. + + // Prepare form values for submission. drupalPostAJAX() is not suitable for + // handling pages with JSON responses, so we need our own solution here. + $form_tokens_found = preg_match('/\sname="form_token" value="([^"]+)"/', $ajax_commands[0]['data'], $token_match) && preg_match('/\sname="form_build_id" value="([^"]+)"/', $ajax_commands[0]['data'], $build_id_match); + $this->assertTrue($form_tokens_found, 'Form tokens found in output.'); + + $post = [ + 'form_id' => 'quickedit_field_form', + 'form_token' => $token_match[1], + 'form_build_id' => $build_id_match[1], + 'body[0][summary]' => '', + 'body[0][value]' => '

Fine thanks.

', + 'body[0][format]' => 'filtered_html', + 'op' => t('Save'), + ]; + // Assume there is another field on this page, which doesn't use a custom + // render pipeline, but the default one, and it uses the "full" view mode. + $post += ['other_view_modes[]' => 'full']; + + // Submit field form and check response. Should render with the custom + // render pipeline. + $response = $client->post($custom_render_url, [ + 'body' => http_build_query($post), + 'query' => [MainContentViewSubscriber::WRAPPER_FORMAT => 'drupal_ajax'], + 'cookies' => $this->getSessionCookies(), + 'headers' => [ + 'Accept' => 'application/json', + 'Content-Type' => 'application/x-www-form-urlencoded', + ], + 'http_errors' => FALSE, + ]); + $ajax_commands = Json::decode($response->getBody()); + $this->assertIdentical(1, count($ajax_commands), 'The field form HTTP request results in one AJAX command.'); + $this->assertIdentical('quickeditFieldFormSaved', $ajax_commands[0]['command'], 'The first AJAX command is a quickeditFieldFormSaved command.'); + $this->assertTrue(strpos($ajax_commands[0]['data'], 'Fine thanks.'), 'Form value saved and printed back.'); + $this->assertTrue(strpos($ajax_commands[0]['data'], '
') !== FALSE, 'Custom render pipeline used to render the value.'); + $this->assertIdentical(array_keys($ajax_commands[0]['other_view_modes']), ['full'], 'Field was also rendered in the "full" view mode.'); + $this->assertTrue(strpos($ajax_commands[0]['other_view_modes']['full'], 'Fine thanks.'), '"full" version of field contains the form value.'); + } + +} diff --git a/core/modules/quickedit/tests/src/FunctionalJavascript/QuickEditAutocompleteTermTest.php b/core/modules/quickedit/tests/src/FunctionalJavascript/QuickEditAutocompleteTermTest.php new file mode 100644 index 0000000000..71b0f3ebec --- /dev/null +++ b/core/modules/quickedit/tests/src/FunctionalJavascript/QuickEditAutocompleteTermTest.php @@ -0,0 +1,206 @@ +drupalCreateContentType([ + 'type' => 'article', + ]); + // Create the vocabulary for the tag field. + $this->vocabulary = Vocabulary::create([ + 'name' => 'quickedit testing tags', + 'vid' => 'quickedit_testing_tags', + ]); + $this->vocabulary->save(); + $this->fieldName = 'field_' . $this->vocabulary->id(); + + $handler_settings = [ + 'target_bundles' => [ + $this->vocabulary->id() => $this->vocabulary->id(), + ], + 'auto_create' => TRUE, + ]; + $this->createEntityReferenceField('node', 'article', $this->fieldName, 'Tags', 'taxonomy_term', 'default', $handler_settings, FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED); + + entity_get_form_display('node', 'article', 'default') + ->setComponent($this->fieldName, [ + 'type' => 'entity_reference_autocomplete_tags', + 'weight' => -4, + ]) + ->save(); + + entity_get_display('node', 'article', 'default') + ->setComponent($this->fieldName, [ + 'type' => 'entity_reference_label', + 'weight' => 10, + ]) + ->save(); + entity_get_display('node', 'article', 'teaser') + ->setComponent($this->fieldName, [ + 'type' => 'entity_reference_label', + 'weight' => 10, + ]) + ->save(); + + $this->term1 = $this->createTerm(); + $this->term2 = $this->createTerm(); + + $node = []; + $node['type'] = 'article'; + $node[$this->fieldName][]['target_id'] = $this->term1->id(); + $node[$this->fieldName][]['target_id'] = $this->term2->id(); + $this->node = $this->drupalCreateNode($node); + + $this->editorUser = $this->drupalCreateUser([ + 'access content', + 'create article content', + 'edit any article content', + 'administer nodes', + 'access contextual links', + 'access in-place editing', + ]); + } + + /** + * Tests Quick Edit autocomplete term behavior. + */ + public function testAutocompleteQuickEdit() { + $page = $this->getSession()->getPage(); + $assert = $this->assertSession(); + + $this->drupalLogin($this->editorUser); + $this->drupalGet('node/' . $this->node->id()); + + // Wait "Quick edit" button for node. + $assert->waitForElement('css', '[data-quickedit-entity-id="node/' . $this->node->id() . '"] .contextual .quickedit'); + // Click by "Quick edit". + $this->clickContextualLink('[data-quickedit-entity-id="node/' . $this->node->id() . '"]', 'Quick edit'); + // Switch to body field. + $page->find('css', '[data-quickedit-field-id="node/' . $this->node->id() . '/' . $this->fieldName . '/' . $this->node->language()->getId() . '/full"]')->click(); + + // Open quickeditor. + $quickedit_field_locator = '[name="field_quickedit_testing_tags[target_id]"]'; + $tag_field = $assert->waitForElementVisible('css', $quickedit_field_locator); + $tag_field->focus(); + $tags = $tag_field->getValue(); + + // Check existing terms. + $this->assertTrue(strpos($tags, $this->term1->label()) !== FALSE); + $this->assertTrue(strpos($tags, $this->term2->label()) !== FALSE); + + // Add new term. + $new_tag = $this->randomMachineName(); + $tags .= ', ' . $new_tag; + $assert->waitForElementVisible('css', $quickedit_field_locator)->setValue($tags); + $assert->waitOnAutocomplete(); + // Wait and click by "Save" button after body field was changed. + $assert->waitForElementVisible('css', '.quickedit-toolgroup.ops [type="submit"][aria-hidden="false"]')->click(); + $assert->waitOnAutocomplete(); + + // Reload the page and check new term. + $this->drupalGet('node/' . $this->node->id()); + $assert->waitForLink($new_tag); + } + + /** + * Returns a new term with random name and description in $this->vocabulary. + * + * @return \Drupal\Core\Entity\EntityInterface|\Drupal\taxonomy\Entity\Term + * The created taxonomy term. + * + * @throws \Drupal\Core\Entity\EntityStorageException + */ + protected function createTerm() { + $filter_formats = filter_formats(); + $format = array_pop($filter_formats); + $term = Term::create([ + 'name' => $this->randomMachineName(), + 'description' => $this->randomMachineName(), + // Use the first available text format. + 'format' => $format->id(), + 'vid' => $this->vocabulary->id(), + 'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED, + ]); + $term->save(); + return $term; + } + +} diff --git a/core/modules/quickedit/tests/src/FunctionalJavascript/QuickEditLoadingTest.php b/core/modules/quickedit/tests/src/FunctionalJavascript/QuickEditLoadingTest.php new file mode 100644 index 0000000000..3ff8b475b3 --- /dev/null +++ b/core/modules/quickedit/tests/src/FunctionalJavascript/QuickEditLoadingTest.php @@ -0,0 +1,378 @@ + 'filtered_html', + 'name' => 'Filtered HTML', + 'weight' => 0, + 'filters' => [], + ]); + $filtered_html_format->save(); + + // Create a node type. + $this->drupalCreateContentType([ + 'type' => 'article', + 'name' => 'Article', + ]); + + // Set the node type to initially not have revisions. + // Testing with revisions will be done later. + $node_type = NodeType::load('article'); + $node_type->setNewRevision(FALSE); + $node_type->save(); + + // Create one node of the above node type using the above text format. + $this->testNode = $this->drupalCreateNode([ + 'type' => 'article', + 'body' => [ + 0 => [ + 'value' => '

How are you?

', + 'format' => 'filtered_html', + ], + ], + 'revision_log' => $this->randomString(), + ]); + + // Create 2 users, the only difference being the ability to use in-place + // editing + $basic_permissions = [ + 'access content', + 'create article content', + 'edit any article content', + 'use text format filtered_html', + 'access contextual links', + ]; + $this->authorUser = $this->drupalCreateUser($basic_permissions); + $this->editorUser = $this->drupalCreateUser(array_merge($basic_permissions, ['access in-place editing'])); + } + + /** + * Test the loading of Quick Edit with different permissions. + */ + public function testUserPermissions() { + $assert = $this->assertSession(); + $this->drupalLogin($this->authorUser); + $this->drupalGet('node/1'); + + // Library and in-place editors. + $this->assertNoRaw('core/modules/quickedit/js/quickedit.js', 'Quick Edit library not loaded.'); + $this->assertNoRaw('core/modules/quickedit/js/editors/formEditor.js', "'form' in-place editor not loaded."); + + // HTML annotation and title class does not exist for users without + // permission to in-place edit. + $this->assertNoRaw('data-quickedit-entity-id="node/1"'); + $this->assertNoRaw('data-quickedit-field-id="node/1/body/en/full"'); + $this->assertNoFieldByXPath('//h1[contains(@class, "js-quickedit-page-title")]'); + $assert->linkNotExists('Quick edit'); + + // Tests the loading of Quick Edit when a user does have access to it. + // Also ensures lazy loading of in-place editors works. + $nid = $this->testNode->id(); + + $this->drupalLogin($this->editorUser); + $this->drupalGet('node/' . $nid); + $page = $this->getSession()->getPage(); + + // Wait "Quick edit" button for node. + $assert->waitForElement('css', '[data-quickedit-entity-id="node/' . $nid . '"] .contextual .quickedit'); + // Click by "Quick edit". + $this->clickContextualLink('[data-quickedit-entity-id="node/' . $nid . '"]', 'Quick edit'); + // Switch to body field. + $page->find('css', '[data-quickedit-field-id="node/' . $nid . '/body/en/full"]')->click(); + $assert->assertWaitOnAjaxRequest(); + + // Wait and update body field. + $body_field_locator = '[name="body[0][value]"]'; + $body_text = 'Fine thanks.'; + $assert->waitForElementVisible('css', $body_field_locator)->setValue('

' . $body_text . '

'); + + // Wait and click by "Save" button after body field was changed. + $assert->waitForElementVisible('css', '.quickedit-toolgroup.ops [type="submit"][aria-hidden="false"]')->click(); + $assert->assertWaitOnAjaxRequest(); + + // Ensure that the changes take effect. + $assert->responseMatches("|\s*$body_text\s*|"); + + // Reload the page and check for updated body. + $this->drupalGet('node/' . $nid); + $assert->pageTextContains($body_text); + } + + /** + * Test quickedit does not appear for entities with pending revisions. + */ + public function testWithPendingRevision() { + $this->drupalLogin($this->editorUser); + + // Verify that the preview is loaded correctly. + $this->drupalPostForm('node/add/article', ['title[0][value]' => 'foo'], 'Preview'); + // Verify that quickedit is not active on preview. + $this->assertNoRaw('data-quickedit-entity-id="node/' . $this->testNode->id() . '"'); + $this->assertNoRaw('data-quickedit-field-id="node/' . $this->testNode->id() . '/title/' . $this->testNode->language()->getId() . '/full"'); + + $this->drupalGet('node/' . $this->testNode->id()); + $this->assertRaw('data-quickedit-entity-id="node/' . $this->testNode->id() . '"'); + $this->assertRaw('data-quickedit-field-id="node/' . $this->testNode->id() . '/title/' . $this->testNode->language()->getId() . '/full"'); + + $this->testNode->title = 'Updated node'; + $this->testNode->setNewRevision(TRUE); + $this->testNode->isDefaultRevision(FALSE); + $this->testNode->save(); + + $this->drupalGet('node/' . $this->testNode->id()); + $this->assertNoRaw('data-quickedit-entity-id="node/' . $this->testNode->id() . '"'); + $this->assertNoRaw('data-quickedit-field-id="node/' . $this->testNode->id() . '/title/' . $this->testNode->language()->getId() . '/full"'); + } + + /** + * Tests the loading of Quick Edit for the title base field. + */ + public function testTitleBaseField() { + $page = $this->getSession()->getPage(); + $assert = $this->assertSession(); + $nid = $this->testNode->id(); + + $this->drupalLogin($this->editorUser); + $this->drupalGet('node/' . $nid); + + // Wait "Quick edit" button for node. + $assert->waitForElement('css', '[data-quickedit-entity-id="node/' . $nid . '"] .contextual .quickedit'); + // Click by "Quick edit". + $this->clickContextualLink('[data-quickedit-entity-id="node/' . $nid . '"]', 'Quick edit'); + // Switch to title field. + $page->find('css', '[data-quickedit-field-id="node/' . $nid . '/title/en/full"]')->click(); + $assert->assertWaitOnAjaxRequest(); + + // Wait and update title field. + $field_locator = '.field--name-title'; + $text_new = 'Obligatory question'; + $assert->waitForElementVisible('css', $field_locator)->setValue($text_new); + + // Wait and click by "Save" button after title field was changed. + $this->assertSession()->waitForElementVisible('css', '.quickedit-toolgroup.ops [type="submit"][aria-hidden="false"]')->click(); + $assert->assertWaitOnAjaxRequest(); + + // Ensure that the changes take effect. + $assert->responseMatches("|\s*$text_new\s*|"); + + // Reload the page and check for updated title. + $this->drupalGet('node/' . $nid); + $assert->pageTextContains($text_new); + } + + /** + * Tests that Quick Edit doesn't make fields rendered with display options + * editable. + */ + public function testDisplayOptions() { + $node = Node::load('1'); + $display_settings = [ + 'label' => 'inline', + ]; + $build = $node->body->view($display_settings); + $output = \Drupal::service('renderer')->renderRoot($build); + $this->assertFalse(strpos($output, 'data-quickedit-field-id'), 'data-quickedit-field-id attribute not added when rendering field using dynamic display options.'); + } + + /** + * Tests Quick Edit on a node that was concurrently edited on the full node + * form. + */ + public function testConcurrentEdit() { + $nid = $this->testNode->id(); + $this->drupalLogin($this->authorUser); + + // Open the edit page in the default session. + $this->drupalGet('node/' . $nid . '/edit'); + + // Switch to a concurrent session and save a quick edit change. + // We need to do some bookkeeping to keep track of the logged in user. + $logged_in_user = $this->loggedInUser; + $this->loggedInUser = FALSE; + // Register a session to preform concurrent editing. + $driver = $this->getDefaultDriverInstance(); + $session = new Session($driver); + $this->mink->registerSession('concurrent', $session); + $this->mink->setDefaultSessionName('concurrent'); + $this->initFrontPage(); + $this->drupalLogin($this->editorUser); + $this->drupalGet('node/' . $nid); + + $assert = $this->assertSession(); + $page = $this->getSession()->getPage(); + + // Wait "Quick edit" button for node. + $assert->waitForElement('css', '[data-quickedit-entity-id="node/' . $nid . '"] .contextual .quickedit'); + // Click by "Quick edit". + $this->clickContextualLink('[data-quickedit-entity-id="node/' . $nid . '"]', 'Quick edit'); + // Switch to body field. + $page->find('css', '[data-quickedit-field-id="node/' . $nid . '/body/en/full"]')->click(); + $assert->assertWaitOnAjaxRequest(); + + // Wait and update body field. + $body_field_locator = '[name="body[0][value]"]'; + $body_text = 'Fine thanks.'; + $assert->waitForElementVisible('css', $body_field_locator)->setValue('

' . $body_text . '

'); + + // Wait and click by "Save" button after body field was changed. + $assert->waitForElementVisible('css', '.quickedit-toolgroup.ops [type="submit"][aria-hidden="false"]')->click(); + $assert->assertWaitOnAjaxRequest(); + + // Ensure that the changes take effect. + $assert->responseMatches("|\s*$body_text\s*|"); + + // Switch back to the default session. + $this->mink->setDefaultSessionName('default'); + $this->loggedInUser = $logged_in_user; + // Ensure different save timestamps for field editing. + sleep(2); + $this->drupalPostForm(NULL, ['body[0][value]' => '

Concurrent edit!

'], 'Save'); + + $this->getSession()->getPage()->hasContent('The content has either been modified by another user, or you have already submitted modifications. As a result, your changes cannot be saved.'); + } + + /** + * Tests that Quick Edit's data- attributes are present for content blocks. + */ + public function testContentBlock() { + \Drupal::service('module_installer')->install(['block_content']); + + // Create and place a content_block block. + $block = BlockContent::create([ + 'info' => $this->randomMachineName(), + 'type' => 'basic', + 'langcode' => 'en', + ]); + $block->save(); + $this->drupalPlaceBlock('block_content:' . $block->uuid()); + + // Check that the data- attribute is present. + $this->drupalLogin($this->editorUser); + $this->drupalGet(''); + $this->assertRaw('data-quickedit-entity-id="block_content/1"'); + } + + /** + * Tests that Quick Edit can handle an image field. + */ + public function testImageField() { + $page = $this->getSession()->getPage(); + $assert = $this->assertSession(); + + // Add an image field to the content type. + FieldStorageConfig::create([ + 'field_name' => 'field_image', + 'type' => 'image', + 'entity_type' => 'node', + ])->save(); + FieldConfig::create([ + 'field_name' => 'field_image', + 'field_type' => 'image', + 'label' => t('Image'), + 'entity_type' => 'node', + 'bundle' => 'article', + ])->save(); + entity_get_form_display('node', 'article', 'default') + ->setComponent('field_image', [ + 'type' => 'image_image', + ]) + ->save(); + $display = EntityViewDisplay::load('node.article.default'); + $display->setComponent('field_image', [ + 'type' => 'image', + ])->save(); + + // Add an image to the node. + $this->drupalLogin($this->editorUser); + $this->drupalGet('node/1/edit'); + $image = $this->drupalGetTestFiles('image')[0]; + $image_path = $this->container->get('file_system')->realpath($image->uri); + $page->attachFileToField('files[field_image_0]', $image_path); + $alt_field = $assert->waitForField('field_image[0][alt]'); + $this->assertNotEmpty($alt_field); + $this->drupalPostForm(NULL, [ + 'field_image[0][alt]' => 'Vivamus aliquet elit', + ], t('Save')); + + // The image field form should load normally. + // Wait "Quick edit" button for node. + $assert->waitForElement('css', '[data-quickedit-entity-id="node/1"] .contextual .quickedit'); + // Click by "Quick edit". + $this->clickContextualLink('[data-quickedit-entity-id="node/1"]', 'Quick edit'); + // Switch to body field. + $assert->waitForElement('css', '[data-quickedit-field-id="node/1/field_image/en/full"]')->click(); + $assert->assertWaitOnAjaxRequest(); + + $field_locator = '.field--name-field-image'; + $assert->waitForElementVisible('css', $field_locator); + } + +}