<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                ## 如何搭建并定制 WebDriverAgent Server Appium 的 iOS 版本的后端用的是[Facebook's WebDriverAgent](https://github.com/facebook/WebDriverAgent)。該后端是基于蘋果公司的 XCTest 框架,所以也有所有 XCTest 框架已知的問題。其中有些問題我們正在設法解決,有一些在現階段可能無法解決,如 https://github.com/facebookarchive/WebDriverAgent/issues/507 。本文中描述的方法已經能夠使您完全掌握在設備上如何構建、管理和運行WDA。通過這種方式,您可以在CI環境中對您的自動化測試進行微調,并使其在長期運行的情況下更加穩定。 重點: * 如果使用了Appium的默認設置,則不需要如下的步驟。服務器將為您搞定一切,當然你也不能對WDA做太多控制。 * 對連接的被測設備必須有SSH或物理訪問權限。 ### 安裝WDA Appium 會自動下載 WebDriverAgent 源碼。如果使用 npm 命令(`npm install -g appium`)安裝Appium的話,通常情況下會保存在/usr/local/lib/node_modules/appium/node_modules/appium-xcuitest-driver/WebDriverAgent目錄下。 如果是首次安裝的話,還需要下載一些第三方依賴("carthage"工具就是為此準備的:`brew install carthage`): ```bash cd /usr/local/lib/node_modules/appium/node_modules/appium-xcuitest-driver/WebDriverAgent ./Scripts/bootstrap.sh -d ``` 并且,可能需要為 WDA 源碼創建一個空文件夾: ```bash mkdir -p /usr/local/lib/node_modules/appium/node_modules/appium-webdriveragent/Resources/WebDriverAgent.bundle ``` 如果是在iOS模擬器上執行自動化測試,是不需要進一步配置的。 但如果是在真機上進行測試的話,則需要進一步配置。參考[real device configuration documentation](https://github.com/appium/appium-xcuitest-driver/blob/master/docs/real-device-config.md)設置代碼簽名。 為了確保 WDA 源代碼配置正確,請執行以下操作: * 用Xcode打開 `/usr/local/lib/node_modules/appium/node_modules/appium-xcuitest-driver/WebDriverAgent/WebDriverAgent.xcodeproj` * 選擇工程*WebDriverAgentRunner* * 選擇要運行自動化測試的真機/模擬器作為構建目標機 * 在主菜單中選擇 Product -> Test Xcode 會成功構建項目并安裝到真機/模擬器上,所以您將在蘋果系統的桌面上看到 WebDriverAgentRunner 應用程序的圖標。 ### 啟動WDA WebDriverAgent 作為一個 REST 服務,監聽外部 API 請求,傳遞給原生 XCTest 調用待測應用。如果是在模擬器上進行測試,REST 服務的地址是 localhost,如果是在真機上運行,REST 服務的地址將是實際的 ip 地址。使用 appium-ios-device(https://github.com/appium/appium-ios-device) 將網絡請求路由到通過 USB 連接的真機上,這意味著可以使用這個工具將模擬器和真實設備上的 WDA 網絡地址統一。 可以使用 appium-ios-device(https://github.com/appium/appium-ios-device) 連接遠程設備,JavaScript 模塊代碼需要和 Appium 一致。 此外,也可以使用*iproxy*在 Appium 之外轉發 WebDriverAgent。安裝命令如下`node install -g iproxy`。 這個用Java編寫的助手類說明了主要的實現細節: ```java public class WDAServer { private static final Logger log = ZLogger.getLog(WDAServer.class.getSimpleName()); private static final int MAX_REAL_DEVICE_RESTART_RETRIES = 1; private static final Timedelta REAL_DEVICE_RUNNING_TIMEOUT = Timedelta.ofMinutes(4); private static final Timedelta RESTART_TIMEOUT = Timedelta.ofMinutes(1); // These settings are needed to properly sign WDA for real device tests // See https://github.com/appium/appium-xcuitest-driver for more details private static final File KEYCHAIN = new File(String.format("%s/%s", System.getProperty("user.home"), "/Library/Keychains/MyKeychain.keychain")); private static final String KEYCHAIN_PASSWORD = "******"; private static final File IPROXY_EXECUTABLE = new File("/usr/local/bin/iproxy"); private static final File XCODEBUILD_EXECUTABLE = new File("/usr/bin/xcodebuild"); private static final File WDA_PROJECT = new File("/usr/local/lib/node_modules/appium/node_modules/appium-xcuitest-driver/" + "WebDriverAgent/WebDriverAgent.xcodeproj"); private static final String WDA_SCHEME = "WebDriverAgentRunner"; private static final String WDA_CONFIGURATION = "Debug"; private static final File XCODEBUILD_LOG = new File("/usr/local/var/log/appium/build.log"); private static final File IPROXY_LOG = new File("/usr/local/var/log/appium/iproxy.log"); private static final int PORT = 8100; public static final String SERVER_URL = String.format("http://127.0.0.1:%d", PORT); private static final String[] IPROXY_CMDLINE = new String[]{ IPROXY_EXECUTABLE.getAbsolutePath(), Integer.toString(PORT), Integer.toString(PORT), String.format("> %s 2>&1 &", IPROXY_LOG.getAbsolutePath()) }; private static WDAServer instance = null; private final boolean isRealDevice; private final String deviceId; private final String platformVersion; private int failedRestartRetriesCount = 0; private WDAServer() { try { this.isRealDevice = !getIsSimulatorFromConfig(getClass()); final String udid; if (isRealDevice) { udid = IOSRealDeviceHelpers.getUDID(); } else { udid = IOSSimulatorHelpers.getId(); } this.deviceId = udid; this.platformVersion = getPlatformVersionFromConfig(getClass()); } catch (Exception e) { throw new RuntimeException(e); } ensureToolsExistence(); ensureParentDirExistence(); } public synchronized static WDAServer getInstance() { if (instance == null) { instance = new WDAServer(); } return instance; } private boolean waitUntilIsRunning(Timedelta timeout) throws Exception { final URL status = new URL(SERVER_URL + "/status"); try { if (timeout.asSeconds() > 5) { log.debug(String.format("Waiting max %s until WDA server starts responding...", timeout)); } new UrlChecker().waitUntilAvailable(timeout.asMillis(), TimeUnit.MILLISECONDS, status); return true; } catch (UrlChecker.TimeoutException e) { return false; } } private static void ensureParentDirExistence() { if (!XCODEBUILD_LOG.getParentFile().exists()) { if (!XCODEBUILD_LOG.getParentFile().mkdirs()) { throw new IllegalStateException(String.format( "The script has failed to create '%s' folder for Appium logs. " + "Please make sure your account has correct access permissions on the parent folder(s)", XCODEBUILD_LOG.getParentFile().getAbsolutePath())); } } } private void ensureToolsExistence() { if (isRealDevice && !IPROXY_EXECUTABLE.exists()) { throw new IllegalStateException(String.format("%s tool is expected to be installed (`npm install -g iproxy`)", IPROXY_EXECUTABLE.getAbsolutePath())); } if (!XCODEBUILD_EXECUTABLE.exists()) { throw new IllegalStateException(String.format("xcodebuild tool is not detected on the current system at %s", XCODEBUILD_EXECUTABLE.getAbsolutePath())); } if (!WDA_PROJECT.exists()) { throw new IllegalStateException(String.format("WDA project is expected to exist at %s", WDA_PROJECT.getAbsolutePath())); } } private List<String> generateXcodebuildCmdline() { final List<String> result = new ArrayList<>(); result.add(XCODEBUILD_EXECUTABLE.getAbsolutePath()); result.add("clean build test"); result.add(String.format("-project %s", WDA_PROJECT.getAbsolutePath())); result.add(String.format("-scheme %s", WDA_SCHEME)); result.add(String.format("-destination id=%s", deviceId)); result.add(String.format("-configuration %s", WDA_CONFIGURATION)); result.add(String.format("IPHONEOS_DEPLOYMENT_TARGET=%s", platformVersion)); result.add(String.format("> %s 2>&1 &", XCODEBUILD_LOG.getAbsolutePath())); return result; } private static List<String> generateKeychainUnlockCmdlines() throws Exception { final List<String> result = new ArrayList<>(); result.add(String.format("/usr/bin/security -v list-keychains -s %s", KEYCHAIN.getAbsolutePath())); result.add(String.format("/usr/bin/security -v unlock-keychain -p %s %s", KEYCHAIN_PASSWORD, KEYCHAIN.getAbsolutePath())); result.add(String.format("/usr/bin/security set-keychain-settings -t 3600 %s", KEYCHAIN.getAbsolutePath())); return result; } public synchronized void restart() throws Exception { if (isRealDevice && failedRestartRetriesCount >= MAX_REAL_DEVICE_RESTART_RETRIES) { throw new IllegalStateException(String.format( "WDA server cannot start on the connected device with udid %s after %s retries. " + "Reboot the device manually and try again", deviceId, MAX_REAL_DEVICE_RESTART_RETRIES)); } final String hostname = InetAddress.getLocalHost().getHostName(); log.info(String.format("Trying to (re)start WDA server on %s:%s...", hostname, PORT)); UnixProcessHelpers.killProcessesGracefully(IPROXY_EXECUTABLE.getName(), XCODEBUILD_EXECUTABLE.getName()); final File scriptFile = File.createTempFile("script", ".sh"); try { final List<String> scriptContent = new ArrayList<>(); scriptContent.add("#!/bin/bash"); if (isRealDevice && isRunningInJenkinsNetwork()) { scriptContent.add(String.join("\n", generateKeychainUnlockCmdlines())); } if (isRealDevice) { scriptContent.add(String.join(" ", IPROXY_CMDLINE)); } final String wdaBuildCmdline = String.join(" ", generateXcodebuildCmdline()); log.debug(String.format("Building WDA with command line:\n%s\n", wdaBuildCmdline)); scriptContent.add(wdaBuildCmdline); try (Writer output = new BufferedWriter(new FileWriter(scriptFile))) { output.write(String.join("\n", scriptContent)); } new ProcessBuilder("/bin/chmod", "u+x", scriptFile.getCanonicalPath()) .redirectErrorStream(true).start().waitFor(5, TimeUnit.SECONDS); final ProcessBuilder pb = new ProcessBuilder("/bin/bash", scriptFile.getCanonicalPath()); final Map<String, String> env = pb.environment(); // This is needed for Jenkins env.put("BUILD_ID", "dontKillMe"); log.info(String.format("Waiting max %s for WDA to be (re)started on %s:%s...", RESTART_TIMEOUT.toString(), hostname, PORT)); final Timedelta started = Timedelta.now(); pb.redirectErrorStream(true).start().waitFor(RESTART_TIMEOUT.asMillis(), TimeUnit.MILLISECONDS); if (!waitUntilIsRunning(RESTART_TIMEOUT)) { ++failedRestartRetriesCount; throw new IllegalStateException( String.format("WDA server has failed to start after %s timeout on server '%s'.\n" + "Please make sure that iDevice is properly connected and you can build " + "WDA manually from XCode.\n" + "Xcodebuild logs:\n\n%s\n\n\niproxy logs:\n\n%s\n\n\n", RESTART_TIMEOUT, hostname, getLog(XCODEBUILD_LOG).orElse("EMPTY"), getLog(IPROXY_LOG).orElse("EMPTY")) ); } log.info(String.format("WDA server has been successfully (re)started after %s " + "and now is listening on %s:%s", Timedelta.now().diff(started).toString(), hostname, PORT)); } finally { scriptFile.delete(); } } public boolean isRunning() throws Exception { if (!isProcessRunning(XCODEBUILD_EXECUTABLE.getName()) || (isRealDevice && !isProcessRunning(IPROXY_EXECUTABLE.getName()))) { return false; } return waitUntilIsRunning(isRealDevice ? REAL_DEVICE_RUNNING_TIMEOUT : Timedelta.ofSeconds(3)); } public Optional<String> getLog(File logFile) { if (logFile.exists()) { try { return Optional.of(new String(Files.readAllBytes(logFile.toPath()), Charset.forName("UTF-8"))); } catch (IOException e) { e.printStackTrace(); } } return Optional.empty(); } public void resetLogs() { for (File logFile : new File[]{XCODEBUILD_LOG, IPROXY_LOG}) { if (logFile.exists()) { try { final PrintWriter writer = new PrintWriter(logFile); writer.print(""); writer.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } } } } } ``` 之前應該調用這段代碼來啟動 Appium iOS 驅動,例如,在 setUp 方法中: ```java if (!WDAServer.getInstance().isRunning()) { WDAServer.getInstance().restart(); } ``` 為 Appium 驅動程序設置 webDriverAgentUrl 非常重要,讓它知道我們的 WDA 驅動程序可以使用: ```java capabilities.setCapability("webDriverAgentUrl", WDAServer.SERVER_URL); ``` ### 重要注釋 * 如果是 jenkins agent 執行的,該進程不能直接訪問鑰匙串(Keychain),所以我們在真機上編譯 WDA 前需要提前準備好鑰匙串,否則驗簽會失敗。 * 就算 xcodebuild 和 iproxy 進程已經被凍結,我們在重新啟動之前殺死這些進程,也可以確保編譯成功 * 我們準備一個單獨的 bash 腳本并獨立于 iproxy / xcodebuild 進程,所以即使在實際的代碼執行完成后,它們也可以在后臺繼續運行。如果在自動化 lab 中的同一機器/節點上執行多個測試/套件,最少的人工干預是非常重要的。 * 更改*BUILD_ID*環境變量的值以避免在作業完成后,后臺進程被 Jenkins agent 程序殺死。 * 通過驗證實際的網絡終端,來檢查*isRunning* * 守護進程的輸出會存入日志,因此可以跟蹤錯誤和意外的故障。如果服務器無法啟動/重啟,日志文件的內容會自動添加到實際的錯誤消息中。 * 真機設備ID可以從 `system_profiler SPUSBDataType` 輸出中解析 * 模擬器ID可以從 `xcrun simctl list` 輸出中解析 * *UrlChecker*類是從 org.openqa.selenium.net 包導入的 本文由 [simple](https://testerhome.com/simple) 翻譯,由 [lihuazhang](https://github.com/lihuazhang) 校驗。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看