Skip to content

Commit

Permalink
Merge pull request #4417 from alphagov/html-validate-rules
Browse files Browse the repository at this point in the history
Add HTML validation rules for missing elements
  • Loading branch information
colinrotherham committed Nov 20, 2023
2 parents d018317 + f4f16a0 commit 46cf96d
Show file tree
Hide file tree
Showing 26 changed files with 111 additions and 76 deletions.
6 changes: 6 additions & 0 deletions packages/govuk-frontend/.htmlvalidate.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,15 @@ module.exports = {
// Allow pattern attribute on input type="number"
'input-attributes': 'off',

// Require input to have a label
'input-missing-label': 'error',

// Allow inline styles for testing purposes
'no-inline-style': 'off',

// Require all form field and ARIA references to exist
'no-missing-references': 'error',

// Allow for explicit roles on regions that have implict roles
// We do this to better support AT with older versions of IE that
// have partial support for HTML5 semantic elements
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,23 +186,23 @@ examples:
element: button
text: Submit
attributes:
aria-controls: content
aria-controls: test-target-element
data-tracking-dimension: 123
- name: link attributes
hidden: true
options:
element: a
text: Submit
attributes:
aria-controls: content
aria-controls: test-target-element
data-tracking-dimension: 123
- name: input attributes
hidden: true
options:
element: input
text: Submit
attributes:
aria-controls: content
aria-controls: test-target-element
data-tracking-dimension: 123
- name: classes
hidden: true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ describe('Button', () => {
const $ = render('button', examples.attributes)

const $component = $('.govuk-button')
expect($component.attr('aria-controls')).toEqual('content')
expect($component.attr('aria-controls')).toEqual('test-target-element')
expect($component.attr('data-tracking-dimension')).toEqual('123')
})

Expand Down Expand Up @@ -123,7 +123,7 @@ describe('Button', () => {
const $ = render('button', examples['link attributes'])

const $component = $('.govuk-button')
expect($component.attr('aria-controls')).toEqual('content')
expect($component.attr('aria-controls')).toEqual('test-target-element')
expect($component.attr('data-tracking-dimension')).toEqual('123')
})

Expand All @@ -148,7 +148,7 @@ describe('Button', () => {
const $ = render('button', examples['input attributes'])

const $component = $('.govuk-button')
expect($component.attr('aria-controls')).toEqual('content')
expect($component.attr('aria-controls')).toEqual('test-target-element')
expect($component.attr('data-tracking-dimension')).toEqual('123')
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -696,7 +696,7 @@ examples:
options:
name: example-name
fieldset:
describedBy: content
describedBy: test-target-element
legend:
text: Which option?
items:
Expand Down Expand Up @@ -804,7 +804,7 @@ examples:
errorMessage:
text: Please select an option
fieldset:
describedBy: content
describedBy: test-target-element
legend:
text: What is your nationality?
hint:
Expand Down Expand Up @@ -858,7 +858,7 @@ examples:
- name: with single option set 'aria-describedby' on input, and describedBy
hidden: true
options:
describedBy: content
describedBy: test-target-element
name: t-and-c
errorMessage:
text: Please accept the terms and conditions
Expand All @@ -868,7 +868,7 @@ examples:
- name: with single option (and hint) set 'aria-describedby' on input, and describedBy
hidden: true
options:
describedBy: content
describedBy: test-target-element
name: t-and-c-with-hint
errorMessage:
text: Please accept the terms and conditions
Expand All @@ -894,7 +894,7 @@ examples:
errorMessage:
text: Please select an option
fieldset:
describedBy: content
describedBy: test-target-element
legend:
text: What is your nationality?
items:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ describe('Checkboxes', () => {
const $ = render('checkboxes', examples['with fieldset describedBy'])

const $fieldset = $('.govuk-fieldset')
expect($fieldset.attr('aria-describedby')).toMatch('content')
expect($fieldset.attr('aria-describedby')).toMatch('test-target-element')
})

it('render attributes', () => {
Expand Down Expand Up @@ -404,7 +404,7 @@ describe('Checkboxes', () => {
const errorMessageId = $('.govuk-error-message').attr('id')

const describedBy = new RegExp(
`${WORD_BOUNDARY}content${WHITESPACE}${errorMessageId}${WORD_BOUNDARY}`
`${WORD_BOUNDARY}test-target-element${WHITESPACE}${errorMessageId}${WORD_BOUNDARY}`
)

expect($fieldset.attr('aria-describedby')).toMatch(describedBy)
Expand Down Expand Up @@ -461,7 +461,7 @@ describe('Checkboxes', () => {
const hintId = $('.govuk-hint').attr('id')

const describedBy = new RegExp(
`${WORD_BOUNDARY}content${WHITESPACE}${hintId}${WORD_BOUNDARY}`
`${WORD_BOUNDARY}test-target-element${WHITESPACE}${hintId}${WORD_BOUNDARY}`
)

expect($fieldset.attr('aria-describedby')).toMatch(describedBy)
Expand Down Expand Up @@ -495,7 +495,7 @@ describe('Checkboxes', () => {
const errorMessageId = $('.govuk-error-message').attr('id')

const describedByCombined = new RegExp(
`${WORD_BOUNDARY}content${WHITESPACE}${hintId}${WHITESPACE}${errorMessageId}${WORD_BOUNDARY}`
`${WORD_BOUNDARY}test-target-element${WHITESPACE}${hintId}${WHITESPACE}${errorMessageId}${WORD_BOUNDARY}`
)

expect($fieldset.attr('aria-describedby')).toMatch(describedByCombined)
Expand Down Expand Up @@ -548,7 +548,9 @@ describe('Checkboxes', () => {
const $ = render('checkboxes', examples[exampleName])
const $input = $('input')

expect($input.attr('aria-describedby')).toMatch('content t-and-c-error')
expect($input.attr('aria-describedby')).toMatch(
'test-target-element t-and-c-error'
)
})
})

Expand All @@ -573,7 +575,7 @@ describe('Checkboxes', () => {
const $input = $('input')

expect($input.attr('aria-describedby')).toMatch(
'content t-and-c-with-hint-error t-and-c-with-hint-item-hint'
'test-target-element t-and-c-with-hint-error t-and-c-with-hint-item-hint'
)
})
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const {
} = require('@govuk-frontend/lib/components')
const validatorConfig = require('govuk-frontend/.htmlvalidate.js')
const { HtmlValidate } = require('html-validate')
const { outdent } = require('outdent')

describe('Components', () => {
let nunjucksEnvCustom
Expand Down Expand Up @@ -65,10 +66,18 @@ describe('Components', () => {
// Validate component examples
for (const { component: componentName, fixtures } of componentsFixtures) {
const fixtureTasks = fixtures.map(async (fixture) => {
const html = render(componentName, {
context: fixture.options,
fixture
})
const html = outdent`
${render(componentName, {
context: fixture.options,
fixture
})}
<!--
Target for references in examples (e.g. aria-controls)
https://html-validate.org/rules/no-missing-references.html
-->
<div id="test-target-element"></div>
`

// Validate HTML
return expect({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,7 @@ examples:
options:
id: dob-errors
fieldset:
describedBy: content
describedBy: test-target-element
legend:
text: What is your date of birth?
hint:
Expand All @@ -359,7 +359,7 @@ examples:
options:
id: dob-errors
fieldset:
describedBy: content
describedBy: test-target-element
legend:
text: What is your date of birth?
errorMessage:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ describe('Date input', () => {
const hintId = $('.govuk-hint').attr('id')

const describedBy = new RegExp(
`${WORD_BOUNDARY}content${WHITESPACE}${hintId}${WORD_BOUNDARY}`
`${WORD_BOUNDARY}test-target-element${WHITESPACE}${hintId}${WORD_BOUNDARY}`
)

expect($fieldset.attr('aria-describedby')).toMatch(describedBy)
Expand Down Expand Up @@ -246,7 +246,7 @@ describe('Date input', () => {
const $fieldset = $('.govuk-fieldset')

expect($fieldset.attr('aria-describedby')).toMatch(
'content dob-errors-error'
'test-target-element dob-errors-error'
)
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ examples:
- name: with describedBy
hidden: true
options:
describedBy: content
describedBy: test-target-element
legend:
text: Which option?
- name: html as text
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ describe('fieldset', () => {
const $ = render('fieldset', examples['with describedBy'])

const $component = $('.govuk-fieldset')
expect($component.attr('aria-describedby')).toEqual('content')
expect($component.attr('aria-describedby')).toEqual('test-target-element')
})

it('escapes HTML in the text argument', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,15 +126,15 @@ examples:
name: file-upload-describedby
label:
text: Upload a file
describedBy: content
describedBy: test-target-element
- name: with hint and describedBy
hidden: true
options:
id: file-upload-hint-describedby
name: file-upload-hint-describedby
label:
text: Upload a file
describedBy: content
describedBy: test-target-element
hint:
text: Your photo may be in your Pictures, Photos, Downloads or Desktop folder. Or in an app like iPhoto.
- name: error
Expand All @@ -153,7 +153,7 @@ examples:
name: file-upload-error-describedby
label:
text: Upload a file
describedBy: content
describedBy: test-target-element
errorMessage:
text: Error message
- name: with error, describedBy and hint
Expand All @@ -163,7 +163,7 @@ examples:
name: file-upload-error-describedby-hint
label:
text: Upload a file
describedBy: content
describedBy: test-target-element
errorMessage:
text: Error message
hint:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ describe('File upload', () => {
const $ = render('file-upload', examples['with describedBy'])

const $component = $('.govuk-file-upload')
expect($component.attr('aria-describedby')).toMatch('content')
expect($component.attr('aria-describedby')).toMatch('test-target-element')
})

it('renders with attributes', () => {
Expand Down Expand Up @@ -104,7 +104,7 @@ describe('File upload', () => {
const hintId = $('.govuk-hint').attr('id')

const describedBy = new RegExp(
`${WORD_BOUNDARY}content${WHITESPACE}${hintId}${WORD_BOUNDARY}`
`${WORD_BOUNDARY}test-target-element${WHITESPACE}${hintId}${WORD_BOUNDARY}`
)

expect($component.attr('aria-describedby')).toMatch(describedBy)
Expand Down Expand Up @@ -138,7 +138,7 @@ describe('File upload', () => {
const errorMessageId = $('.govuk-error-message').attr('id')

const describedBy = new RegExp(
`${WORD_BOUNDARY}content${WHITESPACE}${errorMessageId}${WORD_BOUNDARY}`
`${WORD_BOUNDARY}test-target-element${WHITESPACE}${errorMessageId}${WORD_BOUNDARY}`
)

expect($component.attr('aria-describedby')).toMatch(describedBy)
Expand Down Expand Up @@ -175,7 +175,7 @@ describe('File upload', () => {
})

it('associates the input as described by the hint, error message and parent fieldset', () => {
const describedById = 'content'
const describedById = 'test-target-element'

const $ = render(
'file-upload',
Expand Down
8 changes: 4 additions & 4 deletions packages/govuk-frontend/src/govuk/components/input/input.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,7 @@ examples:
name: with-describedby
label:
text: With describedBy
describedBy: content
describedBy: test-target-element
- name: attributes
hidden: true
options:
Expand All @@ -368,7 +368,7 @@ examples:
name: with-hint-describedby
label:
text: With hint describedBy
describedBy: content
describedBy: test-target-element
hint:
text: It’s on your National Insurance card, benefit letter, payslip or P60. For example, ‘QQ 12 34 56 C’.
- name: error with describedBy
Expand All @@ -378,7 +378,7 @@ examples:
name: with-error-describedby
label:
text: With error describedBy
describedBy: content
describedBy: test-target-element
errorMessage:
text: Error message
- name: with error and hint
Expand All @@ -403,7 +403,7 @@ examples:
text: Error message
hint:
text: Hint
describedBy: content
describedBy: test-target-element
- name: inputmode
hidden: true
options:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ describe('Input', () => {
const $ = render('input', examples['with describedBy'])

const $component = $('.govuk-input')
expect($component.attr('aria-describedby')).toMatch('content')
expect($component.attr('aria-describedby')).toMatch('test-target-element')
})

it('renders with attributes', () => {
Expand Down Expand Up @@ -127,7 +127,7 @@ describe('Input', () => {
const hintId = $('.govuk-hint').attr('id')

const describedBy = new RegExp(
`${WORD_BOUNDARY}content${WHITESPACE}${hintId}${WORD_BOUNDARY}`
`${WORD_BOUNDARY}test-target-element${WHITESPACE}${hintId}${WORD_BOUNDARY}`
)

expect($input.attr('aria-describedby')).toMatch(describedBy)
Expand Down Expand Up @@ -161,7 +161,7 @@ describe('Input', () => {
const errorMessageId = $('.govuk-error-message').attr('id')

const describedBy = new RegExp(
`${WORD_BOUNDARY}content${WHITESPACE}${errorMessageId}${WORD_BOUNDARY}`
`${WORD_BOUNDARY}test-target-element${WHITESPACE}${errorMessageId}${WORD_BOUNDARY}`
)

expect($input.attr('aria-describedby')).toMatch(describedBy)
Expand Down Expand Up @@ -228,7 +228,7 @@ describe('Input', () => {
const hintId = $('.govuk-hint').attr('id')

const describedByCombined = new RegExp(
`${WORD_BOUNDARY}content${WHITESPACE}${hintId}${WHITESPACE}${errorMessageId}${WORD_BOUNDARY}`
`${WORD_BOUNDARY}test-target-element${WHITESPACE}${hintId}${WHITESPACE}${errorMessageId}${WORD_BOUNDARY}`
)

expect($component.attr('aria-describedby')).toMatch(describedByCombined)
Expand Down
Loading

0 comments on commit 46cf96d

Please sign in to comment.