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

4
  window.Util = typeof bootstrap !== 'undefined' ? bootstrap.Util : Util
Johann-S's avatar
Johann-S committed
5
  var Modal = typeof window.bootstrap !== 'undefined' ? window.bootstrap.Modal : window.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
44
45
46
47
  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
48
    } catch (err) {
49
50
51
52
      assert.strictEqual(err.message, 'No method named "noMethod"')
    }
  })

fat's avatar
fat committed
53
54
  QUnit.test('should return jquery collection containing the element', function (assert) {
    assert.expect(2)
Heinrich Fenkart's avatar
Heinrich Fenkart committed
55
56
    var $el = $('<div id="modal-test"/>')
    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"/>')
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"/>')
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"/>')
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

Heinrich Fenkart's avatar
Heinrich Fenkart committed
110
    $('<div id="modal-test"/>')
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"/>')
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()
Heinrich Fenkart's avatar
Heinrich Fenkart committed
143
144

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

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

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

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

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

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

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

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

        $div[0].dispatchEvent(evt)
219
220

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

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

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

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

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

    var triggered

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

270
271
272
273
274
275
276
277
278
279
280
281
282
  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()
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
      })
      .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()

    $('<div id="modal-test"/>')
      .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()
300
301
302
303
      })
      .bootstrapModal('show')
  })

fat's avatar
fat committed
304
305
  QUnit.test('should close reopened modal with [data-dismiss="modal"] click', function (assert) {
    assert.expect(2)
306
    var done = assert.async()
Heinrich Fenkart's avatar
Heinrich Fenkart committed
307
308

    $('<div id="modal-test"><div class="contents"><div id="close" data-dismiss="modal"/></div></div>')
309
      .one('shown.bs.modal', function () {
fat's avatar
fat committed
310
        $('#close').trigger('click')
XhmikosR's avatar
XhmikosR committed
311
312
      })
      .one('hidden.bs.modal', function () {
XhmikosR's avatar
XhmikosR committed
313
        // After one open-close cycle
fat's avatar
fat committed
314
        assert.ok(!$('#modal-test').is(':visible'), 'modal hidden')
315
316
317
318
319
320
321
322
323
324
325
326
327

        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
328
      })
329
      .bootstrapModal('show')
XhmikosR's avatar
XhmikosR committed
330
  })
Chris Rebert's avatar
Chris Rebert committed
331

fat's avatar
fat committed
332
333
  QUnit.test('should restore focus to toggling element when modal is hidden after having been opened via data-api', function (assert) {
    assert.expect(1)
334
    var done = assert.async()
Heinrich Fenkart's avatar
Heinrich Fenkart committed
335
336
337
338

    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
339
      .on('hidden.bs.modal', function () {
Heinrich Fenkart's avatar
Heinrich Fenkart committed
340
        setTimeout(function () {
fat's avatar
fat committed
341
          assert.ok($(document.activeElement).is($toggleBtn), 'toggling element is once again focused')
342
          done()
Chris Rebert's avatar
Chris Rebert committed
343
344
345
        }, 0)
      })
      .on('shown.bs.modal', function () {
fat's avatar
fat committed
346
        $('#close').trigger('click')
Chris Rebert's avatar
Chris Rebert committed
347
348
      })
      .appendTo('#qunit-fixture')
Heinrich Fenkart's avatar
Heinrich Fenkart committed
349

fat's avatar
fat committed
350
    $toggleBtn.trigger('click')
Chris Rebert's avatar
Chris Rebert committed
351
352
  })

fat's avatar
fat committed
353
354
  QUnit.test('should not restore focus to toggling element if the associated show event gets prevented', function (assert) {
    assert.expect(1)
355
    var done = assert.async()
Heinrich Fenkart's avatar
Heinrich Fenkart committed
356
357
358
359
    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
360
361
      .one('show.bs.modal', function (e) {
        e.preventDefault()
fat's avatar
fat committed
362
        $otherBtn.trigger('focus')
Heinrich Fenkart's avatar
Heinrich Fenkart committed
363
364
365
        setTimeout($.proxy(function () {
          $(this).bootstrapModal('show')
        }, this), 0)
Chris Rebert's avatar
Chris Rebert committed
366
367
      })
      .on('hidden.bs.modal', function () {
Heinrich Fenkart's avatar
Heinrich Fenkart committed
368
        setTimeout(function () {
fat's avatar
fat committed
369
          assert.ok($(document.activeElement).is($otherBtn), 'focus returned to toggling element')
370
          done()
Chris Rebert's avatar
Chris Rebert committed
371
372
373
        }, 0)
      })
      .on('shown.bs.modal', function () {
fat's avatar
fat committed
374
        $('#close').trigger('click')
Chris Rebert's avatar
Chris Rebert committed
375
376
      })
      .appendTo('#qunit-fixture')
Heinrich Fenkart's avatar
Heinrich Fenkart committed
377

fat's avatar
fat committed
378
379
380
    $toggleBtn.trigger('click')
  })

David Bailey's avatar
David Bailey committed
381
382
383
384
385
386
387
388
389
390
391
392
393
394
  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')
  })

395
  QUnit.test('should adjust the inline body padding when opening and restore when closing', function (assert) {
fat's avatar
fat committed
396
397
398
    assert.expect(2)
    var done = assert.async()
    var $body = $(document.body)
399
    var originalPadding = $body.css('padding-right')
fat's avatar
fat committed
400
401
402

    $('<div id="modal-test"/>')
      .on('hidden.bs.modal', function () {
403
404
        var currentPadding = $body.css('padding-right')
        assert.strictEqual(currentPadding, originalPadding, 'body padding should be reset after closing')
fat's avatar
fat committed
405
406
407
408
        $body.removeAttr('style')
        done()
      })
      .on('shown.bs.modal', function () {
409
        var expectedPadding = parseFloat(originalPadding) + $(this).getScrollbarWidth() + 'px'
410
        var currentPadding = $body.css('padding-right')
411
        assert.strictEqual(currentPadding, expectedPadding, 'body padding should be adjusted while opening')
fat's avatar
fat committed
412
413
414
415
416
        $(this).bootstrapModal('hide')
      })
      .bootstrapModal('show')
  })

417
418
  QUnit.test('should store the original body padding in data-padding-right before showing', function (assert) {
    assert.expect(2)
fat's avatar
fat committed
419
420
    var done = assert.async()
    var $body = $(document.body)
421
422
    var originalPadding = '0px'
    $body.css('padding-right', originalPadding)
fat's avatar
fat committed
423
424
425

    $('<div id="modal-test"/>')
      .on('hidden.bs.modal', function () {
426
        assert.strictEqual(document.body.getAttribute('data-padding-right'), null, 'data-padding-right should be cleared after closing')
427
        $body.removeAttr('style')
fat's avatar
fat committed
428
429
430
        done()
      })
      .on('shown.bs.modal', function () {
431
        assert.strictEqual($body.data('padding-right'), originalPadding, 'original body padding should be stored in data-padding-right')
fat's avatar
fat committed
432
433
434
435
436
        $(this).bootstrapModal('hide')
      })
      .bootstrapModal('show')
  })

David Bailey's avatar
David Bailey committed
437
438
439
440
441
442
  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
443
    // Hide scrollbars to prevent the body overflowing
XhmikosR's avatar
XhmikosR committed
444
445
    $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
446

David Bailey's avatar
David Bailey committed
447
448
449
450
451
452
    $('<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
453
        // Restore scrollbars
David Bailey's avatar
David Bailey committed
454
455
        $body.css('overflow', 'auto')
        $('html').css('padding-right', '16px')
David Bailey's avatar
David Bailey committed
456
457
458
459
460
        done()
      })
      .bootstrapModal('show')
  })

461
  QUnit.test('should adjust the inline padding of fixed elements when opening and restore when closing', function (assert) {
462
463
    assert.expect(2)
    var done = assert.async()
464
465
    var $element = $('<div class="fixed-top"></div>').appendTo('#qunit-fixture')
    var originalPadding = $element.css('padding-right')
466
467

    $('<div id="modal-test"/>')
468
469
470
471
472
473
      .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()
      })
474
      .on('shown.bs.modal', function () {
475
        var expectedPadding = parseFloat(originalPadding) + $(this).getScrollbarWidth() + 'px'
476
        var currentPadding = $element.css('padding-right')
477
        assert.strictEqual(currentPadding, expectedPadding, 'fixed element padding should be adjusted while opening')
478
479
480
481
482
483
484
485
486
487
488
489
490
491
        $(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 () {
492
        assert.strictEqual($element[0].getAttribute('data-padding-right'), null, 'data-padding-right should be cleared after closing')
493
        $element.remove()
494
495
        done()
      })
496
497
498
499
      .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')
      })
500
501
502
      .bootstrapModal('show')
  })

503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
  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 () {
534
        assert.strictEqual($element[0].getAttribute('data-margin-right'), null, 'data-margin-right should be cleared after closing')
535
536
537
538
539
        $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')
540
541
        $(this).bootstrapModal('hide')
      })
542
543
544
545
546
547
548
549
550
551
      .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"/>')
552
      .on('hidden.bs.modal', function () {
Johann-S's avatar
Johann-S committed
553
        assert.strictEqual($body.css('padding-left'), '0px', 'body does not have inline padding set')
554
        $style.remove()
555
556
        done()
      })
557
558
559
      .on('shown.bs.modal', function () {
        $(this).bootstrapModal('hide')
      })
560
561
562
      .bootstrapModal('show')
  })

fat's avatar
fat committed
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
  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
602
  })
603
604
605
606
607
608
609
610
611
612

  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')

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

613
614
615
616
617
618
    // We need to use CustomEvent here to have a working preventDefault in IE tests.
    var evt = new CustomEvent('click', {
      bubbles: true,
      cancelable: true
    })

619
620
    $('#test')
      .on('click.bs.modal.data-api', function (event) {
621
        assert.notOk(event.defaultPrevented, 'navigating to href will happen')
622
      })
623
624
625

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

lucascono's avatar
lucascono committed
628
629
630
631
632
633
634
635
636
637
  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
638
639
640
641
    })
      .bootstrapModal('show')
      .bootstrapModal('show')
      .bootstrapModal('hide')
lucascono's avatar
lucascono committed
642
  })
643
644

  QUnit.test('transition duration should be the modal-dialog duration before triggering shown event', function (assert) {
645
    assert.expect(1)
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
    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')
671
672
    var expectedTransitionDuration = 300
    var spy = sinon.spy(Util, 'getTransitionDurationFromElement')
673
674

    $modal.on('shown.bs.modal', function () {
675
      assert.ok(spy.returned(expectedTransitionDuration))
676
      $style.remove()
677
      spy.restore()
678
679
680
681
      done()
    })
      .bootstrapModal('show')
  })
682
683

  QUnit.test('should dispose modal', function (assert) {
Johann-S's avatar
Johann-S committed
684
    assert.expect(2)
685
686
687
688
689
690
691
692
693
694
695
696
697
    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
698
699
      var modal = Modal._getInstance($modal[0])
      var spy = sinon.spy($modal[0], 'removeEventListener')
700

Johann-S's avatar
Johann-S committed
701
      modal.dispose()
702

Johann-S's avatar
Johann-S committed
703
704
      assert.ok(!Modal._getInstance($modal[0]), 'modal data object was disposed')
      assert.ok(spy.called)
705
706
707
      done()
    }).bootstrapModal('show')
  })
708
709

  QUnit.test('should enforce focus', function (assert) {
Johann-S's avatar
Johann-S committed
710
    assert.expect(2)
711
712
713
714
715
716
717
718
719
720
721
722
723
724
    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
725
    var modal = Modal._getInstance($modal[0])
726
727
728
729
730
731
    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
732
      function focusInListener() {
733
        assert.ok(spyFocus.called)
Johann-S's avatar
Johann-S committed
734
        document.removeEventListener('focusin', focusInListener)
735
        done()
Johann-S's avatar
Johann-S committed
736
737
738
739
740
741
      }
      document.addEventListener('focusin', focusInListener)

      var focusInEvent = new Event('focusin')
      Object.defineProperty(focusInEvent, 'target', {
        value: $('#qunit-fixture')[0]
742
743
      })

Johann-S's avatar
Johann-S committed
744
      document.dispatchEvent(focusInEvent)
745
746
747
    })
      .bootstrapModal('show')
  })
Shohei Yoshida's avatar
Shohei Yoshida committed
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774

  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)
    assert.strictEqual($modalBody.scrollTop(), 100)

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