modal.js 25.3 KB
Newer Older
1
$(function () {
2
  'use strict'
3

fat's avatar
fat committed
4
  QUnit.module('modal plugin')
XhmikosR's avatar
XhmikosR committed
5

fat's avatar
fat committed
6
7
8
  QUnit.test('should be defined on jquery object', function (assert) {
    assert.expect(1)
    assert.ok($(document.body).modal, 'modal method is defined')
XhmikosR's avatar
XhmikosR committed
9
10
  })

fat's avatar
fat committed
11
  QUnit.module('modal', {
12
13
14
    before: function () {
      // Enable the scrollbar measurer
      $('<style type="text/css"> .modal-scrollbar-measure { position: absolute; top: -9999px; width: 50px; height: 50px; overflow: scroll; } </style>').appendTo('head')
15
16
17
18
19
20
21
22
23
      // Function to calculate the scrollbar width which is then compared to the padding or margin changes
      $.fn.getScrollbarWidth = function () {
        var scrollDiv = document.createElement('div')
        scrollDiv.className = 'modal-scrollbar-measure'
        document.body.appendChild(scrollDiv)
        var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth
        document.body.removeChild(scrollDiv)
        return scrollbarWidth
      }
David Bailey's avatar
David Bailey committed
24
25
      // Simulate scrollbars in PhantomJS
      $('html').css('padding-right', '16px')
26
    },
fat's avatar
fat committed
27
    beforeEach: function () {
28
29
30
      // Run all tests in noConflict mode -- it's the only way to ensure that the plugin works in noConflict mode
      $.fn.bootstrapModal = $.fn.modal.noConflict()
    },
fat's avatar
fat committed
31
    afterEach: function () {
32
33
34
35
36
      $.fn.modal = $.fn.bootstrapModal
      delete $.fn.bootstrapModal
    }
  })

fat's avatar
fat committed
37
38
  QUnit.test('should provide no conflict', function (assert) {
    assert.expect(1)
XhmikosR's avatar
XhmikosR committed
39
    assert.strictEqual(typeof $.fn.modal, 'undefined', 'modal was set back to undefined (orig value)')
40
41
  })

42
43
44
45
46
47
48
49
50
51
52
53
  QUnit.test('should throw explicit error on undefined method', function (assert) {
    assert.expect(1)
    var $el = $('<div id="modal-test"/>')
    $el.bootstrapModal()
    try {
      $el.bootstrapModal('noMethod')
    }
    catch (err) {
      assert.strictEqual(err.message, 'No method named "noMethod"')
    }
  })

fat's avatar
fat committed
54
55
  QUnit.test('should return jquery collection containing the element', function (assert) {
    assert.expect(2)
Heinrich Fenkart's avatar
Heinrich Fenkart committed
56
57
    var $el = $('<div id="modal-test"/>')
    var $modal = $el.bootstrapModal()
fat's avatar
fat committed
58
59
    assert.ok($modal instanceof $, 'returns jquery collection')
    assert.strictEqual($modal[0], $el[0], 'collection contains element')
XhmikosR's avatar
XhmikosR committed
60
61
  })

fat's avatar
fat committed
62
63
  QUnit.test('should expose defaults var for settings', function (assert) {
    assert.expect(1)
64
    assert.ok($.fn.bootstrapModal.Constructor.Default, 'default object exposed')
XhmikosR's avatar
XhmikosR committed
65
66
  })

fat's avatar
fat committed
67
68
  QUnit.test('should insert into dom when show method is called', function (assert) {
    assert.expect(1)
69
    var done = assert.async()
Heinrich Fenkart's avatar
Heinrich Fenkart committed
70
71

    $('<div id="modal-test"/>')
XhmikosR's avatar
XhmikosR committed
72
      .on('shown.bs.modal', function () {
fat's avatar
fat committed
73
        assert.notEqual($('#modal-test').length, 0, 'modal inserted into dom')
74
        done()
XhmikosR's avatar
XhmikosR committed
75
      })
76
      .bootstrapModal('show')
XhmikosR's avatar
XhmikosR committed
77
78
  })

fat's avatar
fat committed
79
80
  QUnit.test('should fire show event', function (assert) {
    assert.expect(1)
81
    var done = assert.async()
Heinrich Fenkart's avatar
Heinrich Fenkart committed
82
83

    $('<div id="modal-test"/>')
XhmikosR's avatar
XhmikosR committed
84
      .on('show.bs.modal', function () {
fat's avatar
fat committed
85
        assert.ok(true, 'show event fired')
86
        done()
XhmikosR's avatar
XhmikosR committed
87
      })
88
      .bootstrapModal('show')
XhmikosR's avatar
XhmikosR committed
89
90
  })

fat's avatar
fat committed
91
92
  QUnit.test('should not fire shown when show was prevented', function (assert) {
    assert.expect(1)
93
    var done = assert.async()
Heinrich Fenkart's avatar
Heinrich Fenkart committed
94
95

    $('<div id="modal-test"/>')
XhmikosR's avatar
XhmikosR committed
96
97
      .on('show.bs.modal', function (e) {
        e.preventDefault()
fat's avatar
fat committed
98
        assert.ok(true, 'show event fired')
99
        done()
XhmikosR's avatar
XhmikosR committed
100
101
      })
      .on('shown.bs.modal', function () {
fat's avatar
fat committed
102
        assert.ok(false, 'shown event fired')
XhmikosR's avatar
XhmikosR committed
103
      })
104
      .bootstrapModal('show')
XhmikosR's avatar
XhmikosR committed
105
106
  })

fat's avatar
fat committed
107
108
  QUnit.test('should hide modal when hide is called', function (assert) {
    assert.expect(3)
109
    var done = assert.async()
XhmikosR's avatar
XhmikosR committed
110

Heinrich Fenkart's avatar
Heinrich Fenkart committed
111
    $('<div id="modal-test"/>')
XhmikosR's avatar
XhmikosR committed
112
      .on('shown.bs.modal', function () {
fat's avatar
fat committed
113
114
        assert.ok($('#modal-test').is(':visible'), 'modal visible')
        assert.notEqual($('#modal-test').length, 0, 'modal inserted into dom')
115
        $(this).bootstrapModal('hide')
XhmikosR's avatar
XhmikosR committed
116
117
      })
      .on('hidden.bs.modal', function () {
fat's avatar
fat committed
118
        assert.ok(!$('#modal-test').is(':visible'), 'modal hidden')
119
        done()
XhmikosR's avatar
XhmikosR committed
120
      })
121
      .bootstrapModal('show')
XhmikosR's avatar
XhmikosR committed
122
123
  })

fat's avatar
fat committed
124
125
  QUnit.test('should toggle when toggle is called', function (assert) {
    assert.expect(3)
126
    var done = assert.async()
Heinrich Fenkart's avatar
Heinrich Fenkart committed
127
128

    $('<div id="modal-test"/>')
XhmikosR's avatar
XhmikosR committed
129
      .on('shown.bs.modal', function () {
fat's avatar
fat committed
130
131
        assert.ok($('#modal-test').is(':visible'), 'modal visible')
        assert.notEqual($('#modal-test').length, 0, 'modal inserted into dom')
Heinrich Fenkart's avatar
Heinrich Fenkart committed
132
        $(this).bootstrapModal('toggle')
XhmikosR's avatar
XhmikosR committed
133
134
      })
      .on('hidden.bs.modal', function () {
fat's avatar
fat committed
135
        assert.ok(!$('#modal-test').is(':visible'), 'modal hidden')
136
        done()
XhmikosR's avatar
XhmikosR committed
137
      })
138
      .bootstrapModal('toggle')
XhmikosR's avatar
XhmikosR committed
139
140
  })

fat's avatar
fat committed
141
142
  QUnit.test('should remove from dom when click [data-dismiss="modal"]', function (assert) {
    assert.expect(3)
143
    var done = assert.async()
Heinrich Fenkart's avatar
Heinrich Fenkart committed
144
145

    $('<div id="modal-test"><span class="close" data-dismiss="modal"/></div>')
XhmikosR's avatar
XhmikosR committed
146
      .on('shown.bs.modal', function () {
fat's avatar
fat committed
147
148
149
        assert.ok($('#modal-test').is(':visible'), 'modal visible')
        assert.notEqual($('#modal-test').length, 0, 'modal inserted into dom')
        $(this).find('.close').trigger('click')
XhmikosR's avatar
XhmikosR committed
150
151
      })
      .on('hidden.bs.modal', function () {
fat's avatar
fat committed
152
        assert.ok(!$('#modal-test').is(':visible'), 'modal hidden')
153
        done()
XhmikosR's avatar
XhmikosR committed
154
      })
155
      .bootstrapModal('toggle')
XhmikosR's avatar
XhmikosR committed
156
157
  })

fat's avatar
fat committed
158
159
  QUnit.test('should allow modal close with "backdrop:false"', function (assert) {
    assert.expect(2)
160
    var done = assert.async()
Heinrich Fenkart's avatar
Heinrich Fenkart committed
161
162

    $('<div id="modal-test" data-backdrop="false"/>')
XhmikosR's avatar
XhmikosR committed
163
      .on('shown.bs.modal', function () {
fat's avatar
fat committed
164
        assert.ok($('#modal-test').is(':visible'), 'modal visible')
Heinrich Fenkart's avatar
Heinrich Fenkart committed
165
        $(this).bootstrapModal('hide')
XhmikosR's avatar
XhmikosR committed
166
167
      })
      .on('hidden.bs.modal', function () {
fat's avatar
fat committed
168
        assert.ok(!$('#modal-test').is(':visible'), 'modal hidden')
169
        done()
XhmikosR's avatar
XhmikosR committed
170
      })
171
      .bootstrapModal('show')
XhmikosR's avatar
XhmikosR committed
172
173
  })

fat's avatar
fat committed
174
175
  QUnit.test('should close modal when clicking outside of modal-content', function (assert) {
    assert.expect(3)
176
    var done = assert.async()
Heinrich Fenkart's avatar
Heinrich Fenkart committed
177
178

    $('<div id="modal-test"><div class="contents"/></div>')
179
      .on('shown.bs.modal', function () {
fat's avatar
fat committed
180
181
182
183
        assert.notEqual($('#modal-test').length, 0, 'modal inserted into dom')
        $('.contents').trigger('click')
        assert.ok($('#modal-test').is(':visible'), 'modal visible')
        $('#modal-test').trigger('click')
XhmikosR's avatar
XhmikosR committed
184
      })
185
      .on('hidden.bs.modal', function () {
fat's avatar
fat committed
186
        assert.ok(!$('#modal-test').is(':visible'), 'modal hidden')
187
        done()
XhmikosR's avatar
XhmikosR committed
188
      })
189
      .bootstrapModal('show')
XhmikosR's avatar
XhmikosR committed
190
191
  })

192
193
194
195
196
197
198
199
200
201
202
203
204
  QUnit.test('should not close modal when clicking outside of modal-content if data-backdrop="true"', function (assert) {
    assert.expect(1)
    var done = assert.async()

    $('<div id="modal-test" data-backdrop="false"><div class="contents"/></div>')
      .on('shown.bs.modal', function () {
        $('#modal-test').trigger('click')
        assert.ok($('#modal-test').is(':visible'), 'modal not hidden')
        done()
      })
      .bootstrapModal('show')
  })

fat's avatar
fat committed
205
206
  QUnit.test('should close modal when escape key is pressed via keydown', function (assert) {
    assert.expect(3)
207
    var done = assert.async()
208

fat's avatar
fat committed
209
210
    var $div = $('<div id="modal-test"/>')
    $div
211
      .on('shown.bs.modal', function () {
212
        assert.ok($('#modal-test').length, 'modal inserted into dom')
fat's avatar
fat committed
213
214
        assert.ok($('#modal-test').is(':visible'), 'modal visible')
        $div.trigger($.Event('keydown', { which: 27 }))
215
216

        setTimeout(function () {
fat's avatar
fat committed
217
218
          assert.ok(!$('#modal-test').is(':visible'), 'modal hidden')
          $div.remove()
219
          done()
220
221
222
223
224
        }, 0)
      })
      .bootstrapModal('show')
  })

fat's avatar
fat committed
225
226
  QUnit.test('should not close modal when escape key is pressed via keyup', function (assert) {
    assert.expect(3)
227
    var done = assert.async()
228

fat's avatar
fat committed
229
230
    var $div = $('<div id="modal-test"/>')
    $div
231
      .on('shown.bs.modal', function () {
fat's avatar
fat committed
232
233
234
        assert.ok($('#modal-test').length, 'modal inserted into dom')
        assert.ok($('#modal-test').is(':visible'), 'modal visible')
        $div.trigger($.Event('keyup', { which: 27 }))
235
236

        setTimeout(function () {
fat's avatar
fat committed
237
238
          assert.ok($div.is(':visible'), 'modal still visible')
          $div.remove()
239
          done()
240
241
242
243
244
        }, 0)
      })
      .bootstrapModal('show')
  })

fat's avatar
fat committed
245
246
  QUnit.test('should trigger hide event once when clicking outside of modal-content', function (assert) {
    assert.expect(1)
247
    var done = assert.async()
XhmikosR's avatar
XhmikosR committed
248
249
250

    var triggered

Heinrich Fenkart's avatar
Heinrich Fenkart committed
251
    $('<div id="modal-test"><div class="contents"/></div>')
252
      .on('shown.bs.modal', function () {
XhmikosR's avatar
XhmikosR committed
253
        triggered = 0
fat's avatar
fat committed
254
        $('#modal-test').trigger('click')
XhmikosR's avatar
XhmikosR committed
255
      })
256
      .on('hide.bs.modal', function () {
XhmikosR's avatar
XhmikosR committed
257
        triggered += 1
fat's avatar
fat committed
258
        assert.strictEqual(triggered, 1, 'modal hide triggered once')
259
        done()
XhmikosR's avatar
XhmikosR committed
260
      })
261
      .bootstrapModal('show')
XhmikosR's avatar
XhmikosR committed
262
263
  })

264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
  QUnit.test('should remove aria-hidden attribute when shown, add it back when hidden', function (assert) {
    assert.expect(3)
    var done = assert.async()

    $('<div id="modal-test" aria-hidden="true"/>')
      .on('shown.bs.modal', function () {
        assert.notOk($('#modal-test').is('[aria-hidden]'), 'aria-hidden attribute removed')
        $(this).bootstrapModal('hide')
      })
      .on('hidden.bs.modal', function () {
        assert.ok($('#modal-test').is('[aria-hidden]'), 'aria-hidden attribute added')
        assert.strictEqual($('#modal-test').attr('aria-hidden'), 'true', 'correct aria-hidden="true" added')
        done()
      })
      .bootstrapModal('show')
  })

fat's avatar
fat committed
281
282
  QUnit.test('should close reopened modal with [data-dismiss="modal"] click', function (assert) {
    assert.expect(2)
283
    var done = assert.async()
Heinrich Fenkart's avatar
Heinrich Fenkart committed
284
285

    $('<div id="modal-test"><div class="contents"><div id="close" data-dismiss="modal"/></div></div>')
286
      .one('shown.bs.modal', function () {
fat's avatar
fat committed
287
        $('#close').trigger('click')
XhmikosR's avatar
XhmikosR committed
288
289
      })
      .one('hidden.bs.modal', function () {
290
        // after one open-close cycle
fat's avatar
fat committed
291
        assert.ok(!$('#modal-test').is(':visible'), 'modal hidden')
Heinrich Fenkart's avatar
Heinrich Fenkart committed
292
        $(this)
293
          .one('shown.bs.modal', function () {
fat's avatar
fat committed
294
            $('#close').trigger('click')
295
          })
Heinrich Fenkart's avatar
Heinrich Fenkart committed
296
          .one('hidden.bs.modal', function () {
fat's avatar
fat committed
297
            assert.ok(!$('#modal-test').is(':visible'), 'modal hidden')
298
            done()
Heinrich Fenkart's avatar
Heinrich Fenkart committed
299
300
          })
          .bootstrapModal('show')
XhmikosR's avatar
XhmikosR committed
301
      })
302
      .bootstrapModal('show')
XhmikosR's avatar
XhmikosR committed
303
  })
Chris Rebert's avatar
Chris Rebert committed
304

fat's avatar
fat committed
305
306
  QUnit.test('should restore focus to toggling element when modal is hidden after having been opened via data-api', function (assert) {
    assert.expect(1)
307
    var done = assert.async()
Heinrich Fenkart's avatar
Heinrich Fenkart committed
308
309
310
311

    var $toggleBtn = $('<button data-toggle="modal" data-target="#modal-test"/>').appendTo('#qunit-fixture')

    $('<div id="modal-test"><div class="contents"><div id="close" data-dismiss="modal"/></div></div>')
Chris Rebert's avatar
Chris Rebert committed
312
      .on('hidden.bs.modal', function () {
Heinrich Fenkart's avatar
Heinrich Fenkart committed
313
        setTimeout(function () {
fat's avatar
fat committed
314
          assert.ok($(document.activeElement).is($toggleBtn), 'toggling element is once again focused')
315
          done()
Chris Rebert's avatar
Chris Rebert committed
316
317
318
        }, 0)
      })
      .on('shown.bs.modal', function () {
fat's avatar
fat committed
319
        $('#close').trigger('click')
Chris Rebert's avatar
Chris Rebert committed
320
321
      })
      .appendTo('#qunit-fixture')
Heinrich Fenkart's avatar
Heinrich Fenkart committed
322

fat's avatar
fat committed
323
    $toggleBtn.trigger('click')
Chris Rebert's avatar
Chris Rebert committed
324
325
  })

fat's avatar
fat committed
326
327
  QUnit.test('should not restore focus to toggling element if the associated show event gets prevented', function (assert) {
    assert.expect(1)
328
    var done = assert.async()
Heinrich Fenkart's avatar
Heinrich Fenkart committed
329
330
331
332
    var $toggleBtn = $('<button data-toggle="modal" data-target="#modal-test"/>').appendTo('#qunit-fixture')
    var $otherBtn = $('<button id="other-btn"/>').appendTo('#qunit-fixture')

    $('<div id="modal-test"><div class="contents"><div id="close" data-dismiss="modal"/></div>')
Chris Rebert's avatar
Chris Rebert committed
333
334
      .one('show.bs.modal', function (e) {
        e.preventDefault()
fat's avatar
fat committed
335
        $otherBtn.trigger('focus')
Heinrich Fenkart's avatar
Heinrich Fenkart committed
336
337
338
        setTimeout($.proxy(function () {
          $(this).bootstrapModal('show')
        }, this), 0)
Chris Rebert's avatar
Chris Rebert committed
339
340
      })
      .on('hidden.bs.modal', function () {
Heinrich Fenkart's avatar
Heinrich Fenkart committed
341
        setTimeout(function () {
fat's avatar
fat committed
342
          assert.ok($(document.activeElement).is($otherBtn), 'focus returned to toggling element')
343
          done()
Chris Rebert's avatar
Chris Rebert committed
344
345
346
        }, 0)
      })
      .on('shown.bs.modal', function () {
fat's avatar
fat committed
347
        $('#close').trigger('click')
Chris Rebert's avatar
Chris Rebert committed
348
349
      })
      .appendTo('#qunit-fixture')
Heinrich Fenkart's avatar
Heinrich Fenkart committed
350

fat's avatar
fat committed
351
352
353
    $toggleBtn.trigger('click')
  })

David Bailey's avatar
David Bailey committed
354
355
356
357
358
359
360
361
362
363
364
365
366
367
  QUnit.test('should adjust the inline padding of the modal when opening', function (assert) {
    assert.expect(1)
    var done = assert.async()

    $('<div id="modal-test"/>')
      .on('shown.bs.modal', function () {
        var expectedPadding = $(this).getScrollbarWidth() + 'px'
        var currentPadding = $(this).css('padding-right')
        assert.strictEqual(currentPadding, expectedPadding, 'modal padding should be adjusted while opening')
        done()
      })
      .bootstrapModal('show')
  })

368
  QUnit.test('should adjust the inline body padding when opening and restore when closing', function (assert) {
fat's avatar
fat committed
369
370
371
    assert.expect(2)
    var done = assert.async()
    var $body = $(document.body)
372
    var originalPadding = $body.css('padding-right')
fat's avatar
fat committed
373
374
375

    $('<div id="modal-test"/>')
      .on('hidden.bs.modal', function () {
376
377
        var currentPadding = $body.css('padding-right')
        assert.strictEqual(currentPadding, originalPadding, 'body padding should be reset after closing')
fat's avatar
fat committed
378
379
380
381
        $body.removeAttr('style')
        done()
      })
      .on('shown.bs.modal', function () {
382
        var expectedPadding = parseFloat(originalPadding) + $(this).getScrollbarWidth() + 'px'
383
        var currentPadding = $body.css('padding-right')
384
        assert.strictEqual(currentPadding, expectedPadding, 'body padding should be adjusted while opening')
fat's avatar
fat committed
385
386
387
388
389
        $(this).bootstrapModal('hide')
      })
      .bootstrapModal('show')
  })

390
391
  QUnit.test('should store the original body padding in data-padding-right before showing', function (assert) {
    assert.expect(2)
fat's avatar
fat committed
392
393
    var done = assert.async()
    var $body = $(document.body)
394
395
    var originalPadding = '0px'
    $body.css('padding-right', originalPadding)
fat's avatar
fat committed
396
397
398

    $('<div id="modal-test"/>')
      .on('hidden.bs.modal', function () {
XhmikosR's avatar
XhmikosR committed
399
        assert.strictEqual(typeof $body.data('padding-right'), 'undefined', 'data-padding-right should be cleared after closing')
400
        $body.removeAttr('style')
fat's avatar
fat committed
401
402
403
        done()
      })
      .on('shown.bs.modal', function () {
404
        assert.strictEqual($body.data('padding-right'), originalPadding, 'original body padding should be stored in data-padding-right')
fat's avatar
fat committed
405
406
407
408
409
        $(this).bootstrapModal('hide')
      })
      .bootstrapModal('show')
  })

David Bailey's avatar
David Bailey committed
410
411
412
413
414
415
  QUnit.test('should not adjust the inline body padding when it does not overflow', function (assert) {
    assert.expect(1)
    var done = assert.async()
    var $body = $(document.body)
    var originalPadding = $body.css('padding-right')

David Bailey's avatar
David Bailey committed
416
417
418
419
    // Hide scrollbars to prevent the body overflowing
    $body.css('overflow', 'hidden')        // real scrollbar (for in-browser testing)
    $('html').css('padding-right', '0px')  // simulated scrollbar (for PhantomJS)

David Bailey's avatar
David Bailey committed
420
421
422
423
424
425
    $('<div id="modal-test"/>')
      .on('shown.bs.modal', function () {
        var currentPadding = $body.css('padding-right')
        assert.strictEqual(currentPadding, originalPadding, 'body padding should not be adjusted')
        $(this).bootstrapModal('hide')

David Bailey's avatar
David Bailey committed
426
427
428
        // restore scrollbars
        $body.css('overflow', 'auto')
        $('html').css('padding-right', '16px')
David Bailey's avatar
David Bailey committed
429
430
431
432
433
        done()
      })
      .bootstrapModal('show')
  })

434
  QUnit.test('should adjust the inline padding of fixed elements when opening and restore when closing', function (assert) {
435
436
    assert.expect(2)
    var done = assert.async()
437
438
    var $element = $('<div class="fixed-top"></div>').appendTo('#qunit-fixture')
    var originalPadding = $element.css('padding-right')
439
440

    $('<div id="modal-test"/>')
441
442
443
444
445
446
      .on('hidden.bs.modal', function () {
        var currentPadding = $element.css('padding-right')
        assert.strictEqual(currentPadding, originalPadding, 'fixed element padding should be reset after closing')
        $element.remove()
        done()
      })
447
      .on('shown.bs.modal', function () {
448
        var expectedPadding = parseFloat(originalPadding) + $(this).getScrollbarWidth() + 'px'
449
        var currentPadding = $element.css('padding-right')
450
        assert.strictEqual(currentPadding, expectedPadding, 'fixed element padding should be adjusted while opening')
451
452
453
454
455
456
457
458
459
460
461
462
463
464
        $(this).bootstrapModal('hide')
      })
      .bootstrapModal('show')
  })

  QUnit.test('should store the original padding of fixed elements in data-padding-right before showing', function (assert) {
    assert.expect(2)
    var done = assert.async()
    var $element = $('<div class="fixed-top"></div>').appendTo('#qunit-fixture')
    var originalPadding = '0px'
    $element.css('padding-right', originalPadding)

    $('<div id="modal-test"/>')
      .on('hidden.bs.modal', function () {
XhmikosR's avatar
XhmikosR committed
465
        assert.strictEqual(typeof $element.data('padding-right'), 'undefined', 'data-padding-right should be cleared after closing')
466
        $element.remove()
467
468
        done()
      })
469
470
471
472
      .on('shown.bs.modal', function () {
        assert.strictEqual($element.data('padding-right'), originalPadding, 'original fixed element padding should be stored in data-padding-right')
        $(this).bootstrapModal('hide')
      })
473
474
475
      .bootstrapModal('show')
  })

476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
  QUnit.test('should adjust the inline margin of sticky elements when opening and restore when closing', function (assert) {
    assert.expect(2)
    var done = assert.async()
    var $element = $('<div class="sticky-top"></div>').appendTo('#qunit-fixture')
    var originalPadding = $element.css('margin-right')

    $('<div id="modal-test"/>')
      .on('hidden.bs.modal', function () {
        var currentPadding = $element.css('margin-right')
        assert.strictEqual(currentPadding, originalPadding, 'sticky element margin should be reset after closing')
        $element.remove()
        done()
      })
      .on('shown.bs.modal', function () {
        var expectedPadding = parseFloat(originalPadding) - $(this).getScrollbarWidth() + 'px'
        var currentPadding = $element.css('margin-right')
        assert.strictEqual(currentPadding, expectedPadding, 'sticky element margin should be adjusted while opening')
        $(this).bootstrapModal('hide')
      })
      .bootstrapModal('show')
  })

  QUnit.test('should store the original margin of sticky elements in data-margin-right before showing', function (assert) {
    assert.expect(2)
    var done = assert.async()
    var $element = $('<div class="sticky-top"></div>').appendTo('#qunit-fixture')
    var originalPadding = '0px'
    $element.css('margin-right', originalPadding)

    $('<div id="modal-test"/>')
      .on('hidden.bs.modal', function () {
        assert.strictEqual(typeof $element.data('margin-right'), 'undefined', 'data-margin-right should be cleared after closing')
        $element.remove()
        done()
      })
      .on('shown.bs.modal', function () {
        assert.strictEqual($element.data('margin-right'), originalPadding, 'original sticky element margin should be stored in data-margin-right')
        $(this).bootstrapModal('hide')
      })
      .bootstrapModal('show')
  })

518
519
520
521
522
523
524
525
526
527
528
529
530
531
  QUnit.test('should adjust the inline margin of the navbar-toggler when opening and restore when closing', function (assert) {
    assert.expect(2)
    var done = assert.async()
    var $element = $('<div class="navbar-toggler"></div>').appendTo('#qunit-fixture')
    var originalMargin = $element.css('margin-right')

    $('<div id="modal-test"/>')
      .on('hidden.bs.modal', function () {
        var currentMargin = $element.css('margin-right')
        assert.strictEqual(currentMargin, originalMargin, 'navbar-toggler margin should be reset after closing')
        $element.remove()
        done()
      })
      .on('shown.bs.modal', function () {
532
        var expectedMargin = parseFloat(originalMargin) + $(this).getScrollbarWidth() + 'px'
533
        var currentMargin = $element.css('margin-right')
534
        assert.strictEqual(currentMargin, expectedMargin, 'navbar-toggler margin should be adjusted while opening')
535
536
537
538
539
540
541
        $(this).bootstrapModal('hide')
      })
      .bootstrapModal('show')
  })

  QUnit.test('should store the original margin of the navbar-toggler in data-margin-right before showing', function (assert) {
    assert.expect(2)
542
    var done = assert.async()
543
544
545
    var $element = $('<div class="navbar-toggler"></div>').appendTo('#qunit-fixture')
    var originalMargin = '0px'
    $element.css('margin-right', originalMargin)
546
547

    $('<div id="modal-test"/>')
548
      .on('hidden.bs.modal', function () {
XhmikosR's avatar
XhmikosR committed
549
        assert.strictEqual(typeof $element.data('margin-right'), 'undefined', 'data-margin-right should be cleared after closing')
550
551
552
        $element.remove()
        done()
      })
553
      .on('shown.bs.modal', function () {
554
        assert.strictEqual($element.data('margin-right'), originalMargin, 'original navbar-toggler margin should be stored in data-margin-right')
555
556
        $(this).bootstrapModal('hide')
      })
557
558
559
560
561
562
563
564
565
566
      .bootstrapModal('show')
  })

  QUnit.test('should ignore values set via CSS when trying to restore body padding after closing', function (assert) {
    assert.expect(1)
    var done = assert.async()
    var $body = $(document.body)
    var $style = $('<style>body { padding-right: 42px; }</style>').appendTo('head')

    $('<div id="modal-test"/>')
567
      .on('hidden.bs.modal', function () {
David Bailey's avatar
David Bailey committed
568
        assert.strictEqual($body.attr('style').indexOf('padding-right'), -1, 'body does not have inline padding set')
569
        $style.remove()
570
571
        done()
      })
572
573
574
      .on('shown.bs.modal', function () {
        $(this).bootstrapModal('hide')
      })
575
576
577
      .bootstrapModal('show')
  })

fat's avatar
fat committed
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
  QUnit.test('should ignore other inline styles when trying to restore body padding after closing', function (assert) {
    assert.expect(2)
    var done = assert.async()
    var $body = $(document.body)
    var $style = $('<style>body { padding-right: 42px; }</style>').appendTo('head')

    $body.css('color', 'red')

    $('<div id="modal-test"/>')
      .on('hidden.bs.modal', function () {
        assert.strictEqual($body[0].style.paddingRight, '', 'body does not have inline padding set')
        assert.strictEqual($body[0].style.color, 'red', 'body still has other inline styles set')
        $body.removeAttr('style')
        $style.remove()
        done()
      })
      .on('shown.bs.modal', function () {
        $(this).bootstrapModal('hide')
      })
      .bootstrapModal('show')
  })

  QUnit.test('should properly restore non-pixel inline body padding after closing', function (assert) {
    assert.expect(1)
    var done = assert.async()
    var $body = $(document.body)

    $body.css('padding-right', '5%')

    $('<div id="modal-test"/>')
      .on('hidden.bs.modal', function () {
        assert.strictEqual($body[0].style.paddingRight, '5%', 'body does not have inline padding set')
        $body.removeAttr('style')
        done()
      })
      .on('shown.bs.modal', function () {
        $(this).bootstrapModal('hide')
      })
      .bootstrapModal('show')
Chris Rebert's avatar
Chris Rebert committed
617
  })
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639

  QUnit.test('should not follow link in area tag', function (assert) {
    assert.expect(2)
    var done = assert.async()

    $('<map><area id="test" shape="default" data-toggle="modal" data-target="#modal-test" href="demo.html"/></map>')
      .appendTo('#qunit-fixture')

    $('<div id="modal-test"><div class="contents"><div id="close" data-dismiss="modal"/></div></div>')
      .appendTo('#qunit-fixture')

    $('#test')
      .on('click.bs.modal.data-api', function (event) {
        assert.notOk(event.isDefaultPrevented(), 'navigating to href will happen')

        setTimeout(function () {
          assert.ok(event.isDefaultPrevented(), 'model shown instead of navigating to href')
          done()
        }, 1)
      })
      .trigger('click')
  })
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675

  QUnit.test('should not parse target as html', function (assert) {
    assert.expect(1)
    var done = assert.async()

    var $toggleBtn = $('<button data-toggle="modal" data-target="&lt;div id=&quot;modal-test&quot;&gt;&lt;div class=&quot;contents&quot;&lt;div&lt;div id=&quot;close&quot; data-dismiss=&quot;modal&quot;/&gt;&lt;/div&gt;&lt;/div&gt;"/>')
      .appendTo('#qunit-fixture')

    $toggleBtn.trigger('click')
    setTimeout(function () {
      assert.strictEqual($('#modal-test').length, 0, 'target has not been parsed and added to the document')
      done()
    }, 1)
  })

  QUnit.test('should not execute js from target', function (assert) {
    assert.expect(0)
    var done = assert.async()

    // This toggle button contains XSS payload in its data-target
    // Note: it uses the onerror handler of an img element to execute the js, because a simple script element does not work here
    //       a script element works in manual tests though, so here it is likely blocked by the qunit framework
    var $toggleBtn = $('<button data-toggle="modal" data-target="&lt;div&gt;&lt;image src=&quot;missing.png&quot; onerror=&quot;$(&apos;#qunit-fixture button.control&apos;).trigger(&apos;click&apos;)&quot;&gt;&lt;/div&gt;"/>')
      .appendTo('#qunit-fixture')
    // The XSS payload above does not have a closure over this function and cannot access the assert object directly
    // However, it can send a click event to the following control button, which will then fail the assert
    $('<button>')
      .addClass('control')
      .on('click', function () {
        assert.notOk(true, 'XSS payload is not executed as js')
      })
      .appendTo('#qunit-fixture')

    $toggleBtn.trigger('click')
    setTimeout(done, 500)
  })
lucascono's avatar
lucascono committed
676
677
678
679
680
681
682
683
684
685
686
687
688

  QUnit.test('should not try to open a modal which is already visible', function (assert) {
    assert.expect(1)
    var done = assert.async()
    var count = 0

    $('<div id="modal-test"/>').on('shown.bs.modal', function () {
      count++
    }).on('hidden.bs.modal', function () {
      assert.strictEqual(count, 1, 'show() runs only once')
      done()
    }).bootstrapModal('show').bootstrapModal('show').bootstrapModal('hide')
  })
689
})