Extremely slow WatiN.Core.Document.FileUpload Set



我在Windows 7 64位上使用带有IE 9的WatiN 2.1.0(C#)。

我的问题是调用此函数

ie.FileUpload(Find.ByName(someRegExp)).Set(fileName);

非常慢。
我的意思是对象很好找到。此代码打开文件对话框,但它在很长时间后开始键入文件名,大约 3 到 5 分钟,比 WatiNs 超时长得多。在此之后,测试的其余部分将正常工作。

有没有治愈的方法?这么大的延迟非常烦人,当文件上传的测试用例更多时,它会显着延长测试持续时间。

好吧,这不是一个 Watin 解决方案,但我们遇到了完全相同的问题; 我们的文件浏览器测试占用了大量的总测试时间,我通过踢出 Watin 进行文件上传并使用(可怕的)UIAutomation 框架来解决这个问题。

用法示例:

    public CustomFileUpload FileUpload
    {
        get
        {
            return new CustomFileUpload(WebBrowser.Current.hWnd, "_Layout");
            //return Document.FileUpload(Find.ByName("file"));
        }
    }

您必须在测试项目中将引用添加到"UIAutomationClient"和"UIAutomationTypes"。下面的解决方案不是通用的,因此您可能需要对其进行调整以满足您的需求。

public class CustomFileUpload
{
    private readonly IntPtr _browserHandle;
    private readonly string _tabHeader;
    public CustomFileUpload(IntPtr browserHandle, string tabHeader)
    {
        _browserHandle = browserHandle;
        _tabHeader = tabHeader;
    }
    public void Set(string filePath)
    {
        Automate(filePath);
    }
    private void Automate(string filePath)
    {
        AutomationElement browser = AutomationElement.FromHandle(_browserHandle);
        AutomationElement tab = FindTab(browser, _tabHeader);

        // IE10 adds the name (or value?) "Browse..." to the upload-button. Need to hack it :)
        AutomationElement uploadButton = tab.FindFirst(TreeScope.Children,
                                                            new AndCondition(
                                                                new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Button),
                                                                new PropertyCondition(AutomationElement.NameProperty, "Browse..."))) ??
                                                                tab.FindFirst(TreeScope.Children,
                                                        new AndCondition(
                                                            new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Button),
                                                            new PropertyCondition(AutomationElement.NameProperty, "")));
        ClickButton(uploadButton);
        var openFileDialog = WaitUntilOpenFileDialogAvailable();
        var valuePattern = FindFileNameTextBox(openFileDialog).GetCurrentPattern(ValuePattern.Pattern) as ValuePattern;
        if (valuePattern == null)
            throw new InvalidOperationException("Can't set the file path");
        valuePattern.SetValue(filePath);
        SetFocusToSomethingElse(browser);
        var okButton = WaitUntilOkButtonLoaded(openFileDialog);
        ClickButton(okButton);
    }
    private static AutomationElement FindTab(AutomationElement browser, string tabHeader)
    {
        return browser.FindFirst(TreeScope.Descendants,
                                 new AndCondition(
                                     new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Pane),
                                     new PropertyCondition(AutomationElement.NameProperty, tabHeader)));
    }
    private static void SetFocusToSomethingElse(AutomationElement elementWhichShouldNotBeSelected)
    {
        do
        {
            foreach (AutomationElement element in AutomationElement.RootElement.FindAll(TreeScope.Children, new PropertyCondition(AutomationElement.IsKeyboardFocusableProperty, true)))
            {
                if (element != elementWhichShouldNotBeSelected)
                {
                    element.SetFocus();
                    return;
                }
            }
        } while (true);
    }
    private static AutomationElement WaitUntilOkButtonLoaded(AutomationElement openFileDialog)
    {
        AutomationElement okButton;
        do
        {
            okButton = openFileDialog.FindFirst(TreeScope.Children,
                                                new AndCondition(
                                                    new PropertyCondition(AutomationElement.IsContentElementProperty, true),
                                                    new PropertyCondition(AutomationElement.IsControlElementProperty, true),
                                                    new PropertyCondition(AutomationElement.NameProperty, "Open"),
                                                    new PropertyCondition(AutomationElement.IsInvokePatternAvailableProperty, true)
                                                    ));
        } while (okButton == null);
        return okButton;
    }
    private static AutomationElement WaitUntilOpenFileDialogAvailable()
    {
        AutomationElement openFileDialog = null;
        do
        {
            AutomationElement openFileDialogContainer = AutomationElement.RootElement.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.ClassNameProperty, "Alternate Modal Top Most"));
            if (openFileDialogContainer != null)
                openFileDialog = openFileDialogContainer.FindFirst(TreeScope.Children, Condition.TrueCondition);
        } while (openFileDialog == null);
        return openFileDialog;
    }
    private static void ClickButton(AutomationElement button)
    {            
        var clickPattern = button.GetCurrentPattern(InvokePattern.Pattern) as InvokePattern;
        if (clickPattern == null)
            throw new InvalidOperationException("Can't find the buttons click pattern");
        clickPattern.Invoke();
    }
    private static AutomationElement FindFileNameTextBox(AutomationElement openFileDialog)
    {
        AutomationElement findElementToTypePathInto;
        do
        {
            findElementToTypePathInto = openFileDialog.FindFirst(TreeScope.Descendants, new AndCondition(new PropertyCondition(AutomationElement.NameProperty, "File name:"), new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Edit)));
        } while (findElementToTypePathInto == null);
        return findElementToTypePathInto;
    }
}

我有一个稍微简单的解决方案(再次依赖于自动化,但仅用于选择文件)我用了.单击不等待()的文件输入不会导致浏览器挂起。

然后我编写了一个扩展方法来设置我要选择的文件:

public static void UploadFile(this Browser browser, string uploadPath)
{
    var trw = new TreeWalker(Condition.TrueCondition);
    var mainWindowElement = trw.GetParent(AutomationElement.FromHandle(browser.hWnd));
    // Wait for the dialog to open
    Thread.Sleep(1000);
    // Get the select dialog
    var selectDialogElement = mainWindowElement.FindFirst(TreeScope.Descendants, 
        new PropertyCondition(AutomationElement.NameProperty, "Choose File to Upload"));
    // Get the file name box and set the path
    var selectTextElement = selectDialogElement.FindFirst(
        TreeScope.Descendants,
        new AndCondition(new PropertyCondition(AutomationElement.NameProperty, "File name:"),
            new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Edit)));
    var selectValue = (ValuePattern)selectTextElement.GetCurrentPattern(ValuePattern.Pattern);
    selectValue.SetValue(uploadPath);
    // Get the open button and click it
    var openButtonElement = selectDialogElement.FindFirst(TreeScope.Descendants,
        new AndCondition(new PropertyCondition(AutomationElement.NameProperty, "Open"),
            new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Button)));
    var openButtonClick = (InvokePattern)openButtonElement.GetCurrentPattern(InvokePattern.Pattern);
    openButtonClick.Invoke();
}

示例用法:

var browser = (IE)Document.DomContainer;     
Document.FileUpload(Find.BySelector("#FileUpload")).ClickNoWait();
browser.UploadFile("c:\myfile.txt");
Document.Button(Find.BySelector("#submit")).Click();

UIAutomation将从调用UploadFile(filepath)接管,并找到对话框窗口,并填写表单,就像用户做了一样。

WatiN 开始在文件上传对话框中键入文件名之前,我还经历了 3 到 5 分钟的延迟。

每当开发人员工具窗格在IE中打开时,这似乎都会发生在我身上。当它未打开时,键入会立即开始。

有关默认浏览器和类似弹出窗口的提示似乎也会导致延迟。

最新更新