From 6b8d93433e65ff2e97a92489dadf70f2700c4eca Mon Sep 17 00:00:00 2001 From: Philipp Daun Date: Thu, 7 Sep 2023 19:59:47 +0200 Subject: [PATCH] Adjust formatting --- .prettierrc.json | 8 +- __tests__/icons.test.ts | 6 +- __tests__/index.test.ts | 98 ++++++++-------- src/formatting.ts | 60 +++++----- src/fs.ts | 14 +-- src/icons.ts | 84 +++++++------- src/index.ts | 242 ++++++++++++++++++++-------------------- src/report.ts | 28 ++--- 8 files changed, 270 insertions(+), 270 deletions(-) diff --git a/.prettierrc.json b/.prettierrc.json index a378146..55e1e67 100644 --- a/.prettierrc.json +++ b/.prettierrc.json @@ -1,15 +1,13 @@ { "printWidth": 80, "tabWidth": 2, - "useTabs": false, + "useTabs": true, "semi": false, "singleQuote": true, - "quoteProps": "as-needed", - "jsxSingleQuote": false, + "quoteProps": "consistent", "trailingComma": "none", "bracketSpacing": true, - "bracketSameLine": true, - "arrowParens": "avoid", + "arrowParens": "always", "proseWrap": "always", "htmlWhitespaceSensitivity": "css", "endOfLine": "lf" diff --git a/__tests__/icons.test.ts b/__tests__/icons.test.ts index d8b299f..ef7a383 100644 --- a/__tests__/icons.test.ts +++ b/__tests__/icons.test.ts @@ -6,7 +6,7 @@ import { expect } from '@jest/globals' import { renderIcon } from '../src/icons' describe('icons.ts', () => { - it('returns a string', async () => { - expect(renderIcon('something')).toBe('') - }) + it('returns a string', async () => { + expect(renderIcon('something')).toBe('') + }) }) diff --git a/__tests__/index.test.ts b/__tests__/index.test.ts index 8d7a010..f77e13b 100644 --- a/__tests__/index.test.ts +++ b/__tests__/index.test.ts @@ -22,59 +22,59 @@ const runMock = jest.spyOn(index, 'run') const timeRegex = /^\d{2}:\d{2}:\d{2}/ describe('action', () => { - beforeEach(() => { - jest.clearAllMocks() - }) + beforeEach(() => { + jest.clearAllMocks() + }) - it('sets the time output', async () => { - // Set the action's inputs as return values from core.getInput() - getInputMock.mockImplementation((name: string): string => { - switch (name) { - case 'milliseconds': - return '500' - default: - return '' - } - }) + it('sets the time output', async () => { + // Set the action's inputs as return values from core.getInput() + getInputMock.mockImplementation((name: string): string => { + switch (name) { + case 'milliseconds': + return '500' + default: + return '' + } + }) - await index.run() - expect(runMock).toHaveReturned() + await index.run() + expect(runMock).toHaveReturned() - // Verify that all of the core library functions were called correctly - expect(debugMock).toHaveBeenNthCalledWith(1, 'Waiting 500 milliseconds ...') - expect(debugMock).toHaveBeenNthCalledWith( - 2, - expect.stringMatching(timeRegex) - ) - expect(debugMock).toHaveBeenNthCalledWith( - 3, - expect.stringMatching(timeRegex) - ) - expect(setOutputMock).toHaveBeenNthCalledWith( - 1, - 'time', - expect.stringMatching(timeRegex) - ) - }) + // Verify that all of the core library functions were called correctly + expect(debugMock).toHaveBeenNthCalledWith(1, 'Waiting 500 milliseconds ...') + expect(debugMock).toHaveBeenNthCalledWith( + 2, + expect.stringMatching(timeRegex) + ) + expect(debugMock).toHaveBeenNthCalledWith( + 3, + expect.stringMatching(timeRegex) + ) + expect(setOutputMock).toHaveBeenNthCalledWith( + 1, + 'time', + expect.stringMatching(timeRegex) + ) + }) - it('sets a failed status', async () => { - // Set the action's inputs as return values from core.getInput() - getInputMock.mockImplementation((name: string): string => { - switch (name) { - case 'milliseconds': - return 'this is not a number' - default: - return '' - } - }) + it('sets a failed status', async () => { + // Set the action's inputs as return values from core.getInput() + getInputMock.mockImplementation((name: string): string => { + switch (name) { + case 'milliseconds': + return 'this is not a number' + default: + return '' + } + }) - await index.run() - expect(runMock).toHaveReturned() + await index.run() + expect(runMock).toHaveReturned() - // Verify that all of the core library functions were called correctly - expect(setFailedMock).toHaveBeenNthCalledWith( - 1, - 'milliseconds not a number' - ) - }) + // Verify that all of the core library functions were called correctly + expect(setFailedMock).toHaveBeenNthCalledWith( + 1, + 'milliseconds not a number' + ) + }) }) diff --git a/src/formatting.ts b/src/formatting.ts index ac54a10..6cceccb 100644 --- a/src/formatting.ts +++ b/src/formatting.ts @@ -1,48 +1,48 @@ export function renderMarkdownTable( - rows: string[][], - headers: string[] = [] + rows: string[][], + headers: string[] = [] ): string { - if (!rows.length) { - return '' - } - const align = [':---', ':---:', ':---:', ':---:'].slice(0, rows[0].length) - const lines = [headers, align, ...rows].filter(Boolean) - return lines.map(columns => `| ${columns.join(' | ')} |`).join('\n') + if (!rows.length) { + return '' + } + const align = [':---', ':---:', ':---:', ':---:'].slice(0, rows[0].length) + const lines = [headers, align, ...rows].filter(Boolean) + return lines.map((columns) => `| ${columns.join(' | ')} |`).join('\n') } export function formatDuration(milliseconds: number): string { - const SECOND = 1000 - const MINUTE = 60 * SECOND - const HOUR = 60 * MINUTE - const DAY = 24 * HOUR + const SECOND = 1000 + const MINUTE = 60 * SECOND + const HOUR = 60 * MINUTE + const DAY = 24 * HOUR - let remaining = milliseconds + let remaining = milliseconds - const days = Math.floor(remaining / DAY) - remaining %= DAY + const days = Math.floor(remaining / DAY) + remaining %= DAY - const hours = Math.floor(remaining / HOUR) - remaining %= HOUR + const hours = Math.floor(remaining / HOUR) + remaining %= HOUR - const minutes = Math.floor(remaining / MINUTE) - remaining %= MINUTE + const minutes = Math.floor(remaining / MINUTE) + remaining %= MINUTE - const seconds = +(remaining / SECOND).toFixed(1) + const seconds = +(remaining / SECOND).toFixed(1) - return [ - days && `${days} ${n('day', days)}`, - hours && `${hours} ${n('hour', hours)}`, - minutes && `${minutes} ${n('minute', minutes)}`, - seconds && `${seconds} ${n('second', seconds)}` - ] - .filter(Boolean) - .join(', ') + return [ + days && `${days} ${n('day', days)}`, + hours && `${hours} ${n('hour', hours)}`, + minutes && `${minutes} ${n('minute', minutes)}`, + seconds && `${seconds} ${n('second', seconds)}` + ] + .filter(Boolean) + .join(', ') } export function upperCaseFirst(str: string): string { - return str.charAt(0).toUpperCase() + str.slice(1) + return str.charAt(0).toUpperCase() + str.slice(1) } export function n(str: string, count: number): string { - return count === 1 ? str : `${str}s` + return count === 1 ? str : `${str}s` } diff --git a/src/fs.ts b/src/fs.ts index 3a39821..4acbf97 100644 --- a/src/fs.ts +++ b/src/fs.ts @@ -1,14 +1,14 @@ import fs from 'fs/promises' export async function fileExists(filename: string): Promise { - try { - await fs.access(filename, fs.constants.F_OK) - return true - } catch (e) { - return false - } + try { + await fs.access(filename, fs.constants.F_OK) + return true + } catch (e) { + return false + } } export async function readFile(path: string): Promise { - return await fs.readFile(path, { encoding: 'utf8' }) + return await fs.readFile(path, { encoding: 'utf8' }) } diff --git a/src/icons.ts b/src/icons.ts index 5af3bdb..e6db195 100644 --- a/src/icons.ts +++ b/src/icons.ts @@ -5,57 +5,57 @@ const iconSize = 14 const defaultIconStyle = 'octicons' export const icons: Record = { - octicons: { - failed: 'stop', - passed: 'check-circle', - flaky: 'alert', - skipped: 'skip', - stats: 'pulse', - duration: 'clock', - link: 'link-external', - report: 'package', - commit: 'git-pull-request' - }, - emojis: { - failed: '❌', - passed: '✅', - flaky: '⚠️', - skipped: '⏭️', - stats: '', - duration: '', - link: '', - report: '', - commit: '' - } + octicons: { + failed: 'stop', + passed: 'check-circle', + flaky: 'alert', + skipped: 'skip', + stats: 'pulse', + duration: 'clock', + link: 'link-external', + report: 'package', + commit: 'git-pull-request' + }, + emojis: { + failed: '❌', + passed: '✅', + flaky: '⚠️', + skipped: '⏭️', + stats: '', + duration: '', + link: '', + report: '', + commit: '' + } } const iconColors: IconColors = { - failed: 'da3633', - passed: '3fb950', - flaky: 'd29922', - skipped: '0967d9', - icon: 'abb4bf' + failed: 'da3633', + passed: '3fb950', + flaky: 'd29922', + skipped: '0967d9', + icon: 'abb4bf' } export function renderIcon( - status: string, - { iconStyle = defaultIconStyle }: { iconStyle?: keyof typeof icons } = {} + status: string, + { iconStyle = defaultIconStyle }: { iconStyle?: keyof typeof icons } = {} ): string { - if (iconStyle === 'emojis') { - return icons.emojis[status] || '' - } else { - const color = iconColors[status] || iconColors.icon - return createOcticonUrl(icons.octicons[status], { label: status, color }) - } + if (iconStyle === 'emojis') { + return icons.emojis[status] || '' + } else { + const color = iconColors[status] || iconColors.icon + return createOcticonUrl(icons.octicons[status], { label: status, color }) + } } function createOcticonUrl( - icon: string, - { label = 'icon', color = iconColors.icon, size = iconSize } = {} + icon: string, + { label = 'icon', color = iconColors.icon, size = iconSize } = {} ): string { - if (icon) { - return `![${label}](https://icongr.am/octicons/${icon}.svg?size=${size}&color=${color})` - } else { - return '' - } + if (icon) { + return `![${label}](https://icongr.am/octicons/${icon}.svg?size=${size}&color=${color})` + } else { + return '' + } } diff --git a/src/index.ts b/src/index.ts index e2e06a6..ba3044f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,11 +1,11 @@ import path from 'path' import { - getInput, - setOutput, - setFailed, - startGroup, - endGroup, - debug + getInput, + setOutput, + setFailed, + startGroup, + endGroup, + debug } from '@actions/core' import { context, getOctokit } from '@actions/github' import { fileExists, readFile } from './fs' @@ -15,135 +15,135 @@ import { parseReport, renderReportSummary } from './report' * The main function for the action. */ export async function run(): Promise { - const cwd = process.cwd() - const token = getInput('github-token') - const octokit = getOctokit(token) + const cwd = process.cwd() + const token = getInput('github-token') + const octokit = getOctokit(token) - const reportFile = getInput('report-file') - const commentTitle = getInput('comment-title') - const iconStyle = getInput('icon-style') + const reportFile = getInput('report-file') + const commentTitle = getInput('comment-title') + const iconStyle = getInput('icon-style') - const { eventName, repo, payload } = context - const { owner, number: pull_number } = context.issue + const { eventName, repo, payload } = context + const { owner, number: pull_number } = context.issue - const base: { ref?: string; sha?: string } = {} - const head: { ref?: string; sha?: string } = {} - if (eventName === 'push') { - base.ref = payload.ref - base.sha = payload.before - head.ref = payload.ref - head.sha = payload.after - console.log(`Commit pushed onto ${base.ref} (${head.sha})`) - } else if ( - eventName === 'pull_request' || - eventName === 'pull_request_target' - ) { - base.ref = payload.pull_request?.base?.ref - base.sha = payload.pull_request?.base?.sha - head.ref = payload.pull_request?.head?.ref - head.sha = payload.pull_request?.head?.sha - console.log(`PR #${pull_number} targeting ${base.ref} (${head.sha})`) - } else { - throw new Error( - `Unsupported event type: ${eventName}. Only "pull_request", "pull_request_target", and "push" triggered workflows are currently supported.` - ) - } + const base: { ref?: string; sha?: string } = {} + const head: { ref?: string; sha?: string } = {} + if (eventName === 'push') { + base.ref = payload.ref + base.sha = payload.before + head.ref = payload.ref + head.sha = payload.after + console.log(`Commit pushed onto ${base.ref} (${head.sha})`) + } else if ( + eventName === 'pull_request' || + eventName === 'pull_request_target' + ) { + base.ref = payload.pull_request?.base?.ref + base.sha = payload.pull_request?.base?.sha + head.ref = payload.pull_request?.head?.ref + head.sha = payload.pull_request?.head?.sha + console.log(`PR #${pull_number} targeting ${base.ref} (${head.sha})`) + } else { + throw new Error( + `Unsupported event type: ${eventName}. Only "pull_request", "pull_request_target", and "push" triggered workflows are currently supported.` + ) + } - const reportPath = path.resolve(cwd, reportFile) - const reportExists = await fileExists(reportPath) - if (!reportExists) { - debug(`Failed to find report file at path ${reportPath}`) - throw new Error( - `Report file ${reportFile} not found. Make sure Playwright is configured to generate a JSON report.` - ) - } + const reportPath = path.resolve(cwd, reportFile) + const reportExists = await fileExists(reportPath) + if (!reportExists) { + debug(`Failed to find report file at path ${reportPath}`) + throw new Error( + `Report file ${reportFile} not found. Make sure Playwright is configured to generate a JSON report.` + ) + } - const data = await readFile(reportPath) - const report = parseReport(data) - const summary = renderReportSummary(report, { - commit: head.sha, - title: commentTitle, - iconStyle - }) + const data = await readFile(reportPath) + const report = parseReport(data) + const summary = renderReportSummary(report, { + commit: head.sha, + title: commentTitle, + iconStyle + }) - const prefix = '' - const body = `${prefix}\n\n${summary}` - let commentId = null + const prefix = '' + const body = `${prefix}\n\n${summary}` + let commentId = null - if (eventName !== 'pull_request' && eventName !== 'pull_request_target') { - console.log( - 'No PR associated with this action run. Not posting a check or comment.' - ) - } else { - startGroup(`Commenting test report on PR`) - try { - const { data: comments } = await octokit.rest.issues.listComments({ - ...repo, - issue_number: pull_number - }) - const existingComment = comments.findLast( - c => c.user?.type === 'Bot' && c.body?.includes(prefix) - ) - commentId = existingComment?.id || null - } catch (error: any) { - console.error(`Error fetching existing comments: ${error.message}`) - } + if (eventName !== 'pull_request' && eventName !== 'pull_request_target') { + console.log( + 'No PR associated with this action run. Not posting a check or comment.' + ) + } else { + startGroup(`Commenting test report on PR`) + try { + const { data: comments } = await octokit.rest.issues.listComments({ + ...repo, + issue_number: pull_number + }) + const existingComment = comments.findLast( + (c) => c.user?.type === 'Bot' && c.body?.includes(prefix) + ) + commentId = existingComment?.id || null + } catch (error: any) { + console.error(`Error fetching existing comments: ${error.message}`) + } - if (commentId) { - console.log(`Found previous comment #${commentId}`) - try { - await octokit.rest.issues.updateComment({ - ...repo, - comment_id: commentId, - body - }) - console.log(`Updated previous comment #${commentId}`) - } catch (error: any) { - console.error(`Error updating previous comment: ${error.message}`) - commentId = null - } - } + if (commentId) { + console.log(`Found previous comment #${commentId}`) + try { + await octokit.rest.issues.updateComment({ + ...repo, + comment_id: commentId, + body + }) + console.log(`Updated previous comment #${commentId}`) + } catch (error: any) { + console.error(`Error updating previous comment: ${error.message}`) + commentId = null + } + } - if (!commentId) { - console.log('Creating new comment') - try { - const { data: newComment } = await octokit.rest.issues.createComment({ - ...repo, - issue_number: pull_number, - body - }) - commentId = newComment.id - console.log(`Created new comment #${commentId}`) - } catch (error: any) { - console.error(`Error creating comment: ${error.message}`) - console.log(`Submitting PR review comment instead...`) - try { - const { issue } = context - await octokit.rest.pulls.createReview({ - owner, - repo: issue.repo, - pull_number: issue.number, - event: 'COMMENT', - body - }) - } catch (error: any) { - console.error(`Error creating PR review: ${error.message}`) - } - } - } - endGroup() - } + if (!commentId) { + console.log('Creating new comment') + try { + const { data: newComment } = await octokit.rest.issues.createComment({ + ...repo, + issue_number: pull_number, + body + }) + commentId = newComment.id + console.log(`Created new comment #${commentId}`) + } catch (error: any) { + console.error(`Error creating comment: ${error.message}`) + console.log(`Submitting PR review comment instead...`) + try { + const { issue } = context + await octokit.rest.pulls.createReview({ + owner, + repo: issue.repo, + pull_number: issue.number, + event: 'COMMENT', + body + }) + } catch (error: any) { + console.error(`Error creating PR review: ${error.message}`) + } + } + } + endGroup() + } - if (!commentId) { - const intro = `Unable to comment on your PR — this can happen for PR's originating from a fork without write permissions. You can copy the test results directly into a comment using the markdown summary below:` - console.log(`${intro}\n\n${body}`) - } + if (!commentId) { + const intro = `Unable to comment on your PR — this can happen for PR's originating from a fork without write permissions. You can copy the test results directly into a comment using the markdown summary below:` + console.log(`${intro}\n\n${body}`) + } - setOutput('comment-id', commentId) + setOutput('comment-id', commentId) } if (process.env.GITHUB_ACTIONS === 'true') { - (async () => { + ;(async () => { try { await run() } catch (error) { diff --git a/src/report.ts b/src/report.ts index 45c3545..ec7d327 100644 --- a/src/report.ts +++ b/src/report.ts @@ -126,12 +126,14 @@ export function parseReport(data: string): ReportSummary { const workers: number = report.config.metadata.actualWorkers || report.config.workers || 1 const shards: number = report.config.shard?.total || 0 - const projects: string[] = report.config.projects.map(project => project.name) + const projects: string[] = report.config.projects.map( + (project) => project.name + ) - const files: string[] = report.suites.map(file => file.title) - const suites: string[] = report.suites.flatMap(file => + const files: string[] = report.suites.map((file) => file.title) + const suites: string[] = report.suites.flatMap((file) => file.suites.length - ? [...file.suites.map(suite => `${file.title} > ${suite.title}`)] + ? [...file.suites.map((suite) => `${file.title} > ${suite.title}`)] : [file.title] ) const specs: SpecSummary[] = report.suites.reduce((all, file) => { @@ -145,10 +147,10 @@ export function parseReport(data: string): ReportSummary { } return all }, [] as SpecSummary[]) - const failed = specs.filter(spec => spec.failed) - const passed = specs.filter(spec => spec.passed) - const flaky = specs.filter(spec => spec.flaky) - const skipped = specs.filter(spec => spec.skipped) + const failed = specs.filter((spec) => spec.failed) + const passed = specs.filter((spec) => spec.passed) + const flaky = specs.filter((spec) => spec.flaky) + const skipped = specs.filter((spec) => spec.skipped) return { version, @@ -172,7 +174,7 @@ function parseSpec(spec: Spec, parents: Suite[] = []): SpecSummary { const status = test.status const project = test.projectName - const path = [project, ...parents.map(p => p.title), spec.title].filter( + const path = [project, ...parents.map((p) => p.title), spec.title].filter( Boolean ) const title = path.join(' → ') @@ -227,25 +229,25 @@ export function renderReportSummary( // Lists of failed/skipped tests const listStatuses = ['failed', 'flaky', 'skipped'] as const - const details = listStatuses.map(status => { + const details = listStatuses.map((status) => { const tests = report[status] if (tests.length) { return `
${upperCaseFirst(status)} tests -
    ${tests.map(test => `
  • ${test.title}
  • `).join('\n')}
+
    ${tests.map((test) => `
  • ${test.title}
  • `).join('\n')}
` } }) paragraphs.push( details .filter(Boolean) - .map(md => (md as string).trim()) + .map((md) => (md as string).trim()) .join('\n') ) return paragraphs - .map(p => p.trim()) + .map((p) => p.trim()) .filter(Boolean) .join('\n\n') }