Skip to content

E2E Test PR--fix(utils): fix tooltip arrow safe padding #9084

E2E Test PR--fix(utils): fix tooltip arrow safe padding

E2E Test PR--fix(utils): fix tooltip arrow safe padding #9084

Workflow file for this run

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'
},
// 匹配示例目录下的组件路径
{
pattern: /^examples\/sites\/demos\/pc\/app\/([^/]+)/,
group: 1,
type: 'demo'
},
// 匹配 packages/theme/src/{组件名} 样式路径
{
pattern: /^packages\/theme\/src\/([^/]+)/,
group: 1,
type: 'theme'
}
];
// 定义子组件到父组件的映射关系
const subComponentMapping = {
'form-item': 'form',
'grid-toolbar': 'grid',
'calendar-bar': 'calendar',
'carousel-item': 'carousel',
'cascader-menu': 'cascader',
'cascader-node': 'cascader',
'cascader-panel': 'cascader',
'checkbox-button': 'checkbox',
'checkbox-group': 'checkbox',
'radio-button': 'radio',
'radio-group': 'radio',
'collapse-item': 'collapse',
'picker': 'date-picker',
'date-range': 'date-picker',
'date-table': 'date-picker',
'month-range': 'date-picker',
'month-table': 'date-picker',
'quarter-panel': 'date-picker',
'year-range': 'date-picker',
'year-table': 'date-picker',
'dropdown-item': 'dropdown',
'dropdown-menu': 'dropdown',
'image-viewer': 'image',
'option': 'select',
'option-group': 'select',
'select-dropdown': 'select',
'pager-item': 'pager',
'skeleton-item': 'skeleton',
'tab-item': 'tabs',
'time-range': 'time-picker',
'time-panel': 'time-picker',
'time-spinner': 'time-picker',
'timeline-item': 'timeline',
'tree-node': 'tree',
'transfer-panel': 'transfer',
'col': 'layout',
'row': 'layout',
};
// 从文件路径中提取组件名称
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 # 带重试和并行工作进程