--- /dev/null
+<?php
+
+namespace Drupal\embed\Tests;
+
+use Drupal\editor\Entity\Editor;
+use Drupal\file\Entity\File;
+use Drupal\filter\Entity\FilterFormat;
+use Drupal\simpletest\WebTestBase;
+
+/**
+ * Base class for all embed tests.
+ */
+abstract class EmbedTestBase extends WebTestBase {
+
+ /**
+ * Modules to enable.
+ *
+ * @var array
+ */
+ public static $modules = [
+ 'block',
+ 'embed',
+ 'embed_test',
+ 'editor',
+ 'ckeditor',
+ ];
+
+ /**
+ * The test administrative user.
+ *
+ * @var \Drupal\user\UserInterface
+ */
+ protected $adminUser;
+
+ /**
+ * The test administrative user.
+ *
+ * @var \Drupal\user\UserInterface
+ */
+ protected $webUser;
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function setUp() {
+ parent::setUp();
+
+ // Create Filtered HTML text format and enable entity_embed filter.
+ $format = FilterFormat::create([
+ 'format' => 'embed_test',
+ 'name' => 'Embed format',
+ 'filters' => [],
+ ]);
+ $format->save();
+
+ $editor_group = [
+ 'name' => 'Embed',
+ 'items' => [
+ 'embed_test_default',
+ ],
+ ];
+ $editor = Editor::create([
+ 'format' => 'embed_test',
+ 'editor' => 'ckeditor',
+ 'settings' => [
+ 'toolbar' => [
+ 'rows' => [[$editor_group]],
+ ],
+ ],
+ ]);
+ $editor->save();
+
+ // Create a user with required permissions.
+ $this->adminUser = $this->drupalCreateUser([
+ 'administer embed buttons',
+ 'use text format embed_test',
+ ]);
+
+ // Create a user with required permissions.
+ $this->webUser = $this->drupalCreateUser([
+ 'use text format embed_test',
+ ]);
+
+ // Set up some standard blocks for the testing theme (Classy).
+ // @see https://www.drupal.org/node/507488?page=1#comment-10291517
+ $this->drupalPlaceBlock('local_tasks_block');
+ $this->drupalPlaceBlock('local_actions_block');
+ }
+
+ /**
+ * Retrieves a sample file of the specified type.
+ *
+ * @return \Drupal\file\FileInterface
+ */
+ protected function getTestFile($type_name, $size = NULL) {
+ // Get a file to upload.
+ $file = current($this->drupalGetTestFiles($type_name, $size));
+
+ // Add a filesize property to files as would be read by
+ // \Drupal\file\Entity\File::load().
+ $file->filesize = filesize($file->uri);
+
+ $file = File::create((array) $file);
+ $file->save();
+ return $file;
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * This is a duplicate of WebTestBase::drupalProcessAjaxResponse() that
+ * includes the fix from https://www.drupal.org/node/2554449 for using ID
+ * selectors in AJAX commands.
+ */
+ protected function drupalProcessAjaxResponse($content, array $ajax_response, array $ajax_settings, array $drupal_settings) {
+ // ajax.js applies some defaults to the settings object, so do the same
+ // for what's used by this function.
+ $ajax_settings += array(
+ 'method' => 'replaceWith',
+ );
+ // DOM can load HTML soup. But, HTML soup can throw warnings, suppress
+ // them.
+ $dom = new \DOMDocument();
+ @$dom->loadHTML($content);
+ // XPath allows for finding wrapper nodes better than DOM does.
+ $xpath = new \DOMXPath($dom);
+ foreach ($ajax_response as $command) {
+ // Error messages might be not commands.
+ if (!is_array($command)) {
+ continue;
+ }
+ switch ($command['command']) {
+ case 'settings':
+ $drupal_settings = NestedArray::mergeDeepArray([$drupal_settings, $command['settings']], TRUE);
+ break;
+
+ case 'insert':
+ $wrapperNode = NULL;
+ // When a command specifies a specific selector, use it.
+ if (!empty($command['selector']) && strpos($command['selector'], '#') === 0) {
+ $wrapperNode = $xpath->query('//*[@id="' . substr($command['selector'], 1) . '"]')->item(0);
+ }
+ // When a command doesn't specify a selector, use the
+ // #ajax['wrapper'] which is always an HTML ID.
+ elseif (!empty($ajax_settings['wrapper'])) {
+ $wrapperNode = $xpath->query('//*[@id="' . $ajax_settings['wrapper'] . '"]')->item(0);
+ }
+ // @todo Ajax commands can target any jQuery selector, but these are
+ // hard to fully emulate with XPath. For now, just handle 'head'
+ // and 'body', since these are used by
+ // \Drupal\Core\Ajax\AjaxResponse::ajaxRender().
+ elseif (in_array($command['selector'], array('head', 'body'))) {
+ $wrapperNode = $xpath->query('//' . $command['selector'])->item(0);
+ }
+ if ($wrapperNode) {
+ // ajax.js adds an enclosing DIV to work around a Safari bug.
+ $newDom = new \DOMDocument();
+ // DOM can load HTML soup. But, HTML soup can throw warnings,
+ // suppress them.
+ @$newDom->loadHTML('<div>' . $command['data'] . '</div>');
+ // Suppress warnings thrown when duplicate HTML IDs are encountered.
+ // This probably means we are replacing an element with the same ID.
+ $newNode = @$dom->importNode($newDom->documentElement->firstChild->firstChild, TRUE);
+ $method = isset($command['method']) ? $command['method'] : $ajax_settings['method'];
+ // The "method" is a jQuery DOM manipulation function. Emulate
+ // each one using PHP's DOMNode API.
+ switch ($method) {
+ case 'replaceWith':
+ $wrapperNode->parentNode->replaceChild($newNode, $wrapperNode);
+ break;
+ case 'append':
+ $wrapperNode->appendChild($newNode);
+ break;
+ case 'prepend':
+ // If no firstChild, insertBefore() falls back to
+ // appendChild().
+ $wrapperNode->insertBefore($newNode, $wrapperNode->firstChild);
+ break;
+ case 'before':
+ $wrapperNode->parentNode->insertBefore($newNode, $wrapperNode);
+ break;
+ case 'after':
+ // If no nextSibling, insertBefore() falls back to
+ // appendChild().
+ $wrapperNode->parentNode->insertBefore($newNode, $wrapperNode->nextSibling);
+ break;
+ case 'html':
+ foreach ($wrapperNode->childNodes as $childNode) {
+ $wrapperNode->removeChild($childNode);
+ }
+ $wrapperNode->appendChild($newNode);
+ break;
+ }
+ }
+ break;
+
+ // @todo Add suitable implementations for these commands in order to
+ // have full test coverage of what ajax.js can do.
+ case 'remove':
+ break;
+ case 'changed':
+ break;
+ case 'css':
+ break;
+ case 'data':
+ break;
+ case 'restripe':
+ break;
+ case 'add_css':
+ break;
+ case 'update_build_id':
+ $buildId = $xpath->query('//input[@name="form_build_id" and @value="' . $command['old'] . '"]')->item(0);
+ if ($buildId) {
+ $buildId->setAttribute('value', $command['new']);
+ }
+ break;
+ }
+ }
+ $content = $dom->saveHTML();
+ $this->setRawContent($content);
+ $this->setDrupalSettings($drupal_settings);
+ }
+
+}