bk-press 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +244 -0
- package/lib/command/index.js +19 -0
- package/lib/command/init.js +177 -0
- package/lib/command/login.js +109 -0
- package/lib/command/run.js +28 -0
- package/lib/config.js +76 -0
- package/lib/index.js +15 -0
- package/lib/util/file.js +151 -0
- package/lib/util/index.js +17 -0
- package/lib/util/pkg.js +28 -0
- package/package.json +41 -0
- package/src/command/index.ts +3 -0
- package/src/command/init.ts +202 -0
- package/src/command/login.ts +133 -0
- package/src/command/run.ts +27 -0
- package/src/config.ts +87 -0
- package/src/index.ts +19 -0
- package/src/util/file.ts +152 -0
- package/src/util/index.ts +1 -0
- package/tsconfig.json +15 -0
package/README.md
ADDED
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
# bk-press
|
|
2
|
+
|
|
3
|
+
一个用于 Cypress 自动化测试的 CLI 工具,帮助快速初始化和运行自动化测试。
|
|
4
|
+
|
|
5
|
+
## 📋 目录
|
|
6
|
+
|
|
7
|
+
- [安装](#安装)
|
|
8
|
+
- [快速开始](#快速开始)
|
|
9
|
+
- [配置文件](#配置文件)
|
|
10
|
+
- [项目结构](#项目结构)
|
|
11
|
+
- [使用示例](#使用示例)
|
|
12
|
+
- [常见问题](#常见问题)
|
|
13
|
+
|
|
14
|
+
## 安装
|
|
15
|
+
|
|
16
|
+
### 全局安装
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install -g bkpress
|
|
20
|
+
# 或
|
|
21
|
+
yarn global add bkpress
|
|
22
|
+
# 或
|
|
23
|
+
pnpm add -g bkpress
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### 本地安装
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npm install bkpress
|
|
30
|
+
# 或
|
|
31
|
+
yarn add bkpress
|
|
32
|
+
# 或
|
|
33
|
+
pnpm add bkpress
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## 快速开始
|
|
37
|
+
|
|
38
|
+
1. **初始化 Cypress 测试环境**
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
bkpress init
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
2. **配置环境变量**
|
|
45
|
+
编辑 `cypress.env.json` 文件,设置开发环境和本地环境的 URL:
|
|
46
|
+
|
|
47
|
+
```json
|
|
48
|
+
{
|
|
49
|
+
"DEV_URL": "https://your-dev-domain.com",
|
|
50
|
+
"LOCAL_URL": "http://localhost:8080",
|
|
51
|
+
"COOKIE_DOMAIN": "your-domain.com"
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
3. **登录并保存 Cookie**
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
bkpress login
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
4. **运行自动化测试**
|
|
62
|
+
```bash
|
|
63
|
+
bkpress run
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## 配置文件
|
|
67
|
+
|
|
68
|
+
### cypress.env.json
|
|
69
|
+
|
|
70
|
+
环境变量配置文件,用于存储测试环境相关的配置和 Cookie。
|
|
71
|
+
|
|
72
|
+
**初始配置:**
|
|
73
|
+
|
|
74
|
+
```json
|
|
75
|
+
{
|
|
76
|
+
"DEV_URL": "https://your-dev-domain.com",
|
|
77
|
+
"LOCAL_URL": "http://localhost:8080",
|
|
78
|
+
"COOKIE_DOMAIN": "your-domain.com"
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
**登录后的配置:**
|
|
83
|
+
登录命令会自动将 Cookie 添加到配置文件中:
|
|
84
|
+
|
|
85
|
+
```json
|
|
86
|
+
{
|
|
87
|
+
"DEV_URL": "https://your-dev-domain.com",
|
|
88
|
+
"LOCAL_URL": "http://localhost:8080",
|
|
89
|
+
"COOKIE_DOMAIN": "your-domain.com",
|
|
90
|
+
"sessionId": "xxx",
|
|
91
|
+
"token": "xxx"
|
|
92
|
+
// ... 其他 Cookie
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
**配置说明:**
|
|
97
|
+
|
|
98
|
+
- `DEV_URL`: 开发环境 URL,用于登录获取 Cookie
|
|
99
|
+
- `LOCAL_URL`: 本地开发服务器 URL,用于运行测试
|
|
100
|
+
- `COOKIE_DOMAIN`: Cookie 的域名,用于设置 Cookie 的作用域
|
|
101
|
+
|
|
102
|
+
### cypress.config.ts
|
|
103
|
+
|
|
104
|
+
Cypress 主配置文件,由 `init` 命令自动创建。
|
|
105
|
+
|
|
106
|
+
**默认配置:**
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
import { defineConfig } from "cypress";
|
|
110
|
+
|
|
111
|
+
export default defineConfig({
|
|
112
|
+
e2e: {},
|
|
113
|
+
});
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
可以根据项目需求自定义配置。
|
|
117
|
+
|
|
118
|
+
## 项目结构
|
|
119
|
+
|
|
120
|
+
初始化后的项目结构:
|
|
121
|
+
|
|
122
|
+
```
|
|
123
|
+
your-project/
|
|
124
|
+
├── cypress/
|
|
125
|
+
│ ├── e2e/ # 端到端测试用例
|
|
126
|
+
│ ├── fixtures/ # 测试数据
|
|
127
|
+
│ ├── support/ # 支持文件
|
|
128
|
+
│ │ ├── commands.ts # 自定义命令
|
|
129
|
+
│ │ ├── e2e.ts # E2E 测试支持
|
|
130
|
+
│ │ └── login.ts # 登录命令(由 login 命令生成)
|
|
131
|
+
│ └── ...
|
|
132
|
+
├── cypress.config.ts # Cypress 配置文件
|
|
133
|
+
├── cypress.env.json # 环境变量配置(包含 Cookie)
|
|
134
|
+
└── package.json # 包含测试脚本
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## 使用示例
|
|
138
|
+
|
|
139
|
+
### 完整工作流程
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
# 1. 初始化项目
|
|
143
|
+
bkpress init
|
|
144
|
+
|
|
145
|
+
# 2. 编辑 cypress.env.json,设置环境 URL
|
|
146
|
+
# {
|
|
147
|
+
# "DEV_URL": "https://dev.example.com",
|
|
148
|
+
# "LOCAL_URL": "http://localhost:3000",
|
|
149
|
+
# "COOKIE_DOMAIN": "example.com"
|
|
150
|
+
# }
|
|
151
|
+
|
|
152
|
+
# 3. 登录并保存 Cookie
|
|
153
|
+
bkpress login
|
|
154
|
+
# 在浏览器中完成登录,然后按回车
|
|
155
|
+
|
|
156
|
+
# 4. 运行测试
|
|
157
|
+
bkpress run
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### 在测试用例中使用登录
|
|
161
|
+
|
|
162
|
+
```typescript
|
|
163
|
+
// cypress/e2e/example.cy.ts
|
|
164
|
+
describe("用户功能测试", () => {
|
|
165
|
+
beforeEach(() => {
|
|
166
|
+
// 使用自动生成的登录命令
|
|
167
|
+
cy.login();
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it("应该显示用户信息", () => {
|
|
171
|
+
cy.visit("/profile");
|
|
172
|
+
cy.contains("用户信息").should("be.visible");
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### 手动运行测试
|
|
178
|
+
|
|
179
|
+
除了使用 `bkpress run`,你也可以使用 `init` 命令添加的 npm scripts:
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
# 打开 Cypress 交互式测试运行器
|
|
183
|
+
npm run e2e-dev
|
|
184
|
+
|
|
185
|
+
# 在无头模式下运行所有测试
|
|
186
|
+
npm run e2e
|
|
187
|
+
|
|
188
|
+
# 运行组件测试
|
|
189
|
+
npm run component-test
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
## 常见问题
|
|
193
|
+
|
|
194
|
+
### 1. init 命令失败:无法克隆模板文件
|
|
195
|
+
|
|
196
|
+
**问题:** 提示 "克隆模板文件失败"
|
|
197
|
+
|
|
198
|
+
**解决方案:**
|
|
199
|
+
|
|
200
|
+
- 确保已安装 Git
|
|
201
|
+
- 确保有访问模板仓库的权限(SSH 密钥配置正确)
|
|
202
|
+
- 检查网络连接
|
|
203
|
+
|
|
204
|
+
### 2. login 命令失败:DEV_URL 配置不存在
|
|
205
|
+
|
|
206
|
+
**问题:** 提示 "cypress.env.json 文件中 DEV_URL 配置不存在"
|
|
207
|
+
|
|
208
|
+
**解决方案:**
|
|
209
|
+
|
|
210
|
+
- 确保已运行 `bkpress init` 初始化项目
|
|
211
|
+
- 检查 `cypress.env.json` 文件是否存在
|
|
212
|
+
- 确保文件中包含 `DEV_URL` 配置项
|
|
213
|
+
|
|
214
|
+
### 3. run 命令失败:无法启动服务器
|
|
215
|
+
|
|
216
|
+
**问题:** 提示 "运行自动化测试失败"
|
|
217
|
+
|
|
218
|
+
**解决方案:**
|
|
219
|
+
|
|
220
|
+
- 确保 `package.json` 中有 `dev` 脚本
|
|
221
|
+
- 检查 `LOCAL_URL` 配置是否正确
|
|
222
|
+
- 确保端口没有被占用
|
|
223
|
+
- 检查开发服务器是否能正常启动
|
|
224
|
+
|
|
225
|
+
### 4. TypeScript 类型错误
|
|
226
|
+
|
|
227
|
+
**问题:** Cypress 相关的 TypeScript 类型未识别
|
|
228
|
+
|
|
229
|
+
**解决方案:**
|
|
230
|
+
|
|
231
|
+
- 确保已运行 `bkpress init`(会自动配置 TypeScript)
|
|
232
|
+
- 检查 `tsconfig.json` 中是否包含 `cypress` 类型
|
|
233
|
+
- 重启 IDE 或 TypeScript 服务器
|
|
234
|
+
|
|
235
|
+
### 5. Cookie 未正确保存
|
|
236
|
+
|
|
237
|
+
**问题:** 登录后 Cookie 未保存或测试中无法使用
|
|
238
|
+
|
|
239
|
+
**解决方案:**
|
|
240
|
+
|
|
241
|
+
- 确保在浏览器中完成登录后再按回车
|
|
242
|
+
- 检查 `cypress.env.json` 文件是否包含 Cookie
|
|
243
|
+
- 检查 `cypress/support/login.ts` 文件是否正确生成
|
|
244
|
+
- 确保 `cypress/support/e2e.ts` 中导入了 `./login`
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./login"), exports);
|
|
18
|
+
__exportStar(require("./init"), exports);
|
|
19
|
+
__exportStar(require("./run"), exports);
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.init = init;
|
|
7
|
+
const child_process_1 = require("child_process");
|
|
8
|
+
const which_pm_runs_1 = __importDefault(require("which-pm-runs"));
|
|
9
|
+
const path_1 = __importDefault(require("path"));
|
|
10
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
11
|
+
const util_1 = require("../util");
|
|
12
|
+
async function init() {
|
|
13
|
+
console.log("开始初始化Cypress自动化测试...");
|
|
14
|
+
console.log("安装相关依赖...");
|
|
15
|
+
await installDependencies();
|
|
16
|
+
console.log("添加类型到 tsconfig.json 文件...");
|
|
17
|
+
await addCypressTypesToTsconfigJsonFile();
|
|
18
|
+
console.log("添加命令到 package.json...");
|
|
19
|
+
await addScriptsToPackageJson();
|
|
20
|
+
console.log("创建 cypress 文件夹并拉取模板用例文件...");
|
|
21
|
+
await addTemplateFiles();
|
|
22
|
+
console.log("创建 cypress.config.ts 配置文件...");
|
|
23
|
+
await addCypressConfigFile();
|
|
24
|
+
console.log("创建 cypress.env.json 配置文件...");
|
|
25
|
+
await addCypressEnvJsonFile();
|
|
26
|
+
console.log("添加忽略文件到 .gitignore 文件...");
|
|
27
|
+
await addIgnoreToGitignoreFile();
|
|
28
|
+
console.log("Cypress自动化测试初始化完成!");
|
|
29
|
+
}
|
|
30
|
+
// 安装自动化测试相关依赖
|
|
31
|
+
function installDependencies() {
|
|
32
|
+
return new Promise((resolve, reject) => {
|
|
33
|
+
const pkgTool = (0, which_pm_runs_1.default)()?.name || "npm";
|
|
34
|
+
const installPrefix = pkgTool === "npm" ? `${pkgTool} install -D` : `${pkgTool} add -D`;
|
|
35
|
+
(0, child_process_1.exec)(`${installPrefix} cypress cypress-parallel cypress-multi-reporters start-server-and-test`, {
|
|
36
|
+
cwd: process.cwd(),
|
|
37
|
+
}, (error, stdout, stderr) => {
|
|
38
|
+
if (error) {
|
|
39
|
+
console.error("依赖安装失败:", error);
|
|
40
|
+
reject(error);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
console.log("依赖安装成功!");
|
|
44
|
+
resolve(stdout);
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
// 添加脚本到 package.json
|
|
49
|
+
async function addScriptsToPackageJson() {
|
|
50
|
+
const packageJsonFile = path_1.default.join(process.cwd(), "package.json");
|
|
51
|
+
const packageJson = (await (0, util_1.readFile)(packageJsonFile));
|
|
52
|
+
packageJson.scripts["e2e-dev"] = "npx cypress open";
|
|
53
|
+
packageJson.scripts["e2e"] = "npx cypress run --browser chrome --headless";
|
|
54
|
+
packageJson.scripts["component-test"] =
|
|
55
|
+
"npx cypress run --component --browser chrome";
|
|
56
|
+
await (0, util_1.writeFile)(packageJsonFile, JSON.stringify(packageJson, null, 2));
|
|
57
|
+
}
|
|
58
|
+
// 创建 cypress 文件夹并拉取模板文件
|
|
59
|
+
async function addTemplateFiles() {
|
|
60
|
+
const isTsProject = await checkIsTsProject();
|
|
61
|
+
console.log("当前项目是否是 TypeScript 项目: ", isTsProject);
|
|
62
|
+
await createCypressFolder();
|
|
63
|
+
await cloneTemplateFiles(isTsProject);
|
|
64
|
+
await copyTemplateFiles();
|
|
65
|
+
await deleteGitFolder();
|
|
66
|
+
}
|
|
67
|
+
// 创建 cypress 文件夹
|
|
68
|
+
async function createCypressFolder() {
|
|
69
|
+
const cypressFolder = path_1.default.join(process.cwd(), "cypress");
|
|
70
|
+
await (0, util_1.createFolder)(cypressFolder);
|
|
71
|
+
}
|
|
72
|
+
async function checkIsTsProject() {
|
|
73
|
+
const tsconfigJsonFile = path_1.default.join(process.cwd(), "tsconfig.json");
|
|
74
|
+
const isTsProject = await (0, util_1.isFileOrFolderExist)(tsconfigJsonFile);
|
|
75
|
+
return isTsProject;
|
|
76
|
+
}
|
|
77
|
+
// 克隆模板文件
|
|
78
|
+
function cloneTemplateFiles(isTsProject = true) {
|
|
79
|
+
const targetDir = path_1.default.join(process.cwd(), "cypress");
|
|
80
|
+
const repoListMap = {
|
|
81
|
+
ts: "git@github.com:jinquantianxia/cypress-template-ts.git",
|
|
82
|
+
js: "git@github.com:jinquantianxia/cypress-template-js.git",
|
|
83
|
+
};
|
|
84
|
+
const repoUrl = repoListMap[isTsProject ? "ts" : "js"];
|
|
85
|
+
return new Promise((resolve, reject) => {
|
|
86
|
+
(0, child_process_1.exec)(`git clone ${repoUrl}`, {
|
|
87
|
+
cwd: targetDir,
|
|
88
|
+
}, (error, stdout, stderr) => {
|
|
89
|
+
if (error) {
|
|
90
|
+
console.error("克隆模板文件失败:", error);
|
|
91
|
+
reject(stderr);
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
resolve(stdout);
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
// 删除 cypress 文件夹中的 .git 文件夹
|
|
99
|
+
async function deleteGitFolder() {
|
|
100
|
+
const targetDir = path_1.default.join(process.cwd(), "cypress", ".git");
|
|
101
|
+
await (0, util_1.deleteFolder)(targetDir);
|
|
102
|
+
}
|
|
103
|
+
// 复制模板文件到 cypress 文件夹
|
|
104
|
+
async function copyTemplateFiles() {
|
|
105
|
+
const sourceDir = path_1.default.join(process.cwd(), "cypress", "cypress-template-ts");
|
|
106
|
+
const targetDir = path_1.default.join(process.cwd(), "cypress");
|
|
107
|
+
// 确保目标目录存在
|
|
108
|
+
await fs_extra_1.default.ensureDir(targetDir);
|
|
109
|
+
// 复制源目录下所有内容到目标目录
|
|
110
|
+
await fs_extra_1.default.copy(sourceDir, targetDir);
|
|
111
|
+
await (0, util_1.deleteFolder)(sourceDir);
|
|
112
|
+
}
|
|
113
|
+
// 添加 cypress.config.ts 文件
|
|
114
|
+
async function addCypressConfigFile() {
|
|
115
|
+
const cypressConfigFile = path_1.default.join(process.cwd(), "cypress.config.ts");
|
|
116
|
+
const fileContent = `import { defineConfig } from 'cypress';
|
|
117
|
+
|
|
118
|
+
export default defineConfig({
|
|
119
|
+
e2e: {}
|
|
120
|
+
});`;
|
|
121
|
+
await (0, util_1.writeFile)(cypressConfigFile, fileContent);
|
|
122
|
+
}
|
|
123
|
+
// 添加 cypress.env.json 文件
|
|
124
|
+
async function addCypressEnvJsonFile() {
|
|
125
|
+
const cypressEnvFile = path_1.default.join(process.cwd(), "cypress.env.json");
|
|
126
|
+
const isExist = await (0, util_1.isFileOrFolderExist)(cypressEnvFile);
|
|
127
|
+
if (isExist) {
|
|
128
|
+
console.log("cypress.env.json 文件已存在,跳过创建");
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
const fileContent = `{
|
|
132
|
+
"DEV_URL": "",
|
|
133
|
+
"LOCAL_URL": "",
|
|
134
|
+
"COOKIE_DOMAIN": ""
|
|
135
|
+
}
|
|
136
|
+
`;
|
|
137
|
+
await (0, util_1.writeFile)(cypressEnvFile, fileContent);
|
|
138
|
+
console.log("cypress.env.json 文件创建成功");
|
|
139
|
+
}
|
|
140
|
+
// 添加忽略文件到 .gitignore 文件
|
|
141
|
+
async function addIgnoreToGitignoreFile() {
|
|
142
|
+
const gitignoreFile = path_1.default.join(process.cwd(), ".gitignore");
|
|
143
|
+
const oldFileContent = await (0, util_1.readFile)(gitignoreFile, false);
|
|
144
|
+
const fileContent = `${oldFileContent}\n
|
|
145
|
+
cypress/parallel-weights.json
|
|
146
|
+
cypress/screenshots
|
|
147
|
+
runner-results/
|
|
148
|
+
cypress.env.json
|
|
149
|
+
multi-reporter-config.json`;
|
|
150
|
+
await (0, util_1.writeFile)(gitignoreFile, fileContent);
|
|
151
|
+
}
|
|
152
|
+
// 添加 cypress 类型到 tsconfig.json 文件
|
|
153
|
+
async function addCypressTypesToTsconfigJsonFile() {
|
|
154
|
+
const tsconfigJsonFile = path_1.default.join(process.cwd(), "tsconfig.json");
|
|
155
|
+
const tsconfigJson = (await (0, util_1.readFile)(tsconfigJsonFile));
|
|
156
|
+
if (tsconfigJson.include) {
|
|
157
|
+
tsconfigJson.include.push("cypress/**/*");
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
tsconfigJson.include = ["cypress/**/*"];
|
|
161
|
+
}
|
|
162
|
+
if (tsconfigJson.compilerOptions) {
|
|
163
|
+
if (tsconfigJson.compilerOptions.types) {
|
|
164
|
+
tsconfigJson.compilerOptions.types.push("cypress");
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
tsconfigJson.compilerOptions.types = ["cypress"];
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
tsconfigJson.compilerOptions = {
|
|
172
|
+
types: ["cypress"],
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
const fileContent = JSON.stringify(tsconfigJson, null, 2);
|
|
176
|
+
await (0, util_1.writeFile)(tsconfigJsonFile, fileContent);
|
|
177
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.login = login;
|
|
7
|
+
exports.readCypressEnvConfig = readCypressEnvConfig;
|
|
8
|
+
const puppeteer_1 = __importDefault(require("puppeteer"));
|
|
9
|
+
const path_1 = __importDefault(require("path"));
|
|
10
|
+
const util_1 = require("../util");
|
|
11
|
+
async function login() {
|
|
12
|
+
const config = await readCypressEnvConfig();
|
|
13
|
+
if (!config.devUrl) {
|
|
14
|
+
throw new Error("cypress.env.json 文件中 DEV_URL 配置不存在");
|
|
15
|
+
}
|
|
16
|
+
const browser = await puppeteer_1.default.launch({
|
|
17
|
+
headless: false,
|
|
18
|
+
args: ["--ignore-certificate-errors"],
|
|
19
|
+
});
|
|
20
|
+
console.log("浏览器启动成功!");
|
|
21
|
+
const page = await browser.newPage();
|
|
22
|
+
console.log(`${config.devUrl}开发环境页面打开成功!`);
|
|
23
|
+
await page.goto(config.devUrl);
|
|
24
|
+
console.log("登录后不要手动关闭浏览器,完成后回到终端按回车键继续...");
|
|
25
|
+
await waitForEnter(); // 等待用户在终端输入回车
|
|
26
|
+
// 4. 登录成功后,获取当前页面的所有 Cookie
|
|
27
|
+
const cookies = await page.cookies();
|
|
28
|
+
await writeCookiesToCypressEnvFile(cookies, config.domainMap);
|
|
29
|
+
await writeCookiesToCypressCommandFiles(cookies, config.domainMap);
|
|
30
|
+
// 5. 将 Cookie 写入本地文件(例如 cookies.json)
|
|
31
|
+
// fs.writeFileSync("cookies.json", JSON.stringify(cookies, null, 2));
|
|
32
|
+
console.log("登录成功,Cookie 已保存至 cypress 命令文件");
|
|
33
|
+
await browser.close(); // 关闭浏览器
|
|
34
|
+
}
|
|
35
|
+
async function readCypressEnvConfig() {
|
|
36
|
+
const cypressEnvFile = path_1.default.join(process.cwd(), "cypress.env.json");
|
|
37
|
+
const isExist = await (0, util_1.isFileOrFolderExist)(cypressEnvFile);
|
|
38
|
+
if (!isExist) {
|
|
39
|
+
throw new Error("cypress.env.json 文件不存在");
|
|
40
|
+
}
|
|
41
|
+
const envJson = (await (0, util_1.readFile)(cypressEnvFile));
|
|
42
|
+
const domainMap = {};
|
|
43
|
+
Object.entries(envJson).forEach(([key, value]) => {
|
|
44
|
+
if (value.includes(".com")) {
|
|
45
|
+
domainMap[value] = key;
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
return {
|
|
49
|
+
devUrl: envJson.DEV_URL || "",
|
|
50
|
+
localUrl: envJson.LOCAL_URL || "",
|
|
51
|
+
domainMap,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
async function writeCookiesToCypressEnvFile(cookies, domainMap) {
|
|
55
|
+
const cypressEnvFile = path_1.default.join(process.cwd(), "cypress.env.json");
|
|
56
|
+
const isExist = await (0, util_1.isFileOrFolderExist)(cypressEnvFile);
|
|
57
|
+
if (!isExist) {
|
|
58
|
+
throw new Error("cypress.env.json 文件不存在");
|
|
59
|
+
}
|
|
60
|
+
const envJson = (await (0, util_1.readFile)(cypressEnvFile));
|
|
61
|
+
cookies.forEach((cookie) => {
|
|
62
|
+
envJson[cookie.name] = cookie.value;
|
|
63
|
+
});
|
|
64
|
+
Object.entries(domainMap).forEach(([key, value]) => {
|
|
65
|
+
envJson[value] = key;
|
|
66
|
+
});
|
|
67
|
+
await (0, util_1.writeFile)(cypressEnvFile, JSON.stringify(envJson, null, 2));
|
|
68
|
+
console.log("cypress.env.json 文件更新成功");
|
|
69
|
+
}
|
|
70
|
+
async function writeCookiesToCypressCommandFiles(cookies, domainMap) {
|
|
71
|
+
const cypressCommandFile = path_1.default.join(process.cwd(), "cypress", "support", "login.ts");
|
|
72
|
+
const isLoginFileExist = await (0, util_1.isFileOrFolderExist)(cypressCommandFile);
|
|
73
|
+
if (!isLoginFileExist) {
|
|
74
|
+
const fileContent = `
|
|
75
|
+
Cypress.Commands.add('login' as any, () => {
|
|
76
|
+
// 缓存登录会话
|
|
77
|
+
cy.session('login', () => {
|
|
78
|
+
${cookies
|
|
79
|
+
.map((cookie) => `cy.setCookie('${cookie.name}', Cypress.env('${cookie.name}'), { path: '${cookie.path}', domain: Cypress.env('${domainMap[cookie.domain]}') });`)
|
|
80
|
+
.join("\n ")}
|
|
81
|
+
});
|
|
82
|
+
});`;
|
|
83
|
+
await (0, util_1.writeFile)(cypressCommandFile, fileContent);
|
|
84
|
+
console.log("cypress/support/login.ts 文件创建成功");
|
|
85
|
+
}
|
|
86
|
+
console.log("cypress/support/login.ts 文件已存在,跳过创建");
|
|
87
|
+
const cypressE2eFile = path_1.default.join(process.cwd(), "cypress", "support", "e2e.ts");
|
|
88
|
+
const e2eFileContent = (await (0, util_1.readFile)(cypressE2eFile, false));
|
|
89
|
+
if (e2eFileContent.includes("./login")) {
|
|
90
|
+
console.log("cypress/support/e2e.ts 文件已存在 ./login 导入,跳过更新");
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
const newE2eFileContent = e2eFileContent + "\nimport './login';";
|
|
94
|
+
await (0, util_1.writeFile)(cypressE2eFile, newE2eFileContent);
|
|
95
|
+
console.log("cypress/support/e2e.ts 文件导入 login.ts 更新成功");
|
|
96
|
+
}
|
|
97
|
+
// 一个简单的等待用户输入回车键的函数
|
|
98
|
+
function waitForEnter() {
|
|
99
|
+
return new Promise((resolve) => {
|
|
100
|
+
const stdin = process.stdin;
|
|
101
|
+
stdin.setRawMode(true);
|
|
102
|
+
stdin.resume();
|
|
103
|
+
stdin.once("data", () => {
|
|
104
|
+
stdin.setRawMode(false);
|
|
105
|
+
stdin.pause();
|
|
106
|
+
resolve(true);
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.run = run;
|
|
7
|
+
const child_process_1 = require("child_process");
|
|
8
|
+
const login_1 = require("./login");
|
|
9
|
+
const os_1 = __importDefault(require("os"));
|
|
10
|
+
async function run() {
|
|
11
|
+
console.log("开始运行自动化测试...");
|
|
12
|
+
const config = await (0, login_1.readCypressEnvConfig)();
|
|
13
|
+
const cpuCount = os_1.default.cpus().length;
|
|
14
|
+
console.log("当前CPU核心数: ", cpuCount, ", 设置测试并发数: ", cpuCount);
|
|
15
|
+
return new Promise((resolve, reject) => {
|
|
16
|
+
(0, child_process_1.exec)(`start-server-and-test dev ${config.localUrl} 'cypress-parallel -s e2e -t ${cpuCount} -d cypress/e2e'`, {
|
|
17
|
+
cwd: process.cwd(),
|
|
18
|
+
}, (error, stdout, stderr) => {
|
|
19
|
+
if (error) {
|
|
20
|
+
console.error("运行自动化测试失败:", error);
|
|
21
|
+
reject(stderr);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
console.log("自动化测试运行成功!");
|
|
25
|
+
resolve(stdout);
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
}
|
package/lib/config.js
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.loadConfig = loadConfig;
|
|
4
|
+
exports.getDefaultConfig = getDefaultConfig;
|
|
5
|
+
exports.mergeConfigs = mergeConfigs;
|
|
6
|
+
// src/config.ts
|
|
7
|
+
const fs_1 = require("fs");
|
|
8
|
+
const path_1 = require("path");
|
|
9
|
+
// 配置读取器函数
|
|
10
|
+
function loadConfig() {
|
|
11
|
+
// 定义可能的配置文件名
|
|
12
|
+
const fileName = "bkpress.json";
|
|
13
|
+
let configPath = null;
|
|
14
|
+
let configContent = null;
|
|
15
|
+
// 1. 在项目目录中查找配置文件
|
|
16
|
+
const potentialPath = (0, path_1.join)(process.cwd(), fileName);
|
|
17
|
+
if ((0, fs_1.existsSync)(potentialPath)) {
|
|
18
|
+
configPath = potentialPath;
|
|
19
|
+
}
|
|
20
|
+
// 也支持检查用户主目录下的全局配置(可选)
|
|
21
|
+
// const globalConfigPath = join(homedir(), '.my-cli.config.json');
|
|
22
|
+
// if (!configPath && existsSync(globalConfigPath)) {
|
|
23
|
+
// configPath = globalConfigPath;
|
|
24
|
+
// }
|
|
25
|
+
if (!configPath) {
|
|
26
|
+
console.log("未找到脚手架配置文件,将使用默认配置或命令行参数。");
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
try {
|
|
30
|
+
// 2. 读取文件内容
|
|
31
|
+
configContent = (0, fs_1.readFileSync)(configPath, "utf-8");
|
|
32
|
+
// 3. 根据文件扩展名解析内容
|
|
33
|
+
if (configPath.endsWith(".json") || configPath.endsWith(".my-clirc")) {
|
|
34
|
+
// 解析 JSON,可去除注释(需自行处理或使用支持注释的JSON解析器)
|
|
35
|
+
return JSON.parse(configContent);
|
|
36
|
+
}
|
|
37
|
+
else if (configPath.endsWith(".ts")) {
|
|
38
|
+
// 对于 TS 文件,可能需要动态导入(需额外处理,复杂度较高,通常更推荐 JSON 或静态配置)
|
|
39
|
+
console.warn("TypeScript 配置文件支持可能需要额外编译步骤,建议使用 JSON 格式。");
|
|
40
|
+
// 示例性实现,实际中需谨慎处理
|
|
41
|
+
// 可能需要先使用 ts-node 或将其编译成 JS 再 require
|
|
42
|
+
// const tsConfig = require(configPath);
|
|
43
|
+
// return tsConfig;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
console.error(`读取或解析配置文件失败 (${configPath}):`, error);
|
|
48
|
+
}
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
// 获取默认配置
|
|
52
|
+
function getDefaultConfig() {
|
|
53
|
+
return {
|
|
54
|
+
local_url: "http://localhost:8080",
|
|
55
|
+
api_base: "/api",
|
|
56
|
+
build_output: "dist",
|
|
57
|
+
features: {
|
|
58
|
+
typescript: false,
|
|
59
|
+
lint: false,
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
// 合并配置(用户配置覆盖默认配置)
|
|
64
|
+
function mergeConfigs(defaultCfg, userCfg) {
|
|
65
|
+
if (!userCfg)
|
|
66
|
+
return defaultCfg;
|
|
67
|
+
// 简单的深度合并,对于复杂结构可使用 lodash.merge 等库
|
|
68
|
+
return {
|
|
69
|
+
...defaultCfg,
|
|
70
|
+
...userCfg,
|
|
71
|
+
features: {
|
|
72
|
+
// ...defaultCfg.features,
|
|
73
|
+
// ...userCfg.features,
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
}
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
const commander_1 = require("commander");
|
|
5
|
+
const command_1 = require("./command");
|
|
6
|
+
const program = new commander_1.Command();
|
|
7
|
+
program
|
|
8
|
+
.name("bk-press")
|
|
9
|
+
.description("一个用于 Cypress 自动化测试的 CLI 工具,帮助快速初始化和运行端到端测试")
|
|
10
|
+
.version("0.0.1");
|
|
11
|
+
program.command("init").action(command_1.init);
|
|
12
|
+
program.command("login").action(command_1.login);
|
|
13
|
+
// @ts-ignore
|
|
14
|
+
program.command("run").action(command_1.run);
|
|
15
|
+
program.parse(process.argv);
|