diff --git a/src/govuk/components/task-list/template.jsdom.test.js b/src/govuk/components/task-list/template.jsdom.test.js new file mode 100644 index 0000000000..63956d58c9 --- /dev/null +++ b/src/govuk/components/task-list/template.jsdom.test.js @@ -0,0 +1,284 @@ +const { getExamples, render } = require('@govuk-frontend/lib/components') + +describe('Task List', () => { + let examples + + beforeAll(async () => { + examples = await getExamples('task-list') + }) + + it('renders the default example', () => { + document.body.innerHTML = render('task-list', examples.default) + + const $component = document.querySelector('.govuk-task-list') + expect($component.tagName).toBe('UL') + }) + + describe('when custom classes are passed', () => { + let $component + + beforeAll(() => { + document.body.innerHTML = render('task-list', examples['custom classes']) + $component = document.querySelector('.govuk-task-list') + }) + + it('includes additional classes from the `classes` option on the root', () => { + expect($component).toHaveClass('custom-class-on-component') + }) + + it('includes additional classes from the `item.classes` option on the item', () => { + const $listItem = $component.querySelector('.govuk-task-list__item') + expect($listItem).toHaveClass('custom-class-on-task') + }) + + it('includes additional classes from the `item.status.classes` option on the status', () => { + const $status = $component.querySelector('.govuk-task-list__status') + expect($status).toHaveClass('custom-class-on-status') + }) + + describe('when a task has a tag status', () => { + it('allows for custom classes on tags', () => { + const $tag = document.querySelector( + '.govuk-task-list__status .govuk-tag' + ) + expect($tag).toHaveClass('custom-class-on-tag') + }) + }) + + describe('when a task has an href set', () => { + it('includes classes from the `item.title.classes` option on the link', () => { + const $itemWithLink = $component.querySelector( + '.govuk-task-list__item:first-child' + ) + const $link = $itemWithLink.querySelector('.govuk-task-list__link') + + expect($link).toHaveClass('custom-class-on-linked-title') + }) + }) + + describe('when a task does not have an href set', () => { + it('includes classes from the `item.title.classes` option on the wrapper div', () => { + const $itemWithoutLink = $component.querySelector( + '.govuk-task-list__item:last-child' + ) + const $wrapper = $itemWithoutLink.querySelector( + '.govuk-task-list__name-and-hint div' + ) + + expect($wrapper).toHaveClass('custom-class-on-unlinked-title') + }) + }) + }) + + it('sets any additional attributes based on the `attributes` option', () => { + document.body.innerHTML = render('task-list', examples['custom attributes']) + + const $component = document.querySelector('.govuk-task-list') + expect($component).toHaveAttribute('data-custom-attribute', 'custom-value') + }) + + it('sets any additional attributes on tags based on `item.status.tag.attributes` option', () => { + document.body.innerHTML = render('task-list', examples['custom attributes']) + + const $component = document.querySelector('.govuk-tag') + expect($component).toHaveAttribute('data-tag-attribute', 'tag-value') + }) + + describe('when a task has an href set', () => { + let $item + let $itemLink + + beforeAll(function () { + document.body.innerHTML = render('task-list', examples.default) + $item = document.querySelector('.govuk-task-list__item') + $itemLink = document.querySelector('a.govuk-task-list__link') + }) + + it('wraps the task title in a link', async () => { + expect($itemLink).toHaveAttribute('href', '#') + }) + + it('adds a with-link modifier class to the task', async () => { + expect($item).toHaveClass('govuk-task-list__item--with-link') + }) + + it('associates the task name link with the status using aria', async () => { + expect($itemLink).toHaveAccessibleDescription('Completed') + }) + }) + + describe('when a task does not have an href set', () => { + it('does not link the task title', () => { + document.body.innerHTML = render( + 'task-list', + examples['example with hint text and additional states'] + ) + + const $itemWithNoLink = document.querySelector( + '.govuk-task-list__item:last-child' + ) + + expect($itemWithNoLink.querySelector('a')).toBeNull() + }) + }) + + describe('when using the `text` option', () => { + beforeAll(() => { + document.body.innerHTML = render( + 'task-list', + examples['html passed as text'] + ) + }) + + describe('when a task has an href set', () => { + it('escapes HTML in the link', () => { + const $itemWithLink = document.querySelector( + '.govuk-task-list__item:first-child' + ) + const $link = $itemWithLink.querySelector('.govuk-task-list__link') + expect($link).toHaveTextContent('Linked Title') + }) + }) + + describe('when a task does not have an href set', () => { + it('escapes the title', () => { + const $itemWithoutLink = document.querySelector( + '.govuk-task-list__item:last-child' + ) + const $title = $itemWithoutLink.querySelector( + '.govuk-task-list__name-and-hint' + ) + + expect($title).toHaveTextContent('Unlinked Title') + }) + }) + + describe('when a task has a tag status', () => { + it('escapes HTML in the tag', () => { + const $tag = document.querySelector('.govuk-tag') + expect($tag).toHaveTextContent('Tag') + }) + }) + + describe('when a task has a non-tag status', () => { + it('escapes HTML in the status', () => { + const $status = document.querySelector('.govuk-task-list__status') + expect($status).toHaveTextContent('Status') + }) + }) + + it('escapes HTML in the hint', () => { + const $hint = document.querySelector('.govuk-task-list__hint') + expect($hint).toHaveTextContent('Hint') + }) + }) + + describe('when using the `html` option', () => { + beforeAll(() => { + document.body.innerHTML = render('task-list', examples.html) + }) + + describe('when a task has an href set', () => { + it('does not escape HTML in the link', () => { + const $itemWithLink = document.querySelector( + '.govuk-task-list__item:first-child' + ) + const $link = $itemWithLink.querySelector('.govuk-task-list__link') + + expect($link).toContainHTML('Linked Title') + }) + }) + + describe('when a task does not have an href set', () => { + it('does not escape HTML in the title', () => { + const $itemWithoutLink = document.querySelector( + '.govuk-task-list__item:last-child' + ) + const $title = $itemWithoutLink.querySelector( + '.govuk-task-list__name-and-hint' + ) + + expect($title).toContainHTML('Unlinked Title') + }) + }) + + describe('when a task has a tag status', () => { + it('does not escape HTML in the tag', () => { + const $tag = document.querySelector('.govuk-tag') + expect($tag).toContainHTML('Tag') + }) + }) + + describe('when a task has a non-tag status', () => { + it('does not escape HTML in the status', () => { + const $status = document.querySelector('.govuk-task-list__status') + expect($status).toContainHTML('Status') + }) + }) + + it('does not escape HTML in the hint', () => { + const $hint = document.querySelector('.govuk-task-list__hint') + expect($hint).toContainHTML('Hint') + }) + }) + + describe('when a task has a hint', () => { + let $component + + beforeAll(function () { + document.body.innerHTML = render( + 'task-list', + examples['example with hint text and additional states'] + ) + $component = document.querySelector('.govuk-task-list') + }) + + it('renders the hint', () => { + const $hintText = $component.querySelector('.govuk-task-list__hint') + expect($hintText).toHaveTextContent( + 'Ensure the plan covers objectives, strategies, sales, marketing and financial forecasts.' + ) + }) + + it('associates the hint text and the status with the task link', () => { + const $task = $component.querySelector( + '.govuk-task-list__item:nth-child(3)' + ) + + const $link = $task.querySelector('.govuk-task-list__link') + const $hint = $task.querySelector('.govuk-task-list__hint') + const $status = $task.querySelector('.govuk-task-list__status') + + expect($link).toHaveAccessibleDescription( + [$hint, $status].map((el) => el.textContent.trim()).join(' ') + ) + }) + }) + + describe('when a custom idPrefix is used', () => { + let $component + + beforeAll(function () { + document.body.innerHTML = render( + 'task-list', + examples['custom id prefix'] + ) + $component = document.querySelector('.govuk-task-list') + }) + + it('uses the id prefix for the hint id', () => { + const $hint = $component.querySelector('.govuk-task-list__hint') + expect($hint).toHaveAttribute('id', 'my-custom-id-1-hint') + }) + + it('uses the id prefix for the status', () => { + const $status = $component.querySelector('.govuk-task-list__status') + expect($status).toHaveAttribute('id', 'my-custom-id-1-status') + }) + + it('uses the id prefix for the aria-describedby association', () => { + const $link = $component.querySelector('.govuk-task-list__link') + expect($link).toHaveAttribute('aria-describedby') + }) + }) +}) diff --git a/src/govuk/components/task-list/template.test.js b/src/govuk/components/task-list/template.test.js deleted file mode 100644 index 31a8af3bd9..0000000000 --- a/src/govuk/components/task-list/template.test.js +++ /dev/null @@ -1,269 +0,0 @@ -const { render } = require('@govuk-frontend/helpers/nunjucks') -const { getExamples } = require('@govuk-frontend/lib/components') - -describe('Task List', () => { - let examples - - beforeAll(async () => { - examples = await getExamples('task-list') - }) - - it('renders the default example', () => { - const $ = render('task-list', examples.default) - - const $component = $('.govuk-task-list') - expect($component.get(0).tagName).toBe('ul') - }) - - it('allows for custom classes on the root of the component', () => { - const $ = render('task-list', examples['custom classes']) - - const $component = $('.govuk-task-list') - expect($component.hasClass('custom-class-on-component')).toBeTruthy() - }) - - it('allows for custom classes on each task', () => { - const $ = render('task-list', examples['custom classes']) - - const $listItem = $('.govuk-task-list__item') - expect($listItem.hasClass('custom-class-on-task')).toBeTruthy() - }) - - it('allows for custom classes on each status', () => { - const $ = render('task-list', examples['custom classes']) - - const $status = $('.govuk-task-list__status') - expect($status.hasClass('custom-class-on-status')).toBeTruthy() - }) - - it('allows for custom attributes', () => { - const $ = render('task-list', examples['custom attributes']) - - const $component = $('.govuk-task-list') - expect($component.attr('data-custom-attribute')).toBe('custom-value') - }) - - describe('when a task has an href set', () => { - let $component - - beforeAll(function () { - const $ = render('task-list', examples.default) - $component = $('.govuk-task-list') - }) - - it('wraps the task title in a link', async () => { - const $itemLink = $component.find('a.govuk-task-list__link') - expect($itemLink.attr('href')).toBe('#') - }) - - it('adds a with-link modifier class to the task', async () => { - const $itemLink = $component.find('.govuk-task-list__item') - expect( - $itemLink.hasClass('govuk-task-list__item--with-link') - ).toBeTruthy() - }) - - it('associates the task name link with the status using aria', async () => { - const $itemLink = $component.find('.govuk-task-list__link') - const $statusWithId = $component.find( - `#${$itemLink.attr('aria-describedby')}` - ) - - expect($statusWithId.text()).toContain('Completed') - }) - - it('applies title classes to the link', () => { - const $ = render('task-list', examples['custom classes']) - - const $itemWithLink = $('.govuk-task-list__item:first-child') - const $itemWithLinkTitle = $itemWithLink.find('.govuk-task-list__link') - expect( - $itemWithLinkTitle.hasClass('custom-class-on-linked-title') - ).toBeTruthy() - }) - - it('escapes the title when passed as text', () => { - const $ = render('task-list', examples['html passed as text']) - - const $itemWithLink = $('.govuk-task-list__item:first-child') - const $itemWithLinkTitle = $itemWithLink.find('.govuk-task-list__link') - expect($itemWithLinkTitle.text().trim()).toBe( - 'Linked Title' - ) - }) - - it('allows HTML in the title when passed as html', () => { - const $ = render('task-list', examples.html) - - const $itemWithLink = $('.govuk-task-list__item:first-child') - const $itemWithLinkTitle = $itemWithLink.find('.govuk-task-list__link') - expect($itemWithLinkTitle.html().trim()).toBe( - 'Linked Title' - ) - }) - }) - - describe('when a task does not have an href set', () => { - it('does not link the task title', () => { - const $ = render( - 'task-list', - examples['example with hint text and additional states'] - ) - - const $itemWithNoLink = $('.govuk-task-list__item:last-child') - const $itemWithNoLinkTitle = $itemWithNoLink.find('div') - expect($itemWithNoLinkTitle.text()).toContain('Payment') - }) - - it('applies title classes to the title wrapper div', () => { - const $ = render('task-list', examples['custom classes']) - - const $itemWithNoLink = $('.govuk-task-list__item:last-child') - const $itemWithNoLinkTitle = $itemWithNoLink.find( - '.govuk-task-list__name-and-hint div' - ) - expect( - $itemWithNoLinkTitle.hasClass('custom-class-on-unlinked-title') - ).toBeTruthy() - }) - - it('escapes the title when passed as text', () => { - const $ = render('task-list', examples['html passed as text']) - - const $itemWithoutLink = $('.govuk-task-list__item:last-child') - const $itemWithoutLinkTitle = $itemWithoutLink.find( - '.govuk-task-list__name-and-hint' - ) - expect($itemWithoutLinkTitle.text()).toContain( - 'Unlinked Title' - ) - }) - - it('allows HTML in the title when passed as html', () => { - const $ = render('task-list', examples.html) - - const $itemWithoutLink = $('.govuk-task-list__item:last-child') - const $itemWithoutLinkTitle = $itemWithoutLink.find( - '.govuk-task-list__name-and-hint' - ) - expect($itemWithoutLinkTitle.html()).toContain( - 'Unlinked Title' - ) - }) - }) - - describe('when a task has a tag status', () => { - it('escapes the tag when passed as text', () => { - const $ = render('task-list', examples['html passed as text']) - - const $tag = $('.govuk-tag') - expect($tag.text()).toContain('Tag') - }) - - it('allows HTML in the tag when passed as html', () => { - const $ = render('task-list', examples.html) - - const $tag = $('.govuk-tag') - expect($tag.html()).toContain('Tag') - }) - - it('allows for custom classes on tags', () => { - const $ = render('task-list', examples['custom classes']) - - const $tag = $('.govuk-task-list__status .govuk-tag') - expect($tag.hasClass('custom-class-on-tag')).toBeTruthy() - }) - - it('allows for custom attributes on tags', () => { - const $ = render('task-list', examples['custom attributes']) - - const $component = $('.govuk-tag') - expect($component.attr('data-tag-attribute')).toBe('tag-value') - }) - }) - - describe('when a task has a non-tag status', () => { - it('escapes the status when passed as text', () => { - const $ = render('task-list', examples['html passed as text']) - - const $status = $('.govuk-task-list__status') - expect($status.text()).toContain('Status') - }) - - it('allows HTML in the tag when passed as html', () => { - const $ = render('task-list', examples.html) - - const $status = $('.govuk-task-list__status') - expect($status.html()).toContain('Status') - }) - }) - - describe('when a task has a hint', () => { - let $component - - beforeAll(function () { - const $ = render( - 'task-list', - examples['example with hint text and additional states'] - ) - $component = $('.govuk-task-list') - }) - - it('renders the hint', () => { - const $hintText = $component.find('.govuk-task-list__hint') - expect($hintText.text()).toContain( - 'Ensure the plan covers objectives, strategies, sales, marketing and financial forecasts.' - ) - }) - - it('associates the hint text with the task link using aria', () => { - const $hintText = $component.find('.govuk-task-list__hint') - expect($hintText.attr('id')).toBe('task-list-3-hint') - - const $itemAssociatedWithHint = $component.find( - `.govuk-task-list__link[aria-describedby~="${$hintText.attr('id')}"]` - ) - expect($itemAssociatedWithHint.text()).toContain('Business plan') - }) - - it('escapes the hint when passed as text', () => { - const $ = render('task-list', examples['html passed as text']) - - const $hint = $('.govuk-task-list__hint') - expect($hint.text()).toContain('Hint') - }) - - it('allows HTML in the hint when passed as html', () => { - const $ = render('task-list', examples.html) - - const $hint = $('.govuk-task-list__hint') - expect($hint.html()).toContain('Hint') - }) - }) - - describe('when a custom idPrefix is used', () => { - let $component - - beforeAll(function () { - const $ = render('task-list', examples['custom id prefix']) - $component = $('.govuk-task-list') - }) - - it('uses the id prefix for the hint id', () => { - const $hint = $component.find('.govuk-task-list__hint') - expect($hint.attr('id')).toBe('my-custom-id-1-hint') - }) - - it('uses the id prefix for the status', () => { - const $hint = $component.find('.govuk-task-list__status') - expect($hint.attr('id')).toBe('my-custom-id-1-status') - }) - - it('uses the id prefix for the aria-describedby association', () => { - const $hint = $component.find('.govuk-task-list__link') - expect($hint.attr('aria-describedby')).toBe( - 'my-custom-id-1-hint my-custom-id-1-status' - ) - }) - }) -})