modal.js 25.4 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
      $('.modal-backdrop, #modal-test').remove()
      $(document.body).removeClass('modal-open')
34
35
36
37
38
      $.fn.modal = $.fn.bootstrapModal
      delete $.fn.bootstrapModal
    }
  })

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    var triggered

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

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

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

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

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

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

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

fat's avatar
fat committed
356
357
358
    $toggleBtn.trigger('click')
  })

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

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

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

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

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

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

David Bailey's avatar
David Bailey committed
425
426
427
428
429
430
    $('<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
431
        // Restore scrollbars
David Bailey's avatar
David Bailey committed
432
433
        $body.css('overflow', 'auto')
        $('html').css('padding-right', '16px')
David Bailey's avatar
David Bailey committed
434
435
436
437
438
        done()
      })
      .bootstrapModal('show')
  })

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

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

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
518
519
520
521
522
  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')
  })

523
524
525
526
527
528
529
530
531
532
533
534
535
536
  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 () {
537
        var expectedMargin = parseFloat(originalMargin) + $(this).getScrollbarWidth() + 'px'
538
        var currentMargin = $element.css('margin-right')
539
        assert.strictEqual(currentMargin, expectedMargin, 'navbar-toggler margin should be adjusted while opening')
540
541
542
543
544
545
546
        $(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)
547
    var done = assert.async()
548
549
550
    var $element = $('<div class="navbar-toggler"></div>').appendTo('#qunit-fixture')
    var originalMargin = '0px'
    $element.css('margin-right', originalMargin)
551
552

    $('<div id="modal-test"/>')
553
      .on('hidden.bs.modal', function () {
XhmikosR's avatar
XhmikosR committed
554
        assert.strictEqual(typeof $element.data('margin-right'), 'undefined', 'data-margin-right should be cleared after closing')
555
556
557
        $element.remove()
        done()
      })
558
      .on('shown.bs.modal', function () {
559
        assert.strictEqual($element.data('margin-right'), originalMargin, 'original navbar-toggler margin should be stored in data-margin-right')
560
561
        $(this).bootstrapModal('hide')
      })
562
563
564
565
566
567
568
569
570
571
      .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"/>')
572
      .on('hidden.bs.modal', function () {
David Bailey's avatar
David Bailey committed
573
        assert.strictEqual($body.attr('style').indexOf('padding-right'), -1, 'body does not have inline padding set')
574
        $style.remove()
575
576
        done()
      })
577
578
579
      .on('shown.bs.modal', function () {
        $(this).bootstrapModal('hide')
      })
580
581
582
      .bootstrapModal('show')
  })

fat's avatar
fat committed
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
617
618
619
620
621
  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
622
  })
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644

  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')
  })
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
676
677
678
679
680

  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
681
682
683
684
685
686
687
688
689
690
691

  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
692
693
694
695
    })
      .bootstrapModal('show')
      .bootstrapModal('show')
      .bootstrapModal('hide')
lucascono's avatar
lucascono committed
696
  })
697
})