I'm trying to run 2 chrome browser instances with selenium webdriver library. However Even if these two browsers were initiated separately and are in separate threads, the code controls and manipulates only one browser.
The code for creating a chrome browser and perform the testing
static void test(string url)
{
ChromeDriver driver = BrowserWorker.GetChromeBrowser(false);
driver.Navigate().GoToUrl(url);
}
The "BrowserWorkser.GetChromeBrowser" function is to return a customized chrome browser instance:
public static ChromeDriver GetChromeBrowser()
{
var chromeoptions = new ChromeOptions();
chromeoptions.AddArguments("--disable-notifications");
chromeoptions.AddArguments("--no-sandbox");
chromeoptions.AddArguments("--disable-dev-shm-usage");
chromeoptions.UnhandledPromptBehavior = UnhandledPromptBehavior.Dismiss;
chromeoptions.AddArguments("--remote-debugging-port=9222");
var chromeDriverService = ChromeDriverService.CreateDefaultService(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location));
chromeDriverService.HideCommandPromptWindow = true;
ChromeDriver browser = null;
int nCount = 0;
while (browser == null && nCount < 3)
{
try
{
browser = new ChromeDriver(chromeDriverService, chromeoptions, TimeSpan.FromSeconds(180));
}
catch (Exception ex)
{
Console.WriteLine("Error initializing chrome browser: " + ex.Message);
}
nCount++;
}
return browser;
}
First method:
var t1 = new Thread(() => test("https://www.google.com/"));
var t2 = new Thread(() => test("https://www.bing.com/"));
t1.Start();
t2.Start();
Second method, changed the signature of "test(string url)" to "async Task" and added "await Task.Delay(1);", so the compiler does not complain:
var list = new List<Task>();
list.Add(test("https://www.google.com/"));
list.Add(test("https://www.bing.com/"));
Task.WaitAll(list.ToArray());
However neither method worked. The two separate threads/tasks controls the same browser instance, it loads google and bing, in a random order.
Please let me know if you need additional information. Thank you.
If you launch Chrome with different arguments, it launches a new instance. So in this case making --remote-debugging-port to be in different port will make the driver launch a new instance instead of using the existing instance.
Related
I apologize if I come across naïve, I am new to Selenium and still learning.
We are attempting to run these selenium tests on a designated machine via IIS.
When I run the code locally, everything works perfectly. When I publish it to our machine, two instances of chromedriver will show up in the taskmanager when it is only being run once. Sometimes it will close one of the instances, while other times it doesn't close either. Sometimes it will even close both instances and work just fine. I can not figure out why it's being so inconsistent.
Here is a screen shot of the task manager after starting the code.
Any suggestions on this is welcome, below is the code I am running.
private IWebDriver _driver;
[Route("/Test_SignIn")]
public IActionResult Test_SignIn(string environment = "development")
{
string response = "Failed";
try
{
string outPutDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
_driver = new ChromeDriver(outPutDirectory);
if (environment == "production")
{
_driver.Navigate().GoToUrl(productionUrl);
}
else
{
_driver.Navigate().GoToUrl(developmentUrl);
}
By userNameFieldLocator = By.Id("AccountUserName");
WebDriverWait wait = new WebDriverWait(_driver, TimeSpan.FromSeconds(30));
wait.Until(ExpectedConditions.ElementIsVisible(userNameFieldLocator));
_waitForLoaderToFinish();
IWebElement userNameField = _driver.FindElement(userNameFieldLocator);
IWebElement passwordField = _driver.FindElement(By.Id("AccountPassword"));
IWebElement signInButton = _driver.FindElement(By.XPath("//button[contains(text(), 'Sign In')]"));
userNameField.SendKeys(_username);
passwordField.SendKeys(_password);
signInButton.Click();
By dashboardLocator = By.Id("portalBanner_EmployeeName");
wait.Until(ExpectedConditions.ElementIsVisible(dashboardLocator));
IWebElement dashboard = _driver.FindElement(dashboardLocator);
response = "Success";
}
catch (Exception ex)
{
response = ex.Message;
}
_driver.Close();
_driver.Quit();
return Json(response);
}
private string _waitForLoaderToFinish()
{
try
{
new WebDriverWait(_driver, TimeSpan.FromSeconds(30)).Until(ExpectedConditions.InvisibilityOfElementLocated(By.Id("loader-wrapper")));
return null;
}
catch (TimeoutException e)
{
return $"{e.Message}";
}
}
ChromeDriver (and all other web drivers) are "disposable" objects in .NET. They implement the IDisposable interface. Objects that implement this interface require special handling in order to clean up after they are finished. Typically you see these objects initialized in a using(...) block, similar to below:
using (IWebDriver driver = new ChromeDriver(...))
{
// Use driver
}
Reference: C# Using Statement.
I see that you are closing and quitting the browser, but you need to call _driver.Dispose(); as well. The quick and easy way to accomplish this in your code is to call Dispose() after calling Quit():
_driver.Close();
_driver.Quit();
_driver.Dispose(); // <-- this kills Chrome.exe
return Json(response);
Or modify the method to wrap the use of _driver in a using(...) statement:
[Route("/Test_SignIn")]
public IActionResult Test_SignIn(string environment = "development")
{
string response = "Failed";
try
{
string outPutDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
// Your web driver is a LOCAL variable now, beware!
using (IWebDriver driver = new ChromeDriver(outPutDirectory))
{
if (environment == "production")
{
driver.Navigate().GoToUrl(productionUrl);
}
else
{
driver.Navigate().GoToUrl(developmentUrl);
}
By userNameFieldLocator = By.Id("AccountUserName");
WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(30));
wait.Until(ExpectedConditions.ElementIsVisible(userNameFieldLocator));
_waitForLoaderToFinish();
IWebElement userNameField = driver.FindElement(userNameFieldLocator);
IWebElement passwordField = driver.FindElement(By.Id("AccountPassword"));
IWebElement signInButton = driver.FindElement(By.XPath("//button[contains(text(), 'Sign In')]"));
userNameField.SendKeys(_username);
passwordField.SendKeys(_password);
signInButton.Click();
By dashboardLocator = By.Id("portalBanner_EmployeeName");
wait.Until(ExpectedConditions.ElementIsVisible(dashboardLocator));
IWebElement dashboard = driver.FindElement(dashboardLocator);
response = "Success";
}
}
catch (Exception ex)
{
response = ex.Message;
}
return Json(response);
}
Please note that your web driver becomes a local variable now. It should not be a field on the class in order to prevent other methods from accessing this disposable resource.
I have automated regression tests that run every morning. Currently, it launches dozens of threads simultaneously, each running its own webdriver in each thread.
ChromeOptions option = new ChromeOptions();
option.AddArgument("--headless");
IWebDriver driver = new ChromeDriver(option);
try
{
SuiteDriver(driver, suiteTable);
LogMonitor.UEErrorHandling();
}
catch (Exception ex)
{
WritetoLogFile("Exception in Main - " + ex);
}
finally
{
workbook.Dispose();
driver.Quit();
}
When the tests complete there are a bunch of webdriver instances still running. When I attempt to clean these up at the end of the test run using driver.Quit() it closes more than just the driver in its own thread, causing the other tests to fail to complete. Driver.Quit() doesn't seem to differentiate between the driver launched by this one instance and other drivers launched by other instances of the test.
Is there a way to ensure driver.Quit() or driver.Close() only closes the instance of webdriver launched by that specific executable running in that thread only?
Below is an example of thread-safe execution in C# using System.Threading.ThreadLocal
ThreadLocal<IWebDriver> DriversThread = new ThreadLocal<IWebDriver>();
IWebDriver Driver
{
set => DriversThread.Value = value;
get => DriversThread.Value;
}
string directoryPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); // Pass this directory path to chromedriver when you are installing chromedriver through Nuget packages.
ChromeOptions chromeOpt = new ChromeOptions();
chromeOpt.AddArguments("--headless");
Driver = new ChromeDriver(directoryPath, chromeOpt);
Driver.Url = "https://google.co.in";
Driver.Quit();
This will set thread specific drivers, each thread will have its own driver no matter how many threads are being executed.
Try and give each webdriver it's own profile, and if needed download path.
private ChromeOptions GetChromeOptions()
{
var options = new ChromeOptions();
ProfilePath = Path.Combine(AppContext.BaseDirectory, "tmp", Guid.NewGuid().ToString());
DownloadPath = Path.Combine(ProfilePath, "Downloads");
if (!Directory.Exists(DownloadPath))
{
Directory.CreateDirectory(DownloadPath);
}
options.AddUserProfilePreference("download.default_directory", DownloadPath);
//--lang=en-US,en headless does not define a language by default
options.AddArguments("--incognito", "--lang=en-US,en", $#"--user-data-dir={ProfilePath}");
return options;
}
I'm using xUnit to spawn dozens of headless chromes tests and I've not ever seen the dispose close all of the instances. The only difference I can think of is that I am spawning each with their own profile.
I would suggest using the setup and teardown of your testing framework onto a base class to handle the setup and teardown for all tests.
public class PageLoad: IDisposable
{
private IWebDriver _driver;
public void PageLoad()
{
_driver = new ChromeDriver();
}
[Fact]
public void PageLoadTest()
{
_driver.Navigate().GoToUrl("http://google.com");
Assert.True(_driver.FindElement(By.XPath("//*[#id='hplogo']")).Displayed);
}
public void Dispose()
{
_driver.Dispose();
}
}
You can also wrap the driver in a using statement
using (var driver = new ChromeDriver())
{
driver.Navigate().GoToUrl("http://google.com");
Assert.True(_driver.FindElement(By.XPath("//*[#id='hplogo']")).Displayed);
}
I am trying to automate the Internet Explorer in c# using selenium-webdriver to fill a formular on an external website.
Sometimes the code throws randomly errors (Unable to find element with name == xxx), because it cannot find the searched elements. It's not happening every time and not necessarily in the same places.
I've already tried setting an implicitWait with the following code which reduced the number of errors.
driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(10);
The external webpage changes the selection options (through reloading) from Dropdowns after selecting an option from another Dropdown.
To additionally intercept these critical points, I wait 2 seconds before trying to find the Dropdown-options ByName().
System.Threading.Thread.Sleep(2000);
The webpage takes less than half a second to reload this Dropdowns, so that 2 seconds should be enough.
Can you tell me what i'm doing wrong or why the webdriver seems to run so instably finding elements.
I have also noticed that I am not allowed to do anything else on the computer as long as the program is running, otherwise the same error occurs more often.
My driver-options for internet explorer 8
var options = new InternetExplorerOptions()
{
InitialBrowserUrl = "ExternalPage",
IntroduceInstabilityByIgnoringProtectedModeSettings = true,
IgnoreZoomLevel = true,
EnableNativeEvents = false,
RequireWindowFocus = false
};
IWebDriver driver = new InternetExplorerDriver(options);
driver.Manage().Window.Maximize();
My final solution working perfectly without a single error in more than 20 tests!
Added the following in the class
enum type
{
Name,
Id,
XPath
};
Added this behind my driver
driver.Manage().Window.Maximize();
wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
Methode to wait for an element
private static void waitForElement(type typeVar, String name)
{
if( type.Id)wait.Until(ExpectedConditions.ElementToBeClickable(driver.FindElement(By.Id(name))));
else if(type.Name)wait.Until(ExpectedConditions.ElementToBeClickable(driver.FindElement(By.Name(name))));
else if(type.XPath)wait.Until(ExpectedConditions.ElementToBeClickable(driver.FindElement(By.XPath(name))));
}
And call the methode before calling any event with the element
waitForElement(type.Id, "ifOfElement");
You can use Explicit wait like this example:
var wait = new WebDriverWait(Driver.Instance, TimeSpan.FromSeconds(10));
wait.until(ExpectedConditions.invisibilityOfElementLocated(By.id("your locator")));
You have two more options in Selenium:
You can use explicit wait via the WebDriverWait object in Selenium. In this way you can wait for elements to appear on the page. When they appear the code continues.
An example:
IWebDriver driver = new FirefoxDriver();
driver.Navigate().GoToUrl("http://yourUrl.com");
WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromMinutes(1));
Func<IWebDriver, bool> waitForElement = new Func<IWebDriver, bool>((IWebDriver Web) =>
{Console.WriteLine(Web.FindElement(By.Id("target")).GetAttribute("innerHTML"));
});
wait.Until(waitForElement);
Furthermore you can use the FluentWait option. In this way you can define the maximum amount of time to wait for a condition, as well as the frequency with which to check the condition.
Wait<WebDriver> wait = new FluentWait<WebDriver>(driver)
.withTimeout(30, SECONDS)
.pollingEvery(5, SECONDS)
.ignoring(NoSuchElementException.class);
WebElement foo = wait.until(new Function<WebDriver, WebElement>()
{
public WebElement apply(WebDriver driver) {
return driver.findElement(By.id("foo"));
}
});
To explain the title.. Selenium RC keeps insisting that
A system shutdown has already been scheduled
and refusing to conduct automated tests because of this.. I can understand the logic here, I mean you're not going to do your homework if you thought the world would end with a nuclear holocaust...
However.. this is not the Cold War and when I inspect several basic things (such as using shutdown \a, completing a full restart) I find that this is actually not the case!
How can I convince selenium that the world is not going to end and that it should probably do the work I'm telling it to?
N.B. Here, selenium has "refused" to initialise any instance of IE, and will continue to hang until it times out, regardless of clicking Yes or No. I'm using NUnit, c#/.net4.0 to control the tests.
To fix this I replaced the default "runSeleniumTest" function with the below patched version as a user extension:
function runSeleniumTest() {
runOptions = new RemoteRunnerOptions();
var testAppWindow;
if (runOptions.isMultiWindowMode()) {
try{
testAppWindow = openSeparateApplicationWindow('Blank.html', true);
}
catch (e) {
window.onunload = function () { };
window.location.reload();
return;
}
} else if (sel$('selenium_myiframe') != null) {
var myiframe = sel$('selenium_myiframe');
if (myiframe) {
testAppWindow = myiframe.contentWindow;
}
}
else {
proxyInjectionMode = true;
testAppWindow = window;
}
selenium = Selenium.createForWindow(testAppWindow, proxyInjectionMode);
if (runOptions.getBaseUrl()) {
selenium.browserbot.baseUrl = runOptions.getBaseUrl();
}
if (!debugMode) {
debugMode = runOptions.isDebugMode();
}
if (proxyInjectionMode) {
LOG.logHook = logToRc;
selenium.browserbot._modifyWindow(testAppWindow);
}
else if (debugMode) {
LOG.logHook = logToRc;
}
window.selenium = selenium;
commandFactory = new CommandHandlerFactory();
commandFactory.registerAll(selenium);
currentTest = new RemoteRunner(commandFactory);
var doContinue = runOptions.getContinue();
if (doContinue != null) postResult = "OK";
currentTest.start();
}
I found that the "a system shutdown has already been scheduled" error occurred inside of "openSeparateApplicationWindow". I also found that refreshing the selenium test runner window after the error occurred would "restart" the test without the error. Therefore, I patched the "runSeleniumTest" with the following try catch statement so the test runner window reloads if there's an error in "openSeparateApplicationWindow":
try{
testAppWindow = openSeparateApplicationWindow('Blank.html', true);
}
catch (e) {
window.onunload = function () { };
window.location.reload();
return;
}
I also used my blog post for a more specific example of selenium user extensions
Selenium isn't doing anything in this case. That's the IE HTA agent (a built-in Windows process) that's preventing you from doing anything. Perhaps rebooting the machine will do the trick? It looks like you may just have a pending Windows update that's scheduled a future reboot.
I am trying test a single page web application with webdriver in page object model using C#. The website javascript intensive and does Lazy loading
i have tried using Explicit wait and used the following code to check if the javascript is active
return jQuery.active == 0
I have tried using a combination of both to know if the page has loaded
WebDriverWait _wait = new WebDriverWait(Driver, TimeSpan.FromSeconds(25));
bool WhenToExit = true;
while (WhenToExit) // Handle timeout somewhere
{
var ajaxIsComplete = (bool)(Browser.Driver as IJavaScriptExecutor).ExecuteScript("return jQuery.active == 0");
if (ajaxIsComplete)
{
bool isDisplayed = _wait.Until(d => d.FindElement(By.ClassName("example"))).Displayed;
if(isDisplayed)
{
break;
}
}
Thread.Sleep(100);
int Timer = 0;
Timer++;
if (Timer == 100)
{
WhenToExit = false;
}
}
.But still the webdriver does not wait for the page to load , it just keeps executing the steps ,So i have been forced to use Thread.Sleep(), which i dont want use and its not good practices.
Can some tell me how to Get around this issue,Thank u in advance