* 启动 Electron 应用
(options = {}, deps = {})
| 145 | * 启动 Electron 应用 |
| 146 | */ |
| 147 | async function launchApp(options = {}, deps = {}) { |
| 148 | const spawnFn = deps.spawnFn || spawn |
| 149 | const findPortFn = deps.findPortFn || findAvailablePortWithReservation |
| 150 | const sleepFn = deps.sleepFn || ((ms) => new Promise((resolve) => setTimeout(resolve, ms))) |
| 151 | const fsImpl = deps.fsImpl || fs |
| 152 | |
| 153 | let reservationServer = null |
| 154 | let port = options.port |
| 155 | const startPort = options.startPort ?? DEFAULT_START_PORT |
| 156 | const maxPortRetries = options.maxPortRetries ?? DEFAULT_MAX_PORT_RETRIES |
| 157 | const portProbeTimeoutMs = options.portProbeTimeoutMs ?? DEFAULT_PORT_PROBE_TIMEOUT_MS |
| 158 | const startupWaitTime = options.startupWaitTime ?? DEFAULT_STARTUP_WAIT_MS |
| 159 | const forceKillTimeoutMs = options.forceKillTimeoutMs ?? DEFAULT_FORCE_KILL_TIMEOUT_MS |
| 160 | const envOverrides = options.envOverrides || {} |
| 161 | |
| 162 | if (!port) { |
| 163 | const reservation = await findPortFn(startPort, maxPortRetries, 0, { |
| 164 | listenTimeoutMs: portProbeTimeoutMs, |
| 165 | }) |
| 166 | |
| 167 | if (!reservation) { |
| 168 | throw new Error('[AppLauncher] 无法找到可用端口') |
| 169 | } |
| 170 | port = reservation.port |
| 171 | reservationServer = reservation.reservationServer |
| 172 | } |
| 173 | |
| 174 | const userDataDir = |
| 175 | options.userDataDir || |
| 176 | (process.env.CHATLAB_E2E_USER_DATA_DIR |
| 177 | ? path.join(process.env.CHATLAB_E2E_USER_DATA_DIR, `instance-${port}`) |
| 178 | : path.join(os.tmpdir(), `chatlab-e2e-${port}`)) |
| 179 | |
| 180 | if (!fsImpl.existsSync(userDataDir)) { |
| 181 | fsImpl.mkdirSync(userDataDir, { recursive: true }) |
| 182 | } |
| 183 | |
| 184 | const appPath = path.resolve(__dirname, '../../../apps/desktop') |
| 185 | if (!fsImpl.existsSync(appPath)) { |
| 186 | throw new Error(`[AppLauncher] 应用目录不存在: ${appPath}`) |
| 187 | } |
| 188 | |
| 189 | const electronExe = resolveElectronExecutable(deps) |
| 190 | |
| 191 | if (!electronExe || !fsImpl.existsSync(electronExe)) { |
| 192 | throw new Error(`Electron 可执行文件不存在: ${electronExe}`) |
| 193 | } |
| 194 | |
| 195 | console.log(`[AppLauncher] 启动 Electron,CDP 端口: ${port}`) |
| 196 | |
| 197 | const electronArgs = [`--remote-debugging-port=${port}`, appPath] |
| 198 | |
| 199 | let proc |
| 200 | let launchError = null |
| 201 | let exitCode = null |
| 202 | let exitSignal = null |
| 203 | |
| 204 | try { |
no test coverage detected