-
Notifications
You must be signed in to change notification settings - Fork 81
fix: Normalize terminology in sample updates #1397
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| const horizontalWhitespace = String.raw`[ \t]+`; | ||
| const legacyAspirePattern = String.raw`\.NET${horizontalWhitespace}Aspire`; | ||
| const legacyAppHostPattern = String.raw`\bapp${horizontalWhitespace}host\b`; | ||
|
|
||
| const aspireWithArticle = new RegExp( | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The article correction only recognizes unformatted text, but |
||
| String.raw`\b([Aa])${horizontalWhitespace}${legacyAspirePattern}\b`, | ||
| 'gi' | ||
| ); | ||
| const legacyAspire = new RegExp(legacyAspirePattern, 'gi'); | ||
| const legacyAppHost = new RegExp(legacyAppHostPattern, 'gi'); | ||
|
|
||
| export function normalizeAspireTerminology(text: string): string { | ||
| return text | ||
| .replace(aspireWithArticle, (_match, article: string) => | ||
| article === 'A' ? 'An Aspire' : 'an Aspire' | ||
| ) | ||
| .replace(legacyAspire, 'Aspire') | ||
| .replace(legacyAppHost, 'AppHost'); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,85 @@ | ||
| import { describe, expect, test } from 'vitest'; | ||
|
|
||
| import samples from '@data/samples.json'; | ||
|
|
||
| import { normalizeAspireTerminology } from '../../scripts/aspire-terminology'; | ||
| import { type SampleResult, normalizeSampleTerminology } from '../../scripts/update-samples'; | ||
|
|
||
| const legacyAspireName = ['.NET', 'Aspire'].join(' '); | ||
| const legacyAppHostName = ['app', 'host'].join(' '); | ||
|
|
||
| describe('Aspire terminology normalization', () => { | ||
| test.each([ | ||
| ['uppercase article', `A ${legacyAspireName} project`, 'An Aspire project'], | ||
| ['lowercase article', `Build a ${legacyAspireName} project`, 'Build an Aspire project'], | ||
| [ | ||
| 'extra horizontal whitespace', | ||
| `A ${['.NET', 'Aspire'].join('\t')} project`, | ||
| 'An Aspire project', | ||
| ], | ||
| ])('uses one space for the %s case', (_scenario, input, expected) => { | ||
| expect(normalizeAspireTerminology(input)).toBe(expected); | ||
| }); | ||
| }); | ||
|
|
||
| describe('sample terminology normalization', () => { | ||
| test('normalizes every generated text field', () => { | ||
| const sample: SampleResult = { | ||
| name: 'terminology-sample', | ||
| title: `${legacyAspireName} ${legacyAppHostName} sample`, | ||
| description: `A ${legacyAspireName} ${legacyAppHostName} project.`, | ||
| href: 'https://github.com/microsoft/aspire-samples/tree/main/samples/terminology-sample', | ||
| readme: `# ${legacyAspireName} sample\n\nRun the ${legacyAppHostName}.`, | ||
| readmeRaw: `# ${legacyAspireName} sample\n\nRun the ${legacyAppHostName.toUpperCase()}.`, | ||
| tags: ['csharp'], | ||
| thumbnail: null, | ||
| appHost: 'csproj', | ||
| appHostPath: 'Terminology.AppHost/AppHost.cs', | ||
| appHostCode: `// Keep the container running between ${legacyAppHostName} sessions.`, | ||
| }; | ||
|
|
||
| expect(normalizeSampleTerminology(sample)).toEqual({ | ||
| ...sample, | ||
| title: 'Aspire AppHost sample', | ||
| description: 'An Aspire AppHost project.', | ||
| readme: '# Aspire sample\n\nRun the AppHost.', | ||
| readmeRaw: '# Aspire sample\n\nRun the AppHost.', | ||
| appHostCode: '// Keep the container running between AppHost sessions.', | ||
| }); | ||
| }); | ||
|
|
||
| test('does not rewrite related words that are not deprecated terms', () => { | ||
| const sample: SampleResult = { | ||
| name: 'hosting-sample', | ||
| title: 'App hosting sample', | ||
| description: null, | ||
| href: 'https://github.com/microsoft/aspire-samples/tree/main/samples/hosting-sample', | ||
| readme: 'This sample demonstrates app hosting.', | ||
| readmeRaw: 'This sample demonstrates app hosting.', | ||
| tags: [], | ||
| thumbnail: null, | ||
| appHost: null, | ||
| appHostPath: null, | ||
| appHostCode: null, | ||
| }; | ||
|
|
||
| expect(normalizeSampleTerminology(sample)).toEqual(sample); | ||
| }); | ||
|
|
||
| test('keeps generated sample data free of deprecated terminology', () => { | ||
| const textFields = ['title', 'description', 'readme', 'readmeRaw', 'appHostCode'] as const; | ||
| const deprecatedTerminology = new RegExp( | ||
| [legacyAspireName, legacyAppHostName] | ||
| .map((term) => term.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')) | ||
| .join('|'), | ||
| 'i' | ||
| ); | ||
| const violations = samples.flatMap((sample) => | ||
| textFields | ||
| .filter((field) => deprecatedTerminology.test(sample[field] ?? '')) | ||
| .map((field) => `${sample.name}.${field}`) | ||
| ); | ||
|
|
||
| expect(violations).toEqual([]); | ||
| }); | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This pattern also matches the
.NET Aspiresubstring inside a longer term. For example,ASP.NET Aspire applicationbecomesASPAspire application, andMicrosoft.NET AspirebecomesMicrosoftAspire. Because this helper runs over arbitrary README and AppHost source text, a future input can be corrupted rather than merely normalized. Please require.NETnot to be preceded by a word character and add regression cases for these inputs.