diff --git a/ConfigurationFiles/SeleniumConfigurationFile/LogConfiguration/ExcelRecordTemplet.xml b/ConfigurationFiles/SeleniumConfigurationFile/LogConfiguration/ExcelRecordTemplet.xml index 126fd90..414a707 100644 --- a/ConfigurationFiles/SeleniumConfigurationFile/LogConfiguration/ExcelRecordTemplet.xml +++ b/ConfigurationFiles/SeleniumConfigurationFile/LogConfiguration/ExcelRecordTemplet.xml @@ -32,9 +32,9 @@ - - - + + + diff --git a/src/main/java/pres/auxiliary/work/selenium/brower/Page.java b/src/main/java/pres/auxiliary/work/selenium/brower/Page.java index 6100446..ec6bc64 100644 --- a/src/main/java/pres/auxiliary/work/selenium/brower/Page.java +++ b/src/main/java/pres/auxiliary/work/selenium/brower/Page.java @@ -10,10 +10,10 @@ import com.alibaba.fastjson.JSONObject; *

文件名:Page.java

*

用途:用于存储对浏览器加载的页面信息以及页面操作

*

编码时间:2020年4月10日上午8:02:45

- *

修改时间:2020年4月10日上午8:02:45

+ *

修改时间:2020年10月12日下午8:02:45

* @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 diff --git a/src/main/java/pres/auxiliary/work/selenium/element/AbstractBy.java b/src/main/java/pres/auxiliary/work/selenium/element/AbstractBy.java index 4e266eb..0f21e3d 100644 --- a/src/main/java/pres/auxiliary/work/selenium/element/AbstractBy.java +++ b/src/main/java/pres/auxiliary/work/selenium/element/AbstractBy.java @@ -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; /** *

文件名:AbstractElement.java

@@ -33,63 +27,50 @@ import pres.auxiliary.work.selenium.xml.ReadXml; */ public abstract class AbstractBy { /** - * 用于存储浏览器的WebDriver对象 + * 存储通过信息获取到的页面元素对象 */ - WebDriver driver; + protected List elementList; + /** - * 用于指向存储控件定位方式的xml文件 + * 用于存储元素信息 */ - static ReadXml xml; + protected ElementData elementData; + /** + * 存储元素的定位方式读取类对象,由于多个子类之间需要互通,故此处标记为static + */ + protected static AbstractRead read; /** * 用于存储浏览器对象 */ - AbstractBrower brower; + protected AbstractBrower brower; /** - * 存储单个控件的等待时间 + * 存储当前定位的窗体层级,由于多个子类之间需要互通,故此处标记为static */ - private HashMap controlWaitTime = new HashMap(); - /** - * 用于存储当前浏览器窗口的Handles值 - */ - private String browserHandles; - /** - * 存储当前定位的窗体层级,由于多个子类之间需要相互通信,故此处标记为static - */ - private static ArrayList iframeNameList = new ArrayList<>(); + private static ArrayList 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); + } } /** - *

- * 通过传入在xml文件中的控件名称,到类中指向的xml文件中查找控件 - * 名称对应的定位方式,或直接传入xpath与css定位方式, - * 根据定位方式对相应的窗体进行定位。当传入的窗体为当前窗体的前层(父层)窗体时, - * 通过该方法将调用切换父层的方法,将窗体切换到父层上,例如:
- * 当前存在f1, f2, f3, f4四层窗体,则调用方法:
{@code - * switchFrame("f2"); - * }
- * 此时窗体将回到f2层,无需再从顶层开始向下切换。 - *

- *

- * 若传入该方法的名称存在于xml文件中,且该元素存在父窗体时,调用 - * 该方法会从xml文件中获取相应所有父窗体,并对相应的父窗体进行切换, - * 从而达到无须切换父窗体的目的,例如,存在以下xml文件片段:

{@code
-	 * ...
-	 * 
-	 * 	
-	 * 
-	 * ...
-	 * }
- * 当调用该方法:
{@code - * switchFrame("f3"); - * }
- * 时,则会先将窗体从f1开始切换,至窗体f2,最后再切换为窗体f3 - *

+ * 该方法用于根据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 nameList = new ArrayList(); - //判断传入的元素名称是否存在于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定位方式, - * 根据定位方式对相应的窗体进行定位。当传入的窗体为当前窗体的前层(父层)窗体时, - * 通过该方法将调用切换父层的方法,将窗体切换到父层上,例如:
- * 当前存在f1, f2, f3, f4四层窗体,则调用方法:
{@code
-	 * List nameList = new ArrayList();
-	 * nameList.add("f2");
-	 * switchFrame(nameList);
-	 * }
- * 此时窗体将回到f2层,无需再从顶层开始向下切换。
- * 注意: - *
    - *
  1. 窗体的切换按照从前向后的顺序进行切换,切换顺序不能相反
  2. - *
  3. 传入的参数若在xml文件中且存在父窗体,调用该方法也不会对窗体进行切换
  4. - *
- * - * @param elementInformationList 存储窗体的名称或xpath与css定位方式的List集合 - */ - void switchFrame(List frameNameList) { + protected void autoSwitchFrame(List 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 handles = driver.getWindowHandles(); - // 判断是否只存在一个窗体,若只存在一个,则直接返回当前浏览器的WebDriver对象 - if (handles.size() == 1) { - return; - } - - List 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(); - } - - /** - *

- * 用于根据传入的控件名称或定位方式,对控件在页面上定位,返回其WebElement对象。形参可以传入在xml文件中元素的名称, - * 亦可以传入页面元素的定位方式,但目前识别只支持xpath和css两种方式。 - * 该方法获取的是一组元素,可用于对列表元素事件中。 - *

- *

- * 元素识别判断方式按照以下步骤进行:
- * 1.先对xml文件进行扫描,若存在该元素对应的标签,则读取xml文件的定位方式,并识别有效的定位方式一一匹配,直到正确为止;
- * 2.若在xml文件中查找不到该元素,则按照xpath和css的规则进行匹配,直到判断出该元素的定位方式位置;
- * 3.若仍找不到元素,则抛出UnrecognizableLocationModeException - *

- * - * @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 getParentFrameName(String name) { - //存储获取到的父层窗体名称 - List nameList = new ArrayList(); - - //获取元素所在窗体的名称 - 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 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 recognitionElement(ElementData elementData) { + //获取元素的定位类型及定位内容 + ArrayList elementByTypeList = elementData.getByTypeList(); + ArrayList elementValueList = elementData.getValueList(); + + //获取两个列表长度的最小者 + int minLength = Math.min(elementByTypeList.size(), elementValueList.size()); + //循环,遍历所有的定位方式,使用根据定位方式,判断页面是否存在定位方式指向的元素 + for (int index = 0; index < minLength; index++) { + //根据当前元素信息,在页面获取元素 + List 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()); } /** - *

文件名:AbstractElement.java

+ * 根据传入的定位方式枚举,以及定位内容,在页面查找 + * 元素,返回查到的元素列表,若查不到元素,则返回空列表 + * @param byType {@link ByType}枚举类 + * @param value 元素定位内容 + * @param waitTime 元素查找超时时间 + * @return 页面查找到的{@link WebElement}类对象{@link List}集合 + */ + protected List 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(); + } + } + + /** + *

文件名:AbstractBy.java

*

用途: - * 存储获取元素时的信息 + * 用于存储通过元素信息查找到的元素内容 *

- *

编码时间:2020年5月9日上午7:57:24

- *

修改时间:2020年5月22日上午8:18:39

+ *

编码时间:2020年10月13日上午8:02:38

+ *

修改时间:2020年10月13日上午8:02:38

* @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 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 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; } - } } diff --git a/src/main/java/pres/auxiliary/work/selenium/element/AbstractElement.java b/src/main/java/pres/auxiliary/work/selenium/element/AbstractElement.java deleted file mode 100644 index 44d3f53..0000000 --- a/src/main/java/pres/auxiliary/work/selenium/element/AbstractElement.java +++ /dev/null @@ -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; - -/** - *

文件名:AbstractElement.java

- *

用途:

- *

对辅助化测试工具selenium的获取元素代码进行的二次封装,通过类中提供的方法以及配合相应存储元素的 - * xml文件,以更简便的方式对页面元素进行获取,减少编程时的代码量。 - *

- *

编码时间:2020年4月25日 下午4:18:37

- *

修改时间:2020年4月25日 下午4:18:37

- * @author 彭宇琦 - * @version Ver1.0 - * @since JDK 1.8 - */ -public abstract class AbstractElement { - /** - * 用于存储浏览器的WebDriver对象 - */ - WebDriver driver; - /** - * 用于存储元素信息,由于 - */ - static ElementData elementData; - - /** - * 用于存储浏览器对象 - */ - static AbstractBrower brower; - /** - * 存储单个控件的等待时间 - */ - private HashMap controlWaitTime = new HashMap(); - /** - * 用于存储当前浏览器窗口的Handles值 - */ - private String browserHandles; - /** - * 存储当前定位的窗体层级,由于多个子类之间需要相互通信,故此处标记为static - */ - private static ArrayList 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; - } - } - - /** - *

- * 通过传入在xml文件中的控件名称,到类中指向的xml文件中查找控件 - * 名称对应的定位方式,或直接传入xpath与css定位方式, - * 根据定位方式对相应的窗体进行定位。当传入的窗体为当前窗体的前层(父层)窗体时, - * 通过该方法将调用切换父层的方法,将窗体切换到父层上,例如:
- * 当前存在f1, f2, f3, f4四层窗体,则调用方法:
{@code - * switchFrame("f2"); - * }
- * 此时窗体将回到f2层,无需再从顶层开始向下切换。 - *

- *

- * 若传入该方法的名称存在于xml文件中,且该元素存在父窗体时,调用 - * 该方法会从xml文件中获取相应所有父窗体,并对相应的父窗体进行切换, - * 从而达到无须切换父窗体的目的,例如,存在以下xml文件片段:

{@code
-	 * ...
-	 * 
-	 * 	
-	 * 
-	 * ...
-	 * }
- * 当调用该方法:
{@code - * switchFrame("f3"); - * }
- * 时,则会先将窗体从f1开始切换,至窗体f2,最后再切换为窗体f3 - *

- * - * @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 nameList = new ArrayList(); - //判断传入的元素名称是否存在于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定位方式, - * 根据定位方式对相应的窗体进行定位。当传入的窗体为当前窗体的前层(父层)窗体时, - * 通过该方法将调用切换父层的方法,将窗体切换到父层上,例如:
- * 当前存在f1, f2, f3, f4四层窗体,则调用方法:
{@code
-	 * List nameList = new ArrayList();
-	 * nameList.add("f2");
-	 * switchFrame(nameList);
-	 * }
- * 此时窗体将回到f2层,无需再从顶层开始向下切换。
- * 注意: - *
    - *
  1. 窗体的切换按照从前向后的顺序进行切换,切换顺序不能相反
  2. - *
  3. 传入的参数若在xml文件中且存在父窗体,调用该方法也不会对窗体进行切换
  4. - *
- * - * @param elementInformationList 存储窗体的名称或xpath与css定位方式的List集合 - */ - void switchFrame(List 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 handles = driver.getWindowHandles(); - // 判断是否只存在一个窗体,若只存在一个,则直接返回当前浏览器的WebDriver对象 - if (handles.size() == 1) { - return; - } - - List 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(); - } - - /** - *

- * 用于根据传入的控件名称或定位方式,对控件在页面上定位,返回其WebElement对象。形参可以传入在xml文件中元素的名称, - * 亦可以传入页面元素的定位方式,但目前识别只支持xpath和css两种方式。 - * 该方法获取的是一组元素,可用于对列表元素事件中。 - *

- *

- * 元素识别判断方式按照以下步骤进行:
- * 1.先对xml文件进行扫描,若存在该元素对应的标签,则读取xml文件的定位方式,并识别有效的定位方式一一匹配,直到正确为止;
- * 2.若在xml文件中查找不到该元素,则按照xpath和css的规则进行匹配,直到判断出该元素的定位方式位置;
- * 3.若仍找不到元素,则抛出UnrecognizableLocationModeException - *

- * - * @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 getParentFrameName(String name) { - //存储获取到的父层窗体名称 - List nameList = new ArrayList(); - - //获取元素所在窗体的名称 - 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 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)); - } - - /** - *

文件名:AbstractElement.java

- *

用途: - * 存储获取元素时的信息 - *

- *

编码时间:2020年5月9日上午7:57:24

- *

修改时间:2020年5月22日上午8:18:39

- * @author 彭宇琦 - * @version Ver1.1 - * @since JDK 1.8 - * - */ - class ElementInformation { - /** - * 存储元素的名称或定位内容 - */ - public String name; - /** - * 存储元素的定位方式 - */ - public ByType byType; - /** - * 用于标记元素的类型 - */ - public ElementType elementType; - /** - * 存储外链词语 - */ - public ArrayList 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 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; - } - - } -} diff --git a/src/main/java/pres/auxiliary/work/selenium/element/CommonBy.java b/src/main/java/pres/auxiliary/work/selenium/element/CommonBy.java index 8d50afd..9f0edbf 100644 --- a/src/main/java/pres/auxiliary/work/selenium/element/CommonBy.java +++ b/src/main/java/pres/auxiliary/work/selenium/element/CommonBy.java @@ -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; /** - *

文件名:CommonElement.java

+ *

文件名:CommonBy.java

*

用途: - * 提供在辅助化测试中,对页面单一元素获取的方法。类中获取元素的方法兼容传入定位方式对 + * 提供在辅助化测试中,对页面单一元素获取的方法。类中获取元素的方法兼容传入定位方式对 * 元素进行查找,建议使用xml对页面元素的定位方式进行存储,以简化编码时的代码量,也便于 * 对代码的维护 *

- *

编码时间:2020年4月26日下午10:34:55

- *

修改时间:2020年4月26日下午10:34:55

+ *

编码时间:2020年10月13日上午9:44:01

+ *

修改时间:2020年10月13日上午9:44:01

* @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); } } diff --git a/src/main/java/pres/auxiliary/work/selenium/element/DataListBy.java b/src/main/java/pres/auxiliary/work/selenium/element/DataListBy.java index 651d64e..bf8d42a 100644 --- a/src/main/java/pres/auxiliary/work/selenium/element/DataListBy.java +++ b/src/main/java/pres/auxiliary/work/selenium/element/DataListBy.java @@ -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; /** - *

文件名:DataListElement.java

+ *

文件名:DataListBy.java

*

用途: - * 提供在辅助化测试中,对页面列表元素获取的方法,并对列表元素的获取做了优化。 - * 类中获取元素的方法兼容传入定位方式对元素进行查找,建议使用xml对页面元素的 - * 定位方式进行存储,以简化编码时的代码量,也便于对代码的维护。 *

- *

编码时间:2020年4月29日下午6:18:46

- *

修改时间:2020年4月29日下午6:18:46

+ *

编码时间:2020年10月14日下午6:45:51

+ *

修改时间:2020年10月14日下午6:45:51

* @author 彭宇琦 * @version Ver1.0 - * @since JDK 12 + * */ -public class DataListBy extends ListBy { +public class DataListBy extends MultiBy { /** - * 存储获取到的元素列表中最多元素列的元素个数 - */ - private int maxColumnSize = -1; - /** - * 存储获取到的元素列表中最多元素列的名称 - */ - private ArrayList maxColumnNameList = new ArrayList<>(); - /** - * 存储获取到的元素列表中最少元素列的元素个数 - */ - private int minColumnSize = Integer.MAX_VALUE; - /** - * 存储获取到的元素列表中最少元素列的名称 - */ - private ArrayList minColumnNameList = new ArrayList<>(); - - /** - * 用于判断列表的第一行元素是否为标题元素 - */ - boolean isFristRowTitle = false; - - /** - * 用于存储元素列累计的个数 - */ -// private HashMap elementSizeMap = new HashMap(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 getMaxColumnName() { - return maxColumnNameList; - } - - /** - * 返回元素最少列的元素个数 - * @return 元素最少列的元素个数 - */ - public int getMinColumnSize() { - return minColumnSize; - } - - /** - * 返回元素最少列的列名称 - * @return 元素最少列的列名称 - */ - public List 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 getElements(String name, int... indexs) { - // 存储所有获取到的事件 - ArrayList events = new ArrayList<>(); - - // 循环,解析所有的下标,并调用getEvent()方法,存储至events - for (int index : indexs) { - events.add(getElement(name, index)); - } - - return events; - } - - /** - * 用于返回列表中指定随机个数的元素 - * - * @param name 列名称 - * @param length 需要返回列表事件的个数 - * @return 列表事件组 - */ - public List 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 indexsList = new ArrayList(); - 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 getRandomElement(int length) { + //存储随机得到的数字 + Set 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 elementList = driver.findElements(by); - - //若获取的标题首行为标题行时,则判断为获取到大于1个元素时返回true,否则则大于0个元素返回true - if (isFristRowTitle) { - return elementList.size() > 1; - } else { - return elementList.size() > 0; - } - }); + return elementSet; } } diff --git a/src/main/java/pres/auxiliary/work/selenium/element/Element.java b/src/main/java/pres/auxiliary/work/selenium/element/Element.java deleted file mode 100644 index 6450ff2..0000000 --- a/src/main/java/pres/auxiliary/work/selenium/element/Element.java +++ /dev/null @@ -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; - -/** - *

文件名:Element.java

- *

用途: - * 用于返回和查找页面元素,以方便在元素过期时能进行重新获取 - *

- *

编码时间:2020年5月18日上午8:39:05

- *

修改时间:2020年5月18日上午8:39:05

- * @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); - } - } -} diff --git a/src/main/java/pres/auxiliary/work/selenium/element/ElementData.java b/src/main/java/pres/auxiliary/work/selenium/element/ElementData.java index 77139d4..b3f1f9d 100644 --- a/src/main/java/pres/auxiliary/work/selenium/element/ElementData.java +++ b/src/main/java/pres/auxiliary/work/selenium/element/ElementData.java @@ -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 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 getValueList() { + //若存储的外链词语不为空,则对需要外链的定位内容进行处理 + if (!linkWordList.isEmpty()) { + for (int i = 0; i < valueList.size(); i++) { + //判断字符串是否包含替换词语的开始标志,若不包含,则进行不进行替换操作 + if (!valueList.get(i).contains(AbstractRead.START_SIGN)) { + continue; + } + + //获取替换词语集合的迭代器 + Iterator 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; } - } diff --git a/src/main/java/pres/auxiliary/work/selenium/element/ListBy.java b/src/main/java/pres/auxiliary/work/selenium/element/ListBy.java deleted file mode 100644 index f93574d..0000000 --- a/src/main/java/pres/auxiliary/work/selenium/element/ListBy.java +++ /dev/null @@ -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; - -/** - *

文件名:ListElement.java

- *

用途: - * 提供获取列表类型元素时的基本方法 - *

- *

编码时间:2020年5月22日上午7:54:55

- *

修改时间:2020年5月22日上午7:54:55

- * @author 彭宇琦 - * @version Ver1.0 - * @since JDK 12 - * - */ -public abstract class ListBy extends MultiBy { - /** - * 用于存储获取到的列表一列元素,key为列表名称,value为列表元素 - */ - LinkedHashMap> 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 getNames() { - ArrayList 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 elementList = new ArrayList(); - //获取元素 - 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 clearColumn(String name, boolean isRemove) { - ElementInformation element = nameToElementInformation(name); - //若元素不存在,则直接返回null - if (element == null) { - return null; - } - - //用于存储被移除的元素 - List 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时,则表示随机获取一个元素,如:
- * {@code getWebElement("姓名", 1)}表示获取名称为“姓名”的列中的第1个元素
- * {@code getWebElement("姓名", 0)}表示获取名称为“姓名”的列中在长度范围内随机一个元素
- * {@code getWebElement("//*[@id='name']", -1)}表示获取“//*[@id='name']”对应列中的倒数第1个元素
- * - * @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 getAllElement(String name) { - //获取元素信息,并判断元素是否存在,不存在则抛出异常 - ElementInformation element = nameToElementInformation(name); - if (element == null) { - throw new NoSuchElementException("不存在的定位方式:" + name); - } - - return elementMap.get(element); - } -} diff --git a/src/main/java/pres/auxiliary/work/selenium/element/MultiBy.java b/src/main/java/pres/auxiliary/work/selenium/element/MultiBy.java index c88b602..5010118 100644 --- a/src/main/java/pres/auxiliary/work/selenium/element/MultiBy.java +++ b/src/main/java/pres/auxiliary/work/selenium/element/MultiBy.java @@ -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; /** - *

文件名:MultiElement.java

- *

用途: - * 提供获取多个元素时使用的基本方法 + *

+ * 文件名:ListBy.java *

- *

编码时间:2020年5月22日上午7:54:28

- *

修改时间:2020年5月22日上午7:54:28

- * @author 彭宇琦 + *

+ * 用途: 提供对多元素获取与返回的基本方法 + *

+ *

+ * 编码时间:2020年10月14日下午6:54:46 + *

+ *

+ * 修改时间:2020年10月14日下午6:54:46 + *

+ * + * @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}对象 + * 用于设置当前元素集合中首元素是否为允许随机返回的元素
+ * 例如:获取列表元素时,首元素为标题元素;获取下拉选项元素集合时, 第一个元素为空选项或者为类似“请选择”等选项 * - * @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; + } + /** - * 该方法用于根据存入的元素名称或定位方式,对元素进行重新获取的操作。主要用于当列表数据翻页后, - * 其原存入的数据将会失效,必须重新获取。注意,调用该方法后会清空原存储的数据。 + *

+ * 用于获取元素集合中指定下标的元素,该下标允许反向遍历与随机返回,其下标所传入的数字即为元素所在的真实下标。 + *

+ *

+ * 例如: + *

    + *
  • 传入1时,表示获取元素集合中的第1个元素
  • + *
  • 传入0时,表示获取元素集合中在长度范围内随机一个元素
  • + *
  • 传入-1时,表示获取元素集合中的倒数第1个元素
  • + *
+ *

+ *

+ * 注意:调用该方法前,若未调用{@link #find(String, String...)}方法对元素进行查找, + * 或者查无元素时,调用该方法不会抛出异常,但当调用{@link Element#getWebElement()}方法时, + * 由于未查到元素则会抛出异常 + *

+ * + * @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 clearColumn(String name, boolean isRemove); - + public List getAllElement() { + //定义返回元素的集合 + List 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); } } } diff --git a/src/main/java/pres/auxiliary/work/selenium/element/SelectBy.java b/src/main/java/pres/auxiliary/work/selenium/element/SelectBy.java index 40e0817..660b0a2 100644 --- a/src/main/java/pres/auxiliary/work/selenium/element/SelectBy.java +++ b/src/main/java/pres/auxiliary/work/selenium/element/SelectBy.java @@ -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; -/** - *

文件名:SelectBy.java

- *

用途: - * 提供获取下拉框中选项元素的方法,支持标准型下拉框选项(由select与option标签组成的下拉框)以及 - * 非标准型下拉框选项(由普通的div、li等元素组成的选项),并支持根据关键词查找选项。需要注意的是, - * 标准下拉选项和非标准下拉选项需要传入的参数不同,例如,
- * 标准下拉框:
- * <select id='test'>
- *  <option>男</option>
- *  <option>女</option>
- *  <option>其他</option>
- * </select>
- * 对于该标准的下拉框选项,只需要定位到//select[@id='test'],得到其WebElement对象即可,但对于非标准的下拉框其下拉框是由input和button标签构成:
- * <div>
- *  <span><input/></span>
- *  <span><button/></span>
- * </div>
- * 点击button对应的按钮后,其下也能弹出选项,但其选项是由div标签写入text构成:
- * <div id='test'>
- *  <div>男</div>
- *  <div>女</div>
- *  <div>其他</div>
- * </div>
- * 对于这种非标准的下拉框选项,需要传入选项所在的所有div标签对应的WebElement元素,在上例,则需要定位到//div[@id='test']/div, - * 注意,末尾的div不指定数字,则可以代表整个选项。
- *

- *

编码时间:2020年5月24日下午3:30:00

- *

修改时间:2020年5月24日下午3:30:00

- * @author 彭宇琦 - * @version Ver1.0 - * @since JDK 12 - * - */ public class SelectBy extends MultiBy { /** - * 用于存储获取下拉选项时的信息 + * 定义标准下拉选项的tagname */ - ElementInformation elementInfo; - /** - * 用于存储下拉选项的元素 - */ - ArrayList option = new ArrayList<>(); - /** - * 用于存储下拉选项的文本 - */ - ArrayList optionText = new ArrayList<>(); + public static final String SELECT_TAGNAME = "select"; /** - * 设置标记的下拉选项的类型 + * 用于存储当前元素列表中所有选项的文本内容 */ - private ElementType elementType; + private List 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}对象 + *

+ * 用于设置获取文本时读取的属性名称,即在调用{@link #getElement(String)}方法时,其元素内容的来源。 + *

+ *

+ * 该方法存在设置一个特殊的属性值“text”(默认),表示获取元素的文本节点内容,其他的值均为 + * 设置读取的属性值,建议设置的属性值的内容不存在重复,否则获取到的元素可能与预期不符。 + *

+ *

+ * 注意:调用该方法时将自动获取一次元素内容,若未调用{@link #find(String, String...)}方法查找 + * 元素或查找的元素不存在时,调用该方法仅设置属性值。设置的属性名称不区分大小写 + *

* - * @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 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异常; - * 当查询到有多个包含关键词的选项时,则选择第一个选项
- * 注意,当传入多个关键词时其选项需要全部满足才会返回相应的选项。 - * - * @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 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()); } } diff --git a/src/main/java/pres/auxiliary/work/selenium/element/delect/AbstractBy.java b/src/main/java/pres/auxiliary/work/selenium/element/delect/AbstractBy.java deleted file mode 100644 index 89e1b6b..0000000 --- a/src/main/java/pres/auxiliary/work/selenium/element/delect/AbstractBy.java +++ /dev/null @@ -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; - -/** - *

文件名:AbstractElement.java

- *

用途:

- *

- *   对辅助化测试工具selenium的获取元素代码进行的二次封装,通过类中提供的方法以及配合相应存储元素的
- * xml文件,以更简便的方式对页面元素进行获取,减少编程时的代码量。
- * 

- *

编码时间:2020年4月25日 下午4:18:37

- *

修改时间:2020年4月25日 下午4:18:37

- * @author 彭宇琦 - * @version Ver1.0 - * @since JDK 12 - */ -public abstract class AbstractBy { - /** - * 用于指向存储控件定位方式的xml文件,设置属性为静态,用于在编写脚本时不需要频繁切换xml文件 - */ - static ReadXml xml; - - /** - * 用于存储浏览器对象 - */ - AbstractBrower brower; - /** - * 存储单个控件的等待时间 - */ - private HashMap controlWaitTime = new HashMap(); - - /** - * 用于存储元素通用的等待时间,默认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(); - } - } - - /** - *

- * 用于根据传入的控件名称或定位方式,对控件在页面上定位,返回其WebElement对象。形参可以传入在xml文件中元素的名称, - * 亦可以传入页面元素的定位方式,但目前识别只支持xpath和css两种方式。 - * 该方法获取的是一组元素,可用于对列表元素事件中。 - *

- *

- * 元素识别判断方式按照以下步骤进行:
- * 1.先对xml文件进行扫描,若存在该元素对应的标签,则读取xml文件的定位方式,并识别有效的定位方式一一匹配,直到正确为止;
- * 2.若在xml文件中查找不到该元素,则按照xpath和css的规则进行匹配,直到判断出该元素的定位方式位置;
- * 3.若仍找不到元素,则抛出UnrecognizableLocationModeException - *

- * - * @param name 元素名称或元素的定位方式 - * @return 返回页面一组元素的定位方式对象 - * @throws TimeoutException 元素在指定时间内未查找到时,抛出的异常 - * @throws UnrecognizableLocationModeException 元素无法识别时抛出的异常 - */ - List 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 recognitionCommonElement(ElementInformation elementInformation) { - List 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 recognitionXmlElement(ElementInformation elementInformation) { - //查找元素的父层级结构 - elementInformation.iframeList = getParentFrameName(elementInformation.name); - - //存储元素的By对象集合 - List 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 getParentFrameName(String name) { - //存储获取到的父层窗体名称 - List nameList = new ArrayList(); - - //获取元素所在窗体的名称 - String iframeName = xml.getIframeName(name); - //循环,判断窗体是否存在(方法返回不为空),若存在,则再将父窗体进行存储 - while(!iframeName.isEmpty()) { - //存储窗体 - nameList.add(iframeName); - //再以当前窗体的名称再次获取该窗体的父窗体 - iframeName = xml.getIframeName(iframeName); - } - - return nameList; - } - - /** - * 用于根据传入的参数,识别非xml文件内的元素定位方式。 - * 该方法能快速识别xpath定位方式以及绝对css定位方式,若不是以上两种定位方式 - * 则会遍历所有的定位方式,此时会降低运行速度,建议在不是以上两种定位方式的 - * 情况下,直接指定元素的定位方式,以提高效率 - */ - List getCommonElementBy(String value) { - List 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 getXmlElementBy(String name, List linkList) { - //用于存储从xml文件中读取到的所有内容,并封装成By对象 - List 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 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()); - } - - /** - *

文件名:AbstractElement.java

- *

用途: - * 存储获取元素时的信息 - *

- *

编码时间:2020年5月9日上午7:57:24

- *

修改时间:2020年5月22日上午8:18:39

- * @author 彭宇琦 - * @version Ver1.1 - * @since JDK 1.8 - * - */ - class ElementInformation { - /** - * 存储元素的名称或定位内容 - */ - public String name; - /** - * 存储元素的定位方式 - */ - public ByType byType; - /** - * 用于标记元素的类型 - */ - public ElementType elementType; - /** - * 用于存储在xml文件中需要外链的词语 - */ - public List linkKeyList; - /** - * 用于存储元素的父层结构 - */ - public List iframeList; - /** - * 构造元素信息 - * @param name 元素名称 - * @param byType 元素定位的By类型,枚举 - * @param elementType 元素类型,枚举 - * @param linkKeyList xml文件中需要外部替换的词语 - */ - public ElementInformation(String name, ByType byType, ElementType elementType, List linkKeyList) { - super(); - this.name = name; - this.byType = byType; - this.elementType = elementType; - this.linkKeyList = linkKeyList; - } - - /** - * 用于判断其元素的使用的外链xml文件词语是否与存储的内容一致 - * @param linkKey 需要判断的外链xml文件词语 - * @return 是否与类中存储的词语一致 - */ - public boolean linkKeyEquals(List 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; - } - - } -} diff --git a/src/main/java/pres/auxiliary/work/selenium/element/delect/CommonBy.java b/src/main/java/pres/auxiliary/work/selenium/element/delect/CommonBy.java deleted file mode 100644 index 534486c..0000000 --- a/src/main/java/pres/auxiliary/work/selenium/element/delect/CommonBy.java +++ /dev/null @@ -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; - -/** - *

文件名:CommonElement.java

- *

用途: - * 提供在辅助化测试中,对页面单一元素获取的方法。类中获取元素的方法兼容传入定位方式对 - * 元素进行查找,建议使用xml对页面元素的定位方式进行存储,以简化编码时的代码量,也便于 - * 对代码的维护 - *

- *

编码时间:2020年4月26日下午10:34:55

- *

修改时间:2020年4月26日下午10:34:55

- * @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; - } -} diff --git a/src/main/java/pres/auxiliary/work/selenium/element/delect/DataListBy.java b/src/main/java/pres/auxiliary/work/selenium/element/delect/DataListBy.java deleted file mode 100644 index 3fcea33..0000000 --- a/src/main/java/pres/auxiliary/work/selenium/element/delect/DataListBy.java +++ /dev/null @@ -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; - -/** - *

文件名:DataListElement.java

- *

用途: - * 提供在辅助化测试中,对页面列表元素获取的方法,并对列表元素的获取做了优化。 - * 类中获取元素的方法兼容传入定位方式对元素进行查找,建议使用xml对页面元素的 - * 定位方式进行存储,以简化编码时的代码量,也便于对代码的维护。 - *

- *

编码时间:2020年4月29日下午6:18:46

- *

修改时间:2020年4月29日下午6:18:46

- * @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 getMaxColumnName() { -// return maxColumnNameList; -// } - - /** - * 返回元素最少列的元素个数 - * @return 元素最少列的元素个数 - */ -// public int getMinColumnSize() { -// return minColumnSize; -// } - - /** - * 返回元素最少列的列名称 - * @return 元素最少列的列名称 - */ -// public List getMinColumnName() { -// return minColumnNameList; -// } - - /** - * 用于返回列表多个指定的元素,传入的下标可参见{@link #getWebElement(String, int)} - * - * @param name 列名称 - * @param indexs 一组元素下标(即列表中对应的某一个元素) - * @return 对应列多个指定下标的元素 - * @throws NoSuchElementException 当未对name列进行获取数据或index的绝对值大于列表最大值时抛出的异常 - * @see #getWebElement(String, int) - */ -// public List getElements(String name, int... indexs) { -// // 存储所有获取到的事件 -// ArrayList 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 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 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时,则表示随机获取一个元素,如:
- * {@code getWebElement("姓名", 1)}表示获取名称为“姓名”的列中的第1个元素
- * {@code getWebElement("姓名", 0)}表示获取名称为“姓名”的列中在长度范围内随机一个元素
- * {@code getWebElement("//*[@id='name']", -1)}表示获取“//*[@id='name']”对应列中的倒数第1个元素
- * 注意,若使用了外链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; - } -} diff --git a/src/main/java/pres/auxiliary/work/selenium/element/delect/Element.java b/src/main/java/pres/auxiliary/work/selenium/element/delect/Element.java deleted file mode 100644 index fab148d..0000000 --- a/src/main/java/pres/auxiliary/work/selenium/element/delect/Element.java +++ /dev/null @@ -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; - -/** - *

文件名:Element.java

- *

用途: - * 用于返回和查找页面元素,以方便在元素过期时能进行重新获取 - *

- *

编码时间:2020年5月18日上午8:39:05

- *

修改时间:2020年5月18日上午8:39:05

- * @author 彭宇琦 - * @version Ver1.0 - * @since JDK 8 - * - */ -public class Element implements Cloneable { - /** - * 用于对文本进行分隔 - */ - private final String SPILT_SIGN = ","; - - /** - * 存储元素 - */ - private WebElement element = null; - /** - * 存储查找元素的By对象 - */ - private List byList; - /** - * 用于存储窗体元素 - */ - private Element iframeElement = null; - /** - * 用于存储当前已指向的窗体 - */ - private static ArrayList 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 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 getAllIframe() { - //存储当前元素的所在的所有窗体 - ArrayList 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 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 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 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 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 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; - } - } -} diff --git a/src/main/java/pres/auxiliary/work/selenium/element/delect/ElementType.java b/src/main/java/pres/auxiliary/work/selenium/element/delect/ElementType.java deleted file mode 100644 index 2420cbd..0000000 --- a/src/main/java/pres/auxiliary/work/selenium/element/delect/ElementType.java +++ /dev/null @@ -1,28 +0,0 @@ -package pres.auxiliary.work.selenium.element.delect; - -/** - *

文件名:EelementType.java

- *

用途: - * 用于标记当前传入的元素是以何种方式进行获取 - *

- *

编码时间:2020年5月22日上午7:57:32

- *

修改时间:2020年5月22日上午7:57:32

- * @author - * @version Ver1.0 - * @since JDK 12 - * - */ -public enum ElementType { - /** - * 指向普通类型元素 - */ - COMMON_ELEMENT, - /** - * 指向数据列表类型元素 - */ - DATA_LIST_ELEMENT, - /** - * 指向下拉框选择类型元素 - */ - SELECT_ELEMENT, -} diff --git a/src/main/java/pres/auxiliary/work/selenium/element/delect/ListBy.java b/src/main/java/pres/auxiliary/work/selenium/element/delect/ListBy.java deleted file mode 100644 index b140ba3..0000000 --- a/src/main/java/pres/auxiliary/work/selenium/element/delect/ListBy.java +++ /dev/null @@ -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; - -/** - *

文件名:ListElement.java

- *

用途: - * 提供获取列表类型元素时的基本方法 - *

- *

编码时间:2020年5月22日上午7:54:55

- *

修改时间:2020年5月22日上午7:54:55

- * @author 彭宇琦 - * @version Ver1.0 - * @since JDK 12 - * - */ -public abstract class ListBy extends AbstractBy { - /** - * 用于存储获取到的列表一列元素,key为列表名称,value为列表元素 - */ - LinkedHashMap elementMap = new LinkedHashMap<>(16); - - /** - * 控制元素首行是否为 - */ - private boolean fristIsEmpty = false; - - /** - * 通过浏览器对象{@link AbstractBrower}进行构造 - * @param brower {@link AbstractBrower}对象 - */ - public ListBy(AbstractBrower brower) { - super(brower); - } - - /** - * 该方法用于返回所有列的名称 - * @return 所有列的名称 - */ - public ArrayList getNames() { - ArrayList 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 linkKey) { - //遍历elementMap,若查找与name一致的名称,则结束循环并返回相应的ElementInformation对象 - for (ElementInformation elementInfo : elementMap.keySet()) { - if (elementInfo.name.equals(name) && elementInfo.linkKeyEquals(linkKey)) { - return elementInfo; - } - } - - return null; - } -} diff --git a/src/main/java/pres/auxiliary/work/selenium/element/delect/SelectBy.java b/src/main/java/pres/auxiliary/work/selenium/element/delect/SelectBy.java deleted file mode 100644 index b9d1e44..0000000 --- a/src/main/java/pres/auxiliary/work/selenium/element/delect/SelectBy.java +++ /dev/null @@ -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; - -/** - *

文件名:SelectBy.java

- *

用途: - * 提供获取下拉框中选项元素的方法,支持标准型下拉框选项(由select与option标签组成的下拉框)以及 - * 非标准型下拉框选项(由普通的div、li等元素组成的选项),并支持根据关键词查找选项。需要注意的是, - * 标准下拉选项和非标准下拉选项需要传入的参数不同,例如,
- * 标准下拉框:
- * <select id='test'>
- *  <option>男</option>
- *  <option>女</option>
- *  <option>其他</option>
- * </select>
- * 对于该标准的下拉框选项,只需要定位到//select[@id='test'],得到其WebElement对象即可,但对于非标准的下拉框其下拉框是由input和button标签构成:
- * <div>
- *  <span><input/></span>
- *  <span><button/></span>
- * </div>
- * 点击button对应的按钮后,其下也能弹出选项,但其选项是由div标签写入text构成:
- * <div id='test'>
- *  <div>男</div>
- *  <div>女</div>
- *  <div>其他</div>
- * </div>
- * 对于这种非标准的下拉框选项,需要传入选项所在的所有div标签对应的WebElement元素,在上例,则需要定位到//div[@id='test']/div, - * 注意,末尾的div不指定数字,则可以代表整个选项。
- *

- *

编码时间:2020年5月24日下午3:30:00

- *

修改时间:2020年5月24日下午3:30:00

- * @author 彭宇琦 - * @version Ver1.0 - * @since JDK 12 - * - */ -public class SelectBy extends ListBy { - /** - * 用于指向存储的元素名称 - */ - private String name; - /** - * 用于指向存储的外链词语 - */ - private List 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时,则表示随机获取一个元素,如:
- * {@code getWebElement(1)}表示获取第1个选项
- * {@code getWebElement(0)}表示获取随机一个选项
- * {@code getWebElement(-1)}表示获取倒数第1个选项
- * - * @param index 元素下标(即列表中对应的某一个元素) - * @return 对应的选项 - * @throws NoSuchElementException 当未对name列进行获取数据或index的绝对值大于列表最大值时抛出的异常 - */ - public Element getElement(int index) { - Element element = findElement(); - element.setElementIndex(index); - return element; - } - - /** - * 根据关键词组查找选项,并返回选项元素,当传入的元素名称不存在时,则抛出NoSuchElementException异常; - * 当查询到有多个包含关键词的选项时,则选择第一个选项
- * 注意,当传入多个关键词时其选项需要全部满足才会返回相应的选项。 - * - * @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; - } -} diff --git a/src/main/java/pres/auxiliary/work/selenium/element/delect/UnrecognizableLocationModeException.java b/src/main/java/pres/auxiliary/work/selenium/element/delect/UnrecognizableLocationModeException.java deleted file mode 100644 index 2c9f86c..0000000 --- a/src/main/java/pres/auxiliary/work/selenium/element/delect/UnrecognizableLocationModeException.java +++ /dev/null @@ -1,38 +0,0 @@ -package pres.auxiliary.work.selenium.element.delect; - -/** - *

文件名:UnrecognizableLocationModeException.java

- *

用途:在元素定位方式无法被识别的情况下,抛出的异常

- *

编码时间:2019年9月24日下午3:19:43

- *

修改时间:2019年9月24日下午3:19:43

- * @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); - } - -} diff --git a/src/main/java/pres/auxiliary/work/selenium/event/ClickEvent.java b/src/main/java/pres/auxiliary/work/selenium/event/ClickEvent.java index 5526b6e..0803a7b 100644 --- a/src/main/java/pres/auxiliary/work/selenium/event/ClickEvent.java +++ b/src/main/java/pres/auxiliary/work/selenium/event/ClickEvent.java @@ -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; - -/** - *

文件名:ClickEvent.java

- *

用途: - * 定义了对控件进行点击操作相关的方法,可通过该类,对页面进行基本的点击操作 - *

- *

编码时间:2019年8月29日下午3:24:34

- *

修改时间:2020年7月10日上午16:49:37

- * - * @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; + +/** + *

文件名:ClickEvent.java

+ *

用途: + * 定义了对控件进行点击操作相关的方法,可通过该类,对页面进行基本的点击操作 + *

+ *

编码时间:2019年8月29日下午3:24:34

+ *

修改时间:2020年7月10日上午16:49:37

+ * + * @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; + } + } + } +} diff --git a/src/main/java/pres/auxiliary/work/selenium/event/EventAction.java b/src/main/java/pres/auxiliary/work/selenium/event/EventAction.java index 9c8a968..fa6291d 100644 --- a/src/main/java/pres/auxiliary/work/selenium/event/EventAction.java +++ b/src/main/java/pres/auxiliary/work/selenium/event/EventAction.java @@ -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; /** *

文件名:EventAction.java

*

用途: * 用于执行事件增强的方法,方法中可通过{@link EventInformation}类对象,回调事件的操作的步骤 - * 以及传入到事件中的{@link Element}类对象 + * 以及传入到事件中的{@link Element_Old}类对象 *

*

编码时间:2020年7月10日下午3:40:13

*

修改时间:2020年7月10日下午3:40:13

@@ -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); diff --git a/src/main/java/pres/auxiliary/work/selenium/event/EventInformation.java b/src/main/java/pres/auxiliary/work/selenium/event/EventInformation.java index 56f2c48..035c955 100644 --- a/src/main/java/pres/auxiliary/work/selenium/event/EventInformation.java +++ b/src/main/java/pres/auxiliary/work/selenium/event/EventInformation.java @@ -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; /** *

文件名:EventInformation.java

@@ -29,7 +29,7 @@ public class EventInformation { /** * 存储传入到方法中的元素类对象,由args进行分离 */ - private ArrayList elementList = new ArrayList<>(); + private ArrayList elementList = new ArrayList<>(); /** * 构造对象 @@ -61,10 +61,10 @@ public class EventInformation { } /** - * 用于返回传入到方法中{@link Element}类对象集合 - * @return {@link Element}类对象集合 + * 用于返回传入到方法中{@link Element_Old}类对象集合 + * @return {@link Element_Old}类对象集合 */ - public ArrayList getElement() { + public ArrayList 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); } }); } diff --git a/src/main/java/pres/auxiliary/work/selenium/event/EventProxy.java b/src/main/java/pres/auxiliary/work/selenium/event/EventProxy.java index d097dca..6d513cf 100644 --- a/src/main/java/pres/auxiliary/work/selenium/event/EventProxy.java +++ b/src/main/java/pres/auxiliary/work/selenium/event/EventProxy.java @@ -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; /** *

文件名:EventProxy.java

@@ -205,9 +205,9 @@ public class EventProxy implements MethodInterceptor { * @param actionMap 需要执行的通知 */ private void runElementAction(EventInformation eventInformation, LinkedHashMap> 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); diff --git a/src/main/java/pres/auxiliary/work/selenium/event/EventWait.java b/src/main/java/pres/auxiliary/work/selenium/event/EventWait.java index 33c76a2..c1c9f96 100644 --- a/src/main/java/pres/auxiliary/work/selenium/event/EventWait.java +++ b/src/main/java/pres/auxiliary/work/selenium/event/EventWait.java @@ -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(); }); diff --git a/src/main/java/pres/auxiliary/work/selenium/event/JsEvent.java b/src/main/java/pres/auxiliary/work/selenium/event/JsEvent.java index 78fc9d0..0b3aca6 100644 --- a/src/main/java/pres/auxiliary/work/selenium/event/JsEvent.java +++ b/src/main/java/pres/auxiliary/work/selenium/event/JsEvent.java @@ -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; - -/** - *

- * 文件名:JsEvent.java - *

- *

- * 用途:用提供通过Javascript来修改或者获取页面元素内容的方法 - *

- *

- * 编码时间:2018年12月2日 下午12:51:19 - *

- *

- * 修改时间:2020年5月17日 下午5:21:44 - *

- * - * @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 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的格式返回被删除的元素信息,其形式为: - * - *
-	 * {
-	 *     "tagname":元素标签名称
-	 *     "attributes":[
-	 *         {
-	 *             "name":属性1名称
-	 *             "value":属性1值
-	 *         }, 
-	 *         {
-	 *             "name":属性1名称
-	 *             "value":属性1值
-	 *         },
-	 *         ...
-	 *     ]
-	 * }
-	 * 
- * - * @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的形式返回。格式为: - * - *
-	 * {
-	 *     "tagname":元素标签名称
-	 *     "attributes":[
-	 *         {
-	 *             "name":属性1名称
-	 *             "value":属性1值
-	 *         }, 
-	 *         {
-	 *             "name":属性1名称
-	 *             "value":属性1值
-	 *         },
-	 *         ...
-	 *     ]
-	 * }
-	 * 
- * - * @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) 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; + +/** + *

+ * 文件名:JsEvent.java + *

+ *

+ * 用途:用提供通过Javascript来修改或者获取页面元素内容的方法 + *

+ *

+ * 编码时间:2018年12月2日 下午12:51:19 + *

+ *

+ * 修改时间:2020年5月17日 下午5:21:44 + *

+ * + * @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 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的格式返回被删除的元素信息,其形式为: + * + *
+	 * {
+	 *     "tagname":元素标签名称
+	 *     "attributes":[
+	 *         {
+	 *             "name":属性1名称
+	 *             "value":属性1值
+	 *         }, 
+	 *         {
+	 *             "name":属性1名称
+	 *             "value":属性1值
+	 *         },
+	 *         ...
+	 *     ]
+	 * }
+	 * 
+ * + * @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的形式返回。格式为: + * + *
+	 * {
+	 *     "tagname":元素标签名称
+	 *     "attributes":[
+	 *         {
+	 *             "name":属性1名称
+	 *             "value":属性1值
+	 *         }, 
+	 *         {
+	 *             "name":属性1名称
+	 *             "value":属性1值
+	 *         },
+	 *         ...
+	 *     ]
+	 * }
+	 * 
+ * + * @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) 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; + } +} diff --git a/src/main/java/pres/auxiliary/work/selenium/event/TextEvent.java b/src/main/java/pres/auxiliary/work/selenium/event/TextEvent.java index 9827db3..fee6bfe 100644 --- a/src/main/java/pres/auxiliary/work/selenium/event/TextEvent.java +++ b/src/main/java/pres/auxiliary/work/selenium/event/TextEvent.java @@ -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; - -/** - *

文件名:TextEvent.java

- *

用途:定义了对控件文本操作相关的方法,可通过该类,对页面进行对控件输入,文本获取等操作

- *

编码时间:2019年9月6日上午9:28:59

- *

修改时间:2020年7月10日上午16:49:37

- * @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); - } - - /** - * 该方法用于将一个指定的整数随机填写到传入的控件组中
- * 建议在指定值远远大于控件的数量时再使用该方法,否则将会出现不可预期的问题 - * - * @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)); - } - - /** - * 用于向控件中上传指定的文件。
- * @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; + +/** + *

文件名:TextEvent.java

+ *

用途:定义了对控件文本操作相关的方法,可通过该类,对页面进行对控件输入,文本获取等操作

+ *

编码时间:2019年9月6日上午9:28:59

+ *

修改时间:2020年7月10日上午16:49:37

+ * @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); + } + + /** + * 该方法用于将一个指定的整数随机填写到传入的控件组中
+ * 建议在指定值远远大于控件的数量时再使用该方法,否则将会出现不可预期的问题 + * + * @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)); + } + + /** + * 用于向控件中上传指定的文件。
+ * @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); + } +} diff --git a/src/main/java/pres/auxiliary/work/selenium/location/AbstractRead.java b/src/main/java/pres/auxiliary/work/selenium/location/AbstractRead.java index 858dada..4cb45b5 100644 --- a/src/main/java/pres/auxiliary/work/selenium/location/AbstractRead.java +++ b/src/main/java/pres/auxiliary/work/selenium/location/AbstractRead.java @@ -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}枚举)集合 diff --git a/src/main/java/pres/auxiliary/work/selenium/location/ReadXml.java b/src/main/java/pres/auxiliary/work/selenium/location/ReadXml.java index 1d89cc0..2338310 100644 --- a/src/main/java/pres/auxiliary/work/selenium/location/ReadXml.java +++ b/src/main/java/pres/auxiliary/work/selenium/location/ReadXml.java @@ -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文件对象 diff --git a/src/main/java/pres/auxiliary/work/selenium/tool/ExcelRecord.java b/src/main/java/pres/auxiliary/work/selenium/tool/ExcelRecord.java index 96656c1..cb85298 100644 --- a/src/main/java/pres/auxiliary/work/selenium/tool/ExcelRecord.java +++ b/src/main/java/pres/auxiliary/work/selenium/tool/ExcelRecord.java @@ -189,9 +189,9 @@ public class ExcelRecord extends AbstractWriteExcel { 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()); diff --git a/src/test/java/pres/auxiliary/work/selenium/element/ElementDataTest.java b/src/test/java/pres/auxiliary/work/selenium/element/ElementDataTest.java new file mode 100644 index 0000000..11e1e09 --- /dev/null +++ b/src/test/java/pres/auxiliary/work/selenium/element/ElementDataTest.java @@ -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; + +/** + *

文件名:ElementDataTest.java

+ *

用途: + * 对{@link ElementData}类进行单元测试 + *

+ *

编码时间:2020年10月12日上午8:47:14

+ *

修改时间:2020年10月12日上午8:47:14

+ * @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()}方法
+ * 预期:
+ * XX控件6 + */ + @Test + public void getNameTest() { + System.out.println(test.getName()); + } + + /** + * 用于测试{@link ElementData#getByTypeList()}方法
+ * 预期:
+ * XPATH
+ * CSS
+ * XPATH + */ + @Test + public void getByTypeListTest() { + test.getByTypeList().forEach(System.out :: println); + } + + /** + * 用于测试{@link ElementData#getValueList()}方法
+ * 预期:
+ * //XXX控件6[@X='XXXX']
+ * http body ${tagName}
+ * //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()}方法
+ * 预期:
+ * COMMON_ELEMENT + */ + @Test + public void getElementTypeTest() { + System.out.println(test.getElementType()); + } + + /** + * 用于测试{@link ElementData#getIframeNameList()}方法
+ * 预期:
+ * 窗体1
+ * 窗体1.1
+ * 窗体1.1.1 + */ + @Test + public void getIframeNameListTest() { + test.getIframeNameList().forEach(System.out :: println); + } + + /** + * 用于测试{@link ElementData#getWaitTime()}方法
+ * 预期:
+ * -1 + */ + @Test + public void getWaitTimeTest() { + System.out.println(test.getWaitTime()); + } + + /** + * 用于测试{@link ElementData#addLinkWord(String...)}方法
+ * 预期:
+ * //XXX控件6[@X='XXXX']
+ * http body 外链1
+ * //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); + } +} diff --git a/src/test/java/pres/auxiliary/work/selenium/element/SelectByTest.java b/src/test/java/pres/auxiliary/work/selenium/element/SelectByTest.java index 95a6788..4240b2a 100644 --- a/src/test/java/pres/auxiliary/work/selenium/element/SelectByTest.java +++ b/src/test/java/pres/auxiliary/work/selenium/element/SelectByTest.java @@ -16,7 +16,7 @@ import pres.auxiliary.work.selenium.event.ClickEvent; /** *

文件名:SelectByText.java

*

用途: - * 对{@link SelectBy}类进行测试 + * 对{@link SelectBy_Old}类进行测试 *

*

页面: * 对标准型下拉选项测试页面为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"); diff --git a/src/test/java/pres/auxiliary/work/selenium/element/测试文件.xml b/src/test/java/pres/auxiliary/work/selenium/element/测试文件.xml deleted file mode 100644 index 46bd990..0000000 --- a/src/test/java/pres/auxiliary/work/selenium/element/测试文件.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - //iframe[contains(@src,'${src}')] - //lable[contains(text(), '${condition}')]/../span/input[1] - - - - - \ No newline at end of file diff --git a/src/test/java/pres/auxiliary/work/selenium/element/测试用xml文件.xml b/src/test/java/pres/auxiliary/work/selenium/element/测试用xml文件.xml new file mode 100644 index 0000000..81d3732 --- /dev/null +++ b/src/test/java/pres/auxiliary/work/selenium/element/测试用xml文件.xml @@ -0,0 +1,110 @@ + + + + //XXX模板控件1[@X='${name}']/div/div[@${att}='${id}']/input + http body ${tagName} + //XXX模板控件1[@X='${src}']/div[@name='${name}'] + //XXX模板控件1[@X='${src}']/div[@name='${name}']/div[@is='${str1}' and text()='${str1}'] + //XXX模板控件1[@X='${src}']/div[@name='${name}']/div[@is='${str1}' and text()='${src}']/span[text()='${str2}']/span[id='${aaaa}'] + ${name} + + + + //XX控件1[@X='XXXX'] + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/java/pres/auxiliary/work/selenium/event/EventProxyTest.java b/src/test/java/pres/auxiliary/work/selenium/event/EventProxyTest.java index f5672a1..e30f762 100644 --- a/src/test/java/pres/auxiliary/work/selenium/event/EventProxyTest.java +++ b/src/test/java/pres/auxiliary/work/selenium/event/EventProxyTest.java @@ -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 clickProxy; - EventProxy 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 clickProxy; + EventProxy 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(); + }); + } +} diff --git a/src/test/java/pres/auxiliary/work/selenium/event/EventWaitTest.java b/src/test/java/pres/auxiliary/work/selenium/event/EventWaitTest.java index 3a30652..ca8f9e0 100644 --- a/src/test/java/pres/auxiliary/work/selenium/event/EventWaitTest.java +++ b/src/test/java/pres/auxiliary/work/selenium/event/EventWaitTest.java @@ -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()); diff --git a/src/test/java/pres/auxiliary/work/selenium/event/JsEventTest.java b/src/test/java/pres/auxiliary/work/selenium/event/JsEventTest.java index 57a3df3..0f5bf6c 100644 --- a/src/test/java/pres/auxiliary/work/selenium/event/JsEventTest.java +++ b/src/test/java/pres/auxiliary/work/selenium/event/JsEventTest.java @@ -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; - -/** - *

文件名:JsEventTest.java

- *

用途: - * 用于对{@link JsEvent}类进行单元测试,使用控制已打开的浏览器 - *

- *

页面: - * https://www.baidu.com/(百度首页),针对搜索条件文本框 - *

- *

编码时间:2020年5月17日 下午1:40:28

- *

修改时间:2020年5月17日 下午1:40:28

- * @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; + +/** + *

文件名:JsEventTest.java

+ *

用途: + * 用于对{@link JsEvent}类进行单元测试,使用控制已打开的浏览器 + *

+ *

页面: + * https://www.baidu.com/(百度首页),针对搜索条件文本框 + *

+ *

编码时间:2020年5月17日 下午1:40:28

+ *

修改时间:2020年5月17日 下午1:40:28

+ * @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)); + } +} diff --git a/src/test/java/pres/auxiliary/work/selenium/event/TextEventTest.java b/src/test/java/pres/auxiliary/work/selenium/event/TextEventTest.java index 8ea2d74..3ff2f56 100644 --- a/src/test/java/pres/auxiliary/work/selenium/event/TextEventTest.java +++ b/src/test/java/pres/auxiliary/work/selenium/event/TextEventTest.java @@ -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 diff --git a/src/test/java/pres/auxiliary/work/selenium/tool/ExcelRecordTest.java b/src/test/java/pres/auxiliary/work/selenium/tool/ExcelRecordTest.java index 8f907e5..3e4f42f 100644 --- a/src/test/java/pres/auxiliary/work/selenium/tool/ExcelRecordTest.java +++ b/src/test/java/pres/auxiliary/work/selenium/tool/ExcelRecordTest.java @@ -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) diff --git a/src/test/java/pres/auxiliary/work/selenium/tool/Test.xlsx b/src/test/java/pres/auxiliary/work/selenium/tool/Test.xlsx index 0a20018..72e748e 100644 Binary files a/src/test/java/pres/auxiliary/work/selenium/tool/Test.xlsx and b/src/test/java/pres/auxiliary/work/selenium/tool/Test.xlsx differ diff --git a/src/test/java/pres/auxiliary/work/selenium/tool/新建文本文档.txt b/src/test/java/pres/auxiliary/work/selenium/tool/新建文本文档.txt new file mode 100644 index 0000000..e69de29 diff --git a/src/test/java/pres/auxiliary/work/testcase/用例xml文件.xml b/src/test/java/pres/auxiliary/work/testcase/用例xml文件.xml index d9b94db..b1b7a58 100644 --- a/src/test/java/pres/auxiliary/work/testcase/用例xml文件.xml +++ b/src/test/java/pres/auxiliary/work/testcase/用例xml文件.xml @@ -1,2 +1,2 @@ - \ No newline at end of file + \ No newline at end of file