-
Notifications
You must be signed in to change notification settings - Fork 344
351 lines (312 loc) · 14.5 KB
/
test-e2e-pr.yml
File metadata and controls
351 lines (312 loc) · 14.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
name: E2E Test PR
run-name: E2E Test PR--${{ github.event.pull_request.title }}
on:
pull_request:
types: [opened, reopened, synchronize, edited]
# 并发控制,确保相同PR的工作流不会同时运行多个实例
concurrency:
group: ${{ github.workflow }}-${{ github.event.number || github.sha }}
cancel-in-progress: true
jobs:
detect-changed-files:
name: Detect Changed Files
runs-on: ubuntu-latest
outputs:
# 输出三个变量供后续任务使用
changed_components: ${{ steps.find-changed-components.outputs.changed_components }} # 自动检测到的变更组件
manual_components: ${{ steps.parse-title.outputs.manual_components }} # 从PR标题手动指定的组件
testclis: ${{ steps.parse-test-cli.outputs.testClis }} # 测试命令列表
steps:
# 检出代码,需要完整历史以便比较变更
- name: Checkout code
uses: actions/checkout@v3
with:
fetch-depth: 0 # 获取完整的git历史,用于检测文件变更
# 使用tj-actions/changed-files获取PR中所有变更的文件
- name: Get changed files
id: changed-files
uses: tj-actions/changed-files@v41
# 根据变更的文件自动识别受影响的组件
- name: Find changed components
id: find-changed-components
uses: actions/github-script@v6
with:
script: |
const changedFiles = '${{ steps.changed-files.outputs.all_changed_files }}'.split(' ').filter(file => file.trim() !== '');
console.log('变更的文件列表:', changedFiles);
// 增强组件路径匹配规则,确保能正确识别各种路径格式
const componentPathRules = [
// 匹配 packages/renderless/src/{组件名} 路径,支持更深层次的子目录
{
pattern: /^packages\/renderless\/src\/([^/]+)\//,
group: 1,
type: 'renderless'
},
// 匹配 packages/vue/src/{组件名} 路径,支持更深层次的子目录
{
pattern: /^packages\/vue\/src\/([^/]+)\//,
group: 1,
type: 'vue'
},
// 匹配示例目录下的组件路径 - 只匹配目录,不匹配package.json等文件
{
pattern: /^examples\/sites\/demos\/pc\/app\/([^/]+)\//,
group: 1,
type: 'demo'
},
// 匹配 packages/theme/src/{组件名} 样式路径
{
pattern: /^packages\/theme\/src\/([^/]+)\//,
group: 1,
type: 'theme'
}
];
// 定义子组件到父组件的映射关系
const subComponentMapping = {
'calendar-bar': 'calendar',
'carousel-item': 'carousel',
'cascader-menu': 'cascader',
'cascader-node': 'cascader',
'cascader-panel': 'cascader',
'checkbox-button': 'checkbox',
'checkbox-group': 'checkbox',
'col': 'layout',
'collapse-item': 'collapse',
'date-range': 'date-picker',
'date-table': 'date-picker',
'dropdown-item': 'dropdown',
'dropdown-menu': 'dropdown',
'form-item': 'form',
'grid-toolbar': 'grid',
'image-viewer': 'image',
'month-range': 'date-picker',
'month-table': 'date-picker',
'option': 'select',
'option-group': 'select',
'pager-item': 'pager',
'picker': 'date-picker',
'quarter-panel': 'date-picker',
'radio-button': 'radio',
'radio-group': 'radio',
'row': 'layout',
'select-dropdown': 'select',
'skeleton-item': 'skeleton',
'svgs': 'icon',
'tab-item': 'tabs',
'textarea': 'input',
'time-panel': 'time-picker',
'time-range': 'time-picker',
'time-spinner': 'time-picker',
'timeline-item': 'timeline',
'transfer-panel': 'transfer',
'tree-node': 'tree',
'upload': 'file-upload',
'upload-dragger': 'file-upload',
'upload-list': 'file-upload',
'year-range': 'date-picker',
'year-table': 'date-picker'
};
// 从文件路径中提取组件名称
const components = new Set();
const detectedComponents = {}; // 用于记录检测到的组件和相关文件
const unmatchedFiles = []; // 记录未匹配的文件
changedFiles.forEach(file => {
// 跳过工作流文件等非组件相关文件
if (file.startsWith('.github/') ||
file.includes('README') ||
file.includes('.md') ||
file.includes('theme-saas') || // 跳过theme-saas相关文件
file.includes('theme/src/common/')) { // 跳过组件通用样式文件
console.log(`跳过非测试相关文件: ${file}`);
return;
}
let matched = false;
for (const rule of componentPathRules) {
const match = file.match(rule.pattern);
if (match && match[rule.group]) {
matched = true;
let componentName = match[rule.group];
// 检查是否是需要映射的子组件
if (subComponentMapping[componentName]) {
console.log(`检测到子组件 ${componentName},映射到父组件 ${subComponentMapping[componentName]}`);
componentName = subComponentMapping[componentName];
}
// 记录找到的组件
components.add(componentName);
// 记录组件来源和相关文件,用于调试
if (!detectedComponents[componentName]) {
detectedComponents[componentName] = {
type: rule.type,
files: []
};
}
detectedComponents[componentName].files.push(file);
console.log(`成功匹配组件 ${componentName}: ${file} (类型: ${rule.type})`);
break; // 一个文件只匹配一个组件
}
}
// 检查theme目录中的特殊路径格式
if (!matched && file.includes('/theme/src/') && !file.includes('/common/')) {
// 尝试从theme路径中提取组件名
const themePath = file.split('/');
// 常见的组件样式文件命名模式
const possibleComponentNames = [];
for (let i = 0; i < themePath.length; i++) {
// 跳过明显不是组件名的路径部分
if (['src', 'lib', 'theme', 'packages'].includes(themePath[i])) {
continue;
}
// 检查是否像是组件名(小写字母,可能包含连字符)
if (/^[a-z][a-z0-9\-]*$/.test(themePath[i])) {
possibleComponentNames.push(themePath[i]);
}
}
if (possibleComponentNames.length > 0) {
// 使用最后一个可能的组件名(通常最具体)
let componentName = possibleComponentNames[possibleComponentNames.length - 1];
// 检查是否是需要映射的子组件
if (subComponentMapping[componentName]) {
console.log(`检测到子组件 ${componentName},映射到父组件 ${subComponentMapping[componentName]}`);
componentName = subComponentMapping[componentName];
}
components.add(componentName);
if (!detectedComponents[componentName]) {
detectedComponents[componentName] = {
type: 'theme',
files: []
};
}
detectedComponents[componentName].files.push(file);
console.log(`通过主题样式路径分析匹配到组件 ${componentName}: ${file}`);
matched = true;
}
}
// 记录未匹配到的文件,用于调试
if (!matched) {
unmatchedFiles.push(file);
console.log(`未能匹配组件: ${file}`);
}
});
// 打印详细的检测结果,方便调试
console.log('检测结果详情:', JSON.stringify(detectedComponents, null, 2));
console.log('未匹配文件列表:', unmatchedFiles);
// 构建E2E测试的组件过滤器表达式
if (components.size > 0) {
// 构建符合E2E测试命令需要的过滤表达式格式
const componentFilters = Array.from(components)
.map(comp => `"\\/app\\/${comp}\\/"`)
.join(' ');
console.log(`检测到变更的组件: ${Array.from(components).join(', ')}`);
core.setOutput('changed_components', componentFilters);
} else {
console.log('没有检测到变更的组件');
core.setOutput('changed_components', '');
}
# 从PR标题中解析手动指定的组件列表
- name: Parse Title for Manual Components
id: parse-title
uses: actions/github-script@v6
with:
script: |
const prTitle = context.payload.pull_request.title
// 匹配PR标题中的 [component1, component2] 格式
const regex = /\[(.*?)\]/
const matches = prTitle.match(regex)
if (matches && matches.length > 1 && matches[1]) {
// 处理和格式化手动指定的组件列表
let components = matches[1]
.split(',')
.map((c) => c.trim())
.filter((c) => /^[a-z\-\/]+$/.test(c)) // 确保组件名符合规范
.map((c) => `"\\/app\\/${c}\\/"`)
components = [...new Set(components)].join(' ') // 去重并转为字符串
core.setOutput('manual_components', components)
} else {
core.setOutput('manual_components', '')
}
# 当没有检测到任何组件时,生成警告提示
- name: Generate warning if no components detected
id: warning
if: ${{ steps.find-changed-components.outputs.changed_components == '' && steps.parse-title.outputs.manual_components == '' }}
run: |
cat << EOF > user-tip.txt
**[e2e-test-warn]**
没有检测到要测试的组件。
系统会自动检测PR中变更的组件文件,或者您可以在PR标题中使用[component1, component2]格式手动指定要测试的组件。
例如: "fix(vue-renderless): [action-menu, alert] fix xxx bug"
请确保您已阅读我们的[贡献指南](https://github.com/opentiny/tiny-vue/blob/dev/CONTRIBUTING.md)
EOF
echo "warning=true" >> $GITHUB_OUTPUT
# 上传警告信息作为工作流制品
- name: Upload warning
if: ${{ steps.warning.outputs.warning }}
uses: actions/upload-artifact@v4
with:
name: user-tip
path: user-tip.txt
retention-days: 1 # 保留1天
# 解析测试命令配置
- name: Parse Test Cli
id: parse-test-cli
uses: actions/github-script@v6
with:
script: |
// 从GitHub变量中获取测试命令列表,如果未配置则使用默认命令
const testClis = '${{ vars.PLAYWRIGHT_CLIS }}' ? '${{ vars.PLAYWRIGHT_CLIS }}'.split(',') : ['pnpm test:e2e3']
core.setOutput('testClis', JSON.stringify(testClis))
# PR测试任务,运行实际的E2E测试
pr-test:
name: PR E2E Test
needs: detect-changed-files # 依赖前一个任务的输出
runs-on: ubuntu-latest
# 只有当检测到变更组件或手动指定组件时才运行测试
if: ${{ needs.detect-changed-files.outputs.changed_components != '' || needs.detect-changed-files.outputs.manual_components != '' }}
strategy:
matrix:
testcli: ${{ fromJson(needs.detect-changed-files.outputs.testclis) }} # 使用矩阵策略运行多个测试命令
env:
# 合并自动检测和手动指定的组件列表
TEST_COMPONENTS: ${{ needs.detect-changed-files.outputs.changed_components }} ${{ needs.detect-changed-files.outputs.manual_components }}
steps:
# 检出代码
- uses: actions/checkout@v3
# 设置pnpm
- name: Setup pnpm
uses: pnpm/action-setup@v2
# 设置Node.js环境
- name: Setup node
uses: actions/setup-node@v3
with:
node-version: 20
# 缓存Playwright浏览器安装,加速工作流
- name: Cache Playwright Installation
uses: actions/cache@v3
with:
path: ~/.cache/ms-playwright
key: playwright-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}
# 获取pnpm缓存目录
- name: Get pnpm store directory
id: pnpm-cache
run: |
echo "pnpm_cache_dir=$(pnpm store path)" >> $GITHUB_OUTPUT
# 设置pnpm缓存
- uses: actions/cache@v3
name: Setup pnpm cache
with:
path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
# 安装依赖
- name: Install dependencies
run: pnpm i --no-frozen-lockfile
# 安装Playwright浏览器
- name: Install Playwright browsers
run: pnpm install:browser --with-deps chromium
# 显示要测试的组件列表,便于调试
- name: Show detected components
run: |
echo "Testing components: $TEST_COMPONENTS"
# 运行E2E测试
- name: E2E Test
run: ${{ matrix.testcli }} ${{ env.TEST_COMPONENTS }} --retries=1 --workers=2 --pass-with-no-tests # 带重试和并行工作进程,无测试时不报错