mirror of
https://github.com/hcengineering/platform.git
synced 2026-09-27 03:54:57 +02:00
fix: handle html tags in markdown comparison (#9838)
Signed-off-by: Alexander Onnikov <Alexander.Onnikov@xored.com>
This commit is contained in:
@@ -36,8 +36,8 @@ expect.extend({
|
||||
return {
|
||||
message: () =>
|
||||
pass
|
||||
? `Expected markdown strings NOT to be equal:\n Received: ${received}\n Expected: ${expected}`
|
||||
: `Expected markdown strings to be equal:\n Received: ${received}\n Expected: ${expected}`,
|
||||
? `Expected markdown strings NOT to be equal:\n Received:\n${received}\n Expected:\n${expected}`
|
||||
: `Expected markdown strings to be equal:\n Received:\n${received}\n Expected:\n${expected}`,
|
||||
pass
|
||||
}
|
||||
}
|
||||
@@ -1033,6 +1033,15 @@ describe('markdownToMarkup -> markupToMarkdown', () => {
|
||||
name: 'Image',
|
||||
markdown: '<img width="320" height="160" src="http://example.com/image" alt="image">'
|
||||
},
|
||||
{
|
||||
name: 'Images',
|
||||
markdown: `
|
||||
<img width="250" height="330" src="https://github.com/user-attachments/assets/f348e016-3f7d-45b1-b8a0-9098e9961885" alt="Screenshot 2025-09-11 at 15 42 40" />
|
||||
|
||||
<img width="250" height="230" alt="Screenshot 2025-09-11 at 15 43 42" src="https://github.com/user-attachments/assets/4502eba1-1f55-44df-b691-c4d3d3d3d67d" >
|
||||
|
||||
<img src="https://github.com/user-attachments/assets/e21431a3-2062-4b0b-9c8f-d06c92ede741" alt="Screenshot 2025-09-11 at 15 43 50" width="250" height="210" >`
|
||||
},
|
||||
{
|
||||
name: 'Image with multiline alt',
|
||||
markdown: ''
|
||||
|
||||
@@ -49,20 +49,71 @@ export function calcSørensenDiceCoefficient (a: string, b: string): number {
|
||||
* Perform markdown diff/comparison to understand do we have a major differences.
|
||||
*/
|
||||
export function isMarkdownsEquals (source1: string, source2: string): boolean {
|
||||
const normalizeLineEndings = (str: string): string => str.replace(/\r?\n/g, '\n')
|
||||
|
||||
const excludeBlankLines = (str: string): string =>
|
||||
str
|
||||
.split('\n')
|
||||
.map((it) => it.trimEnd())
|
||||
.filter((it) => it.length > 0)
|
||||
.join('\n')
|
||||
|
||||
const norm1 = normalizeLineEndings(source1 ?? '')
|
||||
const lines1 = excludeBlankLines(norm1)
|
||||
|
||||
const norm2 = normalizeLineEndings(source2 ?? '')
|
||||
const lines2 = excludeBlankLines(norm2)
|
||||
|
||||
return lines1 === lines2
|
||||
const normalized1 = normalizeMarkdown(source1)
|
||||
const normalized2 = normalizeMarkdown(source2)
|
||||
return normalized1 === normalized2
|
||||
}
|
||||
|
||||
export function normalizeMarkdown (source: string): string {
|
||||
const tagRegex = /<(\w+)([^>]*?)(\/?)>/g
|
||||
const attrRegex = /(\w+)(?:=(?:"([^"]*)"|'([^']*)'|([^\s>]+)))?/g
|
||||
|
||||
// Normalize line endings to LF
|
||||
source = source.replace(/\r?\n/g, '\n')
|
||||
|
||||
// Remove extra blank lines
|
||||
source = source
|
||||
.split('\n')
|
||||
.map((it) => it.trimEnd())
|
||||
.filter((it) => it.length > 0)
|
||||
.join('\n')
|
||||
|
||||
// Normalize HTML tags
|
||||
source = source.replace(tagRegex, (match, tagName, attributes) => {
|
||||
const attrs: Record<string, string> = {}
|
||||
|
||||
let attrMatch = attrRegex.exec(attributes)
|
||||
while (attrMatch !== null) {
|
||||
const attrName = attrMatch[1]
|
||||
const attrValue = attrMatch[2] ?? attrMatch[3] ?? attrMatch[4] ?? ''
|
||||
attrs[attrName] = attrValue
|
||||
attrMatch = attrRegex.exec(attributes)
|
||||
}
|
||||
|
||||
// Sort attributes by name for consistent order
|
||||
const sortedAttrs = Object.keys(attrs)
|
||||
.sort()
|
||||
.map((key) => {
|
||||
const value = attrs[key]
|
||||
return value !== '' ? `${key}="${value}"` : key
|
||||
})
|
||||
.join(' ')
|
||||
|
||||
// Normalize to self-closing format for void elements
|
||||
const voidElements = [
|
||||
'img',
|
||||
'br',
|
||||
'hr',
|
||||
'input',
|
||||
'meta',
|
||||
'area',
|
||||
'base',
|
||||
'col',
|
||||
'embed',
|
||||
'link',
|
||||
'param',
|
||||
'source',
|
||||
'track',
|
||||
'wbr'
|
||||
]
|
||||
const isVoidElement = voidElements.includes(tagName.toLowerCase())
|
||||
|
||||
if (sortedAttrs !== '') {
|
||||
return isVoidElement ? `<${tagName} ${sortedAttrs} />` : `<${tagName} ${sortedAttrs}>`
|
||||
} else {
|
||||
return isVoidElement ? `<${tagName} />` : `<${tagName}>`
|
||||
}
|
||||
})
|
||||
|
||||
return source
|
||||
}
|
||||
|
||||
@@ -204,7 +204,6 @@ export const storeNodes: Record<string, NodeProcessor> = {
|
||||
)
|
||||
} else {
|
||||
if (attrs.width != null || attrs.height != null) {
|
||||
// state.write(`<img width="446" alt="{alt}" src="{src}">`)
|
||||
state.write(
|
||||
'<img' +
|
||||
(attrs.width != null ? ` width="${state.esc(`${attrs.width}`)}"` : '') +
|
||||
|
||||
Reference in New Issue
Block a user