How to use BrowserTests class of com.github.epadronu.balin.core package

Best Balin code snippet using com.github.epadronu.balin.core.BrowserTests

Run Balin automation tests on LambdaTest cloud grid

Perform automation testing on 3000+ real desktop and mobile devices online.

SearchContextListExtensions.kt

Source: SearchContextListExtensions.kt Github

copy
1/******************************************************************************
2 * Copyright 2016 Edinson E. Padrón Urdaneta
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *****************************************************************************/
16
17/* ***************************************************************************/
18package com.github.epadronu.balin.extensions
19/* ***************************************************************************/
20
21/* ***************************************************************************/
22import org.openqa.selenium.SearchContext
23import org.openqa.selenium.WebElement
24/* ***************************************************************************/
25
26/* ***************************************************************************/
27/**
28 * Find the nth element that can be located within the current context by the
29 * given CSS selector.
30 *
31 * This is an alternative to the `find` method.
32 *
33 * @param selector the CSS selector to be used for locating the element.
34 * @param index the index of the element to be returned.
35 * @return The nth matching element within the current context.
36 * @throws java.lang.IndexOutOfBoundsException for an illegal index value.
37 * @sample com.github.epadronu.balin.core.BrowserTests.find_some_basic_elements_in_the_page
38 */
39fun List<SearchContext>.`$`(selector: String, index: Int): WebElement = find(selector, index)
40
41/**
42 * Find all the elements that can be located by the given CSS selector within
43 * the current context, restricted by the specified range.
44 *
45 * This is an alternative to the `find` method.
46 *
47 * @param selector the CSS selector to be used for locating the elements.
48 * @param range specify the indices of the elements to be returned.
49 * @return The matching elements within the current context, restricted by the specified range.
50 * @throws java.lang.IndexOutOfBoundsException for illegal index values within the range.
51 * @sample com.github.epadronu.balin.core.BrowserTests.find_some_basic_elements_in_the_page
52 */
53fun List<SearchContext>.`$`(selector: String, range: IntRange): List<WebElement> = find(selector, range)
54
55/**
56 * Find all the elements that can be located by the given CSS selector within
57 * the current context, restricted by the specified indices. (If no index is
58 * provided, then all matching elements will be returned.)
59 *
60 * This is an alternative to the `find` method.
61 *
62 * @param selector the CSS selector to be used for locating the elements.
63 * @param indices the indices of the elements to be returned.
64 * @return The matching elements within the current context restricted by the specified indices. (Or all matching elements if no index is provided.)
65 * @throws java.lang.IndexOutOfBoundsException for illegal index values.
66 * @sample com.github.epadronu.balin.core.BrowserTests.find_some_basic_elements_in_the_page
67 */
68fun List<SearchContext>.`$`(selector: String, vararg indices: Int): List<WebElement> = find(selector, *indices)
69
70/**
71 * Find the nth element that can be located within the current context by the
72 * given CSS selector.
73 *
74 * @param selector the CSS selector to be used for locating the element.
75 * @param index the index of the element to be returned.
76 * @return The nth matching element within the current context.
77 * @throws java.lang.IndexOutOfBoundsException for illegal index values.
78 * @sample com.github.epadronu.balin.core.BrowserTests.find_some_basic_elements_in_the_page
79 */
80fun List<SearchContext>.find(selector: String, index: Int): WebElement = this.map {
81    it.find(selector)
82}.flatten()[index]
83
84/**
85 * Find all the elements that can be located by the given CSS selector within
86 * the current context, restricted by the specified range.
87 *
88 * @param selector the CSS selector to be used for locating the elements.
89 * @param range specify the indices of the elements to be returned.
90 * @return The matching elements within the current context, restricted by the specified range.
91 * @throws java.lang.IndexOutOfBoundsException for illegal index values within the range.
92 * @sample com.github.epadronu.balin.core.BrowserTests.find_some_basic_elements_in_the_page
93 */
94fun List<SearchContext>.find(selector: String, range: IntRange): List<WebElement> = this.map {
95    it.find(selector)
96}.flatten().slice(range)
97
98/**
99 * Find all the elements that can be located by the given CSS selector within
100 * the current context, restricted by the specified indices. (If no index is
101 * provided, then all matching elements will be returned.)
102 *
103 * @param selector the CSS selector to be used for locating the elements.
104 * @param indices the indices of the elements to be returned.
105 * @return The matching elements within the current context, restricted by the specified indices. (Or all matching elements if no index is provided.)
106 * @throws java.lang.IndexOutOfBoundsException for illegal index values.
107 * @sample com.github.epadronu.balin.core.BrowserTests.find_some_basic_elements_in_the_page
108 */
109fun List<SearchContext>.find(selector: String, vararg indices: Int): List<WebElement> {
110    val elements = this.map { it.find(selector) }.flatten()
111
112    if (indices.isEmpty()) {
113        return elements
114    }
115
116    return elements.slice(indices.asList())
117}
118/* ***************************************************************************/
119
Full Screen

Browser.kt

Source: Browser.kt Github

copy
1/******************************************************************************
2 * Copyright 2016 Edinson E. Padrón Urdaneta
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *****************************************************************************/
16
17/* ***************************************************************************/
18package com.github.epadronu.balin.core
19/* ***************************************************************************/
20
21/* ***************************************************************************/
22import com.github.epadronu.balin.config.Configuration
23import com.github.epadronu.balin.config.ConfigurationBuilder
24import com.github.epadronu.balin.config.ConfigurationSetup
25import com.github.epadronu.balin.exceptions.MissingPageUrlException
26import com.github.epadronu.balin.exceptions.PageImplicitAtVerificationException
27import com.github.epadronu.balin.utils.ThreadLocalDelegate
28import org.openqa.selenium.Alert
29import org.openqa.selenium.NoSuchWindowException
30import org.openqa.selenium.WebDriver
31import org.openqa.selenium.WebElement
32import org.openqa.selenium.support.ui.ExpectedConditions.alertIsPresent
33import kotlin.reflect.full.primaryConstructor
34/* ***************************************************************************/
35
36/* ***************************************************************************/
37/**
38 * Balin's backbone. The `Browser` interface binds together the different
39 * abstractions that form part of the library.
40 *
41 * Additionally, this interface defines the entry point for the Domain-Specific
42 * Language which Balin is built around.
43 */
44interface Browser : JavaScriptSupport, WaitingSupport, WebDriver {
45
46    companion object {
47        /**
48         * The builder in charge of generating the configuration.
49         */
50        private val configurationBuilder: ConfigurationBuilder by ThreadLocalDelegate {
51            ConfigurationBuilder()
52        }
53
54        /**
55         * The name of the property that dictates which setup to use.
56         */
57        internal const val BALIN_SETUP_NAME_PROPERTY: String = "balin.setup.name"
58
59        /**
60         * Retrieves the configuration generated by the builder, taking in
61         * account the value of the [BALIN_SETUP_NAME_PROPERTY] property.
62         */
63        internal val desiredConfiguration: ConfigurationSetup
64            get() = configurationBuilder.build().run {
65                setups[System.getProperty(BALIN_SETUP_NAME_PROPERTY) ?: "default"] ?: this
66            }
67
68        /**
69         * Domain-Specific language that let's you configure Balin's global
70         * behavior.
71         *
72         * @sample com.github.epadronu.balin.config.ConfigurationTests.call_the_configure_method_and_make_changes
73         *
74         * @param block here you can interact with the DSL.
75         */
76        fun configure(block: ConfigurationBuilder.() -> Unit) {
77            block(configurationBuilder)
78        }
79
80        /**
81         * This method represents the entry point for the Domain-Specific
82         * Language which Balin is built around.
83         *
84         * `drive` is the main abstraction layer for Selenium-WebDriver. Inside
85         * the [block] it receives as parameter, you can interact with the
86         * driver and use all the features Balin has to offer.
87         *
88         * @sample com.github.epadronu.balin.core.BrowserTests.perform_a_simple_web_navigation
89         *
90         * @param driverFactory provides the driver on which the navigation and interactions will be performed.
91         * @param autoQuit indicates if the driver should quit at the end of the [block].
92         * @param block here you interact with the driver alongside of Balin's assistance.
93         */
94        fun drive(
95            driverFactory: () -> WebDriver = desiredConfiguration.driverFactory,
96            autoQuit: Boolean = desiredConfiguration.autoQuit,
97            block: Browser.() -> Unit) = drive(Configuration(autoQuit, driverFactory), block)
98
99        /**
100         * This method represents the entry point for the Domain-Specific
101         * Language which Balin is built around.
102         *
103         * `drive` is the main abstraction layer for Selenium-WebDriver. Inside
104         * the [block] it receives as parameter, you can interact with the
105         * driver and use all the features Balin has to offer.
106         *
107         * @sample com.github.epadronu.balin.core.BrowserTests.perform_a_simple_web_navigation
108         *
109         * @param configuration defines Balin's local behavior for [block] only.
110         * @param block here you interact with the driver alongside of Balin's assistance.
111         */
112        fun drive(configuration: Configuration, block: Browser.() -> Unit) {
113            val desiredConfiguration = configuration.run {
114                setups[System.getProperty(BALIN_SETUP_NAME_PROPERTY) ?: "default"] ?: this
115            }
116
117            BrowserImpl(desiredConfiguration).apply {
118                try {
119                    block()
120                } catch (throwable: Throwable) {
121                    throw throwable
122                } finally {
123                    if (configurationSetup.autoQuit) {
124                        quit()
125                    }
126                }
127            }
128        }
129    }
130
131    /**
132     * Tells the browser at what page it should be located.
133     *
134     * If the page defines an _implicit at verification_, then it will be
135     * invoked immediately. If such verification fails, Balin will throw a
136     * [PageImplicitAtVerificationException] in order to perform an early
137     * failure.
138     *
139     * @sample com.github.epadronu.balin.core.BrowserTests.model_a_page_into_a_Page_Object_and_interact_with_it_via_the_at_method
140     *
141     * @param T the page's type.
142     * @param factory provides an instance of the page given the driver being used by the browser.
143     * @Returns An instance of the current page.
144     * @throws PageImplicitAtVerificationException if the page has an _implicit at verification_ which have failed.
145     */
146    fun <T : Page> at(factory: (Browser) -> T): T = factory(this).apply {
147        if (!verifyAt()) {
148            throw PageImplicitAtVerificationException()
149        }
150    }
151
152    /**
153     * Navigates to the given page.
154     *
155     * If the page has not defined a URL, then a
156     * [MissingPageUrlException] will be thrown immediately since
157     * is not possible to perform the navigation.
158     *
159     * If the page defines an _implicit at verification_, then it
160     * will be invoked immediately. If such verification fails, Balin
161     * will throw a [PageImplicitAtVerificationException] in order to
162     * perform an early failure.
163     *
164     * @sample com.github.epadronu.balin.core.BrowserTests.perform_a_simple_web_navigation
165     *
166     * @param T the page's type.
167     * @param factory provides an instance of the page given the driver being used by the browser.
168     * @Returns An instance of the current page.
169     * @throws MissingPageUrlException if the page has not defined a URL.
170     * @throws PageImplicitAtVerificationException if the page has an _implicit at verification_ which have failed.
171     * @see org.openqa.selenium.WebDriver.get
172     */
173    fun <T : Page> to(factory: (Browser) -> T): T = factory(this).apply {
174        get(url ?: throw MissingPageUrlException())
175
176        if (!verifyAt()) {
177            throw PageImplicitAtVerificationException()
178        }
179    }
180
181    /**
182     * Navigates to the given URL.
183     *
184     * @param url the URL the browser will navigate to.
185     * @return The browser's current URL.
186     *
187     * @see org.openqa.selenium.WebDriver.get
188     */
189    fun to(url: String): String {
190        get(url)
191
192        return currentUrl
193    }
194}
195/* ***************************************************************************/
196
197/* ***************************************************************************/
198/**
199 * Switches to the currently active modal dialog for this particular driver instance.
200 *
201 * You can interact with the dialog handler only inside [alertContext].
202 *
203 * @sample com.github.epadronu.balin.core.WithAlertTests.validate_context_switching_to_and_from_an_alert_popup_and_accept_it
204 *
205 * @param alertContext here you can interact with the dialog handler.
206 * @throws org.openqa.selenium.NoAlertPresentException If the dialog cannot be found.
207 */
208inline fun Browser.withAlert(alertContext: Alert.() -> Unit): Unit = try {
209    switchTo().alert().run {
210        alertContext()
211
212        if (this == alertIsPresent().apply(driver)) {
213            dismiss()
214        }
215    }
216} catch (throwable: Throwable) {
217    throw throwable
218} finally {
219    switchTo().defaultContent()
220}
221
222/**
223 * Select a frame by its (zero-based) index and switch the driver's context to
224 * it.
225 *
226 * Once the frame has been selected, all subsequent calls on the WebDriver
227 * interface are made to that frame till the end of [iFrameContext].
228 *
229 * If a exception is thrown inside [iFrameContext], the driver will return to
230 * its default context.
231 *
232 * @sample com.github.epadronu.balin.core.WithFrameTests.validate_context_switching_to_and_from_an_iframe_with_index
233 *
234 * @param index (zero-based) index.
235 * @param iFrameContext here you can interact with the given IFrame.
236 * @throws org.openqa.selenium.NoSuchFrameException If the frame cannot be found.
237 */
238inline fun Browser.withFrame(index: Int, iFrameContext: () -> Unit): Unit = try {
239    switchTo().frame(index)
240    iFrameContext()
241} catch (throwable: Throwable) {
242    throw throwable
243} finally {
244    switchTo().defaultContent()
245}
246
247/**
248 * Select a frame by its name or ID. Frames located by matching name attributes
249 * are always given precedence over those matched by ID.
250 *
251 * Once the frame has been selected, all subsequent calls on the WebDriver
252 * interface are made to that frame till the end of [iFrameContext].
253 *
254 * If a exception is thrown inside [iFrameContext], the driver will return to
255 * its default context.
256 *
257 * @sample com.github.epadronu.balin.core.WithFrameTests.validate_context_switching_to_and_from_an_iframe_with_id
258 *
259 * @param nameOrId the name of the frame window, the id of the &lt;frame&gt; or &lt;iframe&gt; element, or the (zero-based) index.
260 * @param iFrameContext here you can interact with the given IFrame.
261 * @throws org.openqa.selenium.NoSuchFrameException If the frame cannot be found.
262 */
263inline fun Browser.withFrame(nameOrId: String, iFrameContext: () -> Unit): Unit = try {
264    switchTo().frame(nameOrId)
265    iFrameContext()
266} catch (throwable: Throwable) {
267    throw throwable
268} finally {
269    switchTo().defaultContent()
270}
271
272/**
273 * Select a frame using its previously located WebElement.
274 *
275 * Once the frame has been selected, all subsequent calls on the WebDriver
276 * interface are made to that frame till the end of [iFrameContext].
277 *
278 * If a exception is thrown inside [iFrameContext], the driver will return to
279 * its default context.
280 *
281 * @sample com.github.epadronu.balin.core.WithFrameTests.validate_context_switching_to_and_from_an_iframe_with_web_element
282 *
283 * @param webElement the frame element to switch to.
284 * @param iFrameContext here you can interact with the given IFrame.
285 * @throws org.openqa.selenium.NoSuchFrameException If the frame cannot be found.
286 */
287inline fun Browser.withFrame(webElement: WebElement, iFrameContext: () -> Unit): Unit = try {
288    switchTo().frame(webElement)
289    iFrameContext()
290} catch (throwable: Throwable) {
291    throw throwable
292} finally {
293    switchTo().defaultContent()
294}
295
296/**
297 * Select a frame by its (zero-based) index and switch the driver's context to
298 * it.
299 *
300 * Once the frame has been selected, all subsequent calls on the WebDriver
301 * interface are made to that frame via a `Page Object` of type [T] till
302 * the end of [iFrameContext].
303 *
304 * If a exception is thrown inside [iFrameContext], the driver will return to
305 * its default context.
306 *
307 * @sample com.github.epadronu.balin.core.WithFrameTests.validate_context_switching_to_and_from_an_iframe_with_index_and_pages
308 *
309 * @param T the `Page Object`'s type.
310 * @param index (zero-based) index.
311 * @param iFrameContext here you can interact with the given IFrame via a `Page Object`.
312 * @throws org.openqa.selenium.NoSuchFrameException If the frame cannot be found.
313 */
314inline fun <reified T : Page> Browser.withFrame(index: Int, iFrameContext: T.() -> Unit): Unit = try {
315    switchTo().frame(index)
316    @Suppress("UNCHECKED_CAST")
317    iFrameContext(at(T::class.primaryConstructor as (Browser) -> T))
318} catch (throwable: Throwable) {
319    throw throwable
320} finally {
321    switchTo().defaultContent()
322}
323
324/**
325 * Select a frame by its name or ID. Frames located by matching name attributes
326 * are always given precedence over those matched by ID.
327 *
328 * Once the frame has been selected, all subsequent calls on the WebDriver
329 * interface are made to that frame via a `Page Object` of type [T] till
330 * the end of [iFrameContext].
331 *
332 * If a exception is thrown inside [iFrameContext], the driver will return to
333 * its default context.
334 *
335 * @sample com.github.epadronu.balin.core.WithFrameTests.validate_context_switching_to_and_from_an_iframe_with_id_and_pages
336 *
337 * @param T the `Page Object`'s type.
338 * @param nameOrId the name of the frame window, the id of the &lt;frame&gt; or &lt;iframe&gt; element, or the (zero-based) index.
339 * @param iFrameContext here you can interact with the given IFrame via a `Page Object`.
340 * @throws org.openqa.selenium.NoSuchFrameException If the frame cannot be found.
341 */
342inline fun <reified T : Page> Browser.withFrame(nameOrId: String, iFrameContext: T.() -> Unit): Unit = try {
343    switchTo().frame(nameOrId)
344    @Suppress("UNCHECKED_CAST")
345    iFrameContext(at(T::class.primaryConstructor as (Browser) -> T))
346} catch (throwable: Throwable) {
347    throw throwable
348} finally {
349    switchTo().defaultContent()
350}
351
352/**
353 * Select a frame using its previously located WebElement.
354 *
355 * Once the frame has been selected, all subsequent calls on the WebDriver
356 * interface are made to that frame via a `Page Object` of type [T] till
357 * the end of [iFrameContext].
358 *
359 * If a exception is thrown inside [iFrameContext], the driver will return to
360 * its default context.
361 *
362 * @sample com.github.epadronu.balin.core.WithFrameTests.validate_context_switching_to_and_from_an_iframe_with_web_element_and_pages
363 *
364 * @param T the `Page Object`'s type.
365 * @param webElement the frame element to switch to.
366 * @param iFrameContext here you can interact with the given IFrame via a `Page Object`.
367 * @throws org.openqa.selenium.NoSuchFrameException If the frame cannot be found.
368 */
369inline fun <reified T : Page> Browser.withFrame(webElement: WebElement, iFrameContext: T.() -> Unit): Unit = try {
370    switchTo().frame(webElement)
371    @Suppress("UNCHECKED_CAST")
372    iFrameContext(at(T::class.primaryConstructor as (Browser) -> T))
373} catch (throwable: Throwable) {
374    throw throwable
375} finally {
376    switchTo().defaultContent()
377}
378
379/**
380 * Switch the focus of future commands for this driver to the window with the
381 * given name/handle.
382 *
383 * The name/handle can be omitted and the switching will be performed
384 * automatically if and only if there is only two windows currently
385 * opened.
386 *
387 * Once the window has been selected, all subsequent calls on the WebDriver
388 * interface are made to that window till the end of [windowContext].
389 *
390 * If a exception is thrown inside [windowContext], the driver will return to
391 * the previous window.
392 *
393 * @sample com.github.epadronu.balin.core.WithWindowTests.validate_context_switching_to_and_from_a_window
394 *
395 * @param nameOrHandle The name of the window or the handle as returned by [WebDriver.getWindowHandle]
396 * @param windowContext Here you can interact with the given window.
397 * @throws NoSuchWindowException If the window cannot be found or, in the case of no name or handle is indicated,
398 *                               there is not exactly two windows currently opened.
399 */
400inline fun Browser.withWindow(nameOrHandle: String? = null, windowContext: WebDriver.() -> Unit) {
401    val originalWindow = windowHandle
402
403    val targetWindow = nameOrHandle ?: windowHandles.toSet().minus(originalWindow).run {
404        when (size) {
405            0 -> throw NoSuchWindowException("No new window was found")
406            1 -> first()
407            else -> throw NoSuchWindowException("The window cannot be determined automatically")
408        }
409    }
410
411    try {
412        switchTo().window(targetWindow).windowContext()
413    } catch (throwable: Throwable) {
414        throw throwable
415    } finally {
416        if (originalWindow != targetWindow && windowHandles.contains(targetWindow)) {
417            close()
418        }
419
420        switchTo().window(originalWindow)
421    }
422}
423/* ***************************************************************************/
424
Full Screen

WaitingSupport.kt

Source: WaitingSupport.kt Github

copy
1/******************************************************************************
2 * Copyright 2016 Edinson E. Padrón Urdaneta
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *****************************************************************************/
16
17/* ***************************************************************************/
18package com.github.epadronu.balin.core
19/* ***************************************************************************/
20
21/* ***************************************************************************/
22import com.github.epadronu.balin.config.ConfigurationSetup
23import org.openqa.selenium.WebDriver
24import org.openqa.selenium.support.ui.ExpectedCondition
25import org.openqa.selenium.support.ui.WebDriverWait
26/* ***************************************************************************/
27
28/* ***************************************************************************/
29/**
30 * Describes the `waitFor` method support, which aims to ease the use of
31 * [WebDriverWait][org.openqa.selenium.support.ui.WebDriverWait].
32 *
33 * @sample com.github.epadronu.balin.core.BrowserTests.wait_for_the_presence_of_an_element_that_should_be_there
34 */
35interface WaitingSupport {
36
37    /**
38     * The driver to be used when evaluating `isTrue` in [waitFor][waitFor].
39     */
40    val driver: WebDriver
41
42    /**
43     * The configuration setup used to customized Balin's behavior.
44     */
45    val configurationSetup: ConfigurationSetup
46
47    /**
48     * Repeatedly applies the underlying driver to the given function until
49     * one of the following occurs:
50     *
51     * 1. the function returns neither null nor false
52     * 2. the function throws an unignored exception
53     * 3. the timeout expires
54     * 4. the current thread is interrupted
55     *
56     * @param T the function's expected return type.
57     * @param timeOutInSeconds the timeout in seconds when an expectation is called.
58     * @param sleepInMillis the duration in milliseconds to sleep between polls.
59     * @param isTrue the parameter to pass to the ExpectedCondition.
60     * @return The function's return value if the function returned something different from null or false before the timeout expired.
61     */
62    fun <T> waitFor(timeOutInSeconds: Long = configurationSetup.waitForTimeOutTimeInSeconds,
63                    sleepInMillis: Long = configurationSetup.waitForSleepTimeInMilliseconds,
64                    isTrue: () -> ExpectedCondition<T>): T {
65        return WebDriverWait(driver, timeOutInSeconds, sleepInMillis).until(isTrue())
66    }
67}
68/* ***************************************************************************/
69
Full Screen

Accelerate Your Automation Test Cycles With LambdaTest

Leverage LambdaTest’s cloud-based platform to execute your automation tests in parallel and trim down your test execution time significantly. Your first 100 automation testing minutes are on us.

Try LambdaTest

Run Selenium Automation Tests on LambdaTest Cloud Grid

Trigger Selenium automation tests on a cloud-based Grid of 3000+ real browsers and operating systems.

Test now for Free
LambdaTestX

We use cookies to give you the best experience. Cookies help to provide a more personalized experience and relevant advertising for you, and web analytics for us. Learn More in our Cookies policy, Privacy & Terms of service

Allow Cookie