I'm trying to click a button on an external windows application. The following code successfully finds the element, brings the parent window into focus and then "manually" clicks the button
This works okay...
Process tProcess = Process.GetProcesses().FirstOrDefault(x => x.MainWindowTitle.StartsWith("MainWindowName"));
if (tProcess != null)
{
TestStack.White.Application application = TestStack.White.Application.Attach(tProcess.Id);
var tWindow = application.GetWindow(SearchCriteria.ByAutomationId("SubWindowName"), InitializeOption.NoCache);
SearchCriteria searchCriteria = SearchCriteria.ByAutomationId("btnCalibrate");
var calibrateBtn = tWindow.Get<TestStack.White.UIItems.Button>(searchCriteria);
tWindow.Focus();
var clickablePoint = calibrateBtn.AutomationElement.GetClickablePoint();
Mouse.Instance.Click(clickablePoint);
}
The problem with this is that Mouse.Instance.Click(clickablePoint); moves the cursor, ideally I don't want the cursor moved.
My initial code tried to click the button directly using the following
Process tProcess = Process.GetProcesses().FirstOrDefault(x => x.MainWindowTitle.StartsWith("MainWindowName"));
if (tProcess != null)
{
TestStack.White.Application application = TestStack.White.Application.Attach(tProcess.Id);
var tWindow = application.GetWindow(SearchCriteria.ByAutomationId("SubWindowName"), InitializeOption.NoCache);
SearchCriteria searchCriteria = SearchCriteria.ByAutomationId("btnCalibrate");
var calibrateBtn = tWindow.Get<TestStack.White.UIItems.Button>(searchCriteria);
tWindow.Focus();
calibrateBtn.Click();
}
but this gives the following error every time
TestStack.White.AutomationException
HResult=0x80131500
Message=Cannot perform action on Button. AutomationId:btnCalibrate, Name:Calibrate, ControlType:button, FrameworkId:WinForm,
Source=TestStack.White
StackTrace:
at TestStack.White.UIItems.UIItem.PerformIfValid(Action action) in c:\TeamCity\buildAgent\work\89a20b30302799e\src\TestStack.White\UIItems\UIItem.cs:line 254
at TestStack.White.UIItems.UIItem.Click() in c:\TeamCity\buildAgent\work\89a20b30302799e\src\TestStack.White\UIItems\UIItem.cs:line 231
at BetfairStreamingAPI.RadForm1.radLabelBetTime_Click(Object sender, EventArgs e) in D:
Does anyone know why the second method is throwing this error and if it's possible to fix this so that the button can be clicked without manually moving the cursor?
Edit: Screenshot of attempt to set togglestate
The solution to this particular problem appears to be use .RaiseClickEvent() instead of .Click()
The following code works
Process tProcess = Process.GetProcesses().FirstOrDefault(x => x.MainWindowTitle.StartsWith("MainWindowName"));
if (tProcess != null)
{
TestStack.White.Application application = TestStack.White.Application.Attach(tProcess.Id);
var tWindow = application.GetWindow(SearchCriteria.ByAutomationId("SubWindowName"), InitializeOption.NoCache);
SearchCriteria searchCriteria = SearchCriteria.ByAutomationId("btnCalibrate");
var calibrateBtn = tWindow.Get<TestStack.White.UIItems.Button>(searchCriteria);
calibrateBtn.RaiseClickEvent();
}
It's not entirely clear from the White docs when/why this is preferred. I found method RaiseClickEvent this on this link https://github.com/TestStack/White/commit/7b6d4dbc0008c3375e2ebf8810c55cb1abf91b60
EDIT2
I think you might have found something interesting. Since your button state is Indeterminate, it could be worth turning it on before clicking it:
calibrateBtn.State = ToggleState.On;
EDIT1
Alright, let's sort this out together.
There are only two reasons for that action to fail:
The button is not enabled, which I guess can't be the case
The button is OffScreen
If you do something like
Console.WriteLine(calibrateBtn.IsOffScreen.ToString());
You should see
true
If so, try this before you click it:
var pattern = calibrateBtn.AutomationElement.GetCurrentPattern(System.Windows.Automation.InvokePattern.Pattern);
(pattern as System.Windows.Automation.InvokePattern).Invoke();
Related
I am using MVVM for my WPF form and now I want to close the new Dialog that I made when a user presses the Cancel button.
The cancel button is part of a seperate XAML that gets used around multiple forms. (similar to scripts in javascript)
this is what I have so far:
private void CloseDialogView(object sender)
{
var currentElement = (DependencyObject)sender;
List<object> windowTypes = new List<object>() {
typeof(fooDialogView),
typeof(barDialogView),
typeof(foobarDialogView) };
Type elementType;
do
{
var parent = VisualTreeHelper.GetParent(currentElement);
currentElement = parent;
elementType = (currentElement.GetType());
}
while (!windowTypes.Contains(elementType));
foreach (var type in windowTypes)
try
{
var Window = (type.GetType())currentElement;
Window.Close();
}
catch
{ }
}
in the do-while I just pass through all the elements in the form untill I hit the element that is the window.
In the foreach I want to check if it is one of the windowtypes (Xaml-forms) and if it is, cast that type to the 'currentElement' and then close it.
It does work if I just do
var Window = (fooDialogView)currentElement;
Window.Close();
but I want to avoid having to manually enter each seperate form-name.
Someone posted the answer
var Window = (Window)sender;
Window.Close();
but it turned out all I needed was
var Window = (Window)currentElement;
Window.Close();
right after the do-while, no need for the foreach-loop.
thanks for the swift help :)
I tried to use white to click the new project button, I try using the below code, but it can not work, anyone can help me?
public void Notepad()
{
Application app = Application.Launch("C:\\Program Files (x86)\\SourceMonitor\\SourceMonitor.exe");
Window window = app.GetWindow("SourceMonitor", InitializeOption.NoCache);
Button button = window.Get<Button>("File");
button.Click();
app.Kill();
}
I already figured out how to do it, below is the correct code:
public void Notepad()
{
// Arrange
Application app = Application.Launch("C:\\Program Files (x86)\\SourceMonitor\\SourceMonitor.exe");
Window window = app.GetWindow("SourceMonitor", InitializeOption.NoCache);
// Act
MenuBar menuBar = window.MenuBar;
menuBar.MenuItem("File","New Project").Click(); ; //can use any other search criteria as well.
}
I trying to create simple test that runs my application and clicks some button using CodedUI.
First I trying to find the main window, that has ControlName - "LoginForm". This is my code:
[TestMethod]
public void CodedUITestMethod()
{
var app = ApplicationUnderTest.Launch(#"C:\Program Files (x86)\application.exe");
var loginWindow = new WinClient(app);
loginWindow.SearchProperties.Add(loginWindow.ControlName, "LoginForm");
When I run it in debugger, the loginWindow object contains an error:
The property SplashForm is not a valid search property.
What could be the problem?
Redo your SearchProperties line like this:
loginWindow.SearchProperties[WinClient.PropertyNames.ControlName] = "LoginForm";
Trying to write some test cases using selenium webdriver in c# and have a scenario which i'm unsure of how to resolve
user scenario is searching a table for a patient, select a patient then a new window opens and then assert various items on the window
my issue is i'm unable to select the new window to assert anything from, it's not a pop-up window, it's a full new browser window but it has no window title/name to identify it by, how would I be able to switch driver focus to this window?
thanks in advance
It is really easy in Selenium WebDriver. By using SwitchTo method
driver.SwitchTo().Window(driver.WindowHandles.Last());
See this blog post as well
http://binaryclips.com/2015/03/13/selenium-webdriver-in-c-switch-to-new-window/
This code worked for me. In my case the new window/tab is a PDF that have some weight, so I make some custom waits while it loads.
WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(5));
int previousWinCount = driver.WindowHandles.Count;
// Perform the action to open a new Window
wait.Until(driver => driver.WindowHandles.Count == (previousWinCount + 1));
driver.SwitchTo().Window(driver.WindowHandles.Last());
wait.Until(driver => driver.Url.Contains("desired_url_or_a_substring_of_it"));
Note that the driver.Url when the PDF is loading is "about:blank".
IWebDriver _driver = new FirefoxDriver();
_driver.Navigate().GoToUrl("https://www.google.com");
ReadOnlyCollection<string> WindowHandles = _driver.WindowHandles;
foreach (string item in WindowHandles)
{
_driver.SwitchTo().Window(item);
string browserTitle = _driver.Title;
string browserPageSource = _driver.PageSource;
string browserURL = _driver.Url;
}
Use ReadOnlyCollection and handle browser, get the title of your window and compare and get focus on your desire browser window.
If I gather correctly your application will produce the window on it's own without further userintervention. You should be able to wait for the page to load and then you can call your asserts as normal.
Selenium already has your browser-session, so a new window is not an issue for selenium, it is just new content.
foreach (string defwindow in driver.WindowHandles)
{
driver.SwitchTo().Window(defwindow);
if(driver.Title == "")
{
selenium.WindowFocus();
selenium.SelectWindow("");
}
}
"" - indicates your window Title
I've got some code you might like. The quickest solution is to use Popup Finder, but I've made my own method as well. I would never rely on the order the Window Handles are in to select the appropriate window.
Popup Window Finder:
PopupWindowFinder finder = new PopupWindowFinder(driver);
driver.SwitchTo().Window(newWin);
My Custom method. Basically you pass it the element you want to click, your webdriver, and optionally the time to wait before searching after you click the element.
It takes all of your current handles and makes a list. It uses that list to eliminate the previously existing windows from accidentally getting switched to. Then it clicks the element that launches the new window. There should always be some sort of a delay after the click, as nothing happens instantly. And then it makes a new list and compares that against the old one until it finds a new window or the loop expires. If it fails to find a new window it returns null, so if you have an iffy webelement that doesn't always work, you can do a null check to see if the switch worked.
public static string ClickAndSwitchWindow(IWebElement elementToBeClicked,
IWebDriver driver, int timer = 2000)
{
System.Collections.Generic.List<string> previousHandles = new
System.Collections.Generic.List<string>();
System.Collections.Generic.List<string> currentHandles = new
System.Collections.Generic.List<string>();
previousHandles.AddRange(driver.WindowHandles);
elementToBeClicked.Click();
Thread.Sleep(timer);
for (int i = 0; i < 20; i++)
{
currentHandles.Clear();
currentHandles.AddRange(driver.WindowHandles);
foreach (string s in previousHandles)
{
currentHandles.RemoveAll(p => p == s);
}
if (currentHandles.Count == 1)
{
driver.SwitchTo().Window(currentHandles[0]);
Thread.Sleep(100);
return currentHandles[0];
}
else
{
Thread.Sleep(500);
}
}
return null;
}
I am creating a tool on an application that opens some windows forms to get information from users, my tool should deal with these windows forms by itself without users' interactions.
I have initiated an event to get the opened form's process when it opens by the following code:
mgmtWtch = new ManagementEventWatcher("Select * From Win32_ProcessStartTrace");
mgmtWtch.EventArrived += WatchManagementEvent;
mgmtWtch.Start();
The shown window has OK button which i want to click, and i don't know how to make this action. while the parameter that i can get from this event is
EventArrivedEventArgs e
my question is how can i click the OK button through this event handler?
thanks in advance.
Have you looked at .Net's GUI automation API?
You'll want the UIAutomationClient and UIAutomationTypes assemblies.
I've used this API to drive installers, UIs during testing etc.
I found this link useful initially.
http://blogs.msdn.com/b/oldnewthing/archive/2013/04/08/10409196.aspx
e.g. assuming you have the parent window (i.e. the Form) for the button and you know the button's ID:
using System.Windows.Automation;
....
static AutomationElement FindById(AutomationElement root, string id, bool directChild)
{
Assert(root != null, "Invalid input: ParentWindow element 'root' is null.");
Condition conditions = new PropertyCondition(AutomationElement.AutomationIdProperty, id);
return root.FindFirst(directChild ? TreeScope.Children : TreeScope.Descendants, conditions);
}
....
AutomationElement button = FindById(containerWindow, id.ToString(), true);
InvokePattern invokePattern = null;
try
{
invokePattern = button.GetCurrentPattern(InvokePattern.Pattern) as InvokePattern;
}
catch (InvalidOperationException)
{
MessageBox.Show("The UI element named " + button.GetCurrentPropertyValue(AutomationElement.NameProperty) + " is not a button");
return false;
}
invokePattern.Invoke();
If you don't know the button's ID but do know it's name i.e. the text on the button then replace AutomationElement.AutomationIdProperty with AutomationElement.NameProperty in FindById (and rename the method appropriately)
Assuming the button is in the top-level Form window and you know the title displayed in this Form window, the following code will get the button's parent window:
bool ignoreCase = true; // or false if preferred
Condition conditions = new PropertyCondition(
AutomationElement.NameProperty,
windowTitle,
ignoreCase ? PropertyConditionFlags.IgnoreCase : PropertyConditionFlags.None
);
AutomationElement myForm =
AutomationElement.RootElement.FindFirst(
TreeScope.Children,
conditions );
The Window Title can be retrieved from the process you already have via the process' MainWindowTitle property.