[英]C# Selenium WebDriverWait doesn't appear to be waiting
我正在尝试测试我正在执行搜索的页面。 执行搜索后,我首先获取 table 元素,获取 table 行,然后从行中获取一个或多个行来完成我的断言,从而拉出 SearchResults。
我尝试以多种不同的方式使用 WebDriverWaits,但均未成功。 我设法从搜索结果中找到 select 项目的唯一可靠方法是使用 Thread.Sleep(3000)。 我们正在为我们的表格使用 PrimeNG 库。 正在加载的特定表非常大,因此需要进行一些等待
Selenium 信息:
浏览器信息:
Web 驱动程序初始化代码:我通过创建单独的页面对象和页面驱动程序来拆分我的页面和自动化代码。 这以SpecFlow Books 示例为模型
基页 class
public class BasePage
{
protected IWebDriver Driver { get; }
protected WebDriverWait Wait;
public BasePage(IWebDriver driver)
{
Driver = driver;
Wait = new WebDriverWait(Driver, TimeSpan.FromSeconds(10));
}
public T As<T>() where T : BasePage
{
return (T)this;
}
public IWebElement PaginationFirst => Wait.Until(ExpectedConditions.ElementToBeClickable(By.CssSelector("button.p-paginator-first.p-paginator-element")));
public IWebElement PaginationLast => Wait.Until(ExpectedConditions.ElementToBeClickable(By.CssSelector("button.p-paginator-last.p-paginator-element")));
public IWebElement PaginationPageNumber => Wait.Until(ExpectedConditions.ElementToBeClickable(By.CssSelector("button.p-paginator-page.p-paginator-element.p-highlight")));
// Adding waits at this point meant the page elements were timing out due to not being in view.
public virtual IWebElement ResultTable()
{
Thread.Sleep(3000);
return Driver.FindElement(By.CssSelector("table"));
}
public virtual IEnumerable<IWebElement> TableLines()
{
return ResultTable().FindElements(By.CssSelector("tableRow"));
}
private void WaitUntilElementIsDisplayed(By locator)
{
WebDriverWait wait = new WebDriverWait(Driver, TimeSpan.FromSeconds(5));
bool Condition(IWebDriver d)
{
IWebElement e = d.FindElement(locator);
Debug.WriteLine(e.Displayed);
return e.Displayed;
}
wait.Until(Condition);
}
}
用户列表页 class
public class UserListPage : BasePage
{
//private readonly WebDriverWait _wait;
//private readonly IWebDriver _driver;
public UserListPage(IWebDriver driver) : base(driver)
{
PageFactory.InitElements(driver, this);
}
[FindsBy(How = How.Id, Using = "headingUsers")]
public IWebElement PageTitle;
[FindsBy(How = How.Id, Using = "addUser")]
public IWebElement AddUser;
public IWebElement TxtSearchEverywhere => Wait.Until(ExpectedConditions.ElementIsVisible(By.Id("txtSearchEverywhere")));
[FindsBy(How = How.Id, Using = "col-username")]
public IWebElement ColUsername;
[FindsBy(How = How.Id, Using = "col-lastname")]
public IWebElement ColLastname;
[FindsBy(How = How.Id, Using = "col-firstname")]
public IWebElement ColFirstname;
[FindsBy(How = How.Id, Using = "col-hpicpn")]
public IWebElement ColHpicpn;
[FindsBy(How = How.Id, Using = "col-facilitycount")]
public IWebElement ColFacilitycount;
[FindsBy(How = How.Id, Using = "colRoles")]
public IWebElement ColRoles;
[FindsBy(How = How.Id, Using = "col-identitycount")]
public IWebElement ColIdentitycount;
[FindsBy(How = How.Id, Using = "colHasLogin")]
public IWebElement ColHasLogin;
[FindsBy(How = How.Id, Using = "col-onlinelogin")]
public IWebElement ColOnlinelogin;
[FindsBy(How = How.Id, Using = "col-loginstatus")]
public IWebElement ColLoginstatus;
public IEnumerable<UserRow> SearchResults => TableLines().Skip(1).Select(r => new UserRow(r));
}
HTML 代码:
<head>
<body>
<app-root>
<header>
<nav>
<main>
<app-role class=""><button id="addRole" class="btn btn-primary float-right">Add New Role</button>
<h2 id="roleTitle" class="mb-4">Roles</h2>
<peg-admin-list>
<p-table currentpagereporttemplate="Showing {first} to {last} of {totalRecords} entries"
ng-reflect-global-filter-fields="roleName,userFriendlyName,user">
<div>
<div class="p-datatable-header ">
<div class="table-header "><span class="p-input-icon-left"><i class="pi pi-search"></i>
<input
id="txtSearchEverywhere" pinputtext="" type="search" placeholder="Search everywhere"
class="p-inputtext p-component"></span><span class="">
<p-selectbutton class="filter-flag">
<div role="group">
<div role="button" aria-pressed="true" title="isActive" aria-label="Active"
tabindex="0" aria-labelledby="Active">
<span class="p-button-label ">Active</span>
</div>
<div role="button" aria-pressed="false" title="isActive" aria-label="Inactive"
tabindex="0" aria-labelledby="Inactive">
<span class="p-button-label ">Inactive</span>
</div>
<div role="button" aria-pressed="false" title="isSystem" aria-label="Is System"
tabindex="0" aria-labelledby="Is System">
<span class="p-button-label ">Is System</span>
</div>
</div>
</p-selectbutton>
</span>
</div>
</div>
<div class="p-datatable-wrapper ">
<table role="grid">
<thead class="p-datatable-thead">
<tr class="">
<th>
Role <p-sorticon ng-reflect-field="roleName"><i
class="p-sortable-column-icon pi pi-fw pi-sort-alt"
ng-reflect-ng-class="[object Object]"></i>
</p-sorticon>
</th>
<th> Reviewer Role Description <p-sorticon ng-reflect-field="userFriendlyName"><i
class="p-sortable-column-icon pi pi-fw pi-sort-alt"
ng-reflect-ng-class="[object Object]"></i>
</p-sorticon>
</th>
<th>
Users Count <p-sorticon ng-reflect-field="usersCount"><i
class="p-sortable-column-icon pi pi-fw pi-sort-alt"
ng-reflect-ng-class="[object Object]"></i>
</p-sorticon>
</th>
<th> Is
Active <p-sorticon ng-reflect-field="isActive"><i
class="p-sortable-column-icon pi pi-fw pi-sort-alt"
ng-reflect-ng-class="[object Object]"></i>
</p-sorticon>
</th>
<th> Is System <p-sorticon ng-reflect-field="isSystem"><i
class="p-sortable-column-icon pi pi-fw pi-sort-alt"
ng-reflect-ng-class="[object Object]"></i>
</p-sorticon>
</th>
<th>
Role Type <p-sorticon ng-reflect-field="roleType"><i
class="p-sortable-column-icon pi pi-fw pi-sort-alt"
ng-reflect-ng-class="[object Object]"></i>
</p-sorticon>
</th>
</tr>
</thead>
<tbody class="p-datatable-tbody">
<tr class="tableRow ">
<td class="txtroleName">
<div>A new Role Test Test with a new name </div>
</td>
<td class="txtuserFriendlyName">
<div>Check if can be seen in Admin2 bob bob bobby bob bob bob </div>
</td>
<td class="txtusersCount">
<div>4</div>
</td>
<td class="txtisActive">
<div> Yes </div>
</td>
<td class="txtisSystem">
<div> No </div>
</td>
<td class="txtroleType">
<div> Admin </div>
</td>
</tr>
<tr class="tableRow">
<td class="txtroleName">
<div>A new Test Role</div>
</td>
<td class="txtuserFriendlyName">
<div>Testing changes</div>
</td>
<td class="txtusersCount">
<div>2</div>
</td>
<td class="txtisActive">
<div>Yes</div>
</td>
<td class="txtisSystem">
<div>No</div>
</td>
<td class="txtroleType">
<div>Admin</div>
</td>
</tr>
<tr class="tableRow ">
<td class="txtroleName">
<div>ADMS</div>
</td>
<td class="txtuserFriendlyName">
<div> </div>
</td>
<td class="txtusersCount">
<div>2</div>
</td>
<td class="txtisActive">
<div>Yes</div>
</td>
<td class="txtisSystem">
<div>No</div>
</td>
<td class="txtroleType">
<div>Admin</div>
</td>
</tr>
</tbody>
</table>
</div>
<p-paginator>
<span class="p-paginator-current ">Showing 1 to 10 of 66
entries</span>
<button type="button" pripple=""
class="p-paginator-first p-paginator-element p-link p-disabled p-ripple " disabled=""
ng-reflect-ng-class="[object Object]"><span
class="p-paginator-icon pi pi-angle-double-left"></span></button>
<button type="button" pripple=""
class="p-paginator-prev p-paginator-element p-link p-disabled p-ripple" disabled=""
ng-reflect-ng-class="[object Object]">
<span class="p-paginator-icon pi pi-angle-left"></span>
</button>
<span class="p-paginator-pages ">
<button type="button">1</button>
<button type="button">2</button>
<button type="button">3</button>
<button type="button">4</button>
<button type="button">5</button>
</span>
<button type="button">
<span class="p-paginator-icon pi pi-angle-right"></span>
</button>
<button type="button">
<span class="p-paginator-icon pi pi-angle-double-right"></span>
</button>
<p-dropdown styleclass="p-paginator-rpp-options">
<div class="ng-tns-c56-0 p-paginator-rpp-options p-dropdown p-component"
ng-reflect-ng-class="[object Object]">
<div class="p-hidden-accessible ng-tns-c56-0"><input type="text"></div>
<span>10</span>
<div role="button">
<span class="p-dropdown-trigger-icon ng-tns-c56-0 pi pi-chevron-down"
ng-reflect-ng-class="pi pi-chevron-down"></span>
</div>
</div>
</p-dropdown>
</div>
</p-paginator>
</div>
</p-table>
</peg-admin-list>
</app-role>
</main>
</nav>
</header>
</app-root>
</body>
</head>
浏览器驱动 class
public class BrowserDriver : IDisposable
{
private readonly BrowserDriverFactory _browserSeleniumDriverFactory;
private readonly IConfiguration _config;
private readonly ConfigurationDriver _configurationDriver;
private readonly Lazy<IWebDriver> _currentWebDriverLazy;
private readonly Lazy<WebDriverWait> _waitLazy;
private readonly TimeSpan _waitDuration = TimeSpan.FromSeconds(30);
private bool _isDisposed;
public BrowserDriver(BrowserDriverFactory browserSeleniumDriverFactory, ConfigurationDriver configurationDriver, IConfiguration config)
{
_browserSeleniumDriverFactory = browserSeleniumDriverFactory;
_config = config;
_configurationDriver = configurationDriver;
_currentWebDriverLazy = new Lazy<IWebDriver>(GetWebDriver);
_waitLazy = new Lazy<WebDriverWait>(GetWebDriverWait);
}
public IWebDriver Current => _currentWebDriverLazy.Value;
public WebDriverWait Wait => _waitLazy.Value;
private WebDriverWait GetWebDriverWait()
{
return new WebDriverWait(Current, _waitDuration);
}
private IWebDriver GetWebDriver()
{
return _browserSeleniumDriverFactory.GetForBrowser(_configurationDriver.Mode);
}
public void Dispose()
{
if (_isDisposed)
{
return;
}
if (_currentWebDriverLazy.IsValueCreated)
{
Current.Quit();
}
_isDisposed = true;
}
public void Navigate(string urlPart = "")
{
if (!Current.Url.EndsWith(urlPart))
{
var baseUrl = _config.GetConnectionString("BaseUrl");
Current.Manage().Window.Maximize();
Current.Navigate().GoToUrl($"{baseUrl}/{urlPart}");
RetryHelper.WaitFor(() => Current.Url.EndsWith(urlPart));
}
}
}
RoleListPageDriver class
public class RoleListPageDriver : IRoleListDriver
{
private readonly BrowserDriver _browserDriver;
private readonly IHomeDriver _homeDriver;
private readonly RoleListPage _roleListPage;
public IWebElement[] HeaderColumns;
public RoleListPageDriver(BrowserDriver browserDriver, IHomeDriver homeDriver)
{
_browserDriver = browserDriver;
_homeDriver = homeDriver;
_roleListPage = new RoleListPage(_browserDriver.Current);
HeaderColumns = new[]
{
_roleListPage.ColRole,
_roleListPage.ColDescription,
_roleListPage.ColUserCount,
_roleListPage.ColIsActive,
_roleListPage.ColIsSystem,
_roleListPage.ColRoleType
};
}
[AfterScenario()]
public void Dispose()
{
_browserDriver.Dispose();
}
public bool Exists(IWebElement element)
{
return element.ElementExists();
}
public bool IsLoaded()
{
return _roleListPage.PageTitle.ElementExists();
}
public void Navigate()
{
_browserDriver.Navigate("role");
}
public void Search(string searchTerm)
{
_roleListPage.TxtSearchEverywhere.Search(searchTerm);
}
public void SortColumn(string selector)
{
var element = HeaderColumns.Where(h => h.Text == selector).ToList();
if (element.Count > 0)
{
element[0].Clicks();
}
}
public bool TableColumnHeadersExist()
{
return HeaderColumns.All(header => header.ElementExists());
}
public void RoleMenuExists()
{
_roleListPage.ColRole.ElementExists();
}
public bool RoleTitleExists()
{
return _roleListPage.PageTitle.ElementExists();
}
public void RoleListSortResultShouldBeAscendingDescending(string order)
{
switch (order)
{
case Constants.ListOrder.Ascending:
_roleListPage.SearchResults.Take(5).Should().BeInAscendingOrder(r => r.RoleName);
break;
case Constants.ListOrder.Descending:
_roleListPage.SearchResults.Take(5).Should().BeInDescendingOrder(r => r.RoleName);
break;
}
}
public void SearchResultsShouldContainMatchingItems(string searchTerm)
{
var searchResult = _roleListPage.SearchResults.Take(1).Select(s => s.RoleName).ToList();
searchResult.All(str => str.Contains(searchTerm)).Should().BeTrue();
}
public void ClickOnFilterButton(string filterButton)
{
switch (filterButton)
{
case Constants.FilterButton.ActiveRoles:
_roleListPage.BtnActive.Clicks();
break;
case Constants.FilterButton.InactiveRoles:
_roleListPage.BtnInactive.Clicks();
break;
case Constants.FilterButton.IsSystemRoles:
_roleListPage.BtnIsSystem.Clicks();
break;
}
}
public void AssertOnlyActiveRolesDisplayed()
{
_roleListPage.SearchResults.Take(5).All(r => r.IsActive == "Yes").Should().BeTrue();
}
public void AssertOnlyInactiveRolesDisplayed()
{
_roleListPage.SearchResults.Take(1).All(r => r.IsActive == "No").Should().BeTrue();
}
public void AssertOnlySystemRolesDisplayed()
{
_roleListPage.SearchResults.Take(1).All(r => r.IsSystem == "Yes").Should().BeTrue();
}
public void ClickOnLastPaginationButton()
{
_roleListPage.PaginationLast.Clicks();
}
public void AssertLastPageIsDisplayed()
{
int.Parse(_roleListPage.PaginationPageNumber.Text).Should().BeGreaterOrEqualTo(3);
}
public void AssertPageTitleIsDisplayed(string title)
{
_roleListPage.PageTitle.Text.Should().BeEquivalentTo(title);
}
}
BasePage.TableLines()
方法可以使用显式等待:
public virtual IEnumerable<IWebElement> TableLines()
{
var wait = new WebDriverWait(Driver, TimeSpan.FromSeconds(30));
var locator = By.CssSelector("table > tbody > tr");
return wait.Until(ExpectedConditions.PresenceOfAllElementsLocatedBy(locator));
}
这假设在加载数据时对表进行了某种更改。 在不知道结果加载时表格会发生什么的情况下,这就是我所能提供的。
如果在滚动时动态添加表格行,那最终会是一个完全不同的解决方案。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.