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;
}
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'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();
Thanks in advance.
Is it possible to activate a tab in another program using an IntPtr? If so, how?
SendKeys is not an option.
Perhaps what I need is a fishing lesson. I have exhausted Google and my lead developer.
I would appreciate an outright solution OR a recommendation to continue my Google efforts.
basic process is:
I drag a shortcut icon to the launcher
This opens the target application (Notepad++) and grabs IntPtr, etc.
I would like to programmatically select various items in Notepad++ such as Edit, menu items under Edit, or a doc tab.
The basic code I am running is:
the 'blob'
item 1: IntPtr of item
item 2: IntPtr of itemsChild
item 3: control text of item 1
item 4: is rectangle parameters of item 1
root contains similar info:
As others pointed out, the standard way of doing this is to use UI Automation. Notepad++ does support UI Automation (to some extent, as it's somehow automatically provided by the UI Automation Windows layers).
Here is a sample C# console app that demonstrates the following sceanrio (you need to reference UIAutomationClient.dll, UIAutomationProvider.dll and UIAutomationTypes.dll):
1) get the first running notepad++ process (you must start at least one)
2) open two files (note there may be already other opened tabs in notepad++)
3) selects all tabs in an infinite loop
class Program
{
static void Main(string[] args)
{
// this presumes notepad++ has been started somehow
Process process = Process.GetProcessesByName("notepad++").FirstOrDefault();
if (process == null)
{
Console.WriteLine("Cannot find any notepad++ process.");
return;
}
AutomateNpp(process.MainWindowHandle);
}
static void AutomateNpp(IntPtr handle)
{
// get main window handle
AutomationElement window = AutomationElement.FromHandle(handle);
// display the title
Console.WriteLine("Title: " + window.Current.Name);
// open two arbitrary files (change this!)
OpenFile(window, #"d:\my path\file1.txt");
OpenFile(window, #"d:\my path\file2.txt");
// selects all tabs in sequence for demo purposes
// note the user can interact with n++ (for example close tabs) while all this is working
while (true)
{
var tabs = GetTabsNames(window);
if (tabs.Count == 0)
{
Console.WriteLine("notepad++ process seems to have gone.");
return;
}
for (int i = 0; i < tabs.Count; i++)
{
Console.WriteLine("Selecting tab:" + tabs[i]);
SelectTab(window, tabs[i]);
Thread.Sleep(1000);
}
}
}
static IList<string> GetTabsNames(AutomationElement window)
{
List<string> list = new List<string>();
// get tab bar
var tab = window.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Tab));
if (tab != null)
{
foreach (var item in tab.FindAll(TreeScope.Children, PropertyCondition.TrueCondition).OfType<AutomationElement>())
{
list.Add(item.Current.Name);
}
}
return list;
}
static void SelectTab(AutomationElement window, string name)
{
// get tab bar
var tab = window.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Tab));
// get tab
var item = tab.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.NameProperty, name));
if (item == null)
{
Console.WriteLine("Tab item '" + name + "' has been closed.");
return;
}
// select it
((SelectionItemPattern)item.GetCurrentPattern(SelectionItemPattern.Pattern)).Select();
}
static void OpenFile(AutomationElement window, string filePath)
{
// get menu bar
var menu = window.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.MenuBar));
// get the "file" menu
var fileMenu = menu.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.NameProperty, "File"));
// open it
SafeExpand(fileMenu);
// get the new File menu that appears (this is quite specific to n++)
var subFileMenu = fileMenu.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Menu));
// get the "open" menu
var openMenu = subFileMenu.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.NameProperty, "Open..."));
// click it
((InvokePattern)openMenu.GetCurrentPattern(InvokePattern.Pattern)).Invoke();
// get the new Open dialog (from root)
var openDialog = WaitForDialog(window);
// get the combobox
var cb = openDialog.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.ComboBox));
// fill the filename
((ValuePattern)cb.GetCurrentPattern(ValuePattern.Pattern)).SetValue(filePath);
// get the open button
var openButton = openDialog.FindFirst(TreeScope.Children, new AndCondition(
new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Button),
new PropertyCondition(AutomationElement.NameProperty, "Open")));
// press it
((InvokePattern)openButton.GetCurrentPattern(InvokePattern.Pattern)).Invoke();
}
static AutomationElement WaitForDialog(AutomationElement element)
{
// note: this should be improved for error checking (timeouts, etc.)
while(true)
{
var openDialog = element.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Window));
if (openDialog != null)
return openDialog;
}
}
static void SafeExpand(AutomationElement element)
{
// for some reason, menus in np++ behave badly
while (true)
{
try
{
((ExpandCollapsePattern)element.GetCurrentPattern(ExpandCollapsePattern.Pattern)).Expand();
return;
}
catch
{
}
}
}
}
If you wonder how this has been made, then you must read about UI Automation. The mother of all tools is called Inspect: https://msdn.microsoft.com/library/windows/desktop/dd318521.aspx
Make sure you get version at least 7.2.0.0. Note there is another one called UISpy but inspect is better.
Note, unfortunately, notepad++ tab text content - because it's based on the custom scintilla editor control - does not properly supports automation (we can't read from it easily, I suppose we'd have to use scintilla Windows messages for this), but it could be added to it (hey, scintilla guys, if you read this ... :).
In addition to the answer from Garath, you might also want to investigate the Windows automation API's i.e. the technology used to implement coded UI tests for GUI applications. As part of regular functional testing, I routinely control an external application from a set of NUnit tests using these API's.
Tools like UIAVerify will give you an indication of what controls are available in the application and you can use the Invoke Pattern (and many others) to interact with the controls at run-time.
If you want a detailed example of how to use the automation API's, the open source TestStack White project is pretty handy.
It is almost not possible if SendKeys is not an option but read more
Now more important part of the question- why:
We have to look how win32 application works: it has a WndProc/WindowProc method which is resposible for processing "events" form the UI.
So every event in the windows application must go through above method. SendKeys method is a special of SendMessage (MSDN), so you can use SendMessage to control other exe than your.
Simple code could look like:
IntPtr hwnd = FindWindow("Notepad++", null);
SendMessageA(hwnd, WM_COMMAND, SOMETHING1, SOMETHING2);
There is already on StackOverflow example how to do that with chrome: C# - Sending messages to Google Chrome from C# application , but this is only a start. You will have to find out what exactly message you want to send.
In exactly situation which you described I will try to send WM_MOUSE and WM_KEYBORD events to Notepad++ events, but it is only an idea :)
I have a requirement to capture the screen shot of the opened dialog with a particular html control highlighted ( whose static id is given ). currently I Implemented the code following manner :
public void Snapshot()
{
Image currentImage = null;
currentImage = GetOpenedDialogFrame().CaptureImage();
}
public UITestControl GetOpenedDialogFrame()
{
var dialogsFrames = new HtmlDiv(this.BrowserMainWindow.UiMobiControlDocument);
dialogsFrames.SearchProperties.Add(new PropertyExpression(HtmlControl.PropertyNames.Class, "mcw-dialog", PropertyExpressionOperator.Contains));
var dialogs = dialogsFrames.FindMatchingControls();
if (dialogs.Count == 0)
{
return null;
}
return dialogs[dialogs.Count - 1];
}
Now I have to write the code to highlight the particular html control while taking a screenshot. The DrawHighlight() method of Microsoft.VisualStudio.TestTools.UITesting.dll does not take any parameter so how can I highlight a particular html control in the screenshot.
DrawHighlight() is a method of a UI Control. It could be used in this style:
public void Snapshot()
{
Image currentImage = null;
var control = GetOpenedDialogFrame();
// TODO: protect the code below against control==null.
control.DrawHighlight();
currentImage = control.CaptureImage();
}
Whilst that answers your question about DrawHighlight, I am not sure it will achieve what you want. Please see this question the Microsoft forums where they are trying to do a similar screen capture.
Why not simply user the playback settings:
Playback.PlaybackSettings.LoggerOverrideState = HtmlLoggerState.AllActionSnapshot;
This will produce the html log file with all the screenshots that your codedui test went threw.
After searching for the matching controls you can try to highlight each one of them.
something like:
foreach( var control in controls)
{
control.drawhighlight();
}
that way you'll be able to which controls are located by the playback(qtagent to be more precise). furthermore this will help you decide which instance to refer to. (run and wait to see which controls are highlighted, pick the one you need and hard code it to be part of the test).
so after the test run you'll end up with something like:
var dialogs = dialogsFrames.FindMatchingControls();
dialogs[desiredLocation].drawhighlight();
hope this helps.
I'm trying to test file download with Watin 2.1.0 against IE9. I used the suggested code from the accepted answer to the question Downloading a file with Watin in IE9, like this:
var downloadHandler = new FileDownloadHandler(fname);
WebBrowser.Current.AddDialogHandler(downloadHandler);
link.ClickNoWait();
downloadHandler.WaitUntilFileDownloadDialogIsHandled(15);
downloadHandler.WaitUntilDownloadCompleted(200);
However, the downloadHandler.WaitUntilFileDownloadDialogIsHandled(15) call times out. What should I do?
File download dialog doesn't work in IE9 (Windows7) NetFramework 4.0.
Following code snippet might help you resolve the issue:
First you must add references UIAutomationClient and UIAutomationTypes to your test project.
After In Ie9 Tools -> View Downloads -> Options define path to your save folder.
The next method extends Browser class
public static void DownloadIEFile(this Browser browser)
{
// see information here (http://msdn.microsoft.com/en-us/library/windows/desktop/ms633515(v=vs.85).aspx)
Window windowMain = new Window(WatiN.Core.Native.Windows.NativeMethods.GetWindow(browser.hWnd, 5));
System.Windows.Automation.TreeWalker trw = new System.Windows.Automation.TreeWalker(System.Windows.Automation.Condition.TrueCondition);
System.Windows.Automation.AutomationElement mainWindow = trw.GetParent(System.Windows.Automation.AutomationElement.FromHandle(browser.hWnd));
Window windowDialog = new Window(WatiN.Core.Native.Windows.NativeMethods.GetWindow(windowMain.Hwnd, 5));
// if doesn't work try to increase sleep interval or write your own waitUntill method
Thread.Sleep(1000);
windowDialog.SetActivate();
System.Windows.Automation.AutomationElementCollection amc = System.Windows.Automation.AutomationElement.FromHandle(windowDialog.Hwnd).FindAll(System.Windows.Automation.TreeScope.Children, System.Windows.Automation.Condition.TrueCondition);
foreach (System.Windows.Automation.AutomationElement element in amc)
{
// You can use "Save ", "Open", ''Cancel', or "Close" to find necessary button Or write your own enum
if (element.Current.Name.Equals("Save"))
{
// if doesn't work try to increase sleep interval or write your own waitUntil method
// WaitUntilButtonExsist(element,100);
Thread.Sleep(1000);
System.Windows.Automation.AutomationPattern[] pats = element.GetSupportedPatterns();
// replace this foreach if you need 'Save as' with code bellow
foreach (System.Windows.Automation.AutomationPattern pat in pats)
{
// '10000' button click event id
if (pat.Id == 10000)
{
System.Windows.Automation.InvokePattern click = (System.Windows.Automation.InvokePattern)element.GetCurrentPattern(pat);
click.Invoke();
}
}
}
}
}
if you want click 'Save As' replace foreach code with this
System.Windows.Automation.AutomationElementCollection bmc = element.FindAll(System.Windows.Automation.TreeScope.Children, System.Windows.Automation.Automation.ControlViewCondition);
System.Windows.Automation.InvokePattern click1 = (System.Windows.Automation.InvokePattern)bmc[0].GetCurrentPattern(System.Windows.Automation.AutomationPattern.LookupById(10000));
click1.Invoke();
Thread.Sleep(10000);
System.Windows.Automation.AutomationElementCollection main = mainWindow.FindAll(System.Windows.Automation.TreeScope.Children
,System.Windows.Automation.Condition.TrueCondition);
foreach (System.Windows.Automation.AutomationElement el in main)
{
if (el.Current.LocalizedControlType == "menu")
{
// first array element 'Save', second array element 'Save as', third second array element 'Save and open'
System.Windows.Automation.InvokePattern clickMenu = (System.Windows.Automation.InvokePattern)
el.FindAll(System.Windows.Automation.TreeScope.Children, System.Windows.Automation.Condition.TrueCondition) [1].GetCurrentPattern(System.Windows.Automation.AutomationPattern.LookupById(10000));
clickMenu.Invoke();
//add ControlSaveDialog(mainWindow, filename) here if needed
break;
}
}
Edit:
Also if you need to automate the save as dialog specifying a path and clicking save you can do it by adding this code just before break;
private static void ControlSaveDialog(System.Windows.Automation.AutomationElement mainWindow, string path)
{
//obtain the save as dialog
var saveAsDialog = mainWindow
.FindFirst(TreeScope.Descendants,
new PropertyCondition(AutomationElement.NameProperty, "Save As"));
//get the file name box
var saveAsText = saveAsDialog
.FindFirst(TreeScope.Descendants,
new AndCondition(
new PropertyCondition(AutomationElement.NameProperty, "File name:"),
new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Edit)))
.GetCurrentPattern(ValuePattern.Pattern) as ValuePattern;
//fill the filename box
saveAsText.SetValue(path);
Thread.Sleep(1000);
//find the save button
var saveButton =
saveAsDialog.FindFirst(TreeScope.Descendants,
new AndCondition(
new PropertyCondition(AutomationElement.NameProperty, "Save"),
new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Button)));
//invoke the button
var pattern = saveButton.GetCurrentPattern(InvokePattern.Pattern) as InvokePattern;
pattern.Invoke();
}
IE9 no longer uses a dialog window for saving files. Instead, it uses the notification bar to prevent focus from being removed from the web site. See http://msdn.microsoft.com/en-us/ie/ff959805.aspx under "Download Manager" for reference.
Unfortunately, this means that the current FileDownloadHandler in WatiN will not work. It instantiates a "DialogWatcher" class per browser instance that is a basic message pump for any kind of child window. When child windows are encountered, the DialogWatcher checks to see if the window is specifically a dialog (which the notification bar is not). If it is a dialog, it then iterates over the registered IDialogHandler instances calling "CanHandleDialog." Even if the notification bar were a dialog, it is of a different Window Style (http://msdn.microsoft.com/en-us/library/windows/desktop/ms632600(v=vs.85).aspx), which is how WatiN detects the type of dialog.
From what I can see, there is no support yet for detecting the IE 9 notification bar and its prompts in WatiN. Until that support is added, you will not be able to automate downloading files in IE9.