我的任务
步骤:
我可以执行第 1 步和第 2 步,但找不到第 3 步和第 4 步的 WebElements。
我的代码
ChromeOptions options = new ChromeOptions();
options.addArguments("--remote-allow-origins=*");
options.addArguments("--disable-notifications");
WebDriver driver = new ChromeDriver(options);
driver.get("https://www.homedepot.com/");
WebElement First = driver.findElement(By.xpath("(//a[@data-id='departmentsFlyout'])[1]"));
WebElement Second = driver.findElement(By.xpath("(//a[@data-level='Heating & Cooling'])[1]"));
// cannot able to find the below Third and Fourth WebElements//
WebElement Third = driver.findElement(By.xpath("//a[@title='Air Conditioners']"));
WebElement Fourth = driver.findElement(By.xpath("//a[@href='//www.homedepot.com/b/Heating-Venting-Cooling-Air-Conditioners-Coolers-Air-Conditioners-Portable-Air-Conditioners/N-5yc1vZc4m4']"));
Actions act = new Actions(driver);
act.moveToElement(First).build().perform();
act.moveToElement(Second).build().perform();
act.moveToElement(Third).build().perform();
act.moveToElement(Fourth).click().build().perform();
主要问题是元素 3 和 4 在您打开创建它们的导航面板之前不存在。这就是为什么你一开始就找不到 3 和 4 的原因。
另一个问题是最后的点击被另一个元素拦截,直到导航菜单完全打开。这需要一些特殊处理过去只是等待可点击。
改进建议:
你的定位器过于复杂了。
(//a[@data-id='departmentsFlyout'])[1]
可以很容易地成为 //a[@data-id='departmentsFlyout']
并且仍然有效。或者更好的是,让它成为一个 CSS 选择器 a[data-id='departmentsFlyout']
。 CSS 选择器语法更短且更易于阅读,执行速度更快,并且得到了广泛浏览器的更好支持。 XPath 有它的位置,但根据我的经验,我的定位器优先顺序是 1. ID,2. CSS 选择器,然后是 3. XPath,如果需要的话。
你不需要
.build()
动作,你可以.perform()
它们,例如
act.moveToElement(First).perform();
您的定位器不一致。它们仍然有效,但注意并使用一致性可以使您的工作变得容易得多。最后三个定位器都可以有形式,
//a[@title='<Category>']
或 CSS 选择器a[title='<Category>']
.
最后的点击不需要是Action,做成常规点击即可。根据我的经验,Actions 的行为并不一致,所以除非绝对必要,否则我会避免使用它们。
我添加了一个
click()
支持方法,它负责等待可点击并处理ElementClickInterceptedException
和StaleElementReferenceException
。
为了确保每个步骤都有效,我们将
.findElement()
链接起来,然后为每个元素链接 .moveToElement()
,例如
WebElement First = wait.until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector("a[data-id='departmentsFlyout']")));
act.moveToElement(First).perform();
你会注意到第一个、第二个和第三个元素的处理方式完全相同,除了定位器。它们是重构为处理等待可见然后悬停动作的方法的主要候选对象。
最终的工作代码看起来像
import java.time.Duration;
import java.time.LocalDateTime;
import org.openqa.selenium.By;
import org.openqa.selenium.ElementClickInterceptedException;
import org.openqa.selenium.StaleElementReferenceException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.interactions.Actions;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
public class Ques11 {
static WebDriver driver;
public static void main(String[] args) {
ChromeOptions options = new ChromeOptions();
options.addArguments("--remote-allow-origins=*");
options.addArguments("--disable-notifications");
driver = new ChromeDriver(options);
driver.manage().window().maximize();
driver.get("https://www.homedepot.com/");
hover(By.cssSelector("a[data-id='departmentsFlyout']"), 10);
hover(By.cssSelector("a[title='Heating & Cooling']"), 10);
hover(By.cssSelector("a[title='Air Conditioners']"), 10);
click(By.cssSelector("a[title='Portable Air Conditioners']"), 10);
}
public static void click(By locator, int timeOutInSeconds) {
LocalDateTime now = LocalDateTime.now();
while (LocalDateTime.now().isBefore(now.plusSeconds(timeOutInSeconds))) {
try {
new WebDriverWait(driver, Duration.ofSeconds(timeOutInSeconds)).until(ExpectedConditions.elementToBeClickable(locator)).click();
return;
} catch (ElementClickInterceptedException | StaleElementReferenceException e) {
// do nothing, loop again
}
}
}
public static void hover(By locator, int timeOutInSeconds) {
WebElement e = new WebDriverWait(driver, Duration.ofSeconds(timeOutInSeconds)).until(ExpectedConditions.visibilityOfElementLocated(locator));
new Actions(driver).moveToElement(e).perform();
}
}