UI执行接口
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
package com.test.common.utils;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.openqa.selenium.*;
|
||||
import org.openqa.selenium.interactions.Actions;
|
||||
import org.openqa.selenium.support.ui.ExpectedConditions;
|
||||
@@ -13,6 +14,7 @@ import java.util.Set;
|
||||
/**
|
||||
* Selenium 工具类
|
||||
*/
|
||||
@Slf4j
|
||||
public class SeleniumUtils {
|
||||
|
||||
private WebDriver driver;
|
||||
@@ -238,9 +240,9 @@ public class SeleniumUtils {
|
||||
}
|
||||
|
||||
// 获取alert文本
|
||||
public String getAlertText() {
|
||||
return switchToAlert().getText();
|
||||
}
|
||||
// public String getAlertText() {
|
||||
// return switchToAlert().getText();
|
||||
// }
|
||||
|
||||
// 在alert中输入文本
|
||||
public void sendKeysToAlert(String text) {
|
||||
@@ -377,4 +379,480 @@ public class SeleniumUtils {
|
||||
driver.quit();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//Add SeleniumUtils -----
|
||||
|
||||
|
||||
/**
|
||||
* 根据句柄ID切换到指定窗口
|
||||
* @param handleId 窗口句柄ID
|
||||
*/
|
||||
public void switchToWindowByHandle(String handleId) {
|
||||
driver.switchTo().window(handleId);
|
||||
log.info("已切换到句柄ID为 {} 的窗口", handleId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据网页索引号切换到指定窗口
|
||||
* @param index 窗口索引(从0开始)
|
||||
*/
|
||||
public void switchToWindowByIndex(int index) {
|
||||
Set<String> handles = driver.getWindowHandles();
|
||||
if (index >= 0 && index < handles.size()) {
|
||||
String handle = handles.toArray(new String[0])[index];
|
||||
driver.switchTo().window(handle);
|
||||
log.info("已切换到索引为 {} 的窗口", index);
|
||||
} else {
|
||||
throw new NoSuchWindowException("无效的窗口索引: " + index);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换到初始窗口(第一个打开的窗口)
|
||||
*/
|
||||
public void switchToDefaultWindow() {
|
||||
Set<String> handles = driver.getWindowHandles();
|
||||
if (!handles.isEmpty()) {
|
||||
String firstHandle = handles.iterator().next();
|
||||
driver.switchTo().window(firstHandle);
|
||||
log.info("已切换回初始窗口");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 根据索引切换到指定frame
|
||||
* @param index frame索引(从0开始)
|
||||
*/
|
||||
public void switchToFrameByIndex(int index) {
|
||||
try {
|
||||
driver.switchTo().frame(index);
|
||||
log.info("已切换到索引为 {} 的frame", index);
|
||||
} catch (NoSuchFrameException e) {
|
||||
log.error("按索引切换frame失败: {}", e.getMessage());
|
||||
throw new RuntimeException("找不到索引为 " + index + " 的frame", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据定位方式切换到frame
|
||||
* @param locatorType 定位类型(id/name/xpath/css等)
|
||||
* @param locatorValue 定位值
|
||||
*/
|
||||
public void switchToFrameByLocator(String locatorType, String locatorValue) {
|
||||
By locator = getLocator(locatorType, locatorValue);
|
||||
try {
|
||||
WebElement frameElement = wait.until(ExpectedConditions.presenceOfElementLocated(locator));
|
||||
driver.switchTo().frame(frameElement);
|
||||
log.info("已切换到 {} = '{}' 的frame", locatorType, locatorValue);
|
||||
} catch (TimeoutException e) {
|
||||
log.error("等待frame元素超时: {} = '{}'", locatorType, locatorValue);
|
||||
throw new RuntimeException("找不到 " + locatorType + " = '" + locatorValue + "' 的frame元素", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 根据定位类型和值获取Selenium定位器
|
||||
* @param locatorType 定位类型(不区分大小写):
|
||||
* id/name/className/tagName/linkText/partialLinkText/css/xpath/label/value/index
|
||||
* @param locatorValue 定位值
|
||||
* @return By 定位器对象
|
||||
* @throws IllegalArgumentException 如果定位类型不支持
|
||||
*/
|
||||
public By getLocator(String locatorType, String locatorValue) {
|
||||
if (locatorValue == null || locatorValue.trim().isEmpty()) {
|
||||
throw new IllegalArgumentException("定位值不能为空");
|
||||
}
|
||||
|
||||
switch (locatorType.toLowerCase()) {
|
||||
case "id":
|
||||
return By.id(locatorValue);
|
||||
case "name":
|
||||
return By.name(locatorValue);
|
||||
case "classname":
|
||||
case "class":
|
||||
return By.className(locatorValue);
|
||||
case "tagname":
|
||||
case "tag":
|
||||
return By.tagName(locatorValue);
|
||||
case "linktext":
|
||||
case "link":
|
||||
return By.linkText(locatorValue);
|
||||
case "partiallinktext":
|
||||
case "partiallink":
|
||||
return By.partialLinkText(locatorValue);
|
||||
case "css":
|
||||
case "cssselector":
|
||||
return By.cssSelector(locatorValue);
|
||||
case "xpath":
|
||||
return By.xpath(locatorValue);
|
||||
case "label":
|
||||
// 通过label文本定位对应的元素
|
||||
return By.xpath(String.format("//label[contains(text(), '%s')]", locatorValue));
|
||||
case "value":
|
||||
// 通过value属性定位
|
||||
return By.xpath(String.format("//*[@value='%s']", locatorValue));
|
||||
case "index":
|
||||
// 索引定位需要特殊处理,通常结合其他定位方式使用
|
||||
try {
|
||||
int index = Integer.parseInt(locatorValue);
|
||||
return By.xpath(String.format("(//*)[%d]", index + 1)); // XPath索引从1开始
|
||||
} catch (NumberFormatException e) {
|
||||
throw new IllegalArgumentException("索引必须是数字: " + locatorValue);
|
||||
}
|
||||
default:
|
||||
throw new IllegalArgumentException("不支持的定位类型: " + locatorType +
|
||||
"。支持的定位类型: id,name,className,tagName,linkText," +
|
||||
"partialLinkText,css,xpath,label,value,index");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 处理弹窗
|
||||
* @param operationType 操作类型 (1=确定, 2=取消)
|
||||
* @param inputText 需要输入的文本 (可为null表示不输入)
|
||||
*/
|
||||
public void handleAlert(int operationType, String inputText) {
|
||||
try {
|
||||
Alert alert = wait.until(ExpectedConditions.alertIsPresent());
|
||||
|
||||
// 如果有输入文本
|
||||
if (inputText != null && !inputText.isEmpty()) {
|
||||
alert.sendKeys(inputText);
|
||||
log.info("已在弹窗中输入文本: {}", inputText);
|
||||
}
|
||||
|
||||
// 执行确认或取消操作
|
||||
if (operationType == 1) {
|
||||
alert.accept();
|
||||
log.info("已确认弹窗");
|
||||
} else if (operationType == 2) {
|
||||
alert.dismiss();
|
||||
log.info("已取消弹窗");
|
||||
} else {
|
||||
throw new IllegalArgumentException("无效的弹窗操作类型: " + operationType);
|
||||
}
|
||||
|
||||
} catch (NoAlertPresentException e) {
|
||||
log.error("没有找到弹窗", e);
|
||||
throw new RuntimeException("当前没有可操作的弹窗", e);
|
||||
} catch (TimeoutException e) {
|
||||
log.error("等待弹窗超时", e);
|
||||
throw new RuntimeException("等待弹窗出现超时", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取弹窗文本
|
||||
* @return 弹窗显示的文本内容
|
||||
*/
|
||||
public String getAlertText() {
|
||||
try {
|
||||
Alert alert = wait.until(ExpectedConditions.alertIsPresent());
|
||||
String text = alert.getText();
|
||||
log.info("获取到弹窗文本: {}", text);
|
||||
return text;
|
||||
} catch (Exception e) {
|
||||
log.error("获取弹窗文本失败", e);
|
||||
throw new RuntimeException("无法获取弹窗文本", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据定位方式和定位值查找元素
|
||||
* @param locType 定位方式 (如 "id", "xpath", "cssSelector" 等)
|
||||
* @param locValue 定位值
|
||||
* @return 找到的WebElement
|
||||
*/
|
||||
public WebElement findElement(String locType, String locValue) {
|
||||
try {
|
||||
By by = getLocator(locType, locValue);
|
||||
WebElement element = wait.until(ExpectedConditions.presenceOfElementLocated(by));
|
||||
return element;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("无法定位元素,定位方式:" + locType + ",定位值:" + locValue, e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 操作下拉框
|
||||
* @param element 下拉框元素
|
||||
* @param optionType 选项类型 (1=选项文本, 2=索引, 3=值)
|
||||
* @param optionValue 选项值
|
||||
*/
|
||||
public void selectDropdownOption(WebElement element, String optionType, String optionValue) {
|
||||
Select select = new Select(element);
|
||||
try {
|
||||
switch (optionType) {
|
||||
case "1": // 选项文本
|
||||
select.selectByVisibleText(optionValue);
|
||||
log.info("通过选项文本选择下拉框选项: {}", optionValue);
|
||||
break;
|
||||
case "2": // 索引
|
||||
int index = Integer.parseInt(optionValue);
|
||||
select.selectByIndex(index);
|
||||
log.info("通过索引选择下拉框选项: {}", index);
|
||||
break;
|
||||
case "3": // 值
|
||||
select.selectByValue(optionValue);
|
||||
log.info("通过值选择下拉框选项: {}", optionValue);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("不支持的选项类型: " + optionType);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("操作下拉框失败", e);
|
||||
throw new RuntimeException("操作下拉框失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 等待元素满足指定条件
|
||||
* @param locator 元素定位器
|
||||
* @param operate 操作类型:
|
||||
* 1=等待元素文本等于给定值
|
||||
* 2=等待元素存在
|
||||
* 3=等待元素显示
|
||||
* 4=等待元素不显示
|
||||
* 5=等待元素不存在
|
||||
* 6=等待元素可编辑
|
||||
* 7=等待元素不可编辑
|
||||
* @param awaitValue 等待的值(用于文本比较或自定义超时时间)
|
||||
* @param timeoutInMillis 默认超时时间(毫秒)
|
||||
*/
|
||||
public void waitForElement(By locator, int operate, String awaitValue, long timeoutInMillis) {
|
||||
WebDriverWait customWait = new WebDriverWait(driver, Duration.ofMillis(timeoutInMillis));
|
||||
|
||||
try {
|
||||
switch (operate) {
|
||||
case 1: // 等待元素文本等于给定值
|
||||
customWait.until(ExpectedConditions.textToBe(locator, awaitValue));
|
||||
log.info("元素文本已等于: {}", awaitValue);
|
||||
break;
|
||||
case 2: // 等待元素存在
|
||||
customWait.until(ExpectedConditions.presenceOfElementLocated(locator));
|
||||
log.info("元素已存在");
|
||||
break;
|
||||
case 3: // 等待元素显示
|
||||
customWait.until(ExpectedConditions.visibilityOfElementLocated(locator));
|
||||
log.info("元素已显示");
|
||||
break;
|
||||
case 4: // 等待元素不显示
|
||||
customWait.until(ExpectedConditions.invisibilityOfElementLocated(locator));
|
||||
log.info("元素已不显示");
|
||||
break;
|
||||
case 5: // 等待元素不存在
|
||||
boolean isAbsent = customWait.until(ExpectedConditions.not(
|
||||
ExpectedConditions.presenceOfElementLocated(locator)
|
||||
));
|
||||
log.info("元素已不存在");
|
||||
break;
|
||||
case 6: // 等待元素可编辑
|
||||
customWait.until(driver -> {
|
||||
WebElement element = driver.findElement(locator);
|
||||
return element.isEnabled() && !element.getAttribute("readonly").equals("true");
|
||||
});
|
||||
log.info("元素已可编辑");
|
||||
break;
|
||||
case 7: // 等待元素不可编辑
|
||||
customWait.until(driver -> {
|
||||
WebElement element = driver.findElement(locator);
|
||||
return !element.isEnabled() || element.getAttribute("readonly").equals("true");
|
||||
});
|
||||
log.info("元素已不可编辑");
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("不支持的等待操作类型: " + operate);
|
||||
}
|
||||
} catch (TimeoutException e) {
|
||||
log.error("等待元素超时,操作类型: {}, 定位器: {}", operate, locator);
|
||||
throw new RuntimeException("等待元素条件未满足: " + getOperationDescription(operate), e);
|
||||
}
|
||||
}
|
||||
|
||||
// 获取操作类型描述
|
||||
private String getOperationDescription(int operate) {
|
||||
switch (operate) {
|
||||
case 1: return "文本等于给定值";
|
||||
case 2: return "元素存在";
|
||||
case 3: return "元素显示";
|
||||
case 4: return "元素不显示";
|
||||
case 5: return "元素不存在";
|
||||
case 6: return "元素可编辑";
|
||||
case 7: return "元素不可编辑";
|
||||
default: return "未知操作";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行鼠标操作
|
||||
* @param locator 元素定位器
|
||||
* @param clickMethod 点击方式:
|
||||
* 1=单击(左键)
|
||||
* 2=单击(右键)
|
||||
* 3=双击
|
||||
* 4=按下
|
||||
* 5=弹起
|
||||
*/
|
||||
public void performMouseAction(By locator, int clickMethod) {
|
||||
try {
|
||||
WebElement element = wait.until(ExpectedConditions.elementToBeClickable(locator));
|
||||
|
||||
switch (clickMethod) {
|
||||
case 1: // 左键单击
|
||||
actions.click(element).perform();
|
||||
log.info("已执行左键单击操作");
|
||||
break;
|
||||
case 2: // 右键单击
|
||||
actions.contextClick(element).perform();
|
||||
log.info("已执行右键单击操作");
|
||||
break;
|
||||
case 3: // 双击
|
||||
actions.doubleClick(element).perform();
|
||||
log.info("已执行双击操作");
|
||||
break;
|
||||
case 4: // 按下
|
||||
actions.clickAndHold(element).perform();
|
||||
log.info("已执行鼠标按下操作");
|
||||
break;
|
||||
case 5: // 弹起
|
||||
actions.release(element).perform();
|
||||
log.info("已执行鼠标弹起操作");
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("不支持的鼠标操作类型: " + clickMethod);
|
||||
}
|
||||
} catch (TimeoutException e) {
|
||||
log.error("等待元素可点击超时,定位器: {}", locator);
|
||||
throw new RuntimeException("元素不可点击或未找到: " + locator, e);
|
||||
} catch (Exception e) {
|
||||
log.error("鼠标操作执行失败", e);
|
||||
throw new RuntimeException("鼠标操作失败: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行鼠标移动操作
|
||||
* @param locator 元素定位器
|
||||
* @param moveType 移动类型:
|
||||
* 1=鼠标移出元素
|
||||
* 2=鼠标移入元素
|
||||
*/
|
||||
public void performMouseMove(By locator, int moveType) {
|
||||
try {
|
||||
WebElement element = wait.until(ExpectedConditions.visibilityOfElementLocated(locator));
|
||||
|
||||
switch (moveType) {
|
||||
case 1: // 鼠标移出元素
|
||||
// 先移动到元素上,再移动到页面左上角
|
||||
actions.moveToElement(element)
|
||||
.moveByOffset(-element.getSize().getWidth(), -element.getSize().getHeight())
|
||||
.perform();
|
||||
log.info("已将鼠标从元素移出");
|
||||
break;
|
||||
case 2: // 鼠标移入元素(悬停)
|
||||
actions.moveToElement(element).perform();
|
||||
log.info("已将鼠标悬停在元素上");
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("不支持的鼠标移动类型: " + moveType);
|
||||
}
|
||||
} catch (TimeoutException e) {
|
||||
log.error("等待元素可见超时,定位器: {}", locator);
|
||||
throw new RuntimeException("元素不可见或未找到: " + locator, e);
|
||||
} catch (Exception e) {
|
||||
log.error("鼠标移动操作执行失败", e);
|
||||
throw new RuntimeException("鼠标移动操作失败: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 在指定元素中输入文本
|
||||
* @param locator 元素定位器
|
||||
* @param text 要输入的文本
|
||||
* @param clearFirst 是否先清空原有内容
|
||||
* @param inputMethod 输入方式:
|
||||
* 1=普通输入(sendKeys)
|
||||
* 2=通过JavaScript设置值
|
||||
* 3=模拟人工输入(带延迟)
|
||||
*/
|
||||
public void inputText(By locator, String text, boolean clearFirst, int inputMethod) {
|
||||
try {
|
||||
WebElement element = wait.until(ExpectedConditions.visibilityOfElementLocated(locator));
|
||||
|
||||
// 先清空内容(如果需要)
|
||||
if (clearFirst) {
|
||||
element.clear();
|
||||
log.info("已清空输入框内容");
|
||||
}
|
||||
|
||||
switch (inputMethod) {
|
||||
case 1: // 普通输入
|
||||
element.sendKeys(text);
|
||||
log.info("已通过sendKeys输入文本: {}", text);
|
||||
break;
|
||||
case 2: // JavaScript输入
|
||||
JavascriptExecutor js = (JavascriptExecutor) driver;
|
||||
js.executeScript("arguments[0].value = arguments[1]", element, text);
|
||||
log.info("已通过JavaScript设置文本: {}", text);
|
||||
break;
|
||||
case 3: // 模拟人工输入
|
||||
for (char c : text.toCharArray()) {
|
||||
element.sendKeys(String.valueOf(c));
|
||||
Thread.sleep(50); // 每个字符间隔50ms
|
||||
}
|
||||
log.info("已模拟人工输入文本: {}", text);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("不支持的输入方式: " + inputMethod);
|
||||
}
|
||||
} catch (TimeoutException e) {
|
||||
log.error("等待输入元素可见超时,定位器: {}", locator);
|
||||
throw new RuntimeException("输入元素不可见或未找到: " + locator, e);
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
log.error("模拟人工输入被中断");
|
||||
throw new RuntimeException("输入操作被中断", e);
|
||||
} catch (Exception e) {
|
||||
log.error("输入操作执行失败", e);
|
||||
throw new RuntimeException("输入操作失败: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 在可编辑段落中输入文本(如富文本编辑器)
|
||||
* @param locator 元素定位器
|
||||
* @param text 要输入的文本
|
||||
*/
|
||||
public void inputToEditableDiv(By locator, String text) {
|
||||
try {
|
||||
WebElement element = wait.until(ExpectedConditions.visibilityOfElementLocated(locator));
|
||||
element.click(); // 先点击激活
|
||||
element.sendKeys(text);
|
||||
log.info("已在可编辑段落中输入文本: {}", text);
|
||||
} catch (Exception e) {
|
||||
log.error("可编辑段落输入失败", e);
|
||||
throw new RuntimeException("可编辑段落输入失败: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空输入框内容
|
||||
* @param locator 元素定位器
|
||||
*/
|
||||
public void clearInput(By locator) {
|
||||
try {
|
||||
WebElement element = wait.until(ExpectedConditions.visibilityOfElementLocated(locator));
|
||||
element.clear();
|
||||
log.info("已清空输入框内容");
|
||||
} catch (Exception e) {
|
||||
log.error("清空输入框失败", e);
|
||||
throw new RuntimeException("清空输入框失败: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user