Nick DeNardis

User experience, code, higher education, analytics and accessibility.

Getting Cypress JS to interact with CKEditor

Because .type() doesn’t submit when selecting the textarea.

[caption id="attachment_1584" align="alignnone" width="797"]CKEditor CkEditor iframe with custom toolbar[/caption]

While writing a few Cypress.io (opens new window) tests I ran into a problem of manipulating text in a CKEditor (opens new window) window.

Without diving completely into how CKEditor implements the editable iframe, my first thought was to use the native textarea field and skip adding content to the WYSIWYG (opens new window) all together.

# Using the native type() function

// The underlying `textarea`

cy.get("#html_body") .type("

HTML body

");

[caption id="attachment_1585" align="alignleft" width="348"]Cypress native type command Cypress error, can’t type in hidden inputs[/caption]

Cypress doesn’t like typing into hidden input element elements.

Adding the force:true config doesn’t fare much better. With Javascript turned on, CKEditor overwrites the textarea on submit and that value is lost. 😒

# Cypress interacting with an iFrame

Next stop is to search “cypress type in ckeditor (opens new window)” which leads to the issue Cannot target elements inside of an iframe #136 (opens new window).

Reading through the working examples lead to something like this:

// Find the `contenteditable` element within the iframe and type

cy.get("#html_body") .then(function ($iframe) { const $body = $iframe.contents().find("body");

cy.wrap($body\[0\])
  .type("<p>HTML body</p>");

});

[caption id="attachment_1586" align="alignnone" width="644"]Cypress doesn't error Doesn’t error, but also doesn’t submit[/caption]

This does find the DOM element and says that it was typed, but it doesn’t actually result in the words being visible in the CKEditor or submitted with the form. 😢

# Interacting with JS page objects

Last ditch effort is to break outside of Cypress and set the data in CKEditor natively then try to make the same call from within the Cypress test.

CKEditor has [setData()](https://docs.ckeditor.com/ckeditor4/latest/api/CKEDITOR_editor.html#method-setData) and [getData()](https://docs.ckeditor.com/ckeditor4/latest/api/CKEDITOR_editor.html#method-getData) functions for getting contents in and out of the editor window.

// Set the contents of the CKEditor natively on the page

CKEDITOR_.instances["html_body"]._setData("

HTML body

");

Now to call that from within Cypress, led to a search for “cypress get page JS objects (opens new window)” and then to this issue: How can I execute a JavaScript command #897 (opens new window) and the [.window()](https://docs.cypress.io/api/commands/window.html#Syntax) function (opens new window).

# Success!

// Call out to the page window and use the CKEDITOR object

cy_._window() _.then(win => { win.CKEDITOR.instances["html_body"]._setData("

HTML body

"); });

[caption id="attachment_1587" align="alignnone" width="668"]Setting the content of CKEditor with Javascript Setting the content of CKEditor with Javascript[/caption]

# Turning the solution into a reusable command

// Define the command

Cypress.Commands.add("type_ckeditor", (element, content) => { cy.window() .then(win => { win.CKEDITOR.instances[element].setData(content); }); });

// Use the command

cy_._type_ckeditor("html_body", "

HTML body

");

With this approach it is possible to use any of the [CKEDITOR.editor](https://docs.ckeditor.com/ckeditor4/latest/api/CKEDITOR_editor.html) object functions. (opens new window)