mirror of
https://github.com/twbs/bootstrap.git
synced 2024-11-29 11:24:18 +01:00
commit
d5a2b27e16
@ -35,8 +35,8 @@ $(function () {
|
||||
ok(!$affix.hasClass('affix'), 'affix class was not added')
|
||||
})
|
||||
|
||||
test('should trigger affixed event after affix', function () {
|
||||
stop()
|
||||
test('should trigger affixed event after affix', function (assert) {
|
||||
var done = assert.async()
|
||||
|
||||
var templateHTML = '<div id="affixTarget">'
|
||||
+ '<ul>'
|
||||
@ -57,7 +57,7 @@ $(function () {
|
||||
}).on('affixed.bs.affix', function () {
|
||||
ok(true, 'affixed event fired')
|
||||
$('#affixTarget, #affixAfter').remove()
|
||||
start()
|
||||
done()
|
||||
})
|
||||
|
||||
setTimeout(function () {
|
||||
@ -69,8 +69,8 @@ $(function () {
|
||||
}, 0)
|
||||
})
|
||||
|
||||
test('should affix-top when scrolling up to offset when parent has padding', function () {
|
||||
stop()
|
||||
test('should affix-top when scrolling up to offset when parent has padding', function (assert) {
|
||||
var done = assert.async()
|
||||
|
||||
var templateHTML = '<div id="padding-offset" style="padding-top: 20px;">'
|
||||
+ '<div id="affixTopTarget">'
|
||||
@ -87,7 +87,7 @@ $(function () {
|
||||
.on('affixed-top.bs.affix', function () {
|
||||
ok($('#affixTopTarget').hasClass('affix-top'), 'affix-top class applied')
|
||||
$('#padding-offset').remove()
|
||||
start()
|
||||
done()
|
||||
})
|
||||
|
||||
setTimeout(function () {
|
||||
|
@ -55,13 +55,13 @@ $(function () {
|
||||
equal($('#qunit-fixture').find('.alert').length, 0, 'element removed from dom')
|
||||
})
|
||||
|
||||
test('should not fire closed when close is prevented', function () {
|
||||
stop()
|
||||
test('should not fire closed when close is prevented', function (assert) {
|
||||
var done = assert.async()
|
||||
$('<div class="alert"/>')
|
||||
.on('close.bs.alert', function (e) {
|
||||
e.preventDefault()
|
||||
ok(true, 'close event fired')
|
||||
start()
|
||||
done()
|
||||
})
|
||||
.on('closed.bs.alert', function () {
|
||||
ok(false, 'closed event fired')
|
||||
|
@ -29,57 +29,57 @@ $(function () {
|
||||
strictEqual($button[0], $el[0], 'collection contains element')
|
||||
})
|
||||
|
||||
test('should return set state to loading', function () {
|
||||
test('should return set state to loading', function (assert) {
|
||||
var $btn = $('<button class="btn" data-loading-text="fat">mdo</button>')
|
||||
equal($btn.html(), 'mdo', 'btn text equals mdo')
|
||||
$btn.bootstrapButton('loading')
|
||||
stop()
|
||||
var done = assert.async()
|
||||
setTimeout(function () {
|
||||
equal($btn.html(), 'fat', 'btn text equals fat')
|
||||
ok($btn[0].hasAttribute('disabled'), 'btn is disabled')
|
||||
ok($btn.hasClass('disabled'), 'btn has disabled class')
|
||||
start()
|
||||
done()
|
||||
}, 0)
|
||||
})
|
||||
|
||||
test('should return reset state', function () {
|
||||
test('should return reset state', function (assert) {
|
||||
var $btn = $('<button class="btn" data-loading-text="fat">mdo</button>')
|
||||
equal($btn.html(), 'mdo', 'btn text equals mdo')
|
||||
$btn.bootstrapButton('loading')
|
||||
stop()
|
||||
var doneOne = assert.async()
|
||||
setTimeout(function () {
|
||||
equal($btn.html(), 'fat', 'btn text equals fat')
|
||||
ok($btn[0].hasAttribute('disabled'), 'btn is disabled')
|
||||
ok($btn.hasClass('disabled'), 'btn has disabled class')
|
||||
start()
|
||||
stop()
|
||||
doneOne()
|
||||
var doneTwo = assert.async()
|
||||
$btn.bootstrapButton('reset')
|
||||
setTimeout(function () {
|
||||
equal($btn.html(), 'mdo', 'btn text equals mdo')
|
||||
ok(!$btn[0].hasAttribute('disabled'), 'btn is not disabled')
|
||||
ok(!$btn.hasClass('disabled'), 'btn does not have disabled class')
|
||||
start()
|
||||
doneTwo()
|
||||
}, 0)
|
||||
}, 0)
|
||||
})
|
||||
|
||||
test('should work with an empty string as reset state', function () {
|
||||
test('should work with an empty string as reset state', function (assert) {
|
||||
var $btn = $('<button class="btn" data-loading-text="fat"/>')
|
||||
equal($btn.html(), '', 'btn text equals ""')
|
||||
$btn.bootstrapButton('loading')
|
||||
stop()
|
||||
var doneOne = assert.async()
|
||||
setTimeout(function () {
|
||||
equal($btn.html(), 'fat', 'btn text equals fat')
|
||||
ok($btn[0].hasAttribute('disabled'), 'btn is disabled')
|
||||
ok($btn.hasClass('disabled'), 'btn has disabled class')
|
||||
start()
|
||||
stop()
|
||||
doneOne()
|
||||
var doneTwo = assert.async()
|
||||
$btn.bootstrapButton('reset')
|
||||
setTimeout(function () {
|
||||
equal($btn.html(), '', 'btn text equals ""')
|
||||
ok(!$btn[0].hasAttribute('disabled'), 'btn is not disabled')
|
||||
ok(!$btn.hasClass('disabled'), 'btn does not have disabled class')
|
||||
start()
|
||||
doneTwo()
|
||||
}, 0)
|
||||
}, 0)
|
||||
})
|
||||
|
@ -29,13 +29,13 @@ $(function () {
|
||||
strictEqual($carousel[0], $el[0], 'collection contains element')
|
||||
})
|
||||
|
||||
test('should not fire slid when slide is prevented', function () {
|
||||
stop()
|
||||
test('should not fire slid when slide is prevented', function (assert) {
|
||||
var done = assert.async()
|
||||
$('<div class="carousel"/>')
|
||||
.on('slide.bs.carousel', function (e) {
|
||||
e.preventDefault()
|
||||
ok(true, 'slide event fired')
|
||||
start()
|
||||
done()
|
||||
})
|
||||
.on('slid.bs.carousel', function () {
|
||||
ok(false, 'slid event fired')
|
||||
@ -43,7 +43,7 @@ $(function () {
|
||||
.bootstrapCarousel('next')
|
||||
})
|
||||
|
||||
test('should reset when slide is prevented', function () {
|
||||
test('should reset when slide is prevented', function (assert) {
|
||||
var carouselHTML = '<div id="carousel-example-generic" class="carousel slide">'
|
||||
+ '<ol class="carousel-indicators">'
|
||||
+ '<li data-target="#carousel-example-generic" data-slide-to="0" class="active"/>'
|
||||
@ -66,7 +66,7 @@ $(function () {
|
||||
+ '</div>'
|
||||
var $carousel = $(carouselHTML)
|
||||
|
||||
stop()
|
||||
var done = assert.async()
|
||||
$carousel
|
||||
.one('slide.bs.carousel', function (e) {
|
||||
e.preventDefault()
|
||||
@ -82,13 +82,13 @@ $(function () {
|
||||
ok(!$carousel.find('.carousel-indicators li:eq(0)').is('.active'), 'first indicator still active')
|
||||
ok($carousel.find('.item:eq(1)').is('.active'), 'second item active')
|
||||
ok($carousel.find('.carousel-indicators li:eq(1)').is('.active'), 'second indicator active')
|
||||
start()
|
||||
done()
|
||||
}, 0)
|
||||
})
|
||||
.bootstrapCarousel('next')
|
||||
})
|
||||
|
||||
test('should fire slide event with direction', function () {
|
||||
test('should fire slide event with direction', function (assert) {
|
||||
var carouselHTML = '<div id="myCarousel" class="carousel slide">'
|
||||
+ '<div class="carousel-inner">'
|
||||
+ '<div class="item active">'
|
||||
@ -124,7 +124,7 @@ $(function () {
|
||||
+ '</div>'
|
||||
var $carousel = $(carouselHTML)
|
||||
|
||||
stop()
|
||||
var done = assert.async()
|
||||
|
||||
$carousel
|
||||
.one('slide.bs.carousel', function (e) {
|
||||
@ -135,14 +135,14 @@ $(function () {
|
||||
.one('slide.bs.carousel', function (e) {
|
||||
ok(e.direction, 'direction present on prev')
|
||||
strictEqual(e.direction, 'right', 'direction is right on prev')
|
||||
start()
|
||||
done()
|
||||
})
|
||||
.bootstrapCarousel('prev')
|
||||
})
|
||||
.bootstrapCarousel('next')
|
||||
})
|
||||
|
||||
test('should fire slid event with direction', function () {
|
||||
test('should fire slid event with direction', function (assert) {
|
||||
var carouselHTML = '<div id="myCarousel" class="carousel slide">'
|
||||
+ '<div class="carousel-inner">'
|
||||
+ '<div class="item active">'
|
||||
@ -178,7 +178,7 @@ $(function () {
|
||||
+ '</div>'
|
||||
var $carousel = $(carouselHTML)
|
||||
|
||||
stop()
|
||||
var done = assert.async()
|
||||
|
||||
$carousel
|
||||
.one('slid.bs.carousel', function (e) {
|
||||
@ -189,14 +189,14 @@ $(function () {
|
||||
.one('slid.bs.carousel', function (e) {
|
||||
ok(e.direction, 'direction present on prev')
|
||||
strictEqual(e.direction, 'right', 'direction is right on prev')
|
||||
start()
|
||||
done()
|
||||
})
|
||||
.bootstrapCarousel('prev')
|
||||
})
|
||||
.bootstrapCarousel('next')
|
||||
})
|
||||
|
||||
test('should fire slide event with relatedTarget', function () {
|
||||
test('should fire slide event with relatedTarget', function (assert) {
|
||||
var template = '<div id="myCarousel" class="carousel slide">'
|
||||
+ '<div class="carousel-inner">'
|
||||
+ '<div class="item active">'
|
||||
@ -231,18 +231,18 @@ $(function () {
|
||||
+ '<a class="right carousel-control" href="#myCarousel" data-slide="next">›</a>'
|
||||
+ '</div>'
|
||||
|
||||
stop()
|
||||
var done = assert.async()
|
||||
|
||||
$(template)
|
||||
.on('slide.bs.carousel', function (e) {
|
||||
ok(e.relatedTarget, 'relatedTarget present')
|
||||
ok($(e.relatedTarget).hasClass('item'), 'relatedTarget has class "item"')
|
||||
start()
|
||||
done()
|
||||
})
|
||||
.bootstrapCarousel('next')
|
||||
})
|
||||
|
||||
test('should fire slid event with relatedTarget', function () {
|
||||
test('should fire slid event with relatedTarget', function (assert) {
|
||||
var template = '<div id="myCarousel" class="carousel slide">'
|
||||
+ '<div class="carousel-inner">'
|
||||
+ '<div class="item active">'
|
||||
@ -277,13 +277,13 @@ $(function () {
|
||||
+ '<a class="right carousel-control" href="#myCarousel" data-slide="next">›</a>'
|
||||
+ '</div>'
|
||||
|
||||
stop()
|
||||
var done = assert.async()
|
||||
|
||||
$(template)
|
||||
.on('slid.bs.carousel', function (e) {
|
||||
ok(e.relatedTarget, 'relatedTarget present')
|
||||
ok($(e.relatedTarget).hasClass('item'), 'relatedTarget has class "item"')
|
||||
start()
|
||||
done()
|
||||
})
|
||||
.bootstrapCarousel('next')
|
||||
})
|
||||
@ -542,7 +542,7 @@ $(function () {
|
||||
})
|
||||
})
|
||||
|
||||
test('should wrap around from end to start when wrap option is true', function () {
|
||||
test('should wrap around from end to start when wrap option is true', function (assert) {
|
||||
var carouselHTML = '<div id="carousel-example-generic" class="carousel slide" data-wrap="true">'
|
||||
+ '<ol class="carousel-indicators">'
|
||||
+ '<li data-target="#carousel-example-generic" data-slide-to="0" class="active"/>'
|
||||
@ -566,7 +566,7 @@ $(function () {
|
||||
var $carousel = $(carouselHTML)
|
||||
var getActiveId = function () { return $carousel.find('.item.active').attr('id') }
|
||||
|
||||
stop()
|
||||
var done = assert.async()
|
||||
|
||||
$carousel
|
||||
.one('slid.bs.carousel', function () {
|
||||
@ -577,7 +577,7 @@ $(function () {
|
||||
$carousel
|
||||
.one('slid.bs.carousel', function () {
|
||||
strictEqual(getActiveId(), 'one', 'carousel wrapped around and slid from 3rd to 1st slide')
|
||||
start()
|
||||
done()
|
||||
})
|
||||
.bootstrapCarousel('next')
|
||||
})
|
||||
@ -586,7 +586,7 @@ $(function () {
|
||||
.bootstrapCarousel('next')
|
||||
})
|
||||
|
||||
test('should wrap around from start to end when wrap option is true', function () {
|
||||
test('should wrap around from start to end when wrap option is true', function (assert) {
|
||||
var carouselHTML = '<div id="carousel-example-generic" class="carousel slide" data-wrap="true">'
|
||||
+ '<ol class="carousel-indicators">'
|
||||
+ '<li data-target="#carousel-example-generic" data-slide-to="0" class="active"/>'
|
||||
@ -609,17 +609,17 @@ $(function () {
|
||||
+ '</div>'
|
||||
var $carousel = $(carouselHTML)
|
||||
|
||||
stop()
|
||||
var done = assert.async()
|
||||
|
||||
$carousel
|
||||
.on('slid.bs.carousel', function () {
|
||||
strictEqual($carousel.find('.item.active').attr('id'), 'three', 'carousel wrapped around and slid from 1st to 3rd slide')
|
||||
start()
|
||||
done()
|
||||
})
|
||||
.bootstrapCarousel('prev')
|
||||
})
|
||||
|
||||
test('should stay at the end when the next method is called and wrap is false', function () {
|
||||
test('should stay at the end when the next method is called and wrap is false', function (assert) {
|
||||
var carouselHTML = '<div id="carousel-example-generic" class="carousel slide" data-wrap="false">'
|
||||
+ '<ol class="carousel-indicators">'
|
||||
+ '<li data-target="#carousel-example-generic" data-slide-to="0" class="active"/>'
|
||||
@ -643,7 +643,7 @@ $(function () {
|
||||
var $carousel = $(carouselHTML)
|
||||
var getActiveId = function () { return $carousel.find('.item.active').attr('id') }
|
||||
|
||||
stop()
|
||||
var done = assert.async()
|
||||
|
||||
$carousel
|
||||
.one('slid.bs.carousel', function () {
|
||||
@ -657,7 +657,7 @@ $(function () {
|
||||
})
|
||||
.bootstrapCarousel('next')
|
||||
strictEqual(getActiveId(), 'three', 'carousel did not wrap around and stayed on 3rd slide')
|
||||
start()
|
||||
done()
|
||||
})
|
||||
.bootstrapCarousel('next')
|
||||
})
|
||||
|
@ -43,14 +43,14 @@ $(function () {
|
||||
ok(/height/i.test($el.attr('style')), 'has height set')
|
||||
})
|
||||
|
||||
test('should not fire shown when show is prevented', function () {
|
||||
stop()
|
||||
test('should not fire shown when show is prevented', function (assert) {
|
||||
var done = assert.async()
|
||||
|
||||
$('<div class="collapse"/>')
|
||||
.on('show.bs.collapse', function (e) {
|
||||
e.preventDefault()
|
||||
ok(true, 'show event fired')
|
||||
start()
|
||||
done()
|
||||
})
|
||||
.on('shown.bs.collapse', function () {
|
||||
ok(false, 'shown event fired')
|
||||
@ -58,8 +58,8 @@ $(function () {
|
||||
.bootstrapCollapse('show')
|
||||
})
|
||||
|
||||
test('should reset style to auto after finishing opening collapse', function () {
|
||||
stop()
|
||||
test('should reset style to auto after finishing opening collapse', function (assert) {
|
||||
var done = assert.async()
|
||||
|
||||
$('<div class="collapse" style="height: 0px"/>')
|
||||
.on('show.bs.collapse', function () {
|
||||
@ -67,13 +67,13 @@ $(function () {
|
||||
})
|
||||
.on('shown.bs.collapse', function () {
|
||||
strictEqual(this.style.height, '', 'height is auto')
|
||||
start()
|
||||
done()
|
||||
})
|
||||
.bootstrapCollapse('show')
|
||||
})
|
||||
|
||||
test('should remove "collapsed" class from target when collapse is shown', function () {
|
||||
stop()
|
||||
test('should remove "collapsed" class from target when collapse is shown', function (assert) {
|
||||
var done = assert.async()
|
||||
|
||||
var $target = $('<a data-toggle="collapse" class="collapsed" href="#test1"/>').appendTo('#qunit-fixture')
|
||||
|
||||
@ -81,14 +81,14 @@ $(function () {
|
||||
.appendTo('#qunit-fixture')
|
||||
.on('shown.bs.collapse', function () {
|
||||
ok(!$target.hasClass('collapsed'))
|
||||
start()
|
||||
done()
|
||||
})
|
||||
|
||||
$target.click()
|
||||
})
|
||||
|
||||
test('should add "collapsed" class to target when collapse is hidden', function () {
|
||||
stop()
|
||||
test('should add "collapsed" class to target when collapse is hidden', function (assert) {
|
||||
var done = assert.async()
|
||||
|
||||
var $target = $('<a data-toggle="collapse" href="#test1"/>').appendTo('#qunit-fixture')
|
||||
|
||||
@ -96,14 +96,14 @@ $(function () {
|
||||
.appendTo('#qunit-fixture')
|
||||
.on('hidden.bs.collapse', function () {
|
||||
ok($target.hasClass('collapsed'))
|
||||
start()
|
||||
done()
|
||||
})
|
||||
|
||||
$target.click()
|
||||
})
|
||||
|
||||
test('should not close a collapse when initialized with "show" if already shown', function () {
|
||||
stop()
|
||||
test('should not close a collapse when initialized with "show" if already shown', function (assert) {
|
||||
var done = assert.async()
|
||||
|
||||
expect(0)
|
||||
|
||||
@ -115,11 +115,11 @@ $(function () {
|
||||
|
||||
$test.bootstrapCollapse('show')
|
||||
|
||||
setTimeout(start, 0)
|
||||
setTimeout(done, 0)
|
||||
})
|
||||
|
||||
test('should open a collapse when initialized with "show" if not already shown', function () {
|
||||
stop()
|
||||
test('should open a collapse when initialized with "show" if not already shown', function (assert) {
|
||||
var done = assert.async()
|
||||
|
||||
expect(1)
|
||||
|
||||
@ -131,11 +131,11 @@ $(function () {
|
||||
|
||||
$test.bootstrapCollapse('show')
|
||||
|
||||
setTimeout(start, 0)
|
||||
setTimeout(done, 0)
|
||||
})
|
||||
|
||||
test('should remove "collapsed" class from active accordion target', function () {
|
||||
stop()
|
||||
test('should remove "collapsed" class from active accordion target', function (assert) {
|
||||
var done = assert.async()
|
||||
|
||||
var accordionHTML = '<div class="panel-group" id="accordion">'
|
||||
+ '<div class="panel"/>'
|
||||
@ -161,14 +161,14 @@ $(function () {
|
||||
ok($target2.hasClass('collapsed'), 'inactive target 2 does have class "collapsed"')
|
||||
ok(!$target3.hasClass('collapsed'), 'active target 3 does not have class "collapsed"')
|
||||
|
||||
start()
|
||||
done()
|
||||
})
|
||||
|
||||
$target3.click()
|
||||
})
|
||||
|
||||
test('should allow dots in data-parent', function () {
|
||||
stop()
|
||||
test('should allow dots in data-parent', function (assert) {
|
||||
var done = assert.async()
|
||||
|
||||
var accordionHTML = '<div class="panel-group accordion">'
|
||||
+ '<div class="panel"/>'
|
||||
@ -194,14 +194,14 @@ $(function () {
|
||||
ok($target2.hasClass('collapsed'), 'inactive target 2 does have class "collapsed"')
|
||||
ok(!$target3.hasClass('collapsed'), 'active target 3 does not have class "collapsed"')
|
||||
|
||||
start()
|
||||
done()
|
||||
})
|
||||
|
||||
$target3.click()
|
||||
})
|
||||
|
||||
test('should set aria-expanded="true" on target when collapse is shown', function () {
|
||||
stop()
|
||||
test('should set aria-expanded="true" on target when collapse is shown', function (assert) {
|
||||
var done = assert.async()
|
||||
|
||||
var $target = $('<a data-toggle="collapse" class="collapsed" href="#test1" aria-expanded="false"/>').appendTo('#qunit-fixture')
|
||||
|
||||
@ -209,14 +209,14 @@ $(function () {
|
||||
.appendTo('#qunit-fixture')
|
||||
.on('shown.bs.collapse', function () {
|
||||
equal($target.attr('aria-expanded'), 'true', 'aria-expanded on target is "true"')
|
||||
start()
|
||||
done()
|
||||
})
|
||||
|
||||
$target.click()
|
||||
})
|
||||
|
||||
test('should set aria-expanded="false" on target when collapse is hidden', function () {
|
||||
stop()
|
||||
test('should set aria-expanded="false" on target when collapse is hidden', function (assert) {
|
||||
var done = assert.async()
|
||||
|
||||
var $target = $('<a data-toggle="collapse" href="#test1" aria-expanded="true"/>').appendTo('#qunit-fixture')
|
||||
|
||||
@ -224,14 +224,14 @@ $(function () {
|
||||
.appendTo('#qunit-fixture')
|
||||
.on('hidden.bs.collapse', function () {
|
||||
equal($target.attr('aria-expanded'), 'false', 'aria-expanded on target is "false"')
|
||||
start()
|
||||
done()
|
||||
})
|
||||
|
||||
$target.click()
|
||||
})
|
||||
|
||||
test('should change aria-expanded from active accordion target to "false" and set the newly active one to "true"', function () {
|
||||
stop()
|
||||
test('should change aria-expanded from active accordion target to "false" and set the newly active one to "true"', function (assert) {
|
||||
var done = assert.async()
|
||||
|
||||
var accordionHTML = '<div class="panel-group" id="accordion">'
|
||||
+ '<div class="panel"/>'
|
||||
@ -257,14 +257,14 @@ $(function () {
|
||||
equal($target2.attr('aria-expanded'), 'false', 'inactive target 2 has aria-expanded="false"')
|
||||
equal($target3.attr('aria-expanded'), 'true', 'active target 3 has aria-expanded="false"')
|
||||
|
||||
start()
|
||||
done()
|
||||
})
|
||||
|
||||
$target3.click()
|
||||
})
|
||||
|
||||
test('should not fire show event if show is prevented because other element is still transitioning', function () {
|
||||
stop()
|
||||
test('should not fire show event if show is prevented because other element is still transitioning', function (assert) {
|
||||
var done = assert.async()
|
||||
|
||||
var accordionHTML = '<div id="accordion">'
|
||||
+ '<div class="panel"/>'
|
||||
@ -294,12 +294,12 @@ $(function () {
|
||||
|
||||
setTimeout(function () {
|
||||
ok(!showFired, 'show event didn\'t fire')
|
||||
start()
|
||||
done()
|
||||
}, 1)
|
||||
})
|
||||
|
||||
test('should add "collapsed" class to target when collapse is hidden via manual invocation', function () {
|
||||
stop()
|
||||
test('should add "collapsed" class to target when collapse is hidden via manual invocation', function (assert) {
|
||||
var done = assert.async()
|
||||
|
||||
var $target = $('<a data-toggle="collapse" href="#test1"/>').appendTo('#qunit-fixture')
|
||||
|
||||
@ -307,13 +307,13 @@ $(function () {
|
||||
.appendTo('#qunit-fixture')
|
||||
.on('hidden.bs.collapse', function () {
|
||||
ok($target.hasClass('collapsed'))
|
||||
start()
|
||||
done()
|
||||
})
|
||||
.bootstrapCollapse('hide')
|
||||
})
|
||||
|
||||
test('should remove "collapsed" class from target when collapse is shown via manual invocation', function () {
|
||||
stop()
|
||||
test('should remove "collapsed" class from target when collapse is shown via manual invocation', function (assert) {
|
||||
var done = assert.async()
|
||||
|
||||
var $target = $('<a data-toggle="collapse" class="collapsed" href="#test1"/>').appendTo('#qunit-fixture')
|
||||
|
||||
@ -321,7 +321,7 @@ $(function () {
|
||||
.appendTo('#qunit-fixture')
|
||||
.on('shown.bs.collapse', function () {
|
||||
ok(!$target.hasClass('collapsed'))
|
||||
start()
|
||||
done()
|
||||
})
|
||||
.bootstrapCollapse('show')
|
||||
})
|
||||
|
@ -157,7 +157,7 @@ $(function () {
|
||||
strictEqual($('#qunit-fixture .open').length, 0, '"open" class removed')
|
||||
})
|
||||
|
||||
test('should fire show and hide event', function () {
|
||||
test('should fire show and hide event', function (assert) {
|
||||
var dropdownHTML = '<ul class="tabs">'
|
||||
+ '<li class="dropdown">'
|
||||
+ '<a href="#" class="dropdown-toggle" data-toggle="dropdown">Dropdown</a>'
|
||||
@ -174,7 +174,7 @@ $(function () {
|
||||
.find('[data-toggle="dropdown"]')
|
||||
.bootstrapDropdown()
|
||||
|
||||
stop()
|
||||
var done = assert.async()
|
||||
|
||||
$dropdown
|
||||
.parent('.dropdown')
|
||||
@ -183,7 +183,7 @@ $(function () {
|
||||
})
|
||||
.on('hide.bs.dropdown', function () {
|
||||
ok(true, 'hide was fired')
|
||||
start()
|
||||
done()
|
||||
})
|
||||
|
||||
$dropdown.click()
|
||||
@ -191,7 +191,7 @@ $(function () {
|
||||
})
|
||||
|
||||
|
||||
test('should fire shown and hidden event', function () {
|
||||
test('should fire shown and hidden event', function (assert) {
|
||||
var dropdownHTML = '<ul class="tabs">'
|
||||
+ '<li class="dropdown">'
|
||||
+ '<a href="#" class="dropdown-toggle" data-toggle="dropdown">Dropdown</a>'
|
||||
@ -208,7 +208,7 @@ $(function () {
|
||||
.find('[data-toggle="dropdown"]')
|
||||
.bootstrapDropdown()
|
||||
|
||||
stop()
|
||||
var done = assert.async()
|
||||
|
||||
$dropdown
|
||||
.parent('.dropdown')
|
||||
@ -217,15 +217,15 @@ $(function () {
|
||||
})
|
||||
.on('hidden.bs.dropdown', function () {
|
||||
ok(true, 'hidden was fired')
|
||||
start()
|
||||
done()
|
||||
})
|
||||
|
||||
$dropdown.click()
|
||||
$(document.body).click()
|
||||
})
|
||||
|
||||
test('should ignore keyboard events within <input>s and <textarea>s', function () {
|
||||
stop()
|
||||
test('should ignore keyboard events within <input>s and <textarea>s', function (assert) {
|
||||
var done = assert.async()
|
||||
|
||||
var dropdownHTML = '<ul class="tabs">'
|
||||
+ '<li class="dropdown">'
|
||||
@ -259,7 +259,7 @@ $(function () {
|
||||
$textarea.focus().trigger($.Event('keydown', { which: 38 }))
|
||||
ok($(document.activeElement).is($textarea), 'textarea still focused')
|
||||
|
||||
start()
|
||||
done()
|
||||
})
|
||||
|
||||
$dropdown.click()
|
||||
|
@ -33,36 +33,36 @@ $(function () {
|
||||
ok($.fn.bootstrapModal.Constructor.DEFAULTS, 'default object exposed')
|
||||
})
|
||||
|
||||
test('should insert into dom when show method is called', function () {
|
||||
stop()
|
||||
test('should insert into dom when show method is called', function (assert) {
|
||||
var done = assert.async()
|
||||
|
||||
$('<div id="modal-test"/>')
|
||||
.on('shown.bs.modal', function () {
|
||||
notEqual($('#modal-test').length, 0, 'modal inserted into dom')
|
||||
start()
|
||||
done()
|
||||
})
|
||||
.bootstrapModal('show')
|
||||
})
|
||||
|
||||
test('should fire show event', function () {
|
||||
stop()
|
||||
test('should fire show event', function (assert) {
|
||||
var done = assert.async()
|
||||
|
||||
$('<div id="modal-test"/>')
|
||||
.on('show.bs.modal', function () {
|
||||
ok(true, 'show event fired')
|
||||
start()
|
||||
done()
|
||||
})
|
||||
.bootstrapModal('show')
|
||||
})
|
||||
|
||||
test('should not fire shown when show was prevented', function () {
|
||||
stop()
|
||||
test('should not fire shown when show was prevented', function (assert) {
|
||||
var done = assert.async()
|
||||
|
||||
$('<div id="modal-test"/>')
|
||||
.on('show.bs.modal', function (e) {
|
||||
e.preventDefault()
|
||||
ok(true, 'show event fired')
|
||||
start()
|
||||
done()
|
||||
})
|
||||
.on('shown.bs.modal', function () {
|
||||
ok(false, 'shown event fired')
|
||||
@ -70,8 +70,8 @@ $(function () {
|
||||
.bootstrapModal('show')
|
||||
})
|
||||
|
||||
test('should hide modal when hide is called', function () {
|
||||
stop()
|
||||
test('should hide modal when hide is called', function (assert) {
|
||||
var done = assert.async()
|
||||
|
||||
$('<div id="modal-test"/>')
|
||||
.on('shown.bs.modal', function () {
|
||||
@ -81,13 +81,13 @@ $(function () {
|
||||
})
|
||||
.on('hidden.bs.modal', function () {
|
||||
ok(!$('#modal-test').is(':visible'), 'modal hidden')
|
||||
start()
|
||||
done()
|
||||
})
|
||||
.bootstrapModal('show')
|
||||
})
|
||||
|
||||
test('should toggle when toggle is called', function () {
|
||||
stop()
|
||||
test('should toggle when toggle is called', function (assert) {
|
||||
var done = assert.async()
|
||||
|
||||
$('<div id="modal-test"/>')
|
||||
.on('shown.bs.modal', function () {
|
||||
@ -97,13 +97,13 @@ $(function () {
|
||||
})
|
||||
.on('hidden.bs.modal', function () {
|
||||
ok(!$('#modal-test').is(':visible'), 'modal hidden')
|
||||
start()
|
||||
done()
|
||||
})
|
||||
.bootstrapModal('toggle')
|
||||
})
|
||||
|
||||
test('should remove from dom when click [data-dismiss="modal"]', function () {
|
||||
stop()
|
||||
test('should remove from dom when click [data-dismiss="modal"]', function (assert) {
|
||||
var done = assert.async()
|
||||
|
||||
$('<div id="modal-test"><span class="close" data-dismiss="modal"/></div>')
|
||||
.on('shown.bs.modal', function () {
|
||||
@ -113,13 +113,13 @@ $(function () {
|
||||
})
|
||||
.on('hidden.bs.modal', function () {
|
||||
ok(!$('#modal-test').is(':visible'), 'modal hidden')
|
||||
start()
|
||||
done()
|
||||
})
|
||||
.bootstrapModal('toggle')
|
||||
})
|
||||
|
||||
test('should allow modal close with "backdrop:false"', function () {
|
||||
stop()
|
||||
test('should allow modal close with "backdrop:false"', function (assert) {
|
||||
var done = assert.async()
|
||||
|
||||
$('<div id="modal-test" data-backdrop="false"/>')
|
||||
.on('shown.bs.modal', function () {
|
||||
@ -128,13 +128,13 @@ $(function () {
|
||||
})
|
||||
.on('hidden.bs.modal', function () {
|
||||
ok(!$('#modal-test').is(':visible'), 'modal hidden')
|
||||
start()
|
||||
done()
|
||||
})
|
||||
.bootstrapModal('show')
|
||||
})
|
||||
|
||||
test('should close modal when clicking outside of modal-content', function () {
|
||||
stop()
|
||||
test('should close modal when clicking outside of modal-content', function (assert) {
|
||||
var done = assert.async()
|
||||
|
||||
$('<div id="modal-test"><div class="contents"/></div>')
|
||||
.on('shown.bs.modal', function () {
|
||||
@ -145,13 +145,13 @@ $(function () {
|
||||
})
|
||||
.on('hidden.bs.modal', function () {
|
||||
ok(!$('#modal-test').is(':visible'), 'modal hidden')
|
||||
start()
|
||||
done()
|
||||
})
|
||||
.bootstrapModal('show')
|
||||
})
|
||||
|
||||
test('should close modal when escape key is pressed via keydown', function () {
|
||||
stop()
|
||||
test('should close modal when escape key is pressed via keydown', function (assert) {
|
||||
var done = assert.async()
|
||||
|
||||
var div = $('<div id="modal-test"/>')
|
||||
div
|
||||
@ -163,14 +163,14 @@ $(function () {
|
||||
setTimeout(function () {
|
||||
ok(!$('#modal-test').is(':visible'), 'modal hidden')
|
||||
div.remove()
|
||||
start()
|
||||
done()
|
||||
}, 0)
|
||||
})
|
||||
.bootstrapModal('show')
|
||||
})
|
||||
|
||||
test('should not close modal when escape key is pressed via keyup', function () {
|
||||
stop()
|
||||
test('should not close modal when escape key is pressed via keyup', function (assert) {
|
||||
var done = assert.async()
|
||||
|
||||
var div = $('<div id="modal-test"/>')
|
||||
div
|
||||
@ -182,14 +182,14 @@ $(function () {
|
||||
setTimeout(function () {
|
||||
ok($('#modal-test').is(':visible'), 'modal still visible')
|
||||
div.remove()
|
||||
start()
|
||||
done()
|
||||
}, 0)
|
||||
})
|
||||
.bootstrapModal('show')
|
||||
})
|
||||
|
||||
test('should trigger hide event once when clicking outside of modal-content', function () {
|
||||
stop()
|
||||
test('should trigger hide event once when clicking outside of modal-content', function (assert) {
|
||||
var done = assert.async()
|
||||
|
||||
var triggered
|
||||
|
||||
@ -201,31 +201,36 @@ $(function () {
|
||||
.on('hide.bs.modal', function () {
|
||||
triggered += 1
|
||||
strictEqual(triggered, 1, 'modal hide triggered once')
|
||||
start()
|
||||
done()
|
||||
})
|
||||
.bootstrapModal('show')
|
||||
})
|
||||
|
||||
test('should close reopened modal with [data-dismiss="modal"] click', function () {
|
||||
stop()
|
||||
test('should close reopened modal with [data-dismiss="modal"] click', function (assert) {
|
||||
var done = assert.async()
|
||||
|
||||
$('<div id="modal-test"><div class="contents"><div id="close" data-dismiss="modal"/></div></div>')
|
||||
.on('shown.bs.modal', function () {
|
||||
.one('shown.bs.modal', function () {
|
||||
$('#close').click()
|
||||
ok(!$('#modal-test').is(':visible'), 'modal hidden')
|
||||
})
|
||||
.one('hidden.bs.modal', function () {
|
||||
// after one open-close cycle
|
||||
ok(!$('#modal-test').is(':visible'), 'modal hidden')
|
||||
$(this)
|
||||
.one('shown.bs.modal', function () {
|
||||
$('#close').click()
|
||||
})
|
||||
.one('hidden.bs.modal', function () {
|
||||
start()
|
||||
ok(!$('#modal-test').is(':visible'), 'modal hidden')
|
||||
done()
|
||||
})
|
||||
.bootstrapModal('show')
|
||||
})
|
||||
.bootstrapModal('show')
|
||||
})
|
||||
|
||||
test('should restore focus to toggling element when modal is hidden after having been opened via data-api', function () {
|
||||
stop()
|
||||
test('should restore focus to toggling element when modal is hidden after having been opened via data-api', function (assert) {
|
||||
var done = assert.async()
|
||||
|
||||
var $toggleBtn = $('<button data-toggle="modal" data-target="#modal-test"/>').appendTo('#qunit-fixture')
|
||||
|
||||
@ -233,7 +238,7 @@ $(function () {
|
||||
.on('hidden.bs.modal', function () {
|
||||
setTimeout(function () {
|
||||
ok($(document.activeElement).is($toggleBtn), 'toggling element is once again focused')
|
||||
start()
|
||||
done()
|
||||
}, 0)
|
||||
})
|
||||
.on('shown.bs.modal', function () {
|
||||
@ -244,8 +249,8 @@ $(function () {
|
||||
$toggleBtn.click()
|
||||
})
|
||||
|
||||
test('should not restore focus to toggling element if the associated show event gets prevented', function () {
|
||||
stop()
|
||||
test('should not restore focus to toggling element if the associated show event gets prevented', function (assert) {
|
||||
var done = assert.async()
|
||||
var $toggleBtn = $('<button data-toggle="modal" data-target="#modal-test"/>').appendTo('#qunit-fixture')
|
||||
var $otherBtn = $('<button id="other-btn"/>').appendTo('#qunit-fixture')
|
||||
|
||||
@ -260,7 +265,7 @@ $(function () {
|
||||
.on('hidden.bs.modal', function () {
|
||||
setTimeout(function () {
|
||||
ok($(document.activeElement).is($otherBtn), 'focus returned to toggling element')
|
||||
start()
|
||||
done()
|
||||
}, 0)
|
||||
})
|
||||
.on('shown.bs.modal', function () {
|
||||
|
@ -188,7 +188,7 @@ $(function () {
|
||||
equal($('.popover').length, 0, 'popover was removed')
|
||||
})
|
||||
|
||||
test('should detach popover content rather than removing it so that event handlers are left intact', function () {
|
||||
test('should detach popover content rather than removing it so that event handlers are left intact', function (assert) {
|
||||
var $content = $('<div class="content-with-handler"><a class="btn btn-warning">Button with event handler</a></div>').appendTo('#qunit-fixture')
|
||||
|
||||
var handlerCalled = false
|
||||
@ -207,7 +207,7 @@ $(function () {
|
||||
}
|
||||
})
|
||||
|
||||
stop()
|
||||
var done = assert.async()
|
||||
$div
|
||||
.one('shown.bs.popover', function () {
|
||||
$div
|
||||
@ -217,7 +217,7 @@ $(function () {
|
||||
$('.content-with-handler .btn').click()
|
||||
$div.bootstrapPopover('destroy')
|
||||
ok(handlerCalled, 'content\'s event handler still present')
|
||||
start()
|
||||
done()
|
||||
})
|
||||
.bootstrapPopover('show')
|
||||
})
|
||||
|
@ -29,8 +29,8 @@ $(function () {
|
||||
strictEqual($scrollspy[0], $el[0], 'collection contains element')
|
||||
})
|
||||
|
||||
test('should only switch "active" class on current target', function () {
|
||||
stop()
|
||||
test('should only switch "active" class on current target', function (assert) {
|
||||
var done = assert.async()
|
||||
|
||||
var sectionHTML = '<div id="root" class="active">'
|
||||
+ '<div class="topbar">'
|
||||
@ -67,14 +67,14 @@ $(function () {
|
||||
|
||||
$scrollspy.on('scroll.bs.scrollspy', function () {
|
||||
ok($section.hasClass('active'), '"active" class still on root node')
|
||||
start()
|
||||
done()
|
||||
})
|
||||
|
||||
$scrollspy.scrollTop(350)
|
||||
})
|
||||
|
||||
test('should correctly select middle navigation option when large offset is used', function () {
|
||||
stop()
|
||||
test('should correctly select middle navigation option when large offset is used', function (assert) {
|
||||
var done = assert.async()
|
||||
|
||||
var sectionHTML = '<div id="header" style="height: 500px;"></div>'
|
||||
+ '<nav id="navigation" class="navbar">'
|
||||
@ -100,13 +100,13 @@ $(function () {
|
||||
ok(!$section.find('#one-link').parent().hasClass('active'), '"active" class removed from first section')
|
||||
ok($section.find('#two-link').parent().hasClass('active'), '"active" class on middle section')
|
||||
ok(!$section.find('#three-link').parent().hasClass('active'), '"active" class not on last section')
|
||||
start()
|
||||
done()
|
||||
})
|
||||
|
||||
$scrollspy.scrollTop(550)
|
||||
})
|
||||
|
||||
test('should add the active class to the correct element', function () {
|
||||
test('should add the active class to the correct element', function (assert) {
|
||||
var navbarHtml =
|
||||
'<nav class="navbar">'
|
||||
+ '<ul class="nav">'
|
||||
@ -128,10 +128,10 @@ $(function () {
|
||||
var testElementIsActiveAfterScroll = function (element, target) {
|
||||
var deferred = $.Deferred()
|
||||
var scrollHeight = Math.ceil($content.scrollTop() + $(target).position().top)
|
||||
stop()
|
||||
var done = assert.async()
|
||||
$content.one('scroll', function () {
|
||||
ok($(element).hasClass('active'), 'target:' + target + ', element' + element)
|
||||
start()
|
||||
done()
|
||||
deferred.resolve()
|
||||
})
|
||||
$content.scrollTop(scrollHeight)
|
||||
@ -142,8 +142,8 @@ $(function () {
|
||||
.then(function () { return testElementIsActiveAfterScroll('#li-2', '#div-2') })
|
||||
})
|
||||
|
||||
test('should clear selection if above the first section', function () {
|
||||
stop()
|
||||
test('should clear selection if above the first section', function (assert) {
|
||||
var done = assert.async()
|
||||
|
||||
var sectionHTML = '<div id="header" style="height: 500px;"></div>'
|
||||
+ '<nav id="navigation" class="navbar">'
|
||||
@ -176,7 +176,7 @@ $(function () {
|
||||
$scrollspy
|
||||
.one('scroll.bs.scrollspy', function () {
|
||||
strictEqual($('.active').length, 0, 'selection cleared')
|
||||
start()
|
||||
done()
|
||||
})
|
||||
.scrollTop(0)
|
||||
})
|
||||
|
@ -59,14 +59,14 @@ $(function () {
|
||||
equal($('#qunit-fixture').find('.active').attr('id'), 'home')
|
||||
})
|
||||
|
||||
test('should not fire shown when show is prevented', function () {
|
||||
stop()
|
||||
test('should not fire shown when show is prevented', function (assert) {
|
||||
var done = assert.async()
|
||||
|
||||
$('<div class="tab"/>')
|
||||
.on('show.bs.tab', function (e) {
|
||||
e.preventDefault()
|
||||
ok(true, 'show event fired')
|
||||
start()
|
||||
done()
|
||||
})
|
||||
.on('shown.bs.tab', function () {
|
||||
ok(false, 'shown event fired')
|
||||
@ -74,8 +74,8 @@ $(function () {
|
||||
.bootstrapTab('show')
|
||||
})
|
||||
|
||||
test('show and shown events should reference correct relatedTarget', function () {
|
||||
stop()
|
||||
test('show and shown events should reference correct relatedTarget', function (assert) {
|
||||
var done = assert.async()
|
||||
|
||||
var dropHTML = '<ul class="drop">'
|
||||
+ '<li class="dropdown"><a data-toggle="dropdown" href="#">1</a>'
|
||||
@ -93,16 +93,16 @@ $(function () {
|
||||
.find('ul > li:last a')
|
||||
.on('show.bs.tab', function (e) {
|
||||
equal(e.relatedTarget.hash, '#1-1', 'references correct element as relatedTarget')
|
||||
start()
|
||||
})
|
||||
.on('shown.bs.tab', function (e) {
|
||||
equal(e.relatedTarget.hash, '#1-1', 'references correct element as relatedTarget')
|
||||
done()
|
||||
})
|
||||
.bootstrapTab('show')
|
||||
})
|
||||
|
||||
test('should fire hide and hidden events', function () {
|
||||
stop()
|
||||
test('should fire hide and hidden events', function (assert) {
|
||||
var done = assert.async()
|
||||
|
||||
var tabsHTML = '<ul class="tabs">'
|
||||
+ '<li><a href="#home">Home</a></li>'
|
||||
@ -123,7 +123,7 @@ $(function () {
|
||||
.find('li:first a')
|
||||
.on('hidden.bs.tab', function () {
|
||||
ok(true, 'hidden event fired')
|
||||
start()
|
||||
done()
|
||||
})
|
||||
.bootstrapTab('show')
|
||||
.end()
|
||||
@ -131,8 +131,8 @@ $(function () {
|
||||
.bootstrapTab('show')
|
||||
})
|
||||
|
||||
test('should not fire hidden when hide is prevented', function () {
|
||||
stop()
|
||||
test('should not fire hidden when hide is prevented', function (assert) {
|
||||
var done = assert.async()
|
||||
|
||||
var tabsHTML = '<ul class="tabs">'
|
||||
+ '<li><a href="#home">Home</a></li>'
|
||||
@ -144,7 +144,7 @@ $(function () {
|
||||
.on('hide.bs.tab', function (e) {
|
||||
e.preventDefault()
|
||||
ok(true, 'hide event fired')
|
||||
start()
|
||||
done()
|
||||
})
|
||||
.on('hidden.bs.tab', function () {
|
||||
ok(false, 'hidden event fired')
|
||||
@ -155,8 +155,8 @@ $(function () {
|
||||
.bootstrapTab('show')
|
||||
})
|
||||
|
||||
test('hide and hidden events contain correct relatedTarget', function () {
|
||||
stop()
|
||||
test('hide and hidden events contain correct relatedTarget', function (assert) {
|
||||
var done = assert.async()
|
||||
|
||||
var tabsHTML = '<ul class="tabs">'
|
||||
+ '<li><a href="#home">Home</a></li>'
|
||||
@ -170,7 +170,7 @@ $(function () {
|
||||
})
|
||||
.on('hidden.bs.tab', function (e) {
|
||||
equal(e.relatedTarget.hash, '#profile', 'references correct element as relatedTarget')
|
||||
start()
|
||||
done()
|
||||
})
|
||||
.bootstrapTab('show')
|
||||
.end()
|
||||
|
@ -115,37 +115,37 @@ $(function () {
|
||||
equal($('.tooltip').length, 0, 'tooltip removed')
|
||||
})
|
||||
|
||||
test('should fire show event', function () {
|
||||
stop()
|
||||
test('should fire show event', function (assert) {
|
||||
var done = assert.async()
|
||||
|
||||
$('<div title="tooltip title"/>')
|
||||
.on('show.bs.tooltip', function () {
|
||||
ok(true, 'show event fired')
|
||||
start()
|
||||
done()
|
||||
})
|
||||
.bootstrapTooltip('show')
|
||||
})
|
||||
|
||||
test('should fire shown event', function () {
|
||||
stop()
|
||||
test('should fire shown event', function (assert) {
|
||||
var done = assert.async()
|
||||
|
||||
$('<div title="tooltip title"></div>')
|
||||
.appendTo('#qunit-fixture')
|
||||
.on('shown.bs.tooltip', function () {
|
||||
ok(true, 'shown was called')
|
||||
start()
|
||||
done()
|
||||
})
|
||||
.bootstrapTooltip('show')
|
||||
})
|
||||
|
||||
test('should not fire shown event when show was prevented', function () {
|
||||
stop()
|
||||
test('should not fire shown event when show was prevented', function (assert) {
|
||||
var done = assert.async()
|
||||
|
||||
$('<div title="tooltip title"/>')
|
||||
.on('show.bs.tooltip', function (e) {
|
||||
e.preventDefault()
|
||||
ok(true, 'show event fired')
|
||||
start()
|
||||
done()
|
||||
})
|
||||
.on('shown.bs.tooltip', function () {
|
||||
ok(false, 'shown event fired')
|
||||
@ -153,8 +153,8 @@ $(function () {
|
||||
.bootstrapTooltip('show')
|
||||
})
|
||||
|
||||
test('should fire hide event', function () {
|
||||
stop()
|
||||
test('should fire hide event', function (assert) {
|
||||
var done = assert.async()
|
||||
|
||||
$('<div title="tooltip title"/>')
|
||||
.appendTo('#qunit-fixture')
|
||||
@ -163,13 +163,13 @@ $(function () {
|
||||
})
|
||||
.on('hide.bs.tooltip', function () {
|
||||
ok(true, 'hide event fired')
|
||||
start()
|
||||
done()
|
||||
})
|
||||
.bootstrapTooltip('show')
|
||||
})
|
||||
|
||||
test('should fire hidden event', function () {
|
||||
stop()
|
||||
test('should fire hidden event', function (assert) {
|
||||
var done = assert.async()
|
||||
|
||||
$('<div title="tooltip title"/>')
|
||||
.appendTo('#qunit-fixture')
|
||||
@ -178,13 +178,13 @@ $(function () {
|
||||
})
|
||||
.on('hidden.bs.tooltip', function () {
|
||||
ok(true, 'hidden event fired')
|
||||
start()
|
||||
done()
|
||||
})
|
||||
.bootstrapTooltip('show')
|
||||
})
|
||||
|
||||
test('should not fire hidden event when hide was prevented', function () {
|
||||
stop()
|
||||
test('should not fire hidden event when hide was prevented', function (assert) {
|
||||
var done = assert.async()
|
||||
|
||||
$('<div title="tooltip title"/>')
|
||||
.appendTo('#qunit-fixture')
|
||||
@ -194,7 +194,7 @@ $(function () {
|
||||
.on('hide.bs.tooltip', function (e) {
|
||||
e.preventDefault()
|
||||
ok(true, 'hide event fired')
|
||||
start()
|
||||
done()
|
||||
})
|
||||
.on('hidden.bs.tooltip', function () {
|
||||
ok(false, 'hidden event fired')
|
||||
@ -699,8 +699,8 @@ $(function () {
|
||||
ok(passed, '.tooltip(\'show\') should not throw an error if element no longer is in dom')
|
||||
})
|
||||
|
||||
test('should place tooltip on top of element', function () {
|
||||
stop()
|
||||
test('should place tooltip on top of element', function (assert) {
|
||||
var done = assert.async()
|
||||
|
||||
var containerHTML = '<div>'
|
||||
+ '<p style="margin-top: 200px">'
|
||||
@ -732,12 +732,12 @@ $(function () {
|
||||
|
||||
setTimeout(function () {
|
||||
ok(Math.round($tooltip.offset().top + $tooltip.outerHeight()) <= Math.round($trigger.offset().top))
|
||||
start()
|
||||
done()
|
||||
}, 0)
|
||||
})
|
||||
|
||||
test('should place tooltip inside viewport', function () {
|
||||
stop()
|
||||
test('should place tooltip inside viewport', function (assert) {
|
||||
var done = assert.async()
|
||||
|
||||
var $container = $('<div/>')
|
||||
.css({
|
||||
@ -763,12 +763,12 @@ $(function () {
|
||||
|
||||
setTimeout(function () {
|
||||
ok($('.tooltip').offset().left >= 0)
|
||||
start()
|
||||
done()
|
||||
}, 0)
|
||||
})
|
||||
|
||||
test('should show tooltip if leave event hasn\'t occurred before delay expires', function () {
|
||||
stop()
|
||||
test('should show tooltip if leave event hasn\'t occurred before delay expires', function (assert) {
|
||||
var done = assert.async()
|
||||
|
||||
var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"/>')
|
||||
.appendTo('#qunit-fixture')
|
||||
@ -780,14 +780,14 @@ $(function () {
|
||||
|
||||
setTimeout(function () {
|
||||
ok($('.tooltip').is('.fade.in'), '200ms: tooltip is faded in')
|
||||
start()
|
||||
done()
|
||||
}, 200)
|
||||
|
||||
$tooltip.trigger('mouseenter')
|
||||
})
|
||||
|
||||
test('should not show tooltip if leave event occurs before delay expires', function () {
|
||||
stop()
|
||||
test('should not show tooltip if leave event occurs before delay expires', function (assert) {
|
||||
var done = assert.async()
|
||||
|
||||
var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"/>')
|
||||
.appendTo('#qunit-fixture')
|
||||
@ -800,14 +800,14 @@ $(function () {
|
||||
|
||||
setTimeout(function () {
|
||||
ok(!$('.tooltip').is('.fade.in'), '200ms: tooltip not faded in')
|
||||
start()
|
||||
done()
|
||||
}, 200)
|
||||
|
||||
$tooltip.trigger('mouseenter')
|
||||
})
|
||||
|
||||
test('should not hide tooltip if leave event occurs and enter event occurs within the hide delay', function () {
|
||||
stop()
|
||||
test('should not hide tooltip if leave event occurs and enter event occurs within the hide delay', function (assert) {
|
||||
var done = assert.async()
|
||||
|
||||
var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"/>')
|
||||
.appendTo('#qunit-fixture')
|
||||
@ -824,15 +824,15 @@ $(function () {
|
||||
|
||||
setTimeout(function () {
|
||||
ok($('.tooltip').is('.fade.in'), '200ms: tooltip still faded in')
|
||||
start()
|
||||
done()
|
||||
}, 200)
|
||||
}, 0)
|
||||
|
||||
$tooltip.trigger('mouseenter')
|
||||
})
|
||||
|
||||
test('should not show tooltip if leave event occurs before delay expires', function () {
|
||||
stop()
|
||||
test('should not show tooltip if leave event occurs before delay expires', function (assert) {
|
||||
var done = assert.async()
|
||||
|
||||
var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"/>')
|
||||
.appendTo('#qunit-fixture')
|
||||
@ -845,14 +845,14 @@ $(function () {
|
||||
|
||||
setTimeout(function () {
|
||||
ok(!$('.tooltip').is('.fade.in'), '200ms: tooltip not faded in')
|
||||
start()
|
||||
done()
|
||||
}, 200)
|
||||
|
||||
$tooltip.trigger('mouseenter')
|
||||
})
|
||||
|
||||
test('should not show tooltip if leave event occurs before delay expires, even if hide delay is 0', function () {
|
||||
stop()
|
||||
test('should not show tooltip if leave event occurs before delay expires, even if hide delay is 0', function (assert) {
|
||||
var done = assert.async()
|
||||
|
||||
var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"/>')
|
||||
.appendTo('#qunit-fixture')
|
||||
@ -865,14 +865,14 @@ $(function () {
|
||||
|
||||
setTimeout(function () {
|
||||
ok(!$('.tooltip').is('.fade.in'), '250ms: tooltip not faded in')
|
||||
start()
|
||||
done()
|
||||
}, 250)
|
||||
|
||||
$tooltip.trigger('mouseenter')
|
||||
})
|
||||
|
||||
test('should wait 200ms before hiding the tooltip', function () {
|
||||
stop()
|
||||
test('should wait 200ms before hiding the tooltip', function (assert) {
|
||||
var done = assert.async()
|
||||
|
||||
var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"/>')
|
||||
.appendTo('#qunit-fixture')
|
||||
@ -889,7 +889,7 @@ $(function () {
|
||||
|
||||
setTimeout(function () {
|
||||
ok(!$tooltip.data('bs.tooltip').$tip.is('.in'), '200ms: tooltip removed')
|
||||
start()
|
||||
done()
|
||||
}, 200)
|
||||
|
||||
}, 0)
|
||||
@ -897,14 +897,14 @@ $(function () {
|
||||
$tooltip.trigger('mouseenter')
|
||||
})
|
||||
|
||||
test('should correctly position tooltips on SVG elements', function () {
|
||||
test('should correctly position tooltips on SVG elements', function (assert) {
|
||||
if (!window.SVGElement) {
|
||||
// Skip IE8 since it doesn't support SVG
|
||||
expect(0)
|
||||
return
|
||||
}
|
||||
|
||||
stop()
|
||||
var done = assert.async()
|
||||
|
||||
var styles = '<style>'
|
||||
+ '.tooltip, .tooltip *, .tooltip *:before, .tooltip *:after { box-sizing: border-box; }'
|
||||
@ -928,15 +928,15 @@ $(function () {
|
||||
ok(Math.abs(offset.left - 88) <= 1, 'tooltip has correct horizontal location')
|
||||
$circle.bootstrapTooltip('hide')
|
||||
equal($('.tooltip').length, 0, 'tooltip removed from dom')
|
||||
start()
|
||||
done()
|
||||
})
|
||||
.bootstrapTooltip({ container: 'body', placement: 'top', trigger: 'manual' })
|
||||
|
||||
$circle.bootstrapTooltip('show')
|
||||
})
|
||||
|
||||
test('should correctly determine auto placement based on container rather than parent', function () {
|
||||
stop()
|
||||
test('should correctly determine auto placement based on container rather than parent', function (assert) {
|
||||
var done = assert.async()
|
||||
|
||||
var styles = '<style>'
|
||||
+ '.tooltip, .tooltip *, .tooltip *:before, .tooltip *:after { box-sizing: border-box; }'
|
||||
@ -965,7 +965,7 @@ $(function () {
|
||||
$styles.remove()
|
||||
$(this).remove()
|
||||
equal($('.tooltip').length, 0, 'tooltip removed from dom')
|
||||
start()
|
||||
done()
|
||||
})
|
||||
.bootstrapTooltip({
|
||||
container: 'body',
|
||||
@ -1037,8 +1037,8 @@ $(function () {
|
||||
equal(currentUid, $('#tt-content').text())
|
||||
})
|
||||
|
||||
test('should position arrow correctly when tooltip is moved to not appear offscreen', function () {
|
||||
stop()
|
||||
test('should position arrow correctly when tooltip is moved to not appear offscreen', function (assert) {
|
||||
var done = assert.async()
|
||||
|
||||
var styles = '<style>'
|
||||
+ '.tooltip, .tooltip *, .tooltip *:before, .tooltip *:after { box-sizing: border-box; }'
|
||||
@ -1059,7 +1059,7 @@ $(function () {
|
||||
$styles.remove()
|
||||
$(this).remove()
|
||||
equal($('.tooltip').length, 0, 'tooltip removed from dom')
|
||||
start()
|
||||
done()
|
||||
})
|
||||
.bootstrapTooltip({
|
||||
container: 'body',
|
||||
@ -1069,14 +1069,14 @@ $(function () {
|
||||
.bootstrapTooltip('show')
|
||||
})
|
||||
|
||||
test('should correctly position tooltips on transformed elements', function () {
|
||||
test('should correctly position tooltips on transformed elements', function (assert) {
|
||||
var styleProps = document.documentElement.style
|
||||
if (!('transform' in styleProps) && !('webkitTransform' in styleProps) && !('msTransform' in styleProps)) {
|
||||
expect(0)
|
||||
return
|
||||
}
|
||||
|
||||
stop()
|
||||
var done = assert.async()
|
||||
|
||||
var styles = '<style>'
|
||||
+ '#qunit-fixture { top: 0; left: 0; }'
|
||||
@ -1096,7 +1096,7 @@ $(function () {
|
||||
ok(Math.abs(offset.left - 88) <= 1, 'tooltip has correct horizontal location')
|
||||
ok(Math.abs(offset.top - 126) <= 1, 'tooltip has correct vertical location')
|
||||
$element.bootstrapTooltip('hide')
|
||||
start()
|
||||
done()
|
||||
})
|
||||
.bootstrapTooltip({
|
||||
container: 'body',
|
||||
|
51
js/tests/vendor/qunit.css
vendored
51
js/tests/vendor/qunit.css
vendored
@ -1,12 +1,12 @@
|
||||
/*!
|
||||
* QUnit 1.15.0
|
||||
* QUnit 1.17.0
|
||||
* http://qunitjs.com/
|
||||
*
|
||||
* Copyright 2014 jQuery Foundation and other contributors
|
||||
* Copyright jQuery Foundation and other contributors
|
||||
* Released under the MIT license
|
||||
* http://jquery.org/license
|
||||
*
|
||||
* Date: 2014-08-08T16:00Z
|
||||
* Date: 2015-01-19T11:58Z
|
||||
*/
|
||||
|
||||
/** Font Family and Sizes */
|
||||
@ -77,6 +77,18 @@
|
||||
|
||||
#qunit-modulefilter-container {
|
||||
float: right;
|
||||
padding: 0.2em;
|
||||
}
|
||||
|
||||
.qunit-url-config {
|
||||
display: inline-block;
|
||||
padding: 0.1em;
|
||||
}
|
||||
|
||||
.qunit-filter {
|
||||
display: block;
|
||||
float: right;
|
||||
margin-left: 1em;
|
||||
}
|
||||
|
||||
/** Tests: Pass/Fail */
|
||||
@ -91,7 +103,19 @@
|
||||
list-style-position: inside;
|
||||
}
|
||||
|
||||
#qunit-tests.hidepass li.pass, #qunit-tests.hidepass li.running {
|
||||
#qunit-tests > li {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#qunit-tests li.running,
|
||||
#qunit-tests li.pass,
|
||||
#qunit-tests li.fail,
|
||||
#qunit-tests li.skipped {
|
||||
display: list-item;
|
||||
}
|
||||
|
||||
#qunit-tests.hidepass li.running,
|
||||
#qunit-tests.hidepass li.pass {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@ -99,6 +123,10 @@
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#qunit-tests li.skipped strong {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
#qunit-tests li a {
|
||||
padding: 0.5em;
|
||||
color: #C2CCD1;
|
||||
@ -211,6 +239,21 @@
|
||||
|
||||
#qunit-banner.qunit-fail { background-color: #EE5757; }
|
||||
|
||||
/*** Skipped tests */
|
||||
|
||||
#qunit-tests .skipped {
|
||||
background-color: #EBECE9;
|
||||
}
|
||||
|
||||
#qunit-tests .qunit-skipped-label {
|
||||
background-color: #F4FF77;
|
||||
display: inline-block;
|
||||
font-style: normal;
|
||||
color: #366097;
|
||||
line-height: 1.8em;
|
||||
padding: 0 0.5em;
|
||||
margin: -0.4em 0.4em -0.4em 0;
|
||||
}
|
||||
|
||||
/** Result */
|
||||
|
||||
|
1494
js/tests/vendor/qunit.js
vendored
1494
js/tests/vendor/qunit.js
vendored
@ -1,12 +1,12 @@
|
||||
/*!
|
||||
* QUnit 1.15.0
|
||||
* QUnit 1.17.0
|
||||
* http://qunitjs.com/
|
||||
*
|
||||
* Copyright 2014 jQuery Foundation and other contributors
|
||||
* Copyright jQuery Foundation and other contributors
|
||||
* Released under the MIT license
|
||||
* http://jquery.org/license
|
||||
*
|
||||
* Date: 2014-08-08T16:00Z
|
||||
* Date: 2015-01-19T11:58Z
|
||||
*/
|
||||
|
||||
(function( window ) {
|
||||
@ -14,6 +14,7 @@
|
||||
var QUnit,
|
||||
config,
|
||||
onErrorFnPrev,
|
||||
loggingCallbacks = {},
|
||||
fileName = ( sourceFromStacktrace( 0 ) || "" ).replace( /(:\d+)+\)?/, "" ).replace( /.+\//, "" ),
|
||||
toString = Object.prototype.toString,
|
||||
hasOwn = Object.prototype.hasOwnProperty,
|
||||
@ -22,11 +23,13 @@ var QUnit,
|
||||
now = Date.now || function() {
|
||||
return new Date().getTime();
|
||||
},
|
||||
globalStartCalled = false,
|
||||
runStarted = false,
|
||||
setTimeout = window.setTimeout,
|
||||
clearTimeout = window.clearTimeout,
|
||||
defined = {
|
||||
document: typeof window.document !== "undefined",
|
||||
setTimeout: typeof window.setTimeout !== "undefined",
|
||||
document: window.document !== undefined,
|
||||
setTimeout: window.setTimeout !== undefined,
|
||||
sessionStorage: (function() {
|
||||
var x = "qunit-test-string";
|
||||
try {
|
||||
@ -86,132 +89,7 @@ var QUnit,
|
||||
return vals;
|
||||
};
|
||||
|
||||
// Root QUnit object.
|
||||
// `QUnit` initialized at top of scope
|
||||
QUnit = {
|
||||
|
||||
// call on start of module test to prepend name to all tests
|
||||
module: function( name, testEnvironment ) {
|
||||
config.currentModule = name;
|
||||
config.currentModuleTestEnvironment = testEnvironment;
|
||||
config.modules[ name ] = true;
|
||||
},
|
||||
|
||||
asyncTest: function( testName, expected, callback ) {
|
||||
if ( arguments.length === 2 ) {
|
||||
callback = expected;
|
||||
expected = null;
|
||||
}
|
||||
|
||||
QUnit.test( testName, expected, callback, true );
|
||||
},
|
||||
|
||||
test: function( testName, expected, callback, async ) {
|
||||
var test;
|
||||
|
||||
if ( arguments.length === 2 ) {
|
||||
callback = expected;
|
||||
expected = null;
|
||||
}
|
||||
|
||||
test = new Test({
|
||||
testName: testName,
|
||||
expected: expected,
|
||||
async: async,
|
||||
callback: callback,
|
||||
module: config.currentModule,
|
||||
moduleTestEnvironment: config.currentModuleTestEnvironment,
|
||||
stack: sourceFromStacktrace( 2 )
|
||||
});
|
||||
|
||||
if ( !validTest( test ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
test.queue();
|
||||
},
|
||||
|
||||
start: function( count ) {
|
||||
var message;
|
||||
|
||||
// QUnit hasn't been initialized yet.
|
||||
// Note: RequireJS (et al) may delay onLoad
|
||||
if ( config.semaphore === undefined ) {
|
||||
QUnit.begin(function() {
|
||||
// This is triggered at the top of QUnit.load, push start() to the event loop, to allow QUnit.load to finish first
|
||||
setTimeout(function() {
|
||||
QUnit.start( count );
|
||||
});
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
config.semaphore -= count || 1;
|
||||
// don't start until equal number of stop-calls
|
||||
if ( config.semaphore > 0 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the starting time when the first test is run
|
||||
QUnit.config.started = QUnit.config.started || now();
|
||||
// ignore if start is called more often then stop
|
||||
if ( config.semaphore < 0 ) {
|
||||
config.semaphore = 0;
|
||||
|
||||
message = "Called start() while already started (QUnit.config.semaphore was 0 already)";
|
||||
|
||||
if ( config.current ) {
|
||||
QUnit.pushFailure( message, sourceFromStacktrace( 2 ) );
|
||||
} else {
|
||||
throw new Error( message );
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
// A slight delay, to avoid any current callbacks
|
||||
if ( defined.setTimeout ) {
|
||||
setTimeout(function() {
|
||||
if ( config.semaphore > 0 ) {
|
||||
return;
|
||||
}
|
||||
if ( config.timeout ) {
|
||||
clearTimeout( config.timeout );
|
||||
}
|
||||
|
||||
config.blocking = false;
|
||||
process( true );
|
||||
}, 13 );
|
||||
} else {
|
||||
config.blocking = false;
|
||||
process( true );
|
||||
}
|
||||
},
|
||||
|
||||
stop: function( count ) {
|
||||
config.semaphore += count || 1;
|
||||
config.blocking = true;
|
||||
|
||||
if ( config.testTimeout && defined.setTimeout ) {
|
||||
clearTimeout( config.timeout );
|
||||
config.timeout = setTimeout(function() {
|
||||
QUnit.ok( false, "Test timed out" );
|
||||
config.semaphore = 1;
|
||||
QUnit.start();
|
||||
}, config.testTimeout );
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// We use the prototype to distinguish between properties that should
|
||||
// be exposed as globals (and in exports) and those that shouldn't
|
||||
(function() {
|
||||
function F() {}
|
||||
F.prototype = QUnit;
|
||||
QUnit = new F();
|
||||
|
||||
// Make F QUnit's constructor so that we can add to the prototype later
|
||||
QUnit.constructor = F;
|
||||
}());
|
||||
QUnit = {};
|
||||
|
||||
/**
|
||||
* Config object: Maintain internal state
|
||||
@ -225,10 +103,6 @@ config = {
|
||||
// block until document ready
|
||||
blocking: true,
|
||||
|
||||
// when enabled, show only failing tests
|
||||
// gets persisted through sessionStorage and can be changed in UI via checkbox
|
||||
hidepassed: false,
|
||||
|
||||
// by default, run previously failed tests first
|
||||
// very useful in combination with "Hide passed tests" checked
|
||||
reorder: true,
|
||||
@ -245,24 +119,40 @@ config = {
|
||||
// add checkboxes that are persisted in the query-string
|
||||
// when enabled, the id is set to `true` as a `QUnit.config` property
|
||||
urlConfig: [
|
||||
{
|
||||
id: "hidepassed",
|
||||
label: "Hide passed tests",
|
||||
tooltip: "Only show tests and assertions that fail. Stored as query-strings."
|
||||
},
|
||||
{
|
||||
id: "noglobals",
|
||||
label: "Check for Globals",
|
||||
tooltip: "Enabling this will test if any test introduces new properties on the `window` object. Stored as query-strings."
|
||||
tooltip: "Enabling this will test if any test introduces new properties on the " +
|
||||
"`window` object. Stored as query-strings."
|
||||
},
|
||||
{
|
||||
id: "notrycatch",
|
||||
label: "No try-catch",
|
||||
tooltip: "Enabling this will run tests outside of a try-catch block. Makes debugging exceptions in IE reasonable. Stored as query-strings."
|
||||
tooltip: "Enabling this will run tests outside of a try-catch block. Makes debugging " +
|
||||
"exceptions in IE reasonable. Stored as query-strings."
|
||||
}
|
||||
],
|
||||
|
||||
// Set of all modules.
|
||||
modules: {},
|
||||
modules: [],
|
||||
|
||||
// The first unnamed module
|
||||
currentModule: {
|
||||
name: "",
|
||||
tests: []
|
||||
},
|
||||
|
||||
callbacks: {}
|
||||
};
|
||||
|
||||
// Push a loose unnamed module to the modules collection
|
||||
config.modules.push( config.currentModule );
|
||||
|
||||
// Initialize more QUnit.config and QUnit.urlParams
|
||||
(function() {
|
||||
var i, current,
|
||||
@ -286,22 +176,22 @@ config = {
|
||||
}
|
||||
}
|
||||
|
||||
if ( urlParams.filter === true ) {
|
||||
delete urlParams.filter;
|
||||
}
|
||||
|
||||
QUnit.urlParams = urlParams;
|
||||
|
||||
// String search anywhere in moduleName+testName
|
||||
config.filter = urlParams.filter;
|
||||
|
||||
// Exact match of the module name
|
||||
config.module = urlParams.module;
|
||||
config.testId = [];
|
||||
if ( urlParams.testId ) {
|
||||
|
||||
config.testNumber = [];
|
||||
if ( urlParams.testNumber ) {
|
||||
|
||||
// Ensure that urlParams.testNumber is an array
|
||||
urlParams.testNumber = [].concat( urlParams.testNumber );
|
||||
for ( i = 0; i < urlParams.testNumber.length; i++ ) {
|
||||
current = urlParams.testNumber[ i ];
|
||||
config.testNumber.push( parseInt( current, 10 ) );
|
||||
// Ensure that urlParams.testId is an array
|
||||
urlParams.testId = [].concat( urlParams.testId );
|
||||
for ( i = 0; i < urlParams.testId.length; i++ ) {
|
||||
config.testId.push( urlParams.testId[ i ] );
|
||||
}
|
||||
}
|
||||
|
||||
@ -309,8 +199,130 @@ config = {
|
||||
QUnit.isLocal = location.protocol === "file:";
|
||||
}());
|
||||
|
||||
// Root QUnit object.
|
||||
// `QUnit` initialized at top of scope
|
||||
extend( QUnit, {
|
||||
|
||||
// call on start of module test to prepend name to all tests
|
||||
module: function( name, testEnvironment ) {
|
||||
var currentModule = {
|
||||
name: name,
|
||||
testEnvironment: testEnvironment,
|
||||
tests: []
|
||||
};
|
||||
|
||||
// DEPRECATED: handles setup/teardown functions,
|
||||
// beforeEach and afterEach should be used instead
|
||||
if ( testEnvironment && testEnvironment.setup ) {
|
||||
testEnvironment.beforeEach = testEnvironment.setup;
|
||||
delete testEnvironment.setup;
|
||||
}
|
||||
if ( testEnvironment && testEnvironment.teardown ) {
|
||||
testEnvironment.afterEach = testEnvironment.teardown;
|
||||
delete testEnvironment.teardown;
|
||||
}
|
||||
|
||||
config.modules.push( currentModule );
|
||||
config.currentModule = currentModule;
|
||||
},
|
||||
|
||||
// DEPRECATED: QUnit.asyncTest() will be removed in QUnit 2.0.
|
||||
asyncTest: function( testName, expected, callback ) {
|
||||
if ( arguments.length === 2 ) {
|
||||
callback = expected;
|
||||
expected = null;
|
||||
}
|
||||
|
||||
QUnit.test( testName, expected, callback, true );
|
||||
},
|
||||
|
||||
test: function( testName, expected, callback, async ) {
|
||||
var test;
|
||||
|
||||
if ( arguments.length === 2 ) {
|
||||
callback = expected;
|
||||
expected = null;
|
||||
}
|
||||
|
||||
test = new Test({
|
||||
testName: testName,
|
||||
expected: expected,
|
||||
async: async,
|
||||
callback: callback
|
||||
});
|
||||
|
||||
test.queue();
|
||||
},
|
||||
|
||||
skip: function( testName ) {
|
||||
var test = new Test({
|
||||
testName: testName,
|
||||
skip: true
|
||||
});
|
||||
|
||||
test.queue();
|
||||
},
|
||||
|
||||
// DEPRECATED: The functionality of QUnit.start() will be altered in QUnit 2.0.
|
||||
// In QUnit 2.0, invoking it will ONLY affect the `QUnit.config.autostart` blocking behavior.
|
||||
start: function( count ) {
|
||||
var globalStartAlreadyCalled = globalStartCalled;
|
||||
|
||||
if ( !config.current ) {
|
||||
globalStartCalled = true;
|
||||
|
||||
if ( runStarted ) {
|
||||
throw new Error( "Called start() outside of a test context while already started" );
|
||||
} else if ( globalStartAlreadyCalled || count > 1 ) {
|
||||
throw new Error( "Called start() outside of a test context too many times" );
|
||||
} else if ( config.autostart ) {
|
||||
throw new Error( "Called start() outside of a test context when " +
|
||||
"QUnit.config.autostart was true" );
|
||||
} else if ( !config.pageLoaded ) {
|
||||
|
||||
// The page isn't completely loaded yet, so bail out and let `QUnit.load` handle it
|
||||
config.autostart = true;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
|
||||
// If a test is running, adjust its semaphore
|
||||
config.current.semaphore -= count || 1;
|
||||
|
||||
// Don't start until equal number of stop-calls
|
||||
if ( config.current.semaphore > 0 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// throw an Error if start is called more often than stop
|
||||
if ( config.current.semaphore < 0 ) {
|
||||
config.current.semaphore = 0;
|
||||
|
||||
QUnit.pushFailure(
|
||||
"Called start() while already started (test's semaphore was 0 already)",
|
||||
sourceFromStacktrace( 2 )
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
resumeProcessing();
|
||||
},
|
||||
|
||||
// DEPRECATED: QUnit.stop() will be removed in QUnit 2.0.
|
||||
stop: function( count ) {
|
||||
|
||||
// If there isn't a test running, don't allow QUnit.stop() to be called
|
||||
if ( !config.current ) {
|
||||
throw new Error( "Called stop() outside of a test context" );
|
||||
}
|
||||
|
||||
// If a test is running, adjust its semaphore
|
||||
config.current.semaphore += count || 1;
|
||||
|
||||
pauseProcessing();
|
||||
},
|
||||
|
||||
config: config,
|
||||
|
||||
// Safe object type checking
|
||||
@ -351,78 +363,65 @@ extend( QUnit, {
|
||||
return undefined;
|
||||
},
|
||||
|
||||
url: function( params ) {
|
||||
params = extend( extend( {}, QUnit.urlParams ), params );
|
||||
var key,
|
||||
querystring = "?";
|
||||
extend: extend,
|
||||
|
||||
for ( key in params ) {
|
||||
if ( hasOwn.call( params, key ) ) {
|
||||
querystring += encodeURIComponent( key ) + "=" +
|
||||
encodeURIComponent( params[ key ] ) + "&";
|
||||
}
|
||||
load: function() {
|
||||
config.pageLoaded = true;
|
||||
|
||||
// Initialize the configuration options
|
||||
extend( config, {
|
||||
stats: { all: 0, bad: 0 },
|
||||
moduleStats: { all: 0, bad: 0 },
|
||||
started: 0,
|
||||
updateRate: 1000,
|
||||
autostart: true,
|
||||
filter: ""
|
||||
}, true );
|
||||
|
||||
config.blocking = false;
|
||||
|
||||
if ( config.autostart ) {
|
||||
resumeProcessing();
|
||||
}
|
||||
return window.location.protocol + "//" + window.location.host +
|
||||
window.location.pathname + querystring.slice( 0, -1 );
|
||||
},
|
||||
|
||||
extend: extend
|
||||
});
|
||||
|
||||
/**
|
||||
* @deprecated: Created for backwards compatibility with test runner that set the hook function
|
||||
* into QUnit.{hook}, instead of invoking it and passing the hook function.
|
||||
* QUnit.constructor is set to the empty F() above so that we can add to it's prototype here.
|
||||
* Doing this allows us to tell if the following methods have been overwritten on the actual
|
||||
* QUnit object.
|
||||
*/
|
||||
extend( QUnit.constructor.prototype, {
|
||||
|
||||
// Logging callbacks; all receive a single argument with the listed properties
|
||||
// run test/logs.html for any related changes
|
||||
begin: registerLoggingCallback( "begin" ),
|
||||
|
||||
// done: { failed, passed, total, runtime }
|
||||
done: registerLoggingCallback( "done" ),
|
||||
|
||||
// log: { result, actual, expected, message }
|
||||
log: registerLoggingCallback( "log" ),
|
||||
|
||||
// testStart: { name }
|
||||
testStart: registerLoggingCallback( "testStart" ),
|
||||
|
||||
// testDone: { name, failed, passed, total, runtime }
|
||||
testDone: registerLoggingCallback( "testDone" ),
|
||||
|
||||
// moduleStart: { name }
|
||||
moduleStart: registerLoggingCallback( "moduleStart" ),
|
||||
|
||||
// moduleDone: { name, failed, passed, total }
|
||||
moduleDone: registerLoggingCallback( "moduleDone" )
|
||||
});
|
||||
|
||||
QUnit.load = function() {
|
||||
runLoggingCallbacks( "begin", {
|
||||
totalTests: Test.count
|
||||
});
|
||||
|
||||
// Initialize the configuration options
|
||||
extend( config, {
|
||||
stats: { all: 0, bad: 0 },
|
||||
moduleStats: { all: 0, bad: 0 },
|
||||
started: 0,
|
||||
updateRate: 1000,
|
||||
autostart: true,
|
||||
filter: "",
|
||||
semaphore: 1
|
||||
}, true );
|
||||
|
||||
config.blocking = false;
|
||||
|
||||
if ( config.autostart ) {
|
||||
QUnit.start();
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
// Register logging callbacks
|
||||
(function() {
|
||||
var i, l, key,
|
||||
callbacks = [ "begin", "done", "log", "testStart", "testDone",
|
||||
"moduleStart", "moduleDone" ];
|
||||
|
||||
function registerLoggingCallback( key ) {
|
||||
var loggingCallback = function( callback ) {
|
||||
if ( QUnit.objectType( callback ) !== "function" ) {
|
||||
throw new Error(
|
||||
"QUnit logging methods require a callback function as their first parameters."
|
||||
);
|
||||
}
|
||||
|
||||
config.callbacks[ key ].push( callback );
|
||||
};
|
||||
|
||||
// DEPRECATED: This will be removed on QUnit 2.0.0+
|
||||
// Stores the registered functions allowing restoring
|
||||
// at verifyLoggingCallbacks() if modified
|
||||
loggingCallbacks[ key ] = loggingCallback;
|
||||
|
||||
return loggingCallback;
|
||||
}
|
||||
|
||||
for ( i = 0, l = callbacks.length; i < l; i++ ) {
|
||||
key = callbacks[ i ];
|
||||
|
||||
// Initialize key collection of logging callback
|
||||
if ( QUnit.objectType( config.callbacks[ key ] ) === "undefined" ) {
|
||||
config.callbacks[ key ] = [];
|
||||
}
|
||||
|
||||
QUnit[ key ] = registerLoggingCallback( key );
|
||||
}
|
||||
})();
|
||||
|
||||
// `onErrorFnPrev` initialized at top of scope
|
||||
// Preserve other handlers
|
||||
@ -448,7 +447,7 @@ window.onerror = function( error, filePath, linerNr ) {
|
||||
} else {
|
||||
QUnit.test( "global failure", extend(function() {
|
||||
QUnit.pushFailure( error, filePath + ":" + linerNr );
|
||||
}, { validTest: validTest } ) );
|
||||
}, { validTest: true } ) );
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -457,21 +456,25 @@ window.onerror = function( error, filePath, linerNr ) {
|
||||
};
|
||||
|
||||
function done() {
|
||||
var runtime, passed;
|
||||
|
||||
config.autorun = true;
|
||||
|
||||
// Log the last module results
|
||||
if ( config.previousModule ) {
|
||||
runLoggingCallbacks( "moduleDone", {
|
||||
name: config.previousModule,
|
||||
name: config.previousModule.name,
|
||||
tests: config.previousModule.tests,
|
||||
failed: config.moduleStats.bad,
|
||||
passed: config.moduleStats.all - config.moduleStats.bad,
|
||||
total: config.moduleStats.all
|
||||
total: config.moduleStats.all,
|
||||
runtime: now() - config.moduleStats.started
|
||||
});
|
||||
}
|
||||
delete config.previousModule;
|
||||
|
||||
var runtime = now() - config.started,
|
||||
passed = config.stats.all - config.stats.bad;
|
||||
runtime = now() - config.started;
|
||||
passed = config.stats.all - config.stats.bad;
|
||||
|
||||
runLoggingCallbacks( "done", {
|
||||
failed: config.stats.bad,
|
||||
@ -481,47 +484,6 @@ function done() {
|
||||
});
|
||||
}
|
||||
|
||||
/** @return Boolean: true if this test should be ran */
|
||||
function validTest( test ) {
|
||||
var include,
|
||||
filter = config.filter && config.filter.toLowerCase(),
|
||||
module = config.module && config.module.toLowerCase(),
|
||||
fullName = ( test.module + ": " + test.testName ).toLowerCase();
|
||||
|
||||
// Internally-generated tests are always valid
|
||||
if ( test.callback && test.callback.validTest === validTest ) {
|
||||
delete test.callback.validTest;
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( config.testNumber.length > 0 ) {
|
||||
if ( inArray( test.testNumber, config.testNumber ) < 0 ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ( module && ( !test.module || test.module.toLowerCase() !== module ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( !filter ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
include = filter.charAt( 0 ) !== "!";
|
||||
if ( !include ) {
|
||||
filter = filter.slice( 1 );
|
||||
}
|
||||
|
||||
// If the filter matches, we need to honour include
|
||||
if ( fullName.indexOf( filter ) !== -1 ) {
|
||||
return include;
|
||||
}
|
||||
|
||||
// Otherwise, do the opposite
|
||||
return !include;
|
||||
}
|
||||
|
||||
// Doesn't support IE6 to IE9
|
||||
// See also https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error/Stack
|
||||
function extractStacktrace( e, offset ) {
|
||||
@ -565,15 +527,27 @@ function extractStacktrace( e, offset ) {
|
||||
return e.sourceURL + ":" + e.line;
|
||||
}
|
||||
}
|
||||
|
||||
function sourceFromStacktrace( offset ) {
|
||||
try {
|
||||
throw new Error();
|
||||
} catch ( e ) {
|
||||
return extractStacktrace( e, offset );
|
||||
var e = new Error();
|
||||
if ( !e.stack ) {
|
||||
try {
|
||||
throw e;
|
||||
} catch ( err ) {
|
||||
// This should already be true in most browsers
|
||||
e = err;
|
||||
}
|
||||
}
|
||||
return extractStacktrace( e, offset );
|
||||
}
|
||||
|
||||
function synchronize( callback, last ) {
|
||||
if ( QUnit.objectType( callback ) === "array" ) {
|
||||
while ( callback.length ) {
|
||||
synchronize( callback.shift() );
|
||||
}
|
||||
return;
|
||||
}
|
||||
config.queue.push( callback );
|
||||
|
||||
if ( config.autorun && !config.blocking ) {
|
||||
@ -586,10 +560,16 @@ function process( last ) {
|
||||
process( last );
|
||||
}
|
||||
var start = now();
|
||||
config.depth = config.depth ? config.depth + 1 : 1;
|
||||
config.depth = ( config.depth || 0 ) + 1;
|
||||
|
||||
while ( config.queue.length && !config.blocking ) {
|
||||
if ( !defined.setTimeout || config.updateRate <= 0 || ( ( now() - start ) < config.updateRate ) ) {
|
||||
if ( !defined.setTimeout || config.updateRate <= 0 ||
|
||||
( ( now() - start ) < config.updateRate ) ) {
|
||||
if ( config.current ) {
|
||||
|
||||
// Reset async tracking for each phase of the Test lifecycle
|
||||
config.current.usedAsync = false;
|
||||
}
|
||||
config.queue.shift()();
|
||||
} else {
|
||||
setTimeout( next, 13 );
|
||||
@ -602,6 +582,79 @@ function process( last ) {
|
||||
}
|
||||
}
|
||||
|
||||
function begin() {
|
||||
var i, l,
|
||||
modulesLog = [];
|
||||
|
||||
// If the test run hasn't officially begun yet
|
||||
if ( !config.started ) {
|
||||
|
||||
// Record the time of the test run's beginning
|
||||
config.started = now();
|
||||
|
||||
verifyLoggingCallbacks();
|
||||
|
||||
// Delete the loose unnamed module if unused.
|
||||
if ( config.modules[ 0 ].name === "" && config.modules[ 0 ].tests.length === 0 ) {
|
||||
config.modules.shift();
|
||||
}
|
||||
|
||||
// Avoid unnecessary information by not logging modules' test environments
|
||||
for ( i = 0, l = config.modules.length; i < l; i++ ) {
|
||||
modulesLog.push({
|
||||
name: config.modules[ i ].name,
|
||||
tests: config.modules[ i ].tests
|
||||
});
|
||||
}
|
||||
|
||||
// The test run is officially beginning now
|
||||
runLoggingCallbacks( "begin", {
|
||||
totalTests: Test.count,
|
||||
modules: modulesLog
|
||||
});
|
||||
}
|
||||
|
||||
config.blocking = false;
|
||||
process( true );
|
||||
}
|
||||
|
||||
function resumeProcessing() {
|
||||
runStarted = true;
|
||||
|
||||
// A slight delay to allow this iteration of the event loop to finish (more assertions, etc.)
|
||||
if ( defined.setTimeout ) {
|
||||
setTimeout(function() {
|
||||
if ( config.current && config.current.semaphore > 0 ) {
|
||||
return;
|
||||
}
|
||||
if ( config.timeout ) {
|
||||
clearTimeout( config.timeout );
|
||||
}
|
||||
|
||||
begin();
|
||||
}, 13 );
|
||||
} else {
|
||||
begin();
|
||||
}
|
||||
}
|
||||
|
||||
function pauseProcessing() {
|
||||
config.blocking = true;
|
||||
|
||||
if ( config.testTimeout && defined.setTimeout ) {
|
||||
clearTimeout( config.timeout );
|
||||
config.timeout = setTimeout(function() {
|
||||
if ( config.current ) {
|
||||
config.current.semaphore = 0;
|
||||
QUnit.pushFailure( "Test timed out", sourceFromStacktrace( 2 ) );
|
||||
} else {
|
||||
throw new Error( "Test timed out" );
|
||||
}
|
||||
resumeProcessing();
|
||||
}, config.testTimeout );
|
||||
}
|
||||
}
|
||||
|
||||
function saveGlobal() {
|
||||
config.pollution = [];
|
||||
|
||||
@ -671,18 +724,6 @@ function extend( a, b, undefOnly ) {
|
||||
return a;
|
||||
}
|
||||
|
||||
function registerLoggingCallback( key ) {
|
||||
|
||||
// Initialize key collection of logging callback
|
||||
if ( QUnit.objectType( config.callbacks[ key ] ) === "undefined" ) {
|
||||
config.callbacks[ key ] = [];
|
||||
}
|
||||
|
||||
return function( callback ) {
|
||||
config.callbacks[ key ].push( callback );
|
||||
};
|
||||
}
|
||||
|
||||
function runLoggingCallbacks( key, args ) {
|
||||
var i, l, callbacks;
|
||||
|
||||
@ -692,6 +733,34 @@ function runLoggingCallbacks( key, args ) {
|
||||
}
|
||||
}
|
||||
|
||||
// DEPRECATED: This will be removed on 2.0.0+
|
||||
// This function verifies if the loggingCallbacks were modified by the user
|
||||
// If so, it will restore it, assign the given callback and print a console warning
|
||||
function verifyLoggingCallbacks() {
|
||||
var loggingCallback, userCallback;
|
||||
|
||||
for ( loggingCallback in loggingCallbacks ) {
|
||||
if ( QUnit[ loggingCallback ] !== loggingCallbacks[ loggingCallback ] ) {
|
||||
|
||||
userCallback = QUnit[ loggingCallback ];
|
||||
|
||||
// Restore the callback function
|
||||
QUnit[ loggingCallback ] = loggingCallbacks[ loggingCallback ];
|
||||
|
||||
// Assign the deprecated given callback
|
||||
QUnit[ loggingCallback ]( userCallback );
|
||||
|
||||
if ( window.console && window.console.warn ) {
|
||||
window.console.warn(
|
||||
"QUnit." + loggingCallback + " was replaced with a new value.\n" +
|
||||
"Please, check out the documentation on how to apply logging callbacks.\n" +
|
||||
"Reference: http://api.qunitjs.com/category/callbacks/"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// from jquery.js
|
||||
function inArray( elem, array ) {
|
||||
if ( array.indexOf ) {
|
||||
@ -708,16 +777,46 @@ function inArray( elem, array ) {
|
||||
}
|
||||
|
||||
function Test( settings ) {
|
||||
var i, l;
|
||||
|
||||
++Test.count;
|
||||
|
||||
extend( this, settings );
|
||||
this.assert = new Assert( this );
|
||||
this.assertions = [];
|
||||
this.testNumber = ++Test.count;
|
||||
this.semaphore = 0;
|
||||
this.usedAsync = false;
|
||||
this.module = config.currentModule;
|
||||
this.stack = sourceFromStacktrace( 3 );
|
||||
|
||||
// Register unique strings
|
||||
for ( i = 0, l = this.module.tests; i < l.length; i++ ) {
|
||||
if ( this.module.tests[ i ].name === this.testName ) {
|
||||
this.testName += " ";
|
||||
}
|
||||
}
|
||||
|
||||
this.testId = generateHash( this.module.name, this.testName );
|
||||
|
||||
this.module.tests.push({
|
||||
name: this.testName,
|
||||
testId: this.testId
|
||||
});
|
||||
|
||||
if ( settings.skip ) {
|
||||
|
||||
// Skipped tests will fully ignore any sent callback
|
||||
this.callback = function() {};
|
||||
this.async = false;
|
||||
this.expected = 0;
|
||||
} else {
|
||||
this.assert = new Assert( this );
|
||||
}
|
||||
}
|
||||
|
||||
Test.count = 0;
|
||||
|
||||
Test.prototype = {
|
||||
setup: function() {
|
||||
before: function() {
|
||||
if (
|
||||
|
||||
// Emit moduleStart when we're switching from one module to another
|
||||
@ -731,47 +830,43 @@ Test.prototype = {
|
||||
) {
|
||||
if ( hasOwn.call( config, "previousModule" ) ) {
|
||||
runLoggingCallbacks( "moduleDone", {
|
||||
name: config.previousModule,
|
||||
name: config.previousModule.name,
|
||||
tests: config.previousModule.tests,
|
||||
failed: config.moduleStats.bad,
|
||||
passed: config.moduleStats.all - config.moduleStats.bad,
|
||||
total: config.moduleStats.all
|
||||
total: config.moduleStats.all,
|
||||
runtime: now() - config.moduleStats.started
|
||||
});
|
||||
}
|
||||
config.previousModule = this.module;
|
||||
config.moduleStats = { all: 0, bad: 0 };
|
||||
config.moduleStats = { all: 0, bad: 0, started: now() };
|
||||
runLoggingCallbacks( "moduleStart", {
|
||||
name: this.module
|
||||
name: this.module.name,
|
||||
tests: this.module.tests
|
||||
});
|
||||
}
|
||||
|
||||
config.current = this;
|
||||
|
||||
this.testEnvironment = extend({
|
||||
setup: function() {},
|
||||
teardown: function() {}
|
||||
}, this.moduleTestEnvironment );
|
||||
this.testEnvironment = extend( {}, this.module.testEnvironment );
|
||||
delete this.testEnvironment.beforeEach;
|
||||
delete this.testEnvironment.afterEach;
|
||||
|
||||
this.started = now();
|
||||
runLoggingCallbacks( "testStart", {
|
||||
name: this.testName,
|
||||
module: this.module,
|
||||
testNumber: this.testNumber
|
||||
module: this.module.name,
|
||||
testId: this.testId
|
||||
});
|
||||
|
||||
if ( !config.pollution ) {
|
||||
saveGlobal();
|
||||
}
|
||||
if ( config.notrycatch ) {
|
||||
this.testEnvironment.setup.call( this.testEnvironment, this.assert );
|
||||
return;
|
||||
}
|
||||
try {
|
||||
this.testEnvironment.setup.call( this.testEnvironment, this.assert );
|
||||
} catch ( e ) {
|
||||
this.pushFailure( "Setup failed on " + this.testName + ": " + ( e.message || e ), extractStacktrace( e, 0 ) );
|
||||
}
|
||||
},
|
||||
|
||||
run: function() {
|
||||
var promise;
|
||||
|
||||
config.current = this;
|
||||
|
||||
if ( this.async ) {
|
||||
@ -781,18 +876,17 @@ Test.prototype = {
|
||||
this.callbackStarted = now();
|
||||
|
||||
if ( config.notrycatch ) {
|
||||
this.callback.call( this.testEnvironment, this.assert );
|
||||
this.callbackRuntime = now() - this.callbackStarted;
|
||||
promise = this.callback.call( this.testEnvironment, this.assert );
|
||||
this.resolvePromise( promise );
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
this.callback.call( this.testEnvironment, this.assert );
|
||||
this.callbackRuntime = now() - this.callbackStarted;
|
||||
promise = this.callback.call( this.testEnvironment, this.assert );
|
||||
this.resolvePromise( promise );
|
||||
} catch ( e ) {
|
||||
this.callbackRuntime = now() - this.callbackStarted;
|
||||
|
||||
this.pushFailure( "Died on test #" + ( this.assertions.length + 1 ) + " " + this.stack + ": " + ( e.message || e ), extractStacktrace( e, 0 ) );
|
||||
this.pushFailure( "Died on test #" + ( this.assertions.length + 1 ) + " " +
|
||||
this.stack + ": " + ( e.message || e ), extractStacktrace( e, 0 ) );
|
||||
|
||||
// else next test will carry the responsibility
|
||||
saveGlobal();
|
||||
@ -803,31 +897,59 @@ Test.prototype = {
|
||||
}
|
||||
}
|
||||
},
|
||||
teardown: function() {
|
||||
config.current = this;
|
||||
if ( config.notrycatch ) {
|
||||
if ( typeof this.callbackRuntime === "undefined" ) {
|
||||
this.callbackRuntime = now() - this.callbackStarted;
|
||||
}
|
||||
this.testEnvironment.teardown.call( this.testEnvironment, this.assert );
|
||||
return;
|
||||
} else {
|
||||
try {
|
||||
this.testEnvironment.teardown.call( this.testEnvironment, this.assert );
|
||||
} catch ( e ) {
|
||||
this.pushFailure( "Teardown failed on " + this.testName + ": " + ( e.message || e ), extractStacktrace( e, 0 ) );
|
||||
}
|
||||
}
|
||||
|
||||
after: function() {
|
||||
checkPollution();
|
||||
},
|
||||
|
||||
queueHook: function( hook, hookName ) {
|
||||
var promise,
|
||||
test = this;
|
||||
return function runHook() {
|
||||
config.current = test;
|
||||
if ( config.notrycatch ) {
|
||||
promise = hook.call( test.testEnvironment, test.assert );
|
||||
test.resolvePromise( promise, hookName );
|
||||
return;
|
||||
}
|
||||
try {
|
||||
promise = hook.call( test.testEnvironment, test.assert );
|
||||
test.resolvePromise( promise, hookName );
|
||||
} catch ( error ) {
|
||||
test.pushFailure( hookName + " failed on " + test.testName + ": " +
|
||||
( error.message || error ), extractStacktrace( error, 0 ) );
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
// Currently only used for module level hooks, can be used to add global level ones
|
||||
hooks: function( handler ) {
|
||||
var hooks = [];
|
||||
|
||||
// Hooks are ignored on skipped tests
|
||||
if ( this.skip ) {
|
||||
return hooks;
|
||||
}
|
||||
|
||||
if ( this.module.testEnvironment &&
|
||||
QUnit.objectType( this.module.testEnvironment[ handler ] ) === "function" ) {
|
||||
hooks.push( this.queueHook( this.module.testEnvironment[ handler ], handler ) );
|
||||
}
|
||||
|
||||
return hooks;
|
||||
},
|
||||
|
||||
finish: function() {
|
||||
config.current = this;
|
||||
if ( config.requireExpects && this.expected === null ) {
|
||||
this.pushFailure( "Expected number of assertions to be defined, but expect() was not called.", this.stack );
|
||||
this.pushFailure( "Expected number of assertions to be defined, but expect() was " +
|
||||
"not called.", this.stack );
|
||||
} else if ( this.expected !== null && this.expected !== this.assertions.length ) {
|
||||
this.pushFailure( "Expected " + this.expected + " assertions, but " + this.assertions.length + " were run", this.stack );
|
||||
this.pushFailure( "Expected " + this.expected + " assertions, but " +
|
||||
this.assertions.length + " were run", this.stack );
|
||||
} else if ( this.expected === null && !this.assertions.length ) {
|
||||
this.pushFailure( "Expected at least one assertion, but none were run - call expect(0) to accept zero assertions.", this.stack );
|
||||
this.pushFailure( "Expected at least one assertion, but none were run - call " +
|
||||
"expect(0) to accept zero assertions.", this.stack );
|
||||
}
|
||||
|
||||
var i,
|
||||
@ -847,7 +969,8 @@ Test.prototype = {
|
||||
|
||||
runLoggingCallbacks( "testDone", {
|
||||
name: this.testName,
|
||||
module: this.module,
|
||||
module: this.module.name,
|
||||
skipped: !!this.skip,
|
||||
failed: bad,
|
||||
passed: this.assertions.length - bad,
|
||||
total: this.assertions.length,
|
||||
@ -855,12 +978,17 @@ Test.prototype = {
|
||||
|
||||
// HTML Reporter use
|
||||
assertions: this.assertions,
|
||||
testNumber: this.testNumber,
|
||||
testId: this.testId,
|
||||
|
||||
// DEPRECATED: this property will be removed in 2.0.0, use runtime instead
|
||||
duration: this.runtime
|
||||
});
|
||||
|
||||
// QUnit.reset() is deprecated and will be replaced for a new
|
||||
// fixture reset function on QUnit 2.0/2.1.
|
||||
// It's still called here for backwards compatibility handling
|
||||
QUnit.reset();
|
||||
|
||||
config.current = undefined;
|
||||
},
|
||||
|
||||
@ -868,26 +996,39 @@ Test.prototype = {
|
||||
var bad,
|
||||
test = this;
|
||||
|
||||
if ( !this.valid() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
function run() {
|
||||
|
||||
// each of these can by async
|
||||
synchronize(function() {
|
||||
test.setup();
|
||||
});
|
||||
synchronize(function() {
|
||||
test.run();
|
||||
});
|
||||
synchronize(function() {
|
||||
test.teardown();
|
||||
});
|
||||
synchronize(function() {
|
||||
test.finish();
|
||||
});
|
||||
synchronize([
|
||||
function() {
|
||||
test.before();
|
||||
},
|
||||
|
||||
test.hooks( "beforeEach" ),
|
||||
|
||||
function() {
|
||||
test.run();
|
||||
},
|
||||
|
||||
test.hooks( "afterEach" ).reverse(),
|
||||
|
||||
function() {
|
||||
test.after();
|
||||
},
|
||||
function() {
|
||||
test.finish();
|
||||
}
|
||||
]);
|
||||
}
|
||||
|
||||
// `bad` initialized at top of scope
|
||||
// defer when previous test run passed, if storage is available
|
||||
bad = QUnit.config.reorder && defined.sessionStorage &&
|
||||
+sessionStorage.getItem( "qunit-test-" + this.module + "-" + this.testName );
|
||||
+sessionStorage.getItem( "qunit-test-" + this.module.name + "-" + this.testName );
|
||||
|
||||
if ( bad ) {
|
||||
run();
|
||||
@ -899,13 +1040,14 @@ Test.prototype = {
|
||||
push: function( result, actual, expected, message ) {
|
||||
var source,
|
||||
details = {
|
||||
module: this.module,
|
||||
module: this.module.name,
|
||||
name: this.testName,
|
||||
result: result,
|
||||
message: message,
|
||||
actual: actual,
|
||||
expected: expected,
|
||||
testNumber: this.testNumber
|
||||
testId: this.testId,
|
||||
runtime: now() - this.started
|
||||
};
|
||||
|
||||
if ( !result ) {
|
||||
@ -926,16 +1068,18 @@ Test.prototype = {
|
||||
|
||||
pushFailure: function( message, source, actual ) {
|
||||
if ( !this instanceof Test ) {
|
||||
throw new Error( "pushFailure() assertion outside test context, was " + sourceFromStacktrace( 2 ) );
|
||||
throw new Error( "pushFailure() assertion outside test context, was " +
|
||||
sourceFromStacktrace( 2 ) );
|
||||
}
|
||||
|
||||
var details = {
|
||||
module: this.module,
|
||||
module: this.module.name,
|
||||
name: this.testName,
|
||||
result: false,
|
||||
message: message || "error",
|
||||
actual: actual || null,
|
||||
testNumber: this.testNumber
|
||||
testId: this.testId,
|
||||
runtime: now() - this.started
|
||||
};
|
||||
|
||||
if ( source ) {
|
||||
@ -948,20 +1092,132 @@ Test.prototype = {
|
||||
result: false,
|
||||
message: message
|
||||
});
|
||||
},
|
||||
|
||||
resolvePromise: function( promise, phase ) {
|
||||
var then, message,
|
||||
test = this;
|
||||
if ( promise != null ) {
|
||||
then = promise.then;
|
||||
if ( QUnit.objectType( then ) === "function" ) {
|
||||
QUnit.stop();
|
||||
then.call(
|
||||
promise,
|
||||
QUnit.start,
|
||||
function( error ) {
|
||||
message = "Promise rejected " +
|
||||
( !phase ? "during" : phase.replace( /Each$/, "" ) ) +
|
||||
" " + test.testName + ": " + ( error.message || error );
|
||||
test.pushFailure( message, extractStacktrace( error, 0 ) );
|
||||
|
||||
// else next test will carry the responsibility
|
||||
saveGlobal();
|
||||
|
||||
// Unblock
|
||||
QUnit.start();
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
valid: function() {
|
||||
var include,
|
||||
filter = config.filter,
|
||||
module = QUnit.urlParams.module && QUnit.urlParams.module.toLowerCase(),
|
||||
fullName = ( this.module.name + ": " + this.testName ).toLowerCase();
|
||||
|
||||
// Internally-generated tests are always valid
|
||||
if ( this.callback && this.callback.validTest ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( config.testId.length > 0 && inArray( this.testId, config.testId ) < 0 ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( module && ( !this.module.name || this.module.name.toLowerCase() !== module ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( !filter ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
include = filter.charAt( 0 ) !== "!";
|
||||
if ( !include ) {
|
||||
filter = filter.toLowerCase().slice( 1 );
|
||||
}
|
||||
|
||||
// If the filter matches, we need to honour include
|
||||
if ( fullName.indexOf( filter ) !== -1 ) {
|
||||
return include;
|
||||
}
|
||||
|
||||
// Otherwise, do the opposite
|
||||
return !include;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// Resets the test setup. Useful for tests that modify the DOM.
|
||||
/*
|
||||
DEPRECATED: Use multiple tests instead of resetting inside a test.
|
||||
Use testStart or testDone for custom cleanup.
|
||||
This method will throw an error in 2.0, and will be removed in 2.1
|
||||
*/
|
||||
QUnit.reset = function() {
|
||||
|
||||
// Return on non-browser environments
|
||||
// This is necessary to not break on node tests
|
||||
if ( typeof window === "undefined" ) {
|
||||
return;
|
||||
}
|
||||
|
||||
var fixture = defined.document && document.getElementById &&
|
||||
document.getElementById( "qunit-fixture" );
|
||||
|
||||
if ( fixture ) {
|
||||
fixture.innerHTML = config.fixture;
|
||||
}
|
||||
};
|
||||
|
||||
QUnit.pushFailure = function() {
|
||||
if ( !QUnit.config.current ) {
|
||||
throw new Error( "pushFailure() assertion outside test context, in " + sourceFromStacktrace( 2 ) );
|
||||
throw new Error( "pushFailure() assertion outside test context, in " +
|
||||
sourceFromStacktrace( 2 ) );
|
||||
}
|
||||
|
||||
// Gets current test obj
|
||||
var currentTest = QUnit.config.current.assert.test;
|
||||
var currentTest = QUnit.config.current;
|
||||
|
||||
return currentTest.pushFailure.apply( currentTest, arguments );
|
||||
};
|
||||
|
||||
// Based on Java's String.hashCode, a simple but not
|
||||
// rigorously collision resistant hashing function
|
||||
function generateHash( module, testName ) {
|
||||
var hex,
|
||||
i = 0,
|
||||
hash = 0,
|
||||
str = module + "\x1C" + testName,
|
||||
len = str.length;
|
||||
|
||||
for ( ; i < len; i++ ) {
|
||||
hash = ( ( hash << 5 ) - hash ) + str.charCodeAt( i );
|
||||
hash |= 0;
|
||||
}
|
||||
|
||||
// Convert the possibly negative integer hash code into an 8 character hex string, which isn't
|
||||
// strictly necessary but increases user understanding that the id is a SHA-like hash
|
||||
hex = ( 0x100000000 + hash ).toString( 16 );
|
||||
if ( hex.length < 8 ) {
|
||||
hex = "0000000" + hex;
|
||||
}
|
||||
|
||||
return hex.slice( -8 );
|
||||
}
|
||||
|
||||
function Assert( testContext ) {
|
||||
this.test = testContext;
|
||||
}
|
||||
@ -969,7 +1225,8 @@ function Assert( testContext ) {
|
||||
// Assert helpers
|
||||
QUnit.assert = Assert.prototype = {
|
||||
|
||||
// Specify the number of expected assertions to guarantee that failed test (no assertions are run at all) don't slip through.
|
||||
// Specify the number of expected assertions to guarantee that failed test
|
||||
// (no assertions are run at all) don't slip through.
|
||||
expect: function( asserts ) {
|
||||
if ( arguments.length === 1 ) {
|
||||
this.test.expected = asserts;
|
||||
@ -978,20 +1235,51 @@ QUnit.assert = Assert.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
// Increment this Test's semaphore counter, then return a single-use function that
|
||||
// decrements that counter a maximum of once.
|
||||
async: function() {
|
||||
var test = this.test,
|
||||
popped = false;
|
||||
|
||||
test.semaphore += 1;
|
||||
test.usedAsync = true;
|
||||
pauseProcessing();
|
||||
|
||||
return function done() {
|
||||
if ( !popped ) {
|
||||
test.semaphore -= 1;
|
||||
popped = true;
|
||||
resumeProcessing();
|
||||
} else {
|
||||
test.pushFailure( "Called the callback returned from `assert.async` more than once",
|
||||
sourceFromStacktrace( 2 ) );
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
// Exports test.push() to the user API
|
||||
push: function() {
|
||||
var assert = this;
|
||||
push: function( /* result, actual, expected, message */ ) {
|
||||
var assert = this,
|
||||
currentTest = ( assert instanceof Assert && assert.test ) || QUnit.config.current;
|
||||
|
||||
// Backwards compatibility fix.
|
||||
// Allows the direct use of global exported assertions and QUnit.assert.*
|
||||
// Although, it's use is not recommended as it can leak assertions
|
||||
// to other tests from async tests, because we only get a reference to the current test,
|
||||
// not exactly the test where assertion were intended to be called.
|
||||
if ( !QUnit.config.current ) {
|
||||
if ( !currentTest ) {
|
||||
throw new Error( "assertion outside test context, in " + sourceFromStacktrace( 2 ) );
|
||||
}
|
||||
|
||||
if ( currentTest.usedAsync === true && currentTest.semaphore === 0 ) {
|
||||
currentTest.pushFailure( "Assertion after the final `assert.async` was resolved",
|
||||
sourceFromStacktrace( 2 ) );
|
||||
|
||||
// Allow this assertion to continue running anyway...
|
||||
}
|
||||
|
||||
if ( !( assert instanceof Assert ) ) {
|
||||
assert = QUnit.config.current.assert;
|
||||
assert = currentTest.assert;
|
||||
}
|
||||
return assert.test.push.apply( assert.test, arguments );
|
||||
},
|
||||
@ -1005,11 +1293,7 @@ QUnit.assert = Assert.prototype = {
|
||||
ok: function( result, message ) {
|
||||
message = message || ( result ? "okay" : "failed, expected argument to be truthy, was: " +
|
||||
QUnit.dump.parse( result ) );
|
||||
if ( !!result ) {
|
||||
this.push( true, result, true, message );
|
||||
} else {
|
||||
this.test.pushFailure( message, null, result );
|
||||
}
|
||||
this.push( !!result, result, true, message );
|
||||
},
|
||||
|
||||
/**
|
||||
@ -1017,7 +1301,7 @@ QUnit.assert = Assert.prototype = {
|
||||
* Prints out both actual and expected values.
|
||||
* @name equal
|
||||
* @function
|
||||
* @example equal( format( "Received {0} bytes.", 2), "Received 2 bytes.", "format() replaces {0} with next argument" );
|
||||
* @example equal( format( "{0} bytes.", 2), "2 bytes.", "replaces {0} with next argument" );
|
||||
*/
|
||||
equal: function( actual, expected, message ) {
|
||||
/*jshint eqeqeq:false */
|
||||
@ -1143,6 +1427,13 @@ QUnit.assert = Assert.prototype = {
|
||||
}
|
||||
};
|
||||
|
||||
// Provide an alternative to assert.throws(), for enviroments that consider throws a reserved word
|
||||
// Known to us are: Closure Compiler, Narwhal
|
||||
(function() {
|
||||
/*jshint sub:true */
|
||||
Assert.prototype.raises = Assert.prototype[ "throws" ];
|
||||
}());
|
||||
|
||||
// Test for equality any JavaScript type.
|
||||
// Author: Philippe Rathé <prathe@gmail.com>
|
||||
QUnit.equiv = (function() {
|
||||
@ -1356,7 +1647,8 @@ QUnit.equiv = (function() {
|
||||
}
|
||||
|
||||
// apply transition with (1..n) arguments
|
||||
}( args[ 0 ], args[ 1 ] ) ) && innerEquiv.apply( this, args.splice( 1, args.length - 1 ) ) );
|
||||
}( args[ 0 ], args[ 1 ] ) ) &&
|
||||
innerEquiv.apply( this, args.splice( 1, args.length - 1 ) ) );
|
||||
};
|
||||
|
||||
return innerEquiv;
|
||||
@ -1386,6 +1678,11 @@ QUnit.dump = (function() {
|
||||
function array( arr, stack ) {
|
||||
var i = arr.length,
|
||||
ret = new Array( i );
|
||||
|
||||
if ( dump.maxDepth && dump.depth > dump.maxDepth ) {
|
||||
return "[object Array]";
|
||||
}
|
||||
|
||||
this.up();
|
||||
while ( i-- ) {
|
||||
ret[ i ] = this.parse( arr[ i ], undefined, stack );
|
||||
@ -1396,25 +1693,28 @@ QUnit.dump = (function() {
|
||||
|
||||
var reName = /^function (\w+)/,
|
||||
dump = {
|
||||
// type is used mostly internally, you can fix a (custom)type in advance
|
||||
parse: function( obj, type, stack ) {
|
||||
stack = stack || [];
|
||||
var inStack, res,
|
||||
parser = this.parsers[ type || this.typeOf( obj ) ];
|
||||
|
||||
type = typeof parser;
|
||||
inStack = inArray( obj, stack );
|
||||
// objType is used mostly internally, you can fix a (custom) type in advance
|
||||
parse: function( obj, objType, stack ) {
|
||||
stack = stack || [];
|
||||
var res, parser, parserType,
|
||||
inStack = inArray( obj, stack );
|
||||
|
||||
if ( inStack !== -1 ) {
|
||||
return "recursion(" + ( inStack - stack.length ) + ")";
|
||||
}
|
||||
if ( type === "function" ) {
|
||||
|
||||
objType = objType || this.typeOf( obj );
|
||||
parser = this.parsers[ objType ];
|
||||
parserType = typeof parser;
|
||||
|
||||
if ( parserType === "function" ) {
|
||||
stack.push( obj );
|
||||
res = parser.call( this, obj, stack );
|
||||
stack.pop();
|
||||
return res;
|
||||
}
|
||||
return ( type === "string" ) ? parser : this.parsers.error;
|
||||
return ( parserType === "string" ) ? parser : this.parsers.error;
|
||||
},
|
||||
typeOf: function( obj ) {
|
||||
var type;
|
||||
@ -1428,7 +1728,9 @@ QUnit.dump = (function() {
|
||||
type = "date";
|
||||
} else if ( QUnit.is( "function", obj ) ) {
|
||||
type = "function";
|
||||
} else if ( typeof obj.setInterval !== undefined && typeof obj.document !== "undefined" && typeof obj.nodeType === "undefined" ) {
|
||||
} else if ( obj.setInterval !== undefined &&
|
||||
obj.document !== undefined &&
|
||||
obj.nodeType === undefined ) {
|
||||
type = "window";
|
||||
} else if ( obj.nodeType === 9 ) {
|
||||
type = "document";
|
||||
@ -1440,7 +1742,9 @@ QUnit.dump = (function() {
|
||||
toString.call( obj ) === "[object Array]" ||
|
||||
|
||||
// NodeList objects
|
||||
( typeof obj.length === "number" && typeof obj.item !== "undefined" && ( obj.length ? obj.item( 0 ) === obj[ 0 ] : ( obj.item( 0 ) === null && typeof obj[ 0 ] === "undefined" ) ) )
|
||||
( typeof obj.length === "number" && obj.item !== undefined &&
|
||||
( obj.length ? obj.item( 0 ) === obj[ 0 ] : ( obj.item( 0 ) === null &&
|
||||
obj[ 0 ] === undefined ) ) )
|
||||
) {
|
||||
type = "array";
|
||||
} else if ( obj.constructor === Error.prototype.constructor ) {
|
||||
@ -1451,7 +1755,7 @@ QUnit.dump = (function() {
|
||||
return type;
|
||||
},
|
||||
separator: function() {
|
||||
return this.multiline ? this.HTML ? "<br />" : "\n" : this.HTML ? " " : " ";
|
||||
return this.multiline ? this.HTML ? "<br />" : "\n" : this.HTML ? " " : " ";
|
||||
},
|
||||
// extra can be a number, shortcut for increasing-calling-decreasing
|
||||
indent: function( extra ) {
|
||||
@ -1460,7 +1764,7 @@ QUnit.dump = (function() {
|
||||
}
|
||||
var chr = this.indentChar;
|
||||
if ( this.HTML ) {
|
||||
chr = chr.replace( /\t/g, " " ).replace( / /g, " " );
|
||||
chr = chr.replace( /\t/g, " " ).replace( / /g, " " );
|
||||
}
|
||||
return new Array( this.depth + ( extra || 0 ) ).join( chr );
|
||||
},
|
||||
@ -1479,6 +1783,8 @@ QUnit.dump = (function() {
|
||||
join: join,
|
||||
//
|
||||
depth: 1,
|
||||
maxDepth: 5,
|
||||
|
||||
// This is the list of parsers, to modify them, use dump.setParser
|
||||
parsers: {
|
||||
window: "[Window]",
|
||||
@ -1491,6 +1797,7 @@ QUnit.dump = (function() {
|
||||
"undefined": "undefined",
|
||||
"function": function( fn ) {
|
||||
var ret = "function",
|
||||
|
||||
// functions never have name in IE
|
||||
name = "name" in fn ? fn.name : ( reName.exec( fn ) || [] )[ 1 ];
|
||||
|
||||
@ -1506,8 +1813,13 @@ QUnit.dump = (function() {
|
||||
nodelist: array,
|
||||
"arguments": array,
|
||||
object: function( map, stack ) {
|
||||
/*jshint forin:false */
|
||||
var ret = [], keys, key, val, i, nonEnumerableProperties;
|
||||
var keys, key, val, i, nonEnumerableProperties,
|
||||
ret = [];
|
||||
|
||||
if ( dump.maxDepth && dump.depth > dump.maxDepth ) {
|
||||
return "[object Object]";
|
||||
}
|
||||
|
||||
dump.up();
|
||||
keys = [];
|
||||
for ( key in map ) {
|
||||
@ -1526,7 +1838,8 @@ QUnit.dump = (function() {
|
||||
for ( i = 0; i < keys.length; i++ ) {
|
||||
key = keys[ i ];
|
||||
val = map[ key ];
|
||||
ret.push( dump.parse( key, "key" ) + ": " + dump.parse( val, undefined, stack ) );
|
||||
ret.push( dump.parse( key, "key" ) + ": " +
|
||||
dump.parse( val, undefined, stack ) );
|
||||
}
|
||||
dump.down();
|
||||
return join( "{", ret, "}" );
|
||||
@ -1543,10 +1856,12 @@ QUnit.dump = (function() {
|
||||
for ( i = 0, len = attrs.length; i < len; i++ ) {
|
||||
val = attrs[ i ].nodeValue;
|
||||
|
||||
// IE6 includes all attributes in .attributes, even ones not explicitly set.
|
||||
// Those have values like undefined, null, 0, false, "" or "inherit".
|
||||
// IE6 includes all attributes in .attributes, even ones not explicitly
|
||||
// set. Those have values like undefined, null, 0, false, "" or
|
||||
// "inherit".
|
||||
if ( val && val !== "inherit" ) {
|
||||
ret += " " + attrs[ i ].nodeName + "=" + dump.parse( val, "attribute" );
|
||||
ret += " " + attrs[ i ].nodeName + "=" +
|
||||
dump.parse( val, "attribute" );
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1653,9 +1968,17 @@ if ( typeof window !== "undefined" ) {
|
||||
window.QUnit = QUnit;
|
||||
}
|
||||
|
||||
// For CommonJS environments, export everything
|
||||
if ( typeof module !== "undefined" && module.exports ) {
|
||||
// For nodejs
|
||||
if ( typeof module !== "undefined" && module && module.exports ) {
|
||||
module.exports = QUnit;
|
||||
|
||||
// For consistency with CommonJS environments' exports
|
||||
module.exports.QUnit = QUnit;
|
||||
}
|
||||
|
||||
// For CommonJS with exports, but without module.exports, like Rhino
|
||||
if ( typeof exports !== "undefined" && exports ) {
|
||||
exports.QUnit = QUnit;
|
||||
}
|
||||
|
||||
// Get a reference to the global object, like window in browsers
|
||||
@ -1664,6 +1987,7 @@ if ( typeof module !== "undefined" && module.exports ) {
|
||||
})() ));
|
||||
|
||||
/*istanbul ignore next */
|
||||
// jscs:disable maximumLineLength
|
||||
/*
|
||||
* Javascript Diff Algorithm
|
||||
* By John Resig (http://ejohn.org/)
|
||||
@ -1810,6 +2134,7 @@ QUnit.diff = (function() {
|
||||
return str;
|
||||
};
|
||||
}());
|
||||
// jscs:enable
|
||||
|
||||
(function() {
|
||||
|
||||
@ -1828,7 +2153,6 @@ QUnit.init = function() {
|
||||
config.autorun = false;
|
||||
config.filter = "";
|
||||
config.queue = [];
|
||||
config.semaphore = 1;
|
||||
|
||||
// Return on non-browser environments
|
||||
// This is necessary to not break on node tests
|
||||
@ -1867,27 +2191,7 @@ QUnit.init = function() {
|
||||
result.id = "qunit-testresult";
|
||||
result.className = "result";
|
||||
tests.parentNode.insertBefore( result, tests );
|
||||
result.innerHTML = "Running...<br/> ";
|
||||
}
|
||||
};
|
||||
|
||||
// Resets the test setup. Useful for tests that modify the DOM.
|
||||
/*
|
||||
DEPRECATED: Use multiple tests instead of resetting inside a test.
|
||||
Use testStart or testDone for custom cleanup.
|
||||
This method will throw an error in 2.0, and will be removed in 2.1
|
||||
*/
|
||||
QUnit.reset = function() {
|
||||
|
||||
// Return on non-browser environments
|
||||
// This is necessary to not break on node tests
|
||||
if ( typeof window === "undefined" ) {
|
||||
return;
|
||||
}
|
||||
|
||||
var fixture = id( "qunit-fixture" );
|
||||
if ( fixture ) {
|
||||
fixture.innerHTML = config.fixture;
|
||||
result.innerHTML = "Running...<br /> ";
|
||||
}
|
||||
};
|
||||
|
||||
@ -1899,7 +2203,7 @@ if ( typeof window === "undefined" ) {
|
||||
var config = QUnit.config,
|
||||
hasOwn = Object.prototype.hasOwnProperty,
|
||||
defined = {
|
||||
document: typeof window.document !== "undefined",
|
||||
document: window.document !== undefined,
|
||||
sessionStorage: (function() {
|
||||
var x = "qunit-test-string";
|
||||
try {
|
||||
@ -1910,7 +2214,8 @@ var config = QUnit.config,
|
||||
return false;
|
||||
}
|
||||
}())
|
||||
};
|
||||
},
|
||||
modulesList = [];
|
||||
|
||||
/**
|
||||
* Escape text for attribute or text content.
|
||||
@ -2020,13 +2325,16 @@ function getUrlConfigHtml() {
|
||||
escaped = escapeText( val.id );
|
||||
escapedTooltip = escapeText( val.tooltip );
|
||||
|
||||
config[ val.id ] = QUnit.urlParams[ val.id ];
|
||||
if ( config[ val.id ] === undefined ) {
|
||||
config[ val.id ] = QUnit.urlParams[ val.id ];
|
||||
}
|
||||
|
||||
if ( !val.value || typeof val.value === "string" ) {
|
||||
urlConfigHtml += "<input id='qunit-urlconfig-" + escaped +
|
||||
"' name='" + escaped + "' type='checkbox'" +
|
||||
( val.value ? " value='" + escapeText( val.value ) + "'" : "" ) +
|
||||
( config[ val.id ] ? " checked='checked'" : "" ) +
|
||||
" title='" + escapedTooltip + "'><label for='qunit-urlconfig-" + escaped +
|
||||
" title='" + escapedTooltip + "' /><label for='qunit-urlconfig-" + escaped +
|
||||
"' title='" + escapedTooltip + "'>" + val.label + "</label>";
|
||||
} else {
|
||||
urlConfigHtml += "<label for='qunit-urlconfig-" + escaped +
|
||||
@ -2064,69 +2372,145 @@ function getUrlConfigHtml() {
|
||||
return urlConfigHtml;
|
||||
}
|
||||
|
||||
// Handle "click" events on toolbar checkboxes and "change" for select menus.
|
||||
// Updates the URL with the new state of `config.urlConfig` values.
|
||||
function toolbarChanged() {
|
||||
var updatedUrl, value,
|
||||
field = this,
|
||||
params = {};
|
||||
|
||||
// Detect if field is a select menu or a checkbox
|
||||
if ( "selectedIndex" in field ) {
|
||||
value = field.options[ field.selectedIndex ].value || undefined;
|
||||
} else {
|
||||
value = field.checked ? ( field.defaultValue || true ) : undefined;
|
||||
}
|
||||
|
||||
params[ field.name ] = value;
|
||||
updatedUrl = setUrl( params );
|
||||
|
||||
if ( "hidepassed" === field.name && "replaceState" in window.history ) {
|
||||
config[ field.name ] = value || false;
|
||||
if ( value ) {
|
||||
addClass( id( "qunit-tests" ), "hidepass" );
|
||||
} else {
|
||||
removeClass( id( "qunit-tests" ), "hidepass" );
|
||||
}
|
||||
|
||||
// It is not necessary to refresh the whole page
|
||||
window.history.replaceState( null, "", updatedUrl );
|
||||
} else {
|
||||
window.location = updatedUrl;
|
||||
}
|
||||
}
|
||||
|
||||
function setUrl( params ) {
|
||||
var key,
|
||||
querystring = "?";
|
||||
|
||||
params = QUnit.extend( QUnit.extend( {}, QUnit.urlParams ), params );
|
||||
|
||||
for ( key in params ) {
|
||||
if ( hasOwn.call( params, key ) ) {
|
||||
if ( params[ key ] === undefined ) {
|
||||
continue;
|
||||
}
|
||||
querystring += encodeURIComponent( key );
|
||||
if ( params[ key ] !== true ) {
|
||||
querystring += "=" + encodeURIComponent( params[ key ] );
|
||||
}
|
||||
querystring += "&";
|
||||
}
|
||||
}
|
||||
return location.protocol + "//" + location.host +
|
||||
location.pathname + querystring.slice( 0, -1 );
|
||||
}
|
||||
|
||||
function applyUrlParams() {
|
||||
var selectBox = id( "qunit-modulefilter" ),
|
||||
selection = decodeURIComponent( selectBox.options[ selectBox.selectedIndex ].value ),
|
||||
filter = id( "qunit-filter-input" ).value;
|
||||
|
||||
window.location = setUrl({
|
||||
module: ( selection === "" ) ? undefined : selection,
|
||||
filter: ( filter === "" ) ? undefined : filter,
|
||||
|
||||
// Remove testId filter
|
||||
testId: undefined
|
||||
});
|
||||
}
|
||||
|
||||
function toolbarUrlConfigContainer() {
|
||||
var urlConfigContainer = document.createElement( "span" );
|
||||
|
||||
urlConfigContainer.innerHTML = getUrlConfigHtml();
|
||||
addClass( urlConfigContainer, "qunit-url-config" );
|
||||
|
||||
// For oldIE support:
|
||||
// * Add handlers to the individual elements instead of the container
|
||||
// * Use "click" instead of "change" for checkboxes
|
||||
// * Fallback from event.target to event.srcElement
|
||||
addEvents( urlConfigContainer.getElementsByTagName( "input" ), "click", function( event ) {
|
||||
var params = {},
|
||||
target = event.target || event.srcElement;
|
||||
params[ target.name ] = target.checked ?
|
||||
target.defaultValue || true :
|
||||
undefined;
|
||||
window.location = QUnit.url( params );
|
||||
});
|
||||
addEvents( urlConfigContainer.getElementsByTagName( "select" ), "change", function( event ) {
|
||||
var params = {},
|
||||
target = event.target || event.srcElement;
|
||||
params[ target.name ] = target.options[ target.selectedIndex ].value || undefined;
|
||||
window.location = QUnit.url( params );
|
||||
});
|
||||
addEvents( urlConfigContainer.getElementsByTagName( "input" ), "click", toolbarChanged );
|
||||
addEvents( urlConfigContainer.getElementsByTagName( "select" ), "change", toolbarChanged );
|
||||
|
||||
return urlConfigContainer;
|
||||
}
|
||||
|
||||
function getModuleNames() {
|
||||
var i,
|
||||
moduleNames = [];
|
||||
function toolbarLooseFilter() {
|
||||
var filter = document.createElement( "form" ),
|
||||
label = document.createElement( "label" ),
|
||||
input = document.createElement( "input" ),
|
||||
button = document.createElement( "button" );
|
||||
|
||||
for ( i in config.modules ) {
|
||||
if ( config.modules.hasOwnProperty( i ) ) {
|
||||
moduleNames.push( i );
|
||||
addClass( filter, "qunit-filter" );
|
||||
|
||||
label.innerHTML = "Filter: ";
|
||||
|
||||
input.type = "text";
|
||||
input.value = config.filter || "";
|
||||
input.name = "filter";
|
||||
input.id = "qunit-filter-input";
|
||||
|
||||
button.innerHTML = "Go";
|
||||
|
||||
label.appendChild( input );
|
||||
|
||||
filter.appendChild( label );
|
||||
filter.appendChild( button );
|
||||
addEvent( filter, "submit", function( ev ) {
|
||||
applyUrlParams();
|
||||
|
||||
if ( ev && ev.preventDefault ) {
|
||||
ev.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
moduleNames.sort(function( a, b ) {
|
||||
return a.localeCompare( b );
|
||||
return false;
|
||||
});
|
||||
|
||||
return moduleNames;
|
||||
return filter;
|
||||
}
|
||||
|
||||
function toolbarModuleFilterHtml() {
|
||||
var i,
|
||||
moduleFilterHtml = "",
|
||||
moduleNames = getModuleNames();
|
||||
moduleFilterHtml = "";
|
||||
|
||||
if ( moduleNames.length <= 1 ) {
|
||||
if ( !modulesList.length ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
modulesList.sort(function( a, b ) {
|
||||
return a.localeCompare( b );
|
||||
});
|
||||
|
||||
moduleFilterHtml += "<label for='qunit-modulefilter'>Module: </label>" +
|
||||
"<select id='qunit-modulefilter' name='modulefilter'><option value='' " +
|
||||
( config.module === undefined ? "selected='selected'" : "" ) +
|
||||
( QUnit.urlParams.module === undefined ? "selected='selected'" : "" ) +
|
||||
">< All Modules ></option>";
|
||||
|
||||
for ( i = 0; i < moduleNames.length; i++ ) {
|
||||
for ( i = 0; i < modulesList.length; i++ ) {
|
||||
moduleFilterHtml += "<option value='" +
|
||||
escapeText( encodeURIComponent( moduleNames[ i ] ) ) + "' " +
|
||||
( config.module === moduleNames[ i ] ? "selected='selected'" : "" ) +
|
||||
">" + escapeText( moduleNames[ i ] ) + "</option>";
|
||||
escapeText( encodeURIComponent( modulesList[ i ] ) ) + "' " +
|
||||
( QUnit.urlParams.module === modulesList[ i ] ? "selected='selected'" : "" ) +
|
||||
">" + escapeText( modulesList[ i ] ) + "</option>";
|
||||
}
|
||||
moduleFilterHtml += "</select>";
|
||||
|
||||
@ -2134,7 +2518,8 @@ function toolbarModuleFilterHtml() {
|
||||
}
|
||||
|
||||
function toolbarModuleFilter() {
|
||||
var moduleFilter = document.createElement( "span" ),
|
||||
var toolbar = id( "qunit-testrunner-toolbar" ),
|
||||
moduleFilter = document.createElement( "span" ),
|
||||
moduleFilterHtml = toolbarModuleFilterHtml();
|
||||
|
||||
if ( !moduleFilterHtml ) {
|
||||
@ -2144,75 +2529,27 @@ function toolbarModuleFilter() {
|
||||
moduleFilter.setAttribute( "id", "qunit-modulefilter-container" );
|
||||
moduleFilter.innerHTML = moduleFilterHtml;
|
||||
|
||||
addEvent( moduleFilter.lastChild, "change", function() {
|
||||
var selectBox = moduleFilter.getElementsByTagName( "select" )[ 0 ],
|
||||
selectedModule = decodeURIComponent( selectBox.options[ selectBox.selectedIndex ].value );
|
||||
addEvent( moduleFilter.lastChild, "change", applyUrlParams );
|
||||
|
||||
window.location = QUnit.url({
|
||||
module: ( selectedModule === "" ) ? undefined : selectedModule,
|
||||
|
||||
// Remove any existing filters
|
||||
filter: undefined,
|
||||
testNumber: undefined
|
||||
});
|
||||
});
|
||||
|
||||
return moduleFilter;
|
||||
}
|
||||
|
||||
function toolbarFilter() {
|
||||
var testList = id( "qunit-tests" ),
|
||||
filter = document.createElement( "input" );
|
||||
|
||||
filter.type = "checkbox";
|
||||
filter.id = "qunit-filter-pass";
|
||||
|
||||
addEvent( filter, "click", function() {
|
||||
if ( filter.checked ) {
|
||||
addClass( testList, "hidepass" );
|
||||
if ( defined.sessionStorage ) {
|
||||
sessionStorage.setItem( "qunit-filter-passed-tests", "true" );
|
||||
}
|
||||
} else {
|
||||
removeClass( testList, "hidepass" );
|
||||
if ( defined.sessionStorage ) {
|
||||
sessionStorage.removeItem( "qunit-filter-passed-tests" );
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if ( config.hidepassed || defined.sessionStorage &&
|
||||
sessionStorage.getItem( "qunit-filter-passed-tests" ) ) {
|
||||
filter.checked = true;
|
||||
|
||||
addClass( testList, "hidepass" );
|
||||
}
|
||||
|
||||
return filter;
|
||||
}
|
||||
|
||||
function toolbarLabel() {
|
||||
var label = document.createElement( "label" );
|
||||
label.setAttribute( "for", "qunit-filter-pass" );
|
||||
label.setAttribute( "title", "Only show tests and assertions that fail. Stored in sessionStorage." );
|
||||
label.innerHTML = "Hide passed tests";
|
||||
|
||||
return label;
|
||||
toolbar.appendChild( moduleFilter );
|
||||
}
|
||||
|
||||
function appendToolbar() {
|
||||
var moduleFilter,
|
||||
toolbar = id( "qunit-testrunner-toolbar" );
|
||||
var toolbar = id( "qunit-testrunner-toolbar" );
|
||||
|
||||
if ( toolbar ) {
|
||||
toolbar.appendChild( toolbarFilter() );
|
||||
toolbar.appendChild( toolbarLabel() );
|
||||
toolbar.appendChild( toolbarUrlConfigContainer() );
|
||||
toolbar.appendChild( toolbarLooseFilter() );
|
||||
}
|
||||
}
|
||||
|
||||
moduleFilter = toolbarModuleFilter();
|
||||
if ( moduleFilter ) {
|
||||
toolbar.appendChild( moduleFilter );
|
||||
}
|
||||
function appendHeader() {
|
||||
var header = id( "qunit-header" );
|
||||
|
||||
if ( header ) {
|
||||
header.innerHTML = "<a href='" +
|
||||
setUrl({ filter: undefined, module: undefined, testId: undefined }) +
|
||||
"'>" + header.innerHTML + "</a> ";
|
||||
}
|
||||
}
|
||||
|
||||
@ -2221,9 +2558,6 @@ function appendBanner() {
|
||||
|
||||
if ( banner ) {
|
||||
banner.className = "";
|
||||
banner.innerHTML = "<a href='" +
|
||||
QUnit.url({ filter: undefined, module: undefined, testNumber: undefined }) +
|
||||
"'>" + banner.innerHTML + "</a> ";
|
||||
}
|
||||
}
|
||||
|
||||
@ -2241,7 +2575,7 @@ function appendTestResults() {
|
||||
result.id = "qunit-testresult";
|
||||
result.className = "result";
|
||||
tests.parentNode.insertBefore( result, tests );
|
||||
result.innerHTML = "Running...<br> ";
|
||||
result.innerHTML = "Running...<br /> ";
|
||||
}
|
||||
}
|
||||
|
||||
@ -2255,28 +2589,84 @@ function storeFixture() {
|
||||
function appendUserAgent() {
|
||||
var userAgent = id( "qunit-userAgent" );
|
||||
if ( userAgent ) {
|
||||
userAgent.innerHTML = navigator.userAgent;
|
||||
userAgent.innerHTML = "";
|
||||
userAgent.appendChild( document.createTextNode( navigator.userAgent ) );
|
||||
}
|
||||
}
|
||||
|
||||
function appendTestsList( modules ) {
|
||||
var i, l, x, z, test, moduleObj;
|
||||
|
||||
for ( i = 0, l = modules.length; i < l; i++ ) {
|
||||
moduleObj = modules[ i ];
|
||||
|
||||
if ( moduleObj.name ) {
|
||||
modulesList.push( moduleObj.name );
|
||||
}
|
||||
|
||||
for ( x = 0, z = moduleObj.tests.length; x < z; x++ ) {
|
||||
test = moduleObj.tests[ x ];
|
||||
|
||||
appendTest( test.name, test.testId, moduleObj.name );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function appendTest( name, testId, moduleName ) {
|
||||
var title, rerunTrigger, testBlock, assertList,
|
||||
tests = id( "qunit-tests" );
|
||||
|
||||
if ( !tests ) {
|
||||
return;
|
||||
}
|
||||
|
||||
title = document.createElement( "strong" );
|
||||
title.innerHTML = getNameHtml( name, moduleName );
|
||||
|
||||
rerunTrigger = document.createElement( "a" );
|
||||
rerunTrigger.innerHTML = "Rerun";
|
||||
rerunTrigger.href = setUrl({ testId: testId });
|
||||
|
||||
testBlock = document.createElement( "li" );
|
||||
testBlock.appendChild( title );
|
||||
testBlock.appendChild( rerunTrigger );
|
||||
testBlock.id = "qunit-test-output-" + testId;
|
||||
|
||||
assertList = document.createElement( "ol" );
|
||||
assertList.className = "qunit-assert-list";
|
||||
|
||||
testBlock.appendChild( assertList );
|
||||
|
||||
tests.appendChild( testBlock );
|
||||
}
|
||||
|
||||
// HTML Reporter initialization and load
|
||||
QUnit.begin(function() {
|
||||
QUnit.begin(function( details ) {
|
||||
var qunit = id( "qunit" );
|
||||
|
||||
// Fixture is the only one necessary to run without the #qunit element
|
||||
storeFixture();
|
||||
|
||||
if ( qunit ) {
|
||||
qunit.innerHTML =
|
||||
"<h1 id='qunit-header'>" + escapeText( document.title ) + "</h1>" +
|
||||
"<h2 id='qunit-banner'></h2>" +
|
||||
"<div id='qunit-testrunner-toolbar'></div>" +
|
||||
"<h2 id='qunit-userAgent'></h2>" +
|
||||
"<ol id='qunit-tests'></ol>";
|
||||
"<h1 id='qunit-header'>" + escapeText( document.title ) + "</h1>" +
|
||||
"<h2 id='qunit-banner'></h2>" +
|
||||
"<div id='qunit-testrunner-toolbar'></div>" +
|
||||
"<h2 id='qunit-userAgent'></h2>" +
|
||||
"<ol id='qunit-tests'></ol>";
|
||||
}
|
||||
|
||||
appendHeader();
|
||||
appendBanner();
|
||||
appendTestResults();
|
||||
appendUserAgent();
|
||||
appendToolbar();
|
||||
storeFixture();
|
||||
appendTestsList( details.modules );
|
||||
toolbarModuleFilter();
|
||||
|
||||
if ( qunit && config.hidepassed ) {
|
||||
addClass( qunit.lastChild, "hidepass" );
|
||||
}
|
||||
});
|
||||
|
||||
QUnit.done(function( details ) {
|
||||
@ -2286,7 +2676,7 @@ QUnit.done(function( details ) {
|
||||
html = [
|
||||
"Tests completed in ",
|
||||
details.runtime,
|
||||
" milliseconds.<br>",
|
||||
" milliseconds.<br />",
|
||||
"<span class='passed'>",
|
||||
details.passed,
|
||||
"</span> assertions of <span class='total'>",
|
||||
@ -2343,35 +2733,20 @@ function getNameHtml( name, module ) {
|
||||
}
|
||||
|
||||
QUnit.testStart(function( details ) {
|
||||
var a, b, li, running, assertList,
|
||||
name = getNameHtml( details.name, details.module ),
|
||||
tests = id( "qunit-tests" );
|
||||
var running, testBlock;
|
||||
|
||||
if ( tests ) {
|
||||
b = document.createElement( "strong" );
|
||||
b.innerHTML = name;
|
||||
testBlock = id( "qunit-test-output-" + details.testId );
|
||||
if ( testBlock ) {
|
||||
testBlock.className = "running";
|
||||
} else {
|
||||
|
||||
a = document.createElement( "a" );
|
||||
a.innerHTML = "Rerun";
|
||||
a.href = QUnit.url({ testNumber: details.testNumber });
|
||||
|
||||
li = document.createElement( "li" );
|
||||
li.appendChild( b );
|
||||
li.appendChild( a );
|
||||
li.className = "running";
|
||||
li.id = "qunit-test-output" + details.testNumber;
|
||||
|
||||
assertList = document.createElement( "ol" );
|
||||
assertList.className = "qunit-assert-list";
|
||||
|
||||
li.appendChild( assertList );
|
||||
|
||||
tests.appendChild( li );
|
||||
// Report later registered tests
|
||||
appendTest( details.name, details.testId, details.module );
|
||||
}
|
||||
|
||||
running = id( "qunit-testresult" );
|
||||
if ( running ) {
|
||||
running.innerHTML = "Running: <br>" + name;
|
||||
running.innerHTML = "Running: <br />" + getNameHtml( details.name, details.module );
|
||||
}
|
||||
|
||||
});
|
||||
@ -2379,7 +2754,7 @@ QUnit.testStart(function( details ) {
|
||||
QUnit.log(function( details ) {
|
||||
var assertList, assertLi,
|
||||
message, expected, actual,
|
||||
testItem = id( "qunit-test-output" + details.testNumber );
|
||||
testItem = id( "qunit-test-output-" + details.testId );
|
||||
|
||||
if ( !testItem ) {
|
||||
return;
|
||||
@ -2387,6 +2762,7 @@ QUnit.log(function( details ) {
|
||||
|
||||
message = escapeText( details.message ) || ( details.result ? "okay" : "failed" );
|
||||
message = "<span class='test-message'>" + message + "</span>";
|
||||
message += "<span class='runtime'>@ " + details.runtime + " ms</span>";
|
||||
|
||||
// pushFailure doesn't provide details.expected
|
||||
// when it calls, it's implicit to also not show expected and diff stuff
|
||||
@ -2430,19 +2806,15 @@ QUnit.log(function( details ) {
|
||||
|
||||
QUnit.testDone(function( details ) {
|
||||
var testTitle, time, testItem, assertList,
|
||||
good, bad, testCounts,
|
||||
good, bad, testCounts, skipped,
|
||||
tests = id( "qunit-tests" );
|
||||
|
||||
// QUnit.reset() is deprecated and will be replaced for a new
|
||||
// fixture reset function on QUnit 2.0/2.1.
|
||||
// It's still called here for backwards compatibility handling
|
||||
QUnit.reset();
|
||||
|
||||
if ( !tests ) {
|
||||
return;
|
||||
}
|
||||
|
||||
testItem = id( "qunit-test-output" + details.testNumber );
|
||||
testItem = id( "qunit-test-output-" + details.testId );
|
||||
|
||||
assertList = testItem.getElementsByTagName( "ol" )[ 0 ];
|
||||
|
||||
good = details.passed;
|
||||
@ -2471,20 +2843,28 @@ QUnit.testDone(function( details ) {
|
||||
testTitle.innerHTML += " <b class='counts'>(" + testCounts +
|
||||
details.assertions.length + ")</b>";
|
||||
|
||||
addEvent( testTitle, "click", function() {
|
||||
toggleClass( assertList, "qunit-collapsed" );
|
||||
});
|
||||
if ( details.skipped ) {
|
||||
testItem.className = "skipped";
|
||||
skipped = document.createElement( "em" );
|
||||
skipped.className = "qunit-skipped-label";
|
||||
skipped.innerHTML = "skipped";
|
||||
testItem.insertBefore( skipped, testTitle );
|
||||
} else {
|
||||
addEvent( testTitle, "click", function() {
|
||||
toggleClass( assertList, "qunit-collapsed" );
|
||||
});
|
||||
|
||||
time = document.createElement( "span" );
|
||||
time.className = "runtime";
|
||||
time.innerHTML = details.runtime + " ms";
|
||||
testItem.className = bad ? "fail" : "pass";
|
||||
|
||||
testItem.className = bad ? "fail" : "pass";
|
||||
|
||||
testItem.insertBefore( time, assertList );
|
||||
time = document.createElement( "span" );
|
||||
time.className = "runtime";
|
||||
time.innerHTML = details.runtime + " ms";
|
||||
testItem.insertBefore( time, assertList );
|
||||
}
|
||||
});
|
||||
|
||||
if ( !defined.document || document.readyState === "complete" ) {
|
||||
config.pageLoaded = true;
|
||||
config.autorun = true;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user