modal.js 23.6 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
      }
24
25

      // Simulate scrollbars
David Bailey's avatar
David Bailey committed
26
      $('html').css('padding-right', '16px')
27
    },
fat's avatar
fat committed
28
    beforeEach: function () {
29
30
31
      // 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
32
    afterEach: function () {
33
34
      $('.modal-backdrop, #modal-test').remove()
      $(document.body).removeClass('modal-open')
35
36
      $.fn.modal = $.fn.bootstrapModal
      delete $.fn.bootstrapModal
37
      $('#qunit-fixture').html('')
38
39
40
    }
  })

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

46
47
48
49
50
51
  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')
XhmikosR's avatar
XhmikosR committed
52
    } catch (err) {
53
54
55
56
      assert.strictEqual(err.message, 'No method named "noMethod"')
    }
  })

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

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

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

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

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

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

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

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

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

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

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

    $('<div id="modal-test"/>')
XhmikosR's avatar
XhmikosR committed
132
      .on('shown.bs.modal', function () {
fat's avatar
fat committed
133
134
        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
135
        $(this).bootstrapModal('toggle')
XhmikosR's avatar
XhmikosR committed
136
137
      })
      .on('hidden.bs.modal', function () {
fat's avatar
fat committed
138
        assert.ok(!$('#modal-test').is(':visible'), 'modal hidden')
139
        done()
XhmikosR's avatar
XhmikosR committed
140
      })
141
      .bootstrapModal('toggle')
XhmikosR's avatar
XhmikosR committed
142
143
  })

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

    $('<div id="modal-test"><span class="close" data-dismiss="modal"/></div>')
XhmikosR's avatar
XhmikosR committed
149
      .on('shown.bs.modal', function () {
fat's avatar
fat committed
150
151
152
        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
153
154
      })
      .on('hidden.bs.modal', function () {
fat's avatar
fat committed
155
        assert.ok(!$('#modal-test').is(':visible'), 'modal hidden')
156
        done()
XhmikosR's avatar
XhmikosR committed
157
      })
158
      .bootstrapModal('toggle')
XhmikosR's avatar
XhmikosR committed
159
160
  })

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

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

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

    $('<div id="modal-test"><div class="contents"/></div>')
182
      .on('shown.bs.modal', function () {
fat's avatar
fat committed
183
184
185
186
        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
187
      })
188
      .on('hidden.bs.modal', function () {
fat's avatar
fat committed
189
        assert.ok(!$('#modal-test').is(':visible'), 'modal hidden')
190
        done()
XhmikosR's avatar
XhmikosR committed
191
      })
192
      .bootstrapModal('show')
XhmikosR's avatar
XhmikosR committed
193
194
  })

195
196
197
198
199
200
201
202
203
204
205
206
207
  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
208
209
  QUnit.test('should close modal when escape key is pressed via keydown', function (assert) {
    assert.expect(3)
210
    var done = assert.async()
211

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

        setTimeout(function () {
fat's avatar
fat committed
222
223
          assert.ok(!$('#modal-test').is(':visible'), 'modal hidden')
          $div.remove()
224
          done()
225
226
227
228
229
        }, 0)
      })
      .bootstrapModal('show')
  })

fat's avatar
fat committed
230
231
  QUnit.test('should not close modal when escape key is pressed via keyup', function (assert) {
    assert.expect(3)
232
    var done = assert.async()
233

fat's avatar
fat committed
234
235
    var $div = $('<div id="modal-test"/>')
    $div
236
      .on('shown.bs.modal', function () {
fat's avatar
fat committed
237
238
        assert.ok($('#modal-test').length, 'modal inserted into dom')
        assert.ok($('#modal-test').is(':visible'), 'modal visible')
XhmikosR's avatar
XhmikosR committed
239
240
241
        $div.trigger($.Event('keyup', {
          which: 27
        }))
242
243

        setTimeout(function () {
fat's avatar
fat committed
244
245
          assert.ok($div.is(':visible'), 'modal still visible')
          $div.remove()
246
          done()
247
248
249
250
251
        }, 0)
      })
      .bootstrapModal('show')
  })

fat's avatar
fat committed
252
253
  QUnit.test('should trigger hide event once when clicking outside of modal-content', function (assert) {
    assert.expect(1)
254
    var done = assert.async()
XhmikosR's avatar
XhmikosR committed
255
256
257

    var triggered

Heinrich Fenkart's avatar
Heinrich Fenkart committed
258
    $('<div id="modal-test"><div class="contents"/></div>')
259
      .on('shown.bs.modal', function () {
XhmikosR's avatar
XhmikosR committed
260
        triggered = 0
fat's avatar
fat committed
261
        $('#modal-test').trigger('click')
XhmikosR's avatar
XhmikosR committed
262
      })
263
      .on('hide.bs.modal', function () {
XhmikosR's avatar
XhmikosR committed
264
        triggered += 1
fat's avatar
fat committed
265
        assert.strictEqual(triggered, 1, 'modal hide triggered once')
266
        done()
XhmikosR's avatar
XhmikosR committed
267
      })
268
      .bootstrapModal('show')
XhmikosR's avatar
XhmikosR committed
269
270
  })

271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
  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
288
289
  QUnit.test('should close reopened modal with [data-dismiss="modal"] click', function (assert) {
    assert.expect(2)
290
    var done = assert.async()
Heinrich Fenkart's avatar
Heinrich Fenkart committed
291
292

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

fat's avatar
fat committed
312
313
  QUnit.test('should restore focus to toggling element when modal is hidden after having been opened via data-api', function (assert) {
    assert.expect(1)
314
    var done = assert.async()
Heinrich Fenkart's avatar
Heinrich Fenkart committed
315
316
317
318

    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
319
      .on('hidden.bs.modal', function () {
Heinrich Fenkart's avatar
Heinrich Fenkart committed
320
        setTimeout(function () {
fat's avatar
fat committed
321
          assert.ok($(document.activeElement).is($toggleBtn), 'toggling element is once again focused')
322
          done()
Chris Rebert's avatar
Chris Rebert committed
323
324
325
        }, 0)
      })
      .on('shown.bs.modal', function () {
fat's avatar
fat committed
326
        $('#close').trigger('click')
Chris Rebert's avatar
Chris Rebert committed
327
328
      })
      .appendTo('#qunit-fixture')
Heinrich Fenkart's avatar
Heinrich Fenkart committed
329

fat's avatar
fat committed
330
    $toggleBtn.trigger('click')
Chris Rebert's avatar
Chris Rebert committed
331
332
  })

fat's avatar
fat committed
333
334
  QUnit.test('should not restore focus to toggling element if the associated show event gets prevented', function (assert) {
    assert.expect(1)
335
    var done = assert.async()
Heinrich Fenkart's avatar
Heinrich Fenkart committed
336
337
338
339
    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
340
341
      .one('show.bs.modal', function (e) {
        e.preventDefault()
fat's avatar
fat committed
342
        $otherBtn.trigger('focus')
Heinrich Fenkart's avatar
Heinrich Fenkart committed
343
344
345
        setTimeout($.proxy(function () {
          $(this).bootstrapModal('show')
        }, this), 0)
Chris Rebert's avatar
Chris Rebert committed
346
347
      })
      .on('hidden.bs.modal', function () {
Heinrich Fenkart's avatar
Heinrich Fenkart committed
348
        setTimeout(function () {
fat's avatar
fat committed
349
          assert.ok($(document.activeElement).is($otherBtn), 'focus returned to toggling element')
350
          done()
Chris Rebert's avatar
Chris Rebert committed
351
352
353
        }, 0)
      })
      .on('shown.bs.modal', function () {
fat's avatar
fat committed
354
        $('#close').trigger('click')
Chris Rebert's avatar
Chris Rebert committed
355
356
      })
      .appendTo('#qunit-fixture')
Heinrich Fenkart's avatar
Heinrich Fenkart committed
357

fat's avatar
fat committed
358
359
360
    $toggleBtn.trigger('click')
  })

David Bailey's avatar
David Bailey committed
361
362
363
364
365
366
367
368
369
370
371
372
373
374
  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')
  })

375
  QUnit.test('should adjust the inline body padding when opening and restore when closing', function (assert) {
fat's avatar
fat committed
376
377
378
    assert.expect(2)
    var done = assert.async()
    var $body = $(document.body)
379
    var originalPadding = $body.css('padding-right')
fat's avatar
fat committed
380
381
382

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

397
398
  QUnit.test('should store the original body padding in data-padding-right before showing', function (assert) {
    assert.expect(2)
fat's avatar
fat committed
399
400
    var done = assert.async()
    var $body = $(document.body)
401
402
    var originalPadding = '0px'
    $body.css('padding-right', originalPadding)
fat's avatar
fat committed
403
404
405

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

David Bailey's avatar
David Bailey committed
417
418
419
420
421
422
  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
423
    // Hide scrollbars to prevent the body overflowing
XhmikosR's avatar
XhmikosR committed
424
425
    $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
426

David Bailey's avatar
David Bailey committed
427
428
429
430
431
432
    $('<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')

XhmikosR's avatar
XhmikosR committed
433
        // Restore scrollbars
David Bailey's avatar
David Bailey committed
434
435
        $body.css('overflow', 'auto')
        $('html').css('padding-right', '16px')
David Bailey's avatar
David Bailey committed
436
437
438
439
440
        done()
      })
      .bootstrapModal('show')
  })

441
  QUnit.test('should adjust the inline padding of fixed elements when opening and restore when closing', function (assert) {
442
443
    assert.expect(2)
    var done = assert.async()
444
445
    var $element = $('<div class="fixed-top"></div>').appendTo('#qunit-fixture')
    var originalPadding = $element.css('padding-right')
446
447

    $('<div id="modal-test"/>')
448
449
450
451
452
453
      .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()
      })
454
      .on('shown.bs.modal', function () {
455
        var expectedPadding = parseFloat(originalPadding) + $(this).getScrollbarWidth() + 'px'
456
        var currentPadding = $element.css('padding-right')
457
        assert.strictEqual(currentPadding, expectedPadding, 'fixed element padding should be adjusted while opening')
458
459
460
461
462
463
464
465
466
467
468
469
470
471
        $(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
472
        assert.strictEqual(typeof $element.data('padding-right'), 'undefined', 'data-padding-right should be cleared after closing')
473
        $element.remove()
474
475
        done()
      })
476
477
478
479
      .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')
      })
480
481
482
      .bootstrapModal('show')
  })

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
518
519
  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')
520
521
        $(this).bootstrapModal('hide')
      })
522
523
524
525
526
527
528
529
530
531
      .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"/>')
532
      .on('hidden.bs.modal', function () {
David Bailey's avatar
David Bailey committed
533
        assert.strictEqual($body.attr('style').indexOf('padding-right'), -1, 'body does not have inline padding set')
534
        $style.remove()
535
536
        done()
      })
537
538
539
      .on('shown.bs.modal', function () {
        $(this).bootstrapModal('hide')
      })
540
541
542
      .bootstrapModal('show')
  })

fat's avatar
fat committed
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
  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
582
  })
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604

  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')
  })
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640

  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
641
642
643
644
645
646
647
648
649
650
651

  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()
XhmikosR's avatar
XhmikosR committed
652
653
654
655
    })
      .bootstrapModal('show')
      .bootstrapModal('show')
      .bootstrapModal('hide')
lucascono's avatar
lucascono committed
656
  })
657
})