完成元素获取类的改造

This commit is contained in:
彭宇琦 2020-10-16 18:28:17 +08:00
parent 5753292621
commit 296cf3e334
41 changed files with 1758 additions and 4775 deletions

View File

@ -32,9 +32,9 @@
</sheet>
<sheet name="错误记录" freeze="4">
<column id="id" name="运行记录编号" wide="8" align="center"/>
<column id="class_name" name="类名" wide="25" align="left"/>
<column id="method_name" name="方法名" wide="15" align="left"/>
<column id="id" name="运行记录编号" wide="8" align="center" row_text='1'/>
<column id="class_name" name="类名" wide="25" align="left" row_text='1'/>
<column id="method_name" name="方法名" wide="15" align="left" row_text='1'/>
<column id="error_step" name="错误步骤" wide="25" align="left" index='true' row_text='1'/>
<column id="error_class" name="异常类" wide="25" align="left" index='true' row_text='1'/>
<column id="error_information" name="异常信息" wide="25" align="left" index='true' row_text='1'/>

View File

@ -10,10 +10,10 @@ import com.alibaba.fastjson.JSONObject;
* <p><b>文件名</b>Page.java</p>
* <p><b>用途</b>用于存储对浏览器加载的页面信息以及页面操作</p>
* <p><b>编码时间</b>2020年4月10日上午8:02:45</p>
* <p><b>修改时间</b>2020年4月10日上午8:02:45</p>
* <p><b>修改时间</b>2020年10月12日下午8:02:45</p>
* @author 彭宇琦
* @version Ver1.0
* @since JDK 12
* @since JDK 8
*
*/
public class Page {
@ -134,6 +134,53 @@ public class Page {
return pageInformationJson;
}
/**
* 定位到弹框上并且点击确定按钮并返回弹框上的文本
*
* @return 弹框上的文本
*/
public String alertAccept() {
String text = alertGetText();
brower.getDriver().switchTo().alert().accept();
return text;
}
/**
* 定位到弹框上并且点击取消按钮并返回弹框上的文本
*
* @return 弹框上的文本
*/
public String alertDimiss() {
String text = alertGetText();
brower.getDriver().switchTo().alert().dismiss();
return text;
}
/**
* 定位到弹框上并且在其文本框中输入信息
*
* @param content 需要输入的信息
* @return 弹框上的文本
*/
public String alertInput(String content) {
String text = alertGetText();
brower.getDriver().switchTo().alert().sendKeys("");
return text;
}
/**
* 获取弹框上的文本
*
* @return 弹框上的文本
*/
public String alertGetText() {
return brower.getDriver().switchTo().alert().getText();
}
/**
* 用于通过浏览器加载页面并根据页面断言返回页面是否加载成功若未设置断言则无论
* 页面是否成功加载均返回true

View File

@ -1,23 +1,17 @@
package pres.auxiliary.work.selenium.element;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.NoSuchElementException;
import org.openqa.selenium.By;
import org.openqa.selenium.TimeoutException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.WebDriverWait;
import pres.auxiliary.work.selenium.brower.AbstractBrower;
import pres.auxiliary.work.selenium.brower.Page;
import pres.auxiliary.work.selenium.xml.ByType;
import pres.auxiliary.work.selenium.xml.ReadXml;
import pres.auxiliary.work.selenium.location.AbstractRead;
import pres.auxiliary.work.selenium.location.ByType;
/**
* <p><b>文件名</b>AbstractElement.java</p>
@ -33,63 +27,50 @@ import pres.auxiliary.work.selenium.xml.ReadXml;
*/
public abstract class AbstractBy {
/**
* 用于存储浏览器的WebDriver对象
* 存储通过信息获取到的页面元素对象
*/
WebDriver driver;
protected List<WebElement> elementList;
/**
* 用于指向存储控件定位方式的xml文件
* 用于存储元素信息
*/
static ReadXml xml;
protected ElementData elementData;
/**
* 存储元素的定位方式读取类对象由于多个子类之间需要互通故此处标记为static
*/
protected static AbstractRead read;
/**
* 用于存储浏览器对象
*/
AbstractBrower brower;
protected AbstractBrower brower;
/**
* 存储单个控件的等待时间
* 存储当前定位的窗体层级由于多个子类之间需要互通故此处标记为static
*/
private HashMap<String, Long> controlWaitTime = new HashMap<String, Long>();
/**
* 用于存储当前浏览器窗口的Handles值
*/
private String browserHandles;
/**
* 存储当前定位的窗体层级由于多个子类之间需要相互通信故此处标记为static
*/
private static ArrayList<String> iframeNameList = new ArrayList<>();
private static ArrayList<ElementData> nowIframeList = new ArrayList<>();
/**
* 用于当元素获取失败时执行的方法
*/
ExceptionAction action;
protected ExceptionAction action;
/**
* 用于存储元素通用的等待时间默认5秒
*/
private long waitTime = 5;
private long globalWaitTime = 5;
/**
* 控制是否自动切换窗体由于通过Event类调用时会构造另一个事件类
*/
boolean isAutoSwitchIframe = true;
protected boolean isAutoSwitchIframe = true;
/**
* 通过{@link WebDriver}对象进行构造
*
* @param driver {@link WebDriver}对象
*/
public AbstractBy(WebDriver driver) {
this.driver = driver;
browserHandles = this.driver.getWindowHandle();
}
/**
* 通过浏览器对象{@link AbstractBrower}进行构造
* @param brower {@link AbstractBrower}对象
* 通过元素信息类对象{@link ElementData}构造对象
* @param brower {@link AbstractBrower}类对象
* @param elementData {@link ElementData}类对象
*/
public AbstractBy(AbstractBrower brower) {
this.brower = brower;
this.driver = brower.getDriver();
}
/**
@ -106,56 +87,31 @@ public abstract class AbstractBy {
public void clearAction() {
this.action = null;
}
/**
* 用于设置元素等待时间默认时间为5秒
*
* @param waitTime 元素等待时间
*/
public void setWaitTime(long waitTime) {
this.waitTime = waitTime;
}
/**
* 用于对符合正则表达式的控件名称设置等待时间
* 用于设置元素的默认等待时间
*
* @param regex 正则表达式
* @param waitTime 等待时间
* @param waitTime 元素的默认等待时间
*/
public void setContorlWaitTime(String regex, long waitTime) {
controlWaitTime.put(regex, waitTime);
public void setWaitTime(long waitTime) {
this.globalWaitTime = waitTime;
}
/**
* 设置是否自动切换窗体
* @param switchIframe 是否自动切换窗体
* @param isAutoSwitchIframe 是否自动切换窗体
*/
public void setAutoSwitchIframe(boolean isAutoSwitchIframe) {
this.isAutoSwitchIframe = isAutoSwitchIframe;
}
/**
* 该方法用于返回浏览器的WebDriver对象
*
* @return 浏览器的WebDriver对象
*/
public WebDriver getDriver() {
return driver;
}
/**
* 用于设置指向存储元素定位方式的xml文件对象并根据传参判断窗体是否需要回到顶层
* @param xmlFile 存储元素定位方式的xml文件对象
* @param isBreakRootFrame 是否需要将窗体切回到顶层
*/
public void setXmlFile(File xmlFile, boolean isBreakRootFrame) {
if (xml == null) {
xml = new ReadXml(xmlFile);
} else {
xml.setXmlPath(xmlFile);
}
public void setReadMode(AbstractRead read, boolean isBreakRootFrame) {
AbstractBy.read = read;
if (isBreakRootFrame) {
switchRootFrame();
@ -167,447 +123,104 @@ public abstract class AbstractBy {
*/
public void switchRootFrame() {
//切换窗口至顶层
driver.switchTo().defaultContent();
//清空iframeNameList中的内容
iframeNameList.clear();
brower.getDriver().switchTo().defaultContent();
//清空nowIframeNameList中的内容
nowIframeList.clear();
}
/**
* 该方法用于将窗体切换到上一层父层若当前层只有一层则调用方法后切回顶层
* 若当前层为最顶层时则该方法将使用无效
*
* @param count 需要切换父层的次数
*/
public void switchParentFrame() {
//若iframeNameList大于1层则向上切换窗体
if (iframeNameList.size() > 1) {
driver.switchTo().parentFrame();
iframeNameList.remove(iframeNameList.size() - 1);
} else if (iframeNameList.size() == 1) {
//若iframeNameList等于1层则调用切换至顶层的方法
switchRootFrame();
} else {
//若iframeNameList小于1层则不做操作
return;
}
public void switchParentFrame(int count) {
//判断count是否小于等于0若小于等于0则不进行切换
if (count <= 0) {
return;
}
//判断count是否大于iframeElementList中所有元素的个数若大于则切回至顶层
if (count > nowIframeList.size()) {
switchRootFrame();
return;
}
//循环对窗体进行切换并移除nowIframeNameList中最后一个元素
for (int index = 0; index < count; index++) {
brower.getDriver().switchTo().parentFrame();
nowIframeList.remove(nowIframeList.size() - 1);
}
}
/**
* <p>
* 通过传入在xml文件中的控件名称到类中指向的xml文件中查找控件
* 名称对应的定位方式或直接传入xpath与css定位方式
* 根据定位方式对相应的窗体进行定位当传入的窗体为当前窗体的前层父层窗体时
* 通过该方法将调用切换父层的方法将窗体切换到父层上例如<br>
* 当前存在f1, f2, f3, f4四层窗体则调用方法<br>{@code
* switchFrame("f2");
* }<br>
* 此时窗体将回到f2层无需再从顶层开始向下切换
* </p>
* <p>
* 若传入该方法的名称存在于xml文件中且该元素存在父窗体时调用
* 该方法会从xml文件中获取相应所有父窗体并对相应的父窗体进行切换
* 从而达到无须切换父窗体的目的例如存在以下xml文件片段<pre>{@code
* ...
* <iframe name='f1'>
* <xpath>...</xpath>
* <iframe name='f2'>
* <xpath>...</xpath>
* <iframe name='f3'>
* <xpath>...</xpath>
* </iframe>
* </iframe>
* </iframe>
* ...
* }</pre>
* 当调用该方法<br>{@code
* switchFrame("f3");
* }<br>
* 则会先将窗体从f1开始切换至窗体f2最后再切换为窗体f3
* </p>
* 该方法用于根据iframe_id来切换窗体
*
* @param name 窗体的名称或xpath与css定位方式
* @param name 窗体的名称
*/
public void switchFrame(String name) {
switchFrame(new ElementInformation(name, null, ElementType.COMMON_ELEMENT));
public void switchFrame(ElementData iframeElementData) {
brower.getDriver().switchTo().frame(recognitionElement(iframeElementData).get(0));
nowIframeList.add(iframeElementData);
}
/**
* 通过传入在xml文件中的控件名称到类中指向的xml文件中查找控件
* 名称对应的定位方式或直接传入xpath与css定位方式
* 根据定位方式对相应的窗体进行定位通过该方法可以指定使用的定位类型一般用于
* 传入非xml文件的元素也可用于指定xml文件元素的定位方式其他说明参见{@link #switchFrame(String)}
* @param name 窗体的名称或xpath与css定位方式
* @param byType 元素的定位方式
* @see #switchFrame(String)
* 用于根据元素的窗体层级关系列表对元素的窗体进行切换并存储当前切换的窗体
* @param iframeNameList 元素所有窗体名称集合
*/
public void switchFrame(String name, ByType byType) {
switchFrame(new ElementInformation(name, byType, ElementType.COMMON_ELEMENT));
}
/**
* 切换窗体的底层方法
* @param elementInformation 元素信息类对象
*/
private void switchFrame(ElementInformation elementInformation) {
List<String> nameList = new ArrayList<String>();
//判断传入的元素名称是否存在于xml文件中若存在则将该元素的父层名称存储至nameList
if (isXmlElement(elementInformation.name) && isAutoSwitchIframe) {
nameList.addAll(getParentFrameName(elementInformation.name));
}
//调用切换窗体的方法
switchFrame(nameList);
driver.switchTo().frame(driver.findElement(recognitionElement(elementInformation)));
//切换窗体
iframeNameList.add(elementInformation.name);
}
/**
* 通过传入在xml文件中的控件名称到类中指向的xml文件中查找控件
* 名称对应的定位方式或直接传入xpath与css定位方式
* 根据定位方式对相应的窗体进行定位当传入的窗体为当前窗体的前层父层窗体时
* 通过该方法将调用切换父层的方法将窗体切换到父层上例如<br>
* 当前存在f1, f2, f3, f4四层窗体则调用方法<pre>{@code
* List<String> nameList = new ArrayList<String>();
* nameList.add("f2");
* switchFrame(nameList);
* }</pre>
* 此时窗体将回到f2层无需再从顶层开始向下切换<br>
* <b>注意</b>
* <ol>
* <li>窗体的切换按照从前向后的顺序进行切换切换顺序不能相反</li>
* <li>传入的参数若在xml文件中且存在父窗体调用该方法也不会对窗体进行切换</li>
* </ol>
*
* @param elementInformationList 存储窗体的名称或xpath与css定位方式的List集合
*/
void switchFrame(List<String> frameNameList) {
protected void autoSwitchFrame(List<String> iframeNameList) {
//若传参为空则切回到顶层
if (frameNameList.isEmpty()) {
switchRootFrame();
return;
}
//若原窗体和需要切换的窗体的最后一个元素一致则无需切换
if (!iframeNameList.isEmpty() && frameNameList.get(frameNameList.size() - 1).equals(iframeNameList.get(iframeNameList.size() - 1))) {
return;
}
//若需要切换的窗体第一层均不在iframeNameList时则需要切回到顶层
if (!iframeNameList.contains(frameNameList.get(0))) {
switchRootFrame();
}
//若不为空则列表进行切换
frameNameList.forEach(frameName -> {
//判断name指向的窗体是否在iframeNameList中若存在则向上切换父层直到切换到name指向的窗体若不存在则直接切换并添加窗体名称
if (iframeNameList.contains(frameName)) {
//获取name窗体在iframeNameList中的位置
int index = iframeNameList.indexOf(frameName);
//获取需要向上切换窗体的次数公式为推断出来
int count = iframeNameList.size() - index - 1;
for (int i = 0; i < count; i++) {
switchParentFrame();
}
} else {
//切换窗体
driver.switchTo().frame(driver.findElement(recognitionElement(new ElementInformation(frameName, null, ElementType.COMMON_ELEMENT))));
iframeNameList.add(frameName);
}
});
}
/**
* 该方法用于将窗口切换回最原始的窗口上
*/
public void switchOldWindow() {
driver.switchTo().window(browserHandles);
}
/**
* 该方法可根据控件名称之后对比每一个弹窗若某一个弹窗上存在元素名对应的元素则返回相应
* 窗口的WebDriver对象,若无新窗口则返回当前的窗口的WebDriver对象当所有的窗体都
* 不包含相应的元素时则抛出NoSuchWindownException异常
*
* @param controlName 控件名称
* @throws NoSuchWindownException 窗口未找到时抛出的异常
*/
public void switchWindow(String controlName) {
Set<String> handles = driver.getWindowHandles();
// 判断是否只存在一个窗体若只存在一个则直接返回当前浏览器的WebDriver对象
if (handles.size() == 1) {
return;
}
List<Page> pageList = new ArrayList<>();
//若浏览器对象存在则将已打开的页面的handle进行存储优先遍历新打开的标签
if (brower != null) {
pageList.addAll(brower.getOpenPage());
}
//移除已打开的窗口
handles.removeAll(pageList.stream().map(page -> {
return page.getHandle();
}).collect(Collectors.toList()));
// 循环获取所有的页面handle
for (String newWinHandle : handles) {
//切换窗口并查找元素是否在窗口上若存在则结束切换
driver.switchTo().window(newWinHandle);
try {
//构造信息因为在构造过程中会判断元素是否存在
recognitionElement(new ElementInformation(controlName, null, ElementType.COMMON_ELEMENT));
return;
}catch (Exception e) {
continue;
}
}
//若不在新打开的窗口上则遍历已打开的窗口
if (brower != null) {
for (Page page : pageList) {
//切换窗口并查找元素是否在窗口上若存在则结束切换
brower.switchWindow(page);
try {
recognitionElement(new ElementInformation(controlName, null, ElementType.COMMON_ELEMENT));
return;
}catch (Exception e) {
continue;
}
}
}
//若遍历所有窗口后均未能查到元素则抛出异常
throw new NoSuchWindownException("未找到存在元素" + controlName + "所在的窗体");
}
/**
* 定位到弹框上并且点击确定按钮并返回弹框上的文本
*
* @return 弹框上的文本
*/
public String alertAccept() {
String text = alertGetText();
driver.switchTo().alert().accept();
return text;
}
/**
* 定位到弹框上并且点击取消按钮并返回弹框上的文本
*
* @return 弹框上的文本
*/
public String alertDimiss() {
String text = alertGetText();
driver.switchTo().alert().dismiss();
return text;
}
/**
* 定位到弹框上并且在其文本框中输入信息
*
* @param content 需要输入的信息
* @return 弹框上的文本
*/
public String alertInput(String content) {
String text = alertGetText();
driver.switchTo().alert().sendKeys("");
return text;
}
/**
* 获取弹框上的文本
*
* @return 弹框上的文本
*/
public String alertGetText() {
return driver.switchTo().alert().getText();
}
/**
* <p>
* 用于根据传入的控件名称或定位方式对控件在页面上定位返回其WebElement对象形参可以传入在xml文件中元素的名称
* 亦可以传入页面元素的定位方式但目前识别只支持xpath和css两种方式
* 该方法获取的是一组元素可用于对列表元素事件中
* </p>
* <p>
* 元素识别判断方式按照以下步骤进行<br>
* 1.先对xml文件进行扫描若存在该元素对应的标签则读取xml文件的定位方式并识别有效的定位方式一一匹配直到正确为止<br>
* 2.若在xml文件中查找不到该元素则按照xpath和css的规则进行匹配直到判断出该元素的定位方式位置<br>
* 3.若仍找不到元素则抛出UnrecognizableLocationModeException
* </p>
*
* @param name 元素名称或元素的定位方式
* @return 返回页面一组元素WebElement的对象
* @throws TimeoutException 元素在指定时间内未查找到时抛出的异常
* @throws UnrecognizableLocationModeException 元素无法识别时抛出的异常
*/
By recognitionElement(ElementInformation elementInformation) {
By by;
if (isXmlElement(elementInformation.name)) {
//若指定了xml文件且传入的元素名称存在与xml文件中则判断元素相应的定位方式及定位内容
by = recognitionXmlElement(elementInformation);
} else {
//若未指定xml文件或者在xml文件中无法查到相应的元素时则将name的值赋给value且根据value判断相应定位方式
by = recognitionCommonElement(elementInformation);
}
// return driver.findElements(by);
return by;
}
/**
* 获取普通元素的By对象
* @param elementInformation 元素信息类对象
* @return 元素信息指向的By对象
*/
private By recognitionCommonElement(ElementInformation elementInformation) {
//判断传入的ByType对象是否为null
if (elementInformation.byType == null) {
return judgeCommonElementBy(elementInformation.name);
} else {
By by = getBy(elementInformation.name, elementInformation.byType);
if (isExistElement(by, getWaitTime(elementInformation.name)))
return by;
else
throw new TimeoutException("普通元素定位方式类型无法识别:" + by);
}
}
/**
* 获取xml文件内元素的By对象
* @param elementInformation 元素信息类对象
* @return 元素信息指向的By对象
*/
private By recognitionXmlElement(ElementInformation elementInformation) {
//判断传入的ByType对象是否为null
if (elementInformation.byType == null) {
return judgeXmlElementBy(elementInformation.name, elementInformation.linkKeyList);
} else {
By by = xml.getBy(elementInformation.name, elementInformation.byType, elementInformation.linkKeyList);
if (isExistElement(by, getWaitTime(elementInformation.name)))
return by;
else
throw new TimeoutException("普通元素定位方式类型无法识别:" + by);
}
}
/**
* 用于返回控件的等待时间若设置单个控件的等待时间使用{@link #setContorlWaitTime(String, long)}方法设置
* 则返回设置的控件等待时间若未设置单个控件的等待时间则返回设置的通用等待时间使用{@link #setWaitTime(long)}方法
* 若未对通用时间进行设置则返回默认时间{@link #waitTime}
* @param name 控件名称
* @return 相应控件的等待时间
* @see #setContorlWaitTime(String, long)
* @see #setWaitTime(long)
*/
long getWaitTime(String name) {
for (String regex : controlWaitTime.keySet()) {
if (Pattern.compile(regex).matcher(name).matches()) {
return controlWaitTime.get(regex);
}
}
return waitTime;
}
/**
* 用于获取元素在xml文件中所有的父窗体并以集合的形式返回存储的顺序为父窗体在前子窗体在后若当前元素没有窗体
* 则集合的长度为0
* @param name 元素在xml文件中的名称
* @return 元素在xml文件中所有的父窗体集合
*/
List<String> getParentFrameName(String name) {
//存储获取到的父层窗体名称
List<String> nameList = new ArrayList<String>();
//获取元素所在窗体的名称
String iframeName = xml.getIframeName(name);
//循环判断窗体是否存在方法返回不为空若存在则再将父窗体进行存储
while(!iframeName.isEmpty()) {
//存储窗体
nameList.add(iframeName);
//再以当前窗体的名称再次获取该窗体的父窗体
iframeName = xml.getIframeName(iframeName);
}
//将nameList的内容倒序保证父窗体在子窗体之前
Collections.reverse(nameList);
return nameList;
}
/**
* 用于根据传入的参数识别非xml文件内的元素定位方式
* 该方法能快速识别xpath定位方式以及绝对css定位方式若不是以上两种定位方式
* 则会遍历所有的定位方式此时会降低运行速度建议在不是以上两种定位方式的
* 情况下直接指定元素的定位方式以提高效率
*/
By judgeCommonElementBy(String value) {
// 如果抛出元素名称查找不到的的异常则对应匹配xpath和绝对css路径两种定位方式
// 匹配xpath定位判定方法判断text的第一个字符是否是/
//由于是识别普通元素非xml元素其value就是元素的名称name 故获取等待时间时可直接将value传入
if (value.indexOf("/") == 0) {
//在页面中查找元素若元素能找到则结束查找
By by = getBy(value, ByType.XPATH);
if (isExistElement(by, getWaitTime(value))) {
return by;
}
} else if (value.indexOf("html") == 0) {
//在页面中查找元素若元素能找到则结束查找
By by = getBy(value, ByType.CSS);
if (isExistElement(by, getWaitTime(value))) {
return by;
}
}
//若元素无法识别则将所有的定位类型排除xpath类型与之进行对比直到在页面上找到元素为止
for(ByType type : ByType.values()) {
if (type == ByType.XPATH) {
continue;
}
By by = getBy(value, type);
//在页面中查找元素若元素能找到则结束查找
if (isExistElement(by, getWaitTime(value))) {
return by;
}
}
//若所有的定位方式均无法查找到元素则抛出异常
throw new TimeoutException("普通元素定位方式类型无法识别:" + value);
}
/**
* 用于设置xml文件内的元素的定位方式及定位内容
*/
By judgeXmlElementBy(String name, List<String> linkList) {
By by;
// 循环逐个在页面上配对有效的标签对应的定位方式
for (ByType mode : xml.getElementMode(name)) {
by = getBy(xml.getValue(name, mode, linkList), mode);
//若元素能被找到则返回相应的By对象若未找到则再次循环
if (isExistElement(by, getWaitTime(name))) {
return by;
} else {
continue;
}
}
// 若循环结束后仍未能找到该元素则抛出异常
throw new TimeoutException("xml文件内元素“" + name + "”无法查找请核对xml文件" + xml.getXmlFile().getName() + "\n文件路径" + xml.getXmlFile().getAbsolutePath());
if (iframeNameList.size() == 0) {
//若当前窗体也为空则直接返回
if (nowIframeList.size() == 0) {
return;
}
//当前窗体不为空时则切换到顶层
switchRootFrame();
return;
}
//判断nowIframeNameList是否为空或iframeNameList的第一个元素是否存在于nowIframeNameList中
//若不存在则切回到顶层并根据iframeList切换窗体
ElementData firstIframeElementData = new ElementData(iframeNameList.get(0), read);
if (nowIframeList.size() == 0 || nowIframeList.contains(firstIframeElementData)) {
switchRootFrame();
for(String iframeName : iframeNameList) {
switchFrame(new ElementData(iframeName, read));
}
return;
}
//判断iframeElementList中的最后一个元素是否与iframeList中的最后一个元素一致若一致则无需切换
ElementData lastIframeElementData = new ElementData(iframeNameList.get(iframeNameList.size() - 1), read);
if (nowIframeList.get(nowIframeList.size() - 1).equals(lastIframeElementData)) {
return;
}
//遍历iframeList查看iframeList中的窗体元素是否存在于iframeElementList中
int index = 0;
for (; index < iframeNameList.size(); index++) {
if (!nowIframeList.contains(new ElementData(iframeNameList.get(index), read))) {
break;
}
}
//计算需要向上切换窗体的个数
//当窗体不存在于iframeElementList中时其index以比存在的窗体下标大1位
//index可直接用于计算需要切换窗体的个数
switchParentFrame(nowIframeList.size() - index);
//切换到相应的父层后再根据iframeList剩余的元素进行切换
for (; index < iframeNameList.size(); index++) {
switchFrame(new ElementData(iframeNameList.get(index), read));
}
}
/**
* 根据元素的参数返回元素的By对象
* @return 元素的By对象
*/
By getBy(String value, ByType byType) {
protected By getBy(String value, ByType byType) {
//根据元素的定位方式对定位内容进行选择返回相应的By对象
switch (byType) {
case XPATH:
@ -630,105 +243,101 @@ public abstract class AbstractBy {
}
/**
* 根据页面的等待时间和元素定位方式在页面上查找相应的元素返回是否能查到元素
* @param time 控件等待时间
* @param by 元素定位方式
* @return 是否能查找到的元素
* 用于根据元素信息在页面上对数据进行查找
* @param elementData {@link ElementData}类对象
* @return {@link WebElement}类对象{@link List}集合
*/
abstract boolean isExistElement(By by, long waitTime);
/**
* 用于判断元素是否为xml文件内的元素
* @param name 元素名称
* @return 是否为xml文件内的元素
*/
boolean isXmlElement(String name) {
return (xml != null && xml.isElement(name));
protected List<WebElement> recognitionElement(ElementData elementData) {
//获取元素的定位类型及定位内容
ArrayList<ByType> elementByTypeList = elementData.getByTypeList();
ArrayList<String> elementValueList = elementData.getValueList();
//获取两个列表长度的最小者
int minLength = Math.min(elementByTypeList.size(), elementValueList.size());
//循环遍历所有的定位方式使用根据定位方式判断页面是否存在定位方式指向的元素
for (int index = 0; index < minLength; index++) {
//根据当前元素信息在页面获取元素
List<WebElement> elementList = findElement(elementByTypeList.get(index),
elementValueList.get(index),
elementData.getWaitTime() == -1 ? globalWaitTime : elementData.getWaitTime());
//若获取到的元素列表为空则继续循环调用下一个定位方式
if (elementList.size() != 0) {
return elementList;
}
}
throw new TimeoutException("页面上无相应定位方式的元素,元素名称:" + elementData.getName());
}
/**
* <p><b>文件名</b>AbstractElement.java</p>
* 根据传入的定位方式枚举以及定位内容在页面查找
* 元素返回查到的元素列表若查不到元素则返回空列表
* @param byType {@link ByType}枚举类
* @param value 元素定位内容
* @param waitTime 元素查找超时时间
* @return 页面查找到的{@link WebElement}类对象{@link List}集合
*/
protected List<WebElement> findElement(ByType byType, String value, long waitTime) {
try {
return new WebDriverWait(brower.getDriver(), waitTime)
.until(driver -> driver.findElements(getBy(value, byType)));
} catch (TimeoutException e) {
return new ArrayList<WebElement>();
}
}
/**
* <p><b>文件名</b>AbstractBy.java</p>
* <p><b>用途</b>
* 存储获取元素时的信息
* 用于存储通过元素信息查找到的元素内容
* </p>
* <p><b>编码时间</b>2020年5月9日上午7:57:24</p>
* <p><b>修改时间</b>2020年5月22日上午8:18:39</p>
* <p><b>编码时间</b>2020年10月13日上午8:02:38</p>
* <p><b>修改时间</b>2020年10月13日上午8:02:38</p>
* @author 彭宇琦
* @version Ver1.1
* @since JDK 1.8
* @version Ver1.0
*
*/
class ElementInformation {
public class Element {
/**
* 存储元素的名称或定位内容
* 存储元素的获取下标
*/
public String name;
/**
* 存储元素的定位方式
*/
public ByType byType;
/**
* 用于标记元素的类型
*/
public ElementType elementType;
/**
* 存储外链词语
*/
public ArrayList<String> linkKeyList;
private int index;
/**
* 构造元素信息
* @param name 元素名称
* @param byType 元素定位的By类型枚举
* @param elementType 元素类型枚举
* @param linkKeyList xml文件中需要外部替换的词语
* 构造对象其传入的下标允许为-1表示当前元素集合中不存在该元素
* @param element 通过信息获取到的页面元素对象
* @param index 元素的获取下标
*/
public ElementInformation(String name, ByType byType, ElementType elementType) {
super();
this.name = name;
this.byType = byType;
this.elementType = elementType;
splitLinkKey();
public Element(int index) {
this.index = index;
}
/**
* 用于判断其元素的使用的外链xml文件词语是否与存储的内容一致
* @param linkKey 需要判断的外链xml文件词语
* @return 是否与类中存储的词语一致
* 用于返回当前存储的{@link WebElement}对象若该对象为空则抛出元素查找超时异常
* @return {@link WebElement}对象
* @throws TimeoutException 元素在页面不存在时抛出的异常
* @throws NoSuchElementException 元素集合不存在指定的下标时抛出的异常
*/
public boolean linkKeyEquals(List<String> linkKey) {
//判断传入的linkKey是否为null或未传入的linkKey为空为空则直接判断linkKeyList的状态
if (linkKey == null || linkKey.size() == 0) {
//若linkKeyList为null或为空则可直接返回true反之则返回false
if (linkKeyList == null || linkKeyList.size() == 0) {
return true;
} else {
return false;
}
}
//判断linkKey中的元素是否完全存在于linkKeyList且两个集合的长度一致若存在一项判断不符合则返回false
if (linkKeyList.containsAll(linkKey) && linkKeyList.size() == linkKey.size()) {
return true;
} else {
return false;
public WebElement getWebElement() {
//判断元素集合是否为空若为空则抛出查找超时异常
if (elementList == null || elementList.size() == 0) {
throw new TimeoutException("页面上无相应定位方式的元素,当前元素名称:" + elementData.getName());
}
//判断元素下标是否为-1若为-1则抛出元素不存在异常
if (index == -1) {
throw new NoSuchElementException("指定的元素下标值不存在,当前元素集合个数:" + elementList.size());
}
return elementList.get(index);
}
/**
* 用于对在名称后存在的外链词语进行切分
* 重新根据元素信息在页面查找元素
*/
private void splitLinkKey() {
int index = name.indexOf("=");
//判断名称是否存在需要外部替换xml文件的词语
if (index < 0) {
return;
}
//对词语按照分隔符进行切分并去除空格
String linkKeyText = name.substring(index).replaceAll(" ", "");
//再次按照词语分隔符进行切分
linkKeyList = new ArrayList<>(Arrays.asList(linkKeyText.split("\\,")));
public void againFindElement() {
elementList = recognitionElement(elementData);
}
@Override
@ -736,10 +345,7 @@ public abstract class AbstractBy {
final int prime = 31;
int result = 1;
result = prime * result + getEnclosingInstance().hashCode();
result = prime * result + ((byType == null) ? 0 : byType.hashCode());
result = prime * result + ((elementType == null) ? 0 : elementType.hashCode());
result = prime * result + ((linkKeyList == null) ? 0 : linkKeyList.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + index;
return result;
}
@ -751,22 +357,10 @@ public abstract class AbstractBy {
return false;
if (getClass() != obj.getClass())
return false;
ElementInformation other = (ElementInformation) obj;
Element other = (Element) obj;
if (!getEnclosingInstance().equals(other.getEnclosingInstance()))
return false;
if (byType != other.byType)
return false;
if (elementType != other.elementType)
return false;
if (linkKeyList == null) {
if (other.linkKeyList != null)
return false;
} else if (!linkKeyList.equals(other.linkKeyList))
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
if (index != other.index)
return false;
return true;
}
@ -774,6 +368,5 @@ public abstract class AbstractBy {
private AbstractBy getEnclosingInstance() {
return AbstractBy.this;
}
}
}

View File

@ -1,775 +0,0 @@
package pres.auxiliary.work.selenium.element;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import org.openqa.selenium.By;
import org.openqa.selenium.TimeoutException;
import org.openqa.selenium.WebDriver;
import pres.auxiliary.work.selenium.brower.AbstractBrower;
import pres.auxiliary.work.selenium.brower.Page;
import pres.auxiliary.work.selenium.xml.ByType;
import pres.auxiliary.work.selenium.xml.ReadXml;
/**
* <p><b>文件名</b>AbstractElement.java</p>
* <p><b>用途</b></p>
* <p>对辅助化测试工具selenium的获取元素代码进行的二次封装通过类中提供的方法以及配合相应存储元素的
* xml文件以更简便的方式对页面元素进行获取减少编程时的代码量
* </p>
* <p><b>编码时间</b>2020年4月25日 下午4:18:37</p>
* <p><b>修改时间</b>2020年4月25日 下午4:18:37</p>
* @author 彭宇琦
* @version Ver1.0
* @since JDK 1.8
*/
public abstract class AbstractElement {
/**
* 用于存储浏览器的WebDriver对象
*/
WebDriver driver;
/**
* 用于存储元素信息由于
*/
static ElementData elementData;
/**
* 用于存储浏览器对象
*/
static AbstractBrower brower;
/**
* 存储单个控件的等待时间
*/
private HashMap<String, Long> controlWaitTime = new HashMap<String, Long>();
/**
* 用于存储当前浏览器窗口的Handles值
*/
private String browserHandles;
/**
* 存储当前定位的窗体层级由于多个子类之间需要相互通信故此处标记为static
*/
private static ArrayList<String> iframeNameList = new ArrayList<>();
/**
* 用于当元素获取失败时执行的方法
*/
ExceptionAction action;
/**
* 用于存储元素通用的等待时间默认5秒
*/
private long waitTime = 5;
/**
* 控制是否自动切换窗体由于通过Event类调用时会构造另一个事件类
*/
boolean isAutoSwitchIframe = true;
/**
* 通过元素信息类对象{@link ElementData}构造对象
* @param brower {@link AbstractBrower}类对象
* @param elementData {@link ElementData}类对象
*/
public AbstractElement(AbstractBrower brower, ElementData elementData) {
this.brower = brower;
this.driver = brower.getDriver();
this.elementData = elementData;
}
public AbstractElement(AbstractBrower brower, String elemenetName) {
}
/**
* 设置元素查找失败时需要执行的方法
* @param action 执行方法
*/
public void setAction(ExceptionAction action) {
this.action = action;
}
/**
* 用于清除元素查找失败时执行的方法
*/
public void clearAction() {
this.action = null;
}
/**
* 用于设置元素等待时间默认时间为5秒
*
* @param waitTime 元素等待时间
*/
public void setWaitTime(long waitTime) {
this.waitTime = waitTime;
}
/**
* 用于对符合正则表达式的控件名称设置等待时间
*
* @param regex 正则表达式
* @param waitTime 等待时间
*/
public void setContorlWaitTime(String regex, long waitTime) {
controlWaitTime.put(regex, waitTime);
}
/**
* 设置是否自动切换窗体
* @param switchIframe 是否自动切换窗体
*/
public void setAutoSwitchIframe(boolean isAutoSwitchIframe) {
this.isAutoSwitchIframe = isAutoSwitchIframe;
}
/**
* 该方法用于返回浏览器的WebDriver对象
*
* @return 浏览器的WebDriver对象
*/
public WebDriver getDriver() {
return driver;
}
/**
* 用于设置指向存储元素定位方式的xml文件对象并根据传参判断窗体是否需要回到顶层
* @param xmlFile 存储元素定位方式的xml文件对象
* @param isBreakRootFrame 是否需要将窗体切回到顶层
*/
public void setXmlFile(File xmlFile, boolean isBreakRootFrame) {
if (xml == null) {
xml = new ReadXml(xmlFile);
} else {
xml.setXmlPath(xmlFile);
}
if (isBreakRootFrame) {
switchRootFrame();
}
}
/**
* 该方法用于将窗体切回顶层当本身是在最顶层时则该方法将使用无效
*/
public void switchRootFrame() {
//切换窗口至顶层
driver.switchTo().defaultContent();
//清空iframeNameList中的内容
iframeNameList.clear();
}
/**
* 该方法用于将窗体切换到上一层父层若当前层只有一层则调用方法后切回顶层
* 若当前层为最顶层时则该方法将使用无效
*/
public void switchParentFrame() {
//若iframeNameList大于1层则向上切换窗体
if (iframeNameList.size() > 1) {
driver.switchTo().parentFrame();
iframeNameList.remove(iframeNameList.size() - 1);
} else if (iframeNameList.size() == 1) {
//若iframeNameList等于1层则调用切换至顶层的方法
switchRootFrame();
} else {
//若iframeNameList小于1层则不做操作
return;
}
}
/**
* <p>
* 通过传入在xml文件中的控件名称到类中指向的xml文件中查找控件
* 名称对应的定位方式或直接传入xpath与css定位方式
* 根据定位方式对相应的窗体进行定位当传入的窗体为当前窗体的前层父层窗体时
* 通过该方法将调用切换父层的方法将窗体切换到父层上例如<br>
* 当前存在f1, f2, f3, f4四层窗体则调用方法<br>{@code
* switchFrame("f2");
* }<br>
* 此时窗体将回到f2层无需再从顶层开始向下切换
* </p>
* <p>
* 若传入该方法的名称存在于xml文件中且该元素存在父窗体时调用
* 该方法会从xml文件中获取相应所有父窗体并对相应的父窗体进行切换
* 从而达到无须切换父窗体的目的例如存在以下xml文件片段<pre>{@code
* ...
* <iframe name='f1'>
* <xpath>...</xpath>
* <iframe name='f2'>
* <xpath>...</xpath>
* <iframe name='f3'>
* <xpath>...</xpath>
* </iframe>
* </iframe>
* </iframe>
* ...
* }</pre>
* 当调用该方法<br>{@code
* switchFrame("f3");
* }<br>
* 则会先将窗体从f1开始切换至窗体f2最后再切换为窗体f3
* </p>
*
* @param name 窗体的名称或xpath与css定位方式
*/
public void switchFrame(String name) {
switchFrame(new ElementInformation(name, null, ElementType.COMMON_ELEMENT));
}
/**
* 通过传入在xml文件中的控件名称到类中指向的xml文件中查找控件
* 名称对应的定位方式或直接传入xpath与css定位方式
* 根据定位方式对相应的窗体进行定位通过该方法可以指定使用的定位类型一般用于
* 传入非xml文件的元素也可用于指定xml文件元素的定位方式其他说明参见{@link #switchFrame(String)}
* @param name 窗体的名称或xpath与css定位方式
* @param byType 元素的定位方式
* @see #switchFrame(String)
*/
public void switchFrame(String name, ByType byType) {
switchFrame(new ElementInformation(name, byType, ElementType.COMMON_ELEMENT));
}
/**
* 切换窗体的底层方法
* @param elementInformation 元素信息类对象
*/
private void switchFrame(ElementInformation elementInformation) {
List<String> nameList = new ArrayList<String>();
//判断传入的元素名称是否存在于xml文件中若存在则将该元素的父层名称存储至nameList
if (isXmlElement(elementInformation.name) && isAutoSwitchIframe) {
nameList.addAll(getParentFrameName(elementInformation.name));
}
//调用切换窗体的方法
switchFrame(nameList);
driver.switchTo().frame(driver.findElement(recognitionElement(elementInformation)));
//切换窗体
iframeNameList.add(elementInformation.name);
}
/**
* 通过传入在xml文件中的控件名称到类中指向的xml文件中查找控件
* 名称对应的定位方式或直接传入xpath与css定位方式
* 根据定位方式对相应的窗体进行定位当传入的窗体为当前窗体的前层父层窗体时
* 通过该方法将调用切换父层的方法将窗体切换到父层上例如<br>
* 当前存在f1, f2, f3, f4四层窗体则调用方法<pre>{@code
* List<String> nameList = new ArrayList<String>();
* nameList.add("f2");
* switchFrame(nameList);
* }</pre>
* 此时窗体将回到f2层无需再从顶层开始向下切换<br>
* <b>注意</b>
* <ol>
* <li>窗体的切换按照从前向后的顺序进行切换切换顺序不能相反</li>
* <li>传入的参数若在xml文件中且存在父窗体调用该方法也不会对窗体进行切换</li>
* </ol>
*
* @param elementInformationList 存储窗体的名称或xpath与css定位方式的List集合
*/
void switchFrame(List<String> frameNameList) {
//若传参为空则切回到顶层
if (frameNameList.isEmpty()) {
switchRootFrame();
return;
}
//若原窗体和需要切换的窗体的最后一个元素一致则无需切换
if (!iframeNameList.isEmpty() && frameNameList.get(frameNameList.size() - 1).equals(iframeNameList.get(iframeNameList.size() - 1))) {
return;
}
//若需要切换的窗体第一层均不在iframeNameList时则需要切回到顶层
if (!iframeNameList.contains(frameNameList.get(0))) {
switchRootFrame();
}
//若不为空则列表进行切换
frameNameList.forEach(frameName -> {
//判断name指向的窗体是否在iframeNameList中若存在则向上切换父层直到切换到name指向的窗体若不存在则直接切换并添加窗体名称
if (iframeNameList.contains(frameName)) {
//获取name窗体在iframeNameList中的位置
int index = iframeNameList.indexOf(frameName);
//获取需要向上切换窗体的次数公式为推断出来
int count = iframeNameList.size() - index - 1;
for (int i = 0; i < count; i++) {
switchParentFrame();
}
} else {
//切换窗体
driver.switchTo().frame(driver.findElement(recognitionElement(new ElementInformation(frameName, null, ElementType.COMMON_ELEMENT))));
iframeNameList.add(frameName);
}
});
}
/**
* 该方法用于将窗口切换回最原始的窗口上
*/
public void switchOldWindow() {
driver.switchTo().window(browserHandles);
}
/**
* 该方法可根据控件名称之后对比每一个弹窗若某一个弹窗上存在元素名对应的元素则返回相应
* 窗口的WebDriver对象,若无新窗口则返回当前的窗口的WebDriver对象当所有的窗体都
* 不包含相应的元素时则抛出NoSuchWindownException异常
*
* @param controlName 控件名称
* @throws NoSuchWindownException 窗口未找到时抛出的异常
*/
public void switchWindow(String controlName) {
Set<String> handles = driver.getWindowHandles();
// 判断是否只存在一个窗体若只存在一个则直接返回当前浏览器的WebDriver对象
if (handles.size() == 1) {
return;
}
List<Page> pageList = new ArrayList<>();
//若浏览器对象存在则将已打开的页面的handle进行存储优先遍历新打开的标签
if (brower != null) {
pageList.addAll(brower.getOpenPage());
}
//移除已打开的窗口
handles.removeAll(pageList.stream().map(page -> {
return page.getHandle();
}).collect(Collectors.toList()));
// 循环获取所有的页面handle
for (String newWinHandle : handles) {
//切换窗口并查找元素是否在窗口上若存在则结束切换
driver.switchTo().window(newWinHandle);
try {
//构造信息因为在构造过程中会判断元素是否存在
recognitionElement(new ElementInformation(controlName, null, ElementType.COMMON_ELEMENT));
return;
}catch (Exception e) {
continue;
}
}
//若不在新打开的窗口上则遍历已打开的窗口
if (brower != null) {
for (Page page : pageList) {
//切换窗口并查找元素是否在窗口上若存在则结束切换
brower.switchWindow(page);
try {
recognitionElement(new ElementInformation(controlName, null, ElementType.COMMON_ELEMENT));
return;
}catch (Exception e) {
continue;
}
}
}
//若遍历所有窗口后均未能查到元素则抛出异常
throw new NoSuchWindownException("未找到存在元素" + controlName + "所在的窗体");
}
/**
* 定位到弹框上并且点击确定按钮并返回弹框上的文本
*
* @return 弹框上的文本
*/
public String alertAccept() {
String text = alertGetText();
driver.switchTo().alert().accept();
return text;
}
/**
* 定位到弹框上并且点击取消按钮并返回弹框上的文本
*
* @return 弹框上的文本
*/
public String alertDimiss() {
String text = alertGetText();
driver.switchTo().alert().dismiss();
return text;
}
/**
* 定位到弹框上并且在其文本框中输入信息
*
* @param content 需要输入的信息
* @return 弹框上的文本
*/
public String alertInput(String content) {
String text = alertGetText();
driver.switchTo().alert().sendKeys("");
return text;
}
/**
* 获取弹框上的文本
*
* @return 弹框上的文本
*/
public String alertGetText() {
return driver.switchTo().alert().getText();
}
/**
* <p>
* 用于根据传入的控件名称或定位方式对控件在页面上定位返回其WebElement对象形参可以传入在xml文件中元素的名称
* 亦可以传入页面元素的定位方式但目前识别只支持xpath和css两种方式
* 该方法获取的是一组元素可用于对列表元素事件中
* </p>
* <p>
* 元素识别判断方式按照以下步骤进行<br>
* 1.先对xml文件进行扫描若存在该元素对应的标签则读取xml文件的定位方式并识别有效的定位方式一一匹配直到正确为止<br>
* 2.若在xml文件中查找不到该元素则按照xpath和css的规则进行匹配直到判断出该元素的定位方式位置<br>
* 3.若仍找不到元素则抛出UnrecognizableLocationModeException
* </p>
*
* @param name 元素名称或元素的定位方式
* @return 返回页面一组元素WebElement的对象
* @throws TimeoutException 元素在指定时间内未查找到时抛出的异常
* @throws UnrecognizableLocationModeException 元素无法识别时抛出的异常
*/
By recognitionElement(ElementInformation elementInformation) {
By by;
if (isXmlElement(elementInformation.name)) {
//若指定了xml文件且传入的元素名称存在与xml文件中则判断元素相应的定位方式及定位内容
by = recognitionXmlElement(elementInformation);
} else {
//若未指定xml文件或者在xml文件中无法查到相应的元素时则将name的值赋给value且根据value判断相应定位方式
by = recognitionCommonElement(elementInformation);
}
// return driver.findElements(by);
return by;
}
/**
* 获取普通元素的By对象
* @param elementInformation 元素信息类对象
* @return 元素信息指向的By对象
*/
private By recognitionCommonElement(ElementInformation elementInformation) {
//判断传入的ByType对象是否为null
if (elementInformation.byType == null) {
return judgeCommonElementBy(elementInformation.name);
} else {
By by = getBy(elementInformation.name, elementInformation.byType);
if (isExistElement(by, getWaitTime(elementInformation.name)))
return by;
else
throw new TimeoutException("普通元素定位方式类型无法识别:" + by);
}
}
/**
* 获取xml文件内元素的By对象
* @param elementInformation 元素信息类对象
* @return 元素信息指向的By对象
*/
private By recognitionXmlElement(ElementInformation elementInformation) {
//判断传入的ByType对象是否为null
if (elementInformation.byType == null) {
return judgeXmlElementBy(elementInformation.name, elementInformation.linkKeyList);
} else {
By by = xml.getBy(elementInformation.name, elementInformation.byType, elementInformation.linkKeyList);
if (isExistElement(by, getWaitTime(elementInformation.name)))
return by;
else
throw new TimeoutException("普通元素定位方式类型无法识别:" + by);
}
}
/**
* 用于返回控件的等待时间若设置单个控件的等待时间使用{@link #setContorlWaitTime(String, long)}方法设置
* 则返回设置的控件等待时间若未设置单个控件的等待时间则返回设置的通用等待时间使用{@link #setWaitTime(long)}方法
* 若未对通用时间进行设置则返回默认时间{@link #waitTime}
* @param name 控件名称
* @return 相应控件的等待时间
* @see #setContorlWaitTime(String, long)
* @see #setWaitTime(long)
*/
long getWaitTime(String name) {
for (String regex : controlWaitTime.keySet()) {
if (Pattern.compile(regex).matcher(name).matches()) {
return controlWaitTime.get(regex);
}
}
return waitTime;
}
/**
* 用于获取元素在xml文件中所有的父窗体并以集合的形式返回存储的顺序为父窗体在前子窗体在后若当前元素没有窗体
* 则集合的长度为0
* @param name 元素在xml文件中的名称
* @return 元素在xml文件中所有的父窗体集合
*/
List<String> getParentFrameName(String name) {
//存储获取到的父层窗体名称
List<String> nameList = new ArrayList<String>();
//获取元素所在窗体的名称
String iframeName = xml.getIframeName(name);
//循环判断窗体是否存在方法返回不为空若存在则再将父窗体进行存储
while(!iframeName.isEmpty()) {
//存储窗体
nameList.add(iframeName);
//再以当前窗体的名称再次获取该窗体的父窗体
iframeName = xml.getIframeName(iframeName);
}
//将nameList的内容倒序保证父窗体在子窗体之前
Collections.reverse(nameList);
return nameList;
}
/**
* 用于根据传入的参数识别非xml文件内的元素定位方式
* 该方法能快速识别xpath定位方式以及绝对css定位方式若不是以上两种定位方式
* 则会遍历所有的定位方式此时会降低运行速度建议在不是以上两种定位方式的
* 情况下直接指定元素的定位方式以提高效率
*/
By judgeCommonElementBy(String value) {
// 如果抛出元素名称查找不到的的异常则对应匹配xpath和绝对css路径两种定位方式
// 匹配xpath定位判定方法判断text的第一个字符是否是/
//由于是识别普通元素非xml元素其value就是元素的名称name 故获取等待时间时可直接将value传入
if (value.indexOf("/") == 0) {
//在页面中查找元素若元素能找到则结束查找
By by = getBy(value, ByType.XPATH);
if (isExistElement(by, getWaitTime(value))) {
return by;
}
} else if (value.indexOf("html") == 0) {
//在页面中查找元素若元素能找到则结束查找
By by = getBy(value, ByType.CSS);
if (isExistElement(by, getWaitTime(value))) {
return by;
}
}
//若元素无法识别则将所有的定位类型排除xpath类型与之进行对比直到在页面上找到元素为止
for(ByType type : ByType.values()) {
if (type == ByType.XPATH) {
continue;
}
By by = getBy(value, type);
//在页面中查找元素若元素能找到则结束查找
if (isExistElement(by, getWaitTime(value))) {
return by;
}
}
//若所有的定位方式均无法查找到元素则抛出异常
throw new TimeoutException("普通元素定位方式类型无法识别:" + value);
}
/**
* 用于设置xml文件内的元素的定位方式及定位内容
*/
By judgeXmlElementBy(String name, List<String> linkList) {
By by;
// 循环逐个在页面上配对有效的标签对应的定位方式
for (ByType mode : xml.getElementMode(name)) {
by = getBy(xml.getValue(name, mode, linkList), mode);
//若元素能被找到则返回相应的By对象若未找到则再次循环
if (isExistElement(by, getWaitTime(name))) {
return by;
} else {
continue;
}
}
// 若循环结束后仍未能找到该元素则抛出异常
throw new TimeoutException("xml文件内元素“" + name + "”无法查找请核对xml文件" + xml.getXmlFile().getName() + "\n文件路径" + xml.getXmlFile().getAbsolutePath());
}
/**
* 根据元素的参数返回元素的By对象
* @return 元素的By对象
*/
By getBy(String value, ByType byType) {
//根据元素的定位方式对定位内容进行选择返回相应的By对象
switch (byType) {
case XPATH:
return By.xpath(value);
case CLASSNAME:
return By.className(value);
case CSS:
return By.cssSelector(value);
case ID:
return By.id(value);
case LINKTEXT:
return By.linkText(value);
case NAME:
return By.name(value);
case TAGNAME:
return By.tagName(value);
default:
throw new UnrecognizableLocationModeException("无法识别的定位类型:" + byType);
}
}
/**
* 根据页面的等待时间和元素定位方式在页面上查找相应的元素返回是否能查到元素
* @param time 控件等待时间
* @param by 元素定位方式
* @return 是否能查找到的元素
*/
abstract boolean isExistElement(By by, long waitTime);
/**
* 用于判断元素是否为xml文件内的元素
* @param name 元素名称
* @return 是否为xml文件内的元素
*/
boolean isXmlElement(String name) {
return (xml != null && xml.isElement(name));
}
/**
* <p><b>文件名</b>AbstractElement.java</p>
* <p><b>用途</b>
* 存储获取元素时的信息
* </p>
* <p><b>编码时间</b>2020年5月9日上午7:57:24</p>
* <p><b>修改时间</b>2020年5月22日上午8:18:39</p>
* @author 彭宇琦
* @version Ver1.1
* @since JDK 1.8
*
*/
class ElementInformation {
/**
* 存储元素的名称或定位内容
*/
public String name;
/**
* 存储元素的定位方式
*/
public ByType byType;
/**
* 用于标记元素的类型
*/
public ElementType elementType;
/**
* 存储外链词语
*/
public ArrayList<String> linkKeyList;
/**
* 构造元素信息
* @param name 元素名称
* @param byType 元素定位的By类型枚举
* @param elementType 元素类型枚举
* @param linkKeyList xml文件中需要外部替换的词语
*/
public ElementInformation(String name, ByType byType, ElementType elementType) {
super();
this.name = name;
this.byType = byType;
this.elementType = elementType;
splitLinkKey();
}
/**
* 用于判断其元素的使用的外链xml文件词语是否与存储的内容一致
* @param linkKey 需要判断的外链xml文件词语
* @return 是否与类中存储的词语一致
*/
public boolean linkKeyEquals(List<String> linkKey) {
//判断传入的linkKey是否为null或未传入的linkKey为空为空则直接判断linkKeyList的状态
if (linkKey == null || linkKey.size() == 0) {
//若linkKeyList为null或为空则可直接返回true反之则返回false
if (linkKeyList == null || linkKeyList.size() == 0) {
return true;
} else {
return false;
}
}
//判断linkKey中的元素是否完全存在于linkKeyList且两个集合的长度一致若存在一项判断不符合则返回false
if (linkKeyList.containsAll(linkKey) && linkKeyList.size() == linkKey.size()) {
return true;
} else {
return false;
}
}
/**
* 用于对在名称后存在的外链词语进行切分
*/
private void splitLinkKey() {
int index = name.indexOf("=");
//判断名称是否存在需要外部替换xml文件的词语
if (index < 0) {
return;
}
//对词语按照分隔符进行切分并去除空格
String linkKeyText = name.substring(index).replaceAll(" ", "");
//再次按照词语分隔符进行切分
linkKeyList = new ArrayList<>(Arrays.asList(linkKeyText.split("\\,")));
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + getEnclosingInstance().hashCode();
result = prime * result + ((byType == null) ? 0 : byType.hashCode());
result = prime * result + ((elementType == null) ? 0 : elementType.hashCode());
result = prime * result + ((linkKeyList == null) ? 0 : linkKeyList.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ElementInformation other = (ElementInformation) obj;
if (!getEnclosingInstance().equals(other.getEnclosingInstance()))
return false;
if (byType != other.byType)
return false;
if (elementType != other.elementType)
return false;
if (linkKeyList == null) {
if (other.linkKeyList != null)
return false;
} else if (!linkKeyList.equals(other.linkKeyList))
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
private AbstractElement getEnclosingInstance() {
return AbstractElement.this;
}
}
}

View File

@ -1,94 +1,61 @@
package pres.auxiliary.work.selenium.element;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.WebDriverWait;
import java.util.ArrayList;
import org.openqa.selenium.TimeoutException;
import pres.auxiliary.work.selenium.brower.AbstractBrower;
import pres.auxiliary.work.selenium.xml.ByType;
/**
* <p><b>文件名</b>CommonElement.java</p>
* <p><b>文件名</b>CommonBy.java</p>
* <p><b>用途</b>
* 提供在辅助化测试中对页面单一元素获取的方法类中获取元素的方法兼容传入定位方式对
* 提供在辅助化测试中对页面单一元素获取的方法类中获取元素的方法兼容传入定位方式对
* 元素进行查找建议使用xml对页面元素的定位方式进行存储以简化编码时的代码量也便于
* 对代码的维护
* </p>
* <p><b>编码时间</b>2020年4月26日下午10:34:55</p>
* <p><b>修改时间</b>2020年4月26日下午10:34:55</p>
* <p><b>编码时间</b>2020年10月13日上午9:44:01</p>
* <p><b>修改时间</b>2020年10月13日上午9:44:01</p>
* @author 彭宇琦
* @version Ver1.0
* @since JDK 12
*
*/
public class CommonBy extends AbstractBy {
/**
* 构造对象并存储浏览器的WebDriver对象
*
* @param driver 浏览器的WebDriver对象
*/
public CommonBy(WebDriver driver) {
super(driver);
}
/**
* 通过浏览器对象{@link AbstractBrower}进行构造
* @param brower {@link AbstractBrower}对象
* 构造方法初始化浏览器对象
* @param brower {@link AbstractBrower}类对象
*/
public CommonBy(AbstractBrower brower) {
super(brower);
}
/**
* 用于根据xml文件中元素的名称返回对应的{@link Element}对象该方法亦可传入元素
* 定位内容通过遍历所有的定位方式在页面上查找元素来获取元素的WebElement对象
* 建议传入的定位内容为xpath路径或绝对的css路径若非这两路径则在识别元素时会很慢降低
* 程序运行速度若非xml文件中的元素且不是xpath路径或绝对的css路径建议使用{@link #getElement(String, ByType)}方法
* @param name 元素的名称或元素定位内容
* @return {@link Element}对象
* 用于根据元素名称与需要获取的元素位置返回指定的元素调用该方法在无法查到页面元素时其不会
* 抛出{@link TimeoutException}异常但当调用{@link Element#getWebElement()}方法时由于未查到元素
* 则会抛出异常
* @param elementName 元素名称
* @param index 元素所在下标
* @param linkKeys 外链词语组
* @return {@link Element}类对象
*/
public Element getElement(String name) {
return getElement(new ElementInformation(name, null, ElementType.COMMON_ELEMENT));
}
/**
* 用于根据xml文件中元素的名称与定位方式返回对应的{@link Element}对象该方法亦可传入元素
* 定位内容并根据定位方式对页面数据进行查找
* @param name 元素的名称或元素定位内容
* @return {@link Element}对象
*/
public Element getElement(String name, ByType byType) {
return getElement(new ElementInformation(name, byType, ElementType.COMMON_ELEMENT));
}
/**
* 获取元素的底层方法
* @param elementInformation 元素信息类对象
* @return {@link Element}对象
*/
private Element getElement(ElementInformation elementInformation) {
//TODO 添加元素查找失败时执行方法的代码
//判断传入的元素是否在xml文件中若存在再判断是否自动切换窗体若需要则获取元素的所有父窗体并进行切换
if (xml != null && xml.isElement(elementInformation.name) && isAutoSwitchIframe) {
switchFrame(getParentFrameName(elementInformation.name));
public Element getElement(String elementName, String...linkKeys) {
//根据元素名称获取元素信息数据
elementData = new ElementData(elementName, read);
elementData.addLinkWord(linkKeys);
//判断是否需要自动切换窗体若需要则对元素窗体进行切换
if (isAutoSwitchIframe) {
autoSwitchFrame(elementData.getIframeNameList());
}
return new Element(driver, ElementType.COMMON_ELEMENT, recognitionElement(elementInformation), elementInformation.name);
}
@Override
boolean isExistElement(By by, long waitTime) {
//当查找到元素时则返回true若查不到元素则会抛出异常故返回false
return new WebDriverWait(driver, waitTime, 200).
until((driver) -> {
WebElement element = driver.findElement(by);
return element != null;
});
/*
return new WebDriverWait(driver, waitTime, 200).
until(ExpectedConditions.and(ExpectedConditions.visibilityOfAllElementsLocatedBy(by),
ExpectedConditions.invisibilityOfElementLocated(by)));
*/
//获取元素数据在页面上对应的一组元素若无法查到元素则记录elementList为null
try {
elementList = recognitionElement(elementData);
} catch (TimeoutException e) {
elementList = new ArrayList<>();
}
//返回元素对象
return new Element(0);
}
}

View File

@ -1,251 +1,43 @@
package pres.auxiliary.work.selenium.element;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import org.openqa.selenium.By;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.WebDriverWait;
import java.util.HashSet;
import java.util.Set;
import pres.auxiliary.work.selenium.brower.AbstractBrower;
import pres.auxiliary.work.selenium.xml.ByType;
/**
* <p><b>文件名</b>DataListElement.java</p>
* <p><b>文件名</b>DataListBy.java</p>
* <p><b>用途</b>
* 提供在辅助化测试中对页面列表元素获取的方法并对列表元素的获取做了优化
* 类中获取元素的方法兼容传入定位方式对元素进行查找建议使用xml对页面元素的
* 定位方式进行存储以简化编码时的代码量也便于对代码的维护
* </p>
* <p><b>编码时间</b>2020年4月29日下午6:18:46</p>
* <p><b>修改时间</b>2020年4月29日下午6:18:46</p>
* <p><b>编码时间</b>2020年10月14日下午6:45:51</p>
* <p><b>修改时间</b>2020年10月14日下午6:45:51</p>
* @author 彭宇琦
* @version Ver1.0
* @since JDK 12
*
*/
public class DataListBy extends ListBy {
public class DataListBy extends MultiBy {
/**
* 存储获取到的元素列表中最多元素列的元素个数
*/
private int maxColumnSize = -1;
/**
* 存储获取到的元素列表中最多元素列的名称
*/
private ArrayList<String> maxColumnNameList = new ArrayList<>();
/**
* 存储获取到的元素列表中最少元素列的元素个数
*/
private int minColumnSize = Integer.MAX_VALUE;
/**
* 存储获取到的元素列表中最少元素列的名称
*/
private ArrayList<String> minColumnNameList = new ArrayList<>();
/**
* 用于判断列表的第一行元素是否为标题元素
*/
boolean isFristRowTitle = false;
/**
* 用于存储元素列累计的个数
*/
// private HashMap<String, Integer> elementSizeMap = new HashMap<String, Integer>(16);
/**
* 用于存储是否开始累加列表元素的个数
*/
// private boolean isStartAddSize = false;
/**
* 通过浏览器对象{@link AbstractBrower}进行构造
* @param brower {@link AbstractBrower}对象
* 构造方法初始化浏览器对象
* @param brower {@link AbstractBrower}类对象
*/
public DataListBy(AbstractBrower brower) {
super(brower);
}
/**
* 构造对象并存储浏览器的{@link WebDriver}对象
*
* @param driver 浏览器的{@link WebDriver}对象
*/
public DataListBy(WebDriver driver) {
super(driver);
}
/**
* 用于设置首行元素是否为标题元素
* @param isFristRowTitle 首行是否为标题元素
* 用于返回指定个数的随机元素若未调用{@link #find(String, String...)}方法对元素进行查找
* 或者查无元素时则调用该方法时会抛出超时异常
* @param length 随机元素个数
* @return 相应个数的随机不重复元素{@link Set}集合
*/
public void setFristRowTitle(boolean isFristRowTitle) {
this.isFristRowTitle = isFristRowTitle;
}
/**
* 用于设置是否开始计算元素个数
* @param isStartAddSize 是否开始计算元素个数
*/
/*
public void setStartAddSize(boolean isStartAddSize) {
this.isStartAddSize = isStartAddSize;
}
*/
/**
* 返回元素最多列的元素个数
* @return 元素最多列的元素个数
*/
public int getMaxColumnSize() {
return maxColumnSize;
}
/**
* 返回元素最多列的列名称
* @return 元素最多列的列名称
*/
public List<String> getMaxColumnName() {
return maxColumnNameList;
}
/**
* 返回元素最少列的元素个数
* @return 元素最少列的元素个数
*/
public int getMinColumnSize() {
return minColumnSize;
}
/**
* 返回元素最少列的列名称
* @return 元素最少列的列名称
*/
public List<String> getMinColumnName() {
return minColumnNameList;
}
@Override
public void add(String name) {
add(new ElementInformation(name, null, ElementType.DATA_LIST_ELEMENT));
}
@Override
public void add(String name, ByType byType) {
add(new ElementInformation(name, byType, ElementType.DATA_LIST_ELEMENT));
}
@Override
void add(ElementInformation elementInformation) {
//重写父类的add方法使元素能进行极值的统计
super.add(elementInformation);
//判断当前获取到的元素是否大于当前存储的元素的最大值或小于当前存储的最小值若是则将最大值或最小值进行替换
findLimitColumn(elementInformation);
}
/**
* 用于返回列表多个指定的元素传入的下标可参见{@link #getWebElement(String, int)}
*
* @param name 列名称
* @param indexs 一组元素下标即列表中对应的某一个元素
* @return 对应列多个指定下标的元素
* @throws NoSuchElementException 当未对name列进行获取数据或index的绝对值大于列表最大值时抛出的异常
* @see #getWebElement(String, int)
*/
public List<Element> getElements(String name, int... indexs) {
// 存储所有获取到的事件
ArrayList<Element> events = new ArrayList<>();
// 循环解析所有的下标并调用getEvent()方法存储至events
for (int index : indexs) {
events.add(getElement(name, index));
}
return events;
}
/**
* 用于返回列表中指定随机个数的元素
*
* @param name 列名称
* @param length 需要返回列表事件的个数
* @return 列表事件组
*/
public List<Element> getRandomElements(String name, int length) {
//获取元素信息并判断元素是否存在不存在则抛出异常
ElementInformation element = nameToElementInformation(name);
if (element == null) {
throw new NoSuchElementException("不存在的定位方式:" + name);
}
// 判断传入的长度是否大于等于当前
if (length >= elementMap.get(element).size()) {
return getAllElement(name);
}
// 存储通过随机得到的数字
ArrayList<Integer> indexsList = new ArrayList<Integer>();
int randomLength = elementMap.get(element).size() + 1;
// 循环随机获取下标数字
for (int i = 0; i < length; i++) {
int randomIndex = 0;
// 循环直到生成的随机数不在indexs中为止
while (indexsList.contains(randomIndex = new Random().nextInt(randomLength))) {
}
indexsList.add(randomIndex);
}
// 将indexsList转换成int[]
int[] indexs = new int[indexsList.size()];
for (int i = 0; i < indexsList.size(); i++) {
indexs[i] = indexsList.get(i);
}
return getElements(name, indexs);
}
/**
* 用于根据参数求取elementMap中最多或最少列表的元素个数以及列表的名称
* @param key 需要计算的最小值
* @return 极值以及极值所在的列
*/
private void findLimitColumn(ElementInformation key) {
//获取指向的元素列表的元素个数
int size = elementMap.get(key).size();
//根据参数判断获取的列是否为最大值所在的列并对极值做进一步判断
if (maxColumnSize < size) {
maxColumnNameList.clear();
maxColumnSize = size;
maxColumnNameList.add(key.name);
} else if (maxColumnSize == size) {
maxColumnNameList.add(key.name);
public Set<Element> getRandomElement(int length) {
//存储随机得到的数字
Set<Element> elementSet = new HashSet<>();
//循环获取随机数直到其numberSet的长度与传入的个数一致为止
while(elementSet.size() < length) {
elementSet.add(getElement(0));
}
if (minColumnSize > size) {
minColumnNameList.clear();
minColumnSize = size;
minColumnNameList.add(key.name);
} else if (minColumnSize == size) {
minColumnNameList.add(key.name);
}
}
@Override
boolean isExistElement(By by, long waitTime) {
//TODO 修改等待方法改为获取到元素后才返回相应的状态
//当查找到元素时则返回true若查不到元素则会抛出异常故返回false
return new WebDriverWait(driver, waitTime, 200).
until((driver) -> {
List<WebElement> elementList = driver.findElements(by);
//若获取的标题首行为标题行时则判断为获取到大于1个元素时返回true否则则大于0个元素返回true
if (isFristRowTitle) {
return elementList.size() > 1;
} else {
return elementList.size() > 0;
}
});
return elementSet;
}
}

View File

@ -1,129 +0,0 @@
package pres.auxiliary.work.selenium.element;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.Select;
/**
* <p><b>文件名</b>Element.java</p>
* <p><b>用途</b>
* 用于返回和查找页面元素以方便在元素过期时能进行重新获取
* </p>
* <p><b>编码时间</b>2020年5月18日上午8:39:05</p>
* <p><b>修改时间</b>2020年5月18日上午8:39:05</p>
* @author 彭宇琦
* @version Ver1.0
* @since JDK 8
*
*/
public class Element {
/**
* 存储元素
*/
private WebElement element = null;
/**
* 存储查找元素的By对象
*/
private By by;
/**
* 存储获取需要获取的元素下标
*/
private int index;
/**
* 存储WebDriver对象以查找相应元素
*/
private WebDriver driver;
/**
* 用于存储元素的名称
*/
private String name;
/**
* 标记元素的类型以用于重新获取时
*/
private ElementType elementType;
/**
* 初始化信息并添加需要获取元素的下标用于需要获取列表时使用
* @param name 元素名称或定位内容
* @param byType 元素定位
*/
public Element(WebDriver driver, ElementType elementType, By by, String name, int index) {
super();
this.by = by;
this.index = index;
this.driver = driver;
this.elementType = elementType;
this.name = name;
}
/**
* 初始化信息指定获取第一个元素用于只获取单个元素时使用
* @param name 元素名称或定位内容
* @param byType 元素定位
*/
public Element(WebDriver driver, ElementType elementType, By by, String name) {
this(driver, elementType, by, name, 0);
}
/**
* 用于返回元素对应的WebElement对象
* @return 返回元素对应的WebElement对象
*/
public WebElement getWebElement() {
//若元素未进行查找则查找一次元素
if(element == null) {
//对元素进行一次查找
findElement();
}
return element;
}
/**
* 根据存储的WebDriver对象对元素进行更新
* @throws Exception
*/
public void findElement() {
switch (elementType) {
case COMMON_ELEMENT:
case DATA_LIST_ELEMENT:
case SELECT_DATAS_ELEMENT:
element = driver.findElements(by).get(index);
break;
case SELECT_OPTION_ELEMENT:
element = new Select(driver.findElement(by)).getOptions().get(index);
break;
default:
throw new IllegalArgumentException("Unexpected value: " + elementType);
}
}
/**
* 用于返回元素 的名称
* @return 元素名称
*/
public String getName() {
return name;
}
/**
* 用于返回元素信息输出格式元素名称 + 元素 + 列表选项
* @return 元素信息
*/
public String getLog() {
switch (elementType) {
case COMMON_ELEMENT:
return name + "元素";
case DATA_LIST_ELEMENT:
case SELECT_DATAS_ELEMENT:
case SELECT_OPTION_ELEMENT:
return name + "元素第" + index + "个选项";
default:
throw new IllegalArgumentException("Unexpected value: " + elementType);
}
}
}

View File

@ -1,6 +1,8 @@
package pres.auxiliary.work.selenium.element;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import pres.auxiliary.work.selenium.location.AbstractRead;
import pres.auxiliary.work.selenium.location.ByType;
@ -40,10 +42,11 @@ public class ElementData {
* 存储元素
*/
private long waitTime;
/**
* 存储当前采用的读取文件的方式
* 用于存储外链的词语
*/
private AbstractRead read;
private ArrayList<String> linkWordList = new ArrayList<>();
/**
* 根据元素名称在配置文件中查找元素将元素的信息进行存储
@ -53,8 +56,6 @@ public class ElementData {
public ElementData(String name, AbstractRead read) {
//存储元素名称
this.name = name;
//存储读取文件的方式
this.read = read;
//根据传入的读取配置文件类对象使用其中的返回方法初始化元素信息
byTypeList = read.findElementByTypeList(name);
@ -85,6 +86,33 @@ public class ElementData {
* @return 元素定位内容集合
*/
public ArrayList<String> getValueList() {
//若存储的外链词语不为空则对需要外链的定位内容进行处理
if (!linkWordList.isEmpty()) {
for (int i = 0; i < valueList.size(); i++) {
//判断字符串是否包含替换词语的开始标志若不包含则进行不进行替换操作
if (!valueList.get(i).contains(AbstractRead.START_SIGN)) {
continue;
}
//获取替换词语集合的迭代器
Iterator<String> linkWordIter = linkWordList.iterator();
//存储当前定位内容文本
StringBuilder value = new StringBuilder(valueList.get(i));
//循环替换当前定位内容中所有需要替换的词语直到无词语替换或定位内容不存在需要替换的词语为止
while(linkWordIter.hasNext() && value.indexOf(AbstractRead.START_SIGN) > -1) {
//存储替换符的开始和结束位置
int replaceStartIndex = value.indexOf(AbstractRead.START_SIGN);
int replaceEndIndex = value.indexOf(AbstractRead.END_SIGN);
//对当前位置的词语进行替换
value.replace(replaceStartIndex, replaceEndIndex + 1, linkWordIter.next());
}
//存储当前替换后的定位内容
valueList.set(i, value.toString());
}
}
return valueList;
}
@ -113,11 +141,49 @@ public class ElementData {
}
/**
* 返回元素读取方式类对象
* @return {@link AbstractRead}子类对象
* 用于添加元素定位外链词语
* @param linkWords 外链词语
*/
public AbstractRead getRead() {
return read;
public void addLinkWord(String...linkWords) {
if (linkWords != null && linkWords.length != 0) {
linkWordList.addAll(Arrays.asList(linkWords));
}
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((iframeNameList == null) ? 0 : iframeNameList.hashCode());
result = prime * result + ((linkWordList == null) ? 0 : linkWordList.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ElementData other = (ElementData) obj;
if (iframeNameList == null) {
if (other.iframeNameList != null)
return false;
} else if (!iframeNameList.equals(other.iframeNameList))
return false;
if (linkWordList == null) {
if (other.linkWordList != null)
return false;
} else if (!linkWordList.equals(other.linkWordList))
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}

View File

@ -1,197 +0,0 @@
package pres.auxiliary.work.selenium.element;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import org.openqa.selenium.By;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.WebDriver;
import pres.auxiliary.work.selenium.brower.AbstractBrower;
/**
* <p><b>文件名</b>ListElement.java</p>
* <p><b>用途</b>
* 提供获取列表类型元素时的基本方法
* </p>
* <p><b>编码时间</b>2020年5月22日上午7:54:55</p>
* <p><b>修改时间</b>2020年5月22日上午7:54:55</p>
* @author 彭宇琦
* @version Ver1.0
* @since JDK 12
*
*/
public abstract class ListBy extends MultiBy {
/**
* 用于存储获取到的列表一列元素key为列表名称value为列表元素
*/
LinkedHashMap<ElementInformation, List<Element>> elementMap = new LinkedHashMap<>(16);
/**
* 通过浏览器对象{@link AbstractBrower}进行构造
* @param brower {@link AbstractBrower}对象
*/
public ListBy(AbstractBrower brower) {
super(brower);
}
/**
* 构造对象并存储浏览器的{@link WebDriver}对象
*
* @param driver 浏览器的{@link WebDriver}对象
*/
public ListBy(WebDriver driver) {
super(driver);
}
/**
* 返回列表名称对应的元素个数若该列未被获取则返回-1
* @param name 被获取的列名称
* @return 列名对应列的元素个数
*/
public int getSize(String name) {
ElementInformation element = nameToElementInformation(name);
if (element != null) {
return elementMap.get(element).size();
} else {
return -1;
}
}
/**
* 该方法用于返回所有列的名称
* @return 所有列的名称
*/
public ArrayList<String> getNames() {
ArrayList<String> nameList = new ArrayList<>();
elementMap.forEach((key, value) -> {
nameList.add(key.name);
});
return nameList;
}
@Override
void add(ElementInformation elementInformation) {
//判断传入的元素是否在xml文件中若存在再判断是否自动切换窗体若需要则获取元素的所有父窗体并进行切换
if (xml != null && xml.isElement(elementInformation.name) && isAutoSwitchIframe) {
switchFrame(getParentFrameName(elementInformation.name));
}
List<Element> elementList = new ArrayList<Element>();
//获取元素
By by = recognitionElement(elementInformation);
int size = driver.findElements(by).size();
//构造Element对象
for (int i = 0; i < size; i++) {
elementList.add(new Element(driver, ElementType.DATA_LIST_ELEMENT, by, elementInformation.name, i));
}
//elementList = driver.findElements(recognitionElement(elementInformation));
//添加元素
elementMap.put(elementInformation, elementList);
}
/**
* 用于清除或移除指定的列及列中的元素当参数传入false时则只清理列表中存储的元素不移除
* 整个列若传入true时则直接移除整个列若列名称对应的列未被获取则返回null
* @param name 已被获取的元素列名称
* @param isRemove 是否需要将该列移除
* @return 被移除列中存储的所有元素
*/
public List<Element> clearColumn(String name, boolean isRemove) {
ElementInformation element = nameToElementInformation(name);
//若元素不存在则直接返回null
if (element == null) {
return null;
}
//用于存储被移除的元素
List<Element> elementList = elementMap.get(element);
//判断元素是否需要被完全移除
if (isRemove) {
//若元素需要被完全移除则直接移除元素
elementMap.remove(element);
//由于元素被移除若该列存在元素个数统计则同样将该元素移除
/*
if (elementSizeMap.containsKey(name)) {
elementSizeMap.remove(name);
}
*/
} else {
//若元素无需移除则将元素存储的列表内容清空
elementMap.get(element).clear();
}
return elementList;
}
@Override
public void againGetElement() {
// 读取elements中的元素
elementMap.forEach((key, value) -> {
// 清空元素中的内容
clearColumn(key.name, false);
// 对页面内容重新进行获取
add(key);
});
}
/**
* 根据元素名称反推元素信息类对象用于根据列名称查找数据以及判断列是否存在若列名不存在则返回null
* @return ElementInformation对象
*/
ElementInformation nameToElementInformation(String name) {
//遍历elementMap若查找与name一致的名称则结束循环并返回相应的ElementInformation对象
for (ElementInformation element : elementMap.keySet()) {
if (element.name.equals(name)) {
return element;
}
}
return null;
}
/**
* 该方法用于根据列名称查找到相应的列并返回与传入下标对应的元素下标支持从后向前获取传入的下标
* 与元素实际所在位置一致当传入0时则表示随机获取一个元素<br>
* {@code getWebElement("姓名", 1)}表示获取名称为姓名的列中的第1个元素<br>
* {@code getWebElement("姓名", 0)}表示获取名称为姓名的列中在长度范围内随机一个元素<br>
* {@code getWebElement("//*[@id='name']", -1)}表示获取//*[@id='name']对应列中的倒数第1个元素<br>
*
* @param name 列名称
* @param index 元素下标即列表中对应的某一个元素
* @return 对应列指定的元素
* @throws NoSuchElementException 当未对name列进行获取数据或index的绝对值大于列表最大值时抛出的异常
*/
public Element getElement(String name, int index) {
//获取元素信息并判断元素是否存在不存在则抛出异常
ElementInformation element = nameToElementInformation(name);
if (element == null) {
throw new NoSuchElementException("不存在的定位方式:" + name);
}
// 转义下标
index = getIndex(elementMap.get(element).size(), index, true);
// 转义下标后返回对应的元素
return elementMap.get(element).get(index);
}
/**
* 该方法用于根据列名称获取该列下所有的元素
*
* @param name 列名称
* @return 对应列元素
* @throws NoSuchElementException 当未对name列进行获取数据时抛出的异常
*/
public List<Element> getAllElement(String name) {
//获取元素信息并判断元素是否存在不存在则抛出异常
ElementInformation element = nameToElementInformation(name);
if (element == null) {
throw new NoSuchElementException("不存在的定位方式:" + name);
}
return elementMap.get(element);
}
}

View File

@ -1,115 +1,186 @@
package pres.auxiliary.work.selenium.element;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.TimeoutException;
import org.openqa.selenium.WebDriver;
import pres.auxiliary.work.selenium.brower.AbstractBrower;
import pres.auxiliary.work.selenium.xml.ByType;
import pres.auxiliary.work.selenium.element.AbstractBy.Element;
/**
* <p><b>文件名</b>MultiElement.java</p>
* <p><b>用途</b>
* 提供获取多个元素时使用的基本方法
* <p>
* <b>文件名</b>ListBy.java
* </p>
* <p><b>编码时间</b>2020年5月22日上午7:54:28</p>
* <p><b>修改时间</b>2020年5月22日上午7:54:28</p>
* @author 彭宇琦
* <p>
* <b>用途</b> 提供对多元素获取与返回的基本方法
* </p>
* <p>
* <b>编码时间</b>2020年10月14日下午6:54:46
* </p>
* <p>
* <b>修改时间</b>2020年10月14日下午6:54:46
* </p>
*
* @author
* @version Ver1.0
* @since JDK 12
*
*/
public abstract class MultiBy extends AbstractBy {
/**
* 通过浏览器对象{@link AbstractBrower}进行构造
* @param brower {@link AbstractBrower}对象
* 用于记录当前元素集合中第一个元素是否为不可选择的元素用于对元素的随机返回
*/
protected boolean firstEmpty = false;
/**
* 构造方法初始化浏览器对象
*
* @param brower {@link AbstractBrower}类对象
*/
public MultiBy(AbstractBrower brower) {
super(brower);
}
/**
* 构造对象并存储浏览器的{@link WebDriver}对象
* 用于设置当前元素集合中首元素是否为允许随机返回的元素<br>
* 例如获取列表元素时首元素为标题元素获取下拉选项元素集合时 第一个元素为空选项或者为类似请选择等选项
*
* @param driver 浏览器的{@link WebDriver}对象
* @param firstEmpty 首元素是否为允许随机返回的元素
*/
public MultiBy(WebDriver driver) {
super(driver);
public void setFirstEmpty(boolean firstEmpty) {
this.firstEmpty = firstEmpty;
}
/**
* 用于根据传入的元素在xml文件中的名称或者元素的定位内容添加元素由于该方法不指定元素的定位
* 方式若传入的参数不是xml元素且非xpath路径或绝对css路径时其识别效率较慢建议在该情况下
* 调用{@link #add(String, ByType)}方法指定元素定位方法
* @param name 元素在xml文件或者元素的定位内容
* @see #add(String, ByType)
* @throws TimeoutException 元素在指定时间内无法找到时抛出的异常
* 用于指定查找的元素集合名称二次查找元素时将覆盖上一次查找的元素调用该方法在
* 无法查到页面元素时其不会抛出{@link TimeoutException}异常
*
* @param elementName 元素名称
* @param linkKeys 外链词语
*/
public abstract void add(String name);
public void find(String elementName, String... linkKeys) {
//根据元素名称获取元素信息数据
elementData = new ElementData(elementName, read);
elementData.addLinkWord(linkKeys);
//判断是否需要自动切换窗体若需要则对元素窗体进行切换
if (isAutoSwitchIframe) {
autoSwitchFrame(elementData.getIframeNameList());
}
//获取元素数据在页面上对应的一组元素若无法查到元素则记录elementList为null
try {
elementList = recognitionElement(elementData);
} catch (TimeoutException e) {
elementList = new ArrayList<>();
}
}
/**
* 用于根据传入的元素在xml文件中的名称或者元素的定位内容以及元素的定位方式添加元素
* @param name 元素在xml文件或者元素的定位内容
* @param byType 元素定位方式枚举对象{@link ByType}枚举
* @see #add(String)
* @throws TimeoutException 元素在指定时间内无法找到时抛出的异常
* 用于返回当前存储的元素集合长度若未对元素进行获取则返回0
*
* @return 元素集合长度
*/
public abstract void add(String name, ByType byType);
public int size() {
return elementList == null ? 0 : elementList.size();
}
/**
* 添加元素的底层方法
* @param elementInformation 元素信息类对象
* @param isAddSize 是否需要统计
* 用于移除元素集合中的指定下标的元素其下标所传入的数字即为元素所在的真实下标可参见
* {@link #getElement(int)}方法的参数说明若未调用{@link #find(String, String...)}
* 方法对元素进行查找或者查无元素时调用该方法将不进行任何操作
*
* @param index 元素下标
* @return 是否删除成功
*/
abstract void add(ElementInformation elementInformation);
public boolean removeElement(int index) {
//当且仅当元素集合存在并且下标传入正确时将元素删除并返回成功
if (elementList != null && elementList.size() != 0) {
int newIndex = toElementIndex(elementList.size(), index);
if (newIndex != -1) {
elementList.remove(toElementIndex(elementList.size(), index));
return true;
}
}
return false;
}
/**
* 该方法用于根据存入的元素名称或定位方式对元素进行重新获取的操作主要用于当列表数据翻页后
* 其原存入的数据将会失效必须重新获取注意调用该方法后会清空原存储的数据
* <p>
* 用于获取元素集合中指定下标的元素该下标允许反向遍历与随机返回其下标所传入的数字即为元素所在的真实下标
* </p>
* <p>
* 例如
* <ul>
* <li>传入1时表示获取元素集合中的第1个元素</li>
* <li>传入0时表示获取元素集合中在长度范围内随机一个元素</li>
* <li>传入-1时表示获取元素集合中的倒数第1个元素</li>
* </ul>
* </p>
* <p>
* <b>注意</b>调用该方法前若未调用{@link #find(String, String...)}方法对元素进行查找
* 或者查无元素时调用该方法不会抛出异常但当调用{@link Element#getWebElement()}方法时
* 由于未查到元素则会抛出异常
* </p>
*
* @param index 元素下标
* @return {@link Element}类对象
*/
public abstract void againGetElement();
public Element getElement(int index) {
return new Element(toElementIndex(elementList.size(), index));
}
/**
* 用于清除或移除指定的列及列中的元素当参数传入false时则只清理列表中存储的元素不移除
* 整个列若传入true时则直接移除整个列若列名称对应的列未被获取则返回null
* @param name 已被获取的元素列名称
* @param isRemove 是否需要将该列移除
* @return 被移除列中存储的所有元素
* 用于返回当前元素集合中所有的元素集合若未调用{@link #find(String, String...)}方法对元素进行查找
* 或者查无元素时则调用该方法时会抛出超时异常
*
* @return {@link Element}类对象{@link List}集合
* @throws TimeoutException 元素在页面不存在时抛出的异常
*/
// public abstract List<Element> clearColumn(String name, boolean isRemove);
public List<Element> getAllElement() {
//定义返回元素的集合
List<Element> elementList = new ArrayList<>();
//判断当前列表是否为空若为空则向集合中添加一条假数据以便于后续调用时抛出异常
if (this.elementList == null || this.elementList.size() == 0) {
throw new TimeoutException("页面上无相应定位方式的元素,元素名称:" + elementData.getName());
}
//循环遍历类中的elementList根据其长度构造Elemenet对象并进行添加
for (int index = 0; index < this.elementList.size(); index++) {
elementList.add(new Element(index));
}
return elementList;
}
/**
* 由于方法允许传入负数和特殊数字0为下标并且下标的序号由1开始
* 故可通过该方法对下标的含义进行转义得到java能识别的下标
* 由于方法允许传入负数和特殊数字0为下标并且下标的序号由1开始故可通过该方法对下标的含义进行转义得到java能识别的下标
*
* @param length 元素的个数
* @param index 传入的下标
* @param randomZero 标记是否可以随机出数字0
* @param index 传入的下标
* @return 可识别的下标
* @throws NoSuchElementException 当元素无法查找到时抛出的异常
*/
int getIndex(int length, int index, boolean randomZero) {
//判断元素下标是否超出范围由于可以传入负数故需要使用绝对值
protected int toElementIndex(int length, int index) {
// 判断元素下标是否超出范围由于可以传入负数故需要使用绝对值
if (Math.abs(index) > length) {
throw new NoSuchElementException("指定的选项值大于选项的最大值。选项总个数:" + length + ",指定项:" + index);
return -1;
}
//判断index的值若大于0则从前向后遍历若小于0则从后往前遍历若等于0则随机输入
// 判断index的值若大于0则从前向后遍历若小于0则从后往前遍历若等于0则随机输入
if (index > 0) {
//选择元素正数的选项值从1开始故需要减小1
// 选择元素正数的选项值从1开始故需要减小1
return index - 1;
} else if (index < 0) {
//选择元素由于index为负数则长度加上选项值即可得到需要选择的选项
// 选择元素由于index为负数则长度加上选项值即可得到需要选择的选项
return length + index;
} else {
//为0则进行随机选择但需要判断是否允许随机出0第一个元素
int newindex = 0;
do {
newindex = new Random().nextInt(length);
} while(newindex == 0 && !randomZero);
return newindex;
Random ran = new Random();
return firstEmpty ? (ran.nextInt(length - 1) + 1) : ran.nextInt(length);
}
}
}

View File

@ -1,240 +1,138 @@
package pres.auxiliary.work.selenium.element;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import org.openqa.selenium.By;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.Select;
import org.openqa.selenium.support.ui.WebDriverWait;
import pres.auxiliary.work.selenium.brower.AbstractBrower;
import pres.auxiliary.work.selenium.xml.ByType;
import pres.auxiliary.work.selenium.element.AbstractBy.Element;
/**
* <p><b>文件名</b>SelectBy.java</p>
* <p><b>用途</b>
* 提供获取下拉框中选项元素的方法支持标准型下拉框选项由select与option标签组成的下拉框以及
* 非标准型下拉框选项由普通的divli等元素组成的选项并支持根据关键词查找选项需要注意的是
* 标准下拉选项和非标准下拉选项需要传入的参数不同例如<br>
* 标准下拉框<br>
* &lt;select&nbsp;id='test'&gt;<br>
* &emsp;&lt;option&gt;&lt;/option&gt;<br>
* &emsp;&lt;option&gt;&lt;/option&gt;<br>
* &emsp;&lt;option&gt;其他&lt;/option&gt;<br>
* &lt;/select&gt;<br>
* 对于该标准的下拉框选项只需要定位到//select[@id='test']得到其WebElement对象即可但对于非标准的下拉框其下拉框是由input和button标签构成<br>
* &lt;div&gt;<br>
* &emsp;&lt;span&gt;&lt;input/&gt;&lt;/span&gt;<br>
* &emsp;&lt;span&gt;&lt;button/&gt;&lt;/span&gt;<br>
* &lt;/div&gt;<br>
* 点击button对应的按钮后其下也能弹出选项但其选项是由div标签写入text构成<br>
* &lt;div&nbsp;id='test'&gt;<br>
* &emsp;&lt;div&gt;&lt;/div&gt;<br>
* &emsp;&lt;div&gt;&lt;/div&gt;<br>
* &emsp;&lt;div&gt;其他&lt;/div&gt;<br>
* &lt;/div&gt;<br>
* 对于这种非标准的下拉框选项需要传入选项所在的所有div标签对应的WebElement元素在上例则需要定位到//div[@id='test']/div
* 注意末尾的div不指定数字则可以代表整个选项<br>
* </p>
* <p><b>编码时间</b>2020年5月24日下午3:30:00</p>
* <p><b>修改时间</b>2020年5月24日下午3:30:00</p>
* @author 彭宇琦
* @version Ver1.0
* @since JDK 12
*
*/
public class SelectBy extends MultiBy {
/**
* 用于存储获取下拉选项时的信息
* 定义标准下拉选项的tagname
*/
ElementInformation elementInfo;
/**
* 用于存储下拉选项的元素
*/
ArrayList<Element> option = new ArrayList<>();
/**
* 用于存储下拉选项的文本
*/
ArrayList<String> optionText = new ArrayList<>();
public static final String SELECT_TAGNAME = "select";
/**
* 设置标记的下拉选项的类型
* 用于存储当前元素列表中所有选项的文本内容
*/
private ElementType elementType;
private List<String> selectTextList = new ArrayList<>();
/**
* 用于存储按照文本返回下拉选项时其名称读取的属性名
*/
private String attributeName = "text";
/**
* 控制元素首行是否为
*/
private boolean fristIsEmpty = false;
/**
* 通过浏览器对象{@link AbstractBrower}进行构造
*
* @param brower {@link AbstractBrower}对象
* 构造方法初始化浏览器对象
* @param brower {@link AbstractBrower}类对象
*/
public SelectBy(AbstractBrower brower) {
super(brower);
}
/**
* 构造对象并存储浏览器的{@link WebDriver}对象
* <p>
* 用于设置获取文本时读取的属性名称即在调用{@link #getElement(String)}方法时其元素内容的来源
* </p>
* <p>
* 该方法存在设置一个特殊的属性值text默认表示获取元素的文本节点内容其他的值均为
* 设置读取的属性值建议设置的属性值的内容不存在重复否则获取到的元素可能与预期不符
* </p>
* <p>
* <b>注意</b>调用该方法时将自动获取一次元素内容若未调用{@link #find(String, String...)}方法查找
* 元素或查找的元素不存在时调用该方法仅设置属性值设置的属性名称不区分大小写
* </p>
*
* @param driver 浏览器的{@link WebDriver}对象
* @param arributeName 属性名称
*/
public SelectBy(WebDriver driver) {
super(driver);
}
/**
* 设置首个选项是否为不可选择的选项
* @param fristIsEmpty 首个选项是否为不可选择
*/
public void setFristIsEmpty(boolean fristIsEmpty) {
this.fristIsEmpty = fristIsEmpty;
public void setReadArributeName(String attributeName) {
this.attributeName = attributeName;
//判断当前类中是否存在元素若不存在则结束运行
if (elementList == null || elementList.size() == 0) {
return;
}
//存在元素则对元素内容按照设置的查找方式进行查找
findElementContent();
}
@Override
public void add(String name, ByType byType) {
add(new ElementInformation(name, byType, ElementType.SELECT_OPTION_ELEMENT));
}
@Override
public void add(String name) {
add(new ElementInformation(name, null, ElementType.SELECT_OPTION_ELEMENT));
}
@Override
void add(ElementInformation elementInformation) {
//判断传入的元素是否在xml文件中若存在再判断是否自动切换窗体若需要则获取元素的所有父窗体并进行切换
if (xml != null && xml.isElement(elementInformation.name) && isAutoSwitchIframe) {
switchFrame(getParentFrameName(elementInformation.name));
}
//清除原存储的内容
clear();
//获取元素的By对象
By by = recognitionElement(elementInformation);
//根据By获取元素
List<WebElement> elementList = driver.findElements(by);
//获取元素个数
int size = elementList.size();
//根据第一个元素的tagname来判断是否为标准下拉元素
if ("select".equalsIgnoreCase(elementList.get(0).getTagName())) {
elementType = ElementType.SELECT_OPTION_ELEMENT;
//若是标准下拉选项型则需要改变size的值方便后续添加数据
Select select = new Select(driver.findElement(by));
//获取所有的选项内容并计算元素个数
elementList = select.getOptions();
size = elementList.size();
} else {
elementType = ElementType.SELECT_DATAS_ELEMENT;
public void find(String elementName, String... linkKeys) {
super.find(elementName, linkKeys);
//判断当前存储的元素集合中是否为空若为空则直接返回
if (elementList == null || elementList.size() == 0) {
return;
}
//构造Element对象
for (int i = 0; i < size; i++) {
//获取元素
option.add(new Element(driver, elementType, by, elementInformation.name, i));
//获取元素的文本内容
optionText.add(elementList.get(i).getText());
}
}
/**
* 根据选项下标返回相应的选项元素下标与选项元素真实下标一致支持传入负数表示从后向前遍历元素
* 当传入0时表示随机选择一个选项
* @param index 选项下标
* @return 相应的选项元素
*/
public Element getElement(int index) {
//当首选项为空时则在随机时不允许产生0
return option.get(getIndex(option.size(), index, !fristIsEmpty));
}
/**
* 根据关键词组查找选项并返回选项元素当传入的元素名称不存在时则抛出NoSuchElementException异常
* 当查询到有多个包含关键词的选项时则选择第一个选项<br>
* 注意当传入多个关键词时其选项需要全部满足才会返回相应的选项
*
* @param keys 查询选项的关键词组
* @return 相应的选项元素
* @throws NoSuchElementException 查找的选项不存在时抛出的异常
*/
public Element getElement(String...keys) {
//查找完全符合关键词的元素
String elementName = optionText.stream().filter(text -> {
//遍历关键词若元素不符合条件则返回false
for (String key : keys) {
if (text.indexOf(key) < 0) {
return false;
}
//判断当前元素集合中第一个元素的tagName是否为select若为select则表示其为标准下拉
//选项元素则调用标准下拉选项方式获取选项元素并存储若不是则不进行处理
if (SELECT_TAGNAME.equals(elementList.get(0).getTagName())) {
elementList = new Select(elementList.get(0)).getOptions();
if (elementList == null || elementList.size() == 0) {
return;
}
//若条件均符合则返回true
return true;
}).findFirst().orElseThrow(() -> {
//若不存在符合条件的选项则抛出NoSuchElementException异常并返回相应的消息
StringBuilder keyText = new StringBuilder("[");
//拼接查询条件
Arrays.stream(keys).forEach(key -> {
keyText.append(key + ", ");
});
return new NoSuchElementException("不存在符合条件的选项:" + keyText.substring(0, keyText.length() - ", ".length()) + "]");
});
}
return option.get(optionText.indexOf(elementName));
}
@Override
public void againGetElement() {
clear();
add(elementInfo);
}
@Override
boolean isExistElement(By by, long waitTime) {
//当查找到元素时则返回true若查不到元素则会抛出异常故返回false
return new WebDriverWait(driver, waitTime, 200).
until((driver) -> {
List<WebElement> elements = driver.findElements(by);
//根据是否能查找到元素进行判断
if (elements.size() > 0) {
//若获取到的第一个元素的标签名为select标准下拉则可以直接返回true
if ("select".equals(elements.get(0).getTagName())) {
return true;
}
//若查到元素再进一步判断元素内容是否完全加载
int textSize = elements.stream().filter(element -> {
return !element.getText().isEmpty();
}).collect(Collectors.toList()).size();
//若首选项为空时则加载的内容必须大于或等于总选项个数-1
//若首选项不为空时则加载的内容必须与原选项个数一致
if (fristIsEmpty) {
return textSize >= elements.size() - 1;
} else {
return textSize == elements.size();
}
} else {
return false;
}
});
//按照设置的查找方式查找元素内容
findElementContent();
}
/**
* 用于清除原存储的内容
* 用于获取元素集合中指定内容的元素其元素内容可以调用{@link #setReadArributeName(String)}方法进行设置
* 若元素所传的元素内容不在当前元素内容集合中时该方法将不会抛出异常但当调用
* {@link Element#getWebElement()}方法时则会抛出元素不存在异常
*
* @param selectText 元素的内容
* @return 相应内容的元素
*/
void clear() {
option.clear();
optionText.clear();
public Element getElement(String selectText) {
//判断当前内容是否存在于selectTextList中存在则获取下标并返回相应的元素不存在则构造元素下标为-1
int index = selectTextList.indexOf(selectText);
if (index > -1) {
return getElement(index);
} else {
return new Element(-1);
}
}
@Override
public boolean removeElement(int index) {
if (super.removeElement(index)) {
selectTextList.remove(index);
return true;
}
return false;
}
/**
* 用于根据选项内容移除元素
* @param selectText 下拉选项内容
* @return 是否移除成功
*/
public boolean removeElement(String selectText) {
//判断当前内容是否存在于selectTextList中存在则获取下标并返回相应的元素不存在则构造元素下标为-1
int index = selectTextList.indexOf(selectText);
if (index > -1) {
return removeElement(index);
} else {
return false;
}
}
/**
* 用于根据设置查找元素内容并进行存储
*/
private void findElementContent() {
selectTextList = elementList.stream().map(element -> {
if ("text".equalsIgnoreCase(attributeName)) {
return element.getText();
} else {
return element.getAttribute(attributeName);
}
}).collect(Collectors.toList());
}
}

View File

@ -1,424 +0,0 @@
package pres.auxiliary.work.selenium.element.delect;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Pattern;
import org.openqa.selenium.By;
import org.openqa.selenium.TimeoutException;
import pres.auxiliary.work.selenium.brower.AbstractBrower;
import pres.auxiliary.work.selenium.xml.ByType;
import pres.auxiliary.work.selenium.xml.ReadXml;
/**
* <p><b>文件名</b>AbstractElement.java</p>
* <p><b>用途</b></p>
* <p><pre>
* 对辅助化测试工具selenium的获取元素代码进行的二次封装通过类中提供的方法以及配合相应存储元素的
* xml文件以更简便的方式对页面元素进行获取减少编程时的代码量
* </pre></p>
* <p><b>编码时间</b>2020年4月25日 下午4:18:37</p>
* <p><b>修改时间</b>2020年4月25日 下午4:18:37</p>
* @author 彭宇琦
* @version Ver1.0
* @since JDK 12
*/
public abstract class AbstractBy {
/**
* 用于指向存储控件定位方式的xml文件设置属性为静态用于在编写脚本时不需要频繁切换xml文件
*/
static ReadXml xml;
/**
* 用于存储浏览器对象
*/
AbstractBrower brower;
/**
* 存储单个控件的等待时间
*/
private HashMap<String, Long> controlWaitTime = new HashMap<String, Long>();
/**
* 用于存储元素通用的等待时间默认5秒
*/
private long waitTime = 5;
/**
* 通过浏览器对象{@link AbstractBrower}进行构造
* @param brower {@link AbstractBrower}对象
*/
public AbstractBy(AbstractBrower brower) {
this.brower = brower;
}
/**
* 用于设置元素查找等待时间默认时间为5秒
*
* @param waitTime 事件等待时间
*/
public void setWaitTime(long waitTime) {
this.waitTime = waitTime;
}
/**
* 用于对符合正则表达式的控件名称设置等待时间
*
* @param regex 正则表达式
* @param waitTime 等待时间
*/
public void setContorlWaitTime(String regex, long waitTime) {
controlWaitTime.put(regex, waitTime);
}
/**
* 用于设置指向存储元素定位方式的xml文件对象并根据传参判断窗体是否需要回到顶层
* @param xmlFile 存储元素定位方式的xml文件对象
* @param isBreakRootFrame 是否需要将窗体切回到顶层
*/
public void setXmlFile(File xmlFile, boolean isBreakRootFrame) {
if (xml == null) {
xml = new ReadXml(xmlFile);
} else {
xml.setXmlPath(xmlFile);
}
//若需要切回顶层则切换到顶层窗体
if (isBreakRootFrame) {
brower.getDriver().switchTo().defaultContent();
}
}
/**
* <p>
* 用于根据传入的控件名称或定位方式对控件在页面上定位返回其WebElement对象形参可以传入在xml文件中元素的名称
* 亦可以传入页面元素的定位方式但目前识别只支持xpath和css两种方式
* 该方法获取的是一组元素可用于对列表元素事件中
* </p>
* <p>
* 元素识别判断方式按照以下步骤进行<br>
* 1.先对xml文件进行扫描若存在该元素对应的标签则读取xml文件的定位方式并识别有效的定位方式一一匹配直到正确为止<br>
* 2.若在xml文件中查找不到该元素则按照xpath和css的规则进行匹配直到判断出该元素的定位方式位置<br>
* 3.若仍找不到元素则抛出UnrecognizableLocationModeException
* </p>
*
* @param name 元素名称或元素的定位方式
* @return 返回页面一组元素的定位方式对象
* @throws TimeoutException 元素在指定时间内未查找到时抛出的异常
* @throws UnrecognizableLocationModeException 元素无法识别时抛出的异常
*/
List<By> recognitionElement(ElementInformation elementInformation) {
if (isXmlElement(elementInformation.name)) {
//若指定了xml文件且传入的元素名称存在与xml文件中则判断元素相应的定位方式及定位内容
return recognitionXmlElement(elementInformation);
} else {
//若未指定xml文件或者在xml文件中无法查到相应的元素时则将name的值赋给value且根据value判断相应定位方式
return recognitionCommonElement(elementInformation);
}
}
/**
* 获取普通元素的By对象
* @param elementInformation 元素信息类对象
* @return 元素信息指向的By对象
*/
private List<By> recognitionCommonElement(ElementInformation elementInformation) {
List<By> byList = new ArrayList<>();
//判断传入的ByType对象是否为null
if (elementInformation.byType == null) {
byList.addAll(getCommonElementBy(elementInformation.name));
} else {
byList.add(valueToBy(elementInformation.name, elementInformation.byType));
}
return byList;
}
/**
* 获取xml文件内元素的By对象
* @param elementInformation 元素信息类对象
* @return 元素信息指向的By对象
*/
private List<By> recognitionXmlElement(ElementInformation elementInformation) {
//查找元素的父层级结构
elementInformation.iframeList = getParentFrameName(elementInformation.name);
//存储元素的By对象集合
List<By> byList = new ArrayList<>();
//判断传入的ByType对象是否为null
if (elementInformation.byType == null) {
byList.addAll(getXmlElementBy(elementInformation.name, elementInformation.linkKeyList));
} else {
byList.add(xml.getBy(elementInformation.name, elementInformation.byType, elementInformation.linkKeyList));
}
return byList;
}
/**
* 用于返回控件的等待时间若设置单个控件的等待时间使用{@link #setContorlWaitTime(String, long)}方法设置
* 则返回设置的控件等待时间若未设置单个控件的等待时间则返回设置的通用等待时间使用{@link #setWaitTime(long)}方法
* 若未对通用时间进行设置则返回默认时间{@link #waitTime}
* @param name 控件名称
* @return 相应控件的等待时间
* @see #setContorlWaitTime(String, long)
* @see #setWaitTime(long)
*/
long getWaitTime(String name) {
for (String regex : controlWaitTime.keySet()) {
if (Pattern.compile(regex).matcher(name).matches()) {
return controlWaitTime.get(regex);
}
}
return waitTime;
}
/**
* 用于获取元素在xml文件中所有的父窗体并以集合的形式返回存储的顺序为父窗体在前子窗体在后若当前元素没有窗体
* 则集合的长度为0
* @param name 元素在xml文件中的名称
* @return 元素在xml文件中所有的父窗体集合
*/
List<String> getParentFrameName(String name) {
//存储获取到的父层窗体名称
List<String> nameList = new ArrayList<String>();
//获取元素所在窗体的名称
String iframeName = xml.getIframeName(name);
//循环判断窗体是否存在方法返回不为空若存在则再将父窗体进行存储
while(!iframeName.isEmpty()) {
//存储窗体
nameList.add(iframeName);
//再以当前窗体的名称再次获取该窗体的父窗体
iframeName = xml.getIframeName(iframeName);
}
return nameList;
}
/**
* 用于根据传入的参数识别非xml文件内的元素定位方式
* 该方法能快速识别xpath定位方式以及绝对css定位方式若不是以上两种定位方式
* 则会遍历所有的定位方式此时会降低运行速度建议在不是以上两种定位方式的
* 情况下直接指定元素的定位方式以提高效率
*/
List<By> getCommonElementBy(String value) {
List<By> byList = new ArrayList<>();
// 如果抛出元素名称查找不到的的异常则对应匹配xpath和绝对css路径两种定位方式
// 匹配xpath定位判定方法判断text的第一个字符是否是/
//由于是识别普通元素非xml元素其value就是元素的名称name 故获取等待时间时可直接将value传入
if (value.indexOf("/") == 0) {
byList.add(valueToBy(value, ByType.XPATH));
} else if (value.indexOf("html") == 0) {
//在页面中查找元素若元素能找到则结束查找
byList.add(valueToBy(value, ByType.CSS));
} else {
//若元素无法识别则将所有的定位类型排除xpath类型与之进行对比直到在页面上找到元素为止
for(ByType type : ByType.values()) {
if (type == ByType.XPATH) {
continue;
}
byList.add(valueToBy(value, type));
}
}
return byList;
}
/**
* 用于设置xml文件内的元素的定位方式及定位内容
*/
List<By> getXmlElementBy(String name, List<String> linkList) {
//用于存储从xml文件中读取到的所有内容并封装成By对象
List<By> byList = new ArrayList<>();
// 循环逐个在页面上配对有效的标签对应的定位方式
for (ByType mode : xml.getElementMode(name)) {
byList.add(valueToBy(xml.getValue(name, mode, linkList), mode));
}
return byList;
}
/**
* 根据元素的参数返回元素的By对象
* @return 元素的By对象
*/
By valueToBy(String value, ByType byType) {
//根据元素的定位方式对定位内容进行选择返回相应的By对象
switch (byType) {
case XPATH:
return By.xpath(value);
case CLASSNAME:
return By.className(value);
case CSS:
return By.cssSelector(value);
case ID:
return By.id(value);
case LINKTEXT:
return By.linkText(value);
case NAME:
return By.name(value);
case TAGNAME:
return By.tagName(value);
default:
throw new UnrecognizableLocationModeException("无法识别的定位类型:" + byType);
}
}
/**
* 用于判断元素是否为xml文件内的元素
* @param name 元素名称
* @return 是否为xml文件内的元素
*/
boolean isXmlElement(String name) {
return (xml != null && xml.isElement(name));
}
/**
* 用于向元素中添加完整的父窗体结构
* @param element 需要添加窗体的元素
* @param elementInformation 元素信息
*/
void setIframeElement(Element element, ElementInformation elementInformation) {
//指向当前元素用于设置当前元素的父层窗体元素
Element childElement = element;
//使用Iterator
Iterator<String> it = elementInformation.iframeList.iterator();
//循环一一设置父层结构
do {
//获取父层的元素的名称
String iframeName = it.next();
//构造父层元素
Element iframeElement = new Element(brower, iframeName, ElementType.COMMON_ELEMENT);
//设置父层元素的获取方式及等待时间
iframeElement.setWaitTime(getWaitTime(iframeName));
iframeElement.setByList(getXmlElementBy(iframeName, null));
iframeElement.setElementIndex(1);
//设置父层
childElement.setIframeElement(iframeElement);
//将子元素指向父层元素以获取下一层窗体元素
childElement = iframeElement;
} while(it.hasNext());
}
/**
* <p><b>文件名</b>AbstractElement.java</p>
* <p><b>用途</b>
* 存储获取元素时的信息
* </p>
* <p><b>编码时间</b>2020年5月9日上午7:57:24</p>
* <p><b>修改时间</b>2020年5月22日上午8:18:39</p>
* @author 彭宇琦
* @version Ver1.1
* @since JDK 1.8
*
*/
class ElementInformation {
/**
* 存储元素的名称或定位内容
*/
public String name;
/**
* 存储元素的定位方式
*/
public ByType byType;
/**
* 用于标记元素的类型
*/
public ElementType elementType;
/**
* 用于存储在xml文件中需要外链的词语
*/
public List<String> linkKeyList;
/**
* 用于存储元素的父层结构
*/
public List<String> iframeList;
/**
* 构造元素信息
* @param name 元素名称
* @param byType 元素定位的By类型枚举
* @param elementType 元素类型枚举
* @param linkKeyList xml文件中需要外部替换的词语
*/
public ElementInformation(String name, ByType byType, ElementType elementType, List<String> linkKeyList) {
super();
this.name = name;
this.byType = byType;
this.elementType = elementType;
this.linkKeyList = linkKeyList;
}
/**
* 用于判断其元素的使用的外链xml文件词语是否与存储的内容一致
* @param linkKey 需要判断的外链xml文件词语
* @return 是否与类中存储的词语一致
*/
public boolean linkKeyEquals(List<String> linkKey) {
//判断传入的linkKey是否为null或未传入的linkKey为空为空则直接判断linkKeyList的状态
if (linkKey == null || linkKey.size() == 0) {
//若linkKeyList为null或为空则可直接返回true反之则返回false
if (linkKeyList == null || linkKeyList.size() == 0) {
return true;
} else {
return false;
}
}
//判断linkKey中的元素是否完全存在于linkKeyList且两个集合的长度一致若存在一项判断不符合则返回false
if (linkKeyList.containsAll(linkKey) && linkKeyList.size() == linkKey.size()) {
return true;
} else {
return false;
}
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ElementInformation other = (ElementInformation) obj;
if (!getEnclosingInstance().equals(other.getEnclosingInstance()))
return false;
if (byType != other.byType)
return false;
if (elementType != other.elementType)
return false;
if (iframeList == null) {
if (other.iframeList != null)
return false;
} else if (!iframeList.equals(other.iframeList))
return false;
if (linkKeyList == null) {
if (other.linkKeyList != null)
return false;
} else if (!linkKeyList.equals(other.linkKeyList))
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
private AbstractBy getEnclosingInstance() {
return AbstractBy.this;
}
}
}

View File

@ -1,97 +0,0 @@
package pres.auxiliary.work.selenium.element.delect;
import java.util.Arrays;
import pres.auxiliary.work.selenium.brower.AbstractBrower;
import pres.auxiliary.work.selenium.xml.ByType;
/**
* <p><b>文件名</b>CommonElement.java</p>
* <p><b>用途</b>
* 提供在辅助化测试中对页面单一元素获取的方法类中获取元素的方法兼容传入定位方式对
* 元素进行查找建议使用xml对页面元素的定位方式进行存储以简化编码时的代码量也便于
* 对代码的维护
* </p>
* <p><b>编码时间</b>2020年4月26日下午10:34:55</p>
* <p><b>修改时间</b>2020年4月26日下午10:34:55</p>
* @author 彭宇琦
* @version Ver1.0
* @since JDK 8
*
*/
public class CommonBy extends AbstractBy {
/**
* 通过浏览器对象{@link AbstractBrower}进行构造
* @param brower {@link AbstractBrower}对象
*/
public CommonBy(AbstractBrower brower) {
super(brower);
}
/**
* 用于根据xml文件中元素的名称返回对应的{@link Element}对象该方法亦可传入元素
* 定位内容通过遍历所有的定位方式在页面上查找元素来获取元素的WebElement对象
* 建议传入的定位内容为xpath路径或绝对的css路径若非这两路径则在识别元素时会很慢降低
* 程序运行速度若非xml文件中的元素且不是xpath路径或绝对的css路径建议使用{@link #getElement(String, ByType)}方法
* @param name 元素的名称或元素定位内容
* @return {@link Element}对象
*/
public Element getElement(String name) {
return getElement(new ElementInformation(name, null, ElementType.COMMON_ELEMENT, null));
}
/**
* 用于根据xml文件中元素的名称与定位方式返回对应的{@link Element}对象该方法亦可传入元素
* 定位内容并根据定位方式对页面数据进行查找
* @param name 元素的名称或元素定位内容
* @return {@link Element}对象
*/
public Element getElement(String name, ByType byType) {
return getElement(new ElementInformation(name, byType, ElementType.COMMON_ELEMENT, null));
}
/**
* 用于根据xml文件中元素的名称与定位方式返回对应的{@link Element}对象该方法亦可传入元素
* 定位内容并根据定位方式对页面数据进行查找该方法可对由xml文件读取的内容进行词语替换根据
* 传参中词语的顺序对需要替换的词语进行替换
* @param name 元素名称或定位方式内容
* @param byType 元素定位方式
* @param links 替换词语
* @return {@link Element}对象
*/
public Element getElement(String name, ByType byType, String...links) {
return getElement(new ElementInformation(name, byType, ElementType.COMMON_ELEMENT, Arrays.asList(links)));
}
/**
* 用于根据xml文件中元素的名称返回对应的{@link Element}对象该方法亦可传入元素
* 定位内容通过遍历所有的定位方式在页面上查找元素来获取元素的WebElement对象
* 建议传入的定位内容为xpath路径或绝对的css路径若非这两路径则在识别元素时会很慢降低
* 程序运行速度若非xml文件中的元素且不是xpath路径或绝对的css路径建议使用{@link #getElement(String, ByType, String...)}方法
* @param name 元素的名称或元素定位内容
* @param links 替换词语
* @return {@link Element}对象
*/
public Element getElement(String name, String...links) {
return getElement(new ElementInformation(name, null, ElementType.COMMON_ELEMENT, Arrays.asList(links)));
}
/**
* 获取元素的底层方法
* @param elementInformation 元素信息类对象
* @return WebElement对象
*/
private Element getElement(ElementInformation elementInformation) {
Element element = new Element(brower, elementInformation.name, elementInformation.elementType);
element.setWaitTime(getWaitTime(elementInformation.name));
element.setByList(recognitionElement(elementInformation));
element.setElementIndex(1);
//构造元素的父层元素若元素不存在窗体结构则不进行构造
if (elementInformation.iframeList != null && elementInformation.iframeList.size() != 0) {
setIframeElement(element, elementInformation);
}
return element;
}
}

View File

@ -1,167 +0,0 @@
package pres.auxiliary.work.selenium.element.delect;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.openqa.selenium.NoSuchElementException;
import pres.auxiliary.work.selenium.brower.AbstractBrower;
/**
* <p><b>文件名</b>DataListElement.java</p>
* <p><b>用途</b>
* 提供在辅助化测试中对页面列表元素获取的方法并对列表元素的获取做了优化
* 类中获取元素的方法兼容传入定位方式对元素进行查找建议使用xml对页面元素的
* 定位方式进行存储以简化编码时的代码量也便于对代码的维护
* </p>
* <p><b>编码时间</b>2020年4月29日下午6:18:46</p>
* <p><b>修改时间</b>2020年4月29日下午6:18:46</p>
* @author 彭宇琦
* @version Ver1.0
* @since JDK 8
*/
public class DataListBy extends ListBy {
//TODO 添加获取最大列方法
/**
* 通过浏览器对象{@link AbstractBrower}进行构造
* @param brower {@link AbstractBrower}对象
*/
public DataListBy(AbstractBrower brower) {
super(brower);
}
/**
* 返回元素最多列的元素个数
* @return 元素最多列的元素个数
*/
// public int getMaxColumnSize() {
// return maxColumnSize;
// }
/**
* 返回元素最多列的列名称
* @return 元素最多列的列名称
*/
// public List<String> getMaxColumnName() {
// return maxColumnNameList;
// }
/**
* 返回元素最少列的元素个数
* @return 元素最少列的元素个数
*/
// public int getMinColumnSize() {
// return minColumnSize;
// }
/**
* 返回元素最少列的列名称
* @return 元素最少列的列名称
*/
// public List<String> getMinColumnName() {
// return minColumnNameList;
// }
/**
* 用于返回列表多个指定的元素传入的下标可参见{@link #getWebElement(String, int)}
*
* @param name 列名称
* @param indexs 一组元素下标即列表中对应的某一个元素
* @return 对应列多个指定下标的元素
* @throws NoSuchElementException 当未对name列进行获取数据或index的绝对值大于列表最大值时抛出的异常
* @see #getWebElement(String, int)
*/
// public List<Element> getElements(String name, int... indexs) {
// // 存储所有获取到的事件
// ArrayList<Element> events = new ArrayList<>();
//
// // 循环解析所有的下标并调用getEvent()方法存储至events
// for (int index : indexs) {
// events.add(getElement(name, index));
// }
//
// return events;
// }
/**
* 根据元素列名称与外链词语查找并返回该列所有的元素对象注意调用该方法时若未对元素
* 进行查找则会先查找一次元素此时若元素无法找到则可能抛出异常
* @param name 元素名称
* @param linkKey 外链的词语若不存在则不传入
* @return 指定列中所有的元素对象
* @throws NoSuchElementException 当未对name列进行获取数据或index的绝对值大于列表最大值时抛出的异常
*/
public List<Element> getAllElement(String name, String...linkKey) {
//根据元素名称和外链词语获取元素信息用于获取元素
ElementInformation elementInfo = nameToElementInformation(name, Arrays.asList(linkKey));
//若元素不存在则抛出异常
if (elementInfo == null) {
throw new NoSuchElementException("不存在的元素或外链词语不正确:" + name + "=" + Arrays.asList(linkKey));
}
//获取名称对应的元素
Element element = elementMap.get(elementInfo);
//获取元素能查找到的个数
int size = element.getSize();
//判断获取到的元素其长度是否为-1若是-1则表示未对元素进行获取需要先对元素进行一次获取再重新获取元素个数
if (element.getSize() == -1) {
element.getWebElement();
size = element.getSize();
}
// 存储克隆的元素
ArrayList<Element> elementList = new ArrayList<>();
//循环克隆并存储元素
for (int index = 0; index < size; index++) {
Element cloneElement = element.clone();
//下标从1开始0为随机
cloneElement.setElementIndex(index + 1);
elementList.add(cloneElement);
}
return elementList;
}
/**
* 用于根据列名称查找并返回相应列的第一个元素
* @param name 元素名称
* @param linkKey 外链的词语若不存在则不传入
* @return 对应列指定的元素
*/
public Element getElement(String name, String...linkKey) {
return getElement(name, 0, linkKey);
}
/**
* 该方法用于根据列名称查找到相应的列并返回与传入下标对应的元素下标支持从后向前获取传入的下标
* 与元素实际所在位置一致当传入0时则表示随机获取一个元素<br>
* {@code getWebElement("姓名", 1)}表示获取名称为姓名的列中的第1个元素<br>
* {@code getWebElement("姓名", 0)}表示获取名称为姓名的列中在长度范围内随机一个元素<br>
* {@code getWebElement("//*[@id='name']", -1)}表示获取//*[@id='name']对应列中的倒数第1个元素<br>
* 注意若使用了外链xml词语则需要将词语写入到传参中否则无法获取到相应的元素
*
* @param name 列名称
* @param index 元素下标即列表中对应的某一个元素
* @param linkKey 外链的词语若不存在则不传入
* @return 对应列指定的元素
*/
public Element getElement(String name, int index, String...linkKey) {
//获取元素信息并判断元素是否存在不存在则抛出异常
ElementInformation elementInfo = nameToElementInformation(name, Arrays.asList(linkKey));
if (elementInfo == null) {
throw new NoSuchElementException("不存在的元素:" + name);
}
Element element = elementMap.get(elementInfo);
element.setElementIndex(index);
return element;
}
@Override
ElementType setElementType() {
return ElementType.DATA_LIST_ELEMENT;
}
}

View File

@ -1,518 +0,0 @@
package pres.auxiliary.work.selenium.element.delect;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import org.openqa.selenium.By;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.StaleElementReferenceException;
import org.openqa.selenium.TimeoutException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.Select;
import org.openqa.selenium.support.ui.WebDriverWait;
import pres.auxiliary.work.selenium.brower.AbstractBrower;
/**
* <p><b>文件名</b>Element.java</p>
* <p><b>用途</b>
* 用于返回和查找页面元素以方便在元素过期时能进行重新获取
* </p>
* <p><b>编码时间</b>2020年5月18日上午8:39:05</p>
* <p><b>修改时间</b>2020年5月18日上午8:39:05</p>
* @author 彭宇琦
* @version Ver1.0
* @since JDK 8
*
*/
public class Element implements Cloneable {
/**
* 用于对文本进行分隔
*/
private final String SPILT_SIGN = ",";
/**
* 存储元素
*/
private WebElement element = null;
/**
* 存储查找元素的By对象
*/
private List<By> byList;
/**
* 用于存储窗体元素
*/
private Element iframeElement = null;
/**
* 用于存储当前已指向的窗体
*/
private static ArrayList<Element> iframeElementList = new ArrayList<>();
/**
* 存储获取需要获取的元素下标
*/
private String elementIndex = "0";
/**
* 存储WebDriver对象以查找相应元素
*/
private WebDriver driver;
/**
* 用于存储当前打开的浏览器对象
*/
// private AbstractBrower brower;
/**
* 用于存储元素的名称
*/
private String name;
/**
* 标记元素的类型以用于重新获取时
*/
private ElementType elementType;
/**
* 表示当前通过有效By对象能获取到的元素个数
*/
private int size = -1;
/**
* 用于存储元素查找等待时间默认3秒
*/
private long waitTime = 3;
/**
* 用于指向是否需要重新获取元素
*/
private boolean isReplaceFind = false;
/**
* 控制是否允许在随机时出现第一个元素
*/
private boolean isRandomZero = true;
/**
* TODO 确定构造后添加注释
*/
public Element(AbstractBrower brower, String name, ElementType elementType) {
super();
//存储Driver对象
this.driver = brower.getDriver();
this.name = name;
this.elementType = elementType;
}
/**
* TODO 确定构造后添加注释
*/
public Element(WebDriver driver, String name, ElementType elementType) {
super();
//存储Driver对象
this.driver = driver;
this.name = name;
this.elementType = elementType;
}
/**
* 用于根据存储的元素信息在相应的页面中查找并返回一个{@link WebElement}对象
* @return {@link WebElement}对象
*/
public WebElement getWebElement() {
//切换窗体
autoSwitchIframe();
//若element为null则对元素进行一次查找
if (element == null || isReplaceFind || isPageRefresh()) {
findElement();
}
return element;
}
/**
* 返回当前能获取到的元素组的个数若未获取元素或元素获取失败则返回-1
* @return 获取到的元素组的个数
*/
public int getSize() {
return size;
}
/**
* 用于设置元素定位的{@link By}对象集合调用该方法后将清空原存储的内容重新添加{@link By}对象集合
* @param byList {@link By}对象集合
*/
public void setByList(List<By> byList) {
this.byList = byList;
isReplaceFind = true;
}
/**
* 用于设置元素的父层窗体
* @param iframeElement 元素的父层窗体
*/
public void setIframeElement(Element iframeElement) {
this.iframeElement = iframeElement;
isReplaceFind = true;
}
/**
* 用于返回元素所在的窗体元素对象
* @return 元素所在的窗体元素对象
*/
public Element getIframeElement() {
return iframeElement;
}
/**
* 用于返回当前存储的{@link WebDriver}对象
* @return {@link WebDriver}对象
*/
public WebDriver getDriver() {
return driver;
}
/**
* 用于返回元素名称
* @return 元素名称
*/
public String getName() {
return name;
}
/**
* 用于设置元素查询等待时间该时间为元素每个{@link By}对象在页面查询的时间注意若添加
* 过多无效的{@link By}对象会导致查询等待时间过长
* @param waitTime 元素查询等待时间
*/
public void setWaitTime(long waitTime) {
this.waitTime = waitTime;
}
/**
* 用于以数字方式设置元素的下标
* @param elementIndex 元素下标
*/
public void setElementIndex(int elementIndex) {
this.elementIndex = String.valueOf(elementIndex);
isReplaceFind = true;
}
/**
* 用于以文本方式设置元素下标该下标用于以文本的形式查找元素
* @param elementIndex 元素下标
*/
public void setElementIndex(String... elementIndexs) {
//拼接词语
this.elementIndex = "";
for (String elementIndex : elementIndexs) {
this.elementIndex += elementIndex;
}
isReplaceFind = true;
}
/**
* 用于设置是否允许随机出元素组的第一个元素默认为允许
* @param isRandomZero 是否允许随机第一个元素
*/
public void setRandomZero(boolean isRandomZero) {
this.isRandomZero = isRandomZero;
}
/**
* 用于返回元素所在的所有父窗体
* @return 父窗体集合
*/
public ArrayList<Element> getAllIframe() {
//存储当前元素的所在的所有窗体
ArrayList<Element> iframeList = new ArrayList<>();
//用于指向元素所在的窗体
Element iframe = iframeElement;
//若窗体元素不为null则循环
while(iframe != null) {
//存储窗体元素
iframeList.add(iframe);
//向下探寻是否存在下一级窗体元素
iframe = iframe.getIframeElement();
}
//若iframeList中未存储元素则不进行倒叙
if (iframeList.size() != 0) {
//由于存储顺序为子窗体在前不符合切换窗体的顺序故需要对集合进行倒叙操作
Collections.reverse(iframeList);
}
return iframeList;
}
/**
* 用于将窗体切换至顶层
*/
public void switchRootIframe() {
//切换窗体至顶层
driver.switchTo().defaultContent();
//将iframeElementList中的内容清空
iframeElementList.clear();
}
/**
* 用于向下切换一层窗体
* @param iframeElement 窗体元素类对象
*/
public void switchIframe(Element iframeElement) {
//切换窗体
driver.switchTo().frame(iframeElement.findIframeElement());
//存储窗体切换记录
iframeElementList.add(iframeElement);
}
/**
* 用于向上切换指定数量的窗体若传入的数量大于当前窗体的数量则将窗体切换至顶层若传入的数量小于
* 等于0则不进行切换
* @param count 需要向上切换窗体的数量
*/
public void switchParentIframe(int count) {
//判断count是否小于等于0若小于等于0则不进行切换
if (count <= 0) {
return;
}
//判断count是否大于iframeElementList中所有元素的个数若大于则切回至顶层
if (count > iframeElementList.size()) {
switchRootIframe();
return;
}
//循环对窗体进行切换
for (int i = 0; i < count; i++) {
//向上切换窗体
driver.switchTo().parentFrame();
//移除iframeElementList中最后一个元素
iframeElementList.remove(iframeElementList.size() - 1);
}
}
/**
* 用于根据By对象集合查找有效的By对象并根据有效的By对象查找相应的元素集合
*/
public void findElement() {
//遍历By集合查找有效的By对象若所有的By对象均无法查到页面元素则直接抛出超时异常
for (int i = 0; i < byList.size(); i++) {
//查找元素若抛出异常则移除该元素保证有效的By对象在第一个元素上
try {
if (isExistElement(byList.get(i))) {
break;
} else {
byList.remove(i);
i--;
}
} catch (Exception e) {
byList.remove(i);
i--;
}
}
//若byList被清空则抛出查找超时的异常
if (byList.size() == 0) {
throw new TimeoutException("页面无法查找到相应的元素,“" + name + "”元素无有效的By对象");
}
//获取元素
List<WebElement> elementList = getWebElementList();
//记录元素个数
size = elementList.size();
//转换下标若下标能转换成数字则以数字方式获取下标若不能则以文本的形式转换下标根据下标返回元素
try {
element = elementList.get(getIndex(elementList, Integer.valueOf(elementIndex)));
} catch (NumberFormatException e) {
element = elementList.get(getIndex(elementList, elementIndex));
}
}
private List<WebElement> getWebElementList() {
//根据正确的By对象通过Webdriver查找到WebElement对象存储
//由于在遍历byList时无效的By对象被剔除查找到By对象时会返回故此时可直接使用第一个元素为有效的By对象
switch (elementType) {
case COMMON_ELEMENT:
case DATA_LIST_ELEMENT:
return driver.findElements(byList.get(0));
case SELECT_ELEMENT:
//判断第一个元素的TagName是否为select若是select则按照下拉选项进行获取
if (driver.findElement(byList.get(0)).getTagName().equals("select")) {
return new Select(driver.findElement(byList.get(0))).getOptions();
} else {
return driver.findElements(byList.get(0));
}
default:
throw new IllegalArgumentException("Unexpected value: " + elementType);
}
}
/**
* 用于自动切换元素所在窗体
*/
private void autoSwitchIframe() {
//获取元素所在的所有窗体并存储
ArrayList<Element> iframeList = getAllIframe();
//判断iframeList中是否存在元素若不存在则切回顶层后结束运行
if (iframeList.isEmpty()) {
switchRootIframe();
return;
}
//判断iframeElementList是否为空或iframeList的第一个元素是否存在于iframeElementList中若不存在则切回到顶层并根据iframeList切换窗体
if (iframeElementList.size() == 0 || !iframeElementList.contains(iframeList.get(0))) {
switchRootIframe();
iframeList.forEach(iframeElement -> {
switchIframe(iframeElement);
});
return;
}
//判断iframeElementList中的最后一个元素是否与iframeList中的最后一个元素一致若一致则无需切换
if (iframeElementList.get(iframeElementList.size() - 1).equals(iframeList.get(iframeList.size() - 1))) {
return;
}
//遍历iframeList查看iframeList中的窗体元素是否存在于iframeElementList中
int index = 0;
for (; index < iframeList.size(); index++) {
//若当前遍历的元素不存在于iframeElementList中则结束循环
if (!iframeElementList.contains(iframeList.get(index))) {
break;
}
}
//计算需要向上切换窗体的个数
//当窗体不存在于iframeElementList中时其index以比存在的窗体下标大1位
//index可直接用于计算需要切换窗体的个数
switchParentIframe(iframeElementList.size() - index);
//切换到相应的父层后再根据iframeList剩余的元素进行切换
for (; index < iframeList.size(); index++) {
switchIframe(iframeList.get(index));
}
}
/**
* 用于返回窗体{@link WebElement}元素
* @return 窗体{@link WebElement}元素
*/
private WebElement findIframeElement() {
//若elementList为null或其内无元素则对元素进行一次查找
if (element == null) {
findElement();
}
//返回相应的内容
return element;
}
/**
* 判断根据传入的{@link By}对象判断是否能查找到元素
* @param by {@link By}对象
* @return 是否查找到元素
*/
boolean isExistElement(By by) {
//当查找到元素时则返回true若查不到元素则会抛出异常故返回false
return new WebDriverWait(driver, waitTime, 200).
until((driver) -> {
WebElement element = driver.findElement(by);
return element != null;
});
}
/**
* 用于判断页面是否过期刷新等
* @return 页面是否刷新
*/
boolean isPageRefresh() {
//若页面抛出StaleElementReferenceException异常时则表示页面已被更改
try {
element.getTagName();
return false;
} catch (StaleElementReferenceException e) {
return true;
}
}
/**
* 由于方法允许传入负数和特殊数字0为下标并且下标的序号由1开始
* 故可通过该方法对下标的含义进行转义得到java能识别的下标
* @param index 传入的下标
* @param randomZero 标记是否可以随机出数字0
* @return 可识别的下标
* @throws NoSuchElementException 当元素无法查找到时抛出的异常
*/
int getIndex(List<WebElement> elementList, int index) {
//存储传入的元素组长度
int size = elementList.size();
//若当前元素组中只有一个元素则直接返回0
if (size == 1) {
return 0;
}
//判断元素下标是否超出范围由于可以传入负数故需要使用绝对值
if (Math.abs(index) > size) {
throw new NoSuchElementException("指定的选项值大于选项的最大值。选项总个数:" + size + ",指定项:" + index);
}
//判断index的值若大于0则从前向后遍历若小于0则从后往前遍历若等于0则随机输入
if (index > 0) {
//选择元素正数的选项值从1开始故需要减小1
return index - 1;
} else if (index < 0) {
//选择元素由于index为负数则长度加上选项值即可得到需要选择的选项
return size + index;
} else {
//为0则进行随机选择但需要判断是否允许随机出0第一个元素
int newindex = 0;
do {
newindex = new Random().nextInt(size);
} while(newindex == 0 && !isRandomZero);
return newindex;
}
}
/**
* 以文本形式查找元素返回文本对应元素的下标
* @param elementList 元素组
* @param text 需要查找的文本
* @return 文本对应元素的下标
*/
int getIndex(List<WebElement> elementList, String text) {
//切分文本
String[] keys = text.split(SPILT_SIGN);
//遍历元素组若存在文本为所传文本的元素则进行返回
for (int index = 0; index < getSize(); index++) {
//遍历词语将词语与元素的文本一一比对
for(String key : keys) {
//若存在不包含在元素文本的词语则结束当前循环查找下一个
if (elementList.get(index).getText().indexOf(key) < 0) {
break;
}
//若所有的词语均包含在文本中则直接返回当前下标
return index;
}
}
//若所有词语均不包含文本则抛出异常
throw new NoSuchElementException("无法找到指定文本的元素:" + text);
}
@Override
public Element clone() {
try {
return (Element)super.clone();
} catch (CloneNotSupportedException e) {
return this;
}
}
}

View File

@ -1,28 +0,0 @@
package pres.auxiliary.work.selenium.element.delect;
/**
* <p><b>文件名</b>EelementType.java</p>
* <p><b>用途</b>
* 用于标记当前传入的元素是以何种方式进行获取
* </p>
* <p><b>编码时间</b>2020年5月22日上午7:57:32</p>
* <p><b>修改时间</b>2020年5月22日上午7:57:32</p>
* @author
* @version Ver1.0
* @since JDK 12
*
*/
public enum ElementType {
/**
* 指向普通类型元素
*/
COMMON_ELEMENT,
/**
* 指向数据列表类型元素
*/
DATA_LIST_ELEMENT,
/**
* 指向下拉框选择类型元素
*/
SELECT_ELEMENT,
}

View File

@ -1,165 +0,0 @@
package pres.auxiliary.work.selenium.element.delect;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import org.openqa.selenium.TimeoutException;
import pres.auxiliary.work.selenium.brower.AbstractBrower;
import pres.auxiliary.work.selenium.xml.ByType;
/**
* <p><b>文件名</b>ListElement.java</p>
* <p><b>用途</b>
* 提供获取列表类型元素时的基本方法
* </p>
* <p><b>编码时间</b>2020年5月22日上午7:54:55</p>
* <p><b>修改时间</b>2020年5月22日上午7:54:55</p>
* @author 彭宇琦
* @version Ver1.0
* @since JDK 12
*
*/
public abstract class ListBy extends AbstractBy {
/**
* 用于存储获取到的列表一列元素key为列表名称value为列表元素
*/
LinkedHashMap<ElementInformation, Element> elementMap = new LinkedHashMap<>(16);
/**
* 控制元素首行是否为
*/
private boolean fristIsEmpty = false;
/**
* 通过浏览器对象{@link AbstractBrower}进行构造
* @param brower {@link AbstractBrower}对象
*/
public ListBy(AbstractBrower brower) {
super(brower);
}
/**
* 该方法用于返回所有列的名称
* @return 所有列的名称
*/
public ArrayList<String> getNames() {
ArrayList<String> nameList = new ArrayList<>();
elementMap.forEach((key, value) -> {
nameList.add(key.name);
});
return nameList;
}
/**
* 设置首个选项是否为不可选择的选项
* @param fristIsEmpty 首个选项是否为不可选择
*/
public void setFristIsEmpty(boolean fristIsEmpty) {
this.fristIsEmpty = fristIsEmpty;
}
/**
* 用于清空存储的内容
*/
public void clear() {
elementMap.clear();
}
/**
* 用于根据传入的元素在xml文件中的名称或者元素的定位内容添加元素由于该方法不指定元素的定位
* 方式若传入的参数不是xml元素且非xpath路径或绝对css路径时其识别效率较慢建议在该情况下
* 调用{@link #add(String, ByType)}方法指定元素定位方法
* @param name 元素在xml文件或者元素的定位内容
* @see #add(String, ByType)
* @throws TimeoutException 元素在指定时间内无法找到时抛出的异常
*/
public void add(String name) {
add(new ElementInformation(name, null, setElementType(), null));
}
/**
* 用于根据传入的元素在xml文件中的名称或者元素的定位内容以及元素的定位方式添加元素
* @param name 元素在xml文件或者元素的定位内容
* @param byType 元素定位方式枚举对象{@link ByType}枚举
* @see #add(String)
* @throws TimeoutException 元素在指定时间内无法找到时抛出的异常
*/
public void add(String name, ByType byType) {
add(new ElementInformation(name, byType, setElementType(), null));
}
/**
* 用于根据传入的元素在xml文件中的名称或者元素的定位内容以及元素的定位方式添加元素
* 该方法可对由xml文件读取的内容进行词语替换根据
* 传参中词语的顺序对需要替换的词语进行替换
* @param name 元素在xml文件或者元素的定位内容
* @param byType 元素定位方式枚举对象{@link ByType}枚举
* @param links 替换词语
* @see #add(String, ByType)
* @throws TimeoutException 元素在指定时间内无法找到时抛出的异常
*/
public void add(String name, ByType byType, String... links) {
add(new ElementInformation(name, byType, setElementType(), Arrays.asList(links)));
}
/**
* 用于根据传入的元素在xml文件中的名称或者元素的定位内容添加元素由于该方法不指定元素的定位
* 方式若传入的参数不是xml元素且非xpath路径或绝对css路径时其识别效率较慢建议在该情况下
* 调用{@link #add(String, ByType)}方法指定元素定位方法该方法可对由xml文件读取的内容进
* 行词语替换根据传参中词语的顺序对需要替换的词语进行替换
* @param name 元素在xml文件或者元素的定位内容
* @param links 替换词语
* @see #add(String)
* @throws TimeoutException 元素在指定时间内无法找到时抛出的异常
*/
public void add(String name, String... links) {
add(new ElementInformation(name, null, ElementType.DATA_LIST_ELEMENT, Arrays.asList(links)));
}
/**
* 添加元素的底层方法
* @param elementInformation 元素信息类对象
*/
void add(ElementInformation elementInformation) {
Element element = new Element(brower, elementInformation.name, elementInformation.elementType);
element.setWaitTime(getWaitTime(elementInformation.name));
element.setByList(recognitionElement(elementInformation));
//若首元素为空元素或不允许选择的选项则表示随机时不允许出现第一个选项
element.setRandomZero(!fristIsEmpty);
//构造元素的父层元素若元素不存在窗体结构则不进行构造
if (elementInformation.iframeList != null && elementInformation.iframeList.size() != 0) {
setIframeElement(element, elementInformation);
}
//添加元素
elementMap.put(elementInformation, element);
}
/**
* 用于设置元素的类型根据该类型来存储元素信息
* @return
*/
abstract ElementType setElementType();
/**
* 根据元素名称反推元素信息类对象用于根据列名称查找数据以及判断列是否存在若列名不存在则返回null
* @return ElementInformation对象
*/
ElementInformation nameToElementInformation(String name, List<String> linkKey) {
//遍历elementMap若查找与name一致的名称则结束循环并返回相应的ElementInformation对象
for (ElementInformation elementInfo : elementMap.keySet()) {
if (elementInfo.name.equals(name) && elementInfo.linkKeyEquals(linkKey)) {
return elementInfo;
}
}
return null;
}
}

View File

@ -1,123 +0,0 @@
package pres.auxiliary.work.selenium.element.delect;
import java.util.List;
import org.openqa.selenium.NoSuchElementException;
import pres.auxiliary.work.selenium.brower.AbstractBrower;
/**
* <p><b>文件名</b>SelectBy.java</p>
* <p><b>用途</b>
* 提供获取下拉框中选项元素的方法支持标准型下拉框选项由select与option标签组成的下拉框以及
* 非标准型下拉框选项由普通的divli等元素组成的选项并支持根据关键词查找选项需要注意的是
* 标准下拉选项和非标准下拉选项需要传入的参数不同例如<br>
* 标准下拉框<br>
* &lt;select&nbsp;id='test'&gt;<br>
* &emsp;&lt;option&gt;&lt;/option&gt;<br>
* &emsp;&lt;option&gt;&lt;/option&gt;<br>
* &emsp;&lt;option&gt;其他&lt;/option&gt;<br>
* &lt;/select&gt;<br>
* 对于该标准的下拉框选项只需要定位到//select[@id='test']得到其WebElement对象即可但对于非标准的下拉框其下拉框是由input和button标签构成<br>
* &lt;div&gt;<br>
* &emsp;&lt;span&gt;&lt;input/&gt;&lt;/span&gt;<br>
* &emsp;&lt;span&gt;&lt;button/&gt;&lt;/span&gt;<br>
* &lt;/div&gt;<br>
* 点击button对应的按钮后其下也能弹出选项但其选项是由div标签写入text构成<br>
* &lt;div&nbsp;id='test'&gt;<br>
* &emsp;&lt;div&gt;&lt;/div&gt;<br>
* &emsp;&lt;div&gt;&lt;/div&gt;<br>
* &emsp;&lt;div&gt;其他&lt;/div&gt;<br>
* &lt;/div&gt;<br>
* 对于这种非标准的下拉框选项需要传入选项所在的所有div标签对应的WebElement元素在上例则需要定位到//div[@id='test']/div
* 注意末尾的div不指定数字则可以代表整个选项<br>
* </p>
* <p><b>编码时间</b>2020年5月24日下午3:30:00</p>
* <p><b>修改时间</b>2020年5月24日下午3:30:00</p>
* @author 彭宇琦
* @version Ver1.0
* @since JDK 12
*
*/
public class SelectBy extends ListBy {
/**
* 用于指向存储的元素名称
*/
private String name;
/**
* 用于指向存储的外链词语
*/
private List<String> linkKey;
/**
* 通过浏览器对象{@link AbstractBrower}进行构造
*
* @param brower {@link AbstractBrower}对象
*/
public SelectBy(AbstractBrower brower) {
super(brower);
}
@Override
void add(ElementInformation elementInformation) {
//存储元素信息
name = elementInformation.name;
linkKey = elementInformation.linkKeyList;
//清空父类中的链表重新插入数据
clear();
super.add(elementInformation);
}
/**
* 该方法用于根据列名称查找到相应的列并返回与传入下标对应的元素下标支持从后向前获取传入的下标
* 与元素实际所在位置一致当传入0时则表示随机获取一个元素<br>
* {@code getWebElement(1)}表示获取第1个选项<br>
* {@code getWebElement(0)}表示获取随机一个选项<br>
* {@code getWebElement(-1)}表示获取倒数第1个选项<br>
*
* @param index 元素下标即列表中对应的某一个元素
* @return 对应的选项
* @throws NoSuchElementException 当未对name列进行获取数据或index的绝对值大于列表最大值时抛出的异常
*/
public Element getElement(int index) {
Element element = findElement();
element.setElementIndex(index);
return element;
}
/**
* 根据关键词组查找选项并返回选项元素当传入的元素名称不存在时则抛出NoSuchElementException异常
* 当查询到有多个包含关键词的选项时则选择第一个选项<br>
* 注意当传入多个关键词时其选项需要全部满足才会返回相应的选项
*
* @param keys 查询选项的关键词组
* @return 相应的选项元素
* @throws NoSuchElementException 查找的选项不存在时抛出的异常
*/
public Element getElement(String...keys) {
Element element = findElement();
element.setElementIndex(keys);
return element;
}
/**
* 用于查找相应的元素
* @return 需要查找的元素
*/
private Element findElement() {
//获取元素信息并判断元素是否存在不存在则抛出异常
ElementInformation elementInfo = nameToElementInformation(name, linkKey);
if (elementInfo == null) {
throw new NoSuchElementException("不存在的定位方式:" + name);
}
return elementMap.get(elementInfo);
}
@Override
ElementType setElementType() {
return ElementType.SELECT_ELEMENT;
}
}

View File

@ -1,38 +0,0 @@
package pres.auxiliary.work.selenium.element.delect;
/**
* <p><b>文件名</b>UnrecognizableLocationModeException.java</p>
* <p><b>用途</b>在元素定位方式无法被识别的情况下抛出的异常</p>
* <p><b>编码时间</b>2019年9月24日下午3:19:43</p>
* <p><b>修改时间</b>2019年9月24日下午3:19:43</p>
* @author 彭宇琦
* @version Ver1.0
* @since JDK 12
*
*/
public class UnrecognizableLocationModeException extends RuntimeException {
private static final long serialVersionUID = 1L;
public UnrecognizableLocationModeException() {
super();
}
public UnrecognizableLocationModeException(String message, Throwable cause, boolean enableSuppression,
boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
public UnrecognizableLocationModeException(String message, Throwable cause) {
super(message, cause);
}
public UnrecognizableLocationModeException(String message) {
super(message);
}
public UnrecognizableLocationModeException(Throwable cause) {
super(cause);
}
}

View File

@ -1,126 +1,126 @@
package pres.auxiliary.work.selenium.event;
import org.openqa.selenium.ElementClickInterceptedException;
import org.openqa.selenium.StaleElementReferenceException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.interactions.Actions;
import org.openqa.selenium.support.ui.ExpectedConditions;
import pres.auxiliary.work.selenium.element.Element;
/**
* <p><b>文件名</b>ClickEvent.java</p>
* <p><b>用途</b>
* 定义了对控件进行点击操作相关的方法可通过该类对页面进行基本的点击操作
* </p>
* <p><b>编码时间</b>2019年8月29日下午3:24:34</p>
* <p><b>修改时间</b>2020年7月10日上午16:49:37</p>
*
* @author 彭宇琦
* @version Ver2.0
* @since JDK 12
*
*/
public class ClickEvent extends AbstractEvent {
/**
* 构造ClickEvent类对象
*
* @param driver WebDriver类对象
*/
public ClickEvent(WebDriver driver) {
super(driver);
}
/**
* 鼠标左键单击事件
*
* @param element 通过查找页面得到的控件元素对象
*/
public void click(Element element) {
//在等待时间内判断元素是否可以点击
try {
wait.until(ExpectedConditions.elementToBeClickable(element.getWebElement()));
} catch (Exception e) {
}
//进行操作若仍抛出ElementClickInterceptedException异常则再等待直到不再抛出异常
wait.until((driver) -> {
try {
element.getWebElement().click();
return true;
} catch (ElementClickInterceptedException e) {
return false;
} catch (StaleElementReferenceException e) {
element.findElement();
return false;
}
});
}
/**
* 鼠标左键双击事件
*
* @param element 通过查找页面得到的控件元素对象
*/
public void doubleClick(Element element) {
//在等待时间内判断元素是否可以点击
try {
wait.until(ExpectedConditions.elementToBeClickable(element.getWebElement()));
} catch (Exception e) {
}
//进行操作若仍抛出ElementClickInterceptedException异常则再等待直到不再抛出异常
wait.until((driver) -> {
try {
new Actions(driver).doubleClick(element.getWebElement()).perform();
return true;
} catch (ElementClickInterceptedException e) {
return false;
} catch (StaleElementReferenceException e) {
element.findElement();
return false;
}
});
}
/**
* 鼠标右键点击事件
* @param element 通过查找页面得到的控件元素对象
*/
public void rightClick(Element element) {
//在等待时间内判断元素是否可以点击
try {
wait.until(ExpectedConditions.elementToBeClickable(element.getWebElement()));
} catch (Exception e) {
}
//进行操作若仍抛出ElementClickInterceptedException异常则再等待直到不再抛出异常
wait.until((driver) -> {
try {
new Actions(driver).contextClick(element.getWebElement()).perform();
return true;
} catch (ElementClickInterceptedException e) {
return false;
} catch (StaleElementReferenceException e) {
element.findElement();
return false;
}
});
}
/**
* 连续进行指定次数的鼠标左键点击事件
* @param element 通过查找页面得到的控件元素对象
* @param clickCount 点击次数
* @param sleepInMillis 操作时间间隔单位为毫秒
*/
public void continuousClick(Element element, int clickCount, long sleepInMillis) {
for(int i = 0; i < clickCount; i++) {
click(element);
try {
Thread.sleep(sleepInMillis);
} catch (InterruptedException e) {
continue;
}
}
}
}
package pres.auxiliary.work.selenium.event;
import org.openqa.selenium.ElementClickInterceptedException;
import org.openqa.selenium.StaleElementReferenceException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.interactions.Actions;
import org.openqa.selenium.support.ui.ExpectedConditions;
import pres.auxiliary.work.selenium.element.AbstractBy.Element;
/**
* <p><b>文件名</b>ClickEvent.java</p>
* <p><b>用途</b>
* 定义了对控件进行点击操作相关的方法可通过该类对页面进行基本的点击操作
* </p>
* <p><b>编码时间</b>2019年8月29日下午3:24:34</p>
* <p><b>修改时间</b>2020年7月10日上午16:49:37</p>
*
* @author 彭宇琦
* @version Ver2.0
* @since JDK 12
*
*/
public class ClickEvent extends AbstractEvent {
/**
* 构造ClickEvent类对象
*
* @param driver WebDriver类对象
*/
public ClickEvent(WebDriver driver) {
super(driver);
}
/**
* 鼠标左键单击事件
*
* @param element 通过查找页面得到的控件元素对象
*/
public void click(Element element) {
//在等待时间内判断元素是否可以点击
try {
wait.until(ExpectedConditions.elementToBeClickable(element.getWebElement()));
} catch (Exception e) {
}
//进行操作若仍抛出ElementClickInterceptedException异常则再等待直到不再抛出异常
wait.until((driver) -> {
try {
element.getWebElement().click();
return true;
} catch (ElementClickInterceptedException e) {
return false;
} catch (StaleElementReferenceException e) {
element.againFindElement();
return false;
}
});
}
/**
* 鼠标左键双击事件
*
* @param element 通过查找页面得到的控件元素对象
*/
public void doubleClick(Element element) {
//在等待时间内判断元素是否可以点击
try {
wait.until(ExpectedConditions.elementToBeClickable(element.getWebElement()));
} catch (Exception e) {
}
//进行操作若仍抛出ElementClickInterceptedException异常则再等待直到不再抛出异常
wait.until((driver) -> {
try {
new Actions(driver).doubleClick(element.getWebElement()).perform();
return true;
} catch (ElementClickInterceptedException e) {
return false;
} catch (StaleElementReferenceException e) {
element.againFindElement();
return false;
}
});
}
/**
* 鼠标右键点击事件
* @param element 通过查找页面得到的控件元素对象
*/
public void rightClick(Element element) {
//在等待时间内判断元素是否可以点击
try {
wait.until(ExpectedConditions.elementToBeClickable(element.getWebElement()));
} catch (Exception e) {
}
//进行操作若仍抛出ElementClickInterceptedException异常则再等待直到不再抛出异常
wait.until((driver) -> {
try {
new Actions(driver).contextClick(element.getWebElement()).perform();
return true;
} catch (ElementClickInterceptedException e) {
return false;
} catch (StaleElementReferenceException e) {
element.againFindElement();
return false;
}
});
}
/**
* 连续进行指定次数的鼠标左键点击事件
* @param element 通过查找页面得到的控件元素对象
* @param clickCount 点击次数
* @param sleepInMillis 操作时间间隔单位为毫秒
*/
public void continuousClick(Element element, int clickCount, long sleepInMillis) {
for(int i = 0; i < clickCount; i++) {
click(element);
try {
Thread.sleep(sleepInMillis);
} catch (InterruptedException e) {
continue;
}
}
}
}

View File

@ -1,12 +1,12 @@
package pres.auxiliary.work.selenium.event;
import pres.auxiliary.work.selenium.element.Element;
import pres.auxiliary.work.selenium.element.Element_Old;
/**
* <p><b>文件名</b>EventAction.java</p>
* <p><b>用途</b>
* 用于执行事件增强的方法方法中可通过{@link EventInformation}类对象回调事件的操作的步骤
* 以及传入到事件中的{@link Element}类对象
* 以及传入到事件中的{@link Element_Old}类对象
* </p>
* <p><b>编码时间</b>2020年7月10日下午3:40:13</p>
* <p><b>修改时间</b>2020年7月10日下午3:40:13</p>
@ -16,7 +16,7 @@ import pres.auxiliary.work.selenium.element.Element;
*/
public interface EventAction {
/**
* 事件增强的方法可通过形参回调事件的操作步骤以及传入到事件中的{@link Element}类对象
* 事件增强的方法可通过形参回调事件的操作步骤以及传入到事件中的{@link Element_Old}类对象
* @param name {@link EventInformation}类对象
*/
void action(EventInformation elemenetInformation);

View File

@ -4,7 +4,7 @@ import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import pres.auxiliary.work.selenium.element.Element;
import pres.auxiliary.work.selenium.element.Element_Old;
/**
* <p><b>文件名</b>EventInformation.java</p>
@ -29,7 +29,7 @@ public class EventInformation {
/**
* 存储传入到方法中的元素类对象由args进行分离
*/
private ArrayList<Element> elementList = new ArrayList<>();
private ArrayList<Element_Old> elementList = new ArrayList<>();
/**
* 构造对象
@ -61,10 +61,10 @@ public class EventInformation {
}
/**
* 用于返回传入到方法中{@link Element}类对象集合
* @return {@link Element}类对象集合
* 用于返回传入到方法中{@link Element_Old}类对象集合
* @return {@link Element_Old}类对象集合
*/
public ArrayList<Element> getElement() {
public ArrayList<Element_Old> getElement() {
return elementList;
}
@ -73,8 +73,8 @@ public class EventInformation {
*/
private void toElement() {
Arrays.stream(args).forEach(arg -> {
if (arg instanceof Element) {
elementList.add((Element) arg);
if (arg instanceof Element_Old) {
elementList.add((Element_Old) arg);
}
});
}

View File

@ -12,7 +12,7 @@ import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import pres.auxiliary.work.selenium.brower.ChromeBrower;
import pres.auxiliary.work.selenium.element.Element;
import pres.auxiliary.work.selenium.element.Element_Old;
/**
* <p><b>文件名</b>EventProxy.java</p>
@ -205,9 +205,9 @@ public class EventProxy<T extends AbstractEvent> implements MethodInterceptor {
* @param actionMap 需要执行的通知
*/
private void runElementAction(EventInformation eventInformation, LinkedHashMap<String, ArrayList<EventAction>> actionMap) {
Arrays.stream(eventInformation.getParam()).filter(arg -> arg instanceof Element).forEach(arg -> {
Arrays.stream(eventInformation.getParam()).filter(arg -> arg instanceof Element_Old).forEach(arg -> {
actionMap.forEach((key, value) -> {
if (Pattern.compile(key).matcher(((Element) arg).getName()).matches()) {
if (Pattern.compile(key).matcher(((Element_Old) arg).getName()).matches()) {
value.forEach(action -> {
try {
action.action(eventInformation);

View File

@ -5,7 +5,7 @@ import java.time.Duration;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.support.ui.WebDriverWait;
import pres.auxiliary.work.selenium.element.Element;
import pres.auxiliary.work.selenium.element.Element_Old;
public class EventWait {
/**
@ -42,7 +42,7 @@ public class EventWait {
* 用于等待元素消失
* @param waitElement 需要等待的元素
*/
public void disappear(Element waitElement) {
public void disappear(Element_Old waitElement) {
wait.until(driver -> {
return !waitElement.getWebElement().isDisplayed();
});
@ -52,7 +52,7 @@ public class EventWait {
* 用于等待元素元素内出现文本
* @param waitElement 需要等待的元素
*/
public void showText(Element waitElement) {
public void showText(Element_Old waitElement) {
wait.until(driver -> {
return !new TextEvent(driver).getText(waitElement).isEmpty();
});

View File

@ -1,305 +1,305 @@
package pres.auxiliary.work.selenium.event;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import org.openqa.selenium.By;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.StaleElementReferenceException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import pres.auxiliary.work.selenium.element.delect.Element;
import pres.auxiliary.work.selenium.element.delect.ElementType;
/**
* <p>
* <b>文件名</b>JsEvent.java
* </p>
* <p>
* <b>用途</b>用提供通过Javascript来修改或者获取页面元素内容的方法
* </p>
* <p>
* <b>编码时间</b>2018年12月2日 下午12:51:19
* </p>
* <p>
* <b>修改时间</b>2020年5月17日 下午5:21:44
* </p>
*
* @author 彭宇琦
* @version Ver1.0
* @since JDK 12
*/
public class JsEvent extends AbstractEvent {
/**
* 用于使用js
*/
private JavascriptExecutor js;
/**
* 构造对象
*
* @param driver 页面WebDriver对象
*/
public JsEvent(WebDriver driver) {
super(driver);
js = (JavascriptExecutor) this.driver;
}
/**
* 获取元素的指定的属性值若属性不存在时则返回空串
*
* @param element 元素
* @param attributeName 属性名
* @return 元素对应属性的内容
*/
public String getAttribute(Element element, String attributeName) {
// 获取对应元素的内容
String text = (String) (js.executeScript("return arguments[0].getAttribute('" + attributeName + "');",
wait.until(driver -> {
try {
return element.getWebElement();
} catch (StaleElementReferenceException e) {
element.findElement();
return null;
}
})));
// 返回对应属性的内容若传入的属性不存在则返回空串
return (text == null) ? "" : text;
}
/**
* 设置元素的属性值并返回属性的原值若设置的属性名不存在时则在该元素上增加相应的属性
*
* @param element 元素
* @param attributeName 需要设置的属性名
* @param value 需要设置的属性值
* @return 属性的原值
*/
public String putAttribute(Element element, String attributeName, String value) {
// 获取原属性中的值
String oldValue = getAttribute(element, attributeName);
// 执行代码
js.executeScript("arguments[0].setAttribute('" + attributeName + "','" + value + "');", wait.until(driver -> {
try {
return element.getWebElement();
} catch (StaleElementReferenceException e) {
element.findElement();
return null;
}
}));
return oldValue;
}
/**
* 在指定的元素下方添加一个元素元素将带一个名为temp_attribute的属性其属性的值为一个uuid并且添加成功后
* 方法返回该元素定位xpath格式为//标签名[@temp_attribute='uuid']
*
* @param element 元素
* @param elementName 新元素标签的名称
* @return 新增元素的定位方式
*/
public String addElement(Element element, String elementName) {
// 获取并将其作为
String script = "var oldElement = arguments[0];";
// 拼接添加元素的代码
script += "var newElement = document.createElement('" + elementName + "');";
// 给新的元素添加一个属性并将其值设为UUID使其可被搜索得到
String uuid = UUID.randomUUID().toString();
script += "newElement.setAttribute('temp_attribute', '" + uuid + "');";
// 向指定位置添加节点
script += "oldElement.appendChild(newElement);";
// 执行代码
js.executeScript(script, wait.until(driver -> {
try {
return element.getWebElement();
} catch (StaleElementReferenceException e) {
element.findElement();
return null;
}
}));
return "//" + elementName + "[@temp_attribute='" + uuid + "']";
}
/**
* 根据json中的属性及元素名称信息在指定元素下添加一个元素元素将带一个
* 名为temp_attribute的属性其属性的值为一个uuid并且添加成功后
* 方法返回该元素定位xpath格式为//标签名[@temp_attribute='uuid']
* 传入的json格式可以参照{@link #deleteElement(WebElement)}方法中 返回的json形式
*
* @param element 元素
* @param elementJson 新元素标签的信息
* @return 新增元素的定位方式
*/
public String addElement(Element element, JSONObject elementJson) {
// 添加元素
String elementName = elementJson.getString("tagname");
// 获取新添加元素的xpath
String xpath = addElement(element, elementName);
// 查找新添加的元素由于是新添加的元素肯定能查找到故无需编写等待
Element newElement = new Element(driver, "TeamElement", ElementType.COMMON_ELEMENT);
List<By> byList = new ArrayList<>();
byList.add(By.xpath(xpath));
newElement.setByList(byList);
// 获取元素的所有属性
JSONArray attributes = elementJson.getJSONArray("attributes");
// 遍历属性信息向元素中添加属性
for (int i = 0; i < attributes.size(); i++) {
JSONObject attJson = attributes.getJSONObject(i);
putAttribute(newElement, attJson.getString("name"), attJson.getString("value"));
}
return xpath;
}
/**
* 删除指定的元素并以json的格式返回被删除的元素信息其形式为
*
* <pre>
* {
* "tagname":元素标签名称
* "attributes":[
* {
* "name":属性1名称
* "value":属性1值
* },
* {
* "name":属性1名称
* "value":属性1值
* },
* ...
* ]
* }
* </pre>
*
* @param element 元素
* @return 元素的信息
*/
public JSONObject deleteElement(Element element) {
//获取元素信息
JSONObject json = getElementInfromation(wait.until(driver -> {
try {
return element.getWebElement();
} catch (StaleElementReferenceException e) {
element.findElement();
return null;
}
}));
// 获取节点
String script = "var deleteNode = arguments[0];";
// 获取节点的父节点
script += "var parentNode = deleteNode.parentNode;";
// 通过父节点来删除子节点
script += "parentNode.removeChild(deleteNode)";
// 执行代码由于在获取元素信息时已经对元素的过期进行了判断故此处无需在做判断
js.executeScript(script, element.getWebElement());
/*
var a = document.getElementById('psd')
var aa = a.parentNode
aa.removeChild(a)
*
* */
return json;
}
/**
* 用于执行已经写好的js脚本
*
* @param script js脚本
* @return 执行结果
*/
public Object runScript(String script) {
// 执行代码
return js.executeScript(script);
}
/**
* 用于执行已经写好的js脚本
*
* @param script js脚本
* @param args 传入的参数
* @return 执行结果
*/
public Object runScript(String script, Object... args) {
// 执行代码
return js.executeScript(script, args);
}
/**
* 获取元素所有的属性信息以json的形式返回格式为
*
* <pre>
* {
* "tagname":元素标签名称
* "attributes":[
* {
* "name":属性1名称
* "value":属性1值
* },
* {
* "name":属性1名称
* "value":属性1值
* },
* ...
* ]
* }
* </pre>
*
* @param element 元素
* @return 元素属性的信息以json的形式返回
*/
@SuppressWarnings("unchecked")
private JSONObject getElementInfromation(WebElement element) {
JSONObject elementJson = new JSONObject();
// 添加元素的标签名称
elementJson.put("tagname", element.getTagName());
JSONArray attArray = new JSONArray();
// 获取元素的所有属性信息
((ArrayList<Object>) js.executeScript("return arguments[0].attributes;", element)).stream().
// 将属性信息变为字符串的形式
map(obj -> obj.toString()).
// 删除无用的属性信息并将属性信息存储至一个json中进行返回
map(text -> {
// 将属性按照, 进行切分得到每一个信息
String[] atts = text.split("\\,\\ ");
JSONObject json = new JSONObject();
// 过滤属性信息
Arrays.stream(atts).filter(att -> {
String key = att.split("=")[0];
return "name".equals(key) || "value".equals(key);
}).
// 将过滤后的信息存储至json中最后只剩下name和value属性
forEach(att -> {
String[] kv = att.split("=");
json.put(kv[0], kv[1].indexOf("}") > -1 ? kv[1].substring(0, kv[1].length() - 1) : kv[1]);
});
return json;
}).
// 将每一个json存储至attArray中
forEach(json -> {
attArray.add(json);
});
// 存储属性
elementJson.put("attributes", attArray);
return elementJson;
}
}
package pres.auxiliary.work.selenium.event;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import org.openqa.selenium.By;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.StaleElementReferenceException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import pres.auxiliary.work.selenium.element.AbstractBy.Element;
import pres.auxiliary.work.selenium.element.ElementType;
/**
* <p>
* <b>文件名</b>JsEvent.java
* </p>
* <p>
* <b>用途</b>用提供通过Javascript来修改或者获取页面元素内容的方法
* </p>
* <p>
* <b>编码时间</b>2018年12月2日 下午12:51:19
* </p>
* <p>
* <b>修改时间</b>2020年5月17日 下午5:21:44
* </p>
*
* @author 彭宇琦
* @version Ver1.0
* @since JDK 12
*/
public class JsEvent extends AbstractEvent {
/**
* 用于使用js
*/
private JavascriptExecutor js;
/**
* 构造对象
*
* @param driver 页面WebDriver对象
*/
public JsEvent(WebDriver driver) {
super(driver);
js = (JavascriptExecutor) this.driver;
}
/**
* 获取元素的指定的属性值若属性不存在时则返回空串
*
* @param element 元素
* @param attributeName 属性名
* @return 元素对应属性的内容
*/
public String getAttribute(Element element, String attributeName) {
// 获取对应元素的内容
String text = (String) (js.executeScript("return arguments[0].getAttribute('" + attributeName + "');",
wait.until(driver -> {
try {
return element.getWebElement();
} catch (StaleElementReferenceException e) {
element.againFindElement();
return null;
}
})));
// 返回对应属性的内容若传入的属性不存在则返回空串
return (text == null) ? "" : text;
}
/**
* 设置元素的属性值并返回属性的原值若设置的属性名不存在时则在该元素上增加相应的属性
*
* @param element 元素
* @param attributeName 需要设置的属性名
* @param value 需要设置的属性值
* @return 属性的原值
*/
public String putAttribute(Element element, String attributeName, String value) {
// 获取原属性中的值
String oldValue = getAttribute(element, attributeName);
// 执行代码
js.executeScript("arguments[0].setAttribute('" + attributeName + "','" + value + "');", wait.until(driver -> {
try {
return element.getWebElement();
} catch (StaleElementReferenceException e) {
element.againFindElement();
return null;
}
}));
return oldValue;
}
/**
* 在指定的元素下方添加一个元素元素将带一个名为temp_attribute的属性其属性的值为一个uuid并且添加成功后
* 方法返回该元素定位xpath格式为//标签名[@temp_attribute='uuid']
*
* @param element 元素
* @param elementName 新元素标签的名称
* @return 新增元素的定位方式
*/
public String addElement(Element element, String elementName) {
// 获取并将其作为
String script = "var oldElement = arguments[0];";
// 拼接添加元素的代码
script += "var newElement = document.createElement('" + elementName + "');";
// 给新的元素添加一个属性并将其值设为UUID使其可被搜索得到
String uuid = UUID.randomUUID().toString();
script += "newElement.setAttribute('temp_attribute', '" + uuid + "');";
// 向指定位置添加节点
script += "oldElement.appendChild(newElement);";
// 执行代码
js.executeScript(script, wait.until(driver -> {
try {
return element.getWebElement();
} catch (StaleElementReferenceException e) {
element.againFindElement();
return null;
}
}));
return "//" + elementName + "[@temp_attribute='" + uuid + "']";
}
/**
* 根据json中的属性及元素名称信息在指定元素下添加一个元素元素将带一个
* 名为temp_attribute的属性其属性的值为一个uuid并且添加成功后
* 方法返回该元素定位xpath格式为//标签名[@temp_attribute='uuid']
* 传入的json格式可以参照{@link #deleteElement(WebElement)}方法中 返回的json形式
*
* @param element 元素
* @param elementJson 新元素标签的信息
* @return 新增元素的定位方式
*/
public String addElement(Element element, JSONObject elementJson) {
// 添加元素
String elementName = elementJson.getString("tagname");
// 获取新添加元素的xpath
String xpath = addElement(element, elementName);
// 查找新添加的元素由于是新添加的元素肯定能查找到故无需编写等待
Element newElement = new Element(driver, "TeamElement", ElementType.COMMON_ELEMENT);
List<By> byList = new ArrayList<>();
byList.add(By.xpath(xpath));
newElement.setByList(byList);
// 获取元素的所有属性
JSONArray attributes = elementJson.getJSONArray("attributes");
// 遍历属性信息向元素中添加属性
for (int i = 0; i < attributes.size(); i++) {
JSONObject attJson = attributes.getJSONObject(i);
putAttribute(newElement, attJson.getString("name"), attJson.getString("value"));
}
return xpath;
}
/**
* 删除指定的元素并以json的格式返回被删除的元素信息其形式为
*
* <pre>
* {
* "tagname":元素标签名称
* "attributes":[
* {
* "name":属性1名称
* "value":属性1值
* },
* {
* "name":属性1名称
* "value":属性1值
* },
* ...
* ]
* }
* </pre>
*
* @param element 元素
* @return 元素的信息
*/
public JSONObject deleteElement(Element element) {
//获取元素信息
JSONObject json = getElementInfromation(wait.until(driver -> {
try {
return element.getWebElement();
} catch (StaleElementReferenceException e) {
element.againFindElement();
return null;
}
}));
// 获取节点
String script = "var deleteNode = arguments[0];";
// 获取节点的父节点
script += "var parentNode = deleteNode.parentNode;";
// 通过父节点来删除子节点
script += "parentNode.removeChild(deleteNode)";
// 执行代码由于在获取元素信息时已经对元素的过期进行了判断故此处无需在做判断
js.executeScript(script, element.getWebElement());
/*
var a = document.getElementById('psd')
var aa = a.parentNode
aa.removeChild(a)
*
* */
return json;
}
/**
* 用于执行已经写好的js脚本
*
* @param script js脚本
* @return 执行结果
*/
public Object runScript(String script) {
// 执行代码
return js.executeScript(script);
}
/**
* 用于执行已经写好的js脚本
*
* @param script js脚本
* @param args 传入的参数
* @return 执行结果
*/
public Object runScript(String script, Object... args) {
// 执行代码
return js.executeScript(script, args);
}
/**
* 获取元素所有的属性信息以json的形式返回格式为
*
* <pre>
* {
* "tagname":元素标签名称
* "attributes":[
* {
* "name":属性1名称
* "value":属性1值
* },
* {
* "name":属性1名称
* "value":属性1值
* },
* ...
* ]
* }
* </pre>
*
* @param element 元素
* @return 元素属性的信息以json的形式返回
*/
@SuppressWarnings("unchecked")
private JSONObject getElementInfromation(WebElement element) {
JSONObject elementJson = new JSONObject();
// 添加元素的标签名称
elementJson.put("tagname", element.getTagName());
JSONArray attArray = new JSONArray();
// 获取元素的所有属性信息
((ArrayList<Object>) js.executeScript("return arguments[0].attributes;", element)).stream().
// 将属性信息变为字符串的形式
map(obj -> obj.toString()).
// 删除无用的属性信息并将属性信息存储至一个json中进行返回
map(text -> {
// 将属性按照, 进行切分得到每一个信息
String[] atts = text.split("\\,\\ ");
JSONObject json = new JSONObject();
// 过滤属性信息
Arrays.stream(atts).filter(att -> {
String key = att.split("=")[0];
return "name".equals(key) || "value".equals(key);
}).
// 将过滤后的信息存储至json中最后只剩下name和value属性
forEach(att -> {
String[] kv = att.split("=");
json.put(kv[0], kv[1].indexOf("}") > -1 ? kv[1].substring(0, kv[1].length() - 1) : kv[1]);
});
return json;
}).
// 将每一个json存储至attArray中
forEach(json -> {
attArray.add(json);
});
// 存储属性
elementJson.put("attributes", attArray);
return elementJson;
}
}

View File

@ -1,275 +1,282 @@
package pres.auxiliary.work.selenium.event;
import java.io.File;
import java.io.IOException;
import java.util.Random;
import org.openqa.selenium.Rectangle;
import org.openqa.selenium.StaleElementReferenceException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebDriverException;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.ExpectedConditions;
import pres.auxiliary.tool.randomstring.RandomString;
import pres.auxiliary.tool.randomstring.StringMode;
import pres.auxiliary.work.selenium.element.Element;
import pres.auxiliary.work.selenium.tool.RecognitionImage;
import pres.auxiliary.work.selenium.tool.Screenshot;
/**
* <p><b>文件名</b>TextEvent.java</p>
* <p><b>用途</b>定义了对控件文本操作相关的方法可通过该类对页面进行对控件输入文本获取等操作</p>
* <p><b>编码时间</b>2019年9月6日上午9:28:59</p>
* <p><b>修改时间</b>2020年7月10日上午16:49:37</p>
* @author 彭宇琦
* @version Ver1.0
* @since JDK 8
*
*/
public class TextEvent extends AbstractEvent {
/**
* 定义验证码识别的制定文件夹名称
*/
private final String TESSDATA = "tessdata";
/**
* 构造TextEvent类对象
* @param driver WebDriver类对象
*/
public TextEvent(WebDriver driver) {
super(driver);
}
/**
* 该方法通过控件名称或定位方式对页面元素进行定位来清空控件中的内容主要用于清空文本框中已有的数据该方法将存储被清空的
* 文本
* @param element 通过查找页面得到的控件元素对象
* @return 被清空的文本内容
*/
public String clear(Element element) {
//由于需要存储步骤若直接调用getText方法进行返回时其会更改存储的step为保证step正确故存储返回值进行返回
String text = getText(element);
//对元素进行操作若元素过期则重新获取
wait.until(driver -> {
try {
element.getWebElement().clear();
return true;
} catch (StaleElementReferenceException e) {
element.findElement();
return false;
}
});
return text;
}
/**
* 用于获取元素中的指定属性值的内容
* @param element 通过查找页面得到的控件元素对象
* @param attributeName 属性名称
* @return 对应属性的值
*/
public String getAttributeValue(Element element, String attributeName) {
//等待元素中attributeName指向的属性内容出现
wait.until(ExpectedConditions.attributeToBeNotEmpty(element.getWebElement(), attributeName));
//对元素进行操作若元素过期则重新获取
return wait.until(driver -> {
try {
return element.getWebElement().getAttribute(attributeName);
} catch (StaleElementReferenceException e) {
element.findElement();
return null;
}
});
}
/**
* 用于获取相应元素中的文本内容
* @param element 通过查找页面得到的控件元素对象
* @return 对应元素中的文本内容
*/
public String getText(Element element) {
//对元素进行操作若元素过期则重新获取
return wait.until(driver -> {
try {
WebElement webElement = element.getWebElement();
return "input".equalsIgnoreCase(webElement.getTagName()) ? webElement.getAttribute("value") : webElement.getText();
} catch (StaleElementReferenceException e) {
element.findElement();
return null;
}
});
}
/**
* 用于在指定的控件中输入相应的内容
* @param element 通过查找页面得到的控件元素对象
* @param text 需要输入到控件中的
* @return 在控件中输入的内容
*/
public String input(Element element, String text) {
//等待事件可操作后对事件进行操作
wait.until(driver -> {
try {
element.getWebElement().sendKeys(text);
return true;
} catch (Exception e) {
return false;
}
});
return text;
}
/**
* 用于对数字+英文类型的图片验证码进行输入的方法根据验证码图片元素位置识别图片中的验证码
* 并将结果填入对应的文本框中注意该方法识别验证码成功的概率不高数字+英文的验证码模式下
* 经常将数字识别为英文
*
* @param textElement 通过查找页面得到的文本框控件元素对象
* @param codeImageElement 通过查找页面得到的验证码图片控件元素对象
* @return 输入的内容
*/
public String codeInput(Element textElement, Element codeImageElement) {
// 判断验证码信息是否加载加载后获取其Rectang对象
Rectangle r = codeImageElement.getWebElement().getRect();
// 构造截图对象并创建截图
Screenshot sc = new Screenshot(driver, new File("Temp"));
File image = null;
try {
image = sc.creatImage("code");
} catch (WebDriverException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
// 设置图片识别的语言包存放位置
RecognitionImage.setTessdataPath(new File(TESSDATA));
// 识别图片
String text = RecognitionImage.judgeImage(image, r.x, r.y, r.width, r.height);
// 删除生成的图片及文件夹
image.delete();
new File("Temp").delete();
return input(textElement, text);
}
/**
* 该方法用于将一个指定的整数随机填写到传入的控件组中<br>
* <b><i>建议在指定值远远大于控件的数量时再使用该方法否则将会出现不可预期的问题</i></b>
*
* @param num 指定的整数
* @param textElements 通过查找页面得到的一组控件元素对象
* @return 由于涉及到多个文本框故其返回值有多个将以值1,值2,值3...的形式进行返回
*/
public String avgIntergeInput(int num, Element... elements) {
//定义存储控件数量及需要随机的数量
int contrlNum = elements.length;
String inputNumText = "";
String[] inputNum = new String[contrlNum];
// 向下取整获得平均数
int avgNum = num / contrlNum;
// 向上取整获得差值
int diffNum = (int) Math.ceil(avgNum / 10.0);
//求取通过随机得出的值之和用于计算最终其随机值之和与实际值的差值
int sum = 0;
//循环生成控件个数个随机值其随机值在给定数值的均值之前
for (int i = 0; i < contrlNum; i++) {
//注意2 * diffNum为以下算式的简写
//minNum = avgNum - diffNum;
//maxNum = avgNum + diffNum;
//int ranNum = new Random().nextInt(maxNum - minNum + 1) + minNum;
int ranNum = new Random().nextInt(2 * diffNum + 1) + (avgNum - diffNum);
sum += ranNum;
inputNum[i] = String.valueOf(ranNum);
}
//由于数值是随机的可能会出现随机值相加不为指定值故需要补上差值但由于差值通过算法后不会很大故可随机附加到一个控件值上
if ( (diffNum = sum - num) != 0 ) {
inputNum[new Random().nextInt(contrlNum)] = String.valueOf(Integer.valueOf(inputNum[new Random().nextInt(contrlNum)]) - diffNum);
}
//将随机值填写至控件中
for (int i = 0; i < contrlNum; i++) {
inputNumText += (input(elements[i], inputNum[i]) + ",");
}
return inputNumText.substring(0, inputNumText.length() - 1);
}
/**
* 通过指定的随机字符串长度与指定的随机字符串模型枚举{@link StringMode}枚举类向控件中
* 随机输入字符串
* @param element 通过查找页面得到的控件元素对象
* @param minLength 字符串最小长度设为小于等于0的数值时则默认为1
* @param maxLength 字符串最大长度设为小于等于0的数值时则默认为1若需要指定字符串的输入长度可设置minLength数值与maxLength一致
* @param modes {@link StringMode}枚举指定字符串输入的类型可传入多种模型参见{@link RandomString#RandomString(StringMode...)}
* @return 在控件中输入的内容
*/
public String randomInput(Element element, int minLength, int maxLength, StringMode... modes) {
return randomInput(element, minLength, maxLength, new RandomString(modes));
}
/**
* 通过指定的随机字符串长度与指定的随机字符串模型向控件中随机输入字符串操作
* @param element 通过查找页面得到的控件元素对象
* @param minLength 字符串最小长度设为小于等于0的数值时则默认为1
* @param maxLength 字符串最大长度设为小于等于0的数值时则默认为1若需要指定字符串的输入长度可设置minLength数值与maxLength一致
* @param mode 可用的随机字符串抽取范围参见{@link RandomString#RandomString(String)}
* @return 在控件中输入的内容
*/
public String randomInput(Element element, int minLength, int maxLength, String mode) {
return randomInput(element, minLength, maxLength, new RandomString(mode));
}
/**
* 用于向控件中上传指定的文件<br>
* @param element 通过查找页面得到的控件元素对象
* @param updataFile 需要上传到控件中的文件
* @return 上传的文件路径
*/
public String updataFile(Element element, File updataFile) {
return input(element, updataFile.getAbsolutePath());
}
/**
* 向控件随机输入信息的底层方法
* @param element 通过查找页面得到的控件元素对象
* @param minLength 字符串最小长度设为小于等于0的数值时则默认为1
* @param maxLength 字符串最大长度设为小于等于0的数值时则默认为1若需要指定字符串的输入长度可设置minLength数值与maxLength一致
* @param rs 随机字符类对象
* @return 在控件中输入的内容
*/
private String randomInput(Element element, int minLength, int maxLength, RandomString rs) {
// 判断传入的参数是否小于0小于0则将其都设置为1
if (minLength < 0 || maxLength < 0) {
minLength = 1;
maxLength = 1;
}
// 判断传入的随机字符串最小生成长度是否大于最大生成长度若大于则调换两数字的位置
if (minLength > maxLength) {
int tem = minLength;
minLength = maxLength;
maxLength = tem;
}
//根据参数生成随机字符串
String text = "";
if (minLength == maxLength) {
text = rs.toString(maxLength);
} else {
text = rs.toString(minLength, maxLength);
}
//调用input方法进行返回
return input(element, text);
}
}
package pres.auxiliary.work.selenium.event;
import java.io.File;
import java.io.IOException;
import java.util.Random;
import org.openqa.selenium.Keys;
import org.openqa.selenium.Rectangle;
import org.openqa.selenium.StaleElementReferenceException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebDriverException;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.ExpectedConditions;
import pres.auxiliary.tool.randomstring.RandomString;
import pres.auxiliary.tool.randomstring.StringMode;
import pres.auxiliary.work.selenium.element.AbstractBy.Element;
import pres.auxiliary.work.selenium.tool.RecognitionImage;
import pres.auxiliary.work.selenium.tool.Screenshot;
/**
* <p><b>文件名</b>TextEvent.java</p>
* <p><b>用途</b>定义了对控件文本操作相关的方法可通过该类对页面进行对控件输入文本获取等操作</p>
* <p><b>编码时间</b>2019年9月6日上午9:28:59</p>
* <p><b>修改时间</b>2020年7月10日上午16:49:37</p>
* @author 彭宇琦
* @version Ver1.0
* @since JDK 8
*
*/
public class TextEvent extends AbstractEvent {
/**
* 定义验证码识别的制定文件夹名称
*/
private final String TESSDATA = "tessdata";
/**
* 构造TextEvent类对象
* @param driver WebDriver类对象
*/
public TextEvent(WebDriver driver) {
super(driver);
}
/**
* 该方法通过控件名称或定位方式对页面元素进行定位来清空控件中的内容主要用于清空文本框中已有的数据该方法将存储被清空的
* 文本
* @param element 通过查找页面得到的控件元素对象
* @return 被清空的文本内容
*/
public String clear(Element element) {
//由于需要存储步骤若直接调用getText方法进行返回时其会更改存储的step为保证step正确故存储返回值进行返回
String text = getText(element);
//对元素进行操作若元素过期则重新获取
wait.until(driver -> {
try {
element.getWebElement().clear();
return true;
} catch (StaleElementReferenceException e) {
element.againFindElement();
return false;
}
});
return text;
}
/**
* 用于获取元素中的指定属性值的内容
* @param element 通过查找页面得到的控件元素对象
* @param attributeName 属性名称
* @return 对应属性的值
*/
public String getAttributeValue(Element element, String attributeName) {
//等待元素中attributeName指向的属性内容出现
wait.until(ExpectedConditions.attributeToBeNotEmpty(element.getWebElement(), attributeName));
//对元素进行操作若元素过期则重新获取
return wait.until(driver -> {
try {
return element.getWebElement().getAttribute(attributeName);
} catch (StaleElementReferenceException e) {
element.againFindElement();
return null;
}
});
}
/**
* 用于获取相应元素中的文本内容
* @param element 通过查找页面得到的控件元素对象
* @return 对应元素中的文本内容
*/
public String getText(Element element) {
//对元素进行操作若元素过期则重新获取
return wait.until(driver -> {
try {
WebElement webElement = element.getWebElement();
return "input".equalsIgnoreCase(webElement.getTagName()) ? webElement.getAttribute("value") : webElement.getText();
} catch (StaleElementReferenceException e) {
element.againFindElement();
return null;
}
});
}
/**
* 用于在指定的控件中输入相应的内容允许向控件中传入{@link Keys}枚举
* @param element 通过查找页面得到的控件元素对象
* @param keysToSend 需要输入到控件中的
* @return 在控件中输入的内容
*/
public String input(Element element, CharSequence... keysToSend) {
//等待事件可操作后对事件进行操作
wait.until(driver -> {
try {
element.getWebElement().sendKeys(keysToSend);
return true;
} catch (Exception e) {
return false;
}
});
//TODO 拆成两个方法否则不好写返回结果
String result = "[";
for (CharSequence key : keysToSend) {
result += key.toString() + ", ";
}
return keysToSend.toString();
}
/**
* 用于对数字+英文类型的图片验证码进行输入的方法根据验证码图片元素位置识别图片中的验证码
* 并将结果填入对应的文本框中注意该方法识别验证码成功的概率不高数字+英文的验证码模式下
* 经常将数字识别为英文
*
* @param textElement 通过查找页面得到的文本框控件元素对象
* @param codeImageElement 通过查找页面得到的验证码图片控件元素对象
* @return 输入的内容
*/
public String codeInput(Element textElement, Element codeImageElement) {
// 判断验证码信息是否加载加载后获取其Rectang对象
Rectangle r = codeImageElement.getWebElement().getRect();
// 构造截图对象并创建截图
Screenshot sc = new Screenshot(driver, new File("Temp"));
File image = null;
try {
image = sc.creatImage("code");
} catch (WebDriverException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
// 设置图片识别的语言包存放位置
RecognitionImage.setTessdataPath(new File(TESSDATA));
// 识别图片
String text = RecognitionImage.judgeImage(image, r.x, r.y, r.width, r.height);
// 删除生成的图片及文件夹
image.delete();
new File("Temp").delete();
return input(textElement, text);
}
/**
* 该方法用于将一个指定的整数随机填写到传入的控件组中<br>
* <b><i>建议在指定值远远大于控件的数量时再使用该方法否则将会出现不可预期的问题</i></b>
*
* @param num 指定的整数
* @param textElements 通过查找页面得到的一组控件元素对象
* @return 由于涉及到多个文本框故其返回值有多个将以值1,值2,值3...的形式进行返回
*/
public String avgIntergeInput(int num, Element... elements) {
//定义存储控件数量及需要随机的数量
int contrlNum = elements.length;
String inputNumText = "";
String[] inputNum = new String[contrlNum];
// 向下取整获得平均数
int avgNum = num / contrlNum;
// 向上取整获得差值
int diffNum = (int) Math.ceil(avgNum / 10.0);
//求取通过随机得出的值之和用于计算最终其随机值之和与实际值的差值
int sum = 0;
//循环生成控件个数个随机值其随机值在给定数值的均值之前
for (int i = 0; i < contrlNum; i++) {
//注意2 * diffNum为以下算式的简写
//minNum = avgNum - diffNum;
//maxNum = avgNum + diffNum;
//int ranNum = new Random().nextInt(maxNum - minNum + 1) + minNum;
int ranNum = new Random().nextInt(2 * diffNum + 1) + (avgNum - diffNum);
sum += ranNum;
inputNum[i] = String.valueOf(ranNum);
}
//由于数值是随机的可能会出现随机值相加不为指定值故需要补上差值但由于差值通过算法后不会很大故可随机附加到一个控件值上
if ( (diffNum = sum - num) != 0 ) {
inputNum[new Random().nextInt(contrlNum)] = String.valueOf(Integer.valueOf(inputNum[new Random().nextInt(contrlNum)]) - diffNum);
}
//将随机值填写至控件中
for (int i = 0; i < contrlNum; i++) {
inputNumText += (input(elements[i], inputNum[i]) + ",");
}
return inputNumText.substring(0, inputNumText.length() - 1);
}
/**
* 通过指定的随机字符串长度与指定的随机字符串模型枚举{@link StringMode}枚举类向控件中
* 随机输入字符串
* @param element 通过查找页面得到的控件元素对象
* @param minLength 字符串最小长度设为小于等于0的数值时则默认为1
* @param maxLength 字符串最大长度设为小于等于0的数值时则默认为1若需要指定字符串的输入长度可设置minLength数值与maxLength一致
* @param modes {@link StringMode}枚举指定字符串输入的类型可传入多种模型参见{@link RandomString#RandomString(StringMode...)}
* @return 在控件中输入的内容
*/
public String randomInput(Element element, int minLength, int maxLength, StringMode... modes) {
return randomInput(element, minLength, maxLength, new RandomString(modes));
}
/**
* 通过指定的随机字符串长度与指定的随机字符串模型向控件中随机输入字符串操作
* @param element 通过查找页面得到的控件元素对象
* @param minLength 字符串最小长度设为小于等于0的数值时则默认为1
* @param maxLength 字符串最大长度设为小于等于0的数值时则默认为1若需要指定字符串的输入长度可设置minLength数值与maxLength一致
* @param mode 可用的随机字符串抽取范围参见{@link RandomString#RandomString(String)}
* @return 在控件中输入的内容
*/
public String randomInput(Element element, int minLength, int maxLength, String mode) {
return randomInput(element, minLength, maxLength, new RandomString(mode));
}
/**
* 用于向控件中上传指定的文件<br>
* @param element 通过查找页面得到的控件元素对象
* @param updataFile 需要上传到控件中的文件
* @return 上传的文件路径
*/
public String updataFile(Element element, File updataFile) {
return input(element, updataFile.getAbsolutePath());
}
/**
* 向控件随机输入信息的底层方法
* @param element 通过查找页面得到的控件元素对象
* @param minLength 字符串最小长度设为小于等于0的数值时则默认为1
* @param maxLength 字符串最大长度设为小于等于0的数值时则默认为1若需要指定字符串的输入长度可设置minLength数值与maxLength一致
* @param rs 随机字符类对象
* @return 在控件中输入的内容
*/
private String randomInput(Element element, int minLength, int maxLength, RandomString rs) {
// 判断传入的参数是否小于0小于0则将其都设置为1
if (minLength < 0 || maxLength < 0) {
minLength = 1;
maxLength = 1;
}
// 判断传入的随机字符串最小生成长度是否大于最大生成长度若大于则调换两数字的位置
if (minLength > maxLength) {
int tem = minLength;
minLength = maxLength;
maxLength = tem;
}
//根据参数生成随机字符串
String text = "";
if (minLength == maxLength) {
text = rs.toString(maxLength);
} else {
text = rs.toString(minLength, maxLength);
}
//调用input方法进行返回
return input(element, text);
}
}

View File

@ -16,6 +16,23 @@ import pres.auxiliary.work.selenium.element.ElementType;
*
*/
public abstract class AbstractRead {
/**
* 定义用于正则的替换符开始标记
*/
public static final String MATCH_START_SIGN = "\\$\\{";
/**
* 定义替换符开始标志
*/
public static final String START_SIGN = "${";
/**
* 定义用于正则的替换符结束标记
*/
public static final String MATCH_END_SIGN = "\\}";
/**
* 定义替换符结束标志
*/
public static final String END_SIGN = "}";
/**
* 用于返回元素的所有定位方式集合
* @return 元素的所有定位方式{@link ByType}枚举集合

View File

@ -47,23 +47,6 @@ public class ReadXml extends AbstractRead {
*/
private Document dom;
/**
* 定义用于正则的替换符开始标记
*/
public static final String MATCH_START_SIGN = "\\$\\{";
/**
* 定义替换符开始标志
*/
public static final String START_SIGN = "${";
/**
* 定义用于正则的替换符结束标记
*/
public static final String MATCH_END_SIGN = "\\}";
/**
* 定义替换符结束标志
*/
public static final String END_SIGN = "}";
/**
* 构造对象
* @param xmlFile xml文件对象

View File

@ -189,9 +189,9 @@ public class ExcelRecord extends AbstractWriteExcel<ExcelRecord> {
public ExcelRecord exception(Exception exception) {
isError = true;
return switchSheet("错误记录")
.addContent("id", fieldMap.get("运行记录").get("id").getContent(0))
.addContent("class_name", fieldMap.get("运行记录").get("class_name").getContent(0))
.addContent("method_name", fieldMap.get("运行记录").get("method_name").getContent(0))
.addContent("id", fieldMap.get("运行记录").get("id").content.size() == 0 ? "" : fieldMap.get("运行记录").get("id").getContent(0))
.addContent("class_name", fieldMap.get("运行记录").get("class_name").content.size() == 0 ? "" : fieldMap.get("运行记录").get("class_name").getContent(0))
.addContent("method_name", fieldMap.get("运行记录").get("method_name").content.size() == 0 ? "" : fieldMap.get("运行记录").get("method_name").getContent(0))
.addContent("error_step", fieldMap.get("运行记录").get("step").content.size() == 0 ? "" : fieldMap.get("运行记录").get("step").getContent(-1))
.addContent("error_class", exception.getClass().getName())
.addContent("error_information", exception.getMessage());

View File

@ -0,0 +1,117 @@
package pres.auxiliary.work.selenium.element;
import java.io.File;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import pres.auxiliary.work.selenium.location.AbstractRead;
import pres.auxiliary.work.selenium.location.ReadXml;
/**
* <p><b>文件名</b>ElementDataTest.java</p>
* <p><b>用途</b>
* {@link ElementData}类进行单元测试
* </p>
* <p><b>编码时间</b>2020年10月12日上午8:47:14</p>
* <p><b>修改时间</b>2020年10月12日上午8:47:14</p>
* @author 彭宇琦
* @version Ver1.0
*
*/
public class ElementDataTest {
AbstractRead ar;
ElementData test;
final File XML_FILE = new File("src/test/java/pres/auxiliary/work/selenium/element/测试用xml文件.xml");
@BeforeClass
public void init() {
ar = new ReadXml(XML_FILE);
}
@BeforeMethod
public void addElement() {
test = new ElementData("XX控件6", ar);
}
/**
* 用于测试{@link ElementData#getName()}方法<br>
* 预期<br>
* XX控件6
*/
@Test
public void getNameTest() {
System.out.println(test.getName());
}
/**
* 用于测试{@link ElementData#getByTypeList()}方法<br>
* 预期<br>
* XPATH<br>
* CSS<br>
* XPATH
*/
@Test
public void getByTypeListTest() {
test.getByTypeList().forEach(System.out :: println);
}
/**
* 用于测试{@link ElementData#getValueList()}方法<br>
* 预期<br>
* //XXX控件6[@X='XXXX']<br>
* http body ${tagName}<br>
* //XXX模板控件1[@X='${src}']/div[@name='XXX控件6']/div[@is='test' and text()='${src}']/span[text()='${str2}']/span[id='${aaaa}']
*/
@Test
public void getValueListTest() {
test.getValueList().forEach(System.out :: println);
}
/**
* 用于测试{@link ElementData#getElementType()}方法<br>
* 预期<br>
* COMMON_ELEMENT
*/
@Test
public void getElementTypeTest() {
System.out.println(test.getElementType());
}
/**
* 用于测试{@link ElementData#getIframeNameList()}方法<br>
* 预期<br>
* 窗体1<br>
* 窗体1.1<br>
* 窗体1.1.1
*/
@Test
public void getIframeNameListTest() {
test.getIframeNameList().forEach(System.out :: println);
}
/**
* 用于测试{@link ElementData#getWaitTime()}方法<br>
* 预期<br>
* -1
*/
@Test
public void getWaitTimeTest() {
System.out.println(test.getWaitTime());
}
/**
* 用于测试{@link ElementData#addLinkWord(String...)}方法<br>
* 预期<br>
* //XXX控件6[@X='XXXX']<br>
* http body 外链1<br>
* //XXX模板控件1[@X='外链1']/div[@name='XXX控件6']/div[@is='test' and text()='外链2']/span[text()='外链3']/span[id='${aaaa}']
*/
@Test
public void addLinkWordTest() {
test.addLinkWord("外链1", "外链2", "外链3");
test.getValueList().forEach(System.out :: println);
}
}

View File

@ -16,7 +16,7 @@ import pres.auxiliary.work.selenium.event.ClickEvent;
/**
* <p><b>文件名</b>SelectByText.java</p>
* <p><b>用途</b>
* {@link SelectBy}类进行测试
* {@link SelectBy_Old}类进行测试
* </p>
* <p><b>页面</b>
* 对标准型下拉选项测试页面为jira提BUG弹窗对非标准型下拉为运营系统测试环境消息推送管理页面
@ -30,16 +30,16 @@ import pres.auxiliary.work.selenium.event.ClickEvent;
*/
public class SelectByTest {
ChromeBrower cb = new ChromeBrower(new File("Resource/BrowersDriver/Chrom/78.0394.70/chromedriver.exe"));
SelectBy s;
CommonBy cby;
SelectBy_Old s;
CommonBy_Old cby;
ClickEvent ce;
@BeforeClass(alwaysRun = true)
public void init() {
cb.addConfig(ChromeOptionType.CONTRAL_OPEN_BROWER, "127.0.0.1:9222");
s = new SelectBy(cb);
cby = new CommonBy(cb);
s = new SelectBy_Old(cb);
cby = new CommonBy_Old(cb);
ce = new ClickEvent(cb.getDriver());
}
@ -54,7 +54,7 @@ public class SelectByTest {
*/
@Test(groups = "list")
public void changeElement_List() throws InterruptedException {
Element e = cby.getElement("/html/body/div[1]/div/div/section/div/div[1]/div[4]/div[1]/input");
Element_Old e = cby.getElement("/html/body/div[1]/div/div/section/div/div[1]/div[4]/div[1]/input");
ce.click(e);
s.add("/html/body/div/div/div[1]/ul/li/span");

View File

@ -1,26 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project name="工资专户管理">
<templet>
<!-- 窗体模板 -->
<xpath id='1'>//iframe[contains(@src,'${src}')]</xpath>
<xpath id='3'>//lable[contains(text(), '${condition}')]/../span/input[1]</xpath>
</templet>
<iframe name='主窗体'>
<xpath is_use='true' temp_id='1'
src='/Regulatory/admin/index.jsp' />
<iframe name='工资发放详情'>
<xpath is_use='true' temp_id='1' src='工资单管理' />
<!-- 搜索项 -->
<element name='单位名称'>
<xpath is_use='true'>//*[@id="listBox"]/li[1]/div[1]/p/span[1]</xpath>
</element>
<element name='搜索条件'>
<xpath is_use='true' temp_id='3' />
</element>
</iframe>
</iframe>
</project>

View File

@ -0,0 +1,110 @@
<?xml version="1.0" encoding="UTF-8"?>
<project name="">
<templet>
<xpath id='1'>//XXX模板控件1[@X='${name}']/div/div[@${att}='${id}']/input</xpath>
<css id='2'>http body ${tagName}</css>
<xpath id='3'>//XXX模板控件1[@X='${src}']/div[@name='${name}']</xpath>
<xpath id='4'>//XXX模板控件1[@X='${src}']/div[@name='${name}']/div[@is='${str1}' and text()='${str1}']</xpath>
<xpath id='5'>//XXX模板控件1[@X='${src}']/div[@name='${name}']/div[@is='${str1}' and text()='${src}']/span[text()='${str2}']/span[id='${aaaa}']</xpath>
<id id='6'>${name}</id>
</templet>
<element name='XX控件1'>
<xpath is_use='true'>//XX控件1[@X='XXXX']</xpath>
</element>
<iframe name='窗体1' >
<xpath is_use='true'>//窗体1[@X='XXXX']</xpath>
<css temp_id='2' tagName='iframe'/>
<element name='XX控件2'>
<xpath is_use='true'>//XX控件2[@X='XXXX']</xpath>
</element>
<element name='XX控件3'>
<xpath is_use='true'>//XXX控件3[@X='XXXX']</xpath>
</element>
<iframe name='窗体1.1' >
<xpath is_use='true'>//窗体1.1[@X='XXXX']</xpath>
<element name='XX控件4'>
<xpath is_use='true'>//XXX控件4[@X='XXXX']</xpath>
</element>
<element name='XX控件5'>
<xpath is_use='true'>//XXX控件5[@X='XXXX']</xpath>
</element>
<iframe name='窗体1.1.1' >
<xpath is_use='true'>//窗体1.1.1[@X='XXXX']</xpath>
<element name='XX控件6'>
<xpath is_use='true'>//XXX控件6[@X='XXXX']</xpath>
<css temp_id='2'></css>
<xpath temp_id='5' str1='test'></xpath>
</element>
<element name='XX控件7'>
<xpath is_use='true'>//XXX控件7[@X='XXXX']</xpath>
</element>
</iframe>
</iframe>
<iframe name='窗体1.2' >
<xpath is_use='true'>//窗体1.2[@X='XXXX']</xpath>
<element name='XX控件8'>
<xpath is_use='true'>//XXX控件8[@X='XXXX']</xpath>
</element>
<element name='XX控件9'>
<xpath is_use='true'>//XXX控件9[@X='XXXX']</xpath>
</element>
</iframe>
</iframe>
<iframe name='窗体2' >
<xpath is_use='true'>//窗体2[@X='XXXX']</xpath>
<element name='XX控件10'>
<xpath is_use='true'>//XXX控件10[@X='XXXX']</xpath>
</element>
<element name='XX控件11'>
<xpath is_use='true' temp_id='1' id='Test' att='src'/>
<css is_use='true' temp_id='2' tagName='div'/>
<xpath is_use='true' temp_id='3' />
<id temp_id='6' name='测试控件'/>
</element>
<element name='XX控件12'>
<xpath is_use='true' temp_id='3' />
</element>
</iframe>
<iframe name='窗体3' >
<xpath is_use='true'>//窗体3[@X='${ccc}']</xpath>
<element name='XX控件13'>
<xpath is_use='true' temp_id='3' />
</element>
<element name='XX控件14'>
<xpath is_use='true' temp_id='4' />
</element>
<element name='XX控件15' wait='100'>
<xpath is_use='true' temp_id='5' />
</element>
<element name='XX控件16' element_type='0' wait='1'>
<xpath is_use='true' temp_id='1' id='Test' att='src'/>
</element>
<element name='XX控件17' element_type='1' wait='0'>
<xpath is_use='true' temp_id='1' id='Test' att='src'/>
</element>
<element name='XX控件18' element_type='2' wait='-11'>
<xpath is_use='true' temp_id='1' id='Test' att='src'/>
</element>
<element name='XX控件19' element_type='3' wait='ddsf'>
<xpath is_use='true' temp_id='1' id='Test' att='src'/>
</element>
<element name='XX控件20' element_type='3' wait='ddsf'>
<xpath is_use='true' temp_id='测试' id='Test' att='src'/>
</element>
</iframe>
</project>

View File

@ -1,59 +1,59 @@
package pres.auxiliary.work.selenium.event;
import java.io.File;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import pres.auxiliary.work.selenium.brower.ChromeBrower;
import pres.auxiliary.work.selenium.brower.ChromeBrower.ChromeOptionType;
import pres.auxiliary.work.selenium.element.delect.old.CommonBy;
import pres.auxiliary.work.selenium.event.EventProxy.ActionType;
public class EventProxyTest {
EventProxy<ClickEvent> clickProxy;
EventProxy<TextEvent> inputProxy;
ChromeBrower chrome;
CommonBy by;
@BeforeClass
public void init() {
chrome = new ChromeBrower(new File("Resource/BrowersDriver/Chrom/83.0.4103.39/chromedriver.exe"));
chrome.addConfig(ChromeOptionType.CONTRAL_OPEN_BROWER, "127.0.0.1:9222");
clickProxy = new EventProxy<>(new ClickEvent(chrome.getDriver()));
inputProxy = new EventProxy<>(new TextEvent(chrome.getDriver()));
by = new CommonBy(chrome);
}
@AfterClass
public void showResult() {
ClickEvent click = clickProxy.getProxyInstance();
click.doubleClick(by.getElement("//*[text()='登录']"));
}
@Test
public void addAcionTest() {
TextEvent textEvent = new TextEvent(chrome.getDriver());
textEvent.input(by.getElement("//*[@name='account']"), "admin");
textEvent.input(by.getElement("//*[@name='password']"), "1111111");
inputProxy.addAcion(ActionType.FUNCTION_BEFORE, ".*input.*", (info) -> {
inputProxy.getProxyInstance().clear(info.getElement().get(0));
});
clickProxy.addAcion(ActionType.ELEMENT_BEFORE, ".*登录.*", (info) -> {
TextEvent text = inputProxy.getProxyInstance();
text.input(by.getElement("//*[@name='account']"), "admin");
text.input(by.getElement("//*[@name='password']"), "1111111");
});
clickProxy.addAcion(ActionType.ELEMENT_AFTER, ".*登录.*", (info) -> {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
}
by.alertAccept();
});
}
}
package pres.auxiliary.work.selenium.event;
import java.io.File;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import pres.auxiliary.work.selenium.brower.ChromeBrower;
import pres.auxiliary.work.selenium.brower.ChromeBrower.ChromeOptionType;
import pres.auxiliary.work.selenium.element.delect.old.CommonBy;
import pres.auxiliary.work.selenium.event.EventProxy.ActionType;
public class EventProxyTest {
EventProxy<ClickEvent> clickProxy;
EventProxy<TextEvent> inputProxy;
ChromeBrower chrome;
CommonBy_Old by;
@BeforeClass
public void init() {
chrome = new ChromeBrower(new File("Resource/BrowersDriver/Chrom/83.0.4103.39/chromedriver.exe"));
chrome.addConfig(ChromeOptionType.CONTRAL_OPEN_BROWER, "127.0.0.1:9222");
clickProxy = new EventProxy<>(new ClickEvent(chrome.getDriver()));
inputProxy = new EventProxy<>(new TextEvent(chrome.getDriver()));
by = new CommonBy_Old(chrome);
}
@AfterClass
public void showResult() {
ClickEvent click = clickProxy.getProxyInstance();
click.doubleClick(by.getElement("//*[text()='登录']"));
}
@Test
public void addAcionTest() {
TextEvent textEvent = new TextEvent(chrome.getDriver());
textEvent.input(by.getElement("//*[@name='account']"), "admin");
textEvent.input(by.getElement("//*[@name='password']"), "1111111");
inputProxy.addAcion(ActionType.FUNCTION_BEFORE, ".*input.*", (info) -> {
inputProxy.getProxyInstance().clear(info.getElement().get(0));
});
clickProxy.addAcion(ActionType.ELEMENT_BEFORE, ".*登录.*", (info) -> {
TextEvent text = inputProxy.getProxyInstance();
text.input(by.getElement("//*[@name='account']"), "admin");
text.input(by.getElement("//*[@name='password']"), "1111111");
});
clickProxy.addAcion(ActionType.ELEMENT_AFTER, ".*登录.*", (info) -> {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
}
by.alertAccept();
});
}
}

View File

@ -30,13 +30,13 @@ public class EventWaitTest {
EventWait wait;
ClickEvent c;
TextEvent t;
CommonBy cby;
CommonBy_Old cby;
ChromeBrower cb = new ChromeBrower(new File("Resource/BrowersDriver/Chrom/78.0394.70/chromedriver.exe"));
@BeforeClass
public void init() {
cb.addConfig(ChromeOptionType.CONTRAL_OPEN_BROWER, "127.0.0.1:9222");
cby = new CommonBy(cb);
cby = new CommonBy_Old(cb);
c = new ClickEvent(cb.getDriver());
t = new TextEvent(cb.getDriver());
wait = new EventWait(cb.getDriver());
@ -54,7 +54,7 @@ public class EventWaitTest {
@Test
public void disappearTest() {
//获取编号列
DataListBy element = new DataListBy(cb);
DataListBy_Old element = new DataListBy_Old(cb);
element.add("/html/body/div[1]/div/div/section/div/div[2]/div[3]/table//td[contains(@class,'el-table_1_column_1 ')]/div/span");
System.out.println(element.getMinColumnSize());

View File

@ -1,106 +1,106 @@
package pres.auxiliary.work.selenium.event;
import java.io.File;
import java.lang.reflect.Method;
import org.openqa.selenium.WebElement;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import com.alibaba.fastjson.JSONObject;
import pres.auxiliary.work.selenium.brower.ChromeBrower;
import pres.auxiliary.work.selenium.brower.ChromeBrower.ChromeOptionType;
import pres.auxiliary.work.selenium.element.delect.old.CommonBy;
import pres.auxiliary.work.selenium.element.delect.old.Element;
/**
* <p><b>文件名</b>JsEventTest.java</p>
* <p><b>用途</b>
* 用于对{@link JsEvent}类进行单元测试使用控制已打开的浏览器
* </p>
* <p><b>页面</b>
* https://www.baidu.com/百度首页针对搜索条件文本框
* </p>
* <p><b>编码时间</b>2020年5月17日 下午1:40:28</p>
* <p><b>修改时间</b>2020年5月17日 下午1:40:28</p>
* @author 彭宇琦
* @version Ver1.0
* @since JDK 12
*/
public class JsEventTest {
/**
* 输入文本框元素对象
*/
Element inputElemnt;
JsEvent event;
ChromeBrower cb;
/**
* 初始化数据
*/
@BeforeClass
public void init() {
cb = new ChromeBrower(new File("Resource/BrowersDriver/Chrom/78.0394.70/chromedriver.exe"));
cb.addConfig(ChromeOptionType.CONTRAL_OPEN_BROWER, "127.0.0.1:9222");
inputElemnt = new CommonBy(cb).getElement("//*[@id='kw']");
//初始化js类
event = new JsEvent(cb.getDriver());
}
@AfterClass
public void quit() {
cb.getDriver().quit();
}
@BeforeMethod
public void show(Method method) {
System.out.println("================================");
System.out.println(method.getName() + "方法测试结果:");
}
/**
* 测试{@link JsEvent#getAttribute(WebElement, String)}方法
*/
@Test
public void getAttributeTest() {
System.out.println(event.getAttribute(inputElemnt, "class"));
}
/**
* 测试{@link JsEvent#putAttribute(WebElement, String, String)}方法
*/
@Test
public void putAttributeTest() {
System.out.println(event.putAttribute(inputElemnt, "lll", null));
}
/**
* 测试{@link JsEvent#addElement(WebElement, String)}方法
*/
@Test
public void addElementTest_String() {
System.out.println(event.addElement(inputElemnt, "input"));
}
/**
* 测试{@link JsEvent#deleteElement(WebElement)}方法
*/
@Test
public void deleteElementTest() {
System.out.println(event.deleteElement(inputElemnt));
}
/**
* 测试{@link JsEvent#addElement(WebElement, com.alibaba.fastjson.JSONObject)}方法
*/
@Test
public void addElementTest_Json() {
JSONObject json = event.deleteElement(new CommonBy(cb.getDriver()).getElement("//*[@value = '百度一下']"));
Element e = new CommonBy(cb.getDriver()).getElement("//*[text() = '我的关注']");
System.out.println(event.addElement(e, json));
}
}
package pres.auxiliary.work.selenium.event;
import java.io.File;
import java.lang.reflect.Method;
import org.openqa.selenium.WebElement;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import com.alibaba.fastjson.JSONObject;
import pres.auxiliary.work.selenium.brower.ChromeBrower;
import pres.auxiliary.work.selenium.brower.ChromeBrower.ChromeOptionType;
import pres.auxiliary.work.selenium.element.delect.old.CommonBy;
import pres.auxiliary.work.selenium.element.delect.old.Element;
/**
* <p><b>文件名</b>JsEventTest.java</p>
* <p><b>用途</b>
* 用于对{@link JsEvent}类进行单元测试使用控制已打开的浏览器
* </p>
* <p><b>页面</b>
* https://www.baidu.com/百度首页针对搜索条件文本框
* </p>
* <p><b>编码时间</b>2020年5月17日 下午1:40:28</p>
* <p><b>修改时间</b>2020年5月17日 下午1:40:28</p>
* @author 彭宇琦
* @version Ver1.0
* @since JDK 12
*/
public class JsEventTest {
/**
* 输入文本框元素对象
*/
Element_Old inputElemnt;
JsEvent event;
ChromeBrower cb;
/**
* 初始化数据
*/
@BeforeClass
public void init() {
cb = new ChromeBrower(new File("Resource/BrowersDriver/Chrom/78.0394.70/chromedriver.exe"));
cb.addConfig(ChromeOptionType.CONTRAL_OPEN_BROWER, "127.0.0.1:9222");
inputElemnt = new CommonBy_Old(cb).getElement("//*[@id='kw']");
//初始化js类
event = new JsEvent(cb.getDriver());
}
@AfterClass
public void quit() {
cb.getDriver().quit();
}
@BeforeMethod
public void show(Method method) {
System.out.println("================================");
System.out.println(method.getName() + "方法测试结果:");
}
/**
* 测试{@link JsEvent#getAttribute(WebElement, String)}方法
*/
@Test
public void getAttributeTest() {
System.out.println(event.getAttribute(inputElemnt, "class"));
}
/**
* 测试{@link JsEvent#putAttribute(WebElement, String, String)}方法
*/
@Test
public void putAttributeTest() {
System.out.println(event.putAttribute(inputElemnt, "lll", null));
}
/**
* 测试{@link JsEvent#addElement(WebElement, String)}方法
*/
@Test
public void addElementTest_String() {
System.out.println(event.addElement(inputElemnt, "input"));
}
/**
* 测试{@link JsEvent#deleteElement(WebElement)}方法
*/
@Test
public void deleteElementTest() {
System.out.println(event.deleteElement(inputElemnt));
}
/**
* 测试{@link JsEvent#addElement(WebElement, com.alibaba.fastjson.JSONObject)}方法
*/
@Test
public void addElementTest_Json() {
JSONObject json = event.deleteElement(new CommonBy_Old(cb.getDriver()).getElement("//*[@value = '百度一下']"));
Element_Old e = new CommonBy_Old(cb.getDriver()).getElement("//*[text() = '我的关注']");
System.out.println(event.addElement(e, json));
}
}

View File

@ -37,16 +37,16 @@ public class TextEventTest {
final String ID_LIST_XPATH = "//*[@class=\"el-table__body-wrapper\"]/table/tbody/tr/td[1]/div/span";
ChromeBrower cb = new ChromeBrower(new File("Resource/BrowersDriver/Chrom/78.0394.70/chromedriver.exe"));
CommonBy ce;
DataListBy dle;
CommonBy_Old ce;
DataListBy_Old dle;
TextEvent t;
Element turningButton;
Element_Old turningButton;
@BeforeClass
public void init() {
cb.addConfig(ChromeOptionType.CONTRAL_OPEN_BROWER, "127.0.0.1:9222");
ce = new CommonBy(cb.getDriver());
dle = new DataListBy(cb.getDriver());
ce = new CommonBy_Old(cb.getDriver());
dle = new DataListBy_Old(cb.getDriver());
t = new TextEvent(cb.getDriver());
@ -64,7 +64,7 @@ public class TextEventTest {
}
/**
* 测试普通元素调用{@link TextEvent#getText(pres.auxiliary.work.selenium.element.Element)}方法
* 测试普通元素调用{@link TextEvent#getText(pres.auxiliary.work.selenium.element.Element_Old)}方法
* @throws InterruptedException
*/
@Test
@ -82,7 +82,7 @@ public class TextEventTest {
}
/**
* 测试列表元素调用{@link TextEvent#getText(pres.auxiliary.work.selenium.element.Element)}方法
* 测试列表元素调用{@link TextEvent#getText(pres.auxiliary.work.selenium.element.Element_Old)}方法
* @throws InterruptedException
*/
@Test

View File

@ -112,6 +112,7 @@ public class ExcelRecordTest {
.caseStep("测试第1步")
.caseExpect("测试第1预期")
.caseCondition("前置条件1")
.runId("1")
.runStep("实际第1步")
.runStep("实际第2步")
.runStep("实际第3步", "实际第4步")
@ -126,6 +127,7 @@ public class ExcelRecordTest {
.caseStep("测试第1步")
.caseExpect("测试第1预期")
.caseCondition("前置条件1")
.runId("2")
.runStep("实际第1步")
.runStep("实际第2步", "实际第3步")
.exception(new NullPointerException("此时抛出了NullPointerException异常"), imageFile)

File diff suppressed because one or more lines are too long