]>
Commit | Line | Data |
---|---|---|
91e44d91 S |
1 | $(function () { |
2 | 'use strict' | |
3 | ||
4 | QUnit.module('scrollspy plugin') | |
5 | ||
6 | QUnit.test('should be defined on jquery object', function (assert) { | |
7 | assert.expect(1) | |
8 | assert.ok($(document.body).scrollspy, 'scrollspy method is defined') | |
9 | }) | |
10 | ||
11 | QUnit.module('scrollspy', { | |
12 | beforeEach: function () { | |
13 | // Run all tests in noConflict mode -- it's the only way to ensure that the plugin works in noConflict mode | |
14 | $.fn.bootstrapScrollspy = $.fn.scrollspy.noConflict() | |
15 | }, | |
16 | afterEach: function () { | |
17 | $.fn.scrollspy = $.fn.bootstrapScrollspy | |
18 | delete $.fn.bootstrapScrollspy | |
19 | } | |
20 | }) | |
21 | ||
22 | QUnit.test('should provide no conflict', function (assert) { | |
23 | assert.expect(1) | |
24 | assert.strictEqual($.fn.scrollspy, undefined, 'scrollspy was set back to undefined (org value)') | |
25 | }) | |
26 | ||
27 | QUnit.test('should throw explicit error on undefined method', function (assert) { | |
28 | assert.expect(1) | |
29 | var $el = $('<div/>') | |
30 | $el.bootstrapScrollspy() | |
31 | try { | |
32 | $el.bootstrapScrollspy('noMethod') | |
33 | } | |
34 | catch (err) { | |
35 | assert.strictEqual(err.message, 'No method named "noMethod"') | |
36 | } | |
37 | }) | |
38 | ||
39 | QUnit.test('should return jquery collection containing the element', function (assert) { | |
40 | assert.expect(2) | |
41 | var $el = $('<div/>') | |
42 | var $scrollspy = $el.bootstrapScrollspy() | |
43 | assert.ok($scrollspy instanceof $, 'returns jquery collection') | |
44 | assert.strictEqual($scrollspy[0], $el[0], 'collection contains element') | |
45 | }) | |
46 | ||
47 | QUnit.test('should only switch "active" class on current target', function (assert) { | |
48 | assert.expect(1) | |
49 | var done = assert.async() | |
50 | ||
51 | var sectionHTML = '<div id="root" class="active">' | |
52 | + '<div class="topbar">' | |
53 | + '<div class="topbar-inner">' | |
54 | + '<div class="container" id="ss-target">' | |
55 | + '<ul class="nav">' | |
56 | + '<li><a href="#masthead">Overview</a></li>' | |
57 | + '<li><a href="#detail">Detail</a></li>' | |
58 | + '</ul>' | |
59 | + '</div>' | |
60 | + '</div>' | |
61 | + '</div>' | |
62 | + '<div id="scrollspy-example" style="height: 100px; overflow: auto;">' | |
63 | + '<div style="height: 200px;">' | |
64 | + '<h4 id="masthead">Overview</h4>' | |
65 | + '<p style="height: 200px">' | |
66 | + 'Ad leggings keytar, brunch id art party dolor labore.' | |
67 | + '</p>' | |
68 | + '</div>' | |
69 | + '<div style="height: 200px;">' | |
70 | + '<h4 id="detail">Detail</h4>' | |
71 | + '<p style="height: 200px">' | |
72 | + 'Veniam marfa mustache skateboard, adipisicing fugiat velit pitchfork beard.' | |
73 | + '</p>' | |
74 | + '</div>' | |
75 | + '</div>' | |
76 | + '</div>' | |
77 | var $section = $(sectionHTML).appendTo('#qunit-fixture') | |
78 | ||
79 | var $scrollspy = $section | |
80 | .show() | |
81 | .find('#scrollspy-example') | |
82 | .bootstrapScrollspy({ target: '#ss-target' }) | |
83 | ||
84 | $scrollspy.one('scroll', function () { | |
85 | assert.ok($section.hasClass('active'), '"active" class still on root node') | |
86 | done() | |
87 | }) | |
88 | ||
89 | $scrollspy.scrollTop(350) | |
90 | }) | |
91 | ||
92 | QUnit.test('should only switch "active" class on current target specified w element', function (assert) { | |
93 | assert.expect(1) | |
94 | var done = assert.async() | |
95 | ||
96 | var sectionHTML = '<div id="root" class="active">' | |
97 | + '<div class="topbar">' | |
98 | + '<div class="topbar-inner">' | |
99 | + '<div class="container" id="ss-target">' | |
100 | + '<ul class="nav">' | |
101 | + '<li><a href="#masthead">Overview</a></li>' | |
102 | + '<li><a href="#detail">Detail</a></li>' | |
103 | + '</ul>' | |
104 | + '</div>' | |
105 | + '</div>' | |
106 | + '</div>' | |
107 | + '<div id="scrollspy-example" style="height: 100px; overflow: auto;">' | |
108 | + '<div style="height: 200px;">' | |
109 | + '<h4 id="masthead">Overview</h4>' | |
110 | + '<p style="height: 200px">' | |
111 | + 'Ad leggings keytar, brunch id art party dolor labore.' | |
112 | + '</p>' | |
113 | + '</div>' | |
114 | + '<div style="height: 200px;">' | |
115 | + '<h4 id="detail">Detail</h4>' | |
116 | + '<p style="height: 200px">' | |
117 | + 'Veniam marfa mustache skateboard, adipisicing fugiat velit pitchfork beard.' | |
118 | + '</p>' | |
119 | + '</div>' | |
120 | + '</div>' | |
121 | + '</div>' | |
122 | var $section = $(sectionHTML).appendTo('#qunit-fixture') | |
123 | ||
124 | var $scrollspy = $section | |
125 | .show() | |
126 | .find('#scrollspy-example') | |
127 | .bootstrapScrollspy({ target: document.getElementById('#ss-target') }) | |
128 | ||
129 | $scrollspy.one('scroll', function () { | |
130 | assert.ok($section.hasClass('active'), '"active" class still on root node') | |
131 | done() | |
132 | }) | |
133 | ||
134 | $scrollspy.scrollTop(350) | |
135 | }) | |
136 | ||
137 | QUnit.test('should correctly select middle navigation option when large offset is used', function (assert) { | |
138 | assert.expect(3) | |
139 | var done = assert.async() | |
140 | ||
141 | var sectionHTML = '<div id="header" style="height: 500px;"></div>' | |
142 | + '<nav id="navigation" class="navbar">' | |
143 | + '<ul class="nav navbar-nav">' | |
144 | + '<li class="active"><a class="nav-link" id="one-link" href="#one">One</a></li>' | |
145 | + '<li><a class="nav-link" id="two-link" href="#two">Two</a></li>' | |
146 | + '<li><a class="nav-link" id="three-link" href="#three">Three</a></li>' | |
147 | + '</ul>' | |
148 | + '</nav>' | |
149 | + '<div id="content" style="height: 200px; overflow-y: auto;">' | |
150 | + '<div id="one" style="height: 500px;"></div>' | |
151 | + '<div id="two" style="height: 300px;"></div>' | |
152 | + '<div id="three" style="height: 10px;"></div>' | |
153 | + '</div>' | |
154 | var $section = $(sectionHTML).appendTo('#qunit-fixture') | |
155 | var $scrollspy = $section | |
156 | .show() | |
157 | .filter('#content') | |
158 | ||
159 | $scrollspy.bootstrapScrollspy({ target: '#navigation', offset: $scrollspy.position().top }) | |
160 | ||
161 | $scrollspy.one('scroll', function () { | |
162 | assert.ok(!$section.find('#one-link').hasClass('active'), '"active" class removed from first section') | |
163 | assert.ok($section.find('#two-link').hasClass('active'), '"active" class on middle section') | |
164 | assert.ok(!$section.find('#three-link').hasClass('active'), '"active" class not on last section') | |
165 | done() | |
166 | }) | |
167 | ||
168 | $scrollspy.scrollTop(550) | |
169 | }) | |
170 | ||
171 | QUnit.test('should add the active class to the correct element', function (assert) { | |
172 | assert.expect(2) | |
173 | var navbarHtml = | |
174 | '<nav class="navbar">' | |
175 | + '<ul class="nav">' | |
176 | + '<li><a class="nav-link" id="a-1" href="#div-1">div 1</a></li>' | |
177 | + '<li><a class="nav-link" id="a-2" href="#div-2">div 2</a></li>' | |
178 | + '</ul>' | |
179 | + '</nav>' | |
180 | var contentHtml = | |
181 | '<div class="content" style="overflow: auto; height: 50px">' | |
182 | + '<div id="div-1" style="height: 100px; padding: 0; margin: 0">div 1</div>' | |
183 | + '<div id="div-2" style="height: 200px; padding: 0; margin: 0">div 2</div>' | |
184 | + '</div>' | |
185 | ||
186 | $(navbarHtml).appendTo('#qunit-fixture') | |
187 | var $content = $(contentHtml) | |
188 | .appendTo('#qunit-fixture') | |
189 | .bootstrapScrollspy({ offset: 0, target: '.navbar' }) | |
190 | ||
191 | var done = assert.async() | |
192 | var testElementIsActiveAfterScroll = function (element, target) { | |
193 | var deferred = $.Deferred() | |
194 | var scrollHeight = Math.ceil($content.scrollTop() + $(target).position().top) | |
195 | $content.one('scroll', function () { | |
196 | assert.ok($(element).hasClass('active'), 'target:' + target + ', element' + element) | |
197 | deferred.resolve() | |
198 | }) | |
199 | $content.scrollTop(scrollHeight) | |
200 | return deferred.promise() | |
201 | } | |
202 | ||
203 | $.when(testElementIsActiveAfterScroll('#a-1', '#div-1')) | |
204 | .then(function () { return testElementIsActiveAfterScroll('#a-2', '#div-2') }) | |
205 | .then(function () { done() }) | |
206 | }) | |
207 | ||
208 | QUnit.test('should add the active class correctly when there are nested elements at 0 scroll offset', function (assert) { | |
209 | assert.expect(6) | |
210 | var times = 0 | |
211 | var done = assert.async() | |
212 | var navbarHtml = '<nav id="navigation" class="navbar">' | |
213 | + '<ul class="nav">' | |
214 | + '<li><a id="a-1" class="nav-link" href="#div-1">div 1</a>' | |
215 | + '<ul>' | |
216 | + '<li><a id="a-2" class="nav-link" href="#div-2">div 2</a></li>' | |
217 | + '</ul>' | |
218 | + '</li>' | |
219 | + '</ul>' | |
220 | + '</nav>' | |
221 | ||
222 | var contentHtml = '<div class="content" style="position: absolute; top: 0px; overflow: auto; height: 50px">' | |
223 | + '<div id="div-1" style="padding: 0; margin: 0">' | |
224 | + '<div id="div-2" style="height: 200px; padding: 0; margin: 0">div 2</div>' | |
225 | + '</div>' | |
226 | + '</div>' | |
227 | ||
228 | $(navbarHtml).appendTo('#qunit-fixture') | |
229 | ||
230 | var $content = $(contentHtml) | |
231 | .appendTo('#qunit-fixture') | |
232 | .bootstrapScrollspy({ offset: 0, target: '#navigation' }) | |
233 | ||
234 | function testActiveElements() { | |
235 | if (++times > 3) { return done() } | |
236 | ||
237 | $content.one('scroll', function () { | |
238 | assert.ok($('#a-1').hasClass('active'), 'nav item for outer element has "active" class') | |
239 | assert.ok($('#a-2').hasClass('active'), 'nav item for inner element has "active" class') | |
240 | testActiveElements() | |
241 | }) | |
242 | ||
243 | $content.scrollTop($content.scrollTop() + 10) | |
244 | } | |
245 | ||
246 | testActiveElements() | |
247 | }) | |
248 | ||
249 | QUnit.test('should clear selection if above the first section', function (assert) { | |
250 | assert.expect(3) | |
251 | var done = assert.async() | |
252 | ||
253 | var sectionHTML = '<div id="header" style="height: 500px;"></div>' | |
254 | + '<nav id="navigation" class="navbar">' | |
255 | + '<ul class="nav navbar-nav">' | |
256 | + '<li><a id="one-link" class="nav-link active" href="#one">One</a></li>' | |
257 | + '<li><a id="two-link" class="nav-link" href="#two">Two</a></li>' | |
258 | + '<li><a id="three-link" class="nav-link" href="#three">Three</a></li>' | |
259 | + '</ul>' | |
260 | + '</nav>' | |
261 | $(sectionHTML).appendTo('#qunit-fixture') | |
262 | ||
263 | var scrollspyHTML = '<div id="content" style="height: 200px; overflow-y: auto;">' | |
264 | + '<div id="spacer" style="height: 100px;"/>' | |
265 | + '<div id="one" style="height: 100px;"/>' | |
266 | + '<div id="two" style="height: 100px;"/>' | |
267 | + '<div id="three" style="height: 100px;"/>' | |
268 | + '<div id="spacer" style="height: 100px;"/>' | |
269 | + '</div>' | |
270 | var $scrollspy = $(scrollspyHTML).appendTo('#qunit-fixture') | |
271 | ||
272 | $scrollspy | |
273 | .bootstrapScrollspy({ | |
274 | target: '#navigation', | |
275 | offset: $scrollspy.position().top | |
276 | }) | |
277 | .one('scroll', function () { | |
278 | assert.strictEqual($('.active').length, 1, '"active" class on only one element present') | |
279 | assert.strictEqual($('.active').is('#two-link'), true, '"active" class on second section') | |
280 | $scrollspy | |
281 | .one('scroll', function () { | |
282 | assert.strictEqual($('.active').length, 0, 'selection cleared') | |
283 | done() | |
284 | }) | |
285 | .scrollTop(0) | |
286 | }) | |
287 | .scrollTop(201) | |
288 | }) | |
289 | ||
290 | QUnit.test('should NOT clear selection if above the first section and first section is at the top', function (assert) { | |
291 | assert.expect(4) | |
292 | var done = assert.async() | |
293 | ||
294 | var sectionHTML = '<div id="header" style="height: 500px;"></div>' | |
295 | + '<nav id="navigation" class="navbar">' | |
296 | + '<ul class="nav navbar-nav">' | |
297 | + '<li><a id="one-link" class="nav-link active" href="#one">One</a></li>' | |
298 | + '<li><a id="two-link" class="nav-link" href="#two">Two</a></li>' | |
299 | + '<li><a id="three-link" class="nav-link" href="#three">Three</a></li>' | |
300 | + '</ul>' | |
301 | + '</nav>' | |
302 | $(sectionHTML).appendTo('#qunit-fixture') | |
303 | ||
304 | var negativeHeight = -10 | |
305 | var startOfSectionTwo = 101 | |
306 | ||
307 | var scrollspyHTML = '<div id="content" style="height: 200px; overflow-y: auto;">' | |
308 | + '<div id="one" style="height: 100px;"/>' | |
309 | + '<div id="two" style="height: 100px;"/>' | |
310 | + '<div id="three" style="height: 100px;"/>' | |
311 | + '<div id="spacer" style="height: 100px;"/>' | |
312 | + '</div>' | |
313 | var $scrollspy = $(scrollspyHTML).appendTo('#qunit-fixture') | |
314 | ||
315 | $scrollspy | |
316 | .bootstrapScrollspy({ | |
317 | target: '#navigation', | |
318 | offset: $scrollspy.position().top | |
319 | }) | |
320 | .one('scroll', function () { | |
321 | assert.strictEqual($('.active').length, 1, '"active" class on only one element present') | |
322 | assert.strictEqual($('.active').is('#two-link'), true, '"active" class on second section') | |
323 | $scrollspy | |
324 | .one('scroll', function () { | |
325 | assert.strictEqual($('.active').length, 1, '"active" class on only one element present') | |
326 | assert.strictEqual($('.active').is('#one-link'), true, '"active" class on first section') | |
327 | done() | |
328 | }) | |
329 | .scrollTop(negativeHeight) | |
330 | }) | |
331 | .scrollTop(startOfSectionTwo) | |
332 | }) | |
333 | ||
334 | QUnit.test('should correctly select navigation element on backward scrolling when each target section height is 100%', function (assert) { | |
335 | assert.expect(5) | |
336 | var navbarHtml = | |
337 | '<nav class="navbar">' | |
338 | + '<ul class="nav">' | |
339 | + '<li><a id="li-100-1" class="nav-link" href="#div-100-1">div 1</a></li>' | |
340 | + '<li><a id="li-100-2" class="nav-link" href="#div-100-2">div 2</a></li>' | |
341 | + '<li><a id="li-100-3" class="nav-link" href="#div-100-3">div 3</a></li>' | |
342 | + '<li><a id="li-100-4" class="nav-link" href="#div-100-4">div 4</a></li>' | |
343 | + '<li><a id="li-100-5" class="nav-link" href="#div-100-5">div 5</a></li>' | |
344 | + '</ul>' | |
345 | + '</nav>' | |
346 | var contentHtml = | |
347 | '<div class="content" style="position: relative; overflow: auto; height: 100px">' | |
348 | + '<div id="div-100-1" style="position: relative; height: 100%; padding: 0; margin: 0">div 1</div>' | |
349 | + '<div id="div-100-2" style="position: relative; height: 100%; padding: 0; margin: 0">div 2</div>' | |
350 | + '<div id="div-100-3" style="position: relative; height: 100%; padding: 0; margin: 0">div 3</div>' | |
351 | + '<div id="div-100-4" style="position: relative; height: 100%; padding: 0; margin: 0">div 4</div>' | |
352 | + '<div id="div-100-5" style="position: relative; height: 100%; padding: 0; margin: 0">div 5</div>' | |
353 | + '</div>' | |
354 | ||
355 | $(navbarHtml).appendTo('#qunit-fixture') | |
356 | var $content = $(contentHtml) | |
357 | .appendTo('#qunit-fixture') | |
358 | .bootstrapScrollspy({ offset: 0, target: '.navbar' }) | |
359 | ||
360 | var testElementIsActiveAfterScroll = function (element, target) { | |
361 | var deferred = $.Deferred() | |
362 | var scrollHeight = Math.ceil($content.scrollTop() + $(target).position().top) | |
363 | $content.one('scroll', function () { | |
364 | assert.ok($(element).hasClass('active'), 'target:' + target + ', element: ' + element) | |
365 | deferred.resolve() | |
366 | }) | |
367 | $content.scrollTop(scrollHeight) | |
368 | return deferred.promise() | |
369 | } | |
370 | ||
371 | var done = assert.async() | |
372 | $.when(testElementIsActiveAfterScroll('#li-100-5', '#div-100-5')) | |
373 | .then(function () { return testElementIsActiveAfterScroll('#li-100-4', '#div-100-4') }) | |
374 | .then(function () { return testElementIsActiveAfterScroll('#li-100-3', '#div-100-3') }) | |
375 | .then(function () { return testElementIsActiveAfterScroll('#li-100-2', '#div-100-2') }) | |
376 | .then(function () { return testElementIsActiveAfterScroll('#li-100-1', '#div-100-1') }) | |
377 | .then(function () { done() }) | |
378 | }) | |
379 | ||
380 | QUnit.test('should allow passed in option offset method: offset', function (assert) { | |
381 | assert.expect(4) | |
382 | ||
383 | var testOffsetMethod = function (type) { | |
384 | var $navbar = $( | |
385 | '<nav class="navbar"' + (type === 'data' ? ' id="navbar-offset-method-menu"' : '') + '>' | |
386 | + '<ul class="nav">' | |
387 | + '<li><a id="li-' + type + 'm-1" class="nav-link" href="#div-' + type + 'm-1">div 1</a></li>' | |
388 | + '<li><a id="li-' + type + 'm-2" class="nav-link" href="#div-' + type + 'm-2">div 2</a></li>' | |
389 | + '<li><a id="li-' + type + 'm-3" class="nav-link" href="#div-' + type + 'm-3">div 3</a></li>' | |
390 | + '</ul>' | |
391 | + '</nav>' | |
392 | ) | |
393 | var $content = $( | |
394 | '<div class="content"' + (type === 'data' ? ' data-spy="scroll" data-target="#navbar-offset-method-menu" data-offset="0" data-method="offset"' : '') + ' style="position: relative; overflow: auto; height: 100px">' | |
395 | + '<div id="div-' + type + 'm-1" style="position: relative; height: 200px; padding: 0; margin: 0">div 1</div>' | |
396 | + '<div id="div-' + type + 'm-2" style="position: relative; height: 150px; padding: 0; margin: 0">div 2</div>' | |
397 | + '<div id="div-' + type + 'm-3" style="position: relative; height: 250px; padding: 0; margin: 0">div 3</div>' | |
398 | + '</div>' | |
399 | ) | |
400 | ||
401 | $navbar.appendTo('#qunit-fixture') | |
402 | $content.appendTo('#qunit-fixture') | |
403 | ||
404 | if (type === 'js') { | |
405 | $content.bootstrapScrollspy({ target: '.navbar', offset: 0, method: 'offset' }) | |
406 | } | |
407 | else if (type === 'data') { | |
408 | $(window).trigger('load') | |
409 | } | |
410 | ||
411 | var $target = $('#div-' + type + 'm-2') | |
412 | var scrollspy = $content.data('bs.scrollspy') | |
413 | ||
414 | assert.ok(scrollspy._offsets[1] === $target.offset().top, 'offset method with ' + type + ' option') | |
415 | assert.ok(scrollspy._offsets[1] !== $target.position().top, 'position method with ' + type + ' option') | |
416 | $navbar.remove() | |
417 | $content.remove() | |
418 | } | |
419 | ||
420 | testOffsetMethod('js') | |
421 | testOffsetMethod('data') | |
422 | }) | |
423 | ||
424 | QUnit.test('should allow passed in option offset method: position', function (assert) { | |
425 | assert.expect(4) | |
426 | ||
427 | var testOffsetMethod = function (type) { | |
428 | var $navbar = $( | |
429 | '<nav class="navbar"' + (type === 'data' ? ' id="navbar-offset-method-menu"' : '') + '>' | |
430 | + '<ul class="nav">' | |
431 | + '<li><a class="nav-link" id="li-' + type + 'm-1" href="#div-' + type + 'm-1">div 1</a></li>' | |
432 | + '<li><a class="nav-link" id="li-' + type + 'm-2" href="#div-' + type + 'm-2">div 2</a></li>' | |
433 | + '<li><a class="nav-link" id="li-' + type + 'm-3" href="#div-' + type + 'm-3">div 3</a></li>' | |
434 | + '</ul>' | |
435 | + '</nav>' | |
436 | ) | |
437 | var $content = $( | |
438 | '<div class="content"' + (type === 'data' ? ' data-spy="scroll" data-target="#navbar-offset-method-menu" data-offset="0" data-method="position"' : '') + ' style="position: relative; overflow: auto; height: 100px">' | |
439 | + '<div id="div-' + type + 'm-1" style="position: relative; height: 200px; padding: 0; margin: 0">div 1</div>' | |
440 | + '<div id="div-' + type + 'm-2" style="position: relative; height: 150px; padding: 0; margin: 0">div 2</div>' | |
441 | + '<div id="div-' + type + 'm-3" style="position: relative; height: 250px; padding: 0; margin: 0">div 3</div>' | |
442 | + '</div>' | |
443 | ) | |
444 | ||
445 | $navbar.appendTo('#qunit-fixture') | |
446 | $content.appendTo('#qunit-fixture') | |
447 | ||
448 | if (type === 'js') { $content.bootstrapScrollspy({ target: '.navbar', offset: 0, method: 'position' }) } | |
449 | else if (type === 'data') { $(window).trigger('load') } | |
450 | ||
451 | var $target = $('#div-' + type + 'm-2') | |
452 | var scrollspy = $content.data('bs.scrollspy') | |
453 | ||
454 | assert.ok(scrollspy._offsets[1] !== $target.offset().top, 'offset method with ' + type + ' option') | |
455 | assert.ok(scrollspy._offsets[1] === $target.position().top, 'position method with ' + type + ' option') | |
456 | $navbar.remove() | |
457 | $content.remove() | |
458 | } | |
459 | ||
460 | testOffsetMethod('js') | |
461 | testOffsetMethod('data') | |
462 | }) | |
463 | ||
464 | }) |