class CKEditorIntegrationTest

Same name in this branch
  1. 8.9.x core/modules/media_library/tests/src/FunctionalJavascript/CKEditorIntegrationTest.php \Drupal\Tests\media_library\FunctionalJavascript\CKEditorIntegrationTest
  2. 8.9.x core/modules/ckeditor/tests/src/FunctionalJavascript/CKEditorIntegrationTest.php \Drupal\Tests\ckeditor\FunctionalJavascript\CKEditorIntegrationTest
Same name in other branches
  1. 9 core/modules/ckeditor/tests/src/FunctionalJavascript/CKEditorIntegrationTest.php \Drupal\Tests\ckeditor\FunctionalJavascript\CKEditorIntegrationTest

@coversDefaultClass \Drupal\media\Plugin\CKEditorPlugin\DrupalMedia @group media

Hierarchy

Expanded class hierarchy of CKEditorIntegrationTest

File

core/modules/media/tests/src/FunctionalJavascript/CKEditorIntegrationTest.php, line 25

Namespace

Drupal\Tests\media\FunctionalJavascript
View source
class CKEditorIntegrationTest extends WebDriverTestBase {
    use CKEditorTestTrait;
    use MediaTypeCreationTrait;
    use TestFileCreationTrait;
    
    /**
     * The user to use during testing.
     *
     * @var \Drupal\user\UserInterface
     */
    protected $adminUser;
    
    /**
     * The sample Media entity to embed.
     *
     * @var \Drupal\media\MediaInterface
     */
    protected $media;
    
    /**
     * A host entity with a body field to embed media in.
     *
     * @var \Drupal\node\NodeInterface
     */
    protected $host;
    
    /**
     * The character code for the return key.
     *
     * @var int
     */
    const RETURN_KEY = 13;
    
    /**
     * The character code for the space bar.
     *
     * @var int
     */
    const SPACE_BAR = 32;
    
    /**
     * {@inheritdoc}
     */
    protected static $modules = [
        'ckeditor',
        'media',
        'node',
        'text',
        'media_test_ckeditor',
    ];
    
    /**
     * {@inheritdoc}
     */
    protected $defaultTheme = 'classy';
    
    /**
     * {@inheritdoc}
     */
    protected function setUp() {
        parent::setUp();
        FilterFormat::create([
            'format' => 'test_format',
            'name' => 'Test format',
            'filters' => [
                'filter_align' => [
                    'status' => TRUE,
                ],
                'filter_caption' => [
                    'status' => TRUE,
                ],
                'media_embed' => [
                    'status' => TRUE,
                ],
            ],
        ])->save();
        Editor::create([
            'editor' => 'ckeditor',
            'format' => 'test_format',
            'settings' => [
                'toolbar' => [
                    'rows' => [
                        [
                            [
                                'name' => 'All the things',
                                'items' => [
                                    'Source',
                                    'Bold',
                                    'Italic',
                                    'DrupalLink',
                                    'DrupalUnlink',
                                    'DrupalImage',
                                ],
                            ],
                        ],
                    ],
                ],
            ],
        ])->save();
        // Note that media_install() grants 'view media' to all users by default.
        $this->adminUser = $this->drupalCreateUser([
            'use text format test_format',
            'bypass node access',
        ]);
        // Create a sample media entity to be embedded.
        $this->createMediaType('image', [
            'id' => 'image',
        ]);
        File::create([
            'uri' => $this->getTestFiles('image')[0]->uri,
        ])
            ->save();
        $this->media = Media::create([
            'bundle' => 'image',
            'name' => 'Screaming hairy armadillo',
            'field_media_image' => [
                [
                    'target_id' => 1,
                    'alt' => 'default alt',
                    'title' => 'default title',
                ],
            ],
        ]);
        $this->media
            ->save();
        // Create a sample host entity to embed media in.
        $this->drupalCreateContentType([
            'type' => 'blog',
        ]);
        $this->host = $this->createNode([
            'type' => 'blog',
            'title' => 'Animals with strange names',
            'body' => [
                'value' => '<drupal-media data-caption="baz" data-entity-type="media" data-entity-uuid="' . $this->media
                    ->uuid() . '"></drupal-media>',
                'format' => 'test_format',
            ],
        ]);
        $this->host
            ->save();
        $this->drupalLogin($this->adminUser);
    }
    
    /**
     * Tests that only <drupal-media> tags are processed.
     *
     * @see \Drupal\Tests\media\Kernel\MediaEmbedFilterTest::testOnlyDrupalMediaTagProcessed()
     */
    public function testOnlyDrupalMediaTagProcessed() {
        $original_value = $this->host->body->value;
        $this->host->body->value = str_replace('drupal-media', 'p', $original_value);
        $this->host
            ->save();
        // Assert that `<p data-* …>` is not upcast into a CKEditor Widget.
        $this->drupalGet($this->host
            ->toUrl('edit-form'));
        $this->waitForEditor();
        $this->assignNameToCkeditorIframe();
        $this->getSession()
            ->switchToIFrame('ckeditor');
        $assert_session = $this->assertSession();
        $this->assertEmpty($assert_session->waitForElementVisible('css', 'img[src*="image-test.png"]', 1000));
        $assert_session->elementNotExists('css', 'figure');
        $this->host->body->value = $original_value;
        $this->host
            ->save();
        // Assert that `<drupal-media data-* …>` is upcast into a CKEditor Widget.
        $this->getSession()
            ->reload();
        $this->waitForEditor();
        $this->assignNameToCkeditorIframe();
        $this->getSession()
            ->switchToIFrame('ckeditor');
        $this->assertNotEmpty($assert_session->waitForElementVisible('css', 'img[src*="image-test.png"]'));
        $assert_session->elementExists('css', 'figure');
    }
    
    /**
     * Tests that failed media embed preview requests inform the end user.
     */
    public function testErrorMessages() {
        // Assert that a request to the `media.filter.preview` route that does not
        // result in a 200 response (due to server error or network error) is
        // handled in the JavaScript by displaying the expected error message.
        // @see core/modules/media/js/media_embed_ckeditor.theme.js
        // @see core/modules/media/js/plugins/drupalmedia/plugin.js
        $this->container
            ->get('state')
            ->set('test_media_filter_controller_throw_error', TRUE);
        $this->drupalGet($this->host
            ->toUrl('edit-form'));
        $this->waitForEditor();
        $this->assignNameToCkeditorIframe();
        $this->getSession()
            ->switchToIFrame('ckeditor');
        $assert_session = $this->assertSession();
        $this->assertEmpty($assert_session->waitForElementVisible('css', 'img[src*="image-test.png"]', 1000));
        $assert_session->elementNotExists('css', 'figure');
        $this->assertNotEmpty($assert_session->waitForText('An error occurred while trying to preview the media. Please save your work and reload this page.'));
        // Now assert that the error doesn't appear when the override to force an
        // error is removed.
        $this->container
            ->get('state')
            ->set('test_media_filter_controller_throw_error', FALSE);
        $this->getSession()
            ->reload();
        $this->waitForEditor();
        $this->assignNameToCkeditorIframe();
        $this->getSession()
            ->switchToIFrame('ckeditor');
        $this->assertNotEmpty($assert_session->waitForElementVisible('css', 'img[src*="image-test.png"]'));
        // There's a second kind of error message that comes from the back end
        // that happens when the media uuid can't be converted to a media preview.
        // In this case, the error will appear in a the themable
        // media-embed-error.html template.  We have a hook altering the css
        // classes to test the twi template is working properly and picking up our
        // extra class.
        // @see \Drupal\media\Plugin\Filter\MediaEmbed::renderMissingMediaIndicator()
        // @see core/modules/media/templates/media-embed-error.html.twig
        // @see media_test_ckeditor_preprocess_media_embed_error()
        $original_value = $this->host->body->value;
        $this->host->body->value = str_replace($this->media
            ->uuid(), 'invalid_uuid', $original_value);
        $this->host
            ->save();
        $this->drupalGet($this->host
            ->toUrl('edit-form'));
        $this->waitForEditor();
        $this->assignNameToCkeditorIframe();
        $this->getSession()
            ->switchToIFrame('ckeditor');
        $this->assertNotEmpty($assert_session->waitForElement('css', 'drupal-media figure.caption-drupal-media .this-error-message-is-themeable'));
        // Test when using the classy theme, an additional class is added in
        // classy/templates/content/media-embed-error.html.twig.
        $this->assertTrue($this->container
            ->get('theme_installer')
            ->install([
            'classy',
        ]));
        $this->config('system.theme')
            ->set('default', 'classy')
            ->save();
        $this->drupalGet($this->host
            ->toUrl('edit-form'));
        $this->waitForEditor();
        $this->assignNameToCkeditorIframe();
        $this->getSession()
            ->switchToIFrame('ckeditor');
        $this->assertNotEmpty($assert_session->waitForElement('css', 'drupal-media figure.caption-drupal-media .this-error-message-is-themeable.media-embed-error--missing-source'));
        $assert_session->responseContains('classy/css/components/media-embed-error.css');
        // Test that restoring a valid UUID results in the media embed preview
        // displaying.
        $this->host->body->value = $original_value;
        $this->host
            ->save();
        $this->drupalGet($this->host
            ->toUrl('edit-form'));
        $this->waitForEditor();
        $this->assignNameToCkeditorIframe();
        $this->getSession()
            ->switchToIFrame('ckeditor');
        $this->assertNotEmpty($assert_session->waitForElementVisible('css', 'img[src*="image-test.png"]'));
        $assert_session->elementNotExists('css', 'drupal-media figure.caption-drupal-media .this-error-message-is-themeable');
    }
    
    /**
     * The CKEditor Widget must load a preview generated using the default theme.
     */
    public function testPreviewUsesDefaultThemeAndIsClientCacheable() {
        // Make the node edit form use the admin theme, like on most Drupal sites.
        $this->config('node.settings')
            ->set('use_admin_theme', TRUE)
            ->save();
        $this->container
            ->get('router.builder')
            ->rebuild();
        // Allow the test user to view the admin theme.
        $this->adminUser
            ->addRole($this->drupalCreateRole([
            'view the administration theme',
        ]));
        $this->adminUser
            ->save();
        // Configure a different default and admin theme, like on most Drupal sites.
        $this->config('system.theme')
            ->set('default', 'stable')
            ->set('admin', 'classy')
            ->save();
        // Assert that when looking at an embedded entity in the CKEditor Widget,
        // the preview is generated using the default theme, not the admin theme.
        // @see media_test_ckeditor_entity_view_alter()
        $this->drupalGet($this->host
            ->toUrl('edit-form'));
        $this->waitForEditor();
        $this->assignNameToCkeditorIframe();
        $this->getSession()
            ->switchToIFrame('ckeditor');
        $assert_session = $this->assertSession();
        $this->assertNotEmpty($assert_session->waitForElementVisible('css', 'img[src*="image-test.png"]'));
        $element = $assert_session->elementExists('css', '[data-media-embed-test-active-theme]');
        $this->assertSame('stable', $element->getAttribute('data-media-embed-test-active-theme'));
        // Assert that the first preview request transferred >500 B over the wire.
        // Then toggle source mode on and off. This causes the CKEditor widget to be
        // destroyed and then reconstructed. Assert that during this reconstruction,
        // a second request is sent. This second request should have transferred 0
        // bytes: the browser should have cached the response, thus resulting in a
        // much better user experience.
        $this->assertGreaterThan(500, $this->getLastPreviewRequestTransferSize());
        $this->pressEditorButton('source');
        $this->assertNotEmpty($assert_session->waitForElement('css', 'textarea.cke_source'));
        $this->pressEditorButton('source');
        $this->assignNameToCkeditorIframe();
        $this->getSession()
            ->switchToIFrame('ckeditor');
        $this->assertNotEmpty($assert_session->waitForElementVisible('css', 'img[src*="image-test.png"]'));
        $this->assertSame(0, $this->getLastPreviewRequestTransferSize());
    }
    
    /**
     * Tests caption editing in the CKEditor widget.
     */
    public function testEditableCaption() {
        $page = $this->getSession()
            ->getPage();
        $assert_session = $this->assertSession();
        // Test that setting caption to blank string doesn't break 'Edit media'
        // button.
        $original_value = $this->host->body->value;
        $this->host->body->value = str_replace('data-caption="baz"', 'data-caption=""', $original_value);
        $this->host
            ->save();
        $this->drupalGet($this->host
            ->toUrl('edit-form'));
        $this->waitForEditor();
        $this->assignNameToCkeditorIframe();
        $this->getSession()
            ->switchToIFrame('ckeditor');
        $this->assertNotEmpty($assert_session->waitForButton('Edit media'));
        // Test `aria-label` attribute appears on the widget wrapper.
        $assert_session->elementExists('css', '.cke_widget_drupalmedia[aria-label="Screaming hairy armadillo"]');
        $assert_session->elementContains('css', 'figcaption', '');
        $assert_session->elementAttributeContains('css', 'figcaption', 'data-placeholder', 'Enter caption here');
        // Test if you leave the caption blank, but change another attribute,
        // such as the alt text, the editable caption is still there and the edit
        // button still exists.
        $this->fillFieldInMetadataDialogAndSubmit('attributes[alt]', 'Mama, life had just begun');
        $this->assertNotEmpty($assert_session->waitForElementVisible('css', 'drupal-media img[alt*="Mama, life had just begun"]'));
        $assert_session->buttonExists('Edit media');
        $assert_session->elementContains('css', 'figcaption', '');
        $assert_session->elementAttributeContains('css', 'figcaption', 'data-placeholder', 'Enter caption here');
        // Restore caption in saved body value.
        $original_value = $this->host->body->value;
        $this->host->body->value = str_replace('data-caption=""', 'data-caption="baz"', $original_value);
        $this->host
            ->save();
        $this->drupalGet($this->host
            ->toUrl('edit-form'));
        $this->waitForEditor();
        $this->assignNameToCkeditorIframe();
        $this->getSession()
            ->switchToIFrame('ckeditor');
        // Assert that figcaption element exists within the drupal-media element.
        $this->assertNotEmpty($figcaption = $assert_session->waitForElement('css', 'drupal-media figcaption'));
        $this->assertSame('baz', $figcaption->getHtml());
        // Test that disabling the caption in the metadata dialog removes it
        // from the drupal-media element.
        $this->openMetadataDialogWithKeyPress(static::SPACE_BAR);
        $page->uncheckField('hasCaption');
        $this->submitDialog();
        $this->getSession()
            ->switchToIFrame('ckeditor');
        $this->assertNotEmpty($drupal_media = $assert_session->waitForElementVisible('css', 'drupal-media'));
        // Wait for element to update without figcaption.
        $result = $page->waitFor(10, function () use ($drupal_media) {
            return empty($drupal_media->find('css', 'figcaption'));
        });
        // Will be true if no figcaption exists within the drupal-media element.
        $this->assertTrue($result);
        // Test that enabling the caption in the metadata dialog adds an editable
        // caption to the embedded media.
        $this->openMetadataDialogWithKeyPress(static::SPACE_BAR);
        $page->checkField('hasCaption');
        $this->submitDialog();
        $this->getSession()
            ->switchToIFrame('ckeditor');
        $this->assertNotEmpty($drupal_media = $assert_session->waitForElementVisible('css', 'drupal-media figcaption'));
        // Type into the widget's caption element.
        $this->assertNotEmpty($assert_session->waitForElement('css', 'figcaption'));
        $this->setCaption('Caught in a <strong>landslide</strong>! No escape from <em>reality</em>!');
        $this->getSession()
            ->switchToIFrame('ckeditor');
        $assert_session->elementExists('css', 'figcaption > em');
        $assert_session->elementExists('css', 'figcaption > strong')
            ->click();
        // Select the <strong> element and unbold it.
        $this->clickPathLinkByTitleAttribute("strong element");
        $this->pressEditorButton('bold');
        $this->getSession()
            ->switchToIFrame('ckeditor');
        $assert_session->elementExists('css', 'figcaption > em');
        $assert_session->elementNotExists('css', 'figcaption > strong');
        // Select the <em> element and unitalicize it.
        $assert_session->elementExists('css', 'figcaption > em')
            ->click();
        $this->clickPathLinkByTitleAttribute("em element");
        $this->pressEditorButton('italic');
        // The "source" button should reveal the HTML source in a state matching
        // what is shown in the CKEditor widget.
        $this->pressEditorButton('source');
        $source = $assert_session->elementExists('css', 'textarea.cke_source');
        $value = $source->getValue();
        $dom = Html::load($value);
        $xpath = new \DOMXPath($dom);
        $drupal_media = $xpath->query('//drupal-media')[0];
        $this->assertSame('Caught in a landslide! No escape from reality!', $drupal_media->getAttribute('data-caption'));
        // Change the caption by modifying the HTML source directly. When exiting
        // "source" mode, this should be respected.
        $poor_boy_text = "I'm just a <strong>poor boy</strong>, I need no sympathy!";
        $drupal_media->setAttribute("data-caption", $poor_boy_text);
        $source->setValue(Html::serialize($dom));
        $this->pressEditorButton('source');
        $this->assignNameToCkeditorIframe();
        $this->getSession()
            ->switchToIFrame('ckeditor');
        $figcaption = $assert_session->waitForElement('css', 'figcaption');
        $this->assertNotEmpty($figcaption);
        $this->assertSame($poor_boy_text, $figcaption->getHtml());
        // Select the <strong> element that we just set in "source" mode. This
        // proves that it was indeed rendered by the CKEditor widget.
        $strong = $figcaption->find('css', 'strong');
        $this->assertNotEmpty($strong);
        $strong->click();
        $this->pressEditorButton('bold');
        // Insert a link into the caption.
        $this->clickPathLinkByTitleAttribute("Caption element");
        $this->pressEditorButton('drupallink');
        $field = $assert_session->waitForElementVisible('xpath', '//input[@name="attributes[href]"]');
        $this->assertNotEmpty($field);
        $field->setValue('https://www.drupal.org');
        $assert_session->elementExists('css', 'button.form-submit')
            ->press();
        // Wait for the live preview in the CKEditor widget to finish loading, then
        // edit the link; no `data-cke-saved-href` attribute should exist on it.
        $this->getSession()
            ->switchToIFrame('ckeditor');
        $figcaption = $assert_session->waitForElement('css', 'figcaption');
        $page = $this->getSession()
            ->getPage();
        // Wait for AJAX refresh.
        $page->waitFor(10, function () use ($figcaption) {
            return $figcaption->find('xpath', '//a[@href="https://www.drupal.org"]');
        });
        $assert_session->elementExists('css', 'a', $figcaption)
            ->click();
        $this->clickPathLinkByTitleAttribute("a element");
        $this->pressEditorButton('drupallink');
        $field = $assert_session->waitForElementVisible('xpath', '//input[@name="attributes[href]"]');
        $this->assertNotEmpty($field);
        $field->setValue('https://www.drupal.org/project/drupal');
        $assert_session->elementExists('css', 'button.form-submit')
            ->press();
        $this->getSession()
            ->switchToIFrame('ckeditor');
        $figcaption = $assert_session->waitForElement('css', 'figcaption');
        $page = $this->getSession()
            ->getPage();
        // Wait for AJAX refresh.
        $page->waitFor(10, function () use ($figcaption) {
            return $figcaption->find('xpath', '//a[@href="https://www.drupal.org/project/drupal"]');
        });
        $this->pressEditorButton('source');
        $source = $assert_session->elementExists('css', "textarea.cke_source");
        $value = $source->getValue();
        $this->assertStringContainsString('https://www.drupal.org/project/drupal', $value);
        $this->assertStringNotContainsString('data-cke-saved-href', $value);
        // Save the entity.
        $assert_session->buttonExists('Save')
            ->press();
        // Verify the saved entity when viewed also contains the captioned media.
        $link = $assert_session->elementExists('css', 'figcaption > a');
        $this->assertSame('https://www.drupal.org/project/drupal', $link->getAttribute('href'));
        $this->assertSame("I'm just a poor boy, I need no sympathy!", $link->getText());
        // Edit it again, type a different caption in the widget.
        $this->drupalGet($this->host
            ->toUrl('edit-form'));
        $this->waitForEditor();
        $this->assignNameToCkeditorIframe();
        $this->getSession()
            ->switchToIFrame('ckeditor');
        $this->assertNotEmpty($assert_session->waitForElementVisible('css', 'figcaption'));
        $this->setCaption('Scaramouch, <em>Scaramouch</em>, will you do the <strong>Fandango</strong>?');
        // Erase the caption in the CKEditor Widget, verify the <figcaption> still
        // exists and contains placeholder text, then type something else.
        $this->setCaption('');
        $this->getSession()
            ->switchToIFrame('ckeditor');
        $assert_session->elementContains('css', 'figcaption', '');
        $assert_session->elementAttributeContains('css', 'figcaption', 'data-placeholder', 'Enter caption here');
        $this->setCaption('Fin.');
        $this->getSession()
            ->switchToIFrame('ckeditor');
        $assert_session->elementContains('css', 'figcaption', 'Fin.');
    }
    
    /**
     * Test the EditorMediaDialog's form elements' #access logic.
     */
    public function testDialogAccess() {
        $page = $this->getSession()
            ->getPage();
        $assert_session = $this->assertSession();
        $this->drupalGet($this->host
            ->toUrl('edit-form'));
        $this->waitForEditor();
        $this->assignNameToCkeditorIframe();
        $this->getSession()
            ->switchToIFrame('ckeditor');
        // Enable `filter_html` without "alt", "data-align" or "data-caption"
        // attributes added to the drupal-media tag.
        $allowed_html = "<a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type='1 A I'> <li> <dl> <dt> <dd> <h2 id='jump-*'> <h3 id> <h4 id> <h5 id> <h6 id> <drupal-media data-entity-type data-entity-uuid data-view-mode>";
        $filter_format = FilterFormat::load('test_format');
        $filter_format->setFilterConfig('filter_html', [
            'status' => TRUE,
            'settings' => [
                'allowed_html' => $allowed_html,
            ],
        ])
            ->save();
        // Test the validation of attributes in the dialog.  If the alt,
        // data-caption, and data-align attributes are not set on the drupal-media
        // tag, the respective fields shouldn't display in the dialog.
        $this->drupalGet($this->host
            ->toUrl('edit-form'));
        $this->waitForEditor();
        $this->assignNameToCkeditorIframe();
        $this->getSession()
            ->switchToIFrame('ckeditor');
        $this->assertNotEmpty($assert_session->waitForElementVisible('css', 'drupal-media', 2000));
        // Test `aria-label` attribute appears on the widget wrapper.
        $assert_session->elementExists('css', '.cke_widget_drupalmedia[aria-label="Screaming hairy armadillo"]');
        $page->pressButton('Edit media');
        $this->waitForMetadataDialog();
        $assert_session->fieldNotExists('attributes[alt]');
        $assert_session->fieldNotExists('attributes[align]');
        $assert_session->fieldNotExists('hasCaption');
        $assert_session->pageTextContains('There is nothing to configure for this media.');
        // The edit link for the format shouldn't appear unless the user has
        // permission to edit the text format.
        $assert_session->pageTextNotContains('Edit the text format Test format to modify the attributes that can be overridden.');
        $page->pressButton('Close');
        $this->getSession()
            ->switchToIFrame('ckeditor');
        // Now test the same thing with a user who has access to edit text formats.
        // An extra message containing a link to edit the text format should
        // appear.
        Role::load(RoleInterface::AUTHENTICATED_ID)->grantPermission('administer filters')
            ->save();
        $this->drupalGet($this->host
            ->toUrl('edit-form'));
        $this->waitForEditor();
        $this->assignNameToCkeditorIframe();
        $this->getSession()
            ->switchToIFrame('ckeditor');
        $this->assertNotEmpty($assert_session->waitForElementVisible('css', 'drupal-media', 2000));
        $page->pressButton('Edit media');
        $this->waitForMetadataDialog();
        $assert_session->fieldNotExists('attributes[alt]');
        $assert_session->fieldNotExists('attributes[align]');
        $assert_session->fieldNotExists('hasCaption');
        $assert_session->pageTextContains('There is nothing to configure for this media. Edit the text format Test format to modify the attributes that can be overridden.');
        $assert_session->linkExists('Edit the text format Test format');
        $page->pressButton('Close');
        $this->getSession()
            ->switchToIFrame('ckeditor');
        // Now test that adding the attributes to the allowed HTML will allow
        // the fields to display in the dialog.
        $allowed_html = str_replace('<drupal-media data-entity-type data-entity-uuid data-view-mode>', '<drupal-media alt data-align data-caption data-entity-type data-entity-uuid data-view-mode>', $allowed_html);
        $filter_format->setFilterConfig('filter_html', [
            'status' => TRUE,
            'settings' => [
                'allowed_html' => $allowed_html,
            ],
        ])
            ->save();
        $this->assertNotEmpty($assert_session->waitForElementVisible('css', 'drupal-media', 2000));
        $page->pressButton('Edit media');
        $this->waitForMetadataDialog();
        $assert_session->fieldExists('attributes[alt]');
        $assert_session->fieldExists('attributes[data-align]');
        $assert_session->fieldExists('hasCaption');
        $page->pressButton('Close');
        $this->getSession()
            ->switchToIFrame('ckeditor');
        // Test that setting the media image field to not display alt field also
        // disables it in the dialog.
        FieldConfig::loadByName('media', 'image', 'field_media_image')->setSetting('alt_field', FALSE)
            ->save();
        // @todo This manual cache clearing should not be necessary, fix in
        // https://www.drupal.org/project/drupal/issues/3076544
        $this->container
            ->get('cache.discovery')
            ->delete('entity_bundle_field_definitions:media:image:en');
        // Wait for preview.
        $this->assertNotEmpty($assert_session->waitForElementVisible('css', 'drupal-media', 2000));
        $page->pressButton('Edit media');
        $this->waitForMetadataDialog();
        $assert_session->fieldNotExists('attributes[alt]');
        $assert_session->fieldExists('attributes[data-align]');
        $assert_session->fieldExists('hasCaption');
        $page->pressButton('Close');
        $this->getSession()
            ->switchToIFrame('ckeditor');
        // Test that enabling the alt field on the media image field restores
        // the field in the dialog.
        FieldConfig::loadByName('media', 'image', 'field_media_image')->setSetting('alt_field', TRUE)
            ->save();
        // @todo This manual cache clearing should not be necessary, fix in
        // https://www.drupal.org/project/drupal/issues/3076544
        $this->container
            ->get('cache.discovery')
            ->delete('entity_bundle_field_definitions:media:image:en');
        // Wait for preview.
        $this->assertNotEmpty($assert_session->waitForElementVisible('css', 'drupal-media', 2000));
        $page->pressButton('Edit media');
        $this->waitForMetadataDialog();
        $assert_session->fieldExists('attributes[alt]');
        $assert_session->fieldExists('attributes[data-align]');
        $assert_session->fieldExists('hasCaption');
        $page->pressButton('Close');
        $this->getSession()
            ->switchToIFrame('ckeditor');
        // Test that disabling `filter_caption` and `filter_align` disables the
        // respective fields in the dialog.
        $filter_format->setFilterConfig('filter_caption', [
            'status' => FALSE,
        ])
            ->setFilterConfig('filter_align', [
            'status' => FALSE,
        ])
            ->save();
        // Wait for preview.
        $this->assertNotEmpty($assert_session->waitForElementVisible('css', 'drupal-media', 2000));
        $page->pressButton('Edit media');
        $this->waitForMetadataDialog();
        $assert_session->fieldNotExists('attributes[data-align]');
        $assert_session->fieldNotExists('hasCaption');
        // The alt field should be unaffected.
        $assert_session->fieldExists('attributes[alt]');
        $page->pressButton('Close');
        $this->getSession()
            ->switchToIFrame('ckeditor');
        // Test that enabling the two filters restores the fields in the dialog.
        $filter_format->setFilterConfig('filter_caption', [
            'status' => TRUE,
        ])
            ->setFilterConfig('filter_align', [
            'status' => TRUE,
        ])
            ->save();
        // Wait for preview.
        $this->assertNotEmpty($assert_session->waitForElementVisible('css', 'drupal-media', 2000));
        $page->pressButton('Edit media');
        $this->waitForMetadataDialog();
        $assert_session->fieldExists('attributes[data-align]');
        $assert_session->fieldExists('hasCaption');
        $assert_session->pageTextNotContains('There is nothing to configure for this media. Edit the text format Test format to modify the attributes that can be overridden.');
        // The alt field should be unaffected.
        $assert_session->fieldExists('attributes[alt]');
    }
    
    /**
     * Tests the EditorMediaDialog can set the alt attribute.
     */
    public function testAlt() {
        $page = $this->getSession()
            ->getPage();
        $assert_session = $this->assertSession();
        $this->drupalGet($this->host
            ->toUrl('edit-form'));
        $this->waitForEditor();
        $this->assignNameToCkeditorIframe();
        $this->getSession()
            ->switchToIFrame('ckeditor');
        // Test that by default no alt attribute is present on the drupal-media
        // element.
        $this->pressEditorButton('source');
        $this->assertSourceAttributeSame('alt', NULL);
        $this->leaveSourceMode();
        // Test that the preview shows the alt value from the media field's
        // alt text.
        $this->assertNotEmpty($assert_session->waitForElementVisible('css', 'drupal-media img[alt*="default alt"]'));
        $this->openMetadataDialogWithKeyPress(static::RETURN_KEY);
        // Assert that the placeholder is set to the value of the media field's
        // alt text.
        $assert_session->elementAttributeContains('named', [
            'field',
            'attributes[alt]',
        ], 'placeholder', 'default alt');
        // Fill in the alt field, submit and return to CKEditor.
        $who_is_zartan = 'Zartan is the leader of the Dreadnoks.';
        $page->fillField('attributes[alt]', $who_is_zartan);
        $this->submitDialog();
        $this->getSession()
            ->switchToIFrame('ckeditor');
        // Assert that the img within the media embed within the CKEditor contains
        // the overridden alt text set in the dialog.
        $this->assertNotEmpty($assert_session->waitForElementVisible('css', 'drupal-media img[alt*="' . $who_is_zartan . '"]'));
        // Test `aria-label` attribute appears on the widget wrapper.
        $assert_session->elementExists('css', '.cke_widget_drupalmedia[aria-label="Screaming hairy armadillo"]');
        // Test that the downcast drupal-media element now has the alt attribute
        // entered in the dialog.
        $this->pressEditorButton('source');
        $this->assertSourceAttributeSame('alt', $who_is_zartan);
        // The alt field should now display the override instead of the default.
        $this->leaveSourceMode();
        $this->openMetadataDialog();
        $assert_session->fieldValueEquals('attributes[alt]', $who_is_zartan);
        // Test the process again with a different alt text to make sure it works
        // the second time around.
        $cobra_commander_bio = 'The supreme leader of the terrorist organization Cobra';
        // Set the alt field to the new alt text.
        $page->fillField('attributes[alt]', $cobra_commander_bio);
        $this->submitDialog();
        $this->getSession()
            ->switchToIFrame('ckeditor');
        // Assert that the img within the media embed preview
        // within the CKEditor contains the overridden alt text set in the dialog.
        $this->assertNotEmpty($assert_session->waitForElementVisible('css', 'drupal-media img[alt*="' . $cobra_commander_bio . '"]'));
        // Test that the downcast drupal-media element now has the alt attribute
        // entered in the dialog.
        $this->pressEditorButton('source');
        $this->assertSourceAttributeSame('alt', $cobra_commander_bio);
        // The default value of the alt field should now display the override
        // instead of the value on the media image field.
        $this->leaveSourceMode();
        $this->openMetadataDialogWithKeyPress(static::RETURN_KEY);
        $assert_session->fieldValueEquals('attributes[alt]', $cobra_commander_bio);
        // Test that setting alt value to two double quotes will signal to the
        // MediaEmbed filter to unset the attribute on the media image field.
        // We intentionally add a space space after the two double quotes to test
        // the string is trimmed to two quotes.
        $page->fillField('attributes[alt]', '"" ');
        $this->submitDialog();
        $this->getSession()
            ->switchToIFrame('ckeditor');
        // Verify that the two double quote empty alt indicator ('""') set in
        // the dialog has successfully resulted in a media image field with the
        // alt attribute present but without a value.
        $this->assertNotEmpty($assert_session->waitForElementVisible('css', 'drupal-media img[alt=""]'));
        // Test that the downcast drupal-media element's alt attribute now has the
        // empty string indicator.
        $this->pressEditorButton('source');
        $this->assertSourceAttributeSame('alt', '""');
        // Test that setting alt to back to an empty string within the dialog will
        // restore the default alt value saved in to the media image field of the
        // media item.
        $this->leaveSourceMode();
        $this->openMetadataDialog();
        $page->fillField('attributes[alt]', '');
        $this->submitDialog();
        $this->getSession()
            ->switchToIFrame('ckeditor');
        $this->assertNotEmpty($assert_session->waitForElementVisible('css', 'drupal-media img[alt*="default alt"]'));
        // Test that the downcast drupal-media element no longer has an alt
        // attribute.
        $this->pressEditorButton('source');
        $this->assertSourceAttributeSame('alt', NULL);
    }
    
    /**
     * Test that dialog loads appropriate translation's alt text.
     */
    public function testTranslationAlt() {
        \Drupal::service('module_installer')->install([
            'language',
            'content_translation',
        ]);
        $this->resetAll();
        ConfigurableLanguage::create([
            'id' => 'fr',
        ])->save();
        ContentLanguageSettings::loadByEntityTypeBundle('media', 'image')->setDefaultLangcode('en')
            ->setLanguageAlterable(TRUE)
            ->save();
        $media = Media::create([
            'bundle' => 'image',
            'name' => 'Screaming hairy armadillo',
            'field_media_image' => [
                [
                    'target_id' => 1,
                    'alt' => 'default alt',
                    'title' => 'default title',
                ],
            ],
        ]);
        $media->save();
        $media_fr = $media->addTranslation('fr');
        $media_fr->name = "Tatou poilu hurlant";
        $media_fr->field_media_image
            ->setValue([
            [
                'target_id' => '1',
                'alt' => "texte alternatif par défaut",
                'title' => "titre alternatif par défaut",
            ],
        ]);
        $media_fr->save();
        ContentLanguageSettings::loadByEntityTypeBundle('node', 'blog')->setDefaultLangcode('en')
            ->setLanguageAlterable(TRUE)
            ->save();
        $host = $this->createNode([
            'type' => 'blog',
            'title' => 'Animals with strange names',
            'body' => [
                'value' => '<drupal-media data-caption="baz" data-entity-type="media" data-entity-uuid="' . $media->uuid() . '"></drupal-media>',
                'format' => 'test_format',
            ],
        ]);
        $host->save();
        $translation = $host->addTranslation('fr');
        $translation->title = 'Animaux avec des noms étranges';
        $translation->body->value = $host->body->value;
        $translation->body->format = $host->body->format;
        $translation->save();
        Role::load(RoleInterface::AUTHENTICATED_ID)->grantPermission('translate any entity')
            ->save();
        $page = $this->getSession()
            ->getPage();
        $assert_session = $this->assertSession();
        $this->drupalGet('/fr/node/' . $host->id() . '/edit');
        $this->waitForEditor();
        $this->assignNameToCkeditorIframe();
        $this->getSession()
            ->switchToIFrame('ckeditor');
        // Test that the default alt attribute displays without an override.
        $this->assertNotEmpty($assert_session->waitForElementVisible('xpath', '//img[contains(@alt, "texte alternatif par défaut")]'));
        // Test `aria-label` attribute appears on the widget wrapper.
        $assert_session->elementExists('css', '.cke_widget_drupalmedia[aria-label="Tatou poilu hurlant"]');
        $page->pressButton('Edit media');
        $this->waitForMetadataDialog();
        // Assert that the placeholder is set to the value of the media field's
        // alt text.
        $assert_session->elementAttributeContains('named', [
            'field',
            'attributes[alt]',
        ], 'placeholder', 'texte alternatif par défaut');
        // Fill in the alt field in the dialog.
        $qui_est_zartan = 'Zartan est le chef des Dreadnoks.';
        $page->fillField('attributes[alt]', $qui_est_zartan);
        $this->submitDialog();
        $this->getSession()
            ->switchToIFrame('ckeditor');
        // Assert that the img within the media embed within CKEditor contains
        // the overridden alt text set in the dialog.
        $this->assertNotEmpty($assert_session->waitForElementVisible('xpath', '//img[contains(@alt, "' . $qui_est_zartan . '")]'));
        $this->getSession()
            ->switchToIFrame();
        $page->pressButton('Save');
        $assert_session->elementExists('xpath', '//img[contains(@alt, "' . $qui_est_zartan . '")]');
    }
    
    /**
     * Tests linkability of the CKEditor widget.
     *
     * @dataProvider linkabilityProvider
     */
    public function testLinkability($drupalimage_is_enabled) {
        if (!$drupalimage_is_enabled) {
            // Remove the `drupalimage` plugin's `DrupalImage` button.
            $editor = Editor::load('test_format');
            $settings = $editor->getSettings();
            $rows = $settings['toolbar']['rows'];
            foreach ($rows as $row_key => $row) {
                foreach ($row as $group_key => $group) {
                    foreach ($group['items'] as $item_key => $item) {
                        if ($item === 'DrupalImage') {
                            unset($settings['toolbar']['rows'][$row_key][$group_key]['items'][$item_key]);
                        }
                    }
                }
            }
            $editor->setSettings($settings);
            $editor->save();
        }
        $this->host->body->value .= '<p>The pirate is irate.</p><p>';
        if ($drupalimage_is_enabled) {
            // Add an image with a link wrapped around it.
            $uri = $this->media->field_media_image->entity
                ->getFileUri();
            $src = file_url_transform_relative(file_create_url($uri));
            $this->host->body->value .= '<a href="http://www.drupal.org/association"><img alt="drupalimage test image" data-entity-type="" data-entity-uuid="" src="' . $src . '" /></a></p>';
        }
        $this->host
            ->save();
        $this->drupalGet($this->host
            ->toUrl('edit-form'));
        $this->waitForEditor();
        $this->assignNameToCkeditorIframe();
        $this->getSession()
            ->switchToIFrame('ckeditor');
        $assert_session = $this->assertSession();
        // Select the CKEditor Widget.
        $drupalmedia = $assert_session->waitForElementVisible('css', 'drupal-media');
        $this->assertNotEmpty($drupalmedia);
        $drupalmedia->click();
        // While the CKEditor Widget is selected, assert the context menu does not
        // contain link-related context menu items.
        $this->openContextMenu();
        $this->assignNameToCkeditorPanelIframe();
        $this->getSession()
            ->switchToIFrame('panel');
        $this->assertContextMenuItemNotExists('Edit Link');
        $this->assertContextMenuItemNotExists('Unlink');
        $this->closeContextMenu();
        // While the CKEditor Widget is selected, click the "link" button.
        $this->pressEditorButton('drupallink');
        $assert_session->waitForId('drupal-modal');
        // Enter a link in the link dialog and save.
        $field = $assert_session->waitForElementVisible('xpath', '//input[@name="attributes[href]"]');
        $this->assertNotEmpty($field);
        $field->setValue('https://www.drupal.org');
        $assert_session->elementExists('css', 'button.form-submit')
            ->press();
        $this->getSession()
            ->switchToIFrame('ckeditor');
        $link = $assert_session->waitForElementVisible('css', 'a[href="https://www.drupal.org"]');
        $this->assertNotEmpty($link);
        // Select the CKEditor Widget again and assert the context menu now does
        // contain link-related context menu items.
        $drupalmedia = $assert_session->waitForElementVisible('css', 'drupal-media');
        $this->assertNotEmpty($drupalmedia);
        $drupalmedia->click();
        $this->openContextMenu();
        $this->getSession()
            ->switchToIFrame('panel');
        $this->assertContextMenuItemExists('Edit Link');
        $this->assertContextMenuItemExists('Unlink');
        $this->closeContextMenu();
        // Save the entity.
        $this->getSession()
            ->switchToIFrame();
        $assert_session->buttonExists('Save')
            ->press();
        // Verify the saved entity when viewed also contains the linked media.
        $assert_session->elementExists('css', 'figure > a[href="https://www.drupal.org"] > .media--type-image > .field--type-image > img[src*="image-test.png"]');
        // Test that `drupallink` also still works independently: inserting a link
        // is possible.
        $this->drupalGet($this->host
            ->toUrl('edit-form'));
        $this->waitForEditor();
        $this->pressEditorButton('drupallink');
        $assert_session->waitForId('drupal-modal');
        $field = $assert_session->waitForElementVisible('xpath', '//input[@name="attributes[href]"]');
        $this->assertNotEmpty($field);
        $field->setValue('https://wikipedia.org');
        $assert_session->elementExists('css', 'button.form-submit')
            ->press();
        $this->assignNameToCkeditorIframe();
        $this->getSession()
            ->switchToIFrame('ckeditor');
        $link = $assert_session->waitForElementVisible('css', 'body > a[href="https://wikipedia.org"]');
        $this->assertNotEmpty($link);
        $assert_session->elementExists('css', 'body > .cke_widget_drupalmedia > drupal-media > figure > a[href="https://www.drupal.org"]');
        // Select the CKEditor Widget again and assert the `drupalunlink` button is
        // enabled. Also assert the context menu again contains link-related context
        // menu items.
        $drupalmedia = $assert_session->waitForElementVisible('css', 'drupal-media');
        $this->assertNotEmpty($drupalmedia);
        $drupalmedia->click();
        $this->openContextMenu();
        $this->getSession()
            ->switchToIFrame();
        $this->assertEditorButtonEnabled('drupalunlink');
        $this->assignNameToCkeditorPanelIframe();
        $this->getSession()
            ->switchToIFrame('panel');
        $this->assertContextMenuItemExists('Edit Link');
        $this->assertContextMenuItemExists('Unlink');
        // Test that moving focus to another element causes the `drupalunlink`
        // button to become disabled and causes link-related context menu items to
        // disappear.
        $this->getSession()
            ->switchToIFrame();
        $this->getSession()
            ->switchToIFrame('ckeditor');
        $p = $assert_session->waitForElementVisible('xpath', "//p[contains(text(), 'The pirate is irate')]");
        $this->assertNotEmpty($p);
        $p->click();
        $this->assertEditorButtonDisabled('drupalunlink');
        $this->getSession()
            ->switchToIFrame('panel');
        $this->assertContextMenuItemExists('Edit Link');
        $this->assertContextMenuItemExists('Unlink');
        // To switch from the context menu iframe ("panel") back to the CKEditor
        // iframe, we first have to reset to top frame.
        $this->getSession()
            ->switchToIFrame();
        $this->getSession()
            ->switchToIFrame('ckeditor');
        // Test that moving focus to the `drupalimage` CKEditor Widget enables the
        // `drupalunlink` button again, because it is a linked image.
        if ($drupalimage_is_enabled) {
            $drupalimage = $assert_session->waitForElementVisible('xpath', '//img[@alt="drupalimage test image"]');
            $this->assertNotEmpty($drupalimage);
            $drupalimage->click();
            $this->assertEditorButtonEnabled('drupalunlink');
            $this->getSession()
                ->switchToIFrame('panel');
            $this->assertContextMenuItemExists('Edit Link');
            $this->assertContextMenuItemExists('Unlink');
            $this->getSession()
                ->switchToIFrame();
            $this->getSession()
                ->switchToIFrame('ckeditor');
        }
        // Tests the `drupalunlink` button for the `drupalmedia` CKEditor Widget.
        $drupalmedia->click();
        $this->assertEditorButtonEnabled('drupalunlink');
        $this->getSession()
            ->switchToIFrame('panel');
        $this->assertContextMenuItemExists('Edit Link');
        $this->assertContextMenuItemExists('Unlink');
        $this->pressEditorButton('drupalunlink');
        $this->assertEditorButtonDisabled('drupalunlink');
        $this->getSession()
            ->switchToIFrame('ckeditor');
        $assert_session->elementNotExists('css', 'figure > a[href="https://www.drupal.org"] > .media--type-image > .field--type-image > img[src*="image-test.png"]');
        $assert_session->elementExists('css', 'figure .media--type-image > .field--type-image > img[src*="image-test.png"]');
        if ($drupalimage_is_enabled) {
            // Tests the `drupalunlink` button for the `drupalimage` CKEditor Widget.
            $drupalimage->click();
            $this->assertEditorButtonEnabled('drupalunlink');
            $this->pressEditorButton('drupalunlink');
            $this->assertEditorButtonDisabled('drupalunlink');
            $this->getSession()
                ->switchToIFrame('ckeditor');
            $assert_session->elementNotExists('css', 'p > a[href="https://www.drupal.org/association"] > img[src*="image-test.png"]');
            $assert_session->elementExists('css', 'p > img[src*="image-test.png"]');
        }
    }
    
    /**
     * Data Provider for ::testLinkability.
     */
    public function linkabilityProvider() {
        return [
            'linkability when `drupalimage` is enabled' => [
                TRUE,
            ],
            'linkability when `drupalimage` is disabled' => [
                FALSE,
            ],
        ];
    }
    
    /**
     * Tests preview route access.
     *
     * @param bool $media_embed_enabled
     *   Whether to test with media_embed filter enabled on the text format.
     * @param bool $can_use_format
     *   Whether the logged in user is allowed to use the text format.
     *
     * @dataProvider previewAccessProvider
     */
    public function testEmbedPreviewAccess($media_embed_enabled, $can_use_format) {
        // Reconfigure the host entity's text format to suit our needs.
        
        /** @var \Drupal\filter\FilterFormatInterface $format */
        $format = FilterFormat::load($this->host->body->format);
        $format->set('filters', [
            'filter_align' => [
                'status' => TRUE,
            ],
            'filter_caption' => [
                'status' => TRUE,
            ],
            'media_embed' => [
                'status' => $media_embed_enabled,
            ],
        ]);
        $format->save();
        $permissions = [
            'bypass node access',
        ];
        if ($can_use_format) {
            $permissions[] = $format->getPermissionName();
        }
        $this->drupalLogin($this->drupalCreateUser($permissions));
        $this->drupalGet($this->host
            ->toUrl('edit-form'));
        $assert_session = $this->assertSession();
        if ($can_use_format) {
            $this->waitForEditor();
            $this->assignNameToCkeditorIframe();
            $this->getSession()
                ->switchToIFrame('ckeditor');
            if ($media_embed_enabled) {
                $this->assertNotEmpty($assert_session->waitForElementVisible('css', 'article.media'));
            }
            else {
                $assert_session->assertWaitOnAjaxRequest();
                $assert_session->elementNotExists('css', 'article.media');
            }
        }
        else {
            $assert_session->pageTextContains('This field has been disabled because you do not have sufficient permissions to edit it.');
        }
    }
    
    /**
     * Data provider for ::testEmbedPreviewAccess.
     */
    public function previewAccessProvider() {
        return [
            'media_embed filter enabled' => [
                TRUE,
                TRUE,
            ],
            'media_embed filter disabled' => [
                FALSE,
                TRUE,
            ],
            'media_embed filter enabled, user not allowed to use text format' => [
                TRUE,
                FALSE,
            ],
        ];
    }
    
    /**
     * Tests alignment integration.
     *
     * Tests that alignment is reflected onto the CKEditor Widget wrapper, that
     * the EditorMediaDialog allows altering the alignment and that the changes
     * are reflected on the widget and downcast drupal-media tag.
     */
    public function testAlignment() {
        $assert_session = $this->assertSession();
        $this->drupalGet($this->host
            ->toUrl('edit-form'));
        $this->waitForEditor();
        $this->assignNameToCkeditorIframe();
        $this->getSession()
            ->switchToIFrame('ckeditor');
        // Wait for preview to load.
        $this->assertNotEmpty($img = $assert_session->waitForElement('css', 'drupal-media img'));
        // Assert the drupal-media element starts without a data-align attribute.
        $drupal_media = $assert_session->elementExists('css', 'drupal-media');
        $this->assertFalse($drupal_media->hasAttribute('data-align'));
        // Assert that setting the data-align property in the dialog adds the
        // `align-right', `align-left` or `align-center' class on the widget,
        // caption figure and drupal-media element.
        $alignments = [
            'right',
            'left',
            'center',
        ];
        foreach ($alignments as $alignment) {
            $this->fillFieldInMetadataDialogAndSubmit('attributes[data-align]', $alignment);
            // Wait for preview to load.
            $this->assertNotEmpty($img = $assert_session->waitForElement('css', 'drupal-media img'));
            // Now verify the result. Assert the first element within the
            // <drupal-media> element has the alignment class.
            $selector = sprintf('drupal-media[data-align="%s"] .caption-drupal-media.align-%s', $alignment, $alignment);
            $this->assertNotEmpty($assert_session->waitForElementVisible('css', $selector, 2000));
            // Assert that the resultant downcast drupal-media element has the proper
            // `data-align` attribute.
            $this->pressEditorButton('source');
            $this->assertSourceAttributeSame('data-align', $alignment);
            $this->leaveSourceMode();
        }
        // Test that setting the "Align" field to "none" in the dialog will
        // remove the attribute from the drupal-media element in the CKEditor.
        $this->fillFieldInMetadataDialogAndSubmit('attributes[data-align]', 'none');
        // Assert the drupal-media element no longer has data-align attribute.
        $this->assertNotEmpty($assert_session->waitForElementVisible('css', 'drupal-media .caption-drupal-media:not(.align-center)', 2000));
        $drupal_media = $assert_session->elementExists('css', 'drupal-media');
        $this->assertFalse($drupal_media->hasAttribute('data-align'));
        // Assert that the resultant downcast <drupal-media> tag has no data-align
        // attribute.
        $this->pressEditorButton('source');
        $this->assertNotEmpty($drupal_media = $this->getDrupalMediaFromSource());
        $this->assertFalse($drupal_media->hasAttribute('data-align'));
    }
    
    /**
     * Tests the EditorMediaDialog can set the data-view-mode attribute.
     */
    public function testViewMode() {
        EntityViewMode::create([
            'id' => 'media.view_mode_1',
            'targetEntityType' => 'media',
            'status' => TRUE,
            'enabled' => TRUE,
            'label' => 'View Mode 1',
        ])->save();
        EntityViewMode::create([
            'id' => 'media.view_mode_2',
            'targetEntityType' => 'media',
            'status' => TRUE,
            'enabled' => TRUE,
            'label' => 'View Mode 2',
        ])->save();
        $filter_format = FilterFormat::load('test_format');
        $filter_format->setFilterConfig('media_embed', [
            'status' => TRUE,
            'settings' => [
                'default_view_mode' => 'view_mode_1',
                'allowed_media_types' => [],
                'allowed_view_modes' => [
                    'view_mode_1' => 'view_mode_1',
                    'view_mode_2' => 'view_mode_2',
                ],
            ],
        ])
            ->save();
        // Test that view mode dependencies are returned from the MediaEmbed
        // filter's ::getDependencies() method.
        $expected_config_dependencies = [
            'core.entity_view_mode.media.view_mode_1',
            'core.entity_view_mode.media.view_mode_2',
        ];
        $dependencies = $filter_format->getDependencies();
        $this->assertArrayHasKey('config', $dependencies);
        $this->assertSame($expected_config_dependencies, $dependencies['config']);
        // Test MediaEmbed's allowed_view_modes option setting enables a view mode
        // selection field.
        $page = $this->getSession()
            ->getPage();
        $assert_session = $this->assertSession();
        $this->drupalGet($this->host
            ->toUrl('edit-form'));
        $this->waitForEditor();
        $this->assignNameToCkeditorIframe();
        $this->getSession()
            ->switchToIFrame('ckeditor');
        $this->assertNotEmpty($assert_session->waitForElementVisible('css', 'drupal-media'));
        $assert_session->elementExists('css', '.cke_widget_drupalmedia[aria-label="Screaming hairy armadillo"]');
        $page->pressButton('Edit media');
        $this->waitForMetadataDialog();
        $assert_session->optionExists('attributes[data-view-mode]', 'view_mode_1');
        $assert_session->optionExists('attributes[data-view-mode]', 'view_mode_2');
        $assert_session->selectExists('attributes[data-view-mode]')
            ->selectOption('view_mode_2');
        $this->submitDialog();
        $this->getSession()
            ->switchToIFrame('ckeditor');
        $this->assertNotEmpty($assert_session->waitForElementVisible('css', 'article.media--view-mode-view-mode-2'));
        // Test that the downcast drupal-media element contains the
        // `data-view-mode` attribute set in the dialog.
        $this->pressEditorButton('source');
        $this->assertNotEmpty($drupal_media = $this->getDrupalMediaFromSource());
        $this->assertSame('view_mode_2', $drupal_media->getAttribute('data-view-mode'));
        // Press the source button again to leave source mode.
        $this->pressEditorButton('source');
        // Having entered source mode means we need to reassign an ID to the
        // CKEditor iframe.
        $this->assignNameToCkeditorIframe();
        $this->getSession()
            ->switchToIFrame('ckeditor');
        // Test that setting the allowed_view_modes option to only one option hides
        // the field (it requires more than one option).
        $filter_format->setFilterConfig('media_embed', [
            'status' => TRUE,
            'settings' => [
                'default_view_mode' => 'view_mode_1',
                'allowed_media_types' => [],
                'allowed_view_modes' => [
                    'view_mode_1' => 'view_mode_1',
                ],
            ],
        ])
            ->save();
        // Test that the dependencies change when the allowed_view_modes change.
        $dependencies = $filter_format->getDependencies();
        $this->assertArrayHasKey('config', $dependencies);
        $this->assertSame([
            'core.entity_view_mode.media.view_mode_1',
        ], $dependencies['config']);
        $this->assertNotEmpty($assert_session->waitForElementVisible('css', 'drupal-media'));
        $page->pressButton('Edit media');
        $this->waitForMetadataDialog();
        $assert_session->fieldNotExists('attributes[data-view-mode]');
        $page->pressButton('Close');
        $this->getSession()
            ->switchToIFrame('ckeditor');
        // Test that setting allowed_view_modes back to two items restores the
        // field.
        $filter_format->setFilterConfig('media_embed', [
            'status' => TRUE,
            'settings' => [
                'default_view_mode' => 'view_mode_1',
                'allowed_media_types' => [],
                'allowed_view_modes' => [
                    'view_mode_1' => 'view_mode_1',
                    'view_mode_2' => 'view_mode_2',
                ],
            ],
        ])
            ->save();
        // Test that the dependencies change when the allowed_view_modes change.
        $expected_config_dependencies = [
            'core.entity_view_mode.media.view_mode_1',
            'core.entity_view_mode.media.view_mode_2',
        ];
        $dependencies = $filter_format->getDependencies();
        $this->assertArrayHasKey('config', $dependencies);
        $this->assertSame($expected_config_dependencies, $dependencies['config']);
        // Test that setting the view mode back to the default removes the
        // `data-view-mode` attribute.
        $this->assertNotEmpty($assert_session->waitForElementVisible('css', 'drupal-media'));
        $page->pressButton('Edit media');
        $this->waitForMetadataDialog();
        $assert_session->optionExists('attributes[data-view-mode]', 'view_mode_1');
        $assert_session->optionExists('attributes[data-view-mode]', 'view_mode_2');
        $assert_session->selectExists('attributes[data-view-mode]')
            ->selectOption('view_mode_1');
        $this->submitDialog();
        $this->getSession()
            ->switchToIFrame('ckeditor');
        $this->assertNotEmpty($assert_session->waitForElementVisible('css', 'article.media--view-mode-view-mode-1'));
        $this->pressEditorButton('source');
        $this->assertNotEmpty($drupal_media = $this->getDrupalMediaFromSource());
        $this->assertFalse($drupal_media->hasAttribute('data-view-mode'));
        // Test that changing the view mode with an empty editable caption
        // preserves the empty editable caption when the preview reloads.
        $original_value = $this->host->body->value;
        $this->host->body->value = str_replace('data-caption="baz"', '', $original_value);
        $this->host
            ->save();
        $this->drupalGet($this->host
            ->toUrl('edit-form'));
        $this->waitForEditor();
        $this->assignNameToCkeditorIframe();
        $this->getSession()
            ->switchToIFrame('ckeditor');
        // Wait for preview to load with default view mode.
        $this->assertNotEmpty($assert_session->waitForElementVisible('css', 'article.media--view-mode-view-mode-1'));
    }
    
    /**
     * Waits for the form that allows editing metadata.
     *
     * @see \Drupal\media\Form\EditorMediaDialog
     */
    protected function waitForMetadataDialog() {
        $page = $this->getSession()
            ->getPage();
        $this->getSession()
            ->switchToIFrame();
        // Wait for the dialog to open.
        $result = $page->waitFor(10, function ($page) {
            $metadata_editor = $page->find('css', 'form.editor-media-dialog');
            return !empty($metadata_editor);
        });
        $this->assertTrue($result);
    }
    
    /**
     * Fills in a field in the metadata dialog for an embedded media item.
     *
     * This method assumes that the calling code has already switched into the
     * CKEditor iframe.
     *
     * @param string $locator
     *   The field ID, name, or label.
     * @param string $value
     *   The value to set on the field.
     */
    protected function fillFieldInMetadataDialogAndSubmit($locator, $value) {
        // Wait for the drupal-media which holds the "Edit media" button which
        // opens the dialog.
        $this->openMetadataDialog();
        $this->getSession()
            ->getPage()
            ->fillField($locator, $value);
        $this->submitDialog();
        // Since ::waitforMetadataDialog() switches back to the main iframe, we'll
        // need to switch back.
        $this->getSession()
            ->switchToIFrame('ckeditor');
    }
    
    /**
     * Clicks the `Edit media` button and waits for the metadata dialog.
     *
     * This method assumes that the calling code has already switched into the
     * CKEditor iframe.
     */
    protected function openMetadataDialog() {
        $this->assertNotEmpty($embedded_media = $this->assertSession()
            ->waitForElementVisible('css', 'drupal-media'));
        $embedded_media->pressButton('Edit media');
        $this->waitForMetadataDialog();
    }
    
    /**
     * Focuses on `Edit media` button and presses the given key.
     *
     * @param int $char
     *   The character code to press.
     *
     *   This method assumes that the calling code has already switched into the
     *   CKEditor iframe.
     */
    protected function openMetadataDialogWithKeyPress($char) {
        $this->assertNotEmpty($button = $this->assertSession()
            ->waitForButton('Edit media'));
        $button->keyDown($char);
        $this->waitForMetadataDialog();
    }
    
    /**
     * Leaves source mode and returns to the CKEditor iframe.
     */
    protected function leaveSourceMode() {
        // Press the source button again to leave source mode.
        $this->pressEditorButton('source');
        // Having entered source mode means we need to reassign an ID to the
        // CKEditor iframe.
        $this->assignNameToCkeditorIframe();
        $this->getSession()
            ->switchToIFrame('ckeditor');
    }
    
    /**
     * Verifies value of an attribute on the downcast <drupal-media> element.
     *
     * Assumes CKEditor is in source mode.
     *
     * @param string $attribute
     *   The attribute to check.
     * @param mixed $value
     *   Either a string value or if NULL, asserts that <drupal-media> element
     *   doesn't have the attribute.
     */
    protected function assertSourceAttributeSame($attribute, $value) {
        $this->assertNotEmpty($drupal_media = $this->getDrupalMediaFromSource());
        if ($value === NULL) {
            $this->assertFalse($drupal_media->hasAttribute($attribute));
        }
        else {
            $this->assertSame($value, $drupal_media->getAttribute($attribute));
        }
    }
    
    /**
     * Closes and submits the metadata dialog.
     */
    protected function submitDialog() {
        $this->assertNotEmpty($dialog_buttons = $this->assertSession()
            ->elementExists('css', 'div.ui-dialog-buttonpane'));
        $dialog_buttons->pressButton('Save');
    }
    
    /**
     * Closes the metadata dialog.
     */
    protected function closeDialog() {
        $page = $this->getSession()
            ->getPage();
        $page->pressButton('Close');
        $result = $page->waitFor(10, function ($page) {
            $metadata_editor = $page->find('css', 'form.editor-media-dialog');
            return empty($metadata_editor);
        });
        $this->assertTrue($result);
    }
    
    /**
     * Gets the transfer size of the last preview request.
     *
     * @return int
     *   The size of the bytes transferred.
     */
    protected function getLastPreviewRequestTransferSize() {
        $this->getSession()
            ->switchToIFrame();
        $javascript = <<<JS
(function(){
  return window.performance
    .getEntries()
    .filter(function (entry) {
      return entry.initiatorType == 'xmlhttprequest' && entry.name.indexOf('/media/test_format/preview') !== -1;
    })
    .pop()
    .transferSize;
})()
JS;
        return $this->getSession()
            ->evaluateScript($javascript);
    }
    
    /**
     * Sets the text of the editable caption to the given text.
     *
     * @param string $text
     *   The text to set in the caption.
     */
    protected function setCaption($text) {
        $this->getSession()
            ->switchToIFrame();
        $select_and_edit_caption = "var editor = CKEDITOR.instances['edit-body-0-value'];\n       var figcaption = editor.widgets.getByElement(editor.editable().findOne('figcaption'));\n       figcaption.editables.caption.setData('" . $text . "')";
        $this->getSession()
            ->executeScript($select_and_edit_caption);
    }
    
    /**
     * Assigns a name to the CKEditor context menu iframe.
     *
     * Note that this iframe doesn't appear until context menu appears.
     *
     * @see \Behat\Mink\Session::switchToIFrame()
     */
    protected function assignNameToCkeditorPanelIframe() {
        $javascript = <<<JS
(function(){
  document.getElementsByClassName('cke_panel_frame')[0].id = 'panel';
})()
JS;
        $this->getSession()
            ->evaluateScript($javascript);
    }
    
    /**
     * Opens the context menu for the currently selected widget.
     *
     * @param string $instance_id
     *   The CKEditor instance ID.
     */
    protected function openContextMenu($instance_id = 'edit-body-0-value') {
        $this->getSession()
            ->switchToIFrame();
        $script = <<<JS
      (function() {
        var editor = CKEDITOR.instances["{<span class="php-variable">$instance_id</span>}"];
        editor.contextMenu.open(editor.widgets.selected[0].element);
      }());
JS;
        $this->getSession()
            ->executeScript($script);
    }
    
    /**
     * Asserts that a context menu item exists by aria-label attribute.
     *
     * @param string $label
     *   The `aria-label` attribute value of the context menu item.
     */
    protected function assertContextMenuItemExists($label) {
        $this->assertSession()
            ->elementExists('xpath', '//a[@aria-label="' . $label . '"]');
    }
    
    /**
     * Asserts that a context menu item does not exist by aria-label attribute.
     *
     * @param string $label
     *   The `aria-label` attribute value of the context menu item.
     */
    protected function assertContextMenuItemNotExists($label) {
        $this->assertSession()
            ->elementNotExists('xpath', '//a[@aria-label="' . $label . '"]');
    }
    
    /**
     * Closes the open context menu.
     *
     * @param string $instance_id
     *   The CKEditor instance ID.
     */
    protected function closeContextMenu($instance_id = 'edit-body-0-value') {
        $this->getSession()
            ->switchToIFrame();
        $script = <<<JS
      (function() {
        var editor = CKEDITOR.instances["{<span class="php-variable">$instance_id</span>}"];
        editor.contextMenu.hide();
      }());
JS;
        $this->getSession()
            ->executeScript($script);
    }
    
    /**
     * Clicks a link in the editor's path links with the given title text.
     *
     * @param string $text
     *   The title attribute of the link to click.
     */
    protected function clickPathLinkByTitleAttribute($text) {
        $this->getSession()
            ->switchToIFrame();
        $selector = '//span[@id="cke_1_path"]//a[@title="' . $text . '"]';
        $this->assertSession()
            ->elementExists('xpath', $selector)
            ->click();
    }
    
    /**
     * Parses the <drupal-media> element from CKEditor's "source" view.
     *
     * Assumes CKEditor is in source mode.
     *
     * @return \DOMNode|null
     *   The drupal-media element or NULL if it can't be found.
     */
    protected function getDrupalMediaFromSource() {
        $value = $this->assertSession()
            ->elementExists('css', 'textarea.cke_source')
            ->getValue();
        $dom = Html::load($value);
        $xpath = new \DOMXPath($dom);
        $list = $xpath->query('//drupal-media');
        return count($list) > 0 ? $list[0] : NULL;
    }

}

Members

Title Sort descending Deprecated Modifiers Object type Summary Member alias Overriden Title Overrides
AssertHelperTrait::castSafeStrings protected static function Casts MarkupInterface objects into strings.
AssertLegacyTrait::assert protected function Deprecated Scheduled for removal in Drupal 10.0.0. Use self::assertTrue()
instead.
AssertLegacyTrait::assertCacheTag protected function Asserts whether an expected cache tag was present in the last response.
AssertLegacyTrait::assertElementNotPresent protected function Asserts that the element with the given CSS selector is not present.
AssertLegacyTrait::assertElementPresent protected function Asserts that the element with the given CSS selector is present.
AssertLegacyTrait::assertEqual protected function Deprecated Scheduled for removal in Drupal 10.0.0. Use self::assertEquals()
instead.
AssertLegacyTrait::assertEscaped protected function Passes if the raw text IS found escaped on the loaded page, fail otherwise.
AssertLegacyTrait::assertField protected function Asserts that a field exists with the given name or ID.
AssertLegacyTrait::assertFieldById protected function Asserts that a field exists with the given ID and value.
AssertLegacyTrait::assertFieldByName protected function Asserts that a field exists with the given name and value.
AssertLegacyTrait::assertFieldByXPath protected function Asserts that a field exists in the current page by the given XPath.
AssertLegacyTrait::assertFieldChecked protected function Asserts that a checkbox field in the current page is checked.
AssertLegacyTrait::assertFieldsByValue protected function Asserts that a field exists in the current page with a given Xpath result.
AssertLegacyTrait::assertHeader protected function Checks that current response header equals value.
AssertLegacyTrait::assertIdentical protected function Deprecated Scheduled for removal in Drupal 10.0.0. Use self::assertSame()
instead.
AssertLegacyTrait::assertIdenticalObject protected function Deprecated Scheduled for removal in Drupal 10.0.0. Use self::assertEquals()
instead.
AssertLegacyTrait::assertLink protected function Passes if a link with the specified label is found.
AssertLegacyTrait::assertLinkByHref protected function Passes if a link containing a given href (part) is found.
AssertLegacyTrait::assertNoCacheTag protected function Asserts whether an expected cache tag was absent in the last response.
AssertLegacyTrait::assertNoEscaped protected function Passes if the raw text is not found escaped on the loaded page.
AssertLegacyTrait::assertNoField protected function Asserts that a field does NOT exist with the given name or ID.
AssertLegacyTrait::assertNoFieldById protected function Asserts that a field does not exist with the given ID and value.
AssertLegacyTrait::assertNoFieldByName protected function Asserts that a field does not exist with the given name and value.
AssertLegacyTrait::assertNoFieldByXPath protected function Asserts that a field does not exist or its value does not match, by XPath.
AssertLegacyTrait::assertNoFieldChecked protected function Asserts that a checkbox field in the current page is not checked.
AssertLegacyTrait::assertNoLink protected function Passes if a link with the specified label is not found.
AssertLegacyTrait::assertNoLinkByHref protected function Passes if a link containing a given href (part) is not found.
AssertLegacyTrait::assertNoOption protected function Asserts that a select option does NOT exist in the current page.
AssertLegacyTrait::assertNoPattern protected function Triggers a pass if the Perl regex pattern is not found in the raw content.
AssertLegacyTrait::assertNoRaw protected function Passes if the raw text IS not found on the loaded page, fail otherwise. 1
AssertLegacyTrait::assertNotEqual protected function Deprecated Scheduled for removal in Drupal 10.0.0. Use
self::assertNotEquals() instead.
AssertLegacyTrait::assertNoText protected function Passes if the page (with HTML stripped) does not contains the text. 1
AssertLegacyTrait::assertNotIdentical protected function Deprecated Scheduled for removal in Drupal 10.0.0. Use
self::assertNotSame() instead.
AssertLegacyTrait::assertNoUniqueText protected function Passes if the text is found MORE THAN ONCE on the text version of the page.
AssertLegacyTrait::assertOption protected function Asserts that a select option in the current page exists.
AssertLegacyTrait::assertOptionByText protected function Asserts that a select option with the visible text exists.
AssertLegacyTrait::assertOptionSelected protected function Asserts that a select option in the current page is checked.
AssertLegacyTrait::assertPattern protected function Triggers a pass if the Perl regex pattern is found in the raw content.
AssertLegacyTrait::assertRaw protected function Passes if the raw text IS found on the loaded page, fail otherwise. 1
AssertLegacyTrait::assertResponse protected function Asserts the page responds with the specified response code. 1
AssertLegacyTrait::assertText protected function Passes if the page (with HTML stripped) contains the text. 1
AssertLegacyTrait::assertTextHelper protected function Helper for assertText and assertNoText.
AssertLegacyTrait::assertTitle protected function Pass if the page title is the given string.
AssertLegacyTrait::assertUniqueText protected function Passes if the text is found ONLY ONCE on the text version of the page.
AssertLegacyTrait::assertUrl protected function Passes if the internal browser&#039;s URL matches the given path.
AssertLegacyTrait::buildXPathQuery protected function Builds an XPath query.
AssertLegacyTrait::constructFieldXpath protected function Helper: Constructs an XPath for the given set of attributes and value.
AssertLegacyTrait::getAllOptions protected function Get all option elements, including nested options, in a select.
AssertLegacyTrait::getRawContent protected function Gets the current raw content.
AssertLegacyTrait::pass protected function Deprecated Scheduled for removal in Drupal 10.0.0. Use self::assertTrue()
instead.
AssertLegacyTrait::verbose protected function
BlockCreationTrait::placeBlock protected function Creates a block instance based on default settings. Aliased as: drupalPlaceBlock
BrowserHtmlDebugTrait::$htmlOutputBaseUrl protected property The Base URI to use for links to the output files.
BrowserHtmlDebugTrait::$htmlOutputClassName protected property Class name for HTML output logging.
BrowserHtmlDebugTrait::$htmlOutputCounter protected property Counter for HTML output logging.
BrowserHtmlDebugTrait::$htmlOutputCounterStorage protected property Counter storage for HTML output logging.
BrowserHtmlDebugTrait::$htmlOutputDirectory protected property Directory name for HTML output logging.
BrowserHtmlDebugTrait::$htmlOutputEnabled protected property HTML output output enabled.
BrowserHtmlDebugTrait::$htmlOutputFile protected property The file name to write the list of URLs to.
BrowserHtmlDebugTrait::$htmlOutputTestId protected property HTML output test ID.
BrowserHtmlDebugTrait::formatHtmlOutputHeaders protected function Formats HTTP headers as string for HTML output logging.
BrowserHtmlDebugTrait::htmlOutput protected function Logs a HTML output message in a text file.
BrowserHtmlDebugTrait::initBrowserOutputFile protected function Creates the directory to store browser output.
BrowserTestBase::$baseUrl protected property The base URL.
BrowserTestBase::$configImporter protected property The config importer that can be used in a test.
BrowserTestBase::$customTranslations protected property An array of custom translations suitable for drupal_rewrite_settings().
BrowserTestBase::$databasePrefix protected property The database prefix of this test run.
BrowserTestBase::$mink protected property Mink session manager.
BrowserTestBase::$minkDefaultDriverArgs protected property
BrowserTestBase::$originalContainer protected property The original container.
BrowserTestBase::$originalShutdownCallbacks protected property The original array of shutdown function callbacks.
BrowserTestBase::$preserveGlobalState protected property
BrowserTestBase::$profile protected property The profile to install as a basis for testing. 39
BrowserTestBase::$root protected property The app root.
BrowserTestBase::$runTestInSeparateProcess protected property Browser tests are run in separate processes to prevent collisions between
code that may be loaded by tests.
BrowserTestBase::$timeLimit protected property Time limit in seconds for the test.
BrowserTestBase::$translationFilesDirectory protected property The translation file directory for the test environment.
BrowserTestBase::cleanupEnvironment protected function Clean up the Simpletest environment.
BrowserTestBase::config protected function Configuration accessor for tests. Returns non-overridden configuration.
BrowserTestBase::cssSelectToXpath protected function Translates a CSS expression to its XPath equivalent.
BrowserTestBase::drupalGetHeader protected function Gets the value of an HTTP response header.
BrowserTestBase::drupalGetHeaders Deprecated protected function Returns all response headers.
BrowserTestBase::filePreDeleteCallback public static function Ensures test files are deletable.
BrowserTestBase::getDefaultDriverInstance protected function Gets an instance of the default Mink driver.
BrowserTestBase::getHttpClient protected function Obtain the HTTP client for the system under test.
BrowserTestBase::getOptions protected function Helper function to get the options of select field.
BrowserTestBase::getResponseLogHandler protected function Provides a Guzzle middleware handler to log every response received. Overrides BrowserHtmlDebugTrait::getResponseLogHandler
BrowserTestBase::getSession public function Returns Mink session.
BrowserTestBase::getSessionCookies protected function Get session cookies from current session.
BrowserTestBase::getTestMethodCaller protected function Retrieves the current calling line in the class under test. Overrides BrowserHtmlDebugTrait::getTestMethodCaller
BrowserTestBase::installDrupal public function Installs Drupal into the Simpletest site. 1
BrowserTestBase::registerSessions protected function Registers additional Mink sessions.
BrowserTestBase::translatePostValues protected function Transforms a nested array into a flat array suitable for drupalPostForm().
BrowserTestBase::xpath protected function Performs an xpath search on the contents of the internal browser.
BrowserTestBase::__construct public function 1
BrowserTestBase::__sleep public function Prevents serializing any properties.
CKEditorIntegrationTest::$adminUser protected property The user to use during testing.
CKEditorIntegrationTest::$defaultTheme protected property The theme to install as the default for testing. Overrides BrowserTestBase::$defaultTheme
CKEditorIntegrationTest::$host protected property A host entity with a body field to embed media in.
CKEditorIntegrationTest::$media protected property The sample Media entity to embed.
CKEditorIntegrationTest::$modules protected static property Modules to enable. Overrides BrowserTestBase::$modules
CKEditorIntegrationTest::assertContextMenuItemExists protected function Asserts that a context menu item exists by aria-label attribute.
CKEditorIntegrationTest::assertContextMenuItemNotExists protected function Asserts that a context menu item does not exist by aria-label attribute.
CKEditorIntegrationTest::assertSourceAttributeSame protected function Verifies value of an attribute on the downcast &lt;drupal-media&gt; element.
CKEditorIntegrationTest::assignNameToCkeditorPanelIframe protected function Assigns a name to the CKEditor context menu iframe.
CKEditorIntegrationTest::clickPathLinkByTitleAttribute protected function Clicks a link in the editor&#039;s path links with the given title text.
CKEditorIntegrationTest::closeContextMenu protected function Closes the open context menu.
CKEditorIntegrationTest::closeDialog protected function Closes the metadata dialog.
CKEditorIntegrationTest::fillFieldInMetadataDialogAndSubmit protected function Fills in a field in the metadata dialog for an embedded media item.
CKEditorIntegrationTest::getDrupalMediaFromSource protected function Parses the &lt;drupal-media&gt; element from CKEditor&#039;s &quot;source&quot; view.
CKEditorIntegrationTest::getLastPreviewRequestTransferSize protected function Gets the transfer size of the last preview request.
CKEditorIntegrationTest::leaveSourceMode protected function Leaves source mode and returns to the CKEditor iframe.
CKEditorIntegrationTest::linkabilityProvider public function Data Provider for ::testLinkability.
CKEditorIntegrationTest::openContextMenu protected function Opens the context menu for the currently selected widget.
CKEditorIntegrationTest::openMetadataDialog protected function Clicks the `Edit media` button and waits for the metadata dialog.
CKEditorIntegrationTest::openMetadataDialogWithKeyPress protected function Focuses on `Edit media` button and presses the given key.
CKEditorIntegrationTest::previewAccessProvider public function Data provider for ::testEmbedPreviewAccess.
CKEditorIntegrationTest::RETURN_KEY constant The character code for the return key.
CKEditorIntegrationTest::setCaption protected function Sets the text of the editable caption to the given text.
CKEditorIntegrationTest::setUp protected function Overrides BrowserTestBase::setUp
CKEditorIntegrationTest::SPACE_BAR constant The character code for the space bar.
CKEditorIntegrationTest::submitDialog protected function Closes and submits the metadata dialog.
CKEditorIntegrationTest::testAlignment public function Tests alignment integration.
CKEditorIntegrationTest::testAlt public function Tests the EditorMediaDialog can set the alt attribute.
CKEditorIntegrationTest::testDialogAccess public function Test the EditorMediaDialog&#039;s form elements&#039; #access logic.
CKEditorIntegrationTest::testEditableCaption public function Tests caption editing in the CKEditor widget.
CKEditorIntegrationTest::testEmbedPreviewAccess public function Tests preview route access.
CKEditorIntegrationTest::testErrorMessages public function Tests that failed media embed preview requests inform the end user.
CKEditorIntegrationTest::testLinkability public function Tests linkability of the CKEditor widget.
CKEditorIntegrationTest::testOnlyDrupalMediaTagProcessed public function Tests that only &lt;drupal-media&gt; tags are processed.
CKEditorIntegrationTest::testPreviewUsesDefaultThemeAndIsClientCacheable public function The CKEditor Widget must load a preview generated using the default theme.
CKEditorIntegrationTest::testTranslationAlt public function Test that dialog loads appropriate translation&#039;s alt text.
CKEditorIntegrationTest::testViewMode public function Tests the EditorMediaDialog can set the data-view-mode attribute.
CKEditorIntegrationTest::waitForMetadataDialog protected function Waits for the form that allows editing metadata.
CKEditorTestTrait::assertEditorButtonDisabled protected function Asserts a CKEditor button is disabled.
CKEditorTestTrait::assertEditorButtonEnabled protected function Asserts a CKEditor button is enabled.
CKEditorTestTrait::assignNameToCkeditorIframe protected function Assigns a name to the CKEditor iframe.
CKEditorTestTrait::getEditorButton protected function Waits for a CKEditor button and returns it when available and visible.
CKEditorTestTrait::pressEditorButton protected function Clicks a CKEditor button.
CKEditorTestTrait::waitForEditor protected function Waits for CKEditor to initialize.
ConfigTestTrait::configImporter protected function Returns a ConfigImporter object to import test configuration.
ConfigTestTrait::copyConfig protected function Copies configuration objects from source storage to target storage.
ContentTypeCreationTrait::createContentType protected function Creates a custom content type based on default settings. Aliased as: drupalCreateContentType 1
FunctionalTestSetupTrait::$apcuEnsureUniquePrefix protected property The flag to set &#039;apcu_ensure_unique_prefix&#039; setting. 1
FunctionalTestSetupTrait::$classLoader protected property The class loader to use for installation and initialization of setup.
FunctionalTestSetupTrait::$configDirectories Deprecated protected property The config directories used in this test.
FunctionalTestSetupTrait::$rootUser protected property The &quot;#1&quot; admin user.
FunctionalTestSetupTrait::doInstall protected function Execute the non-interactive installer. 1
FunctionalTestSetupTrait::getDatabaseTypes protected function Returns all supported database driver installer objects.
FunctionalTestSetupTrait::initConfig protected function Initialize various configurations post-installation. 2
FunctionalTestSetupTrait::initKernel protected function Initializes the kernel after installation.
FunctionalTestSetupTrait::initSettings protected function Initialize settings created during install.
FunctionalTestSetupTrait::initUserSession protected function Initializes user 1 for the site to be installed.
FunctionalTestSetupTrait::installDefaultThemeFromClassProperty protected function Installs the default theme defined by `static::$defaultTheme` when needed.
FunctionalTestSetupTrait::installParameters protected function Returns the parameters that will be used when Simpletest installs Drupal. 9
FunctionalTestSetupTrait::prepareEnvironment protected function Prepares the current environment for running the test. 23
FunctionalTestSetupTrait::prepareRequestForGenerator protected function Creates a mock request and sets it on the generator.
FunctionalTestSetupTrait::prepareSettings protected function Prepares site settings and services before installation. 2
FunctionalTestSetupTrait::rebuildAll protected function Resets and rebuilds the environment after setup.
FunctionalTestSetupTrait::rebuildContainer protected function Rebuilds \Drupal::getContainer().
FunctionalTestSetupTrait::resetAll protected function Resets all data structures after having enabled new modules.
FunctionalTestSetupTrait::setContainerParameter protected function Changes parameters in the services.yml file.
FunctionalTestSetupTrait::setupBaseUrl protected function Sets up the base URL based upon the environment variable.
FunctionalTestSetupTrait::writeSettings protected function Rewrites the settings.php file of the test site.
MediaTypeCreationTrait::createMediaType protected function Create a media type for a source plugin.
NodeCreationTrait::createNode protected function Creates a node based on default settings. Aliased as: drupalCreateNode
NodeCreationTrait::getNodeByTitle public function Get a node from the database based on its title. Aliased as: drupalGetNodeByTitle
PhpunitCompatibilityTrait::getMock Deprecated public function Returns a mock object for the specified class using the available method.
PhpunitCompatibilityTrait::setExpectedException Deprecated public function Compatibility layer for PHPUnit 6 to support PHPUnit 4 code.
RandomGeneratorTrait::$randomGenerator protected property The random generator.
RandomGeneratorTrait::getRandomGenerator protected function Gets the random generator for the utility methods.
RandomGeneratorTrait::randomMachineName protected function Generates a unique random string containing letters and numbers. 1
RandomGeneratorTrait::randomObject public function Generates a random PHP object.
RandomGeneratorTrait::randomString public function Generates a pseudo-random string of ASCII characters of codes 32 to 126.
RandomGeneratorTrait::randomStringValidate public function Callback for random string validation.
RefreshVariablesTrait::refreshVariables protected function Refreshes in-memory configuration and state information. 3
SessionTestTrait::$sessionName protected property The name of the session cookie.
SessionTestTrait::generateSessionName protected function Generates a session cookie name.
SessionTestTrait::getSessionName protected function Returns the session name in use on the child site.
StorageCopyTrait::replaceStorageContents protected static function Copy the configuration from one storage to another and remove stale items.
TestFileCreationTrait::$generatedTestFiles protected property Whether the files were copied to the test files directory.
TestFileCreationTrait::compareFiles protected function Compares two files based on size and file name.
TestFileCreationTrait::generateFile public static function Generates a test file.
TestFileCreationTrait::getTestFiles protected function Gets a list of files that can be used in tests.
TestRequirementsTrait::checkModuleRequirements private function Checks missing module requirements.
TestRequirementsTrait::checkRequirements protected function Check module requirements for the Drupal use case. 1
TestRequirementsTrait::getDrupalRoot protected static function Returns the Drupal root directory.
TestSetupTrait::$configSchemaCheckerExclusions protected static property An array of config object names that are excluded from schema checking.
TestSetupTrait::$container protected property The dependency injection container used in the test.
TestSetupTrait::$kernel protected property The DrupalKernel instance used in the test.
TestSetupTrait::$originalSite protected property The site directory of the original parent site.
TestSetupTrait::$privateFilesDirectory protected property The private file directory for the test environment.
TestSetupTrait::$publicFilesDirectory protected property The public file directory for the test environment.
TestSetupTrait::$siteDirectory protected property The site directory of this test run.
TestSetupTrait::$strictConfigSchema protected property Set to TRUE to strict check all configuration saved. 2
TestSetupTrait::$tempFilesDirectory protected property The temporary file directory for the test environment.
TestSetupTrait::$testId protected property The test run ID.
TestSetupTrait::changeDatabasePrefix protected function Changes the database connection to the prefixed one.
TestSetupTrait::getConfigSchemaExclusions protected function Gets the config schema exclusions for this test.
TestSetupTrait::getDatabaseConnection public static function Returns the database connection to the site running Simpletest.
TestSetupTrait::prepareDatabasePrefix protected function Generates a database prefix for running tests. 2
UiHelperTrait::$loggedInUser protected property The current user logged in using the Mink controlled browser.
UiHelperTrait::$maximumMetaRefreshCount protected property The number of meta refresh redirects to follow, or NULL if unlimited.
UiHelperTrait::$metaRefreshCount protected property The number of meta refresh redirects followed during ::drupalGet().
UiHelperTrait::buildUrl protected function Builds an a absolute URL from a system path or a URL object.
UiHelperTrait::checkForMetaRefresh protected function Checks for meta refresh tag and if found call drupalGet() recursively.
UiHelperTrait::click protected function Clicks the element with the given CSS selector.
UiHelperTrait::clickLink protected function Follows a link by complete name.
UiHelperTrait::cssSelect protected function Searches elements using a CSS selector in the raw content.
UiHelperTrait::drupalGet protected function Retrieves a Drupal path or an absolute path. 3
UiHelperTrait::drupalLogin protected function Logs in a user using the Mink controlled browser.
UiHelperTrait::drupalLogout protected function Logs a user out of the Mink controlled browser and confirms.
UiHelperTrait::drupalPostForm protected function Executes a form submission.
UiHelperTrait::drupalUserIsLoggedIn protected function Returns whether a given user account is logged in.
UiHelperTrait::getAbsoluteUrl protected function Takes a path and returns an absolute path.
UiHelperTrait::getTextContent protected function Retrieves the plain-text content from the current page.
UiHelperTrait::getUrl protected function Get the current URL from the browser.
UiHelperTrait::prepareRequest protected function Prepare for a request to testing site. 1
UiHelperTrait::submitForm protected function Fills and submits a form.
UserCreationTrait::checkPermissions protected function Checks whether a given list of permission names is valid.
UserCreationTrait::createAdminRole protected function Creates an administrative role.
UserCreationTrait::createRole protected function Creates a role with specified permissions. Aliased as: drupalCreateRole
UserCreationTrait::createUser protected function Create a user with a given set of permissions. Aliased as: drupalCreateUser
UserCreationTrait::grantPermissions protected function Grant permissions to a user role.
UserCreationTrait::setCurrentUser protected function Switch the current logged in user.
UserCreationTrait::setUpCurrentUser protected function Creates a random user account and sets it as current user.
WebDriverTestBase::$disableCssAnimations protected property Disables CSS animations in tests for more reliable testing.
WebDriverTestBase::$minkDefaultDriverClass protected property To use a legacy phantomjs based approach, please use PhantomJSDriver::class. Overrides BrowserTestBase::$minkDefaultDriverClass 2
WebDriverTestBase::assertElementNotVisible Deprecated protected function Asserts that the element with the given CSS selector is not visible.
WebDriverTestBase::assertElementVisible Deprecated protected function Asserts that the element with the given CSS selector is visible.
WebDriverTestBase::assertJsCondition protected function Waits for the given time or until the given JS condition becomes TRUE.
WebDriverTestBase::assertSession public function Returns WebAssert object. Overrides UiHelperTrait::assertSession 1
WebDriverTestBase::createScreenshot protected function Creates a screenshot.
WebDriverTestBase::getDrupalSettings protected function Gets the current Drupal javascript settings and parses into an array. Overrides BrowserTestBase::getDrupalSettings
WebDriverTestBase::getHtmlOutputHeaders protected function Returns headers in HTML output format. Overrides BrowserHtmlDebugTrait::getHtmlOutputHeaders
WebDriverTestBase::getMinkDriverArgs protected function Get the Mink driver args from an environment variable, if it is set. Can
be overridden in a derived class so it is possible to use a different
value for a subset of tests, e.g. the JavaScript tests.
Overrides BrowserTestBase::getMinkDriverArgs
WebDriverTestBase::initFrontPage protected function Visits the front page when initializing Mink. Overrides BrowserTestBase::initFrontPage
WebDriverTestBase::initMink protected function Initializes Mink sessions. Overrides BrowserTestBase::initMink 1
WebDriverTestBase::installModulesFromClassProperty protected function Install modules defined by `static::$modules`. Overrides FunctionalTestSetupTrait::installModulesFromClassProperty
WebDriverTestBase::tearDown protected function Overrides BrowserTestBase::tearDown 1
XdebugRequestTrait::extractCookiesFromRequest protected function Adds xdebug cookies, from request setup.

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.