Downloading a file with Watin in IE9
Asked Answered
M

5

7

I'm having an issue with automating the process of downloading a file from a website. The website has a Java button, that when clicked, triggers the download of an Excel file. I'm using the most recent build of Watin (v2.1).

I've managed to get Watin to log into the website, navigate to the appropriate page, change parameters on the page, and click the button to start the download.

However, when the download has completed, the IE9 download box appears, and nothing happens, until Watin timesout.

I'd appreciate any suggestions as I can't see any way of downloading a file, or getting it to save the file. Even if it passed 'Alt+S' to the page, that would save it. I've tried running it through WatinTestRecorder and that doesn't prompt for saving.

using (var browser = new IE(sLogin))
{
    browser.AddDialogHandler(new OKDialogHandler());
    browser.AddDialogHandler(new DialogHandlerHelper());
    browser.AddDialogHandler(new ConfirmDialogHandler());
    browser.AddDialogHandler(new ReturnDialogHandlerIe9());

    browser.TextField(Find.ByName("txtUserID")).TypeText("username");
    browser.TextField(Find.ByName("txtPassword")).TypeText("password");
    browser.Button(Find.ByName("btnLogin")).Click();

    browser.WaitForComplete();  
    browser.GoTo(targetUri);

    browser.SelectList("ctl00_phFormContent_ucOptionParam0_lst").SelectByValue("4");

    browser.Button(Find.ByName("ctl00$phFormButtonBar$btnRun")).Click();
    browser.WaitForComplete();

    //Some code to download the file here!
}
Marius answered 25/5, 2011 at 13:33 Comment(0)
S
5

This should be supported since version 1.1.0.4000. The release notes for that version aren't online anymore (http://watin.org/documentation/), but I found it in Googles cache (http://svn6.assembla.com/svn/ci-samples/dotnet/watir/website/releasenotes-1-1-0-4000.html)

It should be something like:

using(IE ie = new IE(someUrlToGoTo))
{
    FileDownloadHandler fileDownloadHandler = new FileDownloadHandler(fullFileName);
    ie.AddDialogHandler(fileDownloadHandler);

    ie.Link("startDownloadLinkId").Click();

    fileDownloadHandler.WaitUntilFileDownloadDialogIsHandled(15);
    fileDownloadHandler.WaitUntilDownloadCompleted(200);
}

EDIT: After the comments below, this answer was accepted. So I'm assuming the following code works (which is taken from the link to SourceForge in my last comment, notice the ClickNoWait):

using(IE ie = new IE(someUrlToGoTo))
{
    FileDownloadHandler fileDownloadHandler = new FileDownloadHandler(fullFileName);
    ie.AddDialogHandler(fileDownloadHandler);

    ie.Link("startDownloadLinkId").ClickNoWait();

    fileDownloadHandler.WaitUntilFileDownloadDialogIsHandled(15);
    fileDownloadHandler.WaitUntilDownloadCompleted(200);
}
Surra answered 31/5, 2011 at 8:28 Comment(4)
Hi Peter, Thanks for the suggestion, however using that is resulting in the app hanging at 'WaitUntilFileDownloadDialogIsHandled'. The browser, in the background is sitting with the message 'Do you want to open or save <filename> from <website> ?', and the Open Save or cancel buttons from IE9. I was under the impression that this should save the file appropriately ?Marius
Hi Nick, there appears to have been a fix for IE9: 'Added new static method ReturnDialogHandler.CreateInstance() which will return the right version of this dialoghandler for your installed version of IE.', see watin.org/documentation/release-2-1-0-1196. But there's little information as of now. I'll let you know if I find more on this.Surra
Never mind my comment above, I believe it is incorrect. Have you tried clicking the cancel button? To do this, use the other constructor of the FileDownloadHandler and pass the FileDownloadOptionEnum.Cancel. I'm thinking there might be something wrong in this piece of code: watin.svn.sourceforge.net/viewvc/watin/trunk/src/Core/… (line 168)Surra
Does this help? sourceforge.net/mailarchive/…Surra
P
2

The accepted answer did not work for me because IE 9 pops up a "Notification" you have to navigate before you can get to the actual Save As dialog (and WatiN cannot handle notifications automatically). I followed Borris Pavlov's link which did a good job showing the way. I cleaned up the code a bit that was posted there and this was the resulting file:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using WatiN.Core;
using WatiN.Core.Native.Windows;
using System.Threading;
using System.Windows.Automation;

namespace MyProject
{
    public static class BrowserExtensionMethods
    {
        public static void DownloadIeFile(this IE browser,string saveAsFilename=null)
        {
            // see information here (http://msdn.microsoft.com/en-us/library/windows/desktop/ms633515(v=vs.85).aspx)
            Window windowMain = new Window(NativeMethods.GetWindow(browser.hWnd, 5));
            TreeWalker dialogElementTreeWalker = new TreeWalker(Condition.TrueCondition);
            AutomationElement mainWindow = dialogElementTreeWalker.GetParent(AutomationElement.FromHandle(browser.hWnd));

            Window windowDialog = new Window(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();
            AutomationElementCollection dialogElements = AutomationElement.FromHandle(windowDialog.Hwnd).FindAll(TreeScope.Children, Condition.TrueCondition);

            if (string.IsNullOrEmpty(saveAsFilename))
            {
                ClickSave(dialogElements);
            }
            else
            {
                ClickSaveAs(mainWindow, dialogElements,saveAsFilename);
            }
        }

        private static void ClickSaveAs(AutomationElement mainWindow, AutomationElementCollection dialogElements,string filename)
        {
            foreach (AutomationElement element in dialogElements)
            {

                if (element.Current.Name.Equals("Save"))
                {
                    AutomationElementCollection dialogSubElements = element.FindAll(TreeScope.Children, Automation.ControlViewCondition);
                    InvokePattern clickPatternForSaveDropdown = (InvokePattern)dialogSubElements[0].GetCurrentPattern(AutomationPattern.LookupById(10000));
                    clickPatternForSaveDropdown.Invoke();
                    Thread.Sleep(3000);

                    AutomationElementCollection dialogElementsInMainWindow = mainWindow.FindAll(TreeScope.Children, Condition.TrueCondition);
                    foreach (AutomationElement currentMainWindowDialogElement in dialogElementsInMainWindow)
                    {
                        if (currentMainWindowDialogElement.Current.LocalizedControlType == "menu")
                        {
                            // first array element 'Save', second array element 'Save as', third second array element    'Save and open'
                            InvokePattern clickMenu = (InvokePattern)currentMainWindowDialogElement.FindAll(TreeScope.Children, Condition.TrueCondition)[1].GetCurrentPattern(AutomationPattern.LookupById(10000));
                            clickMenu.Invoke();
                            Thread.Sleep(5000);
                            ControlSaveDialog(mainWindow, filename);
                            break;

                        }
                    }
                }
            }
        }

        private static void ClickSave(AutomationElementCollection dialogElements)
        {
            foreach (AutomationElement element in dialogElements)
            {
                // 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);
                    AutomationPattern[] automationPatterns = element.GetSupportedPatterns();
                    // replace this foreach if you need 'Save as' with code bellow
                    foreach (AutomationPattern currentPattern in automationPatterns)
                    {
                        // '10000' button click event id 
                        if (currentPattern.Id == 10000)
                        {
                            InvokePattern click = (InvokePattern)element.GetCurrentPattern(currentPattern);
                            click.Invoke();
                        }
                    }
                }
            }
        }

        private static void ControlSaveDialog(AutomationElement mainWindow, string path)
        {
            //obtain the save as dialog
            //*** must disable throwing of the NonComVisibleBaseClass "exception" for this to work in debug mode:
            //              1. Navigate to Debug->Exceptions...
            //              2. Expand "Managed Debugging Assistants"
            //              3. Uncheck the NonComVisibleBaseClass Thrown option.
            //              4. Click [Ok]
            //***copied from http://social.msdn.microsoft.com/Forums/en-US/27c3bae8-41fe-4db4-8022-e27d333f714e/noncomvisiblebaseclass-was-detected?forum=Vsexpressvb

            var saveAsDialog = mainWindow.FindFirst(TreeScope.Descendants, new PropertyCondition(AutomationElement.NameProperty, "Save As"));
            //var saveAsDialog = mainWindow.FindFirst(TreeScope.Descendants, new PropertyCondition(AutomationElement.NameProperty, "[#] Save As [#]"));  //needed if using sandboxie
            //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();
        }
    }
}

See the comment in the ControlSaveDialog method for info on how to make this work as a debug build (VS settings).

I actually only tested the "Save As" operation so I hope the other works (it seems that the original poster added the "Save As" as an afterthought so my guess is that he tested the "Save" operation more thoroughly).

To call this you can use some code like:

Link lastMp4Link = mp4Links[mp4Links.Count - 1];  //mp4Links is a WatiN.Core.LinkCollection
lastMp4Link.Click();

browser.DownloadIeFile(string.Format(@"c:\temp\myFile.blah"));  //"browser" is a WatiN.Core.IE object
Paisley answered 29/10, 2013 at 21:3 Comment(1)
note, to get this to work add references to UIAutomationClient.dll and UIAutomationTypes.dll available in this local directory for me : C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0Mollymollycoddle
K
1

I just got word from the Watin-users mailing list that FileDownloadHandler is broken with IE9. There is no fix yet, however I uninstalled IE9 (roll back to IE8) and it has the old style dialog that is handled by WatiN.

Kovacs answered 29/11, 2011 at 18:30 Comment(0)
V
1

Watin can't find download dialog and dialog buttons. It can resolve with this.

How to test file download with Watin / IE9?

See comments

Vannoy answered 16/12, 2011 at 9:4 Comment(1)
As I commented on your previous (exactly the same) answer, you might at least want to add the gist of the answer/cause of the problem to your answer instead of just adding a link. This thread on Meta goes into that issue.Archdeacon
H
0

After many revisions using WaitN, I ended up using Selenium. It gives you a ton more control over what is being processed and used and allows you to use more modern web browsers.

Hysteresis answered 19/5, 2016 at 15:7 Comment(2)
This does not work - cookies not being passed by WebClientGalling
What I know is Selenium does not work with IE so far.Tieratierce

© 2022 - 2024 — McMap. All rights reserved.