I have two instances of the ChromiumWebBrowser in my WinForms project (Visual Studio 2012). My goal is to have the second browser instance "copy" the behavior of the user input in the first browser instance. I can successfully retrieve the input from the first browser, and I managed to hook up Selenium in the project as well.
However, I'm having one issue. Whenever Selenium sends its commands, the first browser is the one that responds to them. For the life of me, I can't seem to figure out how to make the second browser respond. Whenever I completely remove the first browser, the second one starts responding correctly, but adding the first browser again will make only have the first browser use the Selenium commands. I even tried to switch out the moments the browsers are added to the form, but to no avail: whenever there are two available, the wrong one is responsive.
Relevant code:
public BrowserManager(Controller controller, string startingUrl)
{
_controller = controller;
var settings = new CefSettings { RemoteDebuggingPort = 9515 };
Cef.Initialize(settings);
// Input browser
inputBrowser = new ChromiumWebBrowser(startingUrl);
var obj = new XPathHelper(this);
inputBrowser.RegisterJsObject("bound", obj); //Standard object registration
inputBrowser.FrameLoadEnd += obj.OnFrameLoadEnd;
// Output browser
var browserSettings = new BrowserSettings();
var requestContextSettings = new RequestContextSettings { CachePath = "" };
var requestContext = new RequestContext(requestContextSettings);
outputBrowser = new ChromiumWebBrowser(startingUrl);
outputBrowser.RequestContext = requestContext;
outputBrowser.AddressChanged += InitializeOutputBrowser;
outputBrowser.Enabled = false;
outputBrowser.Name = "outputBrowser";
}
The selenium part:
public class SeleniumHelper
{
public SeleniumHelper()
{
DoWorkAsync();
}
private Task DoWorkAsync()
{
Task.Run(() =>
{
string chromeDriverDir = #"ActionRecorder\bin\x64\Debug\Drivers";
var chromeDriverService = ChromeDriverService.CreateDefaultService(chromeDriverDir);
chromeDriverService.HideCommandPromptWindow = true;
ChromeOptions options = new ChromeOptions();
options.BinaryLocation = #"ActionRecorder\bin\x64\Debug\ActionRecorder.exe";
options.DebuggerAddress = "127.0.0.1:9515";
options.AddArguments("--enable-logging");
using (IWebDriver driver = new OpenQA.Selenium.Chrome.ChromeDriver(chromeDriverService, options))
{
driver.Navigate().GoToUrl("http://www.google.com");
var query = driver.FindElement(By.Name("q"));
query.SendKeys("A google search test");
query.Submit();
}
});
return null;
}
}
And finally, a screenshot for some visualization:
Some help with the issue would be very much appreciated. If i missed some crucial info, feel free to ask for it. Thanks in advance!
Greetz,
Tybs
The behavior is correct. You have one debug address and you can only have one debug address for CEF. Which means when you use Selenium it is only seeing one browser.
By default Selenium will send an command to current active Tab or Window. Now in your case you have multiple Chrome view embedded, but they are technically Chrome Tab/Windows which you have placed on the same form.
So if you are in luck below code in should be able to move you to the Window you are interested in
driver.SwitchTo().Window(driver.WindowHandles.Last());
See if it works. If it doesn't then your only other workaround would be to change the order of Adding ChromiumWebBrowser and that should reverse the window it works on.
Below are some important threads that you should read from top to bottom. Very relevant to your issue/request
https://code.google.com/archive/p/chromiumembedded/issues/421
https://github.com/cefsharp/CefSharp/issues/1076
Related
I have implemented a health check with a ResponseWriter:
services.AddHealthChecks()
.AddCheck("My Health Check", new MyHealthCheck(aVariable));
app.UseHealthChecks("/health", new HealthCheckOptions()
{
ResponseWriter = WriteHealthCheckResponse
});
private static Task WriteHealthCheckResponse(HttpContext httpContext, HealthReport result){
httpContext.Response.ContentType = "application/json";
var json = new JObject(
new JProperty("status", result.Status.ToString()),
new JProperty("results", new JObject(result.Entries.Select(pair =>
new JProperty(pair.Key, new JObject(
new JProperty("status", pair.Value.Status.ToString()),
new JProperty("description", pair.Value.Description)))))));
return httpContext.Response.WriteAsync(
json.ToString(Formatting.Indented));}
I was expecting it to return a health.json file, however it returns just health. The browser doesn't recognize the file without an extension and doesn't want to open it, therefor I want to control the filename.
How can I control the file name of the response?
Update:
When the health check passes, I now do get a health.json file (which can be opened).
However, when the health check fails, I get a health file.
Trying to download the fail health message (health without .json extension), I only get a partial download which can be opened, but stays empty.
So, what's wrong with the non happy flow in this code:
public Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default(CancellationToken)){
var isHealthy = false;
try
{
var executionResult = _service.ExecuteExample();
isHealthy = executionResult != null;
}
catch
{
//This should not throw an exception.
}
HealthCheckResult healthResult = isHealthy
? HealthCheckResult.Healthy("The service is responding as expected.")
: HealthCheckResult.Unhealthy("There is a problem with the service.");
return Task.FromResult(healthResult);}
My code runs just fine on my co workers machine.
In the end it seems Internet Explorer 11 is the culprit. In Chrome it just works..
Update and solution:
Thanks to Martin Liversage I found the answer.
By using the F12 developer tools in IE, I found that the HTTP Status Code on unhealthy is 503 Service Unavailable. This prevents IE from downloading the .json result.
Now, this can easily be fixed by setting the HealthCheckOptions:
app.UseHealthChecks("/health", new HealthCheckOptions()
{
ResultStatusCodes = { [HealthStatus.Unhealthy] = 200 },
ResponseWriter = WriteHealthCheckResponse
});
Use this if you integrate health checks on basis of the contents of the .json file. Don't when you only look at HTTP Status.
I'm using Selenium to make an integration test. Part of the test is the logon process that Microsoft Azure Active Directory uses
The test passes when using InternetExplorerWebDriver, but fails with EdgeWebDriver with the error:
Element is obscured
The relevant part of the code:
var wait = new WebDriverWait(webDriver, TimeSpan.FromSeconds(10));
webDriver.Url = Settings.UrlHome();
var signInElement = webDriver.FindElement(By.Id("SignInLink"));
signInElement.Click();
wait.Until(ExpectedConditions.ElementToBeClickable(By.Id("my_login_name")));
var loginLogoMicrosoft = webDriver.FindElement(By.Id("my_login_name"));
loginLogoMicrosoft.Click();
The error occurs when executing the last Click(). I've tried several other elements, but none of them are working. Also 'inspect element' to determine which elements receives the click get this error when implementing in code.
How can I make the Microsoft Azure Active Directory logon process part of my Selenium test?
A same kind of issue is found here.
I use the following code for filling out the Azure AD login form. The caveat is that when you fill the username field the "Sign In" button is disabled until some ajax operation is done in the background. The trick is to wait until there's a Sign In button on the page without this class.
private void SubmitLoginForm()
{
var useAnotherAccount = Driver.FindElements(By.ClassName("use_another_account")).FirstOrDefault();
if (useAnotherAccount != null)
{
useAnotherAccount.Click();
}
var loginInput = Driver.FindElements(By.Id(Site.Login.UserNameInput)).FirstOrDefault();
if (loginInput != null)
{
loginInput.SendKeys(TestingData.UserName);
loginInput.SendKeys(Keys.Tab);
}
var passwordInput = Driver.FindElements(By.Id(Site.Login.PasswordInput)).FirstOrDefault();
if (passwordInput != null)
{
passwordInput.Clear();
passwordInput.SendKeys(TestingData.PassWord);
passwordInput.SendKeys(Keys.Enter);
}
var wait = new WebDriverWait(Driver, TimeSpan.FromSeconds(5));
wait.Until(f => f.FindElement(By.CssSelector("#cred_sign_in_button:not(.disabled_button")));
var loginButton = Driver.FindElements(By.Id(Site.Login.SigninButton)).FirstOrDefault();
if (loginButton != null)
{
loginButton.Click();
return;
}
throw new InvalidOperationException("Could not click the login button");
}
Following this article
http://www.codeproject.com/Articles/141842/Automate-your-UI-using-Microsoft-Automation-Framew
I'm trying to open an application and press a button. This is all that I want.
public RecordProgram()
{
ProcessStartInfo psi = new ProcessStartInfo(#"C:\MouseController.exe", #"C:\test1.mcd");
psi.UseShellExecute = false;
_calculatorProcess = Process.Start(psi);
int ct = 0;
do
{
_calculatorAutomationElement = AutomationElement.RootElement.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.NameProperty, "MouseController (1,0x)"));
++ct;
Thread.Sleep(100);
}
while (_calculatorAutomationElement == null && ct < 50);
if (_calculatorAutomationElement == null)
{
throw new InvalidOperationException("Calculator must be running");
}
_resultTextBoxAutomationElement = _calculatorAutomationElement.FindFirst(TreeScope.Element, new PropertyCondition(AutomationElement.AutomationIdProperty, "920388"));
if (_resultTextBoxAutomationElement == null)
{
throw new InvalidOperationException("Could not find result box");
}
GetInvokePattern(GetFunctionButton(Functions.Clear)).Invoke();
}
The prograns run and open the executable with my file load But _resultTextBoxAutomationElement returns null value.
_resultTextBoxAutomationElement = _calculatorAutomationElement.FindFirst(TreeScope.Element, new PropertyCondition(AutomationElement.AutomationIdProperty, "920388"));
Shouldn't the call to _calculatorAutomationElement.FindFirst() be passing in TreeScope.Children instead of TreeScope.Element? (Assuming the button element you're after is a direct child of the app window element.) By passing in TreeScope.Element as you are doing, UIA will only look at the _calculatorAutomationElement itself for an element with an AutomationId of 920388.
Thanks,
Guy
To illustrate my comment above which mentioned the Run dlg's Browse button as an example of accessing a Win32 button through its AutomationId, I've just written the code below to access the Browse button. The code is using a managed wrapper around the Windows UIA API, that I'd generated using the tlbimp.exe tool, but I expect taking a similar approach with the .NET UIA API would work fine too.
So for the MouseController UI shown above, try changing the line to...
_resultTextBoxAutomationElement = _calculatorAutomationElement.FindFirst(
TreeScope.Children, new PropertyCondition
(AutomationElement.AutomationIdProperty, "2296138"));
(I'm assuming that the Inspect SDK tool does show the AutomationId of the "start playback" button is "2296138".)
Thanks,
Guy
IUIAutomation uiAutomation = new CUIAutomation();
IUIAutomationElement rootElement = uiAutomation.GetRootElement();
int propertyIdName = 30005; // UIA_NamePropertyId
// First find the Run dlg, which is a direct child of the root element.
// For this test, assume there's only one element whose title is "Run"
// beneath the root. Note! This only works in English UI.
IUIAutomationCondition conditionName =
uiAutomation.CreatePropertyCondition(
propertyIdName, "Run");
IUIAutomationElement wndElement = rootElement.FindFirst(
TreeScope.TreeScope_Children, conditionName);
if (wndElement != null)
{
// Ok, we have the Run dialog. Now find the Browse button through its AutomationId.
int propertyAutomationId = 30011; // UIA_AutomationIdPropertyId
// Using the Inspect SDK tool, I could see that the AutomationId of
// the Browse button is "12288".
IUIAutomationCondition conditionAutomationId =
uiAutomation.CreatePropertyCondition(
propertyAutomationId, "12288");
// Get the name of the button cached when we find the button, so that
// we don't have to make a cross-process call later to get the name.
IUIAutomationCacheRequest cacheRequestName = uiAutomation.CreateCacheRequest();
cacheRequestName.AddProperty(propertyIdName);
IUIAutomationElement btnElement = wndElement.FindFirstBuildCache(
TreeScope.TreeScope_Children, conditionAutomationId, cacheRequestName);
if (btnElement != null)
{
// Let's see the name now...
MessageBox.Show(btnElement.CachedName);
}
}
I am testing a website. In the body of website has a URL. After clicking that URL it should be opened in a new tab, not in same window. After clicking the URL link it is opened in new tab browser. I have tested it manually.
I want to verify that feature by using TestComplete with C#Script. How to do it?
You can simulate a click to this link and then check whether the URL of the current page object has been changed and whether a new page object with the target URL has appeared.
function Test1()
{
var browser = Sys.Browser("firefox");
var numOfTabs = browser.FindAllChildren("ObjectType", "Page").toArray().length;
var page = browser.ToUrl("http://www.w3schools.com/html/tryit.asp?filename=tryhtml_links_target");
var pageUrl = page.Url;
frame = page.Panel(0).Panel(1).Panel(0).Panel(1).Frame("iframeResult");
frame.Link(0).Click();
if (page.Url != pageUrl)
Log.Error("The page's URL has been changed!");
if (browser.FindAllChildren("ObjectType", "Page").toArray().length == numOfTabs)
Log.Error("A new tab has not been opened!");
}
I use the Drag and drop code below to drag a picture in to a photofield. This works fine in InternetExplorer, but doesn't work in Firefox or Chrome.
I don't understand why not.
As you can see in the code below I've tried a lot of different ways to do the drag and drop, but none of them works.
The main problem is that the target is not updated after releasing the image.
I see the drop happen but no update.
Does anyone have any idea why this is? I'm using C# and the latest Selenium driver 2.39, chrome driver 2.8.
public static void DoDragAndDrop(IWebDriver driver, string dragImageId, string dropFieldId)
{
Console.WriteLine("Drag and drop image '{0}' to the editor {1}..", dragImageId, dropFieldId);
IWebElement dragElement = WebDriverExtensions.TryFindElement(By.Id(dragImageId));
IWebElement dropElement = WebDriverExtensions.TryFindElement(By.Id(dropFieldId));
if(dragElement == null)
Console.WriteLine("dragElement is null");
if(dropElement == null)
Console.WriteLine("dropElement is null");
((IJavaScriptExecutor)driver).ExecuteScript("arguments[0].scrollIntoView();", dragElement);
Thread.Sleep(500);
((IJavaScriptExecutor)driver).ExecuteScript("arguments[0].scrollIntoView();", dropElement);
Thread.Sleep(200);
Console.WriteLine("Drag and drop 1");
var builder1 = new Actions(driver);
builder1.MoveToElement(dragElement).ClickAndHold();
builder1.MoveToElement(dropElement).Build().Perform();
Thread.Sleep(2000);
Console.WriteLine("Drag and drop 2");
var builder2 = new Actions(driver);
builder2.DragAndDrop(dragElement, dropElement);
Thread.Sleep(2000);
Console.WriteLine("Drag and drop 3");
var builder3 = new Actions(driver);
builder3.DragAndDrop(dragElement, dropElement).Build().Perform();
IAction dragAndDrop = builder3.ClickAndHold(dragElement)
.MoveToElement(dropElement)
.Release(dropElement)
.Build();
dragAndDrop.Perform();
Thread.Sleep(2000);
Thread.Sleep(1000);
Console.WriteLine("Drag and drop succeeded..");
}
This is how I've got it working in FireFox now. Chrome still fails.
The only difference is that I've added offsets in the MoveToElement method, as seen in The Rookies comment.
var builder = new Actions(driver);
builder.ClickAndHold(dragElement);
builder.MoveToElement(dropElement, 5, 5);
builder.Perform();
Thread.Sleep(250);
builder.Release(dropElement);
builder.Perform();
Tried below sample code with chromedriver:2.15, chrome:v43 and is working fine as expected.
Sample Code:
System.setProperty("webdriver.chrome.driver","drivers/chromedriver.exe");
WebDriver driver = new ChromeDriver();
driver.manage().timeouts().implicitlyWait(1,TimeUnit.MINUTES);
driver.get("http://jqueryui.com/droppable");
driver.switchTo().frame(0);
WebElement dragElement = driver.findElement(By.id("draggable"));
WebElement dropElement = driver.findElement(By.id("droppable"));
Actions builder = new Actions(driver);
builder.clickAndHold(dragElement).moveToElement(dropElement).release().build().perform();
For firefox you can use following but it is in ruby
panel = driver.find_element(:id, ' (panel around the picture)')
target = panel.find_element(:xpath, ' ')
source = panel.find_element(:xpath, ' ')
driver.action.click_and_hold(source).move_to(target, 400, 150).release(target).perform
Hope it helps
ChromeDriver doesn't yet support the Actions commands. The Java
language binding translates the Actions requests into corresponding
mouse events before sending them to ChromeDriver, however there is no
guarantee the translated mouse events are completely equivalent to the
original Actions request.
Source: Chromium bugtacker