diff --git a/lib/winappdriver.js b/lib/winappdriver.js index 7afc2ebc..8d7889af 100644 --- a/lib/winappdriver.js +++ b/lib/winappdriver.js @@ -7,7 +7,7 @@ import { getWADExecutablePath } from './installer'; import { waitForCondition } from 'asyncbox'; import { execSync } from 'child_process'; import { util } from 'appium/support'; -import { findAPortNotInUse } from 'portscanner'; +import { findAPortNotInUse, checkPortStatus } from 'portscanner'; const DEFAULT_BASE_PATH = '/wd/hub'; @@ -21,11 +21,24 @@ const PORT_ALLOCATION_GUARD = util.getLockFileGuard(path.resolve(os.tmpdir(), 'w timeout: 5, tryRecovery: true, }); +const TROUBLESHOOTING_LINK = 'https://github.com/appium/appium-windows-driver?tab=readme-ov-file#troubleshooting'; class WADProxy extends JWProxy { /** @type {boolean|undefined} */ didProcessExit; + async isListening() { + const url = this.getUrlForProxy('/status'); + const parsedUrl = new URL(url); + const defaultPort = parsedUrl.protocol === 'https:' ? 443 : 80; + try { + await checkPortStatus(parseInt(parsedUrl.port, 10) || defaultPort, parsedUrl.hostname); + return true; + } catch { + return false; + } + } + /** * @override */ @@ -183,6 +196,8 @@ export class WinAppDriver { } }); + /** @type {Error | undefined} */ + let lastError; try { await waitForCondition(async () => { try { @@ -194,6 +209,7 @@ export class WinAppDriver { if (this.proxy?.didProcessExit) { throw new Error(err.message); } + lastError = err; return false; } }, { @@ -201,13 +217,24 @@ export class WinAppDriver { intervalMs: 1000, }); } catch (e) { - if (/Condition unmet/.test(e.message)) { - throw new Error( - `WinAppDriver server is not listening within ${STARTUP_TIMEOUT_MS}ms timeout. ` + - `Make sure it could be started manually` + if (!lastError || this.proxy.didProcessExit) { + throw e; + } + + const serverUrl = this.proxy.getUrlForProxy('/status'); + let errorMessage = ( + `WinAppDriver server '${executablePath}' is not listening at ${serverUrl} ` + + `after ${STARTUP_TIMEOUT_MS}ms timeout. Make sure it could be started manually.` + ); + if (await this.proxy.isListening()) { + errorMessage = ( + `WinAppDriver server '${executablePath}' is listening at ${serverUrl}, ` + + `but fails to respond with a proper status. It is an issue with the server itself. ` + + `Consider checking the troubleshooting guide at ${TROUBLESHOOTING_LINK}. ` + + `Original error: ${(lastError ?? e).message}` ); } - throw e; + throw new Error(errorMessage); } const pid = this.process.proc?.pid; RUNNING_PROCESS_IDS.push(pid); @@ -246,11 +273,20 @@ export class WinAppDriver { try { await this.proxy.command('/status', 'GET'); - } catch { - throw new Error( + } catch (e) { + let errorMessage = ( `WinAppDriver server is not listening at ${url}. ` + `Make sure it is running and the provided wadUrl is correct` ); + if (await this.proxy.isListening()) { + errorMessage = ( + `WinAppDriver server is listening at ${url}, but fails to respond with a proper status. ` + + `It is an issue with the server itself. ` + + `Consider checking the troubleshooting guide at ${TROUBLESHOOTING_LINK}. ` + + `Original error: ${e.message}` + ); + } + throw new Error(errorMessage); } }