feat: move clean up summary as a blocking error

This commit is contained in:
tdgao
2026-08-26 18:35:07 -06:00
parent 5dcd66f27f
commit 3ec0dfcf35
5 changed files with 40 additions and 13 deletions
@@ -21,7 +21,7 @@ const summaryErrorCodes: readonly ProjectTextValidationCode[] = [
'text-banned-link', 'text-banned-link',
'summary-link', 'summary-link',
] ]
const summaryWarningCodes: readonly ProjectTextValidationCode[] = ['summary-matches-title'] const summaryRequiredCodes: readonly ProjectTextValidationCode[] = ['summary-matches-title']
const descriptionErrorCodes: readonly ProjectTextValidationCode[] = [ const descriptionErrorCodes: readonly ProjectTextValidationCode[] = [
'text-slur', 'text-slur',
'text-profanity', 'text-profanity',
@@ -151,10 +151,10 @@ export const projectValidationNags: Nag[] = [
defaultMessage: 'Review the project summary', defaultMessage: 'Review the project summary',
}), }),
description: (context) => description: (context) =>
getFailureDescription(context, ['summary'], 'warn', summaryWarningCodes), getFailureDescription(context, ['summary'], 'error', summaryRequiredCodes),
status: 'warning', status: 'required',
shouldShow: (context) => shouldShow: (context) =>
getFirstFailure(context, ['summary'], 'warn', summaryWarningCodes) !== undefined, getFirstFailure(context, ['summary'], 'error', summaryRequiredCodes) !== undefined,
link: { link: {
path: 'settings', path: 'settings',
title: defineMessage({ title: defineMessage({
@@ -189,7 +189,7 @@ export const projectValidationNags: Nag[] = [
defaultMessage: 'Clean up the summary', defaultMessage: 'Clean up the summary',
}), }),
description: (context) => getCodedFailureDescription(context, 'summary-special-formatting'), description: (context) => getCodedFailureDescription(context, 'summary-special-formatting'),
status: 'warning', status: 'required',
shouldShow: (context) => getCodedFailure(context, 'summary-special-formatting') !== undefined, shouldShow: (context) => getCodedFailure(context, 'summary-special-formatting') !== undefined,
link: { link: {
path: 'settings', path: 'settings',
@@ -366,7 +366,7 @@
"defaultMessage": "The detected slur “{value}” is not allowed." "defaultMessage": "The detected slur “{value}” is not allowed."
}, },
"project.text-validation.summary-matches-title": { "project.text-validation.summary-matches-title": {
"defaultMessage": "A project summary should not be the same as its title." "defaultMessage": "A project summary cannot be the same as it's title."
}, },
"project.text-validation.summary-too-short": { "project.text-validation.summary-too-short": {
"defaultMessage": "Your summary is {length, plural, one {# character} other {# characters}}. At least {minChars, plural, one {# character} other {# characters}} is recommended to create an informative and enticing summary." "defaultMessage": "Your summary is {length, plural, one {# character} other {# characters}}. At least {minChars, plural, one {# character} other {# characters}} is recommended to create an informative and enticing summary."
@@ -84,7 +84,7 @@ const messages = defineMessages({
}, },
summaryMatchesTitle: { summaryMatchesTitle: {
id: 'project.text-validation.summary-matches-title', id: 'project.text-validation.summary-matches-title',
defaultMessage: 'A project summary should not be the same as its title.', defaultMessage: "A project summary cannot be the same as it's title.",
}, },
summaryTooShort: { summaryTooShort: {
id: 'project.text-validation.summary-too-short', id: 'project.text-validation.summary-too-short',
@@ -138,8 +138,8 @@ export function normalizeProjectFieldText(value: string) {
} }
export function projectSummaryMatchesTitle(summary: string, title: string) { export function projectSummaryMatchesTitle(summary: string, title: string) {
const normalizedSummary = normalizeProjectFieldText(summary) const normalizedSummary = normalizeProjectFieldText(summary).replace(/\s+/g, '')
const normalizedTitle = normalizeProjectFieldText(title) const normalizedTitle = normalizeProjectFieldText(title).replace(/\s+/g, '')
return normalizedSummary.length > 0 && normalizedSummary === normalizedTitle return normalizedSummary.length > 0 && normalizedSummary === normalizedTitle
} }
@@ -353,7 +353,7 @@ export function validateProjectSummary(
return [ return [
{ {
code: 'summary-matches-title', code: 'summary-matches-title',
severity: 'warn', severity: 'error',
message: messages.summaryMatchesTitle, message: messages.summaryMatchesTitle,
}, },
] ]
@@ -372,7 +372,7 @@ export function validateProjectSummary(
if (hasProjectSummaryFormatting(summary) || containsExplicitLink) { if (hasProjectSummaryFormatting(summary) || containsExplicitLink) {
results.push({ results.push({
code: containsExplicitLink ? 'summary-link' : 'summary-special-formatting', code: containsExplicitLink ? 'summary-link' : 'summary-special-formatting',
severity: containsExplicitLink ? 'error' : 'warn', severity: 'error',
message: messages.summarySpecialFormatting, message: messages.summarySpecialFormatting,
}) })
} }
@@ -15,8 +15,9 @@ import {
validateProjectTitle, validateProjectTitle,
} from './index.ts' } from './index.ts'
test('compares summaries and titles after trimming and Unicode normalization', () => { test('compares summaries and titles without whitespace and after Unicode normalization', () => {
assert.equal(projectSummaryMatchesTitle(' Caf\u00e9 ', 'Cafe\u0301'), true) assert.equal(projectSummaryMatchesTitle(' Caf\u00e9 ', 'Cafe\u0301'), true)
assert.equal(projectSummaryMatchesTitle('Project summary', 'Projectsummary'), true)
assert.equal(projectSummaryMatchesTitle('Project summary', 'Project title'), false) assert.equal(projectSummaryMatchesTitle('Project summary', 'Project title'), false)
assert.equal(projectSummaryMatchesTitle('', ''), false) assert.equal(projectSummaryMatchesTitle('', ''), false)
}) })
@@ -127,7 +128,11 @@ test('validates project summaries', () => {
validateProjectSummary(' Caf\u00e9 ', 'Cafe\u0301')[0]?.message.id, validateProjectSummary(' Caf\u00e9 ', 'Cafe\u0301')[0]?.message.id,
'project.text-validation.summary-matches-title', 'project.text-validation.summary-matches-title',
) )
assert.equal(validateProjectSummary(' Caf\u00e9 ', 'Cafe\u0301')[0]?.severity, 'warn') assert.equal(validateProjectSummary(' Caf\u00e9 ', 'Cafe\u0301')[0]?.severity, 'error')
assert.equal(
validateProjectSummary(' Caf\u00e9 ', 'Cafe\u0301')[0]?.message.defaultMessage,
"A project summary cannot be the same as it's title.",
)
assert.deepEqual(validateProjectSummary('Short summary', 'Project title'), [ assert.deepEqual(validateProjectSummary('Short summary', 'Project title'), [
{ {
code: 'summary-too-short', code: 'summary-too-short',
@@ -148,6 +153,7 @@ test('validates project summaries', () => {
validateProjectSummary('# Short summary', 'Project title').map(({ code }) => code), validateProjectSummary('# Short summary', 'Project title').map(({ code }) => code),
['summary-too-short', 'summary-special-formatting'], ['summary-too-short', 'summary-special-formatting'],
) )
assert.equal(validateProjectSummary('# Short summary', 'Project title')[1]?.severity, 'error')
assert.equal( assert.equal(
validateProjectSummary('# Short summary', 'Project title')[1]?.message.defaultMessage, validateProjectSummary('# Short summary', 'Project title')[1]?.message.defaultMessage,
summaryContentMessage, summaryContentMessage,
@@ -87,6 +87,12 @@ test('reports whether a project has field validation failures', () => {
}) })
assert.equal(hasProjectFieldValidationFailures(validProject), false) assert.equal(hasProjectFieldValidationFailures(validProject), false)
assert.equal(hasProjectFieldValidationFailures(invalidProject), true) assert.equal(hasProjectFieldValidationFailures(invalidProject), true)
assert.equal(
hasProjectFieldValidationFailures(
createProject({ name: 'Ordinary Tools', summary: 'Ordinary Tools' }),
),
true,
)
}) })
test('treats version numbers and explicit summary links as errors', () => { test('treats version numbers and explicit summary links as errors', () => {
@@ -148,3 +154,18 @@ test('reports summary recommendations without invalidating the project', () => {
}) })
assert.equal(hasProjectFieldValidationFailures(project), false) assert.equal(hasProjectFieldValidationFailures(project), false)
}) })
test('treats summary formatting as a field validation failure', () => {
const project = createProject({ summary: '# A formatted project summary' })
const result = validateProjectFields(project)
assert.equal(result.valid, false)
assert.deepEqual(
result.failures.map(({ code, severity }) => ({ code, severity })),
[
{ code: 'summary-too-short', severity: 'warn' },
{ code: 'summary-special-formatting', severity: 'error' },
],
)
assert.equal(hasProjectFieldValidationFailures(project), true)
})