dcloudio / dcloudio/hello-uniapp

[uni-automator] Windows 下 mp-weixin E2E 初始化 program 失败:内部 spawn npm.cmd 抛 EINVAL

Open
#116 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Vue
Stars
3k
Forks
1.7k
PR merge metrics
No merged PRs in 30d

Description

## 问题概述

在 Windows 环境下,按照 DCloud 官方 CLI 项目自动化测试文档运行微信小程序 E2E 测试时,`@dcloudio/uni-automator` 无法稳定初始化 `program`。

当前项目的普通微信小程序测试链路可以通过:

```bash
npm run test:mp-weixin
```

该命令通过 `uni-test mp-weixin` 运行普通模块/结构类测试没有问题。

但当测试文件需要使用官方文档中的 `program` 全局对象执行真实页面自动化时,例如:

```js
page = await program.reLaunch('/pages/task/index')
```

E2E 链路无法跑通。

相关官方文档:

- DCloud CLI 项目运行自动化测试文档:
- DCloud 自动化测试快速开始:
- DCloud 自动化测试 API 文档:
- 微信开发者工具 CLI 文档:

## 当前环境

```text
OS: Windows
Node:
- v22.23.2 复现
- v20.20.2 复现
- v18.20.8 复现
npm:
- 10.9.8 / 10.8.2
jest: 27.0.4
jest-environment-node: 27.5.1
@dcloudio/uni-automator: 3.0.0-4080420251103001
@dcloudio/vite-plugin-uni: 3.0.0-4080420251103001
目标平台: mp-weixin
微信开发者工具 CLI: D:/ProgramFiles/微信web开发者工具/cli.bat
HBuilderX CLI: D:/ProgramFiles/HBuilderX.4.87.2025121004/HBuilderX/cli.exe
```

项目中的 `jest` 与 `jest-environment-node` 版本符合官方文档限制:

```text
jest@27.0.4
jest-environment-node@27.5.1
```

实际依赖树:

```text
+-- @dcloudio/uni-automator@3.0.0-4080420251103001
| +-- jest-environment-node@27.5.1 deduped
| `-- jest@27.0.4 deduped
+-- @dcloudio/vite-plugin-uni@3.0.0-4080420251103001
+-- jest-environment-node@27.5.1
`-- jest@27.0.4
```

## package.json 相关脚本

普通微信测试脚本:

```json
{
"scripts": {
"dev:mp-weixin": "uni-launch mp-weixin",
"build:mp-weixin": "uni build -p mp-weixin",
"test:mp-weixin": "cross-env HBUILDERX_CLI_PATH=D:/ProgramFiles/HBuilderX.4.87.2025121004/HBuilderX/cli.exe uni-test mp-weixin",
"test:mp-weixin:e2e": "cross-env UNI_PLATFORM=mp-weixin jest --config jest.e2e.config.js -i"
}
}
```

普通测试 `test:mp-weixin` 可以跑通,但需要 `program` 的 E2E 无法跑通。

## E2E Jest 配置

尝试使用 DCloud CLI 项目自动化测试文档中的 `jest + @dcloudio/uni-automator` 模式:

```js
const testEnvironmentOptions = require('./env.js')

module.exports = {
testEnvironment: '@dcloudio/uni-automator/dist/environment.js',
testEnvironmentOptions: {
...testEnvironmentOptions,
compile: true,
platform: 'mp-weixin'
},
globalTeardown: '@dcloudio/uni-automator/dist/teardown.js',
testMatch: ['/src/pages/**/*.e2e.test.[jt]s?(x)'],
testTimeout: 60000
}
```

## env.js 中 mp-weixin 配置

```js
module.exports = {
compile: true,
"mp-weixin": {
port: 9420,
account: "",
args: ["--port", "9420"],
cwd: "D:/ProgramFiles/微信web开发者工具",
launch: true,
teardown: "disconnect",
remote: false,
executablePath: "D:/ProgramFiles/微信web开发者工具/cli.bat"
}
}
```

说明:当前微信开发者工具 CLI help 中显示使用 `--port` 指定服务端口,因此也尝试显式传入了 `args: ["--port", "9420"]`。

## E2E 测试代码

```js
describe('task page e2e smoke', () => {
let page

beforeAll(async () => {
page = await program.reLaunch('/pages/task/index')
await page.waitFor(3000)
page = await program.currentPage()
})

it('opens task tab page in mp-weixin runtime', async () => {
expect(page).toBeTruthy()
})

it('renders task page root container', async () => {
const root = await page.$('.task-page-e2e')
expect(root).toBeTruthy()
})
})
```

## 失败路径 1:compile:true 时 spawn EINVAL

运行:

```bash
npm run test:mp-weixin:e2e
```

实际报错:

```text
FAIL src/pages/__tests__/task/pages/task-page-smoke.e2e.test.js
● Test suite failed to run

spawn EINVAL

at node_modules/@dcloudio/uni-automator/dist/index.js:15:23515
at Jt.compile (node_modules/@dcloudio/uni-automator/dist/index.js:15:22728)
at Yt.launch (node_modules/@dcloudio/uni-automator/dist/index.js:15:26103)
at module.exports.setup (node_modules/@dcloudio/uni-automator/dist/environment.js:1:1277)

Test Suites: 1 failed, 1 total
Tests: 0 total
```

开启 debug:

```powershell
$env:DEBUG='automator:*'
npm.cmd run test:mp-weixin:e2e
```

可以看到 `uni-automator` 内部尝试执行:

```text
automator:compiler npm.cmd run dev:mp-weixin -- --auto-port 9520
```

随后在 `child_process.spawn()` 阶段抛出:

```text
spawn EINVAL
```

## 最小复现:Node 直接 spawn npm.cmd 失败

在同一台 Windows 机器上直接运行:

```js
const cp = require('child_process')

try {
const p = cp.spawn('npm.cmd', ['--version'], {
cwd: process.cwd()
})

p.on('error', (e) => {
console.error(e.code, e.message)
process.exit(1)
})

p.stdout.on('data', d => process.stdout.write(d))
p.stderr.on('data', d => process.stderr.write(d))
p.on('exit', c => process.exit(c))
} catch (e) {
console.error(e.code, e.message)
process.exit(1)
}
```

在 Node v22.23.2、v20.20.2、v18.20.8 下均复现:

```text
THROW EINVAL spawn EINVAL
```

但使用 `cmd.exe /c npm.cmd` 可以成功:

```js
const cp = require('child_process')

const p = cp.spawn('cmd.exe', ['/c', 'npm.cmd', '--version'], {
cwd: process.cwd()
})

p.stdout.on('data', d => process.stdout.write(d))
```

输出 npm 版本正常。

因此目前看,`@dcloudio/uni-automator` 在 Windows 下直接 `spawn('npm.cmd', ...)` 存在兼容风险。

## 失败路径 2:compile:false 时 runtime 连接超时

为了绕过 `spawn EINVAL`,尝试改为先普通构建,再让 `uni-automator` 连接构建产物:

```json
{
"scripts": {
"test:mp-weixin:e2e": "npm run build:mp-weixin && cross-env UNI_PLATFORM=mp-weixin UNI_AUTOMATOR_COMPILE=false jest --config jest.e2e.config.js -i"
}
}
```

对应配置:

```js
const path = require('path')
const testEnvironmentOptions = require('./env.js')

module.exports = {
testEnvironment: '@dcloudio/uni-automator/dist/environment.js',
testEnvironmentOptions: {
...testEnvironmentOptions,
compile: false,
projectPath: path.resolve(__dirname, 'dist/build/mp-weixin'),
platform: 'mp-weixin'
},
globalTeardown: '@dcloudio/uni-automator/dist/teardown.js',
testMatch: ['/src/pages/**/*.e2e.test.[jt]s?(x)'],
testTimeout: 60000
}
```

普通构建成功:

```text
DONE Build complete.
Run method: open Weixin Mini Program Devtools, import dist\build\mp-weixin run.
```

构建产物中存在微信小程序必需文件:

```text
dist/build/mp-weixin/project.config.json
dist/build/mp-weixin/app.json
dist/build/mp-weixin/app.js
```

但 E2E 最终失败:

```text
FAIL src/pages/__tests__/task/pages/task-page-smoke.e2e.test.js
● Test suite failed to run

Failed to connect to runtime, please make sure the project is running

Test Suites: 1 failed, 1 total
Tests: 0 total
Time: 240.43 s
```

推测原因:普通 `uni build -p mp-weixin` 产物没有经过 `uni-automator` compile 阶段注入自动化 runtime,因此小程序 runtime 不会回连测试进程。也就是说,`compile:false` 不能直接替代 `uni-automator` 的编译注入流程。

## 失败路径 3:混入 uni-test mp-weixin 默认链路时 program 不存在

项目普通测试使用:

```bash
npm run test:mp-weixin
```

脚本:

```json
{
"test:mp-weixin": "cross-env HBUILDERX_CLI_PATH=D:/ProgramFiles/HBuilderX.4.87.2025121004/HBuilderX/cli.exe uni-test mp-weixin"
}
```

普通测试可以成功运行。但如果把 `.e2e.test.js` 混入该链路,E2E 文件会失败:

```text
ReferenceError: program is not defined

at src/pages/__tests__/task/pages/task-page-smoke.e2e.test.js:6:5
```

说明 `uni-test mp-weixin` 默认执行该测试文件时,并未给它注入 `program` 全局对象。

如果按官方 `jest.config.js` 示例在主配置中全局加入:

```js
testEnvironment: '@dcloudio/uni-automator/dist/environment.js'
```

则所有测试 suite 均在初始化阶段失败:

```text
Program init failed

Test Suites: 95 failed, 95 total
Tests: 0 total
```

## 当前判断

当前问题不像是业务测试代码问题,也不像是 `jest` 或 `jest-environment-node` 版本超出官方限制。

更像是:

```text
@dcloudio/uni-automator 在 Windows 下初始化 mp-weixin program 时,对 npm.cmd 的 spawn 方式存在兼容问题。
```

同时:

```text
compile:false + 普通 uni build 产物不能替代 uni-automator compile,因为缺少自动化 runtime 注入。
```

## 期望行为

在 Windows 下,使用官方 CLI 项目自动化测试文档中的方式运行 mp-weixin 自动化测试时,应能正常初始化 `program`:

```bash
cross-env UNI_PLATFORM=mp-weixin jest -i
```

测试代码应能正常执行:

```js
page = await program.reLaunch('/pages/task/index')
const root = await page.$('.task-page-e2e')
```

## 希望官方确认的问题

1. 当前版本 `@dcloudio/uni-automator` 是否支持 Windows 下直接 `child_process.spawn('npm.cmd', ...)`?
2. 如果 Windows 下需要通过 `cmd.exe /c npm.cmd ...` 启动 npm script,是否可以在 `uni-automator` 内部兼容?
3. `compile:false` 是否预期只能连接已经包含 automator runtime 的产物,而不能直接使用普通 `uni build -p mp-weixin` 产物?
4. `uni-test mp-weixin` / HBuilderX `uniapp.test` 链路是否支持需要 `program` 的页面级 E2E 测试?如果支持,推荐配置方式是什么?
5. 如果 `@dcloudio/uni-automator` 接受 PR,请问对应源码仓库和源码文件路径在哪里?当前 npm 包里只能看到压缩后的 `dist/index.js`。

## 可能的修复方向建议

如果官方确认是 Windows spawn `.cmd` 兼容问题,可以考虑在 `uni-automator` 内部编译阶段将 Windows 下的 npm script 启动从:

```js
child_process.spawn('npm.cmd', ['run', scriptName, '--', ...args], options)
```

调整为:

```js
child_process.spawn('cmd.exe', ['/c', 'npm.cmd', 'run', scriptName, '--', ...args], options)
```

或者提供一个可配置项,例如:

```text
UNI_NPM_RUNNER
UNI_NPM_RUNNER_ARGS
```

允许 Windows 用户显式指定 npm runner。

## 附:当前普通测试能力说明

当前项目的普通微信小程序测试链路并非完全不可用。

```bash
npm run test:mp-weixin
```

可以运行普通模块/结构类测试,主要覆盖:

- 页面 wiring
- API module
- mock request wrapper
- 路由配置
- 字段契约
- 源码结构约束
- 部分组件结构回归

当前阻塞的是需要 `program` 的真实页面 E2E 自动化测试。

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.