24

I'm working with Selenium, and am wondering if it's possible to use multiple TABS at once? I do not want to use multiple browser instances (i.e., 2 copies of IE pun). IF IT IS NOT possible, how would one go about switching between individual tabs, that are running sequentially?

Thanks!

rmaddy
  • 298,130
  • 40
  • 468
  • 517
Trevor Tiernan
  • 322
  • 1
  • 2
  • 8

7 Answers7

41

If there is a link that opens up a new window/tab, then you can use driver.switchTo().window();

However, if you want to run something on multiple windows, then I recommend having multiple instances of webdriver. It is much easier to manage, and is supported (There are workarounds on opening a new tab/window, such as pressing a hotkey that opens a new window, but they aren't supported).

If you are wanting to have multiple threads all act on the same driver instance, but different tabs, that is NOT possible.

Nathan Merrill
  • 6,018
  • 5
  • 32
  • 49
  • Wonderful answer. I do have a second question. With IE, how do I make it switch to the new tab? I'm using C#. driver.switchTo().window() requests a string. Thanks – Trevor Tiernan Aug 09 '13 at 18:52
  • You have to get the window handles using driver.getWindowHandles(). You can then iterate through the windows and test the URL or title to make sure its the one you want. – Nathan Merrill Aug 09 '13 at 20:25
  • I think I need a little bit more of an example. I'm using C#. I think that might be java.. – Trevor Tiernan Aug 12 '13 at 16:40
  • Yep, its java. I don't know what it would be in C# but they are usually similarly named. – Nathan Merrill Aug 13 '13 at 05:10
  • Multiple threads acting on the same driver instance IS possible with zeroMQ - thread's zeroMQ sends a request to the driver process's zeroMQ server which then acts on the window or desired tab. As many threads as you like. – venzen Dec 20 '13 at 19:25
  • 1
    @venzen how can we do that? Do you have a sample Github project? – Asad Shakeel May 29 '20 at 08:11
14

It is possible to switch between individual tabs without having multiple browser instances.
There is a difference how web driver handles different windows and how it handles different tabs.

Case 1:
In case there are multiple windows, then the following code can help:

//Get the current window handle
String windowHandle = driver.getWindowHandle();

//Get the list of window handles
ArrayList tabs = new ArrayList (driver.getWindowHandles());
System.out.println(tabs.size());
//Use the list of window handles to switch between windows
driver.switchTo().window(tabs.get(0));

//Switch back to original window
driver.switchTo().window(mainWindowHandle);


Case 2:
In case there are multiple tabs in the same window, then there is only one window handle. Hence switching between window handles keeps the control in the same tab.
In this case using Ctrl + \t (Ctrl + Tab) to switch between tabs is more useful.

//Open a new tab using Ctrl + t
driver.findElement(By.cssSelector("body")).sendKeys(Keys.CONTROL +"t");
//Switch between tabs using Ctrl + \t
driver.findElement(By.cssSelector("body")).sendKeys(Keys.CONTROL +"\t");

Detailed sample code can be found here:
http://design-interviews.blogspot.com/2014/11/switching-between-tabs-in-same-browser-window.html

Sourabh
  • 495
  • 5
  • 12
2

To open multiple tabs:

driver = new ChromeDriver();
IJavaScriptExecutor jscript = driver as IJavaScriptExecutor;
for (int i = 0; i < 10; i++)
{                
  driver.Navigate().GoToUrl(this.baseURL);      
  jscript.ExecuteScript("window.open('{0}', '_blank');", this.baseURL);
}

Swich between them:

for (int i = 0; i < driver.WindowHandles.Count; i++)
{ 
  driver.SwitchTo().Window(driver.WindowHandles[i])]);
}
Marisco
  • 87
  • 1
  • 3
  • 13
1

Try with below code.

    String oldTab = driver.getWindowHandle();
    driver.manage().timeouts().implicitlyWait(1, TimeUnit.SECONDS);
    ArrayList<String> newTab = new ArrayList<String>(driver.getWindowHandles());
    newTab.remove(oldTab);
    driver.switchTo().window(newTab.get(0));
Alien
  • 11,017
  • 3
  • 29
  • 45
Rupam Dhar
  • 41
  • 2
1

I recently implemented a simple multi-thread utility that allows running tests on separate tabs on separate threads WITH JUST ONE WEBDRIVER INSTANCE. The problem with WebDriver is that it can focus only one tab (window) at a time. So, to do tests in multiple tabs, WebDriver must be focused separately on each of them. I'm sure my implementation is not perfect, but here it is (implementation in Kotlin):

Usage:

fun test() {

  val results = ParallelNavigator(webDriver, 
    listOf(
    ::test1, 
    ::test2, 
    ::test3
    )
  ).start()

  println(results)
  // Output: [Success, Failure: java.lang.RuntimeException: Some error, Success]

}

fun test1(pn: ParallelNavigator) {

  /* ... open url, find elements etc. so stuff */

  pn.resumeNext() // transfer flow to another unfinished thread (test2 if not finished)

  /* ... do more stuff */

  pn.resumeNext() // again transfer flow to another thread

}

fun test2(pn: ParallelNavigator) { /* ... */ }
fun test3(pn: ParallelNavigator) { /* ... */ }

Implementation:

import org.openqa.selenium.JavascriptExecutor
import org.openqa.selenium.WebDriver
import org.openqa.selenium.support.ui.WebDriverWait
import java.util.concurrent.locks.Condition
import java.util.concurrent.locks.ReentrantLock
import kotlin.concurrent.thread
import kotlin.concurrent.withLock

class ParallelNavigator(private val webDriver: WebDriver, executions: List<(ParallelNavigator) -> Unit>) {

    private val _executions: List<TabExecution> = executions.map { TabExecution(it) }

    private var currentExecutionIndex: Int = -1

    fun start(): List<Result> {
        createTabs()
        return runInternal()
    }

    fun resumeNext() {
        if (_executions.isEmpty()) {
            throw RuntimeException("No executions provided.")
        }

        val currentExecution: TabExecution? = if (currentExecutionIndex != -1) {
            _executions[currentExecutionIndex]
        } else null

        val unfinished = _executions.filter { !it.finished }

        if(unfinished.isEmpty()) {
            return
        }

        val nextExecutionIndex = if (currentExecutionIndex >= unfinished.lastIndex || currentExecutionIndex <= -1) {
            0
        } else {
            currentExecutionIndex + 1
        }
        val nextExecution = unfinished[nextExecutionIndex]
        currentExecutionIndex = nextExecutionIndex
        webDriver.switchTo().window(nextExecution.windowHandle)
        nextExecution.lock.withLock {
            nextExecution.condition.signal()
        }
        currentExecution?.lock?.withLock {
            if (!currentExecution.finished) {
                currentExecution.condition.await()
            }
        }
    }

    sealed class Result {
        class Success : Result() {
            override fun toString(): String {
                return "Success"
            }
        }
        class Failure(val ex: Throwable) : Result() {
            override fun toString(): String {
                return "Failure: ${ex.javaClass.name}: ${ex.message}"
            }
        }
        class Unfinished : Result() {
            override fun toString(): String {
                return "Unfinished"
            }
        }
    }

    data class TabExecution(
        val test: (ParallelNavigator) -> Unit,
        val lock: ReentrantLock = ReentrantLock(),
        var finished: Boolean = false
    ) {
        lateinit var windowHandle: String
        lateinit var condition: Condition
        lateinit var thread: Thread
    }


    private fun createTabs() = with(webDriver) {
        navigate().to("about:blank")
        val homeWindowHandle = windowHandle
        for (execution in _executions) {
            execution.windowHandle = openNewTab()
        }
        webDriver.switchTo().window(homeWindowHandle)
    }

    private fun runInternal(): List<Result> {
        val results = _executions.map { Result.Unfinished() as Result }.toMutableList()
        for (index in _executions.indices) {
            val execution = _executions[index]
            val condition = execution.lock.newCondition()
            execution.condition = condition
            execution.thread = thread(start = false) {
                execution.lock.withLock {
                    condition.await()
                    try {
                        execution.test(this)
                        results[index] = Result.Success()
                    } catch (ex: Throwable) {
                        ex.printStackTrace()
                        results[index] = Result.Failure(ex)
                    }
                    execution.finished = true
                    currentExecutionIndex--
                    resumeNext()
                }
            }

            execution.thread.start()
        }

        resumeNext() // run first execution

        for (execution in _executions) {
            execution.thread.join()
        }

        return results
    }

    fun waitForNewTabToOpen(oldWindowHandles: Set<String>) = with(webDriver) {
        waitForNewTabToOpen(oldWindowHandles, 10)
    }

    fun waitForNewTabToOpen(oldWindowHandles: Set<String>, seconds: Int) = with(webDriver) {
        WebDriverWait(webDriver, seconds.toLong()).until<Boolean> { WebDriver -> availableWindowHandles().size > oldWindowHandles.size }
    }

    fun availableWindowHandles(): Set<String> = with(webDriver) {
        return webDriver.getWindowHandles()
    }

    private fun getNewTabHandle(oldWindowHandles: Set<String>): String = with(webDriver) {
        waitForNewTabToOpen(oldWindowHandles)
        val newWindowHandles = availableWindowHandles().toMutableSet()
        newWindowHandles.removeAll(oldWindowHandles)
        return newWindowHandles.iterator().next()
    }

    fun openNewTab(): String = with(webDriver) {
        val oldHandles = availableWindowHandles()
        (webDriver as JavascriptExecutor).executeScript("Object.assign(document.createElement('a'), { target: '_blank', href: 'about:blank'}).click();")
        waitForNewTabToOpen(oldHandles)
        return getNewTabHandle(oldHandles)
    }

}
xinaiz
  • 6,915
  • 3
  • 26
  • 67
1

This will solve your problem in case ur working with selenium and nodejs.

driver.get('https://www.google.com/')
  .then(_ =>
    driver.findElement(webdriver.By.tagName('body'))
  )
  .then(bodyElement => {
    bodyElement.sendKeys(webdriver.Key.chord(webdriver.Key.CONTROL, 't'))
  })
  .catch(err => {
    console.log(err);
  })
Roberto Caboni
  • 6,078
  • 10
  • 19
  • 34
Saurabh507
  • 23
  • 6
0

if you are wishing to run the multıple wındows at the same time, use threading with multiple instances of IWebDriver

EX:

public void Work()
{
IWebDriver driver = new ChromeDriver("D:\\Drivers");
driver.Navigate().GoToUrl(URL);
\\Do the rest
}
public void Work2()
{
IWebDriver driver = new ChromeDriver("D:\\Drivers");
driver.Navigate().GoToUrl(URL2);
\\Do the rest
}

and call the function like this:

Thread thread1 = new Thread(new ThreadStart(Work));
thread1.Start();
Thread thread2 = new Thread(new ThreadStart(Work2));
thread2.Start();
Yahya Hussein
  • 7,519
  • 12
  • 45
  • 86