1

I've been looking a bit at Selenium, and I'm beginning to like it, since I know some Java programming and find both Java and C# pretty straight-forward for simple things like this.

However, I'm struggling with a test that creates a new post in Wordpress, from the Dashboard page:

This is the Selenium code (in C#): (The Driver instance is, obviously, a driver class I've created - for starting the browser and connecting to the wordpress site.)

1: Driver.Instance.FindElement(By.Id("title)).SendKeys(title);
2: Thread.Sleep(1000);
3: 
4: Instance.SwitchTo().Frame("content_ifr");
5: Thread.Sleep(1000);
6: 
7: Driver.Instance.SwitchTo().ActiveElement().SendKeys("something");

Now, what happens is that the title is easily found (by ID, so I wouldn't expect problems there), and I can easily insert the title text (line 1).

But the inline frame for the post body is causing problems. When running the test, after the topic is filled in, the cursor changes to the body area (line 4) - as planned. However, nothing more happens. The SendKeys("string") method (ine 7) doesn't seem to work there.

Any ideas?

EDIT: Of course - an important piece of information is that the iframe in Wordpress simply loads a TinyMCE editor. So, in the page source, there's only a body tag with the javascript loading of the editor.

EDIT2: Of course, something suddenly changed. Without ANY change to the wordpress page, the "content_ifr" is now suddenly missing (?!!!!!?) The Selenium test fails with "unable to locate frame...", and it's also suddenly missing from the page source.

enter image description here

EDIT3: I also noticed something:

Driver.Instance.SwitchTo().Frame(iframe);
Driver.Instance.FindElement(By.Id("tinymce")).SendKeys("message body");

It's the SECOND line that makes the cursor switch to the mce field, not the line with the .SwitchTo(). However, I need the first line - the second line does nothing on its own. This is approaching something really stupid. I've been looking for a solution to this for a week - this doesn't exactly bode well for Selenium. The Selenium user group doesn't even want to answer when I ask them.

Also - if I skip the SendKeys() method in the second line, nothing happens. So, it seems that the two lines does ALLMOST what it should, right up to and including placing the cursor in the correct spot. But it never sends any text.

EDIT4 (last): After actually figuring out how to use IJavaScriptExecutor, it works using the solution(s) below.

  • The frame id is: `driver.switchTo().frame("ipteditor_ifr")`. AFAIK it has always been that. – SiKing Jun 05 '14 at 14:51
  • Now that is definetely wrong. When using "content_ifr", the cursor actually switches to the editor. If I change to "ipteditor_ifr", the cursor stays at the title and the test fails. –  Jun 05 '14 at 21:25
  • I suspect Wordpress has more than one entrypoint for a new post, and hence the editor. The official site for TinyMCE http://www.tinymce.com/tryit/basic.php uses `content_ifr`, but that means nothing. – SiKing Jun 05 '14 at 21:44
  • Could you perhaps provide some source/screenshots/plugins/Wordpress version that is used? In my WordPress 3.9.1 only has a 'Quick Draft' section on the dashboard page that doesn't have a TinyMCE editor, just a textarea – thewheat Jun 05 '14 at 21:48
  • I've added a screenshot :-9 –  Jun 06 '14 at 08:34

4 Answers4

3

Java method to handle TinyMCE editor would look like:

public void entersTopicOfBody(String textToBeTyped, WebDriver driver) {
     driver.switchTo().frame("content_ifr");
     WebElement body = driver.findElement(By.xpath("//body"));
     body.click();
     JavascriptExecutor executor = (JavascriptExecutor)driver;
     executor.executeScript("arguments[0].innerHTML = '"+ textToBeTyped+"'", body);
     driver.switchTo().defaultContent();
}
Skatox
  • 4,237
  • 12
  • 42
  • 47
olyv
  • 3,699
  • 5
  • 37
  • 67
  • What is `JavascriptExecutor executor = (JavascriptExecutor)getDriver();` suppose to be? It does not even compile. – SiKing Jun 05 '14 at 14:23
  • Sorry, it should be gust an instance of driver. It was a snippet of code from my last project :) – olyv Jun 05 '14 at 14:39
  • How do I used JavaScriptExecutor (or IJavaScriptExecutor) in .Net? As in, what "using ..." statement? –  Jun 05 '14 at 21:21
  • In fact, I use WebDriver with Java. But I hope this answer http://stackoverflow.com/a/23244164/2504101 will give you a hint how to execute JavaScript via C#. – olyv Jun 06 '14 at 07:16
  • Thanks. However, the problem is that NOWHERE seems to bother mentioning which library I need to "import". Like import statements in Java, C# needs "using" statements. And I cannot get Visual Studio to help me suggest libraries, it seems. –  Jun 06 '14 at 08:38
  • Hm, I think you should know C# better then I do http://selenium.googlecode.com/git/docs/api/dotnet/index.html – olyv Jun 06 '14 at 08:48
  • I should, since I'm using it here. But I actually know Java better :-) –  Jun 06 '14 at 13:43
2

Below is some C# code that publishes a post. I think the main issues you have are due to timing issues.

I've done a bit of Selenium recently and I favour implicit waits: it waits for a maximum time period for the item to be available, but returns as soon as possible. So you can specify a max wait of 100 seconds, but if it finds it in 1 second, it will only wait 1 second. Much more efficient vs sleeping for an arbitrary length of time. See this post about Implicit and Explicit waits

But even with implicit waits, it may not solve all issues. When coding the sample below, I ran into the issue where the "Publish" button was disabled and re-enabled after some time. And that's when you have to look at the code to see what it is doing as well. It's times such as these where sleeps can help you fix the problem for a quick fix if you do not wish to debug too much: just be sure to set a large enough sleep time and be wary that it could be inconsistent in the future.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using OpenQA.Selenium;
using OpenQA.Selenium.Remote;
using OpenQA.Selenium.Support.UI;
using OpenQA.Selenium.Support.Events;

namespace SeleniumTest
{
    class Program
    {
        static void Main(string[] args)
        {
            IWebDriver driver = new OpenQA.Selenium.Firefox.FirefoxDriver();
            driver.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromSeconds(30));

            // enter your configurations here 
            driver.Navigate().GoToUrl("http://localhost/wordpress/wp-admin/post-new.php");
            driver.FindElement(By.Id("user_login")).SendKeys("admin");
            driver.FindElement(By.Id("user_pass")).SendKeys("yourpassword");

            driver.FindElement(By.Id("wp-submit")).Click();

            driver.FindElement(By.Id("title")).SendKeys("the title");
            var iframe = driver.FindElement(By.Id("content_ifr"));
            driver.SwitchTo().Frame(iframe);


            // your solution which works in my instance
            //driver.SwitchTo().ActiveElement().SendKeys("hello tiny mce from selenium");

            // send keys with exact element
            //driver.FindElement(By.Id("tinymce")).SendKeys("hello tiny mce from selenium");

            // javascript - 1
            IJavaScriptExecutor js = driver as IJavaScriptExecutor;
            var tinymce = driver.FindElement(By.Id("tinymce"));
            IJavaScriptExecutor executor = (IJavaScriptExecutor)driver;
            executor.ExecuteScript("arguments[0].innerHTML = 'hello tiny mce via javascript'", tinymce);
            // javascript - 0



            driver.SwitchTo().DefaultContent();

            var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(11));
            wait.Until((d) => { return !d.FindElement(By.Id("publish")).GetAttribute("class").Contains("disabled"); }); // wait for publish button to be enabled
            driver.FindElement(By.Id("publish")).Click();
            driver.FindElement(By.Id("message")); // wait for message on next page to verify it is posted
            driver.Close();
        }
    }
}
thewheat
  • 988
  • 7
  • 13
  • I do not see the `By.Id("tinymce")`. All there is simply `


    `.
    – SiKing Jun 05 '14 at 14:28
  • Thanks, but this does nothing more than the code I posted (which isn't very different, really). I do use implicit waits, implemented in the Driver class. This does the same as my code: It enters the title, then the cursor switches to the mce editor (as in, blinking in the editor, ready for typing). Then nothing more. The SendKeys() method apparently does nothing. –  Jun 05 '14 at 21:00
  • Oh, I thought due to your sleeps that you weren't using implicit waits and that it could be an inconsistency issue as you mentioned that "content_ifr" was missing after supposedly previously working. I also thought that your .SwitchTo().ActiveElement() may not be exact and finding the element by Id would be more succinct, will try update using Javascript suggested above. Also you mention dashboard but reference 'content_ifr' which I do not see in my dashboard, but on the add new post page, so I assume its the new post page? – thewheat Jun 05 '14 at 21:43
  • Yah, it's the New Post page, but accessed from the Dashboard, logged in as admin. –  Jun 06 '14 at 06:38
  • When you get the "unable to locate frame..." error, do you see the TinyMCE editor initialized? Also is it always consistently failing at this step? Cause looking at the source of the "New Post" page shows no reference to an iframe, which means it is probably generated via Javascript. And if you do see it on the page but it fails, it leads me to think that it is a timing issue. Did my code above give the same error as yours? – thewheat Jun 07 '14 at 11:01
  • Hi. Sorry - I forgot to update that exact part. Actually, the "unable to locate frame" error disappeared as quickly and myseriously as it came. Now I'm back to the heart of the matter: The cursor switches to the body part of the post, i.e. the TinyMCE frame. It just stands there, blinking, and I can type text there manually if I want to. But SendKeys() does absolutely nothing. –  Jun 08 '14 at 16:14
  • An apology is in order here. After actually figuring out how to use the IJavaScriptExecutor, I found your solution to be working perfectly. Thsnks, man :-) –  Jun 10 '14 at 17:43
0

A PHP version of olyv solution:

$content = 'my text';
$this->frame( 'content_ifr' );
$body = $this->byXPath( '//body' );
$body->click();
$script = 'arguments[0].innerHTML = "" + arguments[1] + ""; ';
$this->execute( [
            'script' => $script,
            'args'   => [ $body->toWebDriverObject(), $content ],
        ]
    );
$this->frame( null );
WPSOLR
  • 50
  • 1
  • 7
0

I know that I am a bit late to the party, but I just found a solution that is (I believe) much simpler than the answers given so far. So I decided to post it here in case it could help someone else.

There is no need to switch frames here. What you wanna do is 'click' on the button in the top right corner of the text editor that says "Text", which has id = "content-html". Now, you can 'send keys" to the textarea, which has id = "content".

Here is some Python code that does just this:

driver.find_element_by_id("content-html").click()
driver.find_element_by_id("content").send_keys("Some text...")

Hope it helps

Marnix.hoh
  • 1,556
  • 1
  • 15
  • 26