-1

I am following Page Object Model to automate a flow in one application. In one of the module I have to add a new post which have a "Title" and a "Body" field. As of now, I am able to send the text in the "Title" field as it is in the Top Window. But the "Body" is within an iframe. After passing the text in "Title" I tried to switch to the iframe before writing in the "Body". This piece of code I have written in the main file. But Selenium shows an error as org.openqa.selenium.ElementNotVisibleException: element not visible

My PageFactory code is as follows:

package com.wordpress.pom.Pages;

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.How;

public class AddNewPost {

    WebDriver driver;

    public AddNewPost(WebDriver addNewPostDriver)
    {
        this.driver=addNewPostDriver;
    }

    @FindBy(how=How.ID,using="title")
    WebElement post_title;

    @FindBy(how=How.XPATH,using=".//*[@id='tinymce']/p/br")
    WebElement post_body;

    public void construct_title()
    {
        post_title.sendKeys("This is the Title");
        System.out.println("Title written");

    }

    public void construct_body()
    {

        post_body.sendKeys("This is the body");
        System.out.println("Body written");
    }

}

I am using testNG to schedule the testcases. Here is my main file code:

package com.wordpress.pom.Testcase;

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.support.PageFactory;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;

import com.wordpress.pom.Helper.BrowserFactory;
import com.wordpress.pom.Pages.AddNewPost;
import com.wordpress.pom.Pages.Dashboard;
import com.wordpress.pom.Pages.LoginPageNew;
import com.wordpress.pom.Pages.Posts;

public class VerifyValidLogin 
{

    WebDriver driver;

    //code ommitted

    @Test (priority=3)
    public void construct_title()
    {
        //Created Page Object using Page Factory
        AddNewPost add_new_post = PageFactory.initElements(driver, AddNewPost.class);

        //Call the method
        add_new_post.construct_title();
    }

    @Test (priority=4)
    public void construct_body()
    {
        //Created Page Object using Page Factory
        AddNewPost add_new_post = PageFactory.initElements(driver, AddNewPost.class);

        driver.switchTo().frame("content_ifr");

        //Call the method
        add_new_post.construct_body();
    }

}

The HTML DOM is as:

<iframe id="content_ifr" src="javascript:""" allowtransparency="true" title="Rich Text Area Press ALT F10 for toolbar. Press ALT 0 for help." style="width: 100%; height: 330px; display: block;" frameborder="0">
<!DOCTYPE >
<html>
<head xmlns="http://www.w3.org/1999/xhtml">
<body id="tinymce" class="mceContentBody content post-type-post wp-editor" onload="window.parent.tinyMCE.get('content').onLoad.dispatch();" dir="ltr" contenteditable="true">
<p>
<br data-mce-bogus="1">
</p>
</body>
</html>
</iframe>

I feel the elements identified in the PageFactory for the body is not incorrect. Can someone help me out please?

Update:

  1. Add a Thread.sleep(3000) in the main class before switching to the iframe.

    Thread.sleep(3000);
    driver.switchTo().frame("content_ifr");
    
  2. Changed the XPATH of the "Body" field in Page Factory.

    @FindBy(how=How.XPATH,using="//html/body/p")
    WebElement post_body;
    
  3. On debugging found that WebDriver does clicks on the Body field and I can sysout but won't send the keys in the Body field. So I was forced to use Action class.

    public void construct_body()
    {
     Actions actions = new Actions(driver);
     actions.moveToElement(post_body);
     actions.click();
     actions.sendKeys("Some Name");
     actions.build().perform();
     System.out.println("Body written");
    }
    

I am able to pass the text now. But I am still not sure why would I need the Action class even after clicking on the Body element. Finally would like your opinion if my approach is correct here following the Page Object Model.

undetected Selenium
  • 183,867
  • 41
  • 278
  • 352
  • I suppose as your element is just invisible at the time then that should mean that there's nothing wrong with your pageFactory implementation, you just have to work on waiting for the element to be visible. – Kushal Bhalaik Mar 25 '17 at 05:42
  • Does that essentially means that the only solution is to introduce Explicit Wait with until condition? Can you please guide me further? – undetected Selenium Mar 25 '17 at 05:47
  • Yeah Probably. I tried to imitate your scenario for some other URL; which works fine for me, plus your exception is "element not visible" so that means it is present but not visible just yet. – Kushal Bhalaik Mar 25 '17 at 06:21
  • @Kushal Thanks, I tried out your suggestion adding Thread.sleep(3000) & fine tuning the xpath in PageFactory from `.//*[@id='tinymce']/p/br` to `.//*[@id='tinymce']/p` Now the error is `cannot focus element` Any other suggestions? – undetected Selenium Mar 25 '17 at 07:28
  • you can use Actions actions = new Actions(driver); actions.moveToElement(yourBodyElement); just before using sendKeys or any other actions. – Kushal Bhalaik Mar 25 '17 at 09:29
  • @Kushal just updated my query with the recent changes. I am able to send the keys to the Body. Can you please provide all your comments/suggestion in an answer for me to accept & at the same time help me understand why using Actions class at all when the click() was detected? – undetected Selenium Mar 25 '17 at 09:55
  • Glad it's working!! – Kushal Bhalaik Mar 25 '17 at 10:03

3 Answers3

1

All the Things going on here:

1) You were receiving Element Not Visible exception that means element is present in the DOM, but by the time you want to interact, it is just not rendered yet hence you need to use the wait.

2) You were not able to FOCUS on the element on the rendered page, which can happen due to various reasons; that is why we use Action.moveToElement(elementToBeInFocus) to explicitly deliver focus to that particular element

Kushal Bhalaik
  • 3,349
  • 5
  • 23
  • 46
0

IF you are asking about strategy there are different ways to achieve this, but what I do is I simple ask this question to my self. What is this component?

i.e. What is body?

body is part of New Post (right?). And so simply you can create a new Page Object class just for body and create a object inside your NewPost Class. This is Has-A Relationship in my opinion.

You can read more about this in below post. http://www.w3resource.com/java-tutorial/inheritance-composition-relationship.php

About your particular problem, few things.

  • Do not directly intereact with the element in the test, like switching to frame. Handle that inside a class or a function. in you case may be in a construct_body function.
  • You are not getting an error that Element is Not visible, means Element is present and it's a good thing. You can use Explicit Wait to wait until element is visible. Read following article for more details. http://www.seleniumhq.org/docs/04_webdriver_advanced.jsp

Following is just an example how you can handle this scenario.

Base Page: Initialize the driver and some global function

abstract public class BasePage {
}

Body Class: Responsible for handling all the function related to body part of post. It's not public so if tests are in different package they won't have direct access to this class. They will have to get access through Post Class only.

class BodyPage extends BasePage {

    public BodyPage(WebDriver driver){
        PageFactory.initElements(driver, this);
        //Switch to frame
    }

    public void save(){
        //Do some action here
        // Switch Back to main page
    }
}

Post Class: Responsible for iterating with whole page. BodyPage object is private, so you can't directly interact it in test. Now those who are writing test cases, they only know about Post Class.

public class PostPage extends BasePage {

    private BodyPage bodyPage;
    public PostPage(WebDriver driver){
        PageFactory.initElements(driver, this);
        //Or Switch to frame here
        bodyPage = PageFactory.initElements(driver, BodyPage.class);
    }

    public void enterBody(){
        //Call other functions
        bodyPage.save();

        //you can switch back to main window here as well.

    }
}
Gaurang Shah
  • 11,764
  • 9
  • 74
  • 137
  • Tomorrow I will work with the options you provided. Before that I just wanted to confirm in the first part of your answer are you suggesting me to create two new methods switchToFrame() & backToPage() for switching to and fro between page & frame ? Or you are suggesting to create a new PageFactory just for the body? Thanks – undetected Selenium Mar 24 '17 at 20:01
  • 1
    @Dev I could only suggest, you gotta figure out how to implement it. Complexity should be handled in separate function rather than in Test Case. In your case, switch_to_frame should be in some other function, tester even doesn't even need to know there is any frame. – Gaurang Shah Mar 27 '17 at 07:39
  • Thanks, `Complexity should be handled in separate function rather than in Test Case` completely agree with it. But I am still not sure where to handle switch_to_frame(). It can't be in PageFactory, can't be in Testcase, can't be in Browser Factory, is there any other option left? – undetected Selenium Mar 27 '17 at 07:47
  • what do you mean by page factory? your mean page class. It should be there only, rather than in test case. – Gaurang Shah Mar 27 '17 at 07:49
0

I'll give you several advice of how to try to deal with it. Hope it will help you.

  1. Add driver.switchTo().frame("content_ifr"); code to the construct_body method to avoid mistakes in future when you forget to switch to the frame before using this method.
  2. Probably you haven't switched to the correct frame. To check this get outerHTML of the frame or get any other element that is easy to locate. If you can't locate any elements then the frame is wrong. But it's better to look at its outerHTML go make sure it's a correct frame. There are several methods to get outerHTML Get HTML Source of WebElement in Selenium WebDriver using Python
  3. Make sure your xPath is relative to your iframe, not to your main HTML document. The start of the frame should be its beginning point.

And when you manage to switch to your frame and to get element, don't forget to switch back!)

Community
  • 1
  • 1
Denis Koreyba
  • 3,144
  • 1
  • 31
  • 49
  • Tomorrow I would surely give your suggestion a try but I am curious to know how can detecting an element from the outerHTML help me to detect the frame? As far as xpath is concerned I have used firebug and firepath & the iFrame is petty clear on the firebug console. What I am not sure is where to put the switch code & where/how/when do I define the elements of the iFrame in PageFactory. Thanks. – undetected Selenium Mar 24 '17 at 19:40