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
50
51
52
53
54
55
  QUnit.test('should throw explicit error on undefined method', function (assert) {
    assert.expect(1)
    var $el = $('<div id="modal-test"/>')
    $el.bootstrapModal()
    try {
      $el.bootstrapModal('noMethod')
    }
    catch (err) {
      assert.strictEqual(err.message, 'No method named "noMethod"')
    }
  })

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    var triggered

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

266
267
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()
      })
      .bootstrapModal('show')
  })

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

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

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

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

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

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

fat's avatar
fat committed
353
354
355
    $toggleBtn.trigger('click')
  })

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

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

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

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

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

David Bailey's avatar
David Bailey committed
412
413
414
415
416
417
  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
418
419
420
421
    // Hide scrollbars to prevent the body overflowing
    $body.css('overflow', 'hidden')        // real scrollbar (for in-browser testing)
    $('html').css('padding-right', '0px')  // simulated scrollbar (for PhantomJS)

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

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

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

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

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

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

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

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

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

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

fat's avatar
fat committed
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
  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
619
  })
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641

  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')
  })
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677

  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
678
679
680
681
682
683
684
685
686
687
688

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

    $('<div id="modal-test"/>').on('shown.bs.modal', function () {
      count++
    }).on('hidden.bs.modal', function () {
      assert.strictEqual(count, 1, 'show() runs only once')
      done()
XhmikosR's avatar
XhmikosR committed
689
690
691
692
    })
      .bootstrapModal('show')
      .bootstrapModal('show')
      .bootstrapModal('hide')
lucascono's avatar
lucascono committed
693
  })
694
})