diff --git a/README.md b/README.md index 2cbecb5..8c0f686 100644 --- a/README.md +++ b/README.md @@ -58,14 +58,14 @@ sonic-driver-core can be separated from appium and interact directly with webdri io.github.soniccloudorg sonic-driver-core - 1.0.5 + 1.0.6 ``` #### Gradle ``` -implementation 'io.github.soniccloudorg:sonic-driver-core:1.0.5' +implementation 'io.github.soniccloudorg:sonic-driver-core:1.0.6' ``` ### Code diff --git a/README_CN.md b/README_CN.md index 7ab1d99..2a238d9 100644 --- a/README_CN.md +++ b/README_CN.md @@ -53,12 +53,12 @@ sonic-driver-core可以脱离Appium,直接与WebDriverAgent或UIautomator2交 io.github.soniccloudorg sonic-driver-core - 1.0.5 + 1.0.6 ``` #### Gradle ``` -implementation 'io.github.soniccloudorg:sonic-driver-core:1.0.5' +implementation 'io.github.soniccloudorg:sonic-driver-core:1.0.6' ``` ### 代码 diff --git a/pom.xml b/pom.xml index a3fc125..11170a3 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.github.soniccloudorg sonic-driver-core - 1.0.5 + 1.0.6 sonic-driver-core The Sonic Project UIAutomation Driver Core for Android, iOS, Windows, Mac and so on. diff --git a/src/main/java/org/cloud/sonic/core/ios/IOSDriver.java b/src/main/java/org/cloud/sonic/core/ios/IOSDriver.java index b799a3b..0acba52 100644 --- a/src/main/java/org/cloud/sonic/core/ios/IOSDriver.java +++ b/src/main/java/org/cloud/sonic/core/ios/IOSDriver.java @@ -24,6 +24,8 @@ import org.cloud.sonic.core.ios.service.impl.WdaClientImpl; import org.cloud.sonic.core.tool.SonicRespException; +import java.util.List; + /** * @author Eason * ios driver @@ -459,4 +461,118 @@ public WebElement findElement(XCUIElementType xcuiElementType, Integer retry, In public WebElement findElement(String selector, String value, Integer retry, Integer interval) throws SonicRespException { return wdaClient.findElement(selector, value, retry, interval); } + + /** + * find element list in device. + * + * @param iosSelector + * @param value + * @return + * @throws SonicRespException + */ + public List findElementList(IOSSelector iosSelector, String value) throws SonicRespException { + return findElementList(iosSelector, value, null); + } + + /** + * find element list in device. + * + * @param xcuiElementType + * @return + * @throws SonicRespException + */ + public List findElementList(XCUIElementType xcuiElementType) throws SonicRespException { + return findElementList(xcuiElementType, null); + } + + /** + * find element list in device. + * + * @param selector + * @param value + * @return + * @throws SonicRespException + */ + public List findElementList(String selector, String value) throws SonicRespException { + return findElementList(selector, value, null); + } + + /** + * find element list in device. + * + * @param iosSelector + * @param value + * @param retry + * @return + * @throws SonicRespException + */ + public List findElementList(IOSSelector iosSelector, String value, Integer retry) throws SonicRespException { + return findElementList(iosSelector, value, retry, null); + } + + /** + * find element list in device. + * + * @param xcuiElementType + * @param retry + * @return + * @throws SonicRespException + */ + public List findElementList(XCUIElementType xcuiElementType, Integer retry) throws SonicRespException { + return findElementList(xcuiElementType, retry, null); + } + + /** + * find element list in device. + * + * @param selector + * @param value + * @param retry + * @return + * @throws SonicRespException + */ + public List findElementList(String selector, String value, Integer retry) throws SonicRespException { + return findElementList(selector, value, retry, null); + } + + /** + * find element list in device. + * + * @param iosSelector + * @param value + * @param retry + * @param interval + * @return + * @throws SonicRespException + */ + public List findElementList(IOSSelector iosSelector, String value, Integer retry, Integer interval) throws SonicRespException { + return findElementList(iosSelector.getSelector(), value, retry, interval); + } + + /** + * find element list in device. + * + * @param xcuiElementType + * @param retry + * @param interval + * @return + * @throws SonicRespException + */ + public List findElementList(XCUIElementType xcuiElementType, Integer retry, Integer interval) throws SonicRespException { + return findElementList(IOSSelector.CLASS_NAME.getSelector(), xcuiElementType.getType(), retry, interval); + } + + /** + * find element list in device. + * + * @param selector + * @param value + * @param retry + * @param interval + * @return + * @throws SonicRespException + */ + public List findElementList(String selector, String value, Integer retry, Integer interval) throws SonicRespException { + return wdaClient.findElementList(selector, value, retry, interval); + } } diff --git a/src/main/java/org/cloud/sonic/core/ios/models/IOSRect.java b/src/main/java/org/cloud/sonic/core/ios/models/IOSRect.java new file mode 100644 index 0000000..a0999e3 --- /dev/null +++ b/src/main/java/org/cloud/sonic/core/ios/models/IOSRect.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) [SonicCloudOrg] Sonic Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.cloud.sonic.core.ios.models; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.ToString; + +@Getter +@ToString +@AllArgsConstructor +public class IOSRect { + private int x; + private int y; + private int width; + private int height; +} diff --git a/src/main/java/org/cloud/sonic/core/ios/models/SessionInfo.java b/src/main/java/org/cloud/sonic/core/ios/models/SessionInfo.java index 5899430..ce8d1c6 100644 --- a/src/main/java/org/cloud/sonic/core/ios/models/SessionInfo.java +++ b/src/main/java/org/cloud/sonic/core/ios/models/SessionInfo.java @@ -18,8 +18,10 @@ import lombok.AllArgsConstructor; import lombok.Getter; +import lombok.ToString; @Getter +@ToString @AllArgsConstructor public class SessionInfo { private String sessionId; diff --git a/src/main/java/org/cloud/sonic/core/ios/service/WdaClient.java b/src/main/java/org/cloud/sonic/core/ios/service/WdaClient.java index f6203cc..3e6995e 100644 --- a/src/main/java/org/cloud/sonic/core/ios/service/WdaClient.java +++ b/src/main/java/org/cloud/sonic/core/ios/service/WdaClient.java @@ -21,6 +21,8 @@ import org.cloud.sonic.core.ios.models.TouchActions; import org.cloud.sonic.core.tool.SonicRespException; +import java.util.List; + /** * @author Eason * wda client interface @@ -83,4 +85,7 @@ public interface WdaClient { //element handler. WebElement findElement(String selector, String value, Integer retry, Integer interval) throws SonicRespException; + + //element handler. + List findElementList(String selector, String value, Integer retry, Integer interval) throws SonicRespException; } diff --git a/src/main/java/org/cloud/sonic/core/ios/service/WebElement.java b/src/main/java/org/cloud/sonic/core/ios/service/WebElement.java index b4d186a..a87482e 100644 --- a/src/main/java/org/cloud/sonic/core/ios/service/WebElement.java +++ b/src/main/java/org/cloud/sonic/core/ios/service/WebElement.java @@ -16,6 +16,7 @@ */ package org.cloud.sonic.core.ios.service; +import org.cloud.sonic.core.ios.models.IOSRect; import org.cloud.sonic.core.tool.SonicRespException; /** @@ -29,4 +30,10 @@ public interface WebElement { void sendKeys(String text) throws SonicRespException; void sendKeys(String text,int frequency) throws SonicRespException; + + void clear() throws SonicRespException; + + String getText() throws SonicRespException; + + IOSRect getRect() throws SonicRespException; } diff --git a/src/main/java/org/cloud/sonic/core/ios/service/impl/WdaClientImpl.java b/src/main/java/org/cloud/sonic/core/ios/service/impl/WdaClientImpl.java index de56416..a4bbb56 100644 --- a/src/main/java/org/cloud/sonic/core/ios/service/impl/WdaClientImpl.java +++ b/src/main/java/org/cloud/sonic/core/ios/service/impl/WdaClientImpl.java @@ -30,6 +30,7 @@ import org.cloud.sonic.core.tool.SonicRespException; import java.nio.charset.StandardCharsets; +import java.util.ArrayList; import java.util.Arrays; import java.util.Base64; import java.util.List; @@ -371,4 +372,50 @@ public WebElement findElement(String selector, String value, Integer retry, Inte } return webElement; } + + @Override + public List findElementList(String selector, String value, Integer retry, Integer interval) throws SonicRespException { + List webElementList = new ArrayList<>(); + int wait = 0; + int intervalInit = (interval == null ? FIND_ELEMENT_INTERVAL : interval); + int retryInit = (retry == null ? FIND_ELEMENT_RETRY : retry); + String errMsg = ""; + while (wait < retryInit) { + wait++; + checkSessionId(); + JSONObject data = new JSONObject(); + data.put("using", selector); + data.put("value", value); + BaseResp b = respHandler.getResp(HttpUtil.createPost(remoteUrl + "/session/" + sessionId + "/elements") + .body(data.toJSONString())); + if (b.getErr() == null) { + log.info("find elements successful."); + List ids = JSON.parseObject(b.getValue().toString(), ArrayList.class); + for (JSONObject ele : ids) { + String id = parseElementId(ele); + if (id.length() > 0) { + webElementList.add(new WebElementImpl(id, this)); + } else { + log.error("parse element id {} failed.", ele); + continue; + } + } + break; + } else { + log.error("elements not found. retried {} times, retry in {} ms.", wait, intervalInit); + errMsg = b.getErr().getMessage(); + } + if (wait < retryInit) { + try { + Thread.sleep(intervalInit); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + if (webElementList.size() == 0) { + throw new SonicRespException(errMsg); + } + return webElementList; + } } diff --git a/src/main/java/org/cloud/sonic/core/ios/service/impl/WebElementImpl.java b/src/main/java/org/cloud/sonic/core/ios/service/impl/WebElementImpl.java index a56d511..cd55a1c 100644 --- a/src/main/java/org/cloud/sonic/core/ios/service/impl/WebElementImpl.java +++ b/src/main/java/org/cloud/sonic/core/ios/service/impl/WebElementImpl.java @@ -18,8 +18,10 @@ import cn.hutool.http.HttpUtil; import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson2.JSON; import lombok.extern.slf4j.Slf4j; import org.cloud.sonic.core.ios.models.BaseResp; +import org.cloud.sonic.core.ios.models.IOSRect; import org.cloud.sonic.core.ios.service.WdaClient; import org.cloud.sonic.core.ios.service.WebElement; import org.cloud.sonic.core.tool.SonicRespException; @@ -61,7 +63,8 @@ public void sendKeys(String text, int frequency) throws SonicRespException { data.put("frequency", frequency); BaseResp b = wdaClient.getRespHandler().getResp( HttpUtil.createPost(wdaClient.getRemoteUrl() + "/session/" - + wdaClient.getSessionId() + "/element/" + id + "/value")); + + wdaClient.getSessionId() + "/element/" + id + "/value") + .body(data.toJSONString()), 60000); if (b.getErr() == null) { log.info("send key to {}.", id); } else { @@ -69,4 +72,49 @@ public void sendKeys(String text, int frequency) throws SonicRespException { throw new SonicRespException(b.getErr().getMessage()); } } + + @Override + public void clear() throws SonicRespException { + wdaClient.checkSessionId(); + BaseResp b = wdaClient.getRespHandler().getResp( + HttpUtil.createPost(wdaClient.getRemoteUrl() + "/session/" + + wdaClient.getSessionId() + "/element/" + id + "/clear"), 60000); + if (b.getErr() == null) { + log.info("clear {}.", id); + } else { + log.error("clear {} failed.", id); + throw new SonicRespException(b.getErr().getMessage()); + } + } + + @Override + public String getText() throws SonicRespException { + wdaClient.checkSessionId(); + BaseResp b = wdaClient.getRespHandler().getResp( + HttpUtil.createGet(wdaClient.getRemoteUrl() + "/session/" + + wdaClient.getSessionId() + "/element/" + id + "/text")); + if (b.getErr() == null) { + log.info("get {} text {}.", id, b.getValue()); + return b.getValue().toString(); + } else { + log.error("get {} text failed.", id); + throw new SonicRespException(b.getErr().getMessage()); + } + } + + @Override + public IOSRect getRect() throws SonicRespException { + wdaClient.checkSessionId(); + BaseResp b = wdaClient.getRespHandler().getResp( + HttpUtil.createGet(wdaClient.getRemoteUrl() + "/session/" + + wdaClient.getSessionId() + "/element/" + id + "/rect")); + if (b.getErr() == null) { + log.info("get {} rect {}.", id, b.getValue()); + IOSRect iosRect = JSON.parseObject(b.getValue().toString(),IOSRect.class); + return iosRect; + } else { + log.error("get {} rect failed.", id); + throw new SonicRespException(b.getErr().getMessage()); + } + } } diff --git a/src/test/java/org/cloud/sonic/core/ios/IOSDriverTest.java b/src/test/java/org/cloud/sonic/core/ios/IOSDriverTest.java index 3fd24a6..0f5e25c 100644 --- a/src/test/java/org/cloud/sonic/core/ios/IOSDriverTest.java +++ b/src/test/java/org/cloud/sonic/core/ios/IOSDriverTest.java @@ -19,6 +19,7 @@ import com.alibaba.fastjson.JSONObject; import org.cloud.sonic.core.ios.enums.*; import org.cloud.sonic.core.ios.models.*; +import org.cloud.sonic.core.ios.service.WebElement; import org.cloud.sonic.core.tool.SonicRespException; import org.junit.*; import org.junit.runner.RunWith; @@ -124,7 +125,7 @@ public void testPasteboard() throws SonicRespException, InterruptedException { iosDriver.findElement(IOSSelector.ACCESSIBILITY_ID, "WebDriverAgentRunner-Runner").click(); iosDriver.setPasteboard(PasteboardType.PLAIN_TEXT, text); Thread.sleep(1000); - Assert.assertEquals(text, new String(iosDriver.getPasteboard(PasteboardType.PLAIN_TEXT.getType()))); + Assert.assertEquals(text, new String(iosDriver.getPasteboard(PasteboardType.PLAIN_TEXT))); iosDriver.pressButton(SystemButton.HOME); } @@ -206,14 +207,37 @@ public void testSession() { } @Test - public void testFindElement() throws SonicRespException { + public void testFindElement() throws SonicRespException, InterruptedException { iosDriver.pressButton(SystemButton.HOME); + Thread.sleep(2000); iosDriver.findElement("accessibility id", "地图").click(); + iosDriver.findElement(XCUIElementType.ANY); + Thread.sleep(2000); iosDriver.pressButton(SystemButton.HOME); + Thread.sleep(2000); iosDriver.findElement(IOSSelector.ACCESSIBILITY_ID, "地图").click(); + Thread.sleep(2000); + iosDriver.findElement(IOSSelector.ACCESSIBILITY_ID, "搜索地点或地址").click(); + WebElement w = iosDriver.findElement(IOSSelector.ACCESSIBILITY_ID, "搜索地点或地址"); + String text = UUID.randomUUID().toString(); + w.sendKeys(text); + Assert.assertEquals(text, w.getText()); + w.clear(); + Assert.assertEquals("搜索地点或地址", w.getText()); + IOSRect iosRect = w.getRect(); + Assert.assertTrue(iosRect.getX() > 0); + Assert.assertTrue(iosRect.getY() > 0); + Assert.assertTrue(iosRect.getWidth() > 0); + Assert.assertTrue(iosRect.getHeight() > 0); + iosDriver.findElement(IOSSelector.ACCESSIBILITY_ID, "取消").click(); iosDriver.pressButton(SystemButton.HOME); - iosDriver.findElement(XCUIElementType.ANY); - iosDriver.pressButton(SystemButton.HOME); + } + + @Test + public void testFindElementList() throws SonicRespException { + int eleSize = iosDriver.findElementList(XCUIElementType.WINDOW).size(); + Assert.assertEquals(eleSize,iosDriver.findElementList("class name","XCUIElementTypeWindow").size()); + Assert.assertEquals(eleSize,iosDriver.findElementList(IOSSelector.CLASS_NAME,"XCUIElementTypeWindow").size()); } @AfterClass