From b3f162df2e71b1c3b2e488faa275a81dd6251121 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 23 Nov 2024 02:10:23 +0300 Subject: [PATCH 1/6] chore: enhance tests yml --- .github/scripts/update-pr-description.js | 71 +++++ .github/scripts/utils/bundle.js | 40 +++ .github/scripts/utils/format.js | 50 ++++ .github/scripts/utils/results.js | 48 ++++ .github/scripts/utils/test.js | 86 ++++++ .github/workflows/quality.yml | 102 +------ src/github/__tests__/bundle.test.ts | 111 ++++++++ src/github/__tests__/format.test.ts | 101 +++++++ src/github/__tests__/results.test.ts | 135 ++++++++++ src/github/__tests__/test.test.ts | 226 ++++++++++++++++ .../__tests__/update-pr-description.test.ts | 252 ++++++++++++++++++ src/github/types.ts | 127 +++++++++ 12 files changed, 1260 insertions(+), 89 deletions(-) create mode 100644 .github/scripts/update-pr-description.js create mode 100644 .github/scripts/utils/bundle.js create mode 100644 .github/scripts/utils/format.js create mode 100644 .github/scripts/utils/results.js create mode 100644 .github/scripts/utils/test.js create mode 100644 src/github/__tests__/bundle.test.ts create mode 100644 src/github/__tests__/format.test.ts create mode 100644 src/github/__tests__/results.test.ts create mode 100644 src/github/__tests__/test.test.ts create mode 100644 src/github/__tests__/update-pr-description.test.ts create mode 100644 src/github/types.ts diff --git a/.github/scripts/update-pr-description.js b/.github/scripts/update-pr-description.js new file mode 100644 index 000000000..00ae8f3da --- /dev/null +++ b/.github/scripts/update-pr-description.js @@ -0,0 +1,71 @@ +const { compareTests } = require('./utils/test'); +const { generateTestChangesSummary } = require('./utils/format'); +const { generateBundleSizeSection, getBundleInfo } = require('./utils/bundle'); +const { readTestResults, getTestStatus } = require('./utils/results'); + +/** + * Main function to update PR description with test results and bundle size information + * @param {Object} github - GitHub API object + * @param {Object} context - GitHub Actions context + */ +async function updatePRDescription(github, context) { + // Read test results + const currentResults = readTestResults('playwright-artifacts/test-results.json'); + const mainResults = readTestResults('gh-pages/main/test-results.json'); + + // Compare tests + const testComparison = compareTests(currentResults.tests, mainResults.tests); + + // Get test status and report URL + const { status, statusColor } = getTestStatus(currentResults); + const reportUrl = `https://${context.repo.owner}.github.io/${context.repo.repo}/${context.issue.number}/`; + + // Get bundle size information + const bundleInfo = getBundleInfo(); + + // Generate the CI section content + const ciSection = `## CI Results + + ### Test Status: ${status} + 📊 [Full Report](${reportUrl}) + + | Total | Passed | Failed | Flaky | Skipped | + |:-----:|:------:|:------:|:-----:|:-------:| + | ${currentResults.total} | ${currentResults.passed} | ${currentResults.failed} | ${currentResults.flaky} | ${currentResults.skipped} | + + ${generateTestChangesSummary(testComparison)} + + ${generateBundleSizeSection(bundleInfo)} + +
+ â„šī¸ CI Information + + - Test recordings for failed tests are available in the full report. + - Bundle size is measured for the entire 'dist' directory. + - 📊 indicates links to detailed reports. + - đŸ”ē indicates increase, đŸ”Ŋ decrease, and ✅ no change in bundle size. +
`; + + // Update PR description + const { data: pullRequest } = await github.rest.pulls.get({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: context.issue.number, + }); + + const currentBody = pullRequest.body || ''; + const ciSectionRegex = /## CI Results[\s\S]*?(?=\n## (?!CI Results)|$)/; + + const newBody = ciSectionRegex.test(currentBody) + ? currentBody.replace(ciSectionRegex, ciSection) + : currentBody + '\n\n' + ciSection; + + await github.rest.pulls.update({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: context.issue.number, + body: newBody, + }); +} + +module.exports = updatePRDescription; diff --git a/.github/scripts/utils/bundle.js b/.github/scripts/utils/bundle.js new file mode 100644 index 000000000..c1c8be44c --- /dev/null +++ b/.github/scripts/utils/bundle.js @@ -0,0 +1,40 @@ +const { formatSize } = require('./format'); + +/** + * Generates the bundle size status section + * @param {Object} bundleInfo - Bundle size information + * @returns {string} Formatted bundle size section + */ +function generateBundleSizeSection({ currentSize, mainSize, diff, percent }) { + const bundleStatus = percent === 'N/A' ? 'âš ī¸' : + parseFloat(percent) > 0 ? 'đŸ”ē' : + parseFloat(percent) < 0 ? 'đŸ”Ŋ' : '✅'; + + const sizeChangeMessage = percent === 'N/A' ? 'âš ī¸ Unable to calculate change.' : + parseFloat(percent) > 0 ? 'âš ī¸ Bundle size increased. Please review.' : + parseFloat(percent) < 0 ? '✅ Bundle size decreased.' : '✅ Bundle size unchanged.'; + + return `### Bundle Size: ${bundleStatus} + Current: ${formatSize(currentSize)} | Main: ${formatSize(mainSize)} + Diff: ${diff > 0 ? '+' : ''}${formatSize(Math.abs(diff))} (${percent === 'N/A' ? 'N/A' : `${percent}%`}) + + ${sizeChangeMessage}`; +} + +/** + * Gets bundle size information from environment variables + * @returns {Object} Bundle size information + */ +function getBundleInfo() { + return { + currentSize: parseInt(process.env.CURRENT_SIZE || '0'), + mainSize: parseInt(process.env.MAIN_SIZE || '0'), + diff: parseInt(process.env.SIZE_DIFF || '0'), + percent: process.env.SIZE_PERCENT || 'N/A' + }; +} + +module.exports = { + generateBundleSizeSection, + getBundleInfo +}; diff --git a/.github/scripts/utils/format.js b/.github/scripts/utils/format.js new file mode 100644 index 000000000..0fc18ce2a --- /dev/null +++ b/.github/scripts/utils/format.js @@ -0,0 +1,50 @@ +/** + * Formats a size in bytes to a human-readable string (KB or MB) + * @param {number} sizeInBytes - Size in bytes to format + * @returns {string} Formatted size string with units + */ +function formatSize(sizeInBytes) { + const MB_THRESHOLD = 1024; + if (sizeInBytes >= MB_THRESHOLD) { + return `${(sizeInBytes / (1024 * 1024)).toFixed(2)} MB`; + } + return `${(sizeInBytes / 1024).toFixed(2)} KB`; +} + +/** + * Generates a summary of test changes + * @param {Object} comparison - Test comparison results + * @returns {string} Formatted test changes summary + */ +function generateTestChangesSummary(comparison) { + if (!comparison.new.length && !comparison.deleted.length && !comparison.skipped.length) { + return '😟 No changes in tests. 😕'; + } + + const summaryParts = []; + const { new: newTests, skipped, deleted } = comparison; + + if (newTests.length) { + summaryParts.push(`#### ✨ New Tests (${newTests.length})\n${newTests.map((test, i) => `${i + 1}. ${test}`).join('\n')}\n`); + } + + if (skipped.length) { + summaryParts.push(`#### â­ī¸ Skipped Tests (${skipped.length})\n${skipped.map((test, i) => `${i + 1}. ${test}`).join('\n')}\n`); + } + + if (deleted.length) { + summaryParts.push(`#### đŸ—‘ī¸ Deleted Tests (${deleted.length})\n${deleted.map((test, i) => `${i + 1}. ${test}`).join('\n')}`); + } + + return ` +
+ Test Changes Summary ${newTests.length ? `✨${newTests.length} ` : ''}${skipped.length ? `â­ī¸${skipped.length} ` : ''}${deleted.length ? `đŸ—‘ī¸${deleted.length}` : ''} + + ${summaryParts.join('\n')} +
`; +} + +module.exports = { + formatSize, + generateTestChangesSummary +}; diff --git a/.github/scripts/utils/results.js b/.github/scripts/utils/results.js new file mode 100644 index 000000000..89cd2cac0 --- /dev/null +++ b/.github/scripts/utils/results.js @@ -0,0 +1,48 @@ +const fs = require('fs'); +const { extractTestsFromSuite } = require('./test'); + +/** + * Reads and processes test results from a JSON file + * @param {string} filePath - Path to the test results JSON file + * @returns {Object} Processed test results + */ +function readTestResults(filePath) { + if (!fs.existsSync(filePath)) { + console.log(`Test results file not found: ${filePath}`); + return { total: 0, passed: 0, failed: 0, flaky: 0, skipped: 0, tests: [] }; + } + + const data = JSON.parse(fs.readFileSync(filePath)); + const allTests = data.suites.flatMap(suite => extractTestsFromSuite(suite)); + + return { + total: data.stats.expected + data.stats.unexpected + data.stats.flaky + data.stats.skipped, + passed: data.stats.expected, + failed: data.stats.unexpected, + flaky: data.stats.flaky, + skipped: data.stats.skipped, + tests: allTests + }; +} + +/** + * Gets the test status information + * @param {Object} results - Test results object + * @returns {Object} Status information including color and label + */ +function getTestStatus(results) { + const status = results.failed > 0 ? '❌ FAILED' : + results.flaky > 0 ? 'âš ī¸ FLAKY' : + '✅ PASSED'; + + const statusColor = results.failed > 0 ? 'red' : + results.flaky > 0 ? 'orange' : + 'green'; + + return { status, statusColor }; +} + +module.exports = { + readTestResults, + getTestStatus +}; diff --git a/.github/scripts/utils/test.js b/.github/scripts/utils/test.js new file mode 100644 index 000000000..aad4d6d6e --- /dev/null +++ b/.github/scripts/utils/test.js @@ -0,0 +1,86 @@ +/** + * Checks if a test spec is marked as skipped + * @param {Object} spec - Test specification object + * @returns {boolean} True if the test is skipped + */ +function isTestSkipped(spec) { + return spec.tests?.[0] && ( + spec.tests[0].annotations?.some(a => a.type === 'skip') || + spec.tests[0].status === 'skipped' + ); +} + +/** + * Extracts test information from a test suite recursively + * @param {Object} suite - Test suite object + * @param {string} parentTitle - Parent suite title for nested suites + * @returns {Array} Array of test objects with metadata + */ +function extractTestsFromSuite(suite, parentTitle = '') { + const tests = []; + const fullSuiteTitle = parentTitle ? `${parentTitle} > ${suite.title}` : suite.title; + + // Process individual test specs + if (suite.specs) { + const suiteTests = suite.specs.map(spec => { + const isSkipped = isTestSkipped(spec); + return { + title: spec.title, + fullTitle: `${fullSuiteTitle} > ${spec.title}`, + status: isSkipped ? 'skipped' : (spec.ok ? 'passed' : 'failed'), + file: suite.file, + skipped: isSkipped + }; + }); + tests.push(...suiteTests); + } + + // Recursively process nested suites + if (suite.suites) { + suite.suites.forEach(nestedSuite => { + const nestedTests = extractTestsFromSuite(nestedSuite, fullSuiteTitle); + tests.push(...nestedTests); + }); + } + + return tests; +} + +/** + * Compares current and main branch test results + * @param {Array} currentTests - Tests from current branch + * @param {Array} mainTests - Tests from main branch + * @returns {Object} Test comparison results + */ +function compareTests(currentTests, mainTests) { + const comparison = { new: [], skipped: [], deleted: [] }; + + const currentTestMap = new Map(currentTests.map(t => [t.fullTitle, t])); + const mainTestMap = new Map(mainTests.map(t => [t.fullTitle, t])); + + // Find new and skipped tests + for (const [fullTitle, test] of currentTestMap) { + if (!mainTestMap.has(fullTitle)) { + comparison.new.push(`${test.title} (${test.file})`); + } + if (test.skipped) { + comparison.skipped.push(`${test.title} (${test.file})`); + } + } + + // Find deleted tests + for (const [fullTitle, test] of mainTestMap) { + if (!currentTestMap.has(fullTitle)) { + comparison.deleted.push(`${test.title} (${test.file})`); + } + } + + comparison.skipped = Array.from(new Set(comparison.skipped)); + return comparison; +} + +module.exports = { + isTestSkipped, + extractTestsFromSuite, + compareTests +}; diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index 64a020283..770bf38a1 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -221,97 +221,21 @@ jobs: name: playwright-artifacts path: playwright-artifacts + - name: Fetch gh-pages branch + run: | + git fetch origin gh-pages:gh-pages + mkdir gh-pages + git --work-tree=gh-pages checkout gh-pages -- . + - name: Update PR description uses: actions/github-script@v6 + env: + CURRENT_SIZE: ${{ needs.bundle_size.outputs.current_size }} + MAIN_SIZE: ${{ needs.bundle_size.outputs.main_size }} + SIZE_DIFF: ${{ needs.bundle_size.outputs.diff }} + SIZE_PERCENT: ${{ needs.bundle_size.outputs.percent }} with: github-token: ${{secrets.GITHUB_TOKEN}} script: | - const fs = require('fs'); - const testResultsPath = 'playwright-artifacts/test-results.json'; - let testResults; - - if (fs.existsSync(testResultsPath)) { - const rawData = fs.readFileSync(testResultsPath); - const data = JSON.parse(rawData); - testResults = { - total: data.stats.expected + data.stats.unexpected + data.stats.flaky + data.stats.skipped, - passed: data.stats.expected, - failed: data.stats.unexpected, - flaky: data.stats.flaky, - skipped: data.stats.skipped - }; - } else { - console.log('Test results file not found'); - testResults = { total: 0, passed: 0, failed: 0, flaky: 0, skipped: 0 }; - } - - const reportUrl = `https://${context.repo.owner}.github.io/${context.repo.repo}/${context.issue.number}/`; - const status = testResults.failed > 0 ? '❌ FAILED' : (testResults.flaky > 0 ? 'âš ī¸ FLAKY' : '✅ PASSED'); - const statusColor = testResults.failed > 0 ? 'red' : (testResults.flaky > 0 ? 'orange' : 'green'); - - const currentSize = parseInt('${{ needs.bundle_size.outputs.current_size }}'); - const mainSize = parseInt('${{ needs.bundle_size.outputs.main_size }}'); - const diff = parseInt('${{ needs.bundle_size.outputs.diff }}'); - const percent = '${{ needs.bundle_size.outputs.percent }}'; - - const formatSize = (size) => { - if (size >= 1024) { - return `${(size / (1024 * 1024)).toFixed(2)} MB`; - } - return `${(size / 1024).toFixed(2)} KB`; - }; - - const bundleStatus = percent === 'N/A' ? 'âš ī¸' : - parseFloat(percent) > 0 ? 'đŸ”ē' : - parseFloat(percent) < 0 ? 'đŸ”Ŋ' : '✅'; - - const ciSection = `## CI Results - - ### Test Status: ${status} - 📊 [Full Report](${reportUrl}) - - | Total | Passed | Failed | Flaky | Skipped | - |:-----:|:------:|:------:|:-----:|:-------:| - | ${testResults.total} | ${testResults.passed} | ${testResults.failed} | ${testResults.flaky} | ${testResults.skipped} | - - ### Bundle Size: ${bundleStatus} - Current: ${formatSize(currentSize)} | Main: ${formatSize(mainSize)} - Diff: ${diff > 0 ? '+' : ''}${formatSize(Math.abs(diff))} (${percent === 'N/A' ? 'N/A' : `${percent}%`}) - - ${ - percent === 'N/A' ? 'âš ī¸ Unable to calculate change.' : - parseFloat(percent) > 0 ? 'âš ī¸ Bundle size increased. Please review.' : - parseFloat(percent) < 0 ? '✅ Bundle size decreased.' : '✅ Bundle size unchanged.' - } - -
- â„šī¸ CI Information - - - Test recordings for failed tests are available in the full report. - - Bundle size is measured for the entire 'dist' directory. - - 📊 indicates links to detailed reports. - - đŸ”ē indicates increase, đŸ”Ŋ decrease, and ✅ no change in bundle size. -
`; - - const { data: pullRequest } = await github.rest.pulls.get({ - owner: context.repo.owner, - repo: context.repo.repo, - pull_number: context.issue.number, - }); - - const currentBody = pullRequest.body || ''; - const ciSectionRegex = /## CI Results[\s\S]*?(?=\n## (?!CI Results)|$)/; - - let newBody = currentBody; - if (ciSectionRegex.test(newBody)) { - newBody = newBody.replace(ciSectionRegex, ciSection); - } else { - newBody += '\n\n' + ciSection; - } - - await github.rest.pulls.update({ - owner: context.repo.owner, - repo: context.repo.repo, - pull_number: context.issue.number, - body: newBody, - }); + const updatePRDescription = require('./.github/workflows/scripts/update-pr-description.js'); + await updatePRDescription(github, context); diff --git a/src/github/__tests__/bundle.test.ts b/src/github/__tests__/bundle.test.ts new file mode 100644 index 000000000..3d15158a6 --- /dev/null +++ b/src/github/__tests__/bundle.test.ts @@ -0,0 +1,111 @@ +import { + generateBundleSizeSection, + getBundleInfo, +} from '../../../.github/workflows/scripts/utils/bundle'; + +describe('bundle utils', () => { + describe('generateBundleSizeSection', () => { + it('should generate section for increased bundle size', () => { + const bundleInfo = { + currentSize: 1024 * 1024 * 2, // 2MB + mainSize: 1024 * 1024, // 1MB + diff: 1024 * 1024, // 1MB increase + percent: '100', + }; + + const result = generateBundleSizeSection(bundleInfo); + expect(result).toContain('Bundle Size: đŸ”ē'); + expect(result).toContain('Current: 2.00 MB | Main: 1.00 MB'); + expect(result).toContain('Diff: +1.00 MB (100%)'); + expect(result).toContain('âš ī¸ Bundle size increased. Please review.'); + }); + + it('should generate section for decreased bundle size', () => { + const bundleInfo = { + currentSize: 1024 * 1024, // 1MB + mainSize: 1024 * 1024 * 2, // 2MB + diff: -1024 * 1024, // 1MB decrease + percent: '-50', + }; + + const result = generateBundleSizeSection(bundleInfo); + expect(result).toContain('Bundle Size: đŸ”Ŋ'); + expect(result).toContain('Current: 1.00 MB | Main: 2.00 MB'); + expect(result).toContain('Diff: 1.00 MB (-50%)'); + expect(result).toContain('✅ Bundle size decreased.'); + }); + + it('should generate section for unchanged bundle size', () => { + const bundleInfo = { + currentSize: 1024 * 1024, // 1MB + mainSize: 1024 * 1024, // 1MB + diff: 0, + percent: '0', + }; + + const result = generateBundleSizeSection(bundleInfo); + expect(result).toContain('Bundle Size: ✅'); + expect(result).toContain('Current: 1.00 MB | Main: 1.00 MB'); + expect(result).toContain('Diff: 0.00 KB (0%)'); + expect(result).toContain('✅ Bundle size unchanged.'); + }); + + it('should handle N/A percent', () => { + const bundleInfo = { + currentSize: 1024 * 1024, // 1MB + mainSize: 0, + diff: 1024 * 1024, + percent: 'N/A', + }; + + const result = generateBundleSizeSection(bundleInfo); + expect(result).toContain('Bundle Size: âš ī¸'); + expect(result).toContain('Current: 1.00 MB | Main: 0.00 KB'); + expect(result).toContain('Diff: +1.00 MB (N/A)'); + expect(result).toContain('âš ī¸ Unable to calculate change.'); + }); + }); + + describe('getBundleInfo', () => { + const originalEnv = process.env; + + beforeEach(() => { + jest.resetModules(); + process.env = {...originalEnv}; + }); + + afterAll(() => { + process.env = originalEnv; + }); + + it('should get bundle info from environment variables', () => { + process.env.CURRENT_SIZE = '2097152'; // 2MB + process.env.MAIN_SIZE = '1048576'; // 1MB + process.env.SIZE_DIFF = '1048576'; // 1MB + process.env.SIZE_PERCENT = '100'; + + const result = getBundleInfo(); + expect(result).toEqual({ + currentSize: 2097152, + mainSize: 1048576, + diff: 1048576, + percent: '100', + }); + }); + + it('should handle missing environment variables', () => { + delete process.env.CURRENT_SIZE; + delete process.env.MAIN_SIZE; + delete process.env.SIZE_DIFF; + delete process.env.SIZE_PERCENT; + + const result = getBundleInfo(); + expect(result).toEqual({ + currentSize: 0, + mainSize: 0, + diff: 0, + percent: 'N/A', + }); + }); + }); +}); diff --git a/src/github/__tests__/format.test.ts b/src/github/__tests__/format.test.ts new file mode 100644 index 000000000..7f6825772 --- /dev/null +++ b/src/github/__tests__/format.test.ts @@ -0,0 +1,101 @@ +import { + formatSize, + generateTestChangesSummary, +} from '../../../.github/workflows/scripts/utils/format'; + +describe('format utils', () => { + describe('formatSize', () => { + it('should format size in KB when less than 1024 bytes', () => { + const size = 512; // 512 bytes + expect(formatSize(size)).toBe('0.50 KB'); + }); + + it('should format size in MB when greater than or equal to 1024 bytes', () => { + const size = 2.5 * 1024; // 2.5 KB -> will be shown in MB + expect(formatSize(size)).toBe('0.00 MB'); + }); + + it('should handle small sizes', () => { + const size = 100; // 100 bytes + expect(formatSize(size)).toBe('0.10 KB'); + }); + + it('should handle zero', () => { + expect(formatSize(0)).toBe('0.00 KB'); + }); + }); + + describe('generateTestChangesSummary', () => { + it('should generate summary for new tests only', () => { + const comparison = { + new: ['Test 1 (file1.ts)', 'Test 2 (file2.ts)'], + skipped: [], + deleted: [], + }; + + const summary = generateTestChangesSummary(comparison); + expect(summary).toContain('✨ New Tests (2)'); + expect(summary).toContain('1. Test 1 (file1.ts)'); + expect(summary).toContain('2. Test 2 (file2.ts)'); + expect(summary).not.toContain('â­ī¸ Skipped Tests'); + expect(summary).not.toContain('đŸ—‘ī¸ Deleted Tests'); + }); + + it('should generate summary for skipped tests only', () => { + const comparison = { + new: [], + skipped: ['Test 1 (file1.ts)', 'Test 2 (file2.ts)'], + deleted: [], + }; + + const summary = generateTestChangesSummary(comparison); + expect(summary).toContain('â­ī¸ Skipped Tests (2)'); + expect(summary).toContain('1. Test 1 (file1.ts)'); + expect(summary).toContain('2. Test 2 (file2.ts)'); + expect(summary).not.toContain('✨ New Tests'); + expect(summary).not.toContain('đŸ—‘ī¸ Deleted Tests'); + }); + + it('should generate summary for deleted tests only', () => { + const comparison = { + new: [], + skipped: [], + deleted: ['Test 1 (file1.ts)', 'Test 2 (file2.ts)'], + }; + + const summary = generateTestChangesSummary(comparison); + expect(summary).toContain('đŸ—‘ī¸ Deleted Tests (2)'); + expect(summary).toContain('1. Test 1 (file1.ts)'); + expect(summary).toContain('2. Test 2 (file2.ts)'); + expect(summary).not.toContain('✨ New Tests'); + expect(summary).not.toContain('â­ī¸ Skipped Tests'); + }); + + it('should generate summary for all types of changes', () => { + const comparison = { + new: ['New Test (file1.ts)'], + skipped: ['Skipped Test (file2.ts)'], + deleted: ['Deleted Test (file3.ts)'], + }; + + const summary = generateTestChangesSummary(comparison); + expect(summary).toContain('✨ New Tests (1)'); + expect(summary).toContain('â­ī¸ Skipped Tests (1)'); + expect(summary).toContain('đŸ—‘ī¸ Deleted Tests (1)'); + expect(summary).toContain('New Test (file1.ts)'); + expect(summary).toContain('Skipped Test (file2.ts)'); + expect(summary).toContain('Deleted Test (file3.ts)'); + }); + + it('should handle no changes', () => { + const comparison = { + new: [], + skipped: [], + deleted: [], + }; + + const summary = generateTestChangesSummary(comparison); + expect(summary).toBe('😟 No changes in tests. 😕'); + }); + }); +}); diff --git a/src/github/__tests__/results.test.ts b/src/github/__tests__/results.test.ts new file mode 100644 index 000000000..b8e487992 --- /dev/null +++ b/src/github/__tests__/results.test.ts @@ -0,0 +1,135 @@ +import fs from 'fs'; + +import {getTestStatus, readTestResults} from '../../../.github/workflows/scripts/utils/results'; +import type {TestResults, TestResultsInfo, TestStatusInfo} from '../types'; + +jest.mock('fs'); + +describe('results utils', () => { + describe('readTestResults', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should handle non-existent file', () => { + (fs.existsSync as jest.Mock).mockReturnValue(false); + + const result = readTestResults('nonexistent.json'); + expect(result).toEqual({ + total: 0, + passed: 0, + failed: 0, + flaky: 0, + skipped: 0, + tests: [], + }); + }); + + it('should read and process test results correctly', () => { + const mockTestResults: TestResults = { + config: {} as any, + suites: [ + { + title: 'Test Suite', + file: 'test.spec.ts', + column: 1, + line: 1, + specs: [ + { + title: 'Test 1', + ok: true, + tags: [], + id: '1', + file: 'test.spec.ts', + line: 2, + column: 1, + tests: [ + { + timeout: 5000, + annotations: [], + expectedStatus: 'passed', + projectId: '1', + projectName: 'test', + results: [], + status: 'passed', + }, + ], + }, + ], + suites: [], + }, + ], + }; + + (fs.existsSync as jest.Mock).mockReturnValue(true); + (fs.readFileSync as jest.Mock).mockReturnValue( + JSON.stringify({ + ...mockTestResults, + stats: { + expected: 5, + unexpected: 2, + flaky: 1, + skipped: 3, + }, + }), + ); + + const result = readTestResults('test-results.json'); + expect(result).toEqual({ + total: 11, + passed: 5, + failed: 2, + flaky: 1, + skipped: 3, + tests: expect.any(Array), + }); + }); + }); + + describe('getTestStatus', () => { + it('should return failed status when there are failures', () => { + const results: TestResultsInfo = { + total: 10, + passed: 8, + failed: 2, + flaky: 0, + skipped: 0, + tests: [], + }; + + const result = getTestStatus(results) as TestStatusInfo; + expect(result.status).toBe('❌ FAILED'); + expect(result.statusColor).toBe('red'); + }); + + it('should return flaky status when there are flaky tests but no failures', () => { + const results: TestResultsInfo = { + total: 10, + passed: 8, + failed: 0, + flaky: 2, + skipped: 0, + tests: [], + }; + + const result = getTestStatus(results) as TestStatusInfo; + expect(result.status).toBe('âš ī¸ FLAKY'); + expect(result.statusColor).toBe('orange'); + }); + + it('should return passed status when all tests pass', () => { + const results: TestResultsInfo = { + total: 10, + passed: 10, + failed: 0, + flaky: 0, + skipped: 0, + tests: [], + }; + + const result = getTestStatus(results) as TestStatusInfo; + expect(result.status).toBe('✅ PASSED'); + expect(result.statusColor).toBe('green'); + }); + }); +}); diff --git a/src/github/__tests__/test.test.ts b/src/github/__tests__/test.test.ts new file mode 100644 index 000000000..824fe3e58 --- /dev/null +++ b/src/github/__tests__/test.test.ts @@ -0,0 +1,226 @@ +import { + compareTests, + extractTestsFromSuite, + isTestSkipped, +} from '../../../.github/workflows/scripts/utils/test'; +import type {Spec, Suite, TestInfo} from '../types'; + +describe('test utils', () => { + describe('isTestSkipped', () => { + it('should return true for test with skip annotation', () => { + const spec: Spec = { + title: 'Test', + ok: true, + tags: [], + id: '1', + file: 'test.spec.ts', + line: 1, + column: 1, + tests: [ + { + timeout: 5000, + annotations: [{type: 'skip'}], + expectedStatus: 'passed', + projectId: '1', + projectName: 'test', + results: [], + status: 'passed', + }, + ], + }; + expect(isTestSkipped(spec)).toBe(true); + }); + + it('should return true for test with skipped status', () => { + const spec: Spec = { + title: 'Test', + ok: true, + tags: [], + id: '1', + file: 'test.spec.ts', + line: 1, + column: 1, + tests: [ + { + timeout: 5000, + annotations: [], + expectedStatus: 'skipped', + projectId: '1', + projectName: 'test', + results: [], + status: 'skipped', + }, + ], + }; + expect(isTestSkipped(spec)).toBe(true); + }); + + it('should return false for non-skipped test', () => { + const spec: Spec = { + title: 'Test', + ok: true, + tags: [], + id: '1', + file: 'test.spec.ts', + line: 1, + column: 1, + tests: [ + { + timeout: 5000, + annotations: [], + expectedStatus: 'passed', + projectId: '1', + projectName: 'test', + results: [], + status: 'passed', + }, + ], + }; + expect(isTestSkipped(spec)).toBe(false); + }); + }); + + describe('extractTestsFromSuite', () => { + it('should extract tests from a simple suite', () => { + const suite: Suite = { + title: 'Suite 1', + file: 'test.spec.ts', + column: 1, + line: 1, + specs: [ + { + title: 'Test 1', + ok: true, + tags: [], + id: '1', + file: 'test.spec.ts', + line: 2, + column: 1, + tests: [ + { + timeout: 5000, + annotations: [], + expectedStatus: 'passed', + projectId: '1', + projectName: 'test', + results: [], + status: 'passed', + }, + ], + }, + ], + suites: [], + }; + + const result = extractTestsFromSuite(suite); + expect(result).toEqual([ + { + title: 'Test 1', + fullTitle: 'Suite 1 > Test 1', + status: 'passed', + file: 'test.spec.ts', + skipped: false, + }, + ]); + }); + + it('should handle nested suites', () => { + const suite: Suite = { + title: 'Parent Suite', + file: 'test.spec.ts', + column: 1, + line: 1, + specs: [], + suites: [ + { + title: 'Child Suite', + file: 'test.spec.ts', + column: 1, + line: 2, + specs: [ + { + title: 'Test 1', + ok: true, + tags: [], + id: '1', + file: 'test.spec.ts', + line: 3, + column: 1, + tests: [ + { + timeout: 5000, + annotations: [], + expectedStatus: 'passed', + projectId: '1', + projectName: 'test', + results: [], + status: 'passed', + }, + ], + }, + ], + suites: [], + }, + ], + }; + + const result = extractTestsFromSuite(suite); + expect(result).toEqual([ + { + title: 'Test 1', + fullTitle: 'Parent Suite > Child Suite > Test 1', + status: 'passed', + file: 'test.spec.ts', + skipped: false, + }, + ]); + }); + }); + + describe('compareTests', () => { + it('should identify new, skipped, and deleted tests', () => { + const currentTests: TestInfo[] = [ + { + title: 'Test 1', + fullTitle: 'Suite > Test 1', + file: 'test.spec.ts', + status: 'passed', + skipped: false, + }, + { + title: 'Test 2', + fullTitle: 'Suite > Test 2', + file: 'test.spec.ts', + status: 'skipped', + skipped: true, + }, + ]; + + const mainTests: TestInfo[] = [ + { + title: 'Test 3', + fullTitle: 'Suite > Test 3', + file: 'test.spec.ts', + status: 'passed', + skipped: false, + }, + ]; + + const result = compareTests(currentTests, mainTests); + expect(result).toEqual({ + new: ['Test 1 (test.spec.ts)', 'Test 2 (test.spec.ts)'], + skipped: ['Test 2 (test.spec.ts)'], + deleted: ['Test 3 (test.spec.ts)'], + }); + }); + + it('should handle empty test arrays', () => { + const result = compareTests([], []); + expect(result).toEqual({ + new: [], + skipped: [], + deleted: [], + }); + }); + }); +}); diff --git a/src/github/__tests__/update-pr-description.test.ts b/src/github/__tests__/update-pr-description.test.ts new file mode 100644 index 000000000..f646e48ce --- /dev/null +++ b/src/github/__tests__/update-pr-description.test.ts @@ -0,0 +1,252 @@ +import updatePRDescription from '../../../.github/workflows/scripts/update-pr-description'; +import { + generateBundleSizeSection, + getBundleInfo, +} from '../../../.github/workflows/scripts/utils/bundle'; +import {generateTestChangesSummary} from '../../../.github/workflows/scripts/utils/format'; +import {getTestStatus, readTestResults} from '../../../.github/workflows/scripts/utils/results'; +import {compareTests} from '../../../.github/workflows/scripts/utils/test'; +import type {TestResultsInfo} from '../types'; + +// Mock dependencies +jest.mock('../../../.github/workflows/scripts/utils/results', () => ({ + readTestResults: jest.fn(), + getTestStatus: jest.fn(), +})); +jest.mock('../../../.github/workflows/scripts/utils/test'); +jest.mock('../../../.github/workflows/scripts/utils/format'); +jest.mock('../../../.github/workflows/scripts/utils/bundle'); +jest.mock('fs'); + +describe('updatePRDescription', () => { + let mockGithub: { + rest: { + pulls: { + get: jest.Mock; + update: jest.Mock; + }; + }; + }; + let mockContext: { + repo: { + owner: string; + repo: string; + }; + issue: { + number: number; + }; + }; + + beforeEach(() => { + // Reset all mocks + jest.clearAllMocks(); + + // Mock GitHub API + mockGithub = { + rest: { + pulls: { + get: jest.fn(), + update: jest.fn(), + }, + }, + }; + + // Mock GitHub context + mockContext = { + repo: { + owner: 'testOwner', + repo: 'testRepo', + }, + issue: { + number: 123, + }, + }; + + // Mock test results + const mockTestResults: TestResultsInfo = { + total: 10, + passed: 8, + failed: 1, + flaky: 0, + skipped: 1, + tests: [ + { + title: 'Test 1', + fullTitle: 'Suite > Test 1', + status: 'passed', + file: 'test.spec.ts', + skipped: false, + }, + ], + }; + + (readTestResults as jest.Mock).mockImplementation(() => mockTestResults); + (getTestStatus as jest.Mock).mockReturnValue({ + status: '✅ PASSED', + statusColor: 'green', + }); + + // Mock test comparison + (compareTests as jest.Mock).mockReturnValue({ + new: ['New Test (test.spec.ts)'], + skipped: ['Skipped Test (test.spec.ts)'], + deleted: ['Deleted Test (test.spec.ts)'], + }); + + // Mock summary generation + (generateTestChangesSummary as jest.Mock).mockReturnValue('Test Changes Summary'); + + // Mock bundle info + (getBundleInfo as jest.Mock).mockReturnValue({ + currentSize: 1024, + mainSize: 1000, + diff: 24, + percent: '2.4', + }); + + (generateBundleSizeSection as jest.Mock).mockReturnValue('Bundle Size Section'); + + // Mock PR data + mockGithub.rest.pulls.get.mockResolvedValue({ + data: { + body: 'Original PR description', + }, + }); + + mockGithub.rest.pulls.update.mockResolvedValue({}); + }); + + it('should read both current and main test results', async () => { + await updatePRDescription(mockGithub, mockContext); + + expect(readTestResults).toHaveBeenCalledTimes(2); + expect(readTestResults).toHaveBeenCalledWith('playwright-artifacts/test-results.json'); + expect(readTestResults).toHaveBeenCalledWith('gh-pages/main/test-results.json'); + }); + + it('should format CI section with correct table and details', async () => { + const mockResults: TestResultsInfo = { + total: 5, + passed: 3, + failed: 1, + flaky: 0, + skipped: 1, + tests: [], + }; + (readTestResults as jest.Mock).mockReturnValue(mockResults); + (getTestStatus as jest.Mock).mockReturnValue({ + status: '❌ FAILED', + statusColor: 'red', + }); + + await updatePRDescription(mockGithub, mockContext); + + const updateCall = mockGithub.rest.pulls.update.mock.calls[0][0]; + const body = updateCall.body; + + // Check table format + expect(body).toContain('| Total | Passed | Failed | Flaky | Skipped |'); + expect(body).toContain('|:-----:|:------:|:------:|:-----:|:-------:|'); + expect(body).toContain('| 5 | 3 | 1 | 0 | 1 |'); + + // Check details section + expect(body).toContain('
'); + expect(body).toContain('â„šī¸ CI Information'); + expect(body).toContain('Test recordings for failed tests are available'); + expect(body).toContain('Bundle size is measured'); + expect(body).toContain('
'); + }); + + it('should handle PR without existing description', async () => { + mockGithub.rest.pulls.get.mockResolvedValue({ + data: { + body: null, + }, + }); + + await updatePRDescription(mockGithub, mockContext); + + expect(mockGithub.rest.pulls.update).toHaveBeenCalled(); + const updateCall = mockGithub.rest.pulls.update.mock.calls[0][0]; + expect(updateCall.body).toContain('## CI Results'); + }); + + it('should handle errors in test results', async () => { + const emptyResults: TestResultsInfo = { + total: 0, + passed: 0, + failed: 0, + flaky: 0, + skipped: 0, + tests: [], + }; + + (readTestResults as jest.Mock).mockReturnValue(emptyResults); + (getTestStatus as jest.Mock).mockReturnValue({ + status: '✅ PASSED', + statusColor: 'green', + }); + + await updatePRDescription(mockGithub, mockContext); + + expect(mockGithub.rest.pulls.update).toHaveBeenCalled(); + const updateCall = mockGithub.rest.pulls.update.mock.calls[0][0]; + expect(updateCall.body).toContain('## CI Results'); + expect(updateCall.body).toContain('| 0 | 0 | 0 | 0 | 0 |'); + }); + + it('should include report URL in description', async () => { + await updatePRDescription(mockGithub, mockContext); + + expect(mockGithub.rest.pulls.update).toHaveBeenCalled(); + const updateCall = mockGithub.rest.pulls.update.mock.calls[0][0]; + const expectedUrl = `https://testOwner.github.io/testRepo/123/`; + expect(updateCall.body).toContain(expectedUrl); + }); + + it('should handle failed tests status color', async () => { + const failedResults: TestResultsInfo = { + total: 10, + passed: 8, + failed: 2, + flaky: 0, + skipped: 0, + tests: [], + }; + + (readTestResults as jest.Mock).mockReturnValue(failedResults); + (getTestStatus as jest.Mock).mockReturnValue({ + status: '❌ FAILED', + statusColor: 'red', + }); + + await updatePRDescription(mockGithub, mockContext); + + expect(mockGithub.rest.pulls.update).toHaveBeenCalled(); + const updateCall = mockGithub.rest.pulls.update.mock.calls[0][0]; + expect(updateCall.body).toContain('color: red'); + }); + + it('should handle flaky tests status color', async () => { + const flakyResults: TestResultsInfo = { + total: 10, + passed: 8, + failed: 0, + flaky: 2, + skipped: 0, + tests: [], + }; + + (readTestResults as jest.Mock).mockReturnValue(flakyResults); + (getTestStatus as jest.Mock).mockReturnValue({ + status: 'âš ī¸ FLAKY', + statusColor: 'orange', + }); + + await updatePRDescription(mockGithub, mockContext); + + expect(mockGithub.rest.pulls.update).toHaveBeenCalled(); + const updateCall = mockGithub.rest.pulls.update.mock.calls[0][0]; + expect(updateCall.body).toContain('color: orange'); + }); +}); diff --git a/src/github/types.ts b/src/github/types.ts new file mode 100644 index 000000000..f88c42ced --- /dev/null +++ b/src/github/types.ts @@ -0,0 +1,127 @@ +export interface TestResults { + config: Config; + suites: Suite[]; +} + +export interface Config { + configFile: string; + rootDir: string; + forbidOnly: boolean; + fullyParallel: boolean; + globalSetup: string; + globalTeardown: null; + globalTimeout: number; + grep: Record; + grepInvert: null; + maxFailures: number; + metadata: Metadata; + preserveOutput: string; + reporter: Reporter[]; + reportSlowTests: ReportSlowTests; + quiet: boolean; + projects: Project[]; + shard: null; + updateSnapshots: string; + version: string; + workers: number; + webServer: WebServer; +} + +export interface Metadata { + actualWorkers: number; +} + +export interface Reporter { + [index: number]: string | ReporterConfig; +} + +export interface ReporterConfig { + outputFolder?: string; + outputFile?: string; +} + +export interface ReportSlowTests { + max: number; + threshold: number; +} + +export interface Project { + outputDir: string; + repeatEach: number; + retries: number; + id: string; + name: string; + testDir: string; + testIgnore: unknown[]; + testMatch: string[]; + timeout: number; +} + +export interface WebServer { + command: string; + port: number; +} + +export interface Suite { + title: string; + file: string; + column: number; + line: number; + specs: Spec[]; + suites: Suite[]; +} + +export interface Spec { + title: string; + ok: boolean; + tags: unknown[]; + tests: Test[]; + id: string; + file: string; + line: number; + column: number; +} + +export interface Test { + timeout: number; + annotations: unknown[]; + expectedStatus: string; + projectId: string; + projectName: string; + results: Result[]; + status: string; +} + +export interface Result { + workerIndex: number; + status: string; + duration: number; + errors: unknown[]; + stdout: unknown[]; + stderr: unknown[]; + retry: number; + startTime: string; + attachments: unknown[]; +} + +export interface TestInfo { + title: string; + fullTitle: string; + status: string; + file: string; + skipped: boolean; +} + +export interface TestResultsInfo { + total: number; + passed: number; + failed: number; + flaky: number; + skipped: number; + tests: TestInfo[]; +} + +export interface TestStatusInfo { + status: string; + statusColor: string; +} From 41722c88408a79d6fbc1874adc748f92ada822d2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 23 Nov 2024 02:15:36 +0300 Subject: [PATCH 2/6] chore: some test tests --- tests/suites/tenant/queryEditor/queryEditor.test.ts | 4 ++-- tests/suites/tenant/queryEditor/queryTemplates.test.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/suites/tenant/queryEditor/queryEditor.test.ts b/tests/suites/tenant/queryEditor/queryEditor.test.ts index 2d14840fd..c02f8e9de 100644 --- a/tests/suites/tenant/queryEditor/queryEditor.test.ts +++ b/tests/suites/tenant/queryEditor/queryEditor.test.ts @@ -90,7 +90,7 @@ test.describe('Test Query Editor', async () => { await expect(queryEditor.isExplainButtonEnabled()).resolves.toBe(true); }); - test('Stop button and elapsed time label appears when query is running', async ({page}) => { + test('Stop button and elapsed time label appear when query is running', async ({page}) => { const queryEditor = new QueryEditor(page); await queryEditor.setQuery(longRunningQuery); @@ -100,7 +100,7 @@ test.describe('Test Query Editor', async () => { await expect(queryEditor.isElapsedTimeVisible()).resolves.toBe(true); }); - test('Stop button and elapsed time label disappears after query is stopped', async ({page}) => { + test('Stop button and elapsed time label disappear after query is stopped', async ({page}) => { const queryEditor = new QueryEditor(page); await queryEditor.setQuery(longRunningQuery); diff --git a/tests/suites/tenant/queryEditor/queryTemplates.test.ts b/tests/suites/tenant/queryEditor/queryTemplates.test.ts index f5e098db6..ea17b6b58 100644 --- a/tests/suites/tenant/queryEditor/queryTemplates.test.ts +++ b/tests/suites/tenant/queryEditor/queryTemplates.test.ts @@ -121,7 +121,7 @@ test.describe('Query Templates', () => { await expect(queryEditor.editorTextArea).toHaveValue(initialContent); }); - test('Dont save button in unsaved changes modal allows text to change', async ({page}) => { + test('Dont save button in unsaved changes modal allows to change text', async ({page}) => { const objectSummary = new ObjectSummary(page); const unsavedChangesModal = new UnsavedChangesModal(page); const queryEditor = new QueryEditor(page); From 5aec532186488e3e2fb11ac2db288f968976b493 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 23 Nov 2024 02:19:28 +0300 Subject: [PATCH 3/6] fix: tests --- .github/{ => workflows}/scripts/update-pr-description.js | 0 .github/{ => workflows}/scripts/utils/bundle.js | 0 .github/{ => workflows}/scripts/utils/format.js | 0 .github/{ => workflows}/scripts/utils/results.js | 0 .github/{ => workflows}/scripts/utils/test.js | 0 5 files changed, 0 insertions(+), 0 deletions(-) rename .github/{ => workflows}/scripts/update-pr-description.js (100%) rename .github/{ => workflows}/scripts/utils/bundle.js (100%) rename .github/{ => workflows}/scripts/utils/format.js (100%) rename .github/{ => workflows}/scripts/utils/results.js (100%) rename .github/{ => workflows}/scripts/utils/test.js (100%) diff --git a/.github/scripts/update-pr-description.js b/.github/workflows/scripts/update-pr-description.js similarity index 100% rename from .github/scripts/update-pr-description.js rename to .github/workflows/scripts/update-pr-description.js diff --git a/.github/scripts/utils/bundle.js b/.github/workflows/scripts/utils/bundle.js similarity index 100% rename from .github/scripts/utils/bundle.js rename to .github/workflows/scripts/utils/bundle.js diff --git a/.github/scripts/utils/format.js b/.github/workflows/scripts/utils/format.js similarity index 100% rename from .github/scripts/utils/format.js rename to .github/workflows/scripts/utils/format.js diff --git a/.github/scripts/utils/results.js b/.github/workflows/scripts/utils/results.js similarity index 100% rename from .github/scripts/utils/results.js rename to .github/workflows/scripts/utils/results.js diff --git a/.github/scripts/utils/test.js b/.github/workflows/scripts/utils/test.js similarity index 100% rename from .github/scripts/utils/test.js rename to .github/workflows/scripts/utils/test.js From 8926fb455c473c34e06830c38bfa7c07e1dc36af Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 23 Nov 2024 10:44:24 +0300 Subject: [PATCH 4/6] fix: move tests to github dir --- .../scripts}/__tests__/bundle.test.ts | 5 +--- .../scripts}/__tests__/format.test.ts | 5 +--- .../scripts}/__tests__/results.test.ts | 4 ++-- .../workflows/scripts}/__tests__/test.test.ts | 8 ++----- .../workflows/scripts/__tests__}/types.ts | 0 .../__tests__/update-pr-description.test.ts | 23 ++++++++----------- config-overrides.js | 10 ++++++++ 7 files changed, 26 insertions(+), 29 deletions(-) rename {src/github => .github/workflows/scripts}/__tests__/bundle.test.ts (97%) rename {src/github => .github/workflows/scripts}/__tests__/format.test.ts (97%) rename {src/github => .github/workflows/scripts}/__tests__/results.test.ts (97%) rename {src/github => .github/workflows/scripts}/__tests__/test.test.ts (97%) rename {src/github => .github/workflows/scripts/__tests__}/types.ts (100%) rename {src/github => .github/workflows/scripts}/__tests__/update-pr-description.test.ts (90%) diff --git a/src/github/__tests__/bundle.test.ts b/.github/workflows/scripts/__tests__/bundle.test.ts similarity index 97% rename from src/github/__tests__/bundle.test.ts rename to .github/workflows/scripts/__tests__/bundle.test.ts index 3d15158a6..08f2d6436 100644 --- a/src/github/__tests__/bundle.test.ts +++ b/.github/workflows/scripts/__tests__/bundle.test.ts @@ -1,7 +1,4 @@ -import { - generateBundleSizeSection, - getBundleInfo, -} from '../../../.github/workflows/scripts/utils/bundle'; +import {generateBundleSizeSection, getBundleInfo} from '../utils/bundle'; describe('bundle utils', () => { describe('generateBundleSizeSection', () => { diff --git a/src/github/__tests__/format.test.ts b/.github/workflows/scripts/__tests__/format.test.ts similarity index 97% rename from src/github/__tests__/format.test.ts rename to .github/workflows/scripts/__tests__/format.test.ts index 7f6825772..237f3f250 100644 --- a/src/github/__tests__/format.test.ts +++ b/.github/workflows/scripts/__tests__/format.test.ts @@ -1,7 +1,4 @@ -import { - formatSize, - generateTestChangesSummary, -} from '../../../.github/workflows/scripts/utils/format'; +import {formatSize, generateTestChangesSummary} from '../utils/format'; describe('format utils', () => { describe('formatSize', () => { diff --git a/src/github/__tests__/results.test.ts b/.github/workflows/scripts/__tests__/results.test.ts similarity index 97% rename from src/github/__tests__/results.test.ts rename to .github/workflows/scripts/__tests__/results.test.ts index b8e487992..0dc74793c 100644 --- a/src/github/__tests__/results.test.ts +++ b/.github/workflows/scripts/__tests__/results.test.ts @@ -1,7 +1,7 @@ import fs from 'fs'; -import {getTestStatus, readTestResults} from '../../../.github/workflows/scripts/utils/results'; -import type {TestResults, TestResultsInfo, TestStatusInfo} from '../types'; +import {getTestStatus, readTestResults} from '../utils/results'; +import type {TestResults, TestResultsInfo, TestStatusInfo} from './types'; jest.mock('fs'); diff --git a/src/github/__tests__/test.test.ts b/.github/workflows/scripts/__tests__/test.test.ts similarity index 97% rename from src/github/__tests__/test.test.ts rename to .github/workflows/scripts/__tests__/test.test.ts index 824fe3e58..ecc0e888e 100644 --- a/src/github/__tests__/test.test.ts +++ b/.github/workflows/scripts/__tests__/test.test.ts @@ -1,9 +1,5 @@ -import { - compareTests, - extractTestsFromSuite, - isTestSkipped, -} from '../../../.github/workflows/scripts/utils/test'; -import type {Spec, Suite, TestInfo} from '../types'; +import {compareTests, extractTestsFromSuite, isTestSkipped} from '../utils/test'; +import type {Spec, Suite, TestInfo} from './types'; describe('test utils', () => { describe('isTestSkipped', () => { diff --git a/src/github/types.ts b/.github/workflows/scripts/__tests__/types.ts similarity index 100% rename from src/github/types.ts rename to .github/workflows/scripts/__tests__/types.ts diff --git a/src/github/__tests__/update-pr-description.test.ts b/.github/workflows/scripts/__tests__/update-pr-description.test.ts similarity index 90% rename from src/github/__tests__/update-pr-description.test.ts rename to .github/workflows/scripts/__tests__/update-pr-description.test.ts index f646e48ce..ee9251f1d 100644 --- a/src/github/__tests__/update-pr-description.test.ts +++ b/.github/workflows/scripts/__tests__/update-pr-description.test.ts @@ -1,21 +1,18 @@ -import updatePRDescription from '../../../.github/workflows/scripts/update-pr-description'; -import { - generateBundleSizeSection, - getBundleInfo, -} from '../../../.github/workflows/scripts/utils/bundle'; -import {generateTestChangesSummary} from '../../../.github/workflows/scripts/utils/format'; -import {getTestStatus, readTestResults} from '../../../.github/workflows/scripts/utils/results'; -import {compareTests} from '../../../.github/workflows/scripts/utils/test'; -import type {TestResultsInfo} from '../types'; +import updatePRDescription from '../update-pr-description'; +import {generateBundleSizeSection, getBundleInfo} from '../utils/bundle'; +import {generateTestChangesSummary} from '../utils/format'; +import {getTestStatus, readTestResults} from '../utils/results'; +import {compareTests} from '../utils/test'; +import type {TestResultsInfo} from './types'; // Mock dependencies -jest.mock('../../../.github/workflows/scripts/utils/results', () => ({ +jest.mock('../utils/results', () => ({ readTestResults: jest.fn(), getTestStatus: jest.fn(), })); -jest.mock('../../../.github/workflows/scripts/utils/test'); -jest.mock('../../../.github/workflows/scripts/utils/format'); -jest.mock('../../../.github/workflows/scripts/utils/bundle'); +jest.mock('../utils/test'); +jest.mock('../utils/format'); +jest.mock('../utils/bundle'); jest.mock('fs'); describe('updatePRDescription', () => { diff --git a/config-overrides.js b/config-overrides.js index 243ec5675..a16b73a0f 100644 --- a/config-overrides.js +++ b/config-overrides.js @@ -60,6 +60,16 @@ module.exports = { // see https://github.com/timarney/react-app-rewired/issues/241 config.transformIgnorePatterns = ['node_modules/(?!(@gravity-ui)/)']; + // Add .github directory to roots + config.roots = ['/src', '/.github']; + + // Update testMatch to include .github directory + config.testMatch = [ + '/src/**/__tests__/**/*.{js,jsx,ts,tsx}', + '/src/**/*.{spec,test}.{js,jsx,ts,tsx}', + '/.github/**/*.{spec,test}.{js,jsx,ts,tsx}', + ]; + return config; }, }; From dafb73411a766944a6f76b569db265eb641d055e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 23 Nov 2024 12:38:55 +0300 Subject: [PATCH 5/6] fix: copilot review --- .github/workflows/scripts/__tests__/format.test.ts | 2 +- .github/workflows/scripts/utils/format.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/scripts/__tests__/format.test.ts b/.github/workflows/scripts/__tests__/format.test.ts index 237f3f250..eec6949f5 100644 --- a/.github/workflows/scripts/__tests__/format.test.ts +++ b/.github/workflows/scripts/__tests__/format.test.ts @@ -9,7 +9,7 @@ describe('format utils', () => { it('should format size in MB when greater than or equal to 1024 bytes', () => { const size = 2.5 * 1024; // 2.5 KB -> will be shown in MB - expect(formatSize(size)).toBe('0.00 MB'); + expect(formatSize(size)).toBe('2.50 KB'); }); it('should handle small sizes', () => { diff --git a/.github/workflows/scripts/utils/format.js b/.github/workflows/scripts/utils/format.js index 0fc18ce2a..ed1f6d0f0 100644 --- a/.github/workflows/scripts/utils/format.js +++ b/.github/workflows/scripts/utils/format.js @@ -4,7 +4,7 @@ * @returns {string} Formatted size string with units */ function formatSize(sizeInBytes) { - const MB_THRESHOLD = 1024; + const MB_THRESHOLD = 10 * 1024; if (sizeInBytes >= MB_THRESHOLD) { return `${(sizeInBytes / (1024 * 1024)).toFixed(2)} MB`; } From fd7c8e9543e69f1a7cefb49ac71b3f0131e58bc1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 23 Nov 2024 12:58:21 +0300 Subject: [PATCH 6/6] fix: copilot review --- .github/workflows/scripts/__tests__/bundle.test.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/scripts/__tests__/bundle.test.ts b/.github/workflows/scripts/__tests__/bundle.test.ts index 08f2d6436..2fef551a4 100644 --- a/.github/workflows/scripts/__tests__/bundle.test.ts +++ b/.github/workflows/scripts/__tests__/bundle.test.ts @@ -91,10 +91,10 @@ describe('bundle utils', () => { }); it('should handle missing environment variables', () => { - delete process.env.CURRENT_SIZE; - delete process.env.MAIN_SIZE; - delete process.env.SIZE_DIFF; - delete process.env.SIZE_PERCENT; + process.env.CURRENT_SIZE = undefined; + process.env.MAIN_SIZE = undefined; + process.env.SIZE_DIFF = undefined; + process.env.SIZE_PERCENT = undefined; const result = getBundleInfo(); expect(result).toEqual({