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

XhmikosR's avatar
XhmikosR committed
4
5
  window.Util = typeof bootstrap === 'undefined' ? Util : bootstrap.Util
  var Modal = typeof window.bootstrap === 'undefined' ? window.Modal : window.bootstrap.Modal
6

fat's avatar
fat committed
7
  QUnit.module('modal plugin')
XhmikosR's avatar
XhmikosR committed
8

fat's avatar
fat committed
9
10
11
  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
12
13
  })

fat's avatar
fat committed
14
  QUnit.module('modal', {
15
16
17
    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')
18
      // Function to calculate the scrollbar width which is then compared to the padding or margin changes
19
      $.fn.getScrollbarWidth = $.fn.modal.Constructor.prototype._getScrollbarWidth
20
21

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

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
  QUnit.test('should throw explicit error on undefined method', function (assert) {
    assert.expect(1)
44
    var $el = $('<div id="modal-test"><div class="modal-dialog" /></div>')
45
46
47
    $el.bootstrapModal()
    try {
      $el.bootstrapModal('noMethod')
XhmikosR's avatar
XhmikosR committed
48
49
    } catch (error) {
      assert.strictEqual(error.message, 'No method named "noMethod"')
50
51
52
    }
  })

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

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

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

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

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

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

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

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

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

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

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

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

fat's avatar
fat committed
140
141
  QUnit.test('should remove from dom when click [data-dismiss="modal"]', function (assert) {
    assert.expect(3)
142
    var done = assert.async()
143
    $('<div id="modal-test"><div class="modal-dialog" /><span class="close" data-dismiss="modal"/></div>')
XhmikosR's avatar
XhmikosR committed
144
      .on('shown.bs.modal', function () {
fat's avatar
fat committed
145
146
147
        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
148
149
      })
      .on('hidden.bs.modal', function () {
fat's avatar
fat committed
150
        assert.ok(!$('#modal-test').is(':visible'), 'modal hidden')
151
        done()
XhmikosR's avatar
XhmikosR committed
152
      })
153
      .bootstrapModal('toggle')
XhmikosR's avatar
XhmikosR committed
154
155
  })

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

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

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

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

190
191
192
193
  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()

194
    $('<div id="modal-test" data-backdrop="false"><div class="modal-dialog" /><div class="contents"/></div>')
195
196
197
198
199
200
201
202
      .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
203
204
  QUnit.test('should close modal when escape key is pressed via keydown', function (assert) {
    assert.expect(3)
205
    var done = assert.async()
206

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

        var evt = document.createEvent('HTMLEvents')
        evt.initEvent('keydown', true, true)
        evt.which = 27

        $div[0].dispatchEvent(evt)
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

232
    var $div = $('<div id="modal-test"><div class="modal-dialog" /></div>')
fat's avatar
fat committed
233
    $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

256
    $('<div id="modal-test"><div class="modal-dialog" /><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
  QUnit.test('should remove aria-hidden attribute when shown, add it back when hidden', function (assert) {
    assert.expect(3)
    var done = assert.async()

273
    $('<div id="modal-test" aria-hidden="true"><div class="modal-dialog" /></div>')
274
275
276
277
278
279
280
281
      .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()
282
283
284
285
286
287
288
289
      })
      .bootstrapModal('show')
  })

  QUnit.test('should add aria-modal attribute when shown, remove it again when hidden', function (assert) {
    assert.expect(3)
    var done = assert.async()

290
    $('<div id="modal-test"><div class="modal-dialog" /></div>')
291
292
293
294
295
296
297
298
      .on('shown.bs.modal', function () {
        assert.ok($('#modal-test').is('[aria-modal]'), 'aria-modal attribute added')
        assert.strictEqual($('#modal-test').attr('aria-modal'), 'true', 'correct aria-modal="true" added')
        $(this).bootstrapModal('hide')
      })
      .on('hidden.bs.modal', function () {
        assert.notOk($('#modal-test').is('[aria-modal]'), 'aria-modal attribute removed')
        done()
299
300
301
302
      })
      .bootstrapModal('show')
  })

fat's avatar
fat committed
303
304
  QUnit.test('should close reopened modal with [data-dismiss="modal"] click', function (assert) {
    assert.expect(2)
305
    var done = assert.async()
306
307
308
309
310
311
312
313
314
    var html = [
      '<div id="modal-test">',
      '  <div class="modal-dialog">',
      '    <div class="contents"><div id="close" data-dismiss="modal"/></div>',
      '  </div>',
      '</div>'
    ].join('')

    $(html)
315
      .one('shown.bs.modal', function () {
fat's avatar
fat committed
316
        $('#close').trigger('click')
XhmikosR's avatar
XhmikosR committed
317
318
      })
      .one('hidden.bs.modal', function () {
XhmikosR's avatar
XhmikosR committed
319
        // After one open-close cycle
fat's avatar
fat committed
320
        assert.ok(!$('#modal-test').is(':visible'), 'modal hidden')
321
322
323
324
325
326
327
328
329
330
331
332
333

        var $this = $(this)
        setTimeout(function () {
          $this
            .one('shown.bs.modal', function () {
              $('#close').trigger('click')
            })
            .one('hidden.bs.modal', function () {
              assert.ok(!$('#modal-test').is(':visible'), 'modal hidden')
              done()
            })
            .bootstrapModal('show')
        }, 0)
XhmikosR's avatar
XhmikosR committed
334
      })
335
      .bootstrapModal('show')
XhmikosR's avatar
XhmikosR committed
336
  })
Chris Rebert's avatar
Chris Rebert committed
337

fat's avatar
fat committed
338
339
  QUnit.test('should restore focus to toggling element when modal is hidden after having been opened via data-api', function (assert) {
    assert.expect(1)
340
    var done = assert.async()
Heinrich Fenkart's avatar
Heinrich Fenkart committed
341
    var $toggleBtn = $('<button data-toggle="modal" data-target="#modal-test"/>').appendTo('#qunit-fixture')
342
343
344
345
346
347
348
349
350
    var html = [
      '<div id="modal-test">',
      '  <div class="modal-dialog">',
      '    <div class="contents"><div id="close" data-dismiss="modal"/></div>',
      '  </div>',
      '</div>'
    ].join('')

    $(html)
Chris Rebert's avatar
Chris Rebert committed
351
      .on('hidden.bs.modal', function () {
Heinrich Fenkart's avatar
Heinrich Fenkart committed
352
        setTimeout(function () {
fat's avatar
fat committed
353
          assert.ok($(document.activeElement).is($toggleBtn), 'toggling element is once again focused')
354
          done()
Chris Rebert's avatar
Chris Rebert committed
355
356
357
        }, 0)
      })
      .on('shown.bs.modal', function () {
fat's avatar
fat committed
358
        $('#close').trigger('click')
Chris Rebert's avatar
Chris Rebert committed
359
360
      })
      .appendTo('#qunit-fixture')
Heinrich Fenkart's avatar
Heinrich Fenkart committed
361

fat's avatar
fat committed
362
    $toggleBtn.trigger('click')
Chris Rebert's avatar
Chris Rebert committed
363
364
  })

fat's avatar
fat committed
365
366
  QUnit.test('should not restore focus to toggling element if the associated show event gets prevented', function (assert) {
    assert.expect(1)
367
    var done = assert.async()
Heinrich Fenkart's avatar
Heinrich Fenkart committed
368
369
    var $toggleBtn = $('<button data-toggle="modal" data-target="#modal-test"/>').appendTo('#qunit-fixture')
    var $otherBtn = $('<button id="other-btn"/>').appendTo('#qunit-fixture')
370
371
372
373
374
375
376
377
378
    var html = [
      '<div id="modal-test">',
      '  <div class="modal-dialog">',
      '    <div class="contents"><div id="close" data-dismiss="modal"/></div>',
      '  </div>',
      '</div>'
    ].join('')

    $(html)
Chris Rebert's avatar
Chris Rebert committed
379
380
      .one('show.bs.modal', function (e) {
        e.preventDefault()
fat's avatar
fat committed
381
        $otherBtn.trigger('focus')
Heinrich Fenkart's avatar
Heinrich Fenkart committed
382
383
384
        setTimeout($.proxy(function () {
          $(this).bootstrapModal('show')
        }, this), 0)
Chris Rebert's avatar
Chris Rebert committed
385
386
      })
      .on('hidden.bs.modal', function () {
Heinrich Fenkart's avatar
Heinrich Fenkart committed
387
        setTimeout(function () {
fat's avatar
fat committed
388
          assert.ok($(document.activeElement).is($otherBtn), 'focus returned to toggling element')
389
          done()
Chris Rebert's avatar
Chris Rebert committed
390
391
392
        }, 0)
      })
      .on('shown.bs.modal', function () {
fat's avatar
fat committed
393
        $('#close').trigger('click')
Chris Rebert's avatar
Chris Rebert committed
394
395
      })
      .appendTo('#qunit-fixture')
Heinrich Fenkart's avatar
Heinrich Fenkart committed
396

fat's avatar
fat committed
397
398
399
    $toggleBtn.trigger('click')
  })

David Bailey's avatar
David Bailey committed
400
401
402
403
  QUnit.test('should adjust the inline padding of the modal when opening', function (assert) {
    assert.expect(1)
    var done = assert.async()

404
    $('<div id="modal-test"><div class="modal-dialog" /></div>')
David Bailey's avatar
David Bailey committed
405
      .on('shown.bs.modal', function () {
406
407
        var expectedPadding = parseInt($(this).getScrollbarWidth(), 10)
        var currentPadding = parseInt($(this).css('padding-right'), 10)
David Bailey's avatar
David Bailey committed
408
409
410
411
412
413
        assert.strictEqual(currentPadding, expectedPadding, 'modal padding should be adjusted while opening')
        done()
      })
      .bootstrapModal('show')
  })

414
  QUnit.test('should adjust the inline body padding when opening and restore when closing', function (assert) {
fat's avatar
fat committed
415
416
417
    assert.expect(2)
    var done = assert.async()
    var $body = $(document.body)
418
    var originalPadding = parseInt($body.css('padding-right'), 10)
fat's avatar
fat committed
419

420
    $('<div id="modal-test"><div class="modal-dialog" /></div>')
fat's avatar
fat committed
421
      .on('hidden.bs.modal', function () {
422
        var currentPadding = parseInt($body.css('padding-right'), 10)
423
        assert.strictEqual(currentPadding, originalPadding, 'body padding should be reset after closing')
fat's avatar
fat committed
424
425
426
427
        $body.removeAttr('style')
        done()
      })
      .on('shown.bs.modal', function () {
428
429
        var expectedPadding = parseInt(originalPadding, 10) + parseInt($(this).getScrollbarWidth(), 10)
        var currentPadding = parseInt($body.css('padding-right'), 10)
430
        assert.strictEqual(currentPadding, expectedPadding, 'body padding should be adjusted while opening')
fat's avatar
fat committed
431
432
433
434
435
        $(this).bootstrapModal('hide')
      })
      .bootstrapModal('show')
  })

436
437
  QUnit.test('should store the original body padding in data-padding-right before showing', function (assert) {
    assert.expect(2)
fat's avatar
fat committed
438
439
    var done = assert.async()
    var $body = $(document.body)
440
441
    var originalPadding = '0px'
    $body.css('padding-right', originalPadding)
fat's avatar
fat committed
442

443
    $('<div id="modal-test"><div class="modal-dialog" /></div>')
fat's avatar
fat committed
444
      .on('hidden.bs.modal', function () {
445
        assert.strictEqual(document.body.getAttribute('data-padding-right'), null, 'data-padding-right should be cleared after closing')
446
        $body.removeAttr('style')
fat's avatar
fat committed
447
448
449
        done()
      })
      .on('shown.bs.modal', function () {
450
        assert.strictEqual($body.data('padding-right'), originalPadding, 'original body padding should be stored in data-padding-right')
fat's avatar
fat committed
451
452
453
454
455
        $(this).bootstrapModal('hide')
      })
      .bootstrapModal('show')
  })

David Bailey's avatar
David Bailey committed
456
457
458
459
460
461
  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
462
    // Hide scrollbars to prevent the body overflowing
XhmikosR's avatar
XhmikosR committed
463
464
    $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
465

466
    $('<div id="modal-test"><div class="modal-dialog" /></div>')
David Bailey's avatar
David Bailey committed
467
468
469
470
471
      .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
472
        // Restore scrollbars
David Bailey's avatar
David Bailey committed
473
474
        $body.css('overflow', 'auto')
        $('html').css('padding-right', '16px')
David Bailey's avatar
David Bailey committed
475
476
477
478
479
        done()
      })
      .bootstrapModal('show')
  })

480
  QUnit.test('should adjust the inline padding of fixed elements when opening and restore when closing', function (assert) {
481
482
    assert.expect(2)
    var done = assert.async()
483
    var $element = $('<div class="fixed-top"></div>').appendTo('#qunit-fixture')
484
    var originalPadding = parseInt($element.css('padding-right'), 10)
485

486
    $('<div id="modal-test"><div class="modal-dialog" /></div>')
487
      .on('hidden.bs.modal', function () {
488
        var currentPadding = parseInt($element.css('padding-right'), 10)
489
490
491
492
        assert.strictEqual(currentPadding, originalPadding, 'fixed element padding should be reset after closing')
        $element.remove()
        done()
      })
493
      .on('shown.bs.modal', function () {
494
495
        var expectedPadding = parseFloat(originalPadding) + parseInt($(this).getScrollbarWidth(), 10)
        var currentPadding = parseInt($element.css('padding-right'), 10)
496
        assert.strictEqual(currentPadding, expectedPadding, 'fixed element padding should be adjusted while opening')
497
498
499
500
501
502
503
504
505
506
507
508
        $(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)

509
    $('<div id="modal-test"><div class="modal-dialog" /></div>')
510
      .on('hidden.bs.modal', function () {
511
        assert.strictEqual($element[0].getAttribute('data-padding-right'), null, 'data-padding-right should be cleared after closing')
512
        $element.remove()
513
514
        done()
      })
515
516
517
518
      .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')
      })
519
520
521
      .bootstrapModal('show')
  })

522
523
524
525
  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')
526
    var originalPadding = parseInt($element.css('margin-right'), 10)
527

528
    $('<div id="modal-test"><div class="modal-dialog" /></div>')
529
      .on('hidden.bs.modal', function () {
530
        var currentPadding = parseInt($element.css('margin-right'), 10)
531
532
533
534
535
        assert.strictEqual(currentPadding, originalPadding, 'sticky element margin should be reset after closing')
        $element.remove()
        done()
      })
      .on('shown.bs.modal', function () {
536
537
        var expectedPadding = parseFloat(originalPadding) - $(this).getScrollbarWidth()
        var currentPadding = parseInt($element.css('margin-right'), 10)
538
539
540
541
542
543
544
545
546
547
548
549
550
        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)

551
    $('<div id="modal-test"><div class="modal-dialog" /></div>')
552
      .on('hidden.bs.modal', function () {
553
        assert.strictEqual($element[0].getAttribute('data-margin-right'), null, 'data-margin-right should be cleared after closing')
554
555
556
557
558
        $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')
559
560
        $(this).bootstrapModal('hide')
      })
561
562
563
564
565
566
567
568
569
      .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')

570
    $('<div id="modal-test"><div class="modal-dialog" /></div>')
571
      .on('hidden.bs.modal', function () {
Johann-S's avatar
Johann-S committed
572
        assert.strictEqual($body.css('padding-left'), '0px', 'body does not have inline padding set')
573
        $style.remove()
574
575
        done()
      })
576
577
578
      .on('shown.bs.modal', function () {
        $(this).bootstrapModal('hide')
      })
579
580
581
      .bootstrapModal('show')
  })

fat's avatar
fat committed
582
583
584
585
586
587
588
589
  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')

590
    $('<div id="modal-test"><div class="modal-dialog" /></div>')
fat's avatar
fat committed
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
      .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%')

611
    $('<div id="modal-test"><div class="modal-dialog" /></div>')
fat's avatar
fat committed
612
613
614
615
616
617
618
619
620
      .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
621
  })
622
623
624
625
626
627
628

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

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

629
630
631
632
633
634
635
636
637
    var modalHtml = [
      '<div id="modal-test">',
      '  <div class="modal-dialog">',
      '    <div class="contents"><div id="close" data-dismiss="modal"/></div>',
      '  </div>',
      '</div>'
    ].join('')

    $(modalHtml)
638
639
      .appendTo('#qunit-fixture')

640
641
642
643
644
645
    // We need to use CustomEvent here to have a working preventDefault in IE tests.
    var evt = new CustomEvent('click', {
      bubbles: true,
      cancelable: true
    })

646
647
    $('#test')
      .on('click.bs.modal.data-api', function (event) {
648
        assert.notOk(event.defaultPrevented, 'navigating to href will happen')
649
      })
650
651
652

    $('#test')[0].dispatchEvent(evt)
    assert.ok(evt.defaultPrevented, 'model shown instead of navigating to href')
653
  })
654

lucascono's avatar
lucascono committed
655
656
657
658
659
  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

660
    $('<div id="modal-test"><div class="modal-dialog" /></div>').on('shown.bs.modal', function () {
lucascono's avatar
lucascono committed
661
662
663
664
      count++
    }).on('hidden.bs.modal', function () {
      assert.strictEqual(count, 1, 'show() runs only once')
      done()
XhmikosR's avatar
XhmikosR committed
665
666
667
668
    })
      .bootstrapModal('show')
      .bootstrapModal('show')
      .bootstrapModal('hide')
lucascono's avatar
lucascono committed
669
  })
670
671

  QUnit.test('transition duration should be the modal-dialog duration before triggering shown event', function (assert) {
672
    assert.expect(1)
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
    var done = assert.async()
    var style = [
      '<style>',
      '  .modal.fade .modal-dialog {',
      '    transition: -webkit-transform .3s ease-out;',
      '    transition: transform .3s ease-out;',
      '    transition: transform .3s ease-out,-webkit-transform .3s ease-out;',
      '    -webkit-transform: translate(0,-50px);',
      '    transform: translate(0,-50px);',
      '  }',
      '</style>'
    ].join('')

    var $style = $(style).appendTo('head')
    var modalHTML = [
      '<div class="modal fade" id="exampleModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">',
      '  <div class="modal-dialog" role="document">',
      '    <div class="modal-content">',
      '      <div class="modal-body">...</div>',
      '    </div>',
      '  </div>',
      '</div>'
    ].join('')

    var $modal = $(modalHTML).appendTo('#qunit-fixture')

    $modal.on('shown.bs.modal', function () {
      $style.remove()
701
      assert.ok(true)
702
703
704
705
      done()
    })
      .bootstrapModal('show')
  })
706
707

  QUnit.test('should dispose modal', function (assert) {
Johann-S's avatar
Johann-S committed
708
    assert.expect(2)
709
710
711
712
713
714
715
716
717
718
719
720
721
    var done = assert.async()

    var $modal = $([
      '<div id="modal-test">',
      '  <div class="modal-dialog">',
      '    <div class="modal-content">',
      '      <div class="modal-body" />',
      '    </div>',
      '  </div>',
      '</div>'
    ].join('')).appendTo('#qunit-fixture')

    $modal.on('shown.bs.modal', function () {
Johann-S's avatar
Johann-S committed
722
723
      var modal = Modal._getInstance($modal[0])
      var spy = sinon.spy($modal[0], 'removeEventListener')
724

Johann-S's avatar
Johann-S committed
725
      modal.dispose()
726

Johann-S's avatar
Johann-S committed
727
728
      assert.ok(!Modal._getInstance($modal[0]), 'modal data object was disposed')
      assert.ok(spy.called)
729
730
731
      done()
    }).bootstrapModal('show')
  })
732
733

  QUnit.test('should enforce focus', function (assert) {
Johann-S's avatar
Johann-S committed
734
735
736
737
738
739
740
741
    var isIE11 = Boolean(window.MSInputMethodContext) && Boolean(document.documentMode)

    if (isIE11) {
      assert.expect(1)
    } else {
      assert.expect(2)
    }

742
743
744
745
746
747
748
749
750
751
752
753
754
755
    var done = assert.async()

    var $modal = $([
      '<div id="modal-test" data-show="false">',
      '  <div class="modal-dialog">',
      '    <div class="modal-content">',
      '      <div class="modal-body" />',
      '    </div>',
      '  </div>',
      '</div>'
    ].join(''))
      .bootstrapModal()
      .appendTo('#qunit-fixture')

Johann-S's avatar
Johann-S committed
756
    var modal = Modal._getInstance($modal[0])
757
758
759
760
761
762
    var spy = sinon.spy(modal, '_enforceFocus')

    $modal.one('shown.bs.modal', function () {
      assert.ok(spy.called, '_enforceFocus called')
      var spyFocus = sinon.spy(modal._element, 'focus')

Johann-S's avatar
Johann-S committed
763
      function focusInListener() {
764
        assert.ok(spyFocus.called)
Johann-S's avatar
Johann-S committed
765
        document.removeEventListener('focusin', focusInListener)
766
        done()
Johann-S's avatar
Johann-S committed
767
      }
XhmikosR's avatar
XhmikosR committed
768

Johann-S's avatar
Johann-S committed
769
770
771
772
      if (isIE11) {
        done()
      } else {
        document.addEventListener('focusin', focusInListener)
Johann-S's avatar
Johann-S committed
773

Johann-S's avatar
Johann-S committed
774
775
776
777
        var focusInEvent = new Event('focusin')
        Object.defineProperty(focusInEvent, 'target', {
          value: $('#qunit-fixture')[0]
        })
778

Johann-S's avatar
Johann-S committed
779
780
        document.dispatchEvent(focusInEvent)
      }
781
782
783
    })
      .bootstrapModal('show')
  })
Shohei Yoshida's avatar
Shohei Yoshida committed
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802

  QUnit.test('should scroll to top of the modal body if the modal has .modal-dialog-scrollable class', function (assert) {
    assert.expect(2)
    var done = assert.async()

    var $modal = $([
      '<div id="modal-test">',
      '  <div class="modal-dialog modal-dialog-scrollable">',
      '    <div class="modal-content">',
      '      <div class="modal-body" style="height: 100px; overflow-y: auto;">',
      '        <div style="height: 200px" />',
      '      </div>',
      '    </div>',
      '  </div>',
      '</div>'
    ].join('')).appendTo('#qunit-fixture')

    var $modalBody = $('.modal-body')
    $modalBody.scrollTop(100)
Johann-S's avatar
Johann-S committed
803
    assert.ok($modalBody.scrollTop() > 95 && $modalBody.scrollTop() <= 100)
Shohei Yoshida's avatar
Shohei Yoshida committed
804
805
806
807
808
809
810

    $modal.on('shown.bs.modal', function () {
      assert.strictEqual($modalBody.scrollTop(), 0, 'modal body scrollTop should be 0 when opened')
      done()
    })
      .bootstrapModal('show')
  })
811
812
813
814
815

  QUnit.test('should return the version', function (assert) {
    assert.expect(1)
    assert.strictEqual(typeof Modal.VERSION, 'string')
  })
816
})