Skip to content
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

Add HTML validation rules for missing elements #4417

Merged
merged 5 commits into from
Nov 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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