如何使用Selenium和C#访问包装在".lazyload-wrapper"中的元素?



我正在为澳大利亚一个流行的五金店网站构建一个测试工具。我想让我的测试进入一个包含36个结果的页面(这个页面实际上写着"显示151个结果中的36个"),然后点击"添加到字符"。按下一个随机选择的产品。要做到这一点,我正在尝试使用CssSelector与driver.FindElements捕获页面中的所有36个按钮。
我面临的问题是,我只能得到前12个项目,因为其余24个项目都包装在class=lazyload-wrapper中,我只是无法让驱动程序捕获它们。我试过将页面滚动到底部,我试过插入线程。到处睡觉,我已经尝试过WebDriverWait,我还从ExpectedConditions中重新创建了一些过时的函数,使其工作,但我仍然只得到12个元素。
我已经阅读了这里和其他论坛上关于类似问题的几乎所有帖子,但我仍然被相同的12项所困扰。也许硒并不是完成此任务的最佳选择。如果有人能对这个挑战提出一些建议,我将不胜感激。
这是我用来与页面交互的类,请原谅我留下了所有注释掉的代码,这只是为了显示所有失败的尝试:

using System;
using System.Threading;
using OpenQA.Selenium;
using OpenQA.Selenium.Support.UI;
using System.Text.RegularExpressions;
using System.Collections.ObjectModel;
using System.Linq;
namespace Model.Pages
{
public class CarAccessoriesPage : BasePage
{
public CarAccessoriesPage(IWebDriver driver) : base(driver)
{
}
/// <summary>
/// Add a random product to the shopping cart.
/// </summary>
public CarAccessoriesPage AddRandomItemToCart()
{
// smooth scrolling down to force loading did not work
// IJavaScriptExecutor jsex = (IJavaScriptExecutor)driver;
// jsex.ExecuteScript("window.scrollTo({left:0, top:document.body.scrollHeight, behavior:'smooth'});");
WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(5));
// Scroll
IJavaScriptExecutor jsex = (IJavaScriptExecutor)driver;
jsex.ExecuteScript("window.scrollTo(0, document.body.scrollHeight);");
// maybe wait for the page to load? Did not work.
wait.Until(d => ((IJavaScriptExecutor)d).ExecuteScript("return document.readyState").Equals("complete"));

// Maybe inserting an ugly pause... no bueno
Thread.Sleep(3000);
// driver.FindElement(By.CssSelector("[data-index='35']")); // this element is not found
Random r = new Random();
int i = r.Next(0, GetPartialResultsCount());
// First approach throws OutOfRangeException because only 12 elements are found
// IWebElement itemElement = new WebDriverWait(driver, TimeSpan.FromSeconds(5))
//     .Until(d => d.FindElements(By.CssSelector("[data-locator='atcButton']")))[i];
// Implementing a function from ExpectedConditions deprecated in C#, no good enough
var elements = wait
.Until(PresenceOfAllElementsLocatedBy(By.CssSelector("[data-locator='atcButton']")));
// maybe trying access the article elements inside the wrapper... fail
// var elements = driver.FindElements(By.CssSelector("article.lazyload-wrapper "));
Console.WriteLine(elements.Count); // Always 12!!! I know there are better ways to log
IWebElement ranItem = elements[i]; // Throws an OutOfRangeException if i > 11
Thread.Sleep(1000);
// center the element so the tob and bottom banners don intercept the click
jsex.ExecuteScript("arguments[0].scrollIntoView({block: 'center'});", ranItem);
Thread.Sleep(1000);
ranItem.Click();
Thread.Sleep(1000); // important or last function in test does not work (assertion fails: expected != actual)
return this;
}
/// <summary>
/// Get the number of items in the shopping cart.
/// </summary>
public int GetCartCount()
{
return Int32.Parse(driver.FindElement(By.ClassName("cartItemCount")).Text);
}

private Func<IWebDriver, ReadOnlyCollection<IWebElement>> PresenceOfAllElementsLocatedBy(By locator)
{
return (driver) =>
{
try
{
var elements = driver.FindElements(locator);
return elements.Any() ? elements : null;
}
catch (StaleElementReferenceException)
{
return null;
}
};
}
/// <summary>
/// Generate a random int no larger than the Collection of products
/// in the page. But due to lazy loading, the number generated was causing an out of range exception.
/// </summary>
private int GetPartialResultsCount()
{
string partialResultsCount = driver.FindElement(By.CssSelector(".resultsCount > p")).Text;
return Int32.Parse(Regex.Match(partialResultsCount, @"d+").Value);
}
}
}

测试本身实际上非常直接:

using NUnit.Framework;
using Model.Pages;
namespace Tests
{
public class ShoppingCartTests : BaseTests
{
[Test]
public void ValidateCartItemsCount()
{
int cartCount = open<HomePage>()
.GoToCarAccessoriesPage()
.AddRandomItemToCart()
.AddRandomItemToCart()
.GetCartCount();
// Validate cart has 2 items
Assert.AreEqual(2, cartCount);
}
}
}

最后,我可能需要提到的是,如果我只考虑12个项目,测试将运行并工作,但它已经成为一种个人痴迷,以获得用户可以在页面上看到的所有项目。此外,我不确定我是否应该分享我正在测试的实际网站,或者这违背了社区规则或最佳实践。可能足以说这是澳大利亚最受欢迎的五金店,网站相当复杂。谢谢。

可以使用像

这样的xpath选择器找到包装的元素
"//*[contains(@class, 'lazyload-wrapper')]//button[contains(@data-locator, 'atcButton')]"

然后把结果加到你通常得到的结果上?

因此,经过多次尝试后,我找到了一种方法来使测试工作并捕获在lazyloaderdiv中包装的元素。它不是很优雅,我不完全确定为什么我需要滚动两次到浏览器来实际执行滚动,而且我也不确定为什么如果我删除一个暂停,元素会丢失。顺便说一下,我使用的暂停是简单的Thread.Sleep(1000),封装在一个helper类中。下面是代码:

using System;
using OpenQA.Selenium;
using OpenQA.Selenium.Support.UI;
using System.Collections.ObjectModel;
using System.Linq;
using System.Diagnostics;
using Model.Helpers;
namespace Model.Pages
{
public class CarAccessoriesPage : BasePage
{
public CarAccessoriesPage(IWebDriver driver) : base(driver)
{
}
/// <summary>
/// Add a random product to the shopping cart.
/// </summary>
public CarAccessoriesPage AddRandomItemToCart()
{
WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(5));
IJavaScriptExecutor jsex = (IJavaScriptExecutor)driver;
Random r = new Random();
// Wait for the page to load.
wait.Until(d => ((IJavaScriptExecutor)d).ExecuteScript("return document.readyState").Equals("complete"));
// Scroll
jsex.ExecuteScript("window.scrollTo(0, document.body.scrollHeight);");
// Slow down
BrowserHelper.Pause();
// Scroll again
jsex.ExecuteScript("window.scrollTo(0, document.body.scrollHeight);");
// Brake
BrowserHelper.Pause();
// Get all the 'add to cart' button elements
var elements = wait
.Until(PresenceOfAllElementsLocatedBy(By.CssSelector("[data-locator='atcButton']")));
// Test output
Trace.WriteLine($"Total products found: {elements.Count}");
// Select a random item
IWebElement ranItem = elements[r.Next(0, elements.Count)];
// Brake
BrowserHelper.Pause();
// Center the element so the top and bottom banners don't intercept the click
jsex.ExecuteScript("arguments[0].scrollIntoView({block: 'center'});", ranItem);
// Brake a little 
BrowserHelper.Pause();
// Click 'add to cart'
ranItem.Click();
// Brake again. Important or last function in test does not work (assertion fails: expected != actual)
BrowserHelper.Pause(); 
return this;
}
/// <summary>
/// Get the number of items in the shopping cart.
/// </summary>
public int GetCartCount()
{
return Int32.Parse(driver.FindElement(By.ClassName("cartItemCount")).Text);
}

private Func<IWebDriver, ReadOnlyCollection<IWebElement>> PresenceOfAllElementsLocatedBy(By locator)
{
return (driver) =>
{
try
{
var elements = driver.FindElements(locator);
return elements.Any() ? elements : null;
}
catch (StaleElementReferenceException)
{
return null;
}
};
}
}
}

最新更新