Skip to content

Commit 487eae3

Browse files
authored
Don't validate html comments (exercism#7861)
* Don't validate html comments * Recognise svg selfclosing tags * Add tests
1 parent 37dae27 commit 487eae3

File tree

6 files changed

+73
-4
lines changed

6 files changed

+73
-4
lines changed

app/javascript/components/bootcamp/CSSExercisePage/LHS/ControlButtons.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { runHtmlChecks } from '../checks/runHtmlChecks'
1717
import { CheckResult } from '../checks/runChecks'
1818
import { runCssChecks } from '../checks/runCssChecks'
1919
import { validateHtml5 } from '../../common/validateHtml5/validateHtml5'
20+
import { normalizeHtmlText } from '../../common/validateHtml5/normalizeHtmlText'
2021

2122
export function ControlButtons({
2223
getEditorValues,
@@ -54,7 +55,8 @@ export function ControlButtons({
5455
let firstFailingCheck: CheckResult | null = null
5556

5657
if (htmlValue.length > 0) {
57-
const isHTMLValid = validateHtml5(htmlValue)
58+
const normalizedHtml = normalizeHtmlText(htmlValue)
59+
const isHTMLValid = validateHtml5(normalizedHtml)
5860

5961
if (!isHTMLValid.isValid) {
6062
toast.error(

app/javascript/components/bootcamp/FrontendExercisePage/LHS/LHS.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,13 @@ import { useFrontendExercisePageStore } from '../store/frontendExercisePageStore
1111
import toast from 'react-hot-toast'
1212
import { wrapJSCode } from './wrapJSCode'
1313
import { validateHtml5 } from '../../common/validateHtml5/validateHtml5'
14+
import { normalizeHtmlText } from '../../common/validateHtml5/normalizeHtmlText'
1415

1516
export type TabIndex = 'html' | 'css' | 'javascript'
1617

1718
export const TabsContext = createContext<TabContext>({
1819
current: 'html',
19-
switchToTab: () => { },
20+
switchToTab: () => {},
2021
})
2122

2223
export function LHS() {
@@ -59,7 +60,8 @@ export function LHS() {
5960
const htmlText = htmlEditorRef.current.state.doc.toString()
6061

6162
if (htmlText.length > 0) {
62-
const isHTMLValid = validateHtml5(htmlText)
63+
const normalizedHtml = normalizeHtmlText(htmlText)
64+
const isHTMLValid = validateHtml5(normalizedHtml)
6365

6466
if (!isHTMLValid.isValid) {
6567
setTab('html')
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export function normalizeHtmlText(html: string) {
2+
// remove comments
3+
html = html.replace(/<!--[\s\S]*?-->/g, '')
4+
// remove svg
5+
html = html.replace(/<svg[\s\S]*?<\/svg>/g, '')
6+
7+
return html
8+
}

app/javascript/components/bootcamp/common/validateHtml5/rules/checkVoidTagClosure.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
export function checkVoidTagClosure(html: string): void {
22
const voidRegex =
3-
/<(area|base|br|col|embed|hr|img|input|link|meta|source|track|wbr)\b[^<>]*?(?!>)$/gim
3+
/<(area|base|br|col|embed|hr|img|input|link|meta|source|track|wbr|circle|ellipse|line|path|polygon|polyline|rect|stop|use|image)\b[^<>]*?(?!>)$/gim
44

55
const malformedVoidTag = html.match(voidRegex)
66

app/javascript/components/bootcamp/common/validateHtml5/voidElements.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// list of elements that don't require a closing tag
22
export const voidElements = new Set([
3+
// HTML void elements
34
'area',
45
'base',
56
'br',
@@ -13,6 +14,17 @@ export const voidElements = new Set([
1314
'source',
1415
'track',
1516
'wbr',
17+
// svg
18+
'circle',
19+
'ellipse',
20+
'line',
21+
'path',
22+
'polygon',
23+
'polyline',
24+
'rect',
25+
'stop',
26+
'use',
27+
'image',
1628
])
1729

1830
export function isVoidElement(tagName: string): boolean {
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { normalizeHtmlText } from '../../../app/javascript/components/bootcamp/common/validateHtml5/normalizeHtmlText'
2+
3+
describe('normalizeHtmlText', () => {
4+
it('removes HTML comments', () => {
5+
expect(normalizeHtmlText('<p>Hello</p><!-- comment -->')).toBe(
6+
'<p>Hello</p>'
7+
)
8+
})
9+
10+
it('removes multiple comments', () => {
11+
expect(normalizeHtmlText('<!--A--><div>Content</div><!--B-->')).toBe(
12+
'<div>Content</div>'
13+
)
14+
})
15+
16+
it('removes SVG block', () => {
17+
expect(normalizeHtmlText('<div><svg><circle /></svg></div>')).toBe(
18+
'<div></div>'
19+
)
20+
})
21+
22+
it('removes multiline SVG and comments', () => {
23+
const cleaned = normalizeHtmlText(`
24+
<div>
25+
<!-- Comment -->
26+
<svg>
27+
<rect />
28+
</svg>
29+
Text
30+
</div>
31+
`)
32+
expect(cleaned.replace(/\s+/g, ' ').trim()).toBe('<div> Text </div>')
33+
})
34+
35+
it('removes multiple SVGs and comments', () => {
36+
const result = normalizeHtmlText(`
37+
<!-- top -->
38+
<svg><circle /></svg>
39+
<p>Hello</p>
40+
<svg><rect /></svg>
41+
<!-- bottom -->
42+
`)
43+
expect(result.replace(/\s+/g, ' ').trim()).toBe('<p>Hello</p>')
44+
})
45+
})

0 commit comments

Comments
 (0)