Transcripted Summary

In this chapter we're going to look at frames.

A frame is an HTML document, which is embedded inside of another HTML document.

We're going to scroll down to this WYSIWYG Editor link and let's click there.



If we inspect this editor, we see that this is inside of an HTML tag, which is embedded inside of this page.

This HTML document is a child of this iframe and the iframe element represents frames.


When using Selenium, we always start off in the uppermost DOM or document — in this case, this is the page.



If we want to go inside of an embedded document, or an iframe in this case, then we'll need to switch context to that frame, similar to how we did in the last chapter with alerts.


Let's go ahead and create a scenario so that we'll understand how this works.

  1. From the home page we're going to click the “WYSIWYG Editor” link

  2. Then inside of this editor we're going to type "Hello world"

  3. We see here that the existing sentence does not go away, so we'll make a note to clear this field before we try to type in it

  4. We'll type "Hello" and then we'll switch back to the editor's header, which is in a different frame

  5. We'll click one of the buttons

  6. Then we'll come back to the text frame and type in the word "World"


Let's begin.

In our HomePage class I've already created the clickWysiwygEditor() method, which will click on the "WYSIWYG Editor" link and return the WysiwygEditorPage.


public WysiwygEditorPage clickWysiwygEditor(){
    clickLink("WYSIWYG Editor");
    return new WysiwygEditorPage(driver);
}

Inside of this WysiwygEditorPage class, I've already created the driver and the constructor.


package pages;

import org.openqa.selenium.WebDriver;

public class WysiwygEditorPage {

    private WebDriver driver;

    public WysiwygEditorPage(WebDriver driver){
        this.driver = driver;
    } 
}

Once we've come to this page, the first thing we want to do is to clear this editor.

Let's go ahead and find the locator for this editor.



And again, this is inside an <html> and it's a little bit further down but more importantly it's inside of an iframe.

So, the first thing we're going to want to do is to switch to this iframe.


Let's go ahead and create the method.

We'll make a method here to switch the iframe.

I'm going to make this private because this isn't something that my test should have to worry about. As the user, the user has no idea that that’s inside of an iframe. So, our test shouldn't have to worry about those types of implementation details.

So, we'll just make this private and we'll say, switchToEditArea.


private void switchToEditArea(){

} 

And we'll say, driver.switchTo and we see a frame.



Actually, we see three overloaded methods that allow us to switch to a frame.

We can switch to a frame by finding the web element of that frame.

  • If we needed to use a CSS Selector or an Xpath or class name or anything else, we could do that by first finding the element and then passing it to this frame method

  • Another option is to find the frame by its index. So, we would pass in an integer so if a document contained frames, we could give it the position in the document that we wanted to switch to. In our example, I believe that that was the first frame there, so we could just give it a 0 (this is zero based)

  • Or we could provide an id for that frame


Let's see what we have in the DOM.

This frame does have an id— “mce_0_ifr” — so let's copy this.



We'll make sure that this is the only id. And yes, “1 of 1” so we'll go ahead and use that.


I'm going to go ahead and create a locator.

Instead of using a By, I'm not going to do a WebElement. Since it takes the String of an id, I can just do the String.

So, we'll say:


private String editorIframeId = "mce_0_ifr";

And so back in our method we'll need say: switchTo, we call frame and pass in the “editorIframeId”.


private void switchToEditArea(){
    driver.switchTo().frame(editorIframeId);
}

This will switch the context from the page's DOM to this iframe DOM.

Once we're inside of this iframe, the only things that we can access, are things inside of this HTML tag. This is just the editor's iframe.


So, we're in that iframe now — we want to get a handle to this text area.

Let's see what we have here.

We have this body which has an id — “tinymce” — so let's go ahead and use that.

We're going to add this as a By in our framework.


private By textArea = By.id("tinymce");

Remember we needed to clear that area before we can do anything to it, so let's create a method to do that.

When or test calls this at first, we're in the main area of the page, so first we have to switch to the edit area.

So, we make a call to that method and then we can do whatever we want to do within that text box.

We can then say driver.findElement give it the textArea and then say go ahead and clear that please.


public void clearTextArea(){
    switchToEditArea();
    driver.findElement(textArea).clear();
} 

Good Practice

If you enter into an iframe within your method call, once you're done whatever it is that you need to do, you should exit out of that iframe. You never know what your test's next action is going to be. And if it's something that's at the parent level then you have them stuck in that iframe. So, it's a good habit for your method to enter into the iframe if it needs to, do whatever it needs to do and then exit out of the iframe.


To exit out of the iframe, we're going to make another method here.


private void switchToMainArea(){
    driver.switchTo().parentFrame();
}

What this will do is, switch us to the parent frame so that's the page's frame.


Then we make a call to this method from the clear method.


public void clearTextArea(){
    switchToEditArea();
    driver.findElement(textArea).clear();
    switchToMainArea();
} 

So, we've entered into the iframe, we've done our task, and now we're exiting out.


Let's go ahead and make a method to send text to this area as well.

It's going to be similar.

We'll allow our test to pass in some text.

So, we're going to switch to the edit area, so that's switching the iframe

We'll find the text area and this time we're going to say .sendKeys and then we'll pass in the text

Then we switch to the main area.


public void setTextArea(String text){
    switchToEditArea();
    driver.findElement(textArea).sendKeys(text);
    switchToMainArea();
}

Okay, let's go back to our application.

Our test is going to clear this area. It's going to type in a word “hello” and then we want to go to this Indent button.

Now first let's look at where we are now.



This edit area is inside of this frame, and we see that here by this trail of breadcrumbs.

We can see this HTML tag which is letting us know that this is inside of an iframe and we see the very first HTML tag which is the parent. So, we see that this is inside of the very first iframe.

Now let's look at this button.



Here's this button.

We don't see an HTML here. In fact, the only HTML is the parent one. We know that this is not inside of an iframe, but it's in the parent HTML.

So, we don't have to worry about switching context in order to get to this.

This button doesn't have a locator, but it is inside of a <div> which has an id. If we do that id and button — #mceu_12 button — we see here that it found “1 of 1”.


Let's go ahead and use that as a CSS Selector and add this button to our framework.


/**
* Note from Angie - I should have actually called this the increase indent button! 🤦🏾‍♀️ Everything still works though lol
* */
private By decreaseIndentButton = By.cssSelector("#mceu_12 button");

Let's create a method to click that button.

And again, that is not inside of an iframe.

So, we don't have to worry about switching context, we can just to a driver.findElement.


public void decreaseIndention(){
    driver.findElement(decreaseIndentButton).click();
}

The next part of our test is to then continue typing.

So, we already have the method to do that: setTextArea

  • It'll switch to the frame
  • It'll enter the keys
  • Then it will exit back to the main area

Then we'll type in a new word here “world”.


The final step is to verify this, so we'll need one more method to read the text from this field.

In order to get the text from the editor, we are going to want to switch to that iframe and then find the element, and we'll get the text from there.


public String getTextFromEditor(){
    switchToEditArea();
    String text = driver.findElement(textArea).getText();
    switchToMainArea();
    return text;
}

We can't just return this because we need to switch back to the main area. So, let's just store this inside of a variable, then we will switch back to the main area and then return that text.

Okay, we have everything we need.


# WysiwygEditorPage.java

package pages;

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;

public class WysiwygEditorPage {

    private WebDriver driver;
    private String editorIframeId = "mce_0_ifr";
    private By textArea = By.id("tinymce");

    private By decreaseIndentButton = By.cssSelector("#mceu_12 button");

    public WysiwygEditorPage(WebDriver driver){
        this.driver = driver;
    }

    public void clearTextArea(){
        switchToEditArea();
        driver.findElement(textArea).clear();
        switchToMainArea();
    }

    public void setTextArea(String text){

        switchToEditArea();
        driver.findElement(textArea).sendKeys(text);
        switchToMainArea();
    }

    public String getTextFromEditor(){
        switchToEditArea();
        String text = driver.findElement(textArea).getText();
        switchToMainArea();
        return text;
    }

    public void decreaseIndention(){
        driver.findElement(decreaseIndentButton).click();
    }

    private void switchToEditArea(){
        driver.switchTo().frame(editorIframeId);
    }

    private void switchToMainArea(){
        driver.switchTo().parentFrame();
    }
}

Let's go ahead and write our test.

We go inside of the test area and let's create a new package.

This one we will call, "frames" and we'll create a new class called FrameTest.

From the home page we're going to say, “click the WYSIWYG Editor” and we'll store this in the page.


@Test
public void testWysiwyg(){
    var editorPage = homePage.clickWysiwygEditor();
}

Then we can say, go ahead and clear the text area.


editorPage.clearTextArea();

Next we're going to want to set the text and we're actually going to do this twice. Let's say we want to text the first time to be “hello ” (with a space) and the second time we want it to be “world”.


String text1 = "hello ";
String text2 = "world";

So now we can say enter text1 into the text area.


editorPage.setTextArea(text1);

Then we want to click on the indent button.


editorPage.decreaseIndention();

Then we want to set some more text.


editorPage.setTextArea(text2);

Finally, we want to verify our text.


assertEquals(editorPage.getTextFromEditor(), text1+text2, "Text from editor is incorrect");

# FrameTests.java

package frames;

import base.BaseTests;
import org.testng.annotations.Test;
import static org.testng.Assert.assertEquals;

public class FrameTests extends BaseTests {

    @Test

    public void testWysiwyg(){

        var editorPage = homePage.clickWysiwygEditor();
        editorPage.clearTextArea();

        String text1 = "hello ";
        String text2 = "world";

        editorPage.setTextArea(text1);
        editorPage.decreaseIndention();
        editorPage.setTextArea(text2);

        assertEquals(editorPage.getTextFromEditor(), text1+text2, "Text from editor is incorrect");
    }
}

Okay let's run this.

Great, so that worked.

Now let me show you what would happen if we didn't switch to the right frame.

Inside of this set text area, let's just comment out the part that says switch to the main area.



So, we'll clear the text. We'll come back in our test and we'll add the first word.

Then we'll try to click on that indent button, but we're inside of the frame.

Let's see what happens in that case.

Notice that the test failed this time.



We get this NoSuchElementException and it says it's unable to locate the element with the CSS Selector of this button selector.

So, you see you have to be in the right context in order to interact with the elements.



Optional Independent Exercise

For your option independent exercise, go to the-internet page and click on the Frames link.

Then click on the Nested Frames link and verify two of these frames.



The two that I would like for you to verify is the left frame and then also the bottom frame, but do these both inside of one test.

So, your test should click to this page, and then verify that the text inside of this particular frame says "Left" and that the text inside of this particular frame says "Bottom".

Good luck!


Solution

Programming can be done many different ways, but here’s my solution: FramesPage, NestedFramesPage, FramesTests.



Resources