diff --git a/src/utils/__tests__/changelog-extract.test.ts b/src/utils/__tests__/changelog-extract.test.ts index ba119ca8..c64fb637 100644 --- a/src/utils/__tests__/changelog-extract.test.ts +++ b/src/utils/__tests__/changelog-extract.test.ts @@ -302,3 +302,126 @@ func foo() {} expect(result).toBeNull(); }); }); + +describe('extractChangelogEntry HTML blocks', () => { + it('preserves tag after paragraph as nested content', () => { + const prBody = `### Changelog Entry + +Add a chart rendering engine for \`sentry dashboard view\`. + +screenshot + +### Next Section`; + + const result = extractChangelogEntry(prBody); + expect(result).toHaveLength(1); + expect(result![0].text).toBe( + 'Add a chart rendering engine for `sentry dashboard view`.', + ); + expect(result![0].nestedContent).toContain(' tag after list item as nested content', () => { + const prBody = `### Changelog Entry + +- Added dashboard charts + +`; + + const result = extractChangelogEntry(prBody); + expect(result).toHaveLength(1); + expect(result![0].text).toBe('Added dashboard charts'); + expect(result![0].nestedContent).toContain(' { + const prBody = `### Changelog Entry + + + +### Next Section`; + + const result = extractChangelogEntry(prBody); + // A bare HTML block without descriptive text is not a meaningful entry + expect(result).toBeNull(); + }); + + it('preserves inline within paragraph text', () => { + const prBody = `### Changelog Entry + +See the result: pretty cool`; + + const result = extractChangelogEntry(prBody); + expect(result).toHaveLength(1); + // Inline HTML within a paragraph is preserved in the text itself + expect(result![0].text).toContain(' { + const prBody = `### Changelog Entry + +- Entry with code and image: + \`\`\`go + func foo() {} + \`\`\` + +`; + + const result = extractChangelogEntry(prBody); + expect(result).toHaveLength(1); + expect(result![0].text).toBe('Entry with code and image:'); + expect(result![0].nestedContent).toContain('```go'); + expect(result![0].nestedContent).toContain(' { + // GitHub stores uploaded images as HTML img tags with width/height + const prBody = `## Changelog Entry + +Add a full chart rendering engine. + +image + +## Chart Types`; + + const result = extractChangelogEntry(prBody); + expect(result).toHaveLength(1); + expect(result![0].text).toBe('Add a full chart rendering engine.'); + expect(result![0].nestedContent).toContain('width="2512"'); + expect(result![0].nestedContent).toContain( + 'src="https://github.com/user-attachments/assets/abc123"', + ); + }); + + it('indents all lines of multi-line HTML blocks', () => { + const prBody = `### Changelog Entry + +Added collapsible details section. + +
+Click to expand +Some hidden content here. +
`; + + const result = extractChangelogEntry(prBody); + expect(result).toHaveLength(1); + expect(result![0].text).toBe('Added collapsible details section.'); + // Every line should be indented with 2 spaces + const lines = result![0].nestedContent!.split('\n'); + for (const line of lines) { + expect(line).toMatch(/^ {2}/); + } + expect(result![0].nestedContent).toContain('
'); + expect(result![0].nestedContent).toContain( + ' Click to expand', + ); + expect(result![0].nestedContent).toContain('
'); + }); +}); diff --git a/src/utils/changelog.ts b/src/utils/changelog.ts index 6dbdb8b0..b2eb618f 100644 --- a/src/utils/changelog.ts +++ b/src/utils/changelog.ts @@ -428,6 +428,24 @@ function parseTokensToEntries(tokens: Token[]): ChangelogEntryItem[] | null { } // If no previous entry exists, skip the orphaned code block — a bare // code block without descriptive text isn't a meaningful changelog entry. + } else if (token.type === 'html') { + // HTML blocks (e.g., tags from GitHub image uploads) become + // nested content on the previous entry, similar to code blocks. + // Indent every line with 2 spaces so multi-line HTML (e.g.,
, + // ) nests properly under the list item in the final markdown. + const htmlContent = (token as Tokens.HTML).raw.trim(); + if (htmlContent && entries.length > 0) { + const indented = htmlContent + .split('\n') + .map(line => ` ${line}`) + .join('\n'); + const prev = entries[entries.length - 1]; + prev.nestedContent = prev.nestedContent + ? `${prev.nestedContent}\n${indented}` + : indented; + } + // If no previous entry exists, skip — an orphaned HTML block + // without descriptive text isn't a meaningful changelog entry. } }