• <menu id="w2i4a"></menu>
  • logo DevExpress WinForm中文手冊

    文檔首頁>>DevExpress WinForm中文手冊>>自動化UI測試(UI自動化、Appium、編碼UI)

    自動化UI測試(UI自動化、Appium、編碼UI)


    立即下載DevExpress WinForms

    用戶界面 (UI) 測試可驗證應用程序的所有視覺元素是否正常運行。UI測試可以由測試人員手動執(zhí)行,也可以借助自動化測試工具執(zhí)行,自動化測試更快、更可靠且更具成本效益。

    微軟編碼UI測試(CUIT)框架

    編碼UI測試框架是微軟的一個解決方案,它利用控件的可訪問性層來記錄和運行UI測試,CUIT組件通過Visual Studio Installer分發(fā)。

    該解決方案在Visual Studio 2019及以后被宣布過時,在Visual Studio 2022中,您仍然可以運行已編碼UI測試,但不能記錄新測試,較新的IDE版本將完全放棄對CUIT的支持。

    參見:

    編碼UI測試

    DevExpress編碼UI擴展

    DevExpress Coded UI是Microsoft Coded UI Tests的擴展,專為基于DevExpress的應用程序量身定制。這些解決方案之間的區(qū)別在于與Microsoft CUIT不同,DevExpress編碼UI擴展不利用輔助功能,該框架通過專有通道與控件進行通信,并使用DevExpress控件中聲明的幫助程序類。

    Microsoft 終止CUIT的決定也會影響DevExpress編碼UI擴展,對于較新的項目,我們建議您改用Appium或UI Automation。

    也可以看看:

    Appium和UI自動化

    Appium是一款開源工具,可讓您為 Web、混合、iOS 移動、Android 移動和 Windows 桌面平臺創(chuàng)建自動化UI測試,要測試Windows應用程序則需要設置WinAppDriver 。

    也可以看看:

    Appium(以及多個其他測試框架)利用UI Automation ——Microsoft 的Windows輔助功能框架,您可以直接使用此框架(不涉及任何第三方解決方案)來編寫UI測試。

    也可以看看:

    Appium和UI Automation 之間的選擇取決于場景和測試要求的復雜性,Appium更容易使用,但也有更多限制,因為它沒有實現(xiàn)所有UIA功能。例如,Appium 允許您使用pattern 成員,但只能使用屬性,不能使用方法。

    提示:調(diào)度程序、富編輯器、PDF查看器和電子表格控件目前不支持UI自動化。

    步驟記錄器和手動測試腳本

    大多數(shù)測試自動化平臺都提供了記錄工具,這些工具在運行時跟蹤您的操作(光標移動、單擊和鍵盤按鍵),并生成模擬這些操作的代碼。下面的博客文章展示了如何使用Appium步進記錄器與DevExpress控件:從CodedUI移動到appium。

    記錄器允許您編寫更少的代碼,但它們可能產(chǎn)生不穩(wěn)定的測試并導致性能問題。例如,大多數(shù)測試記錄器在元素選擇代碼中枚舉目標UI元素的所有父元素,因此,一個小的UI修改(比如添加一個新的Panel容器)會導致這個選擇代碼失敗。

    為了避免潛在的問題并更好地理解測試的功能,我們建議手動編寫測試腳本。例如,您可以選擇為目標UI元素檢查哪些父控件,而不是列出元素父元素的整個層次結(jié)構(gòu),或者直接獲取該元素而不訪問其任何父元素。

    如何編寫Appium和UI自動化測試

    常用測試結(jié)構(gòu)

    Appium和UI自動化測試共享類似的代碼塊層次結(jié)構(gòu),每個塊都由一個 NUnit屬性裝飾。

    修飾包含測試的類。

    每次測試即將開始時,都會調(diào)用帶有此屬性的方法。

    與SetUp屬性相反,此屬性修飾每次測試完成時執(zhí)行的一組指令。

    修飾一個包含測試腳本的方法。

    Appium和UIA測試的一般實現(xiàn)如下所示:

    C#:

    using System;
    using NUnit.Framework;
    
    namespace VisualTests {
    [TestFixture]
    public class MyAppTests {
    [SetUp]
    public void Setup() {
    // Actions repeated before each test
    }
    [TearDown]
    public void Cleanup() {
    // Actions repeated after each test
    }
    [Test]
    public void Test1() {
    // Test #1
    }
    [Test]
    public void Test2() {
    // Test #2
    }
    }
    }

    VB.NET:

    Imports System
    Imports NUnit.Framework
    
    Namespace VisualTests
    <TestFixture>
    Public Class MyAppTests
    <SetUp>
    Public Sub Setup()
    ' Actions repeated before each test
    End Sub
    <TearDown>
    Public Sub Cleanup()
    ' Actions repeated after each test
    End Sub
    <Test>
    Public Sub Test1()
    ' Test #1
    End Sub
    <Test>
    Public Sub Test2()
    ' Test #2
    End Sub
    End Class
    End Namespace

    檢查Tool

    要為任何UI元素編寫測試,需要做以下事情:

    • 通過ID或名稱獲取該元素。
    • 檢查它支持哪些模式,并利用這些模式的屬性和方法來模擬用戶操作。
    • 調(diào)用Assert.AreEqual 方法來比較實際和預期的控制狀態(tài)。

    要獲取元素名稱和 ID,并檢查其可用的模式 API,請使用Microsoft Inspect —— Windows SDK安裝中包含的免費工具。

    WPF可及性和測試檢查

    手工檢查UI元素還允許您定位不良的可訪問性名稱和其他問題,要解決這些問題,請?zhí)幚鞤XAccessible.QueryAccessibleInfo事件。

    如何編寫 Appium 測試

    1. 在 Windows 設置中啟用Developer Mode。
    2. 下載、安裝并運行WinAppDriver 。
    3. 在需要測試的項目中打開全局WindowsFormsSettings.UseUIAutomation。
    4. 在 Visual Studio 中創(chuàng)建一個新的“單元測試項目” 。
    5. 安裝“Appium.WebDriver” NuGet 包。
    6. 根據(jù)通用測試結(jié)構(gòu)部分創(chuàng)建測試,下面的代碼說明了一個自動化測試示例。

    C#:

    using System;
    using System.Windows.Forms;
    using NUnit.Framework;
    using OpenQA.Selenium.Appium;
    using OpenQA.Selenium.Appium.Windows;
    
    namespace AppiumTests {
    [TestFixture]
    public class EditorsDemoTests {
    WindowsDriver<WindowsElement> driver;
    string editorsDemoPath =
    @"C:\Work\2022.1\Demos.Win\EditorsDemos\CS\EditorsMainDemo\bin\Debug\EditorsMainDemo.exe";
    [SetUp]
    public void Setup() {
    AppiumOptions options = new AppiumOptions();
    options.AddAdditionalCapability("app", editorsDemoPath);
    driver = new WindowsDriver<WindowsElement>(new Uri("http://127.0.0.1:4723"), options);
    }
    [TearDown]
    public void Cleanup() {
    driver.Close();
    }
    [Test]
    public void ProgressBarTest() {
    var form = driver.FindElementByAccessibilityId("RibbonMainForm");
    
    var progressBarAccordionItem =
    form.FindElementByAccessibilityId("accordionControl1").FindElementByName("Progress Bar");
    progressBarAccordionItem.Click();
    Assert.AreEqual("True", progressBarAccordionItem.GetAttribute("SelectionItem.IsSelected"));
    AccessibleStates itemStates =
    (AccessibleStates)int.Parse(progressBarAccordionItem.GetAttribute("LegacyState"));
    Assert.IsTrue(itemStates.HasFlag(AccessibleStates.Selected));
    
    form.FindElementByName("Position Management").Click();
    
    var minMaxComboBox = form.FindElementByAccessibilityId("comboBoxMaxMin");
    minMaxComboBox.Click();
    minMaxComboBox.SendKeys(
    OpenQA.Selenium.Keys.Down + OpenQA.Selenium.Keys.Down + OpenQA.Selenium.Keys.Enter);
    Assert.AreEqual("Min = 100; Max = 200", minMaxComboBox.Text);
    
    var progressBar = form.FindElementByAccessibilityId("progressBarSample2");
    Assert.AreEqual("100", progressBar.GetAttribute("RangeValue.Minimum"));
    Assert.AreEqual("200", progressBar.GetAttribute("RangeValue.Maximum"));
    Assert.AreEqual("100", progressBar.GetAttribute("RangeValue.Value"));
    Assert.AreEqual("0%", progressBar.Text);
    
    form.FindElementByName("Step!").Click();
    Assert.AreEqual("110", progressBar.GetAttribute("RangeValue.Value"));
    Assert.AreEqual("10%", progressBar.Text);
    }
    }
    }

    VB.NET:

    Imports System
    Imports System.Windows.Forms
    Imports NUnit.Framework
    Imports OpenQA.Selenium.Appium
    Imports OpenQA.Selenium.Appium.Windows
    
    Namespace AppiumTests
    <TestFixture>
    Public Class EditorsDemoTests
    Private driver As WindowsDriver(Of WindowsElement)
    Private editorsDemoPath As String = "C:\Work\2022.1\Demos.Win\EditorsDemos\CS\EditorsMainDemo\bin\Debug\EditorsMainDemo.exe"
    <SetUp>
    Public Sub Setup()
    Dim options As New AppiumOptions()
    options.AddAdditionalCapability("app", editorsDemoPath)
    driver = New WindowsDriver(Of WindowsElement)(New Uri("http://127.0.0.1:4723"), options)
    End Sub
    <TearDown>
    Public Sub Cleanup()
    driver.Close()
    End Sub
    <Test>
    Public Sub ProgressBarTest()
    Dim form = driver.FindElementByAccessibilityId("RibbonMainForm")
    
    Dim progressBarAccordionItem = form.FindElementByAccessibilityId("accordionControl1").FindElementByName("Progress Bar")
    progressBarAccordionItem.Click()
    Assert.AreEqual("True", progressBarAccordionItem.GetAttribute("SelectionItem.IsSelected"))
    Dim itemStates As AccessibleStates = CType(Integer.Parse(progressBarAccordionItem.GetAttribute("LegacyState")), AccessibleStates)
    Assert.IsTrue(itemStates.HasFlag(AccessibleStates.Selected))
    
    form.FindElementByName("Position Management").Click()
    
    Dim minMaxComboBox = form.FindElementByAccessibilityId("comboBoxMaxMin")
    minMaxComboBox.Click()
    minMaxComboBox.SendKeys(OpenQA.Selenium.Keys.Down + OpenQA.Selenium.Keys.Down + OpenQA.Selenium.Keys.Enter)
    Assert.AreEqual("Min = 100; Max = 200", minMaxComboBox.Text)
    
    Dim progressBar = form.FindElementByAccessibilityId("progressBarSample2")
    Assert.AreEqual("100", progressBar.GetAttribute("RangeValue.Minimum"))
    Assert.AreEqual("200", progressBar.GetAttribute("RangeValue.Maximum"))
    Assert.AreEqual("100", progressBar.GetAttribute("RangeValue.Value"))
    Assert.AreEqual("0%", progressBar.Text)
    
    form.FindElementByName("Step!").Click()
    Assert.AreEqual("110", progressBar.GetAttribute("RangeValue.Value"))
    Assert.AreEqual("10%", progressBar.Text)
    End Sub
    End Class
    End Namespace
    • 上面的代碼借助FindElementByName和FindElementByAccessibilityId方法定位所需的UI元素,要獲取元素名稱或ID,請在Inspect中瀏覽元素屬性
    • 要模擬鼠標單擊和按鍵,請調(diào)用Click()和SendKeys方法。
    • 使用UIElement.GetAttribute方法獲取模式屬性的值,這些名稱在Inspect中也可見。

    要訪問模式的屬性LegacyIAccessible,請使用“Legacy{PropertyName}”格式:

    C#:

    var value = progressBarAccordionItem.GetAttribute("LegacyState");

    點擊復制

    VB.NET:

    Dim value = progressBarAccordionItem.GetAttribute("LegacyState")

    點擊復制

    其他模式的屬性用“{PatternName}.{PropertyName}”格式訪問:

    C#:

    var value = progressBar.GetAttribute("RangeValue.Maximum");

    點擊復制

    VB.NET:

    Dim value = progressBar.GetAttribute("RangeValue.Maximum")

    點擊復制

    • DevExpress 上下文菜單沒有直接所有者,因此它們的可訪問對象是桌面窗口的子窗口,而不是應用程序窗口,要訪問這些菜單中的項目,請使用桌面窗口驅(qū)動程序。

    C#:

    AppiumOptions globalDriverOptions = new AppiumOptions();
    globalDriverOptions.AddAdditionalCapability("app", "Root");
    var globalDriver = new WindowsDriver<WindowsElement>(new Uri("http://127.0.0.1:4723"), globalDriverOptions);
    var menuItem = globalDriver.FindElementByName("ItemName");

    點擊復制

    VB.NET:

    Dim globalDriverOptions As AppiumOptions = New AppiumOptions()
    globalDriverOptions.AddAdditionalCapability("app", "Root")
    Dim globalDriver = New WindowsDriver(Of WindowsElement)(New Uri("http://127.0.0.1:4723"), globalDriverOptions)
    Dim menuItem = globalDriver.FindElementByName("ItemName")

    點擊復制

    如何編寫 UI 自動化測試

    1. 在需要測試的項目中打開全局WindowsFormsSettings.UseUIAutomation屬性。
    2. 在Visual Studio中創(chuàng)建一個新的“Unit Test Project”。
    3. 在您的項目中包括UIAutomationClient.dll和UIAutomationTypes.dll庫。
    4. 根據(jù)公共測試結(jié)構(gòu)部分創(chuàng)建測試,下面的代碼演示了一個自動化測試示例。

    C#:

    using System;
    using System.Diagnostics;
    using System.Threading;
    using System.Windows.Automation;
    using Microsoft.Test.Input;
    using NUnit.Framework;
    
    namespace UIAutomationTests {
    [TestFixture]
    public class OutlookInspiredTests {
    string path =
    @"C:\Work\2022.1\Demos.RealLife\DevExpress.OutlookInspiredApp\
    bin\Debug\DevExpress.OutlookInspiredApp.Win.exe";
    Process appProcess;
    [SetUp]
    public void Setup() {
    appProcess = Process.Start(path);
    }
    [TearDown]
    public void TearDown() {
    appProcess.Kill();
    }
    [Test]
    public void Test1() {
    AutomationElement form =
    AutomationElement.RootElement.FindFirstWithTimeout(TreeScope.Children, new PropertyCondition(
    AutomationElement.AutomationIdProperty, "MainForm"), 10000);
    
    AutomationElement grid =
    form.FindFirstWithTimeout(TreeScope.Descendants, new PropertyCondition(
    AutomationElement.AutomationIdProperty, "gridControl"), 5000);
    
    AutomationElement cell = FindCellByValue(grid, "FULL NAME", "Greta Sims");
    Mouse.MoveTo(cell.GetPoint());
    Mouse.DoubleClick(MouseButton.Left);
    
    AutomationElement detailForm =
    form.FindFirstWithTimeout(TreeScope.Children, new PropertyCondition(
    AutomationElement.AutomationIdProperty, "DetailForm"), 5000);
    
    AutomationElement jobTitleEdit =
    detailForm.FindFirstWithTimeout(TreeScope.Descendants, new PropertyCondition(
    AutomationElement.AutomationIdProperty, "TitleTextEdit"));
    ((ValuePattern)jobTitleEdit.GetCurrentPattern(ValuePattern.Pattern)).SetValue("HR Head");
    
    AutomationElement department =
    detailForm.FindFirstWithTimeout(TreeScope.Descendants, new PropertyCondition(
    AutomationElement.AutomationIdProperty, "DepartmentImageComboBoxEdit"));
    ((ExpandCollapsePattern)department.GetCurrentPattern(ExpandCollapsePattern.Pattern)).Expand();
    
    AutomationElement managementItem =
    detailForm.FindFirstWithTimeout(TreeScope.Descendants, new PropertyCondition(
    AutomationElement.NameProperty, "Management"));
    ((InvokePattern)managementItem.GetCurrentPattern(InvokePattern.Pattern)).Invoke();
    
    AutomationElement saveClose =
    detailForm.FindFirstWithTimeout(TreeScope.Descendants, new PropertyCondition(
    AutomationElement.NameProperty, "Save & Close"));
    ((InvokePattern)saveClose.GetCurrentPattern(InvokePattern.Pattern)).Invoke();
    
    AutomationElement jobTitle =
    form.FindFirstWithTimeout(TreeScope.Descendants, new PropertyCondition(
    AutomationElement.AutomationIdProperty, "sliTitle"));
    Assert.AreEqual("HR Head", jobTitle.Current.Name);
    }
    
    AutomationElement FindCellByValue(AutomationElement grid, string columnName, string cellValue) {
    TablePattern tablePattern = (TablePattern)grid.GetCurrentPattern(TablePattern.Pattern);
    AutomationElement[] headers = tablePattern.Current.GetColumnHeaders();
    int columnIndex = -1;
    for(int i = 0; i < headers.Length - 1; i++)
    if(headers[i].Current.Name == columnName)
    columnIndex = i;
    if(columnIndex == -1)
    return null;
    for(int i = 0; i < tablePattern.Current.RowCount; i++) {
    AutomationElement cell = tablePattern.GetItem(i, columnIndex);
    if(cell != null) {
    ValuePattern valuePattern = (ValuePattern)cell.GetCurrentPattern(ValuePattern.Pattern);
    if(valuePattern.Current.Value == cellValue) {
    return cell;
    }
    }
    }
    return null;
    }
    }
    
    public static class AutomationElementExtensions {
    public static System.Drawing.Point GetPoint(this AutomationElement @this) {
    System.Windows.Point windowsPoint = @this.GetClickablePoint();
    return new System.Drawing.Point(Convert.ToInt32(windowsPoint.X), Convert.ToInt32(windowsPoint.Y));
    }
    public static AutomationElement FindFirstWithTimeout(this AutomationElement @this,
    TreeScope scope, Condition condition, int timeoutMilliseconds = 1000) {
    Stopwatch stopwatch = new Stopwatch();
    stopwatch.Start();
    do {
    var result = @this.FindFirst(scope, condition);
    if(result != null)
    return result;
    Thread.Sleep(100);
    }
    while(stopwatch.ElapsedMilliseconds < timeoutMilliseconds);
    return null;
    }
    }
    }

    VB.NET:

    Imports System
    Imports System.Diagnostics
    Imports System.Threading
    Imports System.Windows.Automation
    Imports Microsoft.Test.Input
    Imports NUnit.Framework
    
    Namespace UIAutomationTests
    <TestFixture>
    Public Class OutlookInspiredTests
    Private path As String =
    "C:\Work\2022.1\Demos.RealLife\DevExpress.OutlookInspiredApp\bin\Debug\DevExpress.OutlookInspiredApp.Win.exe"
    Private appProcess As Process
    <SetUp>
    Public Sub Setup()
    appProcess = Process.Start(path)
    End Sub
    <TearDown>
    Public Sub TearDown()
    appProcess.Kill()
    End Sub
    <Test>
    Public Sub Test1()
    Dim form As AutomationElement = AutomationElement.RootElement.FindFirstWithTimeout(TreeScope.Children, New PropertyCondition(AutomationElement.AutomationIdProperty, "MainForm"), 10000)
    
    Dim grid As AutomationElement = form.FindFirstWithTimeout(TreeScope.Descendants, New PropertyCondition(AutomationElement.AutomationIdProperty, "gridControl"), 5000)
    
    Dim cell As AutomationElement = FindCellByValue(grid, "FULL NAME", "Greta Sims")
    Mouse.MoveTo(cell.GetPoint())
    Mouse.DoubleClick(MouseButton.Left)
    
    Dim detailForm As AutomationElement = form.FindFirstWithTimeout(TreeScope.Children, New PropertyCondition(AutomationElement.AutomationIdProperty, "DetailForm"), 5000)
    
    Dim jobTitleEdit As AutomationElement = detailForm.FindFirstWithTimeout(TreeScope.Descendants, New PropertyCondition(AutomationElement.AutomationIdProperty, "TitleTextEdit"))
    CType(jobTitleEdit.GetCurrentPattern(ValuePattern.Pattern), ValuePattern).SetValue("HR Head")
    
    Dim department As AutomationElement = detailForm.FindFirstWithTimeout(TreeScope.Descendants, New PropertyCondition(AutomationElement.AutomationIdProperty, "DepartmentImageComboBoxEdit"))
    CType(department.GetCurrentPattern(ExpandCollapsePattern.Pattern), ExpandCollapsePattern).Expand()
    
    Dim managementItem As AutomationElement = detailForm.FindFirstWithTimeout(TreeScope.Descendants, New PropertyCondition(AutomationElement.NameProperty, "Management"))
    CType(managementItem.GetCurrentPattern(InvokePattern.Pattern), InvokePattern).Invoke()
    
    Dim saveClose As AutomationElement = detailForm.FindFirstWithTimeout(TreeScope.Descendants, New PropertyCondition(AutomationElement.NameProperty, "Save & Close"))
    CType(saveClose.GetCurrentPattern(InvokePattern.Pattern), InvokePattern).Invoke()
    
    Dim jobTitle As AutomationElement = form.FindFirstWithTimeout(TreeScope.Descendants, New PropertyCondition(AutomationElement.AutomationIdProperty, "sliTitle"))
    Assert.AreEqual("HR Head", jobTitle.Current.Name)
    End Sub
    
    Private Function FindCellByValue(ByVal grid As AutomationElement, ByVal columnName As String, ByVal cellValue As String) As AutomationElement
    Dim tablePattern As TablePattern = CType(grid.GetCurrentPattern(TablePattern.Pattern), TablePattern)
    Dim headers() As AutomationElement = tablePattern.Current.GetColumnHeaders()
    Dim columnIndex As Integer = -1
    For i As Integer = 0 To headers.Length - 2
    If headers(i).Current.Name = columnName Then
    columnIndex = i
    End If
    Next i
    If columnIndex = -1 Then
    Return Nothing
    End If
    For i As Integer = 0 To tablePattern.Current.RowCount - 1
    Dim cell As AutomationElement = tablePattern.GetItem(i, columnIndex)
    If cell IsNot Nothing Then
    Dim valuePattern As ValuePattern = CType(cell.GetCurrentPattern(ValuePattern.Pattern), ValuePattern)
    If valuePattern.Current.Value = cellValue Then
    Return cell
    End If
    End If
    Next i
    Return Nothing
    End Function
    End Class
    
    Public Module AutomationElementExtensions
    <System.Runtime.CompilerServices.Extension> _
    Public Function GetPoint(ByVal this As AutomationElement) As System.Drawing.Point
    Dim windowsPoint As System.Windows.Point = this.GetClickablePoint()
    Return New System.Drawing.Point(Convert.ToInt32(windowsPoint.X), Convert.ToInt32(windowsPoint.Y))
    End Function
    <System.Runtime.CompilerServices.Extension> _
    Public Function FindFirstWithTimeout(ByVal this As AutomationElement, ByVal scope As TreeScope, ByVal condition As Condition, Optional ByVal timeoutMilliseconds As Integer = 1000) As AutomationElement
    Dim stopwatch As New Stopwatch()
    stopwatch.Start()
    Do
    Dim result = this.FindFirst(scope, condition)
    If result IsNot Nothing Then
    Return result
    End If
    Thread.Sleep(100)
    Loop While stopwatch.ElapsedMilliseconds < timeoutMilliseconds
    Return Nothing
    End Function
    End Module
    End Namespace
    • 與Appium測試類似,根據(jù)從Inspect復制的名稱或id檢索元素,使用AutomationElement.FindFirst 來查找所需的元素。
    • 自定義FindFirstWithTimeout方法通過添加超時閾值來擴展FindFirst,此值指定當元素不能立即可用時,腳本可以重試獲取該元素的時間。
    • 該類Mouse公開了允許模擬鼠標操作的方法,安裝“Microsoft.TestApi” NuGet 包后,此類即可使用,也可以使用其他方式來模擬單擊和指針移動。
    • 模式方法(TablePattern.GetColumnHeaders()、ValuePattern.SetValue()等)允許您快速找到所需的元素、設置新的控件值、執(zhí)行默認控件操作(例如單擊)等等,正如在Appium和UI自動化一節(jié)中提到的,這些方法在Appium中不可用。
    • 要獲得上下文菜單項,可以使用RootElements和TreeScope.Descendants。

    C#:

    AutomationElement menuItem = AutomationElement.RootElement.FindFirst(TreeScope.Descendants, new PropertyCondition(AutomationElement.NameProperty, "itemName"));
    ((InvokePattern)menuItem.GetCurrentPattern(InvokePattern.Pattern)).Invoke();

    VB.NET:

    Dim globalDriverOptions As AppiumOptions = New AppiumOptions()
    globalDriverOptions.AddAdditionalCapability("app", "Root")
    Dim globalDriver = New WindowsDriver(Of WindowsElement)(New Uri("http://127.0.0.1:4723"), globalDriverOptions)
    Dim menuItem = globalDriver.FindElementByName("ItemName")
    掃碼咨詢


    添加微信 立即咨詢

    電話咨詢

    客服熱線
    023-68661681

    TOP
    三级成人熟女影院,欧美午夜成人精品视频,亚洲国产成人乱色在线观看,色中色成人论坛 (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })();