Chris@0: 'filtered_html', Chris@0: 'name' => 'Filtered HTML', Chris@0: 'weight' => 0, Chris@0: 'filters' => [], Chris@0: ]); Chris@0: $filtered_html_format->save(); Chris@0: Chris@0: // Create a node type. Chris@0: $this->drupalCreateContentType([ Chris@0: 'type' => 'article', Chris@0: 'name' => 'Article', Chris@0: ]); Chris@0: Chris@0: // Set the node type to initially not have revisions. Chris@0: // Testing with revisions will be done later. Chris@0: $node_type = NodeType::load('article'); Chris@0: $node_type->setNewRevision(FALSE); Chris@0: $node_type->save(); Chris@0: Chris@0: // Create one node of the above node type using the above text format. Chris@0: $this->testNode = $this->drupalCreateNode([ Chris@0: 'type' => 'article', Chris@0: 'body' => [ Chris@0: 0 => [ Chris@0: 'value' => '

How are you?

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

Malicious content.

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

Fine thanks.

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

Fine thanks.

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

Fine thanks.

', Chris@0: 'body[0][format]' => 'filtered_html', Chris@0: 'op' => t('Save'), Chris@0: ]; Chris@0: Chris@0: // Save the node on the regular node edit form. Chris@0: $this->drupalPostForm('node/1/edit', [], t('Save')); Chris@0: // Ensure different save timestamps for field editing. Chris@0: sleep(2); Chris@0: Chris@0: // Submit field form and check response. Should throw a validation error Chris@0: // because the node was changed in the meantime. Chris@0: $response = $this->drupalPost('quickedit/form/' . 'node/1/body/en/full', '', $post, ['query' => [MainContentViewSubscriber::WRAPPER_FORMAT => 'drupal_ajax']]); Chris@0: $this->assertResponse(200); Chris@0: $ajax_commands = Json::decode($response); Chris@0: $this->assertIdentical(2, count($ajax_commands), 'The field form HTTP request results in two AJAX commands.'); Chris@0: $this->assertIdentical('quickeditFieldFormValidationErrors', $ajax_commands[1]['command'], 'The second AJAX command is a quickeditFieldFormValidationErrors command.'); Chris@0: $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.'); Chris@0: } Chris@0: } Chris@0: Chris@0: /** Chris@0: * Tests that Quick Edit's data- attributes are present for content blocks. Chris@0: */ Chris@0: public function testContentBlock() { Chris@0: \Drupal::service('module_installer')->install(['block_content']); Chris@0: Chris@0: // Create and place a content_block block. Chris@0: $block = BlockContent::create([ Chris@0: 'info' => $this->randomMachineName(), Chris@0: 'type' => 'basic', Chris@0: 'langcode' => 'en', Chris@0: ]); Chris@0: $block->save(); Chris@0: $this->drupalPlaceBlock('block_content:' . $block->uuid()); Chris@0: Chris@0: // Check that the data- attribute is present. Chris@0: $this->drupalLogin($this->editorUser); Chris@0: $this->drupalGet(''); Chris@0: $this->assertRaw('data-quickedit-entity-id="block_content/1"'); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Tests that Quick Edit can handle an image field. Chris@0: */ Chris@0: public function testImageField() { Chris@0: // Add an image field to the content type. Chris@0: FieldStorageConfig::create([ Chris@0: 'field_name' => 'field_image', Chris@0: 'type' => 'image', Chris@0: 'entity_type' => 'node', Chris@0: ])->save(); Chris@0: FieldConfig::create([ Chris@0: 'field_name' => 'field_image', Chris@0: 'field_type' => 'image', Chris@0: 'label' => t('Image'), Chris@0: 'entity_type' => 'node', Chris@0: 'bundle' => 'article', Chris@0: ])->save(); Chris@0: entity_get_form_display('node', 'article', 'default') Chris@0: ->setComponent('field_image', [ Chris@0: 'type' => 'image_image', Chris@0: ]) Chris@0: ->save(); Chris@0: Chris@0: // Add an image to the node. Chris@0: $this->drupalLogin($this->editorUser); Chris@0: $image = $this->drupalGetTestFiles('image')[0]; Chris@0: $this->drupalPostForm('node/1/edit', [ Chris@0: 'files[field_image_0]' => $image->uri, Chris@0: ], t('Upload')); Chris@0: $this->drupalPostForm(NULL, [ Chris@0: 'field_image[0][alt]' => 'Vivamus aliquet elit', Chris@0: ], t('Save')); Chris@0: Chris@0: // The image field form should load normally. Chris@0: $response = $this->drupalPost('quickedit/form/node/1/field_image/en/full', '', ['nocssjs' => 'true'] + $this->getAjaxPageStatePostData(), ['query' => [MainContentViewSubscriber::WRAPPER_FORMAT => 'drupal_ajax']]); Chris@0: $this->assertResponse(200); Chris@0: $ajax_commands = Json::decode($response); Chris@0: $this->assertIdentical('