How to fill a rich text editor field for a Codeception Acceptance test
Asked Answered
V

6

8

I'm trying to fill a rich text editor field (TinyMCE) within my acceptance test in Codeception.

Using the fillField() function doesn't work as this 'field' isn't really an input field. It's an iframe, styled to look like a fancy textarea.

How can I set some text into the body of the TinyMCE box? I think I'm looking for the addition of a $I->setContent(xpathOrCSS) function. Or does something else already exist to do this?

Victor answered 20/3, 2015 at 13:50 Comment(1)
What are you using for carrying your acceptance tests? Since TinyMCE is mostly JS-driven, using PHPBrowser is not an option, you would need to use WebDriver (recommended) or PhantomJS (if you need a headless JS-enabled browser)Defrayal
M
7

It is best to do this by adding re-usable actions to your Actor class (AcceptanceTester, by default). You can then use the actions in your tests to set the content of rich text editor fields without reducing the readability of your tests. More details on this are available in the Codeception documentation.

I have included solutions for TinyMCE and CKEditor below. The solution uses the executeInSelenium() call to give us access to Facebook's underlying WebDriver bindings. From there, we simply use the frame switching/Javascript injection technique described here to set the content of our target editor.

Note that the final call to $webDriver->switchTo()->defaultContent() is very important - this switches WebDriver's focus back from the RTE iframe to the page that contains it.

Actor functions:

<?php

class AcceptanceTester extends \Codeception\Actor {
    use _generated\AcceptanceTesterActions;

    public function fillCkEditorById($element_id, $content) {
        $this->fillRteEditor(
            \Facebook\WebDriver\WebDriverBy::cssSelector(
                '#cke_' . $element_id . ' .cke_wysiwyg_frame'
            ),
            $content
        );
    }

    public function fillCkEditorByName($element_name, $content) {
        $this->fillRteEditor(
            \Facebook\WebDriver\WebDriverBy::cssSelector(
                'textarea[name="' . $element_name . '"] + .cke .cke_wysiwyg_frame'
            ),
            $content
        );
    }

    public function fillTinyMceEditorById($id, $content) {
        $this->fillTinyMceEditor('id', $id, $content);
    }

    public function fillTinyMceEditorByName($name, $content) {
        $this->fillTinyMceEditor('name', $name, $content);
    }

    private function fillTinyMceEditor($attribute, $value, $content) {
        $this->fillRteEditor(
            \Facebook\WebDriver\WebDriverBy::xpath(
                '//textarea[@' . $attribute . '=\'' . $value . '\']/../div[contains(@class, \'mce-tinymce\')]//iframe'
            ),
            $content
        );
    }

    private function fillRteEditor($selector, $content) {
        $this->executeInSelenium(
            function (\Facebook\WebDriver\Remote\RemoteWebDriver $webDriver)
            use ($selector, $content) {
                $webDriver->switchTo()->frame(
                    $webDriver->findElement($selector)
                );

                $webDriver->executeScript(
                    'arguments[0].innerHTML = "' . addslashes($content) . '"',
                    [$webDriver->findElement(\Facebook\WebDriver\WebDriverBy::tagName('body'))]
                );

                $webDriver->switchTo()->defaultContent();
            });
    }
}

Example Usage:

$content = '<h1>Hello, world!</h1>';

// CKEditor
$I->fillCkEditorByName('rich_content', $content);
$I->fillCkEditorById('my_ckeditor_textarea', $content);

// TinyMCE
$I->fillTinyMceEditorByName('rich_content', $content);
$I->fillTinyMceEditorById('my_tinymce_textarea', $content);

In all cases, the first parameter refers to the name/id attribute of the original textarea element, and the second parameter is the HTML content to fill it with.

Moneyer answered 2/11, 2015 at 15:14 Comment(2)
Great answer, it saved my day and its very readable for all ckeditor tests in the future. Thanks Alex !Bergren
Thank you very much, this has helped me after many hours looking for a solution!Corpuz
E
1

Best way:

$I->executeJS('$("#selector").val("Value")');
Expletive answered 17/4, 2015 at 23:53 Comment(2)
you're definitive wrong, it's all you need to work on this problem. Think by yourself. any more point is unnecessary overhead information. its not about teaching all about the questions, it's about answer just that the problem says.Plating
Works perfectly for: Yii2+RedactorDyanna
D
1

If you have a simple setup and only need to test one instance in tinyMCE 4, this worked for me.

$I->executeJS('tinyMCE.activeEditor.setContent(" your content goes here ");');
Die answered 28/9, 2016 at 18:37 Comment(0)
L
1

IF you have and iframe like this:

<iframe id="myFrameID" allowtransparency="true"></iframe>

notice, that Codeception switches to the iframe using

$I->switchToIFrame("myFrameID");

Keep in mind, to omit the # in front of "myFrameID", as switch to iframe does not use a css selector but rather just the name of the iframe.

Then do

$I->executeJS('document.getElementById("tinymce").innerHTML = "<p>Some Text Here!</p>";');

and don't forget to switch back to previous window:

$I->switchToIFrame();

as stated in https://codeception.com/docs/modules/WebDriver#switchToIFrame

Laresa answered 7/3, 2019 at 13:1 Comment(0)
K
0

Tried and following solution works:

$I->switchToIFrame('#frameName');
$I->executeJS('document.getElementById("tinymce").innerHTML = "<p>Test abc def</p>";');
Kemp answered 18/4, 2015 at 2:10 Comment(0)
D
0

try this

$x = $I->grabAttributeFrom('//iframe', 'id');
$I->switchToIframe($x);
$I->fillField('//*[@id="tinymce"]', '<p>Test abc</p>');
Diglot answered 6/4, 2016 at 18:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.