1<?php 2 3namespace Drupal\Tests\quickedit\Kernel; 4 5use Drupal\Component\Serialization\Json; 6use Drupal\Core\EventSubscriber\AjaxResponseSubscriber; 7use Drupal\Core\Language\LanguageInterface; 8use Drupal\editor\Entity\Editor; 9use Drupal\entity_test\Entity\EntityTest; 10use Drupal\quickedit\MetadataGenerator; 11use Drupal\quickedit_test\MockQuickEditEntityFieldAccessCheck; 12use Drupal\editor\EditorController; 13use Symfony\Component\HttpFoundation\Request; 14use Symfony\Component\HttpKernel\Event\ResponseEvent; 15use Symfony\Component\HttpKernel\HttpKernelInterface; 16use Drupal\filter\Entity\FilterFormat; 17 18/** 19 * Tests Edit module integration (Editor module's inline editing support). 20 * 21 * @group quickedit 22 */ 23class EditorIntegrationTest extends QuickEditTestBase { 24 25 /** 26 * {@inheritdoc} 27 */ 28 protected static $modules = ['editor', 'editor_test']; 29 30 /** 31 * The manager for editor plug-ins. 32 * 33 * @var \Drupal\Component\Plugin\PluginManagerInterface 34 */ 35 protected $editorManager; 36 37 /** 38 * The metadata generator object to be tested. 39 * 40 * @var \Drupal\quickedit\MetadataGeneratorInterface 41 */ 42 protected $metadataGenerator; 43 44 /** 45 * The editor selector object to be used by the metadata generator object. 46 * 47 * @var \Drupal\quickedit\EditorSelectorInterface 48 */ 49 protected $editorSelector; 50 51 /** 52 * The access checker object to be used by the metadata generator object. 53 * 54 * @var \Drupal\quickedit\Access\QuickEditEntityFieldAccessCheckInterface 55 */ 56 protected $accessChecker; 57 58 /** 59 * The name of the field ued for tests. 60 * 61 * @var string 62 */ 63 protected $fieldName; 64 65 protected function setUp(): void { 66 parent::setUp(); 67 68 // Install the Filter module. 69 70 // Create a field. 71 $this->fieldName = 'field_textarea'; 72 $this->createFieldWithStorage( 73 $this->fieldName, 'text', 1, 'Long text field', 74 // Instance settings. 75 [], 76 // Widget type & settings. 77 'text_textarea', 78 ['size' => 42], 79 // 'default' formatter type & settings. 80 'text_default', 81 [] 82 ); 83 84 // Create text format. 85 $full_html_format = FilterFormat::create([ 86 'format' => 'full_html', 87 'name' => 'Full HTML', 88 'weight' => 1, 89 'filters' => [], 90 ]); 91 $full_html_format->save(); 92 93 // Associate text editor with text format. 94 $editor = Editor::create([ 95 'format' => $full_html_format->id(), 96 'editor' => 'unicorn', 97 ]); 98 $editor->save(); 99 100 // Also create a text format without an associated text editor. 101 FilterFormat::create([ 102 'format' => 'no_editor', 103 'name' => 'No Text Editor', 104 'weight' => 2, 105 'filters' => [], 106 ])->save(); 107 } 108 109 /** 110 * Returns the in-place editor that quickedit selects. 111 * 112 * @param int $entity_id 113 * An entity ID. 114 * @param string $field_name 115 * A field name. 116 * @param string $view_mode 117 * A view mode. 118 * 119 * @return string 120 * Returns the selected in-place editor. 121 */ 122 protected function getSelectedEditor($entity_id, $field_name, $view_mode = 'default') { 123 $storage = $this->container->get('entity_type.manager')->getStorage('entity_test'); 124 $storage->resetCache([$entity_id]); 125 $entity = $storage->load($entity_id); 126 $items = $entity->get($field_name); 127 $options = \Drupal::service('entity_display.repository') 128 ->getViewDisplay('entity_test', 'entity_test', $view_mode) 129 ->getComponent($field_name); 130 return $this->editorSelector->getEditor($options['type'], $items); 131 } 132 133 /** 134 * Tests editor selection when the Editor module is present. 135 * 136 * Tests a textual field, with text filtering, with cardinality 1 and >1, 137 * always with a ProcessedTextEditor plug-in present, but with varying text 138 * format compatibility. 139 */ 140 public function testEditorSelection() { 141 $this->editorManager = $this->container->get('plugin.manager.quickedit.editor'); 142 $this->editorSelector = $this->container->get('quickedit.editor.selector'); 143 144 // Create an entity with values for this text field. 145 $entity = EntityTest::create(); 146 $entity->{$this->fieldName}->value = 'Hello, world!'; 147 $entity->{$this->fieldName}->format = 'filtered_html'; 148 $entity->save(); 149 150 // Editor selection w/ cardinality 1, text format w/o associated text editor. 151 $this->assertEquals('form', $this->getSelectedEditor($entity->id(), $this->fieldName), "With cardinality 1, and the filtered_html text format, the 'form' editor is selected."); 152 153 // Editor selection w/ cardinality 1, text format w/ associated text editor. 154 $entity->{$this->fieldName}->format = 'full_html'; 155 $entity->save(); 156 $this->assertEquals('editor', $this->getSelectedEditor($entity->id(), $this->fieldName), "With cardinality 1, and the full_html text format, the 'editor' editor is selected."); 157 158 // Editor selection with text processing, cardinality >1 159 $this->fields->field_textarea_field_storage->setCardinality(2); 160 $this->fields->field_textarea_field_storage->save(); 161 $this->assertEquals('form', $this->getSelectedEditor($entity->id(), $this->fieldName), "With cardinality >1, and both items using the full_html text format, the 'form' editor is selected."); 162 } 163 164 /** 165 * Tests (custom) metadata when the formatted text editor is used. 166 */ 167 public function testMetadata() { 168 $this->editorManager = $this->container->get('plugin.manager.quickedit.editor'); 169 $this->accessChecker = new MockQuickEditEntityFieldAccessCheck(); 170 $this->editorSelector = $this->container->get('quickedit.editor.selector'); 171 $this->metadataGenerator = new MetadataGenerator($this->accessChecker, $this->editorSelector, $this->editorManager); 172 173 // Create an entity with values for the field. 174 $entity = EntityTest::create(); 175 $entity->{$this->fieldName}->value = 'Test'; 176 $entity->{$this->fieldName}->format = 'full_html'; 177 $entity->save(); 178 $entity = EntityTest::load($entity->id()); 179 180 // Verify metadata. 181 $items = $entity->get($this->fieldName); 182 \Drupal::state()->set('quickedit_test_field_access', 'forbidden'); 183 $this->assertSame(['access' => FALSE], $this->metadataGenerator->generateFieldMetadata($items, 'default')); 184 \Drupal::state()->set('quickedit_test_field_access', 'neutral'); 185 $this->assertSame(['access' => FALSE], $this->metadataGenerator->generateFieldMetadata($items, 'default')); 186 \Drupal::state()->set('quickedit_test_field_access', 'allowed'); 187 $metadata = $this->metadataGenerator->generateFieldMetadata($items, 'default'); 188 $expected = [ 189 'access' => TRUE, 190 'label' => 'Long text field', 191 'editor' => 'editor', 192 'custom' => [ 193 'format' => 'full_html', 194 'formatHasTransformations' => FALSE, 195 ], 196 ]; 197 $this->assertEquals($expected, $metadata, 'The correct metadata (including custom metadata) is generated.'); 198 } 199 200 /** 201 * Tests in-place editor attachments when the Editor module is present. 202 */ 203 public function testAttachments() { 204 $this->editorSelector = $this->container->get('quickedit.editor.selector'); 205 206 $editors = ['editor']; 207 $attachments = $this->editorSelector->getEditorAttachments($editors); 208 $this->assertSame(['library' => ['editor/quickedit.inPlaceEditor.formattedText']], $attachments, "Expected attachments for Editor module's in-place editor found."); 209 } 210 211 /** 212 * Tests GetUntransformedTextCommand AJAX command. 213 */ 214 public function testGetUntransformedTextCommand() { 215 // Create an entity with values for the field. 216 $entity = EntityTest::create(); 217 $entity->{$this->fieldName}->value = 'Test'; 218 $entity->{$this->fieldName}->format = 'full_html'; 219 $entity->save(); 220 $entity = EntityTest::load($entity->id()); 221 222 // Verify AJAX response. 223 $controller = new EditorController(); 224 $request = new Request(); 225 $response = $controller->getUntransformedText($entity, $this->fieldName, LanguageInterface::LANGCODE_DEFAULT, 'default'); 226 $expected = [ 227 [ 228 'command' => 'editorGetUntransformedText', 229 'data' => 'Test', 230 ], 231 ]; 232 233 $ajax_response_attachments_processor = \Drupal::service('ajax_response.attachments_processor'); 234 $subscriber = new AjaxResponseSubscriber($ajax_response_attachments_processor); 235 $event = new ResponseEvent( 236 \Drupal::service('http_kernel'), 237 $request, 238 HttpKernelInterface::MASTER_REQUEST, 239 $response 240 ); 241 $subscriber->onResponse($event); 242 243 $this->assertEquals(Json::encode($expected), $response->getContent(), 'The GetUntransformedTextCommand AJAX command works correctly.'); 244 } 245 246} 247