Browser, Navigation, Window, and Tab Commands

In the previous article, you learned that WebDriver owns the browser session and WebElement is a handle to one control on the page. You also picked up a clean lifecycle: setup, act, assert, teardown.

That test only did one thing: it opened a page and typed into a field. Most real tests do more. They move between pages, check what loaded, resize the window so the layout behaves the same on every run, and sometimes open a second tab to compare two pages side by side.

All of that lives on WebDriver, not on any single WebElement. This lesson walks through the browser-level commands you will use in nearly every project: opening pages, reading the title and URL, moving through history, controlling the window, and working with tabs and windows. By the end, you will write one TestNG test that uses all of them together.

It helps to sort Selenium commands into two groups before you learn new ones. You already met the first group in Lesson 1.

Command groupWhat it affectsExample
Browser-level (WebDriver)The whole browser window or session: which URL is loaded, window size, tabs, session lifetimedriver.get(url), driver.navigate().back(), driver.manage().window().maximize()
Page-level (WebElement)One control inside the currently loaded page: typing, clicking, reading textelement.sendKeys("codekru"), element.click()

Every command in this lesson belongs to the first group. You call them directly on driver, and none of them need a locator, because they are not asking about one specific field or button. They are asking about the browser itself: where is it, how big is it, and how many tabs does it have open.

You already used driver.get(url) in Lesson 1 to open the TestKru Text Fields page. It is the command you will reach for most often.

driver.get("https://testkru.com/Elements/TextFields");

get() loads the given URL in the current browser window and waits until the page finishes loading before your next line runs. That waiting behavior matters: Selenium’s official WebDriver documentation states that get() blocks until the load completes under the driver’s default page load strategy, so your very next command already has a loaded page to work with.

Selenium also gives you a second way to load a URL, through the navigation object:

driver.navigate().to("https://testkru.com/Elements/TextFields");

The official Selenium Javadoc calls get(String url) a synonym for navigate().to(String url). Both commands load the URL and both block until the page finishes loading. There is no hidden difference in what they load or how they wait.

Since get() and navigate().to() load a page the same way, the real reason to write navigate() is what comes bundled with it. The navigate() object also gives you back(), forward(), and refresh(), so once you plan to move through browser history in the same test, many teams simply write every URL load as navigate().to(...) for consistency. Either choice is correct Selenium 4 code. This lesson uses get() for the first page load, since that is what you already know from Lesson 1, and navigate() for history moves.

One myth worth clearing up now: some older tutorials claim you must open the first page with navigate().to() before back() or forward() will work, and that a page opened with get() will not respond to history commands. That is not true. Browser history is tracked by the browser itself, not by which Selenium method you happened to call. Whether you load a page with get() or navigate().to(), the browser adds it to history, and navigate().back() and navigate().forward() work the same way afterward.

Once a page is loaded, your test often needs to confirm it landed in the right place. Two WebDriver methods answer that.

String pageTitle = driver.getTitle();
String pageUrl = driver.getCurrentUrl();

getTitle() returns the text inside the page’s <title> tag, the same text you see on a browser tab. getCurrentUrl() returns the address currently shown in the address bar, including any redirect the site performed after you loaded it.

On https://testkru.com/Elements/TextFields, getTitle() returns Text Fields. That makes it a reliable, low-effort assertion: after navigating somewhere, check the title (or the URL) to confirm you actually arrived, before you try to find any element on that page.

There is a third method, getPageSource(), which returns the entire rendered HTML of the page as one long string. It exists for occasional debugging or for searching raw HTML text, but you will rarely use it in normal tests. It returns a large amount of text and is not meant for everyday assertions. Module 9 revisits it briefly; for now, prefer getTitle() and getCurrentUrl() for confirming where you are.

Real users click a link, then click their browser’s back button, then move forward again. Selenium can reproduce that exact behavior through the navigate() object.

driver.navigate().back();
driver.navigate().forward();
driver.navigate().refresh();

back() moves one step backward in browser history, the same as clicking the browser’s back arrow. forward() moves one step forward, and does nothing if you are already on the most recent page you visited. refresh() reloads the current page, the same as pressing F5 or clicking the reload icon.

These three commands work on whatever history the browser has already built up in the current session, regardless of whether each page was opened with get() or navigate().to(), as you just learned above.

If you called driver.findElement(...) before a refresh(), back(), or forward(), that old WebElement can become unusable afterward. The page you are looking at is technically a new copy of the DOM, even if it looks identical, so a handle that pointed at the old copy can throw StaleElementReferenceException the next time you use it.

You do not need to solve this deeply yet. The simple habit from Lesson 1 still applies: find an element again after the page changes, rather than reusing an old reference across a navigation. Module 4 explains staleness and waits in full detail.

Selenium can also resize, position, and change the display mode of the browser window itself through driver.manage().window().

driver.manage().window().maximize();
driver.manage().window().minimize();
driver.manage().window().fullscreen();

maximize() expands the window to fill the available screen space, which is the most common choice at the start of a test, since a small default window can hide elements that only appear at a wider layout. minimize() shrinks the window to the taskbar or dock. fullscreen() removes the browser’s title bar and toolbar, similar to pressing F11 in a real browser.

You can also read or set an exact size and position when a test needs a specific layout, for example to test a responsive design at a known width.

import org.openqa.selenium.Dimension;
import org.openqa.selenium.Point;

Dimension currentSize = driver.manage().window().getSize();
driver.manage().window().setSize(new Dimension(1280, 800));

Point currentPosition = driver.manage().window().getPosition();
driver.manage().window().setPosition(new Point(0, 0));

getSize() and setSize() work with a Dimension object holding width and height in pixels. getPosition() and setPosition() work with a Point object holding the window’s x and y coordinates on the screen. Most beginner tests only need maximize(), but it helps to know these exist once you start testing layouts at specific screen sizes.

Some scenarios genuinely need two browser tabs open at once, for example comparing a page before and after an action, or following a link that a site opens in a new tab by design. Selenium 4 added a dedicated command for this.

import org.openqa.selenium.WindowType;

driver.switchTo().newWindow(WindowType.TAB);

switchTo().newWindow(WindowType.TAB) opens a new browser tab and automatically switches WebDriver’s focus to it. Pass WindowType.WINDOW instead of WindowType.TAB to open a separate browser window rather than a tab in the same window. Either way, you do not need any extra step to start controlling the new tab or window: Selenium’s own documentation for this feature confirms that focus moves to the new target automatically.

Right after opening it, the new tab is blank, so you load a page into it exactly like any other tab:

driver.get("https://testkru.com/Elements/Buttons");

Every open tab or window has a unique window handle, a string identifier that Selenium assigns internally. You will not read much meaning into the handle itself, but you need it to switch between tabs.

String originalWindow = driver.getWindowHandle();
Set<String> allWindowHandles = driver.getWindowHandles();

getWindowHandle() returns the handle of the tab WebDriver is currently focused on. Call it right after opening your first page, before you open any new tab, so you have a reliable way back. getWindowHandles() returns the handles of every open tab or window as a Set<String>, which is useful for counting how many are open or for finding the one you have not visited yet.

To move WebDriver’s focus to a specific tab later, pass its handle to switchTo().window(...):

driver.switchTo().window(originalWindow);

You do not need this to reach the newly opened tab, since newWindow() already switches you there. You need it to come back to a tab you opened earlier. Module 5 goes deeper into working with several windows and iterating over unfamiliar handles. For this lesson, remembering the original handle before you branch off is enough.

Lesson 1 introduced close() and quit() for ending a single-window test. Now that a test can have more than one tab open, the difference matters in practice, not just in theory.

CallWhat happens
driver.close()Closes only the window or tab that WebDriver is currently focused on. If other tabs are still open, the session stays alive, but WebDriver does not automatically move focus to a remaining tab.
driver.quit()Ends the entire WebDriver session and closes every window or tab associated with it, in one call.

This is exactly why the window handle habit above matters. If you open a second tab, finish your work there, and call driver.close(), your session is still alive, but focus is no longer on a usable window until you switch. You must call driver.switchTo().window(originalWindow) before you try to find elements or make assertions again, or Selenium will complain that it cannot interact with a closed window.

Keep using quit() in @AfterMethod for teardown, exactly as you did in Lesson 1. It reliably closes every tab and window your test opened, even the ones you forgot about.

Here is a complete, runnable test that uses every command from this lesson in one flow: opening a page, reading its title, moving through history, opening a new tab, checking window handles, and switching back before quitting.

Save it in the same Maven project from earlier lessons, under src/test/java/com/codekru/tests/.

package com.codekru.tests;

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WindowType;
import org.openqa.selenium.chrome.ChromeDriver;
import org.testng.Assert;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

import java.util.Set;

public class BrowserNavigationWindowTabTest {

    private WebDriver driver;

    @BeforeMethod
    public void setUp() {
        driver = new ChromeDriver();
        driver.manage().window().maximize();
    }

    @Test
    public void navigateWindowsAndTabsTogether() {
        driver.get("https://testkru.com/Elements/TextFields");
        Assert.assertEquals(driver.getTitle(), "Text Fields");
        Assert.assertTrue(driver.getCurrentUrl().contains("TextFields"));

        driver.navigate().to("https://testkru.com/Elements/Dropdowns");
        Assert.assertEquals(driver.getTitle(), "Dropdowns");

        driver.navigate().back();
        Assert.assertEquals(driver.getTitle(), "Text Fields");

        driver.navigate().forward();
        Assert.assertEquals(driver.getTitle(), "Dropdowns");

        driver.navigate().refresh();
        Assert.assertEquals(driver.getTitle(), "Dropdowns");

        String originalWindow = driver.getWindowHandle();

        driver.switchTo().newWindow(WindowType.TAB);
        driver.get("https://testkru.com/Elements/Buttons");
        Assert.assertEquals(driver.getTitle(), "Buttons");

        Set<String> allWindowHandles = driver.getWindowHandles();
        Assert.assertEquals(allWindowHandles.size(), 2);

        driver.close();
        driver.switchTo().window(originalWindow);
        Assert.assertEquals(driver.getTitle(), "Dropdowns");
    }

    @AfterMethod
    public void tearDown() {
        if (driver != null) {
            driver.quit();
        }
    }
}
  1. setUp() creates a fresh Chrome session and maximizes the window, so every run starts from the same screen size.
  2. driver.get(...) opens the TestKru Text Fields page, and the first two assertions confirm both the title and that the URL contains TextFields.
  3. driver.navigate().to(...) loads the Dropdowns page. Notice this is a second, different way of loading a URL, and the title assertion confirms it worked exactly like get() did.
  4. driver.navigate().back() returns to the Text Fields page, and the assertion checks the title changed back.
  5. driver.navigate().forward() moves forward again to Dropdowns, proving forward history works after a plain get() was used for the very first page.
  6. driver.navigate().refresh() reloads Dropdowns. The title assertion still passes, because a refresh loads the same page again rather than changing it.
  7. driver.getWindowHandle() stores the current tab’s handle in originalWindow, before any second tab exists.
  8. driver.switchTo().newWindow(WindowType.TAB) opens a new tab and switches focus there automatically, then driver.get(...) loads the Buttons page into it.
  9. driver.getWindowHandles() returns both open tabs, and the assertion confirms there are exactly two.
  10. driver.close() closes only the current tab (Buttons), leaving the original tab’s session alive.
  11. driver.switchTo().window(originalWindow) brings focus back to the first tab, and the final assertion confirms its title is still Dropdowns.
  12. tearDown() calls quit(), which closes the remaining tab and ends the whole session.
  1. Save the file in the Maven project you set up in Module 0.
  2. Click the green run icon next to the test method or the class name in IntelliJ.
  3. Choose the TestNG runner if IntelliJ asks which one to use.
  4. Watch Chrome open, move between the Text Fields and Dropdowns pages, briefly open a second tab on the Buttons page, close it, and return to Dropdowns before both tabs close.

TestNG reports the test as passed, and every assertion inside it matched. If any title assertion fails, open the browser’s developer tools on the page in question and confirm the <title> tag still matches what this lesson expects, since TestKru pages occasionally get small content updates.

Extend the same project with a new @Test method that:

  1. Uses the same @BeforeMethod and @AfterMethod lifecycle (ChromeDriver plus quit()).
  2. Opens https://testkru.com/Elements/TextFields.
  3. Navigates to https://testkru.com/Elements/Dropdowns using driver.navigate().to(...).
  4. Calls driver.navigate().back() and asserts the title is Text Fields.
  5. Calls driver.navigate().forward() and asserts the title is Dropdowns again.
  6. Calls driver.manage().window().getSize() and asserts the width is greater than zero, just to confirm the window object responds.

Do not look for a finished solution first. Run it, watch Chrome move between the two pages, and confirm every assertion passes on its own.

Continue practicing: Browser Back and Forward Navigation

If you want more repetition moving between pages before tackling history commands, the earlier Navigate Between Pages assignment is a good warm-up.

Next lesson: Finding and Inspecting Elements in the Browser. So far you have used By.id(...) once, without much explanation. The next lesson slows down and shows you how to open browser developer tools, read an element’s HTML, and choose a locator with confidence before Module 2 teaches every locator strategy in depth.

1. Is there a real difference between driver.get(url) and driver.navigate().to(url)?
No functional difference in how they load a page. Selenium’s own documentation calls get() a synonym for navigate().to(). Use navigate() when you also plan to call back(), forward(), or refresh() in the same test.

2. Do I need to open the first page with navigate().to() for back and forward to work?
No. Browser history is tracked by the browser itself, not by which Selenium method loaded the page. navigate().back() and navigate().forward() work correctly even when the first page was opened with get().

3. What does driver.navigate().refresh() actually do?
It reloads the current page, the same as pressing F5. Any WebElement you found before the refresh can become stale afterward, so find elements again once the page reloads.

4. What is the difference between getTitle() and getCurrentUrl()?
getTitle() returns the text in the page’s <title> tag. getCurrentUrl() returns the full address currently shown in the browser, including redirects.

5. When should I use getPageSource()?
Rarely, for a beginner test. It returns the entire HTML of the page as text, which is useful for occasional debugging but too heavy for routine assertions. Prefer getTitle() or getCurrentUrl() for confirming where you are.

6. What does switchTo().newWindow(WindowType.TAB) do?
It opens a new browser tab and automatically switches WebDriver’s focus to it. Pass WindowType.WINDOW instead to open a full new window rather than a tab.

7. Do I need to manually switch focus to a tab I just opened with newWindow()?
No. Selenium documentation confirms focus moves to the new tab or window automatically. You only need switchTo().window(handle) to return to a tab you opened earlier.

8. What is a window handle?
A unique string identifier Selenium assigns to each open tab or window. You store one with getWindowHandle() before opening a new tab, so you can switch back to it later.

9. If I call driver.close() on a second tab, is my whole test session over?
No, not if other tabs are still open. close() only closes the current tab. The session ends only when the last window closes, or when you call quit() directly.

10. Should I still prefer quit() in @AfterMethod?
Yes. quit() reliably ends the session and closes every tab or window it opened, even ones you did not track a handle for, so it remains the safest teardown choice.

Liked the article? Share this on

Leave a Comment

Your email address will not be published. Required fields are marked *