Selenium multiple tabs at once

前端 未结 7 1532
孤城傲影
孤城傲影 2020-11-27 17: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 I

7条回答
  •  伪装坚强ぢ
    2020-11-27 17:49

    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 = executions.map { TabExecution(it) }
    
        private var currentExecutionIndex: Int = -1
    
        fun start(): List {
            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 {
            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) = with(webDriver) {
            waitForNewTabToOpen(oldWindowHandles, 10)
        }
    
        fun waitForNewTabToOpen(oldWindowHandles: Set, seconds: Int) = with(webDriver) {
            WebDriverWait(webDriver, seconds.toLong()).until { WebDriver -> availableWindowHandles().size > oldWindowHandles.size }
        }
    
        fun availableWindowHandles(): Set = with(webDriver) {
            return webDriver.getWindowHandles()
        }
    
        private fun getNewTabHandle(oldWindowHandles: Set): 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)
        }
    
    }
    

提交回复
热议问题