1
0
mirror of https://github.com/desktop/desktop synced 2024-06-30 22:54:41 +00:00

Make the checks and steps summary use same logic

Fixes the sentence logic so that no comma between two statuses
This commit is contained in:
tidy-dev 2024-03-13 12:13:16 -04:00
parent 109e514608
commit 00abd76bf0
3 changed files with 52 additions and 84 deletions

View File

@ -0,0 +1,31 @@
/**
* Converts the array to a comma-separated sentence where the last element is joined by the connector word
*
* Example output:
* [].to_sentence # => ""
* ['one'].to_sentence # => "one"
* ['one', 'two'].to_sentence # => "one and two"
* ['one', 'two', 'three'].to_sentence # => "one, two, and three"
*
* Based on https://gist.github.com/mudge/1076046 to emulate https://apidock.com/rails/Array/to_sentence
*/
export function toSentence(array: ReadonlyArray<string>): string {
const wordsConnector = ', ',
twoWordsConnector = ' and ',
lastWordConnector = ', and '
switch (array.length) {
case 0:
return ''
case 1:
return array.at(0) ?? ''
case 2:
return array.at(0) + twoWordsConnector + array.at(1)
default:
return (
array.slice(0, -1).join(wordsConnector) +
lastWordConnector +
array.at(-1)
)
}
}

View File

@ -6,10 +6,11 @@ import classNames from 'classnames'
import * as octicons from '../octicons/octicons.generated'
import { TooltippedContent } from '../lib/tooltipped-content'
import { CICheckRunActionsJobStepList } from './ci-check-run-actions-job-step-list'
import { APICheckConclusion, IAPIWorkflowJobStep } from '../../lib/api'
import { IAPIWorkflowJobStep } from '../../lib/api'
import { TooltipDirection } from '../lib/tooltip'
import { Button } from '../lib/button'
import { LinkButton } from '../lib/link-button'
import { getCombinedStatusSummary } from './ci-check-run-popover'
interface ICICheckRunListItemProps {
/** The check run to display **/
@ -199,65 +200,7 @@ export class CICheckRunListItem extends React.PureComponent<ICICheckRunListItemP
return ''
}
const conclusions = new Map<APICheckConclusion | 'in_progress', number>()
for (const step of actionJobSteps) {
const conclusion = step.conclusion ?? 'in_progress'
if (!conclusions.has(conclusion)) {
conclusions.set(conclusion, 1)
} else {
const count = conclusions.get(conclusion) ?? 0
conclusions.set(conclusion, count + 1)
}
}
let conclusionText = ''
// The order below was pulled from https://github.com/github/github/blob/5bb7c283fb19aee35f1f3c5eb929a3b031da3512/packages/checks/app/models/status_check_config.rb#L22
const orderedConclussions: ReadonlyArray<{
key: APICheckConclusion | 'in_progress'
adjective: string
}> = [
{ key: APICheckConclusion.ActionRequired, adjective: 'require action' },
{ key: APICheckConclusion.TimedOut, adjective: 'timed out' },
{ key: APICheckConclusion.Failure, adjective: 'failed' },
{ key: APICheckConclusion.Canceled, adjective: 'canceled' },
{ key: APICheckConclusion.Stale, adjective: 'stale' },
{ key: 'in_progress', adjective: 'in progress' },
{ key: APICheckConclusion.Neutral, adjective: 'neutral' },
{ key: APICheckConclusion.Skipped, adjective: 'skipped' },
{ key: APICheckConclusion.Success, adjective: 'successful' },
]
const appended: Array<APICheckConclusion | 'in_progress'> = []
for (const conclusion of orderedConclussions) {
if (conclusions.has(conclusion.key)) {
if (conclusionText !== '') {
conclusionText +=
appended.length < conclusions.size - 1 ? ', ' : ', and '
}
conclusionText += `${conclusions.get(conclusion.key)} ${
conclusion.adjective
}`
appended.push(conclusion.key)
}
if (appended.length === 3) {
break
}
}
if (conclusions.size > 3) {
const remaining = Array.from(conclusions.keys()).filter(
c => !appended.includes(c)
)
const remainingCount = remaining.reduce(
(count, key) => count + (conclusions.get(key) ?? 0),
0
)
return `${conclusionText}, and ${remainingCount} steps`
}
return `${conclusionText} steps`
return getCombinedStatusSummary(actionJobSteps, 'step')
}
public renderStepsHeader = (): JSX.Element | null => {

View File

@ -32,12 +32,29 @@ import {
} from '../../lib/endpoint-capabilities'
import { getPullRequestCommitRef } from '../../models/pull-request'
import { CICheckReRunButton } from './ci-check-re-run-button'
import groupBy from 'lodash/groupBy'
import { toSentence } from '../../lib/to_sentence'
const BlankSlateImage = encodePathAsUrl(
__dirname,
'static/empty-no-pull-requests.svg'
)
export function getCombinedStatusSummary(
statusHolders: ReadonlyArray<{ conclusion: APICheckConclusion | null }>,
description?: 'check' | 'step'
): string {
const conclusions = Object.values(groupBy(statusHolders, 'conclusion')).map(
g =>
`${g.length} ${getCheckRunConclusionAdjective(
g[0].conclusion
).toLocaleLowerCase()}`
)
const pluralize = statusHolders.length > 1 ? `${description}s` : description
return `${toSentence(conclusions)} ${pluralize}`
}
interface ICICheckRunPopoverProps {
readonly dispatcher: Dispatcher
@ -171,30 +188,7 @@ export class CICheckRunPopover extends React.PureComponent<
if (combinedCheck === null || combinedCheck.checks.length === 0) {
return ''
}
const { checks } = combinedCheck
const conclusionMap = new Map<string, number>()
for (const check of checks) {
const adj = getCheckRunConclusionAdjective(
check.conclusion
).toLocaleLowerCase()
conclusionMap.set(adj, (conclusionMap.get(adj) ?? 0) + 1)
}
const summaryArray = []
for (const [conclusion, count] of conclusionMap.entries()) {
summaryArray.push({ count, conclusion })
}
if (summaryArray.length > 1) {
const output = summaryArray.map(
({ count, conclusion }) => `${count} ${conclusion}`
)
return `${output.slice(0, -1).join(', ')}, and ${output.slice(-1)} checks`
}
const pluralize = summaryArray[0].count > 1 ? 'checks' : 'check'
return `${summaryArray[0].count} ${summaryArray[0].conclusion} ${pluralize}`
return getCombinedStatusSummary(combinedCheck.checks, 'check')
}
private rerunChecks = (