Getting Cypress JS to interact with CKEditor
Because .type()
doesn’t submit when selecting the textarea
While writing a few Cypress.io tests I ran into a problem of manipulating text in a CKEditor 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 all together.
Using the native type()
function
// The underlying `textarea`cy.get("#html_body")
.type("<p>HTML body</p>");
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” which leads to the issue Cannot target elements inside of an iframe #136.
Reading through the working examples lead to something like this:
// Find the `contenteditable` element within the iframe and typecy.get("#html_body")
.then(function ($iframe) {
const $body = $iframe.contents().find("body");
cy.wrap($body[0])
.type("<p>HTML body</p>");
});
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()
and getData()
functions for getting contents in and out of the editor window.
// Set the contents of the CKEditor natively on the pageCKEDITOR.instances["html_body"].setData("<p>HTML body</p>");
Now to call that from within Cypress, led to a search for “cypress get page JS objects” and then to this issue: How can I execute a JavaScript command #897 and the .window()
function.
Success! 🎉
// Call out to the page window and use the CKEDITOR objectcy.window()
.then(win => {
win.CKEDITOR.instances["html_body"].setData("<p>HTML body</p>");
});
Turning the solution into a reusable command
// Define the commandCypress.Commands.add("type_ckeditor", (element, content) => {
cy.window()
.then(win => {
win.CKEDITOR.instances[element].setData(content);
});
});// Use the commandcy.type_ckeditor("html_body", "<p>HTML body</p>");
With this approach it is possible to use any of the CKEDITOR.editor
object functions.