Skip to content

Commit b3f162d

Browse files
chore: enhance tests yml
1 parent e33b702 commit b3f162d

File tree

12 files changed

+1260
-89
lines changed

12 files changed

+1260
-89
lines changed
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
const { compareTests } = require('./utils/test');
2+
const { generateTestChangesSummary } = require('./utils/format');
3+
const { generateBundleSizeSection, getBundleInfo } = require('./utils/bundle');
4+
const { readTestResults, getTestStatus } = require('./utils/results');
5+
6+
/**
7+
* Main function to update PR description with test results and bundle size information
8+
* @param {Object} github - GitHub API object
9+
* @param {Object} context - GitHub Actions context
10+
*/
11+
async function updatePRDescription(github, context) {
12+
// Read test results
13+
const currentResults = readTestResults('playwright-artifacts/test-results.json');
14+
const mainResults = readTestResults('gh-pages/main/test-results.json');
15+
16+
// Compare tests
17+
const testComparison = compareTests(currentResults.tests, mainResults.tests);
18+
19+
// Get test status and report URL
20+
const { status, statusColor } = getTestStatus(currentResults);
21+
const reportUrl = `https://${context.repo.owner}.github.io/${context.repo.repo}/${context.issue.number}/`;
22+
23+
// Get bundle size information
24+
const bundleInfo = getBundleInfo();
25+
26+
// Generate the CI section content
27+
const ciSection = `## CI Results
28+
29+
### Test Status: <span style="color: ${statusColor};">${status}</span>
30+
📊 [Full Report](${reportUrl})
31+
32+
| Total | Passed | Failed | Flaky | Skipped |
33+
|:-----:|:------:|:------:|:-----:|:-------:|
34+
| ${currentResults.total} | ${currentResults.passed} | ${currentResults.failed} | ${currentResults.flaky} | ${currentResults.skipped} |
35+
36+
${generateTestChangesSummary(testComparison)}
37+
38+
${generateBundleSizeSection(bundleInfo)}
39+
40+
<details>
41+
<summary>ℹ️ CI Information</summary>
42+
43+
- Test recordings for failed tests are available in the full report.
44+
- Bundle size is measured for the entire 'dist' directory.
45+
- 📊 indicates links to detailed reports.
46+
- 🔺 indicates increase, 🔽 decrease, and ✅ no change in bundle size.
47+
</details>`;
48+
49+
// Update PR description
50+
const { data: pullRequest } = await github.rest.pulls.get({
51+
owner: context.repo.owner,
52+
repo: context.repo.repo,
53+
pull_number: context.issue.number,
54+
});
55+
56+
const currentBody = pullRequest.body || '';
57+
const ciSectionRegex = /## CI Results[\s\S]*?(?=\n## (?!CI Results)|$)/;
58+
59+
const newBody = ciSectionRegex.test(currentBody)
60+
? currentBody.replace(ciSectionRegex, ciSection)
61+
: currentBody + '\n\n' + ciSection;
62+
63+
await github.rest.pulls.update({
64+
owner: context.repo.owner,
65+
repo: context.repo.repo,
66+
pull_number: context.issue.number,
67+
body: newBody,
68+
});
69+
}
70+
71+
module.exports = updatePRDescription;

.github/scripts/utils/bundle.js

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
const { formatSize } = require('./format');
2+
3+
/**
4+
* Generates the bundle size status section
5+
* @param {Object} bundleInfo - Bundle size information
6+
* @returns {string} Formatted bundle size section
7+
*/
8+
function generateBundleSizeSection({ currentSize, mainSize, diff, percent }) {
9+
const bundleStatus = percent === 'N/A' ? '⚠️' :
10+
parseFloat(percent) > 0 ? '🔺' :
11+
parseFloat(percent) < 0 ? '🔽' : '✅';
12+
13+
const sizeChangeMessage = percent === 'N/A' ? '⚠️ Unable to calculate change.' :
14+
parseFloat(percent) > 0 ? '⚠️ Bundle size increased. Please review.' :
15+
parseFloat(percent) < 0 ? '✅ Bundle size decreased.' : '✅ Bundle size unchanged.';
16+
17+
return `### Bundle Size: ${bundleStatus}
18+
Current: ${formatSize(currentSize)} | Main: ${formatSize(mainSize)}
19+
Diff: ${diff > 0 ? '+' : ''}${formatSize(Math.abs(diff))} (${percent === 'N/A' ? 'N/A' : `${percent}%`})
20+
21+
${sizeChangeMessage}`;
22+
}
23+
24+
/**
25+
* Gets bundle size information from environment variables
26+
* @returns {Object} Bundle size information
27+
*/
28+
function getBundleInfo() {
29+
return {
30+
currentSize: parseInt(process.env.CURRENT_SIZE || '0'),
31+
mainSize: parseInt(process.env.MAIN_SIZE || '0'),
32+
diff: parseInt(process.env.SIZE_DIFF || '0'),
33+
percent: process.env.SIZE_PERCENT || 'N/A'
34+
};
35+
}
36+
37+
module.exports = {
38+
generateBundleSizeSection,
39+
getBundleInfo
40+
};

.github/scripts/utils/format.js

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/**
2+
* Formats a size in bytes to a human-readable string (KB or MB)
3+
* @param {number} sizeInBytes - Size in bytes to format
4+
* @returns {string} Formatted size string with units
5+
*/
6+
function formatSize(sizeInBytes) {
7+
const MB_THRESHOLD = 1024;
8+
if (sizeInBytes >= MB_THRESHOLD) {
9+
return `${(sizeInBytes / (1024 * 1024)).toFixed(2)} MB`;
10+
}
11+
return `${(sizeInBytes / 1024).toFixed(2)} KB`;
12+
}
13+
14+
/**
15+
* Generates a summary of test changes
16+
* @param {Object} comparison - Test comparison results
17+
* @returns {string} Formatted test changes summary
18+
*/
19+
function generateTestChangesSummary(comparison) {
20+
if (!comparison.new.length && !comparison.deleted.length && !comparison.skipped.length) {
21+
return '😟 No changes in tests. 😕';
22+
}
23+
24+
const summaryParts = [];
25+
const { new: newTests, skipped, deleted } = comparison;
26+
27+
if (newTests.length) {
28+
summaryParts.push(`#### ✨ New Tests (${newTests.length})\n${newTests.map((test, i) => `${i + 1}. ${test}`).join('\n')}\n`);
29+
}
30+
31+
if (skipped.length) {
32+
summaryParts.push(`#### ⏭️ Skipped Tests (${skipped.length})\n${skipped.map((test, i) => `${i + 1}. ${test}`).join('\n')}\n`);
33+
}
34+
35+
if (deleted.length) {
36+
summaryParts.push(`#### 🗑️ Deleted Tests (${deleted.length})\n${deleted.map((test, i) => `${i + 1}. ${test}`).join('\n')}`);
37+
}
38+
39+
return `
40+
<details>
41+
<summary>Test Changes Summary ${newTests.length ? `✨${newTests.length} ` : ''}${skipped.length ? `⏭️${skipped.length} ` : ''}${deleted.length ? `🗑️${deleted.length}` : ''}</summary>
42+
43+
${summaryParts.join('\n')}
44+
</details>`;
45+
}
46+
47+
module.exports = {
48+
formatSize,
49+
generateTestChangesSummary
50+
};

.github/scripts/utils/results.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
const fs = require('fs');
2+
const { extractTestsFromSuite } = require('./test');
3+
4+
/**
5+
* Reads and processes test results from a JSON file
6+
* @param {string} filePath - Path to the test results JSON file
7+
* @returns {Object} Processed test results
8+
*/
9+
function readTestResults(filePath) {
10+
if (!fs.existsSync(filePath)) {
11+
console.log(`Test results file not found: ${filePath}`);
12+
return { total: 0, passed: 0, failed: 0, flaky: 0, skipped: 0, tests: [] };
13+
}
14+
15+
const data = JSON.parse(fs.readFileSync(filePath));
16+
const allTests = data.suites.flatMap(suite => extractTestsFromSuite(suite));
17+
18+
return {
19+
total: data.stats.expected + data.stats.unexpected + data.stats.flaky + data.stats.skipped,
20+
passed: data.stats.expected,
21+
failed: data.stats.unexpected,
22+
flaky: data.stats.flaky,
23+
skipped: data.stats.skipped,
24+
tests: allTests
25+
};
26+
}
27+
28+
/**
29+
* Gets the test status information
30+
* @param {Object} results - Test results object
31+
* @returns {Object} Status information including color and label
32+
*/
33+
function getTestStatus(results) {
34+
const status = results.failed > 0 ? '❌ FAILED' :
35+
results.flaky > 0 ? '⚠️ FLAKY' :
36+
'✅ PASSED';
37+
38+
const statusColor = results.failed > 0 ? 'red' :
39+
results.flaky > 0 ? 'orange' :
40+
'green';
41+
42+
return { status, statusColor };
43+
}
44+
45+
module.exports = {
46+
readTestResults,
47+
getTestStatus
48+
};

.github/scripts/utils/test.js

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/**
2+
* Checks if a test spec is marked as skipped
3+
* @param {Object} spec - Test specification object
4+
* @returns {boolean} True if the test is skipped
5+
*/
6+
function isTestSkipped(spec) {
7+
return spec.tests?.[0] && (
8+
spec.tests[0].annotations?.some(a => a.type === 'skip') ||
9+
spec.tests[0].status === 'skipped'
10+
);
11+
}
12+
13+
/**
14+
* Extracts test information from a test suite recursively
15+
* @param {Object} suite - Test suite object
16+
* @param {string} parentTitle - Parent suite title for nested suites
17+
* @returns {Array} Array of test objects with metadata
18+
*/
19+
function extractTestsFromSuite(suite, parentTitle = '') {
20+
const tests = [];
21+
const fullSuiteTitle = parentTitle ? `${parentTitle} > ${suite.title}` : suite.title;
22+
23+
// Process individual test specs
24+
if (suite.specs) {
25+
const suiteTests = suite.specs.map(spec => {
26+
const isSkipped = isTestSkipped(spec);
27+
return {
28+
title: spec.title,
29+
fullTitle: `${fullSuiteTitle} > ${spec.title}`,
30+
status: isSkipped ? 'skipped' : (spec.ok ? 'passed' : 'failed'),
31+
file: suite.file,
32+
skipped: isSkipped
33+
};
34+
});
35+
tests.push(...suiteTests);
36+
}
37+
38+
// Recursively process nested suites
39+
if (suite.suites) {
40+
suite.suites.forEach(nestedSuite => {
41+
const nestedTests = extractTestsFromSuite(nestedSuite, fullSuiteTitle);
42+
tests.push(...nestedTests);
43+
});
44+
}
45+
46+
return tests;
47+
}
48+
49+
/**
50+
* Compares current and main branch test results
51+
* @param {Array} currentTests - Tests from current branch
52+
* @param {Array} mainTests - Tests from main branch
53+
* @returns {Object} Test comparison results
54+
*/
55+
function compareTests(currentTests, mainTests) {
56+
const comparison = { new: [], skipped: [], deleted: [] };
57+
58+
const currentTestMap = new Map(currentTests.map(t => [t.fullTitle, t]));
59+
const mainTestMap = new Map(mainTests.map(t => [t.fullTitle, t]));
60+
61+
// Find new and skipped tests
62+
for (const [fullTitle, test] of currentTestMap) {
63+
if (!mainTestMap.has(fullTitle)) {
64+
comparison.new.push(`${test.title} (${test.file})`);
65+
}
66+
if (test.skipped) {
67+
comparison.skipped.push(`${test.title} (${test.file})`);
68+
}
69+
}
70+
71+
// Find deleted tests
72+
for (const [fullTitle, test] of mainTestMap) {
73+
if (!currentTestMap.has(fullTitle)) {
74+
comparison.deleted.push(`${test.title} (${test.file})`);
75+
}
76+
}
77+
78+
comparison.skipped = Array.from(new Set(comparison.skipped));
79+
return comparison;
80+
}
81+
82+
module.exports = {
83+
isTestSkipped,
84+
extractTestsFromSuite,
85+
compareTests
86+
};

0 commit comments

Comments
 (0)