Skip to content

Commit 15e3906

Browse files
authored
Merge pull request #557 from mrholek/main
Add missing tests
2 parents 0b4f24f + ae335e3 commit 15e3906

File tree

6 files changed

+1562
-2
lines changed

6 files changed

+1562
-2
lines changed

js/src/util/time.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,5 +189,5 @@ export const isAmPm = locale =>
189189
*/
190190
export const isValidTime = time => {
191191
const d = new Date(`1970-01-01 ${time}`)
192-
return d instanceof Date && d.getTime()
192+
return d instanceof Date && !Number.isNaN(d.getTime())
193193
}

js/tests/unit/range-slider.spec.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import EventHandler from '../../src/dom/event-handler.js'
33
import RangeSlider from '../../src/range-slider.js'
44
import {
5-
clearFixture, createEvent, getFixture, jQueryMock
5+
clearFixture, getFixture, jQueryMock
66
} from '../helpers/fixture.js'
77

88
describe('RangeSlider', () => {

js/tests/unit/rating.spec.js

Lines changed: 363 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,363 @@
1+
/* eslint-env jasmine */
2+
3+
import Rating from '../../src/rating.js'
4+
import {
5+
getFixture, clearFixture, createEvent, jQueryMock
6+
} from '../helpers/fixture.js'
7+
8+
describe('Rating', () => {
9+
let fixtureEl
10+
11+
beforeAll(() => {
12+
fixtureEl = getFixture()
13+
})
14+
15+
afterEach(() => {
16+
clearFixture()
17+
})
18+
19+
describe('Default', () => {
20+
it('should return plugin default config', () => {
21+
expect(Rating.Default).toEqual(jasmine.any(Object))
22+
})
23+
})
24+
25+
describe('DefaultType', () => {
26+
it('should return plugin default type config', () => {
27+
expect(Rating.DefaultType).toEqual(jasmine.any(Object))
28+
})
29+
})
30+
31+
describe('NAME', () => {
32+
it('should return plugin NAME', () => {
33+
expect(Rating.NAME).toEqual('rating')
34+
})
35+
})
36+
37+
describe('constructor', () => {
38+
it('should create a Rating instance with default config if no config is provided', () => {
39+
fixtureEl.innerHTML = '<div></div>'
40+
41+
const div = fixtureEl.querySelector('div')
42+
const rating = new Rating(div)
43+
44+
expect(rating).toBeInstanceOf(Rating)
45+
expect(rating._config).toBeDefined()
46+
expect(rating._element).toEqual(div)
47+
})
48+
49+
it('should allow overriding default config', () => {
50+
fixtureEl.innerHTML = '<div></div>'
51+
52+
const div = fixtureEl.querySelector('div')
53+
const rating = new Rating(div, {
54+
itemCount: 3,
55+
value: 2,
56+
readOnly: true
57+
})
58+
59+
expect(rating._config.itemCount).toEqual(3)
60+
expect(rating._currentValue).toEqual(2)
61+
expect(rating._config.readOnly).toBeTrue()
62+
})
63+
64+
it('should apply the "disabled" class when config.disabled = true', () => {
65+
fixtureEl.innerHTML = '<div></div>'
66+
67+
const div = fixtureEl.querySelector('div')
68+
// for instance: disabled is set to true
69+
const rating = new Rating(div, { disabled: true })
70+
expect(rating._element.classList).toContain('disabled')
71+
})
72+
73+
it('should create the correct number of rating items', () => {
74+
fixtureEl.innerHTML = '<div></div>'
75+
const div = fixtureEl.querySelector('div')
76+
new Rating(div, { itemCount: 5 }) // eslint-disable-line no-new
77+
78+
expect(div.querySelectorAll('.rating-item')).toHaveSize(5)
79+
})
80+
81+
it('should set the initial checked input if "value" is provided', () => {
82+
fixtureEl.innerHTML = '<div></div>'
83+
const div = fixtureEl.querySelector('div')
84+
new Rating(div, { value: 2 }) // eslint-disable-line no-new
85+
86+
const checkedInputs = div.querySelectorAll('.rating-item-input:checked')
87+
expect(checkedInputs).toHaveSize(1)
88+
expect(checkedInputs[0].value).toEqual('2')
89+
})
90+
})
91+
92+
describe('update', () => {
93+
it('should update config and re-render the rating UI', () => {
94+
fixtureEl.innerHTML = '<div></div>'
95+
const div = fixtureEl.querySelector('div')
96+
const rating = new Rating(div, { itemCount: 3, value: 1 })
97+
98+
const previousHTML = div.innerHTML
99+
rating.update({ itemCount: 5, value: 3 })
100+
101+
expect(div.innerHTML).not.toEqual(previousHTML)
102+
expect(div.querySelectorAll('.rating-item')).toHaveSize(5)
103+
const checkedInput = div.querySelector('.rating-item-input:checked')
104+
expect(checkedInput.value).toEqual('3')
105+
})
106+
})
107+
108+
describe('reset', () => {
109+
it('should reset the rating to the new given value', () => {
110+
fixtureEl.innerHTML = '<div></div>'
111+
const div = fixtureEl.querySelector('div')
112+
const rating = new Rating(div, { itemCount: 5, value: 4 })
113+
114+
rating.reset(2)
115+
const checkedInput = div.querySelector('.rating-item-input:checked')
116+
expect(checkedInput.value).toEqual('2')
117+
})
118+
119+
it('should reset the rating to null if no argument is provided', () => {
120+
fixtureEl.innerHTML = '<div></div>'
121+
const div = fixtureEl.querySelector('div')
122+
const rating = new Rating(div, { value: 3 })
123+
124+
rating.reset()
125+
const checkedInput = div.querySelector('.rating-item-input:checked')
126+
expect(checkedInput).toBeNull()
127+
})
128+
129+
it('should emit a "change.coreui.rating" event on reset', () => {
130+
fixtureEl.innerHTML = '<div></div>'
131+
const div = fixtureEl.querySelector('div')
132+
const rating = new Rating(div, { value: 3 })
133+
const listener = jasmine.createSpy('listener')
134+
135+
div.addEventListener('change.coreui.rating', listener)
136+
rating.reset()
137+
expect(listener).toHaveBeenCalled()
138+
})
139+
})
140+
141+
describe('events', () => {
142+
it('should emit "change.coreui.rating" when a rating input is changed', () => {
143+
return new Promise(resolve => {
144+
fixtureEl.innerHTML = '<div></div>'
145+
const div = fixtureEl.querySelector('div')
146+
new Rating(div, { itemCount: 3 }) // eslint-disable-line no-new
147+
148+
div.addEventListener('change.coreui.rating', event => {
149+
expect(event.value).toBe('2')
150+
resolve()
151+
})
152+
153+
// Simulate clicking the second radio
154+
const input = div.querySelectorAll('.rating-item-label')[1]
155+
input.click()
156+
})
157+
})
158+
159+
it('should clear the rating if "allowClear" is true and the same value is clicked again', () => {
160+
return new Promise(resolve => {
161+
fixtureEl.innerHTML = '<div></div>'
162+
const div = fixtureEl.querySelector('div')
163+
// eslint-disable-next-line no-new
164+
new Rating(div, {
165+
value: 2,
166+
allowClear: true,
167+
itemCount: 5
168+
})
169+
170+
// Listen for a new change event
171+
div.addEventListener('change.coreui.rating', event => {
172+
expect(event.value).toBeNull()
173+
resolve()
174+
})
175+
176+
const input2 = div.querySelectorAll('.rating-item-input')[1] // value="2"
177+
input2.click()
178+
})
179+
})
180+
181+
it('should emit "hover.coreui.rating" on mouseenter with the hovered value', () => {
182+
return new Promise(resolve => {
183+
fixtureEl.innerHTML = '<div></div>'
184+
const div = fixtureEl.querySelector('div')
185+
new Rating(div, { itemCount: 3 }) // eslint-disable-line no-new
186+
187+
const label = div.querySelectorAll('.rating-item-label')[1]
188+
div.addEventListener('hover.coreui.rating', event => {
189+
expect(event.value).toBe('2')
190+
resolve()
191+
})
192+
193+
const mouseover = createEvent('mouseover')
194+
label.dispatchEvent(mouseover)
195+
})
196+
})
197+
198+
it('should remove "active" class from all labels when mouse leaves, unless an input is checked', () => {
199+
fixtureEl.innerHTML = '<div></div>'
200+
const div = fixtureEl.querySelector('div')
201+
new Rating(div, { itemCount: 3, value: 2 }) // eslint-disable-line no-new
202+
203+
const label = div.querySelectorAll('.rating-item-label')[2]
204+
// first hover:
205+
const mouseenter = createEvent('mouseenter')
206+
label.dispatchEvent(mouseenter)
207+
// all previous labels (0,1,2) active
208+
209+
const mouseleave = createEvent('mouseleave')
210+
label.dispatchEvent(mouseleave)
211+
212+
// Because the rating has a checked input for value="2", items 0 & 1 should remain active
213+
const activeLabels = div.querySelectorAll('.rating-item-label.active')
214+
expect(activeLabels).toHaveSize(2)
215+
})
216+
217+
it('should remove all "active" classes if no input is checked and mouse leaves', () => {
218+
fixtureEl.innerHTML = '<div></div>'
219+
const div = fixtureEl.querySelector('div')
220+
new Rating(div, { itemCount: 3, value: null }) // eslint-disable-line no-new
221+
222+
const label = div.querySelectorAll('.rating-item-label')[1]
223+
const mouseover = createEvent('mouseover')
224+
label.dispatchEvent(mouseover)
225+
226+
let activeLabels = div.querySelectorAll('.rating-item-label.active')
227+
expect(activeLabels).toHaveSize(2) // items[0] and items[1]
228+
229+
const mouseout = createEvent('mouseout')
230+
label.dispatchEvent(mouseout)
231+
232+
activeLabels = div.querySelectorAll('.rating-item-label.active')
233+
expect(activeLabels).toHaveSize(0)
234+
})
235+
})
236+
237+
describe('readonly & disabled', () => {
238+
it('should not change or hover if readOnly is true', () => {
239+
fixtureEl.innerHTML = '<div></div>'
240+
const div = fixtureEl.querySelector('div')
241+
new Rating(div, { itemCount: 3, readOnly: true }) // eslint-disable-line no-new
242+
243+
// Attempt to click on an input
244+
const inputs = div.querySelectorAll('.rating-item-input')
245+
inputs[1].click()
246+
247+
const checkedInput = div.querySelector('.rating-item-input:checked')
248+
expect(checkedInput).toBeNull() // Did not change
249+
250+
// Attempt to trigger mouseenter
251+
const label = div.querySelectorAll('.rating-item-label')[1]
252+
const mouseenter = createEvent('mouseenter')
253+
label.dispatchEvent(mouseenter)
254+
255+
const activeLabels = div.querySelectorAll('.rating-item-label.active')
256+
expect(activeLabels).toHaveSize(0)
257+
})
258+
259+
it('should not change or hover if disabled is true', () => {
260+
fixtureEl.innerHTML = '<div></div>'
261+
const div = fixtureEl.querySelector('div')
262+
new Rating(div, { itemCount: 3, disabled: true }) // eslint-disable-line no-new
263+
264+
// Attempt to click on an input
265+
const inputs = div.querySelectorAll('.rating-item-input')
266+
inputs[1].click()
267+
268+
const checkedInput = div.querySelector('.rating-item-input:checked')
269+
expect(checkedInput).toBeNull() // Did not change
270+
})
271+
})
272+
273+
describe('data-api', () => {
274+
it('should create rating elements on window load event', () => {
275+
fixtureEl.innerHTML = `
276+
<div id="myRating" data-coreui-toggle="rating" data-coreui-value="2" data-coreui-item-count="4"></div>
277+
`
278+
const ratingEl = fixtureEl.querySelector('#myRating')
279+
280+
// Manually trigger the load event
281+
const loadEvent = createEvent('load')
282+
window.dispatchEvent(loadEvent)
283+
284+
const ratingInstance = Rating.getInstance(ratingEl)
285+
expect(ratingInstance).not.toBeNull()
286+
expect(ratingInstance._config.itemCount).toEqual(4)
287+
expect(ratingInstance._currentValue).toEqual(2) // from data attribute
288+
})
289+
})
290+
291+
describe('jQueryInterface', () => {
292+
it('should create a rating via jQueryInterface', () => {
293+
fixtureEl.innerHTML = '<div></div>'
294+
const div = fixtureEl.querySelector('div')
295+
296+
jQueryMock.fn.rating = Rating.jQueryInterface
297+
jQueryMock.elements = [div]
298+
jQueryMock.fn.rating.call(jQueryMock)
299+
300+
expect(Rating.getInstance(div)).not.toBeNull()
301+
})
302+
303+
it('should throw error on undefined method', () => {
304+
fixtureEl.innerHTML = '<div></div>'
305+
const div = fixtureEl.querySelector('div')
306+
jQueryMock.fn.rating = Rating.jQueryInterface
307+
jQueryMock.elements = [div]
308+
309+
expect(() => {
310+
jQueryMock.fn.rating.call(jQueryMock, 'noMethod')
311+
}).toThrowError(TypeError, 'No method named "noMethod"')
312+
})
313+
})
314+
315+
describe('getInstance', () => {
316+
it('should return rating instance', () => {
317+
fixtureEl.innerHTML = '<div></div>'
318+
const div = fixtureEl.querySelector('div')
319+
const rating = new Rating(div)
320+
321+
expect(Rating.getInstance(div)).toEqual(rating)
322+
expect(Rating.getInstance(div)).toBeInstanceOf(Rating)
323+
})
324+
325+
it('should return null when there is no rating instance', () => {
326+
fixtureEl.innerHTML = '<div></div>'
327+
const div = fixtureEl.querySelector('div')
328+
329+
expect(Rating.getInstance(div)).toBeNull()
330+
})
331+
})
332+
333+
describe('getOrCreateInstance', () => {
334+
it('should return rating instance', () => {
335+
fixtureEl.innerHTML = '<div></div>'
336+
const div = fixtureEl.querySelector('div')
337+
const rating = new Rating(div)
338+
339+
expect(Rating.getOrCreateInstance(div)).toEqual(rating)
340+
expect(Rating.getInstance(div)).toEqual(Rating.getOrCreateInstance(div, {}))
341+
expect(Rating.getOrCreateInstance(div)).toBeInstanceOf(Rating)
342+
})
343+
344+
it('should return new instance when there is no rating instance', () => {
345+
fixtureEl.innerHTML = '<div></div>'
346+
const div = fixtureEl.querySelector('div')
347+
348+
expect(Rating.getInstance(div)).toBeNull()
349+
expect(Rating.getOrCreateInstance(div)).toBeInstanceOf(Rating)
350+
})
351+
352+
it('should return the same instance when exists, ignoring new configuration', () => {
353+
fixtureEl.innerHTML = '<div></div>'
354+
const div = fixtureEl.querySelector('div')
355+
const rating = new Rating(div, { itemCount: 3 })
356+
357+
const rating2 = Rating.getOrCreateInstance(div, { itemCount: 5 })
358+
expect(rating2).toEqual(rating)
359+
// config should still show itemCount as 3
360+
expect(rating2._config.itemCount).toEqual(3)
361+
})
362+
})
363+
})

0 commit comments

Comments
 (0)