Mercurial > hg > isophonics-drupal-site
comparison core/modules/quickedit/src/Tests/QuickEditLoadingTest.php @ 0:4c8ae668cc8c
Initial import (non-working)
author | Chris Cannam |
---|---|
date | Wed, 29 Nov 2017 16:09:58 +0000 |
parents | |
children | 7a779792577d |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:4c8ae668cc8c |
---|---|
1 <?php | |
2 | |
3 namespace Drupal\quickedit\Tests; | |
4 | |
5 use Drupal\Component\Serialization\Json; | |
6 use Drupal\Component\Utility\Unicode; | |
7 use Drupal\block_content\Entity\BlockContent; | |
8 use Drupal\field\Entity\FieldConfig; | |
9 use Drupal\field\Entity\FieldStorageConfig; | |
10 use Drupal\Core\EventSubscriber\MainContentViewSubscriber; | |
11 use Drupal\Core\Url; | |
12 use Drupal\node\Entity\Node; | |
13 use Drupal\node\Entity\NodeType; | |
14 use Drupal\simpletest\WebTestBase; | |
15 use Drupal\filter\Entity\FilterFormat; | |
16 | |
17 /** | |
18 * Tests loading of in-place editing functionality and lazy loading of its | |
19 * in-place editors. | |
20 * | |
21 * @group quickedit | |
22 */ | |
23 class QuickEditLoadingTest extends WebTestBase { | |
24 | |
25 /** | |
26 * Modules to enable. | |
27 * | |
28 * @var array | |
29 */ | |
30 public static $modules = [ | |
31 'contextual', | |
32 'quickedit', | |
33 'filter', | |
34 'node', | |
35 'image', | |
36 ]; | |
37 | |
38 /** | |
39 * An user with permissions to create and edit articles. | |
40 * | |
41 * @var \Drupal\user\UserInterface | |
42 */ | |
43 protected $authorUser; | |
44 | |
45 /** | |
46 * A test node. | |
47 * | |
48 * @var \Drupal\node\NodeInterface | |
49 */ | |
50 protected $testNode; | |
51 | |
52 /** | |
53 * A author user with permissions to access in-place editor. | |
54 * | |
55 * @var \Drupal\user\UserInterface | |
56 */ | |
57 protected $editorUser; | |
58 | |
59 protected function setUp() { | |
60 parent::setUp(); | |
61 | |
62 // Create a text format. | |
63 $filtered_html_format = FilterFormat::create([ | |
64 'format' => 'filtered_html', | |
65 'name' => 'Filtered HTML', | |
66 'weight' => 0, | |
67 'filters' => [], | |
68 ]); | |
69 $filtered_html_format->save(); | |
70 | |
71 // Create a node type. | |
72 $this->drupalCreateContentType([ | |
73 'type' => 'article', | |
74 'name' => 'Article', | |
75 ]); | |
76 | |
77 // Set the node type to initially not have revisions. | |
78 // Testing with revisions will be done later. | |
79 $node_type = NodeType::load('article'); | |
80 $node_type->setNewRevision(FALSE); | |
81 $node_type->save(); | |
82 | |
83 // Create one node of the above node type using the above text format. | |
84 $this->testNode = $this->drupalCreateNode([ | |
85 'type' => 'article', | |
86 'body' => [ | |
87 0 => [ | |
88 'value' => '<p>How are you?</p>', | |
89 'format' => 'filtered_html', | |
90 ] | |
91 ], | |
92 'revision_log' => $this->randomString(), | |
93 ]); | |
94 | |
95 // Create 2 users, the only difference being the ability to use in-place | |
96 // editing | |
97 $basic_permissions = ['access content', 'create article content', 'edit any article content', 'use text format filtered_html', 'access contextual links']; | |
98 $this->authorUser = $this->drupalCreateUser($basic_permissions); | |
99 $this->editorUser = $this->drupalCreateUser(array_merge($basic_permissions, ['access in-place editing'])); | |
100 } | |
101 | |
102 /** | |
103 * Test the loading of Quick Edit when a user doesn't have access to it. | |
104 */ | |
105 public function testUserWithoutPermission() { | |
106 $this->drupalLogin($this->authorUser); | |
107 $this->drupalGet('node/1'); | |
108 | |
109 // Library and in-place editors. | |
110 $this->assertNoRaw('core/modules/quickedit/js/quickedit.js', 'Quick Edit library not loaded.'); | |
111 $this->assertNoRaw('core/modules/quickedit/js/editors/formEditor.js', "'form' in-place editor not loaded."); | |
112 | |
113 // HTML annotation and title class does not exist for users without | |
114 // permission to in-place edit. | |
115 $this->assertNoRaw('data-quickedit-entity-id="node/1"'); | |
116 $this->assertNoRaw('data-quickedit-field-id="node/1/body/en/full"'); | |
117 $this->assertNoFieldByXPath('//h1[contains(@class, "js-quickedit-page-title")]'); | |
118 | |
119 // Retrieving the metadata should result in an empty 403 response. | |
120 $post = ['fields[0]' => 'node/1/body/en/full']; | |
121 $response = $this->drupalPostWithFormat(Url::fromRoute('quickedit.metadata'), 'json', $post); | |
122 $this->assertIdentical(Json::encode(['message' => "The 'access in-place editing' permission is required."]), $response); | |
123 $this->assertResponse(403); | |
124 | |
125 // Quick Edit's JavaScript would never hit these endpoints if the metadata | |
126 // was empty as above, but we need to make sure that malicious users aren't | |
127 // able to use any of the other endpoints either. | |
128 $post = ['editors[0]' => 'form'] + $this->getAjaxPageStatePostData(); | |
129 $response = $this->drupalPost('quickedit/attachments', '', $post, ['query' => [MainContentViewSubscriber::WRAPPER_FORMAT => 'drupal_ajax']]); | |
130 $message = Json::encode(['message' => "The 'access in-place editing' permission is required."]); | |
131 $this->assertIdentical($message, $response); | |
132 $this->assertResponse(403); | |
133 $post = ['nocssjs' => 'true'] + $this->getAjaxPageStatePostData(); | |
134 $response = $this->drupalPost('quickedit/form/' . 'node/1/body/en/full', '', $post, ['query' => [MainContentViewSubscriber::WRAPPER_FORMAT => 'drupal_ajax']]); | |
135 $this->assertIdentical($message, $response); | |
136 $this->assertResponse(403); | |
137 $edit = []; | |
138 $edit['form_id'] = 'quickedit_field_form'; | |
139 $edit['form_token'] = 'xIOzMjuc-PULKsRn_KxFn7xzNk5Bx7XKXLfQfw1qOnA'; | |
140 $edit['form_build_id'] = 'form-kVmovBpyX-SJfTT5kY0pjTV35TV-znor--a64dEnMR8'; | |
141 $edit['body[0][summary]'] = ''; | |
142 $edit['body[0][value]'] = '<p>Malicious content.</p>'; | |
143 $edit['body[0][format]'] = 'filtered_html'; | |
144 $edit['op'] = t('Save'); | |
145 $response = $this->drupalPost('quickedit/form/' . 'node/1/body/en/full', '', $edit, ['query' => [MainContentViewSubscriber::WRAPPER_FORMAT => 'drupal_ajax']]); | |
146 $this->assertIdentical($message, $response); | |
147 $this->assertResponse(403); | |
148 $post = ['nocssjs' => 'true']; | |
149 $response = $this->drupalPostWithFormat('quickedit/entity/' . 'node/1', 'json', $post); | |
150 $this->assertIdentical(Json::encode(['message' => "The 'access in-place editing' permission is required."]), $response); | |
151 $this->assertResponse(403); | |
152 } | |
153 | |
154 /** | |
155 * Tests the loading of Quick Edit when a user does have access to it. | |
156 * | |
157 * Also ensures lazy loading of in-place editors works. | |
158 */ | |
159 public function testUserWithPermission() { | |
160 $this->drupalLogin($this->editorUser); | |
161 $this->drupalGet('node/1'); | |
162 | |
163 // Library and in-place editors. | |
164 $settings = $this->getDrupalSettings(); | |
165 $libraries = explode(',', $settings['ajaxPageState']['libraries']); | |
166 $this->assertTrue(in_array('quickedit/quickedit', $libraries), 'Quick Edit library loaded.'); | |
167 $this->assertFalse(in_array('quickedit/quickedit.inPlaceEditor.form', $libraries), "'form' in-place editor not loaded."); | |
168 | |
169 // HTML annotation and title class must always exist (to not break the | |
170 // render cache). | |
171 $this->assertRaw('data-quickedit-entity-id="node/1"'); | |
172 $this->assertRaw('data-quickedit-field-id="node/1/body/en/full"'); | |
173 $this->assertFieldByXPath('//h1[contains(@class, "js-quickedit-page-title")]'); | |
174 | |
175 // There should be only one revision so far. | |
176 $node = Node::load(1); | |
177 $vids = \Drupal::entityManager()->getStorage('node')->revisionIds($node); | |
178 $this->assertIdentical(1, count($vids), 'The node has only one revision.'); | |
179 $original_log = $node->revision_log->value; | |
180 | |
181 // Retrieving the metadata should result in a 200 JSON response. | |
182 $htmlPageDrupalSettings = $this->drupalSettings; | |
183 $post = ['fields[0]' => 'node/1/body/en/full']; | |
184 $response = $this->drupalPostWithFormat('quickedit/metadata', 'json', $post); | |
185 $this->assertResponse(200); | |
186 $expected = [ | |
187 'node/1/body/en/full' => [ | |
188 'label' => 'Body', | |
189 'access' => TRUE, | |
190 'editor' => 'form', | |
191 ] | |
192 ]; | |
193 $this->assertIdentical(Json::decode($response), $expected, 'The metadata HTTP request answers with the correct JSON response.'); | |
194 // Restore drupalSettings to build the next requests; simpletest wipes them | |
195 // after a JSON response. | |
196 $this->drupalSettings = $htmlPageDrupalSettings; | |
197 | |
198 // Retrieving the attachments should result in a 200 response, containing: | |
199 // 1. a settings command with useless metadata: AjaxController is dumb | |
200 // 2. an insert command that loads the required in-place editors | |
201 $post = ['editors[0]' => 'form'] + $this->getAjaxPageStatePostData(); | |
202 $response = $this->drupalPost('quickedit/attachments', '', $post, ['query' => [MainContentViewSubscriber::WRAPPER_FORMAT => 'drupal_ajax']]); | |
203 $ajax_commands = Json::decode($response); | |
204 $this->assertIdentical(2, count($ajax_commands), 'The attachments HTTP request results in two AJAX commands.'); | |
205 // First command: settings. | |
206 $this->assertIdentical('settings', $ajax_commands[0]['command'], 'The first AJAX command is a settings command.'); | |
207 // Second command: insert libraries into DOM. | |
208 $this->assertIdentical('insert', $ajax_commands[1]['command'], 'The second AJAX command is an append command.'); | |
209 $this->assertTrue(in_array('quickedit/quickedit.inPlaceEditor.form', explode(',', $ajax_commands[0]['settings']['ajaxPageState']['libraries'])), 'The quickedit.inPlaceEditor.form library is loaded.'); | |
210 | |
211 // Retrieving the form for this field should result in a 200 response, | |
212 // containing only a quickeditFieldForm command. | |
213 $post = ['nocssjs' => 'true', 'reset' => 'true'] + $this->getAjaxPageStatePostData(); | |
214 $response = $this->drupalPost('quickedit/form/' . 'node/1/body/en/full', '', $post, ['query' => [MainContentViewSubscriber::WRAPPER_FORMAT => 'drupal_ajax']]); | |
215 $this->assertResponse(200); | |
216 $ajax_commands = Json::decode($response); | |
217 $this->assertIdentical(1, count($ajax_commands), 'The field form HTTP request results in one AJAX command.'); | |
218 $this->assertIdentical('quickeditFieldForm', $ajax_commands[0]['command'], 'The first AJAX command is a quickeditFieldForm command.'); | |
219 $this->assertIdentical('<form ', Unicode::substr($ajax_commands[0]['data'], 0, 6), 'The quickeditFieldForm command contains a form.'); | |
220 | |
221 // Prepare form values for submission. drupalPostAjaxForm() is not suitable | |
222 // for handling pages with JSON responses, so we need our own solution here. | |
223 $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); | |
224 $this->assertTrue($form_tokens_found, 'Form tokens found in output.'); | |
225 | |
226 if ($form_tokens_found) { | |
227 $edit = [ | |
228 'body[0][summary]' => '', | |
229 'body[0][value]' => '<p>Fine thanks.</p>', | |
230 'body[0][format]' => 'filtered_html', | |
231 'op' => t('Save'), | |
232 ]; | |
233 $post = [ | |
234 'form_id' => 'quickedit_field_form', | |
235 'form_token' => $token_match[1], | |
236 'form_build_id' => $build_id_match[1], | |
237 ]; | |
238 $post += $edit + $this->getAjaxPageStatePostData(); | |
239 | |
240 // Submit field form and check response. This should store the updated | |
241 // entity in PrivateTempStore on the server. | |
242 $response = $this->drupalPost('quickedit/form/' . 'node/1/body/en/full', '', $post, ['query' => [MainContentViewSubscriber::WRAPPER_FORMAT => 'drupal_ajax']]); | |
243 $this->assertResponse(200); | |
244 $ajax_commands = Json::decode($response); | |
245 $this->assertIdentical(1, count($ajax_commands), 'The field form HTTP request results in one AJAX command.'); | |
246 $this->assertIdentical('quickeditFieldFormSaved', $ajax_commands[0]['command'], 'The first AJAX command is a quickeditFieldFormSaved command.'); | |
247 $this->assertTrue(strpos($ajax_commands[0]['data'], 'Fine thanks.'), 'Form value saved and printed back.'); | |
248 $this->assertIdentical($ajax_commands[0]['other_view_modes'], [], 'Field was not rendered in any other view mode.'); | |
249 | |
250 // Ensure the text on the original node did not change yet. | |
251 $this->drupalGet('node/1'); | |
252 $this->assertText('How are you?'); | |
253 | |
254 // Save the entity by moving the PrivateTempStore values to entity storage. | |
255 $post = ['nocssjs' => 'true']; | |
256 $response = $this->drupalPostWithFormat('quickedit/entity/' . 'node/1', 'json', $post); | |
257 $this->assertResponse(200); | |
258 $ajax_commands = Json::decode($response); | |
259 $this->assertIdentical(1, count($ajax_commands), 'The entity submission HTTP request results in one AJAX command.'); | |
260 $this->assertIdentical('quickeditEntitySaved', $ajax_commands[0]['command'], 'The first AJAX command is a quickeditEntitySaved command.'); | |
261 $this->assertIdentical($ajax_commands[0]['data']['entity_type'], 'node', 'Saved entity is of type node.'); | |
262 $this->assertIdentical($ajax_commands[0]['data']['entity_id'], '1', 'Entity id is 1.'); | |
263 | |
264 // Ensure the text on the original node did change. | |
265 $this->drupalGet('node/1'); | |
266 $this->assertText('Fine thanks.'); | |
267 | |
268 // Ensure no new revision was created and the log message is unchanged. | |
269 $node = Node::load(1); | |
270 $vids = \Drupal::entityManager()->getStorage('node')->revisionIds($node); | |
271 $this->assertIdentical(1, count($vids), 'The node has only one revision.'); | |
272 $this->assertIdentical($original_log, $node->revision_log->value, 'The revision log message is unchanged.'); | |
273 | |
274 // Now configure this node type to create new revisions automatically, | |
275 // then again retrieve the field form, fill it, submit it (so it ends up | |
276 // in PrivateTempStore) and then save the entity. Now there should be two | |
277 // revisions. | |
278 $node_type = NodeType::load('article'); | |
279 $node_type->setNewRevision(TRUE); | |
280 $node_type->save(); | |
281 | |
282 // Retrieve field form. | |
283 $post = ['nocssjs' => 'true', 'reset' => 'true']; | |
284 $response = $this->drupalPost('quickedit/form/' . 'node/1/body/en/full', '', $post, ['query' => [MainContentViewSubscriber::WRAPPER_FORMAT => 'drupal_ajax']]); | |
285 $this->assertResponse(200); | |
286 $ajax_commands = Json::decode($response); | |
287 $this->assertIdentical(1, count($ajax_commands), 'The field form HTTP request results in one AJAX command.'); | |
288 $this->assertIdentical('quickeditFieldForm', $ajax_commands[0]['command'], 'The first AJAX command is a quickeditFieldForm command.'); | |
289 $this->assertIdentical('<form ', Unicode::substr($ajax_commands[0]['data'], 0, 6), 'The quickeditFieldForm command contains a form.'); | |
290 | |
291 // Submit field form. | |
292 preg_match('/\sname="form_token" value="([^"]+)"/', $ajax_commands[0]['data'], $token_match); | |
293 preg_match('/\sname="form_build_id" value="([^"]+)"/', $ajax_commands[0]['data'], $build_id_match); | |
294 $edit['body[0][value]'] = '<p>kthxbye</p>'; | |
295 $post = [ | |
296 'form_id' => 'quickedit_field_form', | |
297 'form_token' => $token_match[1], | |
298 'form_build_id' => $build_id_match[1], | |
299 ]; | |
300 $post += $edit + $this->getAjaxPageStatePostData(); | |
301 $response = $this->drupalPost('quickedit/form/' . 'node/1/body/en/full', '', $post, ['query' => [MainContentViewSubscriber::WRAPPER_FORMAT => 'drupal_ajax']]); | |
302 $this->assertResponse(200); | |
303 $ajax_commands = Json::decode($response); | |
304 $this->assertIdentical(1, count($ajax_commands), 'The field form HTTP request results in one AJAX command.'); | |
305 $this->assertIdentical('quickeditFieldFormSaved', $ajax_commands[0]['command'], 'The first AJAX command is an quickeditFieldFormSaved command.'); | |
306 $this->assertTrue(strpos($ajax_commands[0]['data'], 'kthxbye'), 'Form value saved and printed back.'); | |
307 | |
308 // Save the entity. | |
309 $post = ['nocssjs' => 'true']; | |
310 $response = $this->drupalPostWithFormat('quickedit/entity/' . 'node/1', 'json', $post); | |
311 $this->assertResponse(200); | |
312 $ajax_commands = Json::decode($response); | |
313 $this->assertIdentical(1, count($ajax_commands)); | |
314 $this->assertIdentical('quickeditEntitySaved', $ajax_commands[0]['command'], 'The first AJAX command is an quickeditEntitySaved command.'); | |
315 $this->assertEqual($ajax_commands[0]['data'], ['entity_type' => 'node', 'entity_id' => 1], 'Updated entity type and ID returned'); | |
316 | |
317 // Test that a revision was created with the correct log message. | |
318 $vids = \Drupal::entityManager()->getStorage('node')->revisionIds(Node::load(1)); | |
319 $this->assertIdentical(2, count($vids), 'The node has two revisions.'); | |
320 $revision = node_revision_load($vids[0]); | |
321 $this->assertIdentical($original_log, $revision->revision_log->value, 'The first revision log message is unchanged.'); | |
322 $revision = node_revision_load($vids[1]); | |
323 $this->assertIdentical('Updated the <em class="placeholder">Body</em> field through in-place editing.', $revision->revision_log->value, 'The second revision log message was correctly generated by Quick Edit module.'); | |
324 } | |
325 } | |
326 | |
327 /** | |
328 * Test quickedit does not appear for entities with pending revisions. | |
329 */ | |
330 public function testWithPendingRevision() { | |
331 $this->drupalLogin($this->editorUser); | |
332 | |
333 $this->drupalGet('node/' . $this->testNode->id()); | |
334 $this->assertRaw('data-quickedit-entity-id="node/' . $this->testNode->id() . '"'); | |
335 $this->assertRaw('data-quickedit-field-id="node/' . $this->testNode->id() . '/title/' . $this->testNode->language()->getId() . '/full"'); | |
336 | |
337 $this->testNode->title = 'Updated node'; | |
338 $this->testNode->setNewRevision(TRUE); | |
339 $this->testNode->isDefaultRevision(FALSE); | |
340 $this->testNode->save(); | |
341 | |
342 $this->drupalGet('node/' . $this->testNode->id()); | |
343 $this->assertNoRaw('data-quickedit-entity-id="node/' . $this->testNode->id() . '"'); | |
344 $this->assertNoRaw('data-quickedit-field-id="node/' . $this->testNode->id() . '/title/' . $this->testNode->language()->getId() . '/full"'); | |
345 } | |
346 | |
347 /** | |
348 * Tests the loading of Quick Edit for the title base field. | |
349 */ | |
350 public function testTitleBaseField() { | |
351 $this->drupalLogin($this->editorUser); | |
352 $this->drupalGet('node/1'); | |
353 | |
354 // Ensure that the full page title is actually in-place editable | |
355 $node = Node::load(1); | |
356 $elements = $this->xpath('//h1/span[@data-quickedit-field-id="node/1/title/en/full" and normalize-space(text())=:title]', [':title' => $node->label()]); | |
357 $this->assertTrue(!empty($elements), 'Title with data-quickedit-field-id attribute found.'); | |
358 | |
359 // Retrieving the metadata should result in a 200 JSON response. | |
360 $htmlPageDrupalSettings = $this->drupalSettings; | |
361 $post = ['fields[0]' => 'node/1/title/en/full']; | |
362 $response = $this->drupalPostWithFormat('quickedit/metadata', 'json', $post); | |
363 $this->assertResponse(200); | |
364 $expected = [ | |
365 'node/1/title/en/full' => [ | |
366 'label' => 'Title', | |
367 'access' => TRUE, | |
368 'editor' => 'plain_text', | |
369 ] | |
370 ]; | |
371 $this->assertIdentical(Json::decode($response), $expected, 'The metadata HTTP request answers with the correct JSON response.'); | |
372 // Restore drupalSettings to build the next requests; simpletest wipes them | |
373 // after a JSON response. | |
374 $this->drupalSettings = $htmlPageDrupalSettings; | |
375 | |
376 // Retrieving the form for this field should result in a 200 response, | |
377 // containing only a quickeditFieldForm command. | |
378 $post = ['nocssjs' => 'true', 'reset' => 'true'] + $this->getAjaxPageStatePostData(); | |
379 $response = $this->drupalPost('quickedit/form/' . 'node/1/title/en/full', '', $post, ['query' => [MainContentViewSubscriber::WRAPPER_FORMAT => 'drupal_ajax']]); | |
380 $this->assertResponse(200); | |
381 $ajax_commands = Json::decode($response); | |
382 $this->assertIdentical(1, count($ajax_commands), 'The field form HTTP request results in one AJAX command.'); | |
383 $this->assertIdentical('quickeditFieldForm', $ajax_commands[0]['command'], 'The first AJAX command is a quickeditFieldForm command.'); | |
384 $this->assertIdentical('<form ', Unicode::substr($ajax_commands[0]['data'], 0, 6), 'The quickeditFieldForm command contains a form.'); | |
385 | |
386 // Prepare form values for submission. drupalPostAjaxForm() is not suitable | |
387 // for handling pages with JSON responses, so we need our own solution | |
388 // here. | |
389 $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); | |
390 $this->assertTrue($form_tokens_found, 'Form tokens found in output.'); | |
391 | |
392 if ($form_tokens_found) { | |
393 $edit = [ | |
394 'title[0][value]' => 'Obligatory question', | |
395 'op' => t('Save'), | |
396 ]; | |
397 $post = [ | |
398 'form_id' => 'quickedit_field_form', | |
399 'form_token' => $token_match[1], | |
400 'form_build_id' => $build_id_match[1], | |
401 ]; | |
402 $post += $edit + $this->getAjaxPageStatePostData(); | |
403 | |
404 // Submit field form and check response. This should store the | |
405 // updated entity in PrivateTempStore on the server. | |
406 $response = $this->drupalPost('quickedit/form/' . 'node/1/title/en/full', '', $post, ['query' => [MainContentViewSubscriber::WRAPPER_FORMAT => 'drupal_ajax']]); | |
407 $this->assertResponse(200); | |
408 $ajax_commands = Json::decode($response); | |
409 $this->assertIdentical(1, count($ajax_commands), 'The field form HTTP request results in one AJAX command.'); | |
410 $this->assertIdentical('quickeditFieldFormSaved', $ajax_commands[0]['command'], 'The first AJAX command is a quickeditFieldFormSaved command.'); | |
411 $this->assertTrue(strpos($ajax_commands[0]['data'], 'Obligatory question'), 'Form value saved and printed back.'); | |
412 | |
413 // Ensure the text on the original node did not change yet. | |
414 $this->drupalGet('node/1'); | |
415 $this->assertNoText('Obligatory question'); | |
416 | |
417 // Save the entity by moving the PrivateTempStore values to entity storage. | |
418 $post = ['nocssjs' => 'true']; | |
419 $response = $this->drupalPostWithFormat('quickedit/entity/' . 'node/1', 'json', $post); | |
420 $this->assertResponse(200); | |
421 $ajax_commands = Json::decode($response); | |
422 $this->assertIdentical(1, count($ajax_commands), 'The entity submission HTTP request results in one AJAX command.'); | |
423 $this->assertIdentical('quickeditEntitySaved', $ajax_commands[0]['command'], 'The first AJAX command is n quickeditEntitySaved command.'); | |
424 $this->assertIdentical($ajax_commands[0]['data']['entity_type'], 'node', 'Saved entity is of type node.'); | |
425 $this->assertIdentical($ajax_commands[0]['data']['entity_id'], '1', 'Entity id is 1.'); | |
426 | |
427 // Ensure the text on the original node did change. | |
428 $this->drupalGet('node/1'); | |
429 $this->assertText('Obligatory question'); | |
430 } | |
431 } | |
432 | |
433 /** | |
434 * Tests that Quick Edit doesn't make fields rendered with display options | |
435 * editable. | |
436 */ | |
437 public function testDisplayOptions() { | |
438 $node = Node::load('1'); | |
439 $display_settings = [ | |
440 'label' => 'inline', | |
441 ]; | |
442 $build = $node->body->view($display_settings); | |
443 $output = \Drupal::service('renderer')->renderRoot($build); | |
444 $this->assertFalse(strpos($output, 'data-quickedit-field-id'), 'data-quickedit-field-id attribute not added when rendering field using dynamic display options.'); | |
445 } | |
446 | |
447 /** | |
448 * Tests that Quick Edit works with custom render pipelines. | |
449 */ | |
450 public function testCustomPipeline() { | |
451 \Drupal::service('module_installer')->install(['quickedit_test']); | |
452 | |
453 $custom_render_url = 'quickedit/form/node/1/body/en/quickedit_test-custom-render-data'; | |
454 $this->drupalLogin($this->editorUser); | |
455 | |
456 // Request editing to render results with the custom render pipeline. | |
457 $post = ['nocssjs' => 'true'] + $this->getAjaxPageStatePostData(); | |
458 $response = $this->drupalPost($custom_render_url, '', $post, ['query' => [MainContentViewSubscriber::WRAPPER_FORMAT => 'drupal_ajax']]); | |
459 $ajax_commands = Json::decode($response); | |
460 | |
461 // Prepare form values for submission. drupalPostAJAX() is not suitable for | |
462 // handling pages with JSON responses, so we need our own solution here. | |
463 $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); | |
464 $this->assertTrue($form_tokens_found, 'Form tokens found in output.'); | |
465 | |
466 if ($form_tokens_found) { | |
467 $post = [ | |
468 'form_id' => 'quickedit_field_form', | |
469 'form_token' => $token_match[1], | |
470 'form_build_id' => $build_id_match[1], | |
471 'body[0][summary]' => '', | |
472 'body[0][value]' => '<p>Fine thanks.</p>', | |
473 'body[0][format]' => 'filtered_html', | |
474 'op' => t('Save'), | |
475 ]; | |
476 // Assume there is another field on this page, which doesn't use a custom | |
477 // render pipeline, but the default one, and it uses the "full" view mode. | |
478 $post += ['other_view_modes[]' => 'full']; | |
479 | |
480 // Submit field form and check response. Should render with the custom | |
481 // render pipeline. | |
482 $response = $this->drupalPost($custom_render_url, '', $post, ['query' => [MainContentViewSubscriber::WRAPPER_FORMAT => 'drupal_ajax']]); | |
483 $this->assertResponse(200); | |
484 $ajax_commands = Json::decode($response); | |
485 $this->assertIdentical(1, count($ajax_commands), 'The field form HTTP request results in one AJAX command.'); | |
486 $this->assertIdentical('quickeditFieldFormSaved', $ajax_commands[0]['command'], 'The first AJAX command is a quickeditFieldFormSaved command.'); | |
487 $this->assertTrue(strpos($ajax_commands[0]['data'], 'Fine thanks.'), 'Form value saved and printed back.'); | |
488 $this->assertTrue(strpos($ajax_commands[0]['data'], '<div class="quickedit-test-wrapper">') !== FALSE, 'Custom render pipeline used to render the value.'); | |
489 $this->assertIdentical(array_keys($ajax_commands[0]['other_view_modes']), ['full'], 'Field was also rendered in the "full" view mode.'); | |
490 $this->assertTrue(strpos($ajax_commands[0]['other_view_modes']['full'], 'Fine thanks.'), '"full" version of field contains the form value.'); | |
491 } | |
492 } | |
493 | |
494 /** | |
495 * Tests Quick Edit on a node that was concurrently edited on the full node | |
496 * form. | |
497 */ | |
498 public function testConcurrentEdit() { | |
499 $this->drupalLogin($this->editorUser); | |
500 | |
501 $post = ['nocssjs' => 'true'] + $this->getAjaxPageStatePostData(); | |
502 $response = $this->drupalPost('quickedit/form/' . 'node/1/body/en/full', '', $post, ['query' => [MainContentViewSubscriber::WRAPPER_FORMAT => 'drupal_ajax']]); | |
503 $this->assertResponse(200); | |
504 $ajax_commands = Json::decode($response); | |
505 | |
506 // Prepare form values for submission. drupalPostAJAX() is not suitable for | |
507 // handling pages with JSON responses, so we need our own solution here. | |
508 $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); | |
509 $this->assertTrue($form_tokens_found, 'Form tokens found in output.'); | |
510 | |
511 if ($form_tokens_found) { | |
512 $post = [ | |
513 'nocssjs' => 'true', | |
514 'form_id' => 'quickedit_field_form', | |
515 'form_token' => $token_match[1], | |
516 'form_build_id' => $build_id_match[1], | |
517 'body[0][summary]' => '', | |
518 'body[0][value]' => '<p>Fine thanks.</p>', | |
519 'body[0][format]' => 'filtered_html', | |
520 'op' => t('Save'), | |
521 ]; | |
522 | |
523 // Save the node on the regular node edit form. | |
524 $this->drupalPostForm('node/1/edit', [], t('Save')); | |
525 // Ensure different save timestamps for field editing. | |
526 sleep(2); | |
527 | |
528 // Submit field form and check response. Should throw a validation error | |
529 // because the node was changed in the meantime. | |
530 $response = $this->drupalPost('quickedit/form/' . 'node/1/body/en/full', '', $post, ['query' => [MainContentViewSubscriber::WRAPPER_FORMAT => 'drupal_ajax']]); | |
531 $this->assertResponse(200); | |
532 $ajax_commands = Json::decode($response); | |
533 $this->assertIdentical(2, count($ajax_commands), 'The field form HTTP request results in two AJAX commands.'); | |
534 $this->assertIdentical('quickeditFieldFormValidationErrors', $ajax_commands[1]['command'], 'The second AJAX command is a quickeditFieldFormValidationErrors command.'); | |
535 $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.'); | |
536 } | |
537 } | |
538 | |
539 /** | |
540 * Tests that Quick Edit's data- attributes are present for content blocks. | |
541 */ | |
542 public function testContentBlock() { | |
543 \Drupal::service('module_installer')->install(['block_content']); | |
544 | |
545 // Create and place a content_block block. | |
546 $block = BlockContent::create([ | |
547 'info' => $this->randomMachineName(), | |
548 'type' => 'basic', | |
549 'langcode' => 'en', | |
550 ]); | |
551 $block->save(); | |
552 $this->drupalPlaceBlock('block_content:' . $block->uuid()); | |
553 | |
554 // Check that the data- attribute is present. | |
555 $this->drupalLogin($this->editorUser); | |
556 $this->drupalGet(''); | |
557 $this->assertRaw('data-quickedit-entity-id="block_content/1"'); | |
558 } | |
559 | |
560 /** | |
561 * Tests that Quick Edit can handle an image field. | |
562 */ | |
563 public function testImageField() { | |
564 // Add an image field to the content type. | |
565 FieldStorageConfig::create([ | |
566 'field_name' => 'field_image', | |
567 'type' => 'image', | |
568 'entity_type' => 'node', | |
569 ])->save(); | |
570 FieldConfig::create([ | |
571 'field_name' => 'field_image', | |
572 'field_type' => 'image', | |
573 'label' => t('Image'), | |
574 'entity_type' => 'node', | |
575 'bundle' => 'article', | |
576 ])->save(); | |
577 entity_get_form_display('node', 'article', 'default') | |
578 ->setComponent('field_image', [ | |
579 'type' => 'image_image', | |
580 ]) | |
581 ->save(); | |
582 | |
583 // Add an image to the node. | |
584 $this->drupalLogin($this->editorUser); | |
585 $image = $this->drupalGetTestFiles('image')[0]; | |
586 $this->drupalPostForm('node/1/edit', [ | |
587 'files[field_image_0]' => $image->uri, | |
588 ], t('Upload')); | |
589 $this->drupalPostForm(NULL, [ | |
590 'field_image[0][alt]' => 'Vivamus aliquet elit', | |
591 ], t('Save')); | |
592 | |
593 // The image field form should load normally. | |
594 $response = $this->drupalPost('quickedit/form/node/1/field_image/en/full', '', ['nocssjs' => 'true'] + $this->getAjaxPageStatePostData(), ['query' => [MainContentViewSubscriber::WRAPPER_FORMAT => 'drupal_ajax']]); | |
595 $this->assertResponse(200); | |
596 $ajax_commands = Json::decode($response); | |
597 $this->assertIdentical('<form ', Unicode::substr($ajax_commands[0]['data'], 0, 6), 'The quickeditFieldForm command contains a form.'); | |
598 } | |
599 | |
600 } |