Skip to content

Commit e4f387a

Browse files
Add tests for all components
1 parent 5daf5fb commit e4f387a

File tree

9 files changed

+1170
-148
lines changed

9 files changed

+1170
-148
lines changed
Lines changed: 55 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,59 @@
1+
import { getByRole } from '@testing-library/dom'
2+
13
import initButton from './button.js'
24

3-
describe('mis-instantiation', () => {
4-
it('does not prevent further JavaScript from running', () => {
5-
expect(() => {
6-
// `undefined` simulates the element being missing,
7-
// from an unchecked `document.querySelector` for example
8-
initButton(undefined)
9-
}).not.toThrow()
5+
describe('Button', () => {
6+
/** @type {HTMLButtonElement} */
7+
let button
8+
9+
beforeEach(() => {
10+
document.body.innerHTML = `
11+
<button class="nhsuk-button" data-module="nhsuk-button" type="submit">
12+
Save and continue
13+
</button>
14+
`
15+
16+
button = getByRole(document.body, 'button')
17+
18+
jest.spyOn(button, 'addEventListener')
19+
})
20+
21+
describe('Exports', () => {
22+
it('should export init function', () => {
23+
expect(initButton).toBeInstanceOf(Function)
24+
})
25+
})
26+
27+
describe('Initialisation', () => {
28+
it('should add event listeners', () => {
29+
initButton()
30+
31+
expect(button.addEventListener).toHaveBeenNthCalledWith(
32+
1,
33+
'keydown',
34+
expect.any(Function)
35+
)
36+
37+
expect(button.addEventListener).toHaveBeenNthCalledWith(
38+
2,
39+
'click',
40+
expect.any(Function)
41+
)
42+
})
43+
44+
it('should not throw with missing button', () => {
45+
button.remove()
46+
expect(() => initButton()).not.toThrow()
47+
})
48+
49+
it('should not throw with empty body', () => {
50+
document.body.innerHTML = ''
51+
expect(() => initButton()).not.toThrow()
52+
})
53+
54+
it('should not throw with empty scope', () => {
55+
const scope = document.createElement('div')
56+
expect(() => initButton({ scope })).not.toThrow()
57+
})
1058
})
1159
})
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import { getByRole } from '@testing-library/dom'
2+
3+
import initCharacterCount from './character-count.js'
4+
5+
describe('Character count', () => {
6+
/** @type {HTMLTextAreaElement} */
7+
let textarea
8+
9+
beforeEach(() => {
10+
document.body.innerHTML = `
11+
<div class="nhsuk-character-count" data-module="nhsuk-character-count" data-maxlength="10">
12+
<div class="nhsuk-form-group">
13+
<label class="nhsuk-label" for="more-detail">
14+
Can you provide more detail?
15+
</label>
16+
<div class="nhsuk-hint" id="more-detail-hint">
17+
Don't include personal or financial information, eg your National Insurance number or credit card details.
18+
</div>
19+
<textarea class="nhsuk-textarea nhsuk-js-character-count" id="more-detail" name="more-detail" rows="5" aria-describedby="more-detail-hint"></textarea>
20+
</div>
21+
<div class="nhsuk-hint nhsuk-character-count__message" id="more-detail-info">
22+
You can enter up to 10 characters
23+
</div>
24+
</div>
25+
`
26+
27+
const container = document.querySelector('.nhsuk-character-count')
28+
29+
textarea = getByRole(container, 'textbox', {
30+
name: 'Can you provide more detail?'
31+
})
32+
33+
jest.spyOn(textarea, 'addEventListener')
34+
})
35+
36+
describe('Exports', () => {
37+
it('should export init function', () => {
38+
expect(initCharacterCount).toBeInstanceOf(Function)
39+
})
40+
})
41+
42+
describe('Initialisation', () => {
43+
it('should add event listeners', () => {
44+
initCharacterCount()
45+
46+
expect(textarea.addEventListener).toHaveBeenCalledWith(
47+
'keyup',
48+
expect.any(Function)
49+
)
50+
51+
expect(textarea.addEventListener).toHaveBeenCalledWith(
52+
'focus',
53+
expect.any(Function)
54+
)
55+
56+
expect(textarea.addEventListener).toHaveBeenCalledWith(
57+
'blur',
58+
expect.any(Function)
59+
)
60+
})
61+
62+
it('should not throw with missing textarea', () => {
63+
textarea.remove()
64+
expect(() => initCharacterCount()).not.toThrow()
65+
})
66+
67+
it('should not throw with empty body', () => {
68+
document.body.innerHTML = ''
69+
expect(() => initCharacterCount()).not.toThrow()
70+
})
71+
72+
it('should not throw with empty scope', () => {
73+
const scope = document.createElement('div')
74+
expect(() => initCharacterCount({ scope })).not.toThrow()
75+
})
76+
})
77+
})
Lines changed: 173 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,188 @@
1+
import { getByRole } from '@testing-library/dom'
2+
13
import initCheckboxes from './checkboxes.js'
24

3-
describe('Checkboxes module', () => {
4-
describe('does not throw an error', () => {
5-
it('if there is no conditional checkboxes container', () => {
5+
describe('Checkboxes', () => {
6+
/** @type {HTMLDivElement[]} */
7+
let conditionals
8+
9+
/** @type {HTMLInputElement[]} */
10+
let inputs
11+
12+
beforeEach(() => {
13+
document.body.innerHTML = `
14+
<form method="post" novalidate>
15+
<div class="nhsuk-form-group">
16+
<fieldset class="nhsuk-fieldset" aria-describedby="contact-hint">
17+
<legend class="nhsuk-fieldset__legend nhsuk-fieldset__legend--m">
18+
How would you prefer to be contacted?
19+
</legend>
20+
21+
<div class="nhsuk-hint" id="contact-hint">
22+
Select all options that are relevant to you.
23+
</div>
24+
25+
<div class="nhsuk-checkboxes nhsuk-checkboxes--conditional">
26+
<div class="nhsuk-checkboxes__item">
27+
<input class="nhsuk-checkboxes__input" id="contact-1" name="contact" type="checkbox" value="email" aria-controls="conditional-contact-1" aria-expanded="false">
28+
<label class="nhsuk-label nhsuk-checkboxes__label" for="contact-1">
29+
Email
30+
</label>
31+
</div>
32+
33+
<div class="nhsuk-checkboxes__conditional nhsuk-checkboxes__conditional--hidden" id="conditional-contact-1">
34+
<div class="nhsuk-form-group">
35+
<label class="nhsuk-label" for="email">
36+
Email address
37+
</label>
38+
<input class="nhsuk-input nhsuk-u-width-two-thirds" id="email" name="email" type="text">
39+
</div>
40+
</div>
41+
42+
<div class="nhsuk-checkboxes__item">
43+
<input class="nhsuk-checkboxes__input" id="contact-2" name="contact" type="checkbox" value="phone" aria-controls="conditional-contact-2" aria-expanded="false">
44+
<label class="nhsuk-label nhsuk-checkboxes__label" for="contact-2">
45+
Phone
46+
</label>
47+
</div>
48+
49+
<div class="nhsuk-checkboxes__conditional nhsuk-checkboxes__conditional--hidden" id="conditional-contact-2">
50+
<div class="nhsuk-form-group">
51+
<label class="nhsuk-label" for="phone">
52+
Phone number
53+
</label>
54+
<input class="nhsuk-input nhsuk-u-width-two-thirds" id="phone" name="phone" type="text">
55+
</div>
56+
</div>
57+
58+
<div class="nhsuk-checkboxes__item">
59+
<input class="nhsuk-checkboxes__input" id="contact-3" name="contact" type="checkbox" value="text" aria-controls="conditional-contact-3" aria-expanded="false">
60+
<label class="nhsuk-label nhsuk-checkboxes__label" for="contact-3">
61+
Text message
62+
</label>
63+
</div>
64+
65+
<div class="nhsuk-checkboxes__conditional nhsuk-checkboxes__conditional--hidden" id="conditional-contact-3">
66+
<div class="nhsuk-form-group">
67+
<label class="nhsuk-label" for="text">
68+
Mobile phone number
69+
</label>
70+
<input class="nhsuk-input nhsuk-u-width-two-thirds" id="mobile" name="mobile" type="text">
71+
</div>
72+
</div>
73+
</div>
74+
</fieldset>
75+
</div>
76+
</form>
77+
`
78+
79+
const container = document.querySelector('.nhsuk-checkboxes')
80+
81+
conditionals = [
82+
...container.querySelectorAll('.nhsuk-checkboxes__conditional')
83+
]
84+
85+
const input1 = getByRole(container, 'checkbox', {
86+
name: 'Email'
87+
})
88+
89+
const input2 = getByRole(container, 'checkbox', {
90+
name: 'Phone'
91+
})
92+
93+
const input3 = getByRole(container, 'checkbox', {
94+
name: 'Text message'
95+
})
96+
97+
inputs = [input1, input2, input3]
98+
99+
jest.spyOn(input1, 'addEventListener')
100+
jest.spyOn(input2, 'addEventListener')
101+
jest.spyOn(input3, 'addEventListener')
102+
})
103+
104+
describe('Exports', () => {
105+
it('should export init function', () => {
106+
expect(initCheckboxes).toBeInstanceOf(Function)
107+
})
108+
})
109+
110+
describe('Initialisation', () => {
111+
it('should add event listeners', () => {
112+
initCheckboxes()
113+
114+
for (const input of inputs) {
115+
expect(input.addEventListener).toHaveBeenCalledWith(
116+
'change',
117+
expect.any(Function)
118+
)
119+
}
120+
})
121+
122+
it('should not throw with missing checkboxes', () => {
123+
for (const input of inputs) {
124+
input.remove()
125+
}
126+
6127
expect(() => initCheckboxes()).not.toThrow()
7128
})
8-
it('if there are no conditional checkboxes inside the container', () => {
9-
document.body.innerHTML = '<div class="nhsuk-checkboxes"></div>'
129+
130+
it('should not throw with empty body', () => {
131+
document.body.innerHTML = ''
10132
expect(() => initCheckboxes()).not.toThrow()
11133
})
134+
135+
it('should not throw with empty scope', () => {
136+
const scope = document.createElement('div')
137+
expect(() => initCheckboxes({ scope })).not.toThrow()
138+
})
12139
})
13140

14-
describe('displays conditional content', () => {
15-
it('when checking the input', () => {
16-
document.body.innerHTML = `<form><div class="nhsuk-checkboxes">
17-
<input class="nhsuk-checkboxes__input" id="input-1" type="checkbox" aria-controls="conditional-1" aria-expanded="false" />
18-
<div class="nhsuk-checkboxes__conditional--hidden" id="conditional-1">Test</div>
19-
</div></form>`
20-
const input = document.querySelector('#input-1')
21-
const conditional = document.querySelector('#conditional-1')
141+
describe('Conditional content', () => {
142+
it('should be hidden by default', () => {
143+
initCheckboxes()
144+
145+
for (const input of inputs) {
146+
const index = inputs.indexOf(input)
147+
const conditional = conditionals.at(index)
148+
149+
// Conditional content hidden
150+
expect(input).toHaveAttribute('aria-expanded', 'false')
151+
expect(conditional).toHaveClass('nhsuk-checkboxes__conditional--hidden')
152+
}
153+
})
154+
155+
it('should be visible when input is checked', () => {
22156
initCheckboxes()
23-
input.click()
24-
expect(conditional).not.toHaveClass(
25-
'nhsuk-checkboxes__conditional--hidden'
26-
)
27-
expect(input).toHaveAttribute('aria-expanded', 'true')
157+
158+
for (const input of inputs) {
159+
const index = inputs.indexOf(input)
160+
const conditional = conditionals.at(index)
161+
162+
input.click()
163+
164+
// Conditional content visible
165+
expect(input).toHaveAttribute('aria-expanded', 'true')
166+
expect(conditional).not.toHaveClass(
167+
'nhsuk-checkboxes__conditional--hidden'
168+
)
169+
}
28170
})
29-
})
30171

31-
describe('hides conditional content', () => {
32-
it('when unchecking the input', () => {
33-
document.body.innerHTML = `<form><div class="nhsuk-checkboxes">
34-
<input class="nhsuk-checkboxes__input" id="input-1" type="checkbox" aria-controls="conditional-1" aria-expanded="false" />
35-
<div class="nhsuk-checkboxes__conditional nhsuk-checkboxes__conditional--hidden" id="conditional-1">Test</div>
36-
</div></form>`
37-
const input = document.querySelector('#input-1')
38-
const conditional = document.querySelector('#conditional-1')
172+
it('should be hidden when input is unchecked', () => {
39173
initCheckboxes()
40-
input.click()
41-
expect(conditional).not.toHaveClass(
42-
'nhsuk-checkboxes__conditional--hidden'
43-
)
44-
expect(input).toHaveAttribute('aria-expanded', 'true')
45-
input.click()
46-
expect(conditional).toHaveClass('nhsuk-checkboxes__conditional--hidden')
47-
expect(input).toHaveAttribute('aria-expanded', 'false')
174+
175+
for (const input of inputs) {
176+
const index = inputs.indexOf(input)
177+
const conditional = conditionals.at(index)
178+
179+
input.click()
180+
input.click()
181+
182+
// Conditional content hidden
183+
expect(input).toHaveAttribute('aria-expanded', 'false')
184+
expect(conditional).toHaveClass('nhsuk-checkboxes__conditional--hidden')
185+
}
48186
})
49187
})
50188
})

0 commit comments

Comments
 (0)