Search code examples
c#seleniumatata

How to set content inside ckeditor using Atata framework?


I am trying to set content inside ckeditor, to be precise inside <body> of an <iframe>, but once when I switch to frame, system throws: NoSuchElementException. I have tried to find element by class or by css selector - none of these gave results.

I have found that issue is in setting content of <body> itself, and this post:

How to get and set text editor value in selenium

but I am not sure how to execute JavaScript using Atata. Below are parts of my code.

Page where <iframe> is located:

    public class DocumentEditPage : Page<_>
    {
        [FindById("cke_1_contents")]
        //[FindByClass("cke_wysiwyg_frame cke_reset")] 
        //[FindByIndex(0)]
        public Frame<DocumentFramePage, _> ContentSwedish { get; private set; 
    }

Frame page:

    public class DocumentFramePage : Page<_>
    {
        [FindByClass("cke_editable cke_editable_themed cke_contents_ltr cke_show_borders")]
        //[FindByCss("body")]
        public TextInput<_> TextBoxEditingContent { get; private set; }

    }

Test:

    [Test]
        public void SaveContentInsideFrame()
        {

            string ID = "7";
            string valueToBeSet = "TestContent";


            Go.To<DocumentsPage>().
               Documents.Rows[x => x.Dokument_ID == ID].Edit().
               // Refresh page so the content can be visible
               RefreshPage().
               ContentSwedish.SwitchTo().
               TextBoxEditingContent.Clear().
               TextBoxEditingContent.Set(valueToBeSet).
               SwitchToRoot<DocumentEditPage>().
               // Click on Save button
               Save();
        }

HTML code of page:


<div id="cke_1_contents" class="cke_contents cke_reset" role="presentation" style="height: 200px;">
<span id="cke_52" class="cke_voice_label">Press ALT 0 for help</span>
<iframe src="" style="width: 100%; height: 100%;" class="cke_wysiwyg_frame cke_reset" title="Rich Text Editor, Details_0__Content" aria-describedby="cke_52" tabindex="0" allowtransparency="true" frameborder="0"></iframe></div>

<body class="cke_editable cke_editable_themed cke_contents_ltr cke_show_borders" spellcheck="false" contenteditable="true">
<h1>Test</h1>
<p>Test<br></p>
<p>Test<br></p>
<p><br></p>
</body>


Solution

  • You can create a control class for CKEditor as follows:

    [ControlDefinition("iframe", ContainingClass = "cke_wysiwyg_frame", ComponentTypeName = "CKEditor")]
    public class CKEditor<TOwner> : EditableField<string, TOwner>
        where TOwner : PageObject<TOwner>
    {
        protected override string GetValue()
        {
            string value = null;
    
            DoWithinFrame(body =>
            {
                value = body.Text;
            });
    
            return value;
        }
    
        protected override void SetValue(string value)
        {
            DoWithinFrame(body =>
            {
                body.Clear();
                body.SendKeys(value);
            });
        }
    
        // An appoach to set a value using JavaScript.
        //protected override void SetValue(string value)
        //{
        //    Driver.ExecuteScript(
        //        "arguments[0].contentDocument.getElementsByClassName('cke_editable_themed')[0].innerHTML = arguments[1];",
        //        Scope, // Actual CKEditor <iframe> element.
        //        value);
        //}
    
        protected void DoWithinFrame(Action<IWebElement> frameBodyAction)
        {
            var frame = Driver.SwitchTo().Frame(Scope);
            var frameBody = frame.Get(By.TagName("body"));
    
            frameBodyAction?.Invoke(frameBody);
    
            Driver.SwitchTo().DefaultContent();
        }
    }
    

    The logic of iframe switches encapsulates inside it.

    Then use it in the page object:

    public class DocumentEditPage : Page<_>
    {
        public CKEditor<_> Editor { get; private set; }
    }
    

    And in the test interact with it as with a regular input field:

    Go.To<DocumentEditPage>().
        Editor.Set("sample text").
        Editor.Should.Equal("sample text");
    

    Above code works for CKEditor 4. Tested on https://ckeditor.com/ckeditor-4/demo/ page.