How to use ExecutableFinder class of org.openqa.selenium.os package

Best Selenium code snippet using org.openqa.selenium.os.ExecutableFinder

Run Selenium automation tests on LambdaTest cloud grid

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

copy
1package tk.gustavo;
2
3import org.fluentlenium.adapter.junit.FluentTest;
4import org.junit.BeforeClass;
5import org.openqa.selenium.os.ExecutableFinder;
6
7public abstract class AbstractChromeTest extends FluentTest {
8
9    private static final String PATH_TO_CHROME_DRIVER =
10            "C:\\Users\\Gustavo Maciel\\Documents\\Diversos\\Projects\\flask-selenium-test\\driver\\chromedriver.exe";
11    private static final String CHROME_DRIVER_PROPERTY = "webdriver.chrome.driver";
12
13    @BeforeClass
14    public static void setup() {
15        if (systemPropertyNotSet() && executableNotPresentInPath()) {
16            setSystemProperty();
17        }
18    }
19
20    private static boolean executableNotPresentInPath() {
21        return new ExecutableFinder().find("chromedriver") == null;
22    }
23
24    private static boolean systemPropertyNotSet() {
25        return System.getProperty(CHROME_DRIVER_PROPERTY) == null;
26    }
27
28    private static void setSystemProperty() {
29        System.setProperty(CHROME_DRIVER_PROPERTY, PATH_TO_CHROME_DRIVER);
30    }
31
32}
33
Full Screen
copy
1package com.kms.katalon.core.webui.util;
2
3import static org.openqa.selenium.Platform.MAC;
4import static org.openqa.selenium.Platform.UNIX;
5import static org.openqa.selenium.Platform.WINDOWS;
6
7import java.io.File;
8import java.io.IOException;
9
10import org.apache.commons.lang.math.NumberUtils;
11import org.openqa.selenium.Platform;
12import org.openqa.selenium.WebDriverException;
13import org.openqa.selenium.firefox.FirefoxDriver;
14import org.openqa.selenium.os.ExecutableFinder;
15import org.openqa.selenium.os.WindowsUtils;
16import org.openqa.selenium.remote.DesiredCapabilities;
17
18import com.google.common.collect.ImmutableList;
19
20// This class duplicated codes from org.openqa.selenium.firefox.internal.Executable as it is internal.
21// Should not be re-factored.
22public class FirefoxExecutable {
23    private static final String VERSION_SEPARATOR_REGEX = "\\.";
24
25    private static final String MOZILLA_FIREFOX_VERSION_STRING_PREFIX = "Mozilla Firefox";
26
27    private static final File SYSTEM_BINARY = locateFirefoxBinaryFromSystemProperty();
28
29    private static final File PLATFORM_BINARY = locateFirefoxBinaryFromPlatform();
30
31    private FirefoxExecutable() {
32    }
33
34    public static int getFirefoxVersion(DesiredCapabilities desiredCapabilities) {
35        File defaultFirefoxBinary = FirefoxExecutable.getFirefoxBinaryFile(getFirefoxBinary(desiredCapabilities));
36        try {
37            String firefoxVersionString = ConsoleCommandExecutor.runConsoleCommandAndCollectFirstResult(
38                    new String[] { defaultFirefoxBinary.getAbsolutePath(), "-v", "|", "more" });
39            if (firefoxVersionString == null
40                    || !firefoxVersionString.startsWith(MOZILLA_FIREFOX_VERSION_STRING_PREFIX)) {
41                return 0;
42            }
43            firefoxVersionString = firefoxVersionString.substring(MOZILLA_FIREFOX_VERSION_STRING_PREFIX.length())
44                    .trim();
45            String firefoxVersionMajor = firefoxVersionString.split(VERSION_SEPARATOR_REGEX)[0];
46            Number firefoxVersion = NumberUtils.createNumber(firefoxVersionMajor);
47            return firefoxVersion.intValue();
48        } catch (IOException | InterruptedException | NumberFormatException e) {
49            // Exception happened, ignore
50        }
51        return 0;
52    }
53
54    private static String getFirefoxBinary(DesiredCapabilities desiredCapabilities) {
55        if (desiredCapabilities == null || desiredCapabilities.getCapability(FirefoxDriver.BINARY) == null) {
56            return null;
57        }
58        Object raw = desiredCapabilities.getCapability(FirefoxDriver.BINARY);
59        if (raw instanceof String) {
60            return (String) raw;
61        }
62        return null;
63    }
64
65    public static File getFirefoxBinaryFile(String userSpecifiedBinaryPath) throws WebDriverException {
66        if (userSpecifiedBinaryPath != null) {
67
68            File userSpecifiedBinaryFile = new File(userSpecifiedBinaryPath);
69            // It should exist and be a file.
70            if (userSpecifiedBinaryFile.exists() && userSpecifiedBinaryFile.isFile()) {
71                return userSpecifiedBinaryFile;
72            }
73
74            throw new WebDriverException("Specified firefox binary location does not exist or is not a real file: "
75                    + userSpecifiedBinaryPath);
76        }
77
78        if (SYSTEM_BINARY != null && SYSTEM_BINARY.exists()) {
79            return SYSTEM_BINARY;
80        }
81
82        if (PLATFORM_BINARY != null && PLATFORM_BINARY.exists()) {
83            return PLATFORM_BINARY;
84        }
85
86        throw new WebDriverException("Cannot find firefox binary in PATH. "
87                + "Make sure firefox is installed. OS appears to be: " + Platform.getCurrent());
88    }
89
90    private static File locateFirefoxBinaryFromSystemProperty() {
91        String binaryName = System.getProperty(FirefoxDriver.SystemProperty.BROWSER_BINARY);
92        if (binaryName == null)
93            return null;
94
95        File binary = new File(binaryName);
96        if (binary.exists())
97            return binary;
98
99        Platform current = Platform.getCurrent();
100        if (current.is(WINDOWS)) {
101            if (!binaryName.endsWith(".exe"))
102                binaryName += ".exe";
103
104        } else if (current.is(MAC)) {
105            if (!binaryName.endsWith(".app"))
106                binaryName += ".app";
107            binaryName += "/Contents/MacOS/firefox-bin";
108        }
109
110        binary = new File(binaryName);
111        if (binary.exists())
112            return binary;
113
114        throw new WebDriverException(String.format("'%s' property set, but unable to locate the requested binary: %s",
115                FirefoxDriver.SystemProperty.BROWSER_BINARY, binaryName));
116    }
117
118    /**
119     * Locates the firefox binary by platform.
120     */
121    @SuppressWarnings("deprecation")
122    private static File locateFirefoxBinaryFromPlatform() {
123        File binary = null;
124
125        Platform current = Platform.getCurrent();
126        if (current.is(WINDOWS)) {
127            binary = findExistingBinary(WindowsUtils.getPathsInProgramFiles("Mozilla Firefox\\firefox.exe"));
128
129        } else if (current.is(MAC)) {
130            binary = new File("/Applications/Firefox.app/Contents/MacOS/firefox-bin");
131            // fall back to homebrew install location if default is not found
132            if (!binary.exists()) {
133                binary = new File(System.getProperty("user.home") + binary.getAbsolutePath());
134            }
135        }
136
137        if (binary != null && binary.exists()) {
138            return binary;
139        }
140
141        ExecutableFinder binaryFinder = new ExecutableFinder();
142        if (current.is(UNIX)) {
143            String systemFirefox = binaryFinder.find("firefox-bin");
144            if (systemFirefox != null) {
145                return new File(systemFirefox);
146            }
147        }
148
149        String systemFirefox = binaryFinder.find("firefox");
150        if (systemFirefox != null) {
151            return new File(systemFirefox);
152        }
153
154        return null;
155    }
156
157    private static File findExistingBinary(final ImmutableList<String> paths) {
158        for (String path : paths) {
159            File file = new File(path);
160            if (file.exists()) {
161                return file;
162            }
163        }
164        return null;
165    }
166
167}
168
Full Screen
copy
1package com.upgrade.ui.driver;
2
3import com.google.common.base.Preconditions;
4import com.upgrade.ui.helpers.AbstractUITest;
5import org.openqa.selenium.Platform;
6import org.openqa.selenium.WebDriver;
7import org.openqa.selenium.os.ExecutableFinder;
8
9import java.io.File;
10import java.net.URL;
11
12public abstract class DriverManager {
13
14  protected WebDriver driver;
15
16  private ThreadLocal<WebDriver> driverThreads;
17
18  protected abstract void createDriver(AbstractUITest ab);
19
20
21  protected DriverManager() {
22    driverThreads = new ThreadLocal<>();
23  }
24
25  public void quitDriver() {
26    if (driverThreads.get() != null) {
27      driverThreads.get().quit();
28      driverThreads.set(null);
29    }
30  }
31
32  public WebDriver getDriver(AbstractUITest ab) {
33    if (driverThreads.get() == null) {
34      createDriver(ab);
35    }
36    driverThreads.set(driver);
37    return driverThreads.get();
38  }
39
40  protected File getExecutable(String executableName) {
41    File file;
42    Preconditions.checkNotNull(executableName);
43    if (Platform.getCurrent().is(Platform.WINDOWS)) {
44      file = new File(DriverManager.class.getResource("/" + executableName + ".exe").getFile());
45      if (canExecute(file)) {
46        return file;
47      }
48    } else if(Platform.getCurrent().is(Platform.MAC)) {
49
50      URL res = getClass().getClassLoader().getResource("/macDrivers/chromeDriver.exe");
51      file = new File(DriverManager.class.getResource(("/macDrivers/" + executableName)).getFile());
52      return file;
53    }
54    ExecutableFinder executableFinder = new ExecutableFinder();
55    return new File(executableFinder.find(executableName +".exe"));
56    }
57
58
59  private static boolean canExecute(File file) {
60    return file.exists() && !file.isDirectory();
61  }
62
63}
64
Full Screen
copy
1public static File checkAndGetFromPATHEnvVar(final String matchesExecutable) {
2    String[] pathParts = System.getenv("PATH").split(File.pathSeparator);
3    for (String pathPart : pathParts) {
4        File pathFile = new File(pathPart);
5
6        if (pathFile.isFile() && pathFile.getName().toLowerCase().contains(matchesExecutable)) {
7            return pathFile;
8        } else if (pathFile.isDirectory()) {
9            File[] matchedFiles = pathFile.listFiles(new FileFilter() {
10                @Override
11                public boolean accept(File pathname) {
12                    return FileUtil.getFileNameWithoutExtension(pathname).toLowerCase().equals(matchesExecutable);
13                }
14            });
15
16            if (matchedFiles != null) {
17                for (File matchedFile : matchedFiles) {
18                    if (FileUtil.canRunCmd(new String[]{matchedFile.getAbsolutePath()})) {
19                        return matchedFile;
20                    }
21                }
22            }
23        }
24    }
25    return null;
26}
27
Full Screen
copy
1public static String getFileNameWithoutExtension(File file) {
2        String fileName = file.getName();
3        int pos = fileName.lastIndexOf(".");
4        if (pos > 0) {
5            fileName = fileName.substring(0, pos);
6        }
7        return fileName;
8}
9
10public static boolean canRunCmd(String[] cmd) {
11        try {
12            ProcessBuilder pb = new ProcessBuilder(cmd);
13            pb.redirectErrorStream(true);
14            Process process = pb.start();
15            try (BufferedReader inStreamReader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
16                while ((inStreamReader.readLine()) != null) {
17                }
18            }
19            process.waitFor();
20        } catch (Exception e) {
21            return false;
22        }
23        return true;
24}
25
Full Screen
copy
1private static final String ENVIRONMENT_VARIABLES_TEXT = System.getenv("PATH");
2
3private static boolean isCommandAvailable(String executableFileName)
4{
5    String[] environmentVariables = ENVIRONMENT_VARIABLES_TEXT.split(File.pathSeparator);
6    for (String environmentVariable : environmentVariables)
7    {
8        try
9        {
10            Path environmentVariablePath = Paths.get(environmentVariable);
11            if (Files.exists(environmentVariablePath))
12            {
13                Path resolvedEnvironmentVariableFilePath = environmentVariablePath.resolve(executableFileName);
14                if (Files.isExecutable(resolvedEnvironmentVariableFilePath))
15                {
16                    return true;
17                }
18            }
19        } catch (InvalidPathException exception)
20        {
21            exception.printStackTrace();
22        }
23    }
24
25    return false;
26}
27
Full Screen
copy
1import java.io.BufferedReader;
2import java.io.IOException;
3import java.io.InputStreamReader;
4import java.nio.file.Path;
5import java.nio.file.Paths;
6import java.util.logging.Logger;
7
8
9public class SimulationUtils
10{
11    private final static Logger LOGGER = Logger.getLogger(SimulationUtils.class.getName());
12
13    public static Path lookForProgramInPath(String desiredProgram) {
14        ProcessBuilder pb = new ProcessBuilder(isWindows() ? "where" : "which", desiredProgram);
15        Path foundProgram = null;
16        try {
17            Process proc = pb.start();
18            int errCode = proc.waitFor();
19            if (errCode == 0) {
20                try (BufferedReader reader = new BufferedReader(new InputStreamReader(proc.getInputStream()))) {
21                    foundProgram = Paths.get(reader.readLine());
22                }
23                LOGGER.info(desiredProgram + " has been found at : " + foundProgram);
24            } else {
25                LOGGER.warning(desiredProgram + " not in PATH");
26            }
27        } catch (IOException | InterruptedException ex) {
28            LOGGER.warning("Something went wrong while searching for " + desiredProgram);
29        }
30        return foundProgram;
31    }
32
33    private static boolean isWindows() {
34        return System.getProperty("os.name").toLowerCase().contains("windows");
35    }
36}
37
Full Screen
copy
1    System.out.println(SimulationUtils.lookForProgramInPath("notepad"));
2
Full Screen
copy
1    System.out.println(SimulationUtils.lookForProgramInPath("psql"));
2
Full Screen
copy
1    SimulationUtils.lookForProgramInPath("gnuplot")
2
Full Screen
copy
1Runtime rt = Runtime.getRuntime();
2Process proc = rt.exec("svn help");
3int exitVal = proc.exitValue();
4
Full Screen
copy
1String exec = <executable name>;
2boolean existsInPath = Stream.of(System.getenv("PATH").split(Pattern.quote(File.pathSeparator)))
3        .map(Paths::get)
4        .anyMatch(path -> Files.exists(path.resolve(exec)));
5
Full Screen
copy
1import java.io.File;
2import java.nio.file.Paths;
3import java.util.stream.Stream;
4
5import static java.io.File.pathSeparator;
6import static java.nio.file.Files.isExecutable;
7import static java.lang.System.getenv;
8import static java.util.regex.Pattern.quote;
9
10public static boolean canExecute( final String exe ) {
11  final var paths = getenv( "PATH" ).split( quote( pathSeparator ) );
12  return Stream.of( paths ).map( Paths::get ).anyMatch(
13    path -> {
14      final var p = path.resolve( exe );
15      var found = false;
16
17      for( final var extension : EXTENSIONS ) {
18        if( isExecutable( Path.of( p.toString() + extension ) ) ) {
19          found = true;
20          break;
21        }
22      }
23
24      return found;
25    }
26  );
27}
28
Full Screen
copy
1 /**
2   *
3   * @param exeName Name of the executable file to look for in PATH
4   * @param exeProperty Name of a system property that specifies the path to the executable file
5   * @param exeDocs The link to the driver documentation page
6   * @param exeDownload The link to the driver download page
7   *
8   * @return The driver executable as a {@link File} object
9   * @throws IllegalStateException If the executable not found or cannot be executed
10   */
11  protected static File findExecutable(
12      String exeName,
13      String exeProperty,
14      String exeDocs,
15      String exeDownload) {
16    String defaultPath = new ExecutableFinder().find(exeName);
17    String exePath = System.getProperty(exeProperty, defaultPath);
18    checkState(exePath != null,
19        "The path to the driver executable must be set by the %s system property;"
20            + " for more information, see %s. "
21            + "The latest version can be downloaded from %s",
22            exeProperty, exeDocs, exeDownload);
23
24    File exe = new File(exePath);
25    checkExecutable(exe);
26    return exe;
27  }
28
Full Screen
copy
1  /**
2   * Ensures the truth of an expression involving the state of the calling instance, but not
3   * involving any parameters to the calling method.
4   *
5   * <p>See {@link #checkState(boolean, String, Object...)} for details.
6   */
7  public static void checkState(
8      boolean b,
9      @Nullable String errorMessageTemplate,
10      @Nullable Object p1,
11      @Nullable Object p2,
12      @Nullable Object p3) {
13    if (!b) {
14      throw new IllegalStateException(format(errorMessageTemplate, p1, p2, p3));
15    }
16  }
17
Full Screen
copy
1System.setProperty("webdriver.gecko.driver", "./libs/geckodriver.exe");
2WebDriver driver = new FirefoxDriver();
3
Full Screen
copy
1@Override
2 protected File findDefaultExecutable() {
3      return findExecutable(
4        "geckodriver", GECKO_DRIVER_EXE_PROPERTY,
5        "https://github.com/mozilla/geckodriver",
6        "https://github.com/mozilla/geckodriver/releases");
7    }
8
Full Screen
copy
1   @Override
2    protected File findDefaultExecutable() {
3      return findExecutable("chromedriver", CHROME_DRIVER_EXE_PROPERTY,
4          "https://github.com/SeleniumHQ/selenium/wiki/ChromeDriver",
5          "http://chromedriver.storage.googleapis.com/index.html");
6    }
7
Full Screen
copy
1System.setProperty("webdriver.gecko.driver","c:/your/path/to/geckodriver.exe");
2WebDriver driver = new FirefoxDriver();
3...
4
Full Screen
copy
1<match target="font">
2  <edit mode="assign" name="antialias">
3    <bool>false</bool>
4  </edit>
5</match>
6
Full Screen
copy
1public class MyPhantomDriverService {
2    public static PhantomJSDriverService createDefaultService(Capabilities desiredCapabilities, Map<String, String> env) {
3        Proxy proxy = null;
4        if (desiredCapabilities != null) {
5            proxy = Proxy.extractFrom(desiredCapabilities);
6        }
7
8        File phantomjsfile = findPhantomJS(desiredCapabilities, "https://github.com/ariya/phantomjs/wiki", "http://phantomjs.org/download.html");
9        File ghostDriverfile = findGhostDriver(desiredCapabilities, "https://github.com/detro/ghostdriver/blob/master/README.md", "https://github.com/detro/ghostdriver/downloads");
10        Builder builder = new Builder();
11        builder.usingPhantomJSExecutable(phantomjsfile)
12            .usingGhostDriver(ghostDriverfile)
13            .usingAnyFreePort()
14            .withProxy(proxy)
15            .withLogFile(new File("phantomjsdriver.log"))
16            .usingCommandLineArguments(findCLIArgumentsFromCaps(desiredCapabilities, "phantomjs.cli.args"))
17            .usingGhostDriverCommandLineArguments(findCLIArgumentsFromCaps(desiredCapabilities, "phantomjs.ghostdriver.cli.args"));
18        if(null != env)
19            builder.withEnvironment(env);
20        return builder.build();
21    }
22
23    public static File findPhantomJS(Capabilities desiredCapabilities, String docsLink, String downloadLink) {
24        String phantomjspath;
25        if (desiredCapabilities != null && desiredCapabilities.getCapability("phantomjs.binary.path") != null) {
26            phantomjspath = (String)desiredCapabilities.getCapability("phantomjs.binary.path");
27        } else {
28            phantomjspath = (new ExecutableFinder()).find("phantomjs");
29            phantomjspath = System.getProperty("phantomjs.binary.path", phantomjspath);
30        }
31
32        Preconditions.checkState(phantomjspath != null, "The path to the driver executable must be set by the %s capability/system property/PATH variable; for more information, see %s. The latest version can be downloaded from %s", "phantomjs.binary.path", docsLink, downloadLink);
33        File phantomjs = new File(phantomjspath);
34        checkExecutable(phantomjs);
35        return phantomjs;
36    }
37
38    protected static File findGhostDriver(Capabilities desiredCapabilities, String docsLink, String downloadLink) {
39        String ghostdriverpath;
40        if (desiredCapabilities != null && desiredCapabilities.getCapability("phantomjs.ghostdriver.path") != null) {
41            ghostdriverpath = (String)desiredCapabilities.getCapability("phantomjs.ghostdriver.path");
42        } else {
43            ghostdriverpath = System.getProperty("phantomjs.ghostdriver.path");
44        }
45
46        if (ghostdriverpath != null) {
47            File ghostdriver = new File(ghostdriverpath);
48            Preconditions.checkState(ghostdriver.exists(), "The GhostDriver does not exist: %s", ghostdriver.getAbsolutePath());
49            Preconditions.checkState(ghostdriver.isFile(), "The GhostDriver is a directory: %s", ghostdriver.getAbsolutePath());
50            Preconditions.checkState(ghostdriver.canRead(), "The GhostDriver is not a readable file: %s", ghostdriver.getAbsolutePath());
51            return ghostdriver;
52        } else {
53            return null;
54        }
55    }
56
57    protected static void checkExecutable(File exe) {
58        Preconditions.checkState(exe.exists(), "The driver executable does not exist: %s", exe.getAbsolutePath());
59        Preconditions.checkState(!exe.isDirectory(), "The driver executable is a directory: %s", exe.getAbsolutePath());
60        Preconditions.checkState(exe.canExecute(), "The driver is not executable: %s", exe.getAbsolutePath());
61    }
62
63    private static String[] findCLIArgumentsFromCaps(Capabilities desiredCapabilities, String capabilityName) {
64        if (desiredCapabilities != null) {
65            Object cap = desiredCapabilities.getCapability(capabilityName);
66            if (cap != null) {
67                if (cap instanceof String[]) {
68                    return (String[])((String[])cap);
69                }
70
71                if (cap instanceof Collection) {
72                    try {
73                        Collection<String> capCollection = (Collection<String>)cap;
74                        return (String[])capCollection.toArray(new String[capCollection.size()]);
75                    } catch (Exception var4) {
76                        System.err.println(String.format("Unable to set Capability '%s' as it was neither a String[] or a Collection<String>", capabilityName));
77                    }
78                }
79            }
80        }
81
82        return new String[0];
83    }
84}
85
Full Screen
copy
1        //-- 1. Make a temp directory which will contain our fonts.conf
2        String tmp = System.getProperty("java.io.tmpdir");
3        if(tmp == null) {
4            tmp = "/tmp";
5        }
6        File dir = new File(tmp + File.separator + "/_phantomjs-config/fontconfig");
7        dir.mkdirs();
8        if(! dir.exists()) {
9            throw new IOException("Can't create fontconfig directory to override phantomjs font settings at " + dir);
10        }
11
12        File conf = new File(dir, "fonts.conf");
13        String text = "<match target=\"font\">\n"
14            + "<edit mode=\"assign\" name=\"antialias\">\n"
15            + "<bool>false</bool>\n"
16            + "</edit>\n"
17            + "</match>";
18        try(FileOutputStream fos = new FileOutputStream(conf)) {
19            fos.write(text.getBytes("UTF-8"));
20        }
21
22        //-- Set the XDG_CONFIG_HOME envvar; this is used by fontconfig as one of its locations
23
24        Map<String, String> env = new HashMap<>();
25        env.put("XDG_CONFIG_HOME", dir.getParentFile().getAbsolutePath());
26
27        PhantomJSDriverService service = MyPhantomDriverService.createDefaultService(capabilities, env);
28        wd = new PhantomJSDriver(service, capabilities);
29
Full Screen
copy
1import org.openqa.selenium.chrome.ChromeDriver;
2import org.openqa.selenium.chrome.ChromeDriverService;
3import org.openqa.selenium.chrome.ChromeDriverService.Builder;
4
5...
6
7Map<String, String> env = new HashMap<>();
8env.put("XDG_CONFIG_HOME", dir.getParentFile().getAbsolutePath());
9
10Builder builder = new Builder();
11builder.usingAnyFreePort();
12builder.withEnvironment(env);
13ChromeDriverService service = builder.build();
14return new ChromeDriver(service, dc);
15
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

Most used methods in ExecutableFinder

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
Sarah

I hope you find the best code examples for your project.

If you want to accelerate automated browser testing, try LambdaTest. Your first 100 automation testing minutes are FREE.

Sarah Elson (Product & Growth Lead)