UI执行接口

This commit is contained in:
2025-04-27 14:19:12 +08:00
parent 83109f3170
commit d71d5c070e
6 changed files with 1126 additions and 71 deletions

View File

@@ -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);
}
}
}