Commit 9238337c authored by Chris Rebert's avatar Chris Rebert

Merge branch 'master' into v4

parents 9e0c4ff7 1ce502dc
...@@ -180,6 +180,9 @@ The `.dropdown-backdrop` element isn't used on iOS in the nav because of the com ...@@ -180,6 +180,9 @@ The `.dropdown-backdrop` element isn't used on iOS in the nav because of the com
Page zooming inevitably presents rendering artifacts in some components, both in Bootstrap and the rest of the web. Depending on the issue, we may be able to fix it (search first and then open an issue if need be). However, we tend to ignore these as they often have no direct solution other than hacky workarounds. Page zooming inevitably presents rendering artifacts in some components, both in Bootstrap and the rest of the web. Depending on the issue, we may be able to fix it (search first and then open an issue if need be). However, we tend to ignore these as they often have no direct solution other than hacky workarounds.
### Sticky `:hover`/`:focus` on mobile
Even though real hovering isn't possible on most touchscreens, most mobile browsers emulate hovering support and make `:hover` "sticky". In other words, `:hover` styles start applying after tapping an element and only stop applying after the user taps some other element. This can cause Bootstrap's `:hover` states to become unwantedly "stuck" on such browsers. Some mobile browsers also make `:focus` similarly sticky. There is currently no simple workaround for these issues other than removing such styles entirely.
### Printing ### Printing
Even in some modern browsers, printing can be quirky. Even in some modern browsers, printing can be quirky.
......
...@@ -35,8 +35,8 @@ $(function () { ...@@ -35,8 +35,8 @@ $(function () {
ok(!$affix.hasClass('affix'), 'affix class was not added') ok(!$affix.hasClass('affix'), 'affix class was not added')
}) })
test('should trigger affixed event after affix', function () { test('should trigger affixed event after affix', function (assert) {
stop() var done = assert.async()
var templateHTML = '<div id="affixTarget">' var templateHTML = '<div id="affixTarget">'
+ '<ul>' + '<ul>'
...@@ -57,7 +57,7 @@ $(function () { ...@@ -57,7 +57,7 @@ $(function () {
}).on('affixed.bs.affix', function () { }).on('affixed.bs.affix', function () {
ok(true, 'affixed event fired') ok(true, 'affixed event fired')
$('#affixTarget, #affixAfter').remove() $('#affixTarget, #affixAfter').remove()
start() done()
}) })
setTimeout(function () { setTimeout(function () {
...@@ -69,8 +69,8 @@ $(function () { ...@@ -69,8 +69,8 @@ $(function () {
}, 0) }, 0)
}) })
test('should affix-top when scrolling up to offset when parent has padding', function () { test('should affix-top when scrolling up to offset when parent has padding', function (assert) {
stop() var done = assert.async()
var templateHTML = '<div id="padding-offset" style="padding-top: 20px;">' var templateHTML = '<div id="padding-offset" style="padding-top: 20px;">'
+ '<div id="affixTopTarget">' + '<div id="affixTopTarget">'
...@@ -87,7 +87,7 @@ $(function () { ...@@ -87,7 +87,7 @@ $(function () {
.on('affixed-top.bs.affix', function () { .on('affixed-top.bs.affix', function () {
ok($('#affixTopTarget').hasClass('affix-top'), 'affix-top class applied') ok($('#affixTopTarget').hasClass('affix-top'), 'affix-top class applied')
$('#padding-offset').remove() $('#padding-offset').remove()
start() done()
}) })
setTimeout(function () { setTimeout(function () {
......
...@@ -55,13 +55,13 @@ $(function () { ...@@ -55,13 +55,13 @@ $(function () {
equal($('#qunit-fixture').find('.alert').length, 0, 'element removed from dom') equal($('#qunit-fixture').find('.alert').length, 0, 'element removed from dom')
}) })
test('should not fire closed when close is prevented', function () { test('should not fire closed when close is prevented', function (assert) {
stop() var done = assert.async()
$('<div class="alert"/>') $('<div class="alert"/>')
.on('close.bs.alert', function (e) { .on('close.bs.alert', function (e) {
e.preventDefault() e.preventDefault()
ok(true, 'close event fired') ok(true, 'close event fired')
start() done()
}) })
.on('closed.bs.alert', function () { .on('closed.bs.alert', function () {
ok(false, 'closed event fired') ok(false, 'closed event fired')
......
...@@ -29,57 +29,57 @@ $(function () { ...@@ -29,57 +29,57 @@ $(function () {
strictEqual($button[0], $el[0], 'collection contains element') 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>') var $btn = $('<button class="btn" data-loading-text="fat">mdo</button>')
equal($btn.html(), 'mdo', 'btn text equals mdo') equal($btn.html(), 'mdo', 'btn text equals mdo')
$btn.bootstrapButton('loading') $btn.bootstrapButton('loading')
stop() var done = assert.async()
setTimeout(function () { setTimeout(function () {
equal($btn.html(), 'fat', 'btn text equals fat') equal($btn.html(), 'fat', 'btn text equals fat')
ok($btn[0].hasAttribute('disabled'), 'btn is disabled') ok($btn[0].hasAttribute('disabled'), 'btn is disabled')
ok($btn.hasClass('disabled'), 'btn has disabled class') ok($btn.hasClass('disabled'), 'btn has disabled class')
start() done()
}, 0) }, 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>') var $btn = $('<button class="btn" data-loading-text="fat">mdo</button>')
equal($btn.html(), 'mdo', 'btn text equals mdo') equal($btn.html(), 'mdo', 'btn text equals mdo')
$btn.bootstrapButton('loading') $btn.bootstrapButton('loading')
stop() var doneOne = assert.async()
setTimeout(function () { setTimeout(function () {
equal($btn.html(), 'fat', 'btn text equals fat') equal($btn.html(), 'fat', 'btn text equals fat')
ok($btn[0].hasAttribute('disabled'), 'btn is disabled') ok($btn[0].hasAttribute('disabled'), 'btn is disabled')
ok($btn.hasClass('disabled'), 'btn has disabled class') ok($btn.hasClass('disabled'), 'btn has disabled class')
start() doneOne()
stop() var doneTwo = assert.async()
$btn.bootstrapButton('reset') $btn.bootstrapButton('reset')
setTimeout(function () { setTimeout(function () {
equal($btn.html(), 'mdo', 'btn text equals mdo') equal($btn.html(), 'mdo', 'btn text equals mdo')
ok(!$btn[0].hasAttribute('disabled'), 'btn is not disabled') ok(!$btn[0].hasAttribute('disabled'), 'btn is not disabled')
ok(!$btn.hasClass('disabled'), 'btn does not have disabled class') ok(!$btn.hasClass('disabled'), 'btn does not have disabled class')
start() doneTwo()
}, 0) }, 0)
}, 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"/>') var $btn = $('<button class="btn" data-loading-text="fat"/>')
equal($btn.html(), '', 'btn text equals ""') equal($btn.html(), '', 'btn text equals ""')
$btn.bootstrapButton('loading') $btn.bootstrapButton('loading')
stop() var doneOne = assert.async()
setTimeout(function () { setTimeout(function () {
equal($btn.html(), 'fat', 'btn text equals fat') equal($btn.html(), 'fat', 'btn text equals fat')
ok($btn[0].hasAttribute('disabled'), 'btn is disabled') ok($btn[0].hasAttribute('disabled'), 'btn is disabled')
ok($btn.hasClass('disabled'), 'btn has disabled class') ok($btn.hasClass('disabled'), 'btn has disabled class')
start() doneOne()
stop() var doneTwo = assert.async()
$btn.bootstrapButton('reset') $btn.bootstrapButton('reset')
setTimeout(function () { setTimeout(function () {
equal($btn.html(), '', 'btn text equals ""') equal($btn.html(), '', 'btn text equals ""')
ok(!$btn[0].hasAttribute('disabled'), 'btn is not disabled') ok(!$btn[0].hasAttribute('disabled'), 'btn is not disabled')
ok(!$btn.hasClass('disabled'), 'btn does not have disabled class') ok(!$btn.hasClass('disabled'), 'btn does not have disabled class')
start() doneTwo()
}, 0) }, 0)
}, 0) }, 0)
}) })
......
...@@ -29,13 +29,13 @@ $(function () { ...@@ -29,13 +29,13 @@ $(function () {
strictEqual($carousel[0], $el[0], 'collection contains element') strictEqual($carousel[0], $el[0], 'collection contains element')
}) })
test('should not fire slid when slide is prevented', function () { test('should not fire slid when slide is prevented', function (assert) {
stop() var done = assert.async()
$('<div class="carousel"/>') $('<div class="carousel"/>')
.on('slide.bs.carousel', function (e) { .on('slide.bs.carousel', function (e) {
e.preventDefault() e.preventDefault()
ok(true, 'slide event fired') ok(true, 'slide event fired')
start() done()
}) })
.on('slid.bs.carousel', function () { .on('slid.bs.carousel', function () {
ok(false, 'slid event fired') ok(false, 'slid event fired')
...@@ -43,7 +43,7 @@ $(function () { ...@@ -43,7 +43,7 @@ $(function () {
.bootstrapCarousel('next') .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">' var carouselHTML = '<div id="carousel-example-generic" class="carousel slide">'
+ '<ol class="carousel-indicators">' + '<ol class="carousel-indicators">'
+ '<li data-target="#carousel-example-generic" data-slide-to="0" class="active"/>' + '<li data-target="#carousel-example-generic" data-slide-to="0" class="active"/>'
...@@ -66,7 +66,7 @@ $(function () { ...@@ -66,7 +66,7 @@ $(function () {
+ '</div>' + '</div>'
var $carousel = $(carouselHTML) var $carousel = $(carouselHTML)
stop() var done = assert.async()
$carousel $carousel
.one('slide.bs.carousel', function (e) { .one('slide.bs.carousel', function (e) {
e.preventDefault() e.preventDefault()
...@@ -82,13 +82,13 @@ $(function () { ...@@ -82,13 +82,13 @@ $(function () {
ok(!$carousel.find('.carousel-indicators li:eq(0)').is('.active'), 'first indicator still active') ok(!$carousel.find('.carousel-indicators li:eq(0)').is('.active'), 'first indicator still active')
ok($carousel.find('.carousel-item:eq(1)').is('.active'), 'second item active') ok($carousel.find('.carousel-item:eq(1)').is('.active'), 'second item active')
ok($carousel.find('.carousel-indicators li:eq(1)').is('.active'), 'second indicator active') ok($carousel.find('.carousel-indicators li:eq(1)').is('.active'), 'second indicator active')
start() done()
}, 0) }, 0)
}) })
.bootstrapCarousel('next') .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">' var carouselHTML = '<div id="myCarousel" class="carousel slide">'
+ '<div class="carousel-inner">' + '<div class="carousel-inner">'
+ '<div class="carousel-item active">' + '<div class="carousel-item active">'
...@@ -124,7 +124,7 @@ $(function () { ...@@ -124,7 +124,7 @@ $(function () {
+ '</div>' + '</div>'
var $carousel = $(carouselHTML) var $carousel = $(carouselHTML)
stop() var done = assert.async()
$carousel $carousel
.one('slide.bs.carousel', function (e) { .one('slide.bs.carousel', function (e) {
...@@ -135,14 +135,14 @@ $(function () { ...@@ -135,14 +135,14 @@ $(function () {
.one('slide.bs.carousel', function (e) { .one('slide.bs.carousel', function (e) {
ok(e.direction, 'direction present on prev') ok(e.direction, 'direction present on prev')
strictEqual(e.direction, 'right', 'direction is right on prev') strictEqual(e.direction, 'right', 'direction is right on prev')
start() done()
}) })
.bootstrapCarousel('prev') .bootstrapCarousel('prev')
}) })
.bootstrapCarousel('next') .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">' var carouselHTML = '<div id="myCarousel" class="carousel slide">'
+ '<div class="carousel-inner">' + '<div class="carousel-inner">'
+ '<div class="carousel-item active">' + '<div class="carousel-item active">'
...@@ -178,7 +178,7 @@ $(function () { ...@@ -178,7 +178,7 @@ $(function () {
+ '</div>' + '</div>'
var $carousel = $(carouselHTML) var $carousel = $(carouselHTML)
stop() var done = assert.async()
$carousel $carousel
.one('slid.bs.carousel', function (e) { .one('slid.bs.carousel', function (e) {
...@@ -189,14 +189,14 @@ $(function () { ...@@ -189,14 +189,14 @@ $(function () {
.one('slid.bs.carousel', function (e) { .one('slid.bs.carousel', function (e) {
ok(e.direction, 'direction present on prev') ok(e.direction, 'direction present on prev')
strictEqual(e.direction, 'right', 'direction is right on prev') strictEqual(e.direction, 'right', 'direction is right on prev')
start() done()
}) })
.bootstrapCarousel('prev') .bootstrapCarousel('prev')
}) })
.bootstrapCarousel('next') .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">' var template = '<div id="myCarousel" class="carousel slide">'
+ '<div class="carousel-inner">' + '<div class="carousel-inner">'
+ '<div class="carousel-item active">' + '<div class="carousel-item active">'
...@@ -231,18 +231,18 @@ $(function () { ...@@ -231,18 +231,18 @@ $(function () {
+ '<a class="right carousel-control" href="#myCarousel" data-slide="next">&rsaquo;</a>' + '<a class="right carousel-control" href="#myCarousel" data-slide="next">&rsaquo;</a>'
+ '</div>' + '</div>'
stop() var done = assert.async()
$(template) $(template)
.on('slide.bs.carousel', function (e) { .on('slide.bs.carousel', function (e) {
ok(e.relatedTarget, 'relatedTarget present') ok(e.relatedTarget, 'relatedTarget present')
ok($(e.relatedTarget).hasClass('carousel-item'), 'relatedTarget has class "carousel-item"') ok($(e.relatedTarget).hasClass('carousel-item'), 'relatedTarget has class "carousel-item"')
start() done()
}) })
.bootstrapCarousel('next') .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">' var template = '<div id="myCarousel" class="carousel slide">'
+ '<div class="carousel-inner">' + '<div class="carousel-inner">'
+ '<div class="carousel-item active">' + '<div class="carousel-item active">'
...@@ -277,13 +277,13 @@ $(function () { ...@@ -277,13 +277,13 @@ $(function () {
+ '<a class="right carousel-control" href="#myCarousel" data-slide="next">&rsaquo;</a>' + '<a class="right carousel-control" href="#myCarousel" data-slide="next">&rsaquo;</a>'
+ '</div>' + '</div>'
stop() var done = assert.async()
$(template) $(template)
.on('slid.bs.carousel', function (e) { .on('slid.bs.carousel', function (e) {
ok(e.relatedTarget, 'relatedTarget present') ok(e.relatedTarget, 'relatedTarget present')
ok($(e.relatedTarget).hasClass('carousel-item'), 'relatedTarget has class "carousel-item"') ok($(e.relatedTarget).hasClass('carousel-item'), 'relatedTarget has class "carousel-item"')
start() done()
}) })
.bootstrapCarousel('next') .bootstrapCarousel('next')
}) })
...@@ -542,7 +542,7 @@ $(function () { ...@@ -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">' var carouselHTML = '<div id="carousel-example-generic" class="carousel slide" data-wrap="true">'
+ '<ol class="carousel-indicators">' + '<ol class="carousel-indicators">'
+ '<li data-target="#carousel-example-generic" data-slide-to="0" class="active"/>' + '<li data-target="#carousel-example-generic" data-slide-to="0" class="active"/>'
...@@ -566,7 +566,7 @@ $(function () { ...@@ -566,7 +566,7 @@ $(function () {
var $carousel = $(carouselHTML) var $carousel = $(carouselHTML)
var getActiveId = function () { return $carousel.find('.carousel-item.active').attr('id') } var getActiveId = function () { return $carousel.find('.carousel-item.active').attr('id') }
stop() var done = assert.async()
$carousel $carousel
.one('slid.bs.carousel', function () { .one('slid.bs.carousel', function () {
...@@ -577,7 +577,7 @@ $(function () { ...@@ -577,7 +577,7 @@ $(function () {
$carousel $carousel
.one('slid.bs.carousel', function () { .one('slid.bs.carousel', function () {
strictEqual(getActiveId(), 'one', 'carousel wrapped around and slid from 3rd to 1st slide') strictEqual(getActiveId(), 'one', 'carousel wrapped around and slid from 3rd to 1st slide')
start() done()
}) })
.bootstrapCarousel('next') .bootstrapCarousel('next')
}) })
...@@ -586,7 +586,7 @@ $(function () { ...@@ -586,7 +586,7 @@ $(function () {
.bootstrapCarousel('next') .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">' var carouselHTML = '<div id="carousel-example-generic" class="carousel slide" data-wrap="true">'
+ '<ol class="carousel-indicators">' + '<ol class="carousel-indicators">'
+ '<li data-target="#carousel-example-generic" data-slide-to="0" class="active"/>' + '<li data-target="#carousel-example-generic" data-slide-to="0" class="active"/>'
...@@ -609,17 +609,17 @@ $(function () { ...@@ -609,17 +609,17 @@ $(function () {
+ '</div>' + '</div>'
var $carousel = $(carouselHTML) var $carousel = $(carouselHTML)
stop() var done = assert.async()
$carousel $carousel
.on('slid.bs.carousel', function () { .on('slid.bs.carousel', function () {
strictEqual($carousel.find('.carousel-item.active').attr('id'), 'three', 'carousel wrapped around and slid from 1st to 3rd slide') strictEqual($carousel.find('.carousel-item.active').attr('id'), 'three', 'carousel wrapped around and slid from 1st to 3rd slide')
start() done()
}) })
.bootstrapCarousel('prev') .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">' var carouselHTML = '<div id="carousel-example-generic" class="carousel slide" data-wrap="false">'
+ '<ol class="carousel-indicators">' + '<ol class="carousel-indicators">'
+ '<li data-target="#carousel-example-generic" data-slide-to="0" class="active"/>' + '<li data-target="#carousel-example-generic" data-slide-to="0" class="active"/>'
...@@ -643,7 +643,7 @@ $(function () { ...@@ -643,7 +643,7 @@ $(function () {
var $carousel = $(carouselHTML) var $carousel = $(carouselHTML)
var getActiveId = function () { return $carousel.find('.carousel-item.active').attr('id') } var getActiveId = function () { return $carousel.find('.carousel-item.active').attr('id') }
stop() var done = assert.async()
$carousel $carousel
.one('slid.bs.carousel', function () { .one('slid.bs.carousel', function () {
...@@ -657,7 +657,7 @@ $(function () { ...@@ -657,7 +657,7 @@ $(function () {
}) })
.bootstrapCarousel('next') .bootstrapCarousel('next')
strictEqual(getActiveId(), 'three', 'carousel did not wrap around and stayed on 3rd slide') strictEqual(getActiveId(), 'three', 'carousel did not wrap around and stayed on 3rd slide')
start() done()
}) })
.bootstrapCarousel('next') .bootstrapCarousel('next')
}) })
......
...@@ -43,14 +43,14 @@ $(function () { ...@@ -43,14 +43,14 @@ $(function () {
ok(/height/i.test($el.attr('style')), 'has height set') ok(/height/i.test($el.attr('style')), 'has height set')
}) })
test('should not fire shown when show is prevented', function () { test('should not fire shown when show is prevented', function (assert) {
stop() var done = assert.async()
$('<div class="collapse"/>') $('<div class="collapse"/>')
.on('show.bs.collapse', function (e) { .on('show.bs.collapse', function (e) {
e.preventDefault() e.preventDefault()
ok(true, 'show event fired') ok(true, 'show event fired')
start() done()
}) })
.on('shown.bs.collapse', function () { .on('shown.bs.collapse', function () {
ok(false, 'shown event fired') ok(false, 'shown event fired')
...@@ -58,8 +58,8 @@ $(function () { ...@@ -58,8 +58,8 @@ $(function () {
.bootstrapCollapse('show') .bootstrapCollapse('show')
}) })
test('should reset style to auto after finishing opening collapse', function () { test('should reset style to auto after finishing opening collapse', function (assert) {
stop() var done = assert.async()
$('<div class="collapse" style="height: 0px"/>') $('<div class="collapse" style="height: 0px"/>')
.on('show.bs.collapse', function () { .on('show.bs.collapse', function () {
...@@ -67,13 +67,13 @@ $(function () { ...@@ -67,13 +67,13 @@ $(function () {
}) })
.on('shown.bs.collapse', function () { .on('shown.bs.collapse', function () {
strictEqual(this.style.height, '', 'height is auto') strictEqual(this.style.height, '', 'height is auto')
start() done()
}) })
.bootstrapCollapse('show') .bootstrapCollapse('show')
}) })
test('should remove "collapsed" class from target when collapse is shown', function () { test('should remove "collapsed" class from target when collapse is shown', function (assert) {
stop() var done = assert.async()
var $target = $('<a data-toggle="collapse" class="collapsed" href="#test1"/>').appendTo('#qunit-fixture') var $target = $('<a data-toggle="collapse" class="collapsed" href="#test1"/>').appendTo('#qunit-fixture')
...@@ -81,14 +81,14 @@ $(function () { ...@@ -81,14 +81,14 @@ $(function () {
.appendTo('#qunit-fixture') .appendTo('#qunit-fixture')
.on('shown.bs.collapse', function () { .on('shown.bs.collapse', function () {
ok(!$target.hasClass('collapsed')) ok(!$target.hasClass('collapsed'))
start() done()
}) })
$target.click() $target.click()
}) })
test('should add "collapsed" class to target when collapse is hidden', function () { test('should add "collapsed" class to target when collapse is hidden', function (assert) {
stop() var done = assert.async()
var $target = $('<a data-toggle="collapse" href="#test1"/>').appendTo('#qunit-fixture') var $target = $('<a data-toggle="collapse" href="#test1"/>').appendTo('#qunit-fixture')
...@@ -96,14 +96,14 @@ $(function () { ...@@ -96,14 +96,14 @@ $(function () {
.appendTo('#qunit-fixture') .appendTo('#qunit-fixture')
.on('hidden.bs.collapse', function () { .on('hidden.bs.collapse', function () {
ok($target.hasClass('collapsed')) ok($target.hasClass('collapsed'))
start() done()
}) })
$target.click() $target.click()
}) })
test('should not close a collapse when initialized with "show" if already shown', function () { test('should not close a collapse when initialized with "show" if already shown', function (assert) {
stop() var done = assert.async()
expect(0) expect(0)
...@@ -115,11 +115,11 @@ $(function () { ...@@ -115,11 +115,11 @@ $(function () {
$test.bootstrapCollapse('show') $test.bootstrapCollapse('show')
setTimeout(start, 0) setTimeout(done, 0)
}) })
test('should open a collapse when initialized with "show" if not already shown', function () { test('should open a collapse when initialized with "show" if not already shown', function (assert) {
stop() var done = assert.async()
expect(1) expect(1)
...@@ -131,11 +131,11 @@ $(function () { ...@@ -131,11 +131,11 @@ $(function () {
$test.bootstrapCollapse('show') $test.bootstrapCollapse('show')
setTimeout(start, 0) setTimeout(done, 0)
}) })
test('should remove "collapsed" class from active accordion target', function () { test('should remove "collapsed" class from active accordion target', function (assert) {
stop() var done = assert.async()
var accordionHTML = '<div class="panel-group" id="accordion">' var accordionHTML = '<div class="panel-group" id="accordion">'
+ '<div class="panel"/>' + '<div class="panel"/>'
...@@ -161,14 +161,14 @@ $(function () { ...@@ -161,14 +161,14 @@ $(function () {
ok($target2.hasClass('collapsed'), 'inactive target 2 does have class "collapsed"') ok($target2.hasClass('collapsed'), 'inactive target 2 does have class "collapsed"')
ok(!$target3.hasClass('collapsed'), 'active target 3 does not have class "collapsed"') ok(!$target3.hasClass('collapsed'), 'active target 3 does not have class "collapsed"')
start() done()
}) })
$target3.click() $target3.click()
}) })
test('should allow dots in data-parent', function () { test('should allow dots in data-parent', function (assert) {
stop() var done = assert.async()
var accordionHTML = '<div class="panel-group accordion">' var accordionHTML = '<div class="panel-group accordion">'
+ '<div class="panel"/>' + '<div class="panel"/>'
...@@ -194,14 +194,14 @@ $(function () { ...@@ -194,14 +194,14 @@ $(function () {
ok($target2.hasClass('collapsed'), 'inactive target 2 does have class "collapsed"') ok($target2.hasClass('collapsed'), 'inactive target 2 does have class "collapsed"')
ok(!$target3.hasClass('collapsed'), 'active target 3 does not have class "collapsed"') ok(!$target3.hasClass('collapsed'), 'active target 3 does not have class "collapsed"')
start() done()
}) })
$target3.click() $target3.click()
}) })
test('should set aria-expanded="true" on target when collapse is shown', function () { test('should set aria-expanded="true" on target when collapse is shown', function (assert) {
stop() var done = assert.async()
var $target = $('<a data-toggle="collapse" class="collapsed" href="#test1" aria-expanded="false"/>').appendTo('#qunit-fixture') var $target = $('<a data-toggle="collapse" class="collapsed" href="#test1" aria-expanded="false"/>').appendTo('#qunit-fixture')
...@@ -209,14 +209,14 @@ $(function () { ...@@ -209,14 +209,14 @@ $(function () {
.appendTo('#qunit-fixture') .appendTo('#qunit-fixture')
.on('shown.bs.collapse', function () { .on('shown.bs.collapse', function () {
equal($target.attr('aria-expanded'), 'true', 'aria-expanded on target is "true"') equal($target.attr('aria-expanded'), 'true', 'aria-expanded on target is "true"')
start() done()
}) })
$target.click() $target.click()
}) })
test('should set aria-expanded="false" on target when collapse is hidden', function () { test('should set aria-expanded="false" on target when collapse is hidden', function (assert) {
stop() var done = assert.async()
var $target = $('<a data-toggle="collapse" href="#test1" aria-expanded="true"/>').appendTo('#qunit-fixture') var $target = $('<a data-toggle="collapse" href="#test1" aria-expanded="true"/>').appendTo('#qunit-fixture')
...@@ -224,14 +224,14 @@ $(function () { ...@@ -224,14 +224,14 @@ $(function () {
.appendTo('#qunit-fixture') .appendTo('#qunit-fixture')
.on('hidden.bs.collapse', function () { .on('hidden.bs.collapse', function () {
equal($target.attr('aria-expanded'), 'false', 'aria-expanded on target is "false"') equal($target.attr('aria-expanded'), 'false', 'aria-expanded on target is "false"')
start() done()
}) })
$target.click() $target.click()
}) })
test('should change aria-expanded from active accordion target to "false" and set the newly active one to "true"', function () { test('should change aria-expanded from active accordion target to "false" and set the newly active one to "true"', function (assert) {
stop() var done = assert.async()
var accordionHTML = '<div class="panel-group" id="accordion">' var accordionHTML = '<div class="panel-group" id="accordion">'
+ '<div class="panel"/>' + '<div class="panel"/>'
...@@ -257,14 +257,14 @@ $(function () { ...@@ -257,14 +257,14 @@ $(function () {
equal($target2.attr('aria-expanded'), 'false', 'inactive target 2 has aria-expanded="false"') 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"') equal($target3.attr('aria-expanded'), 'true', 'active target 3 has aria-expanded="false"')
start() done()
}) })
$target3.click() $target3.click()
}) })
test('should not fire show event if show is prevented because other element is still transitioning', function () { test('should not fire show event if show is prevented because other element is still transitioning', function (assert) {
stop() var done = assert.async()
var accordionHTML = '<div id="accordion">' var accordionHTML = '<div id="accordion">'
+ '<div class="panel"/>' + '<div class="panel"/>'
...@@ -294,12 +294,12 @@ $(function () { ...@@ -294,12 +294,12 @@ $(function () {
setTimeout(function () { setTimeout(function () {
ok(!showFired, 'show event didn\'t fire') ok(!showFired, 'show event didn\'t fire')
start() done()
}, 1) }, 1)
}) })
test('should add "collapsed" class to target when collapse is hidden via manual invocation', function () { test('should add "collapsed" class to target when collapse is hidden via manual invocation', function (assert) {
stop() var done = assert.async()
var $target = $('<a data-toggle="collapse" href="#test1"/>').appendTo('#qunit-fixture') var $target = $('<a data-toggle="collapse" href="#test1"/>').appendTo('#qunit-fixture')
...@@ -307,13 +307,13 @@ $(function () { ...@@ -307,13 +307,13 @@ $(function () {
.appendTo('#qunit-fixture') .appendTo('#qunit-fixture')
.on('hidden.bs.collapse', function () { .on('hidden.bs.collapse', function () {
ok($target.hasClass('collapsed')) ok($target.hasClass('collapsed'))
start() done()
}) })
.bootstrapCollapse('hide') .bootstrapCollapse('hide')
}) })
test('should remove "collapsed" class from target when collapse is shown via manual invocation', function () { test('should remove "collapsed" class from target when collapse is shown via manual invocation', function (assert) {
stop() var done = assert.async()
var $target = $('<a data-toggle="collapse" class="collapsed" href="#test1"/>').appendTo('#qunit-fixture') var $target = $('<a data-toggle="collapse" class="collapsed" href="#test1"/>').appendTo('#qunit-fixture')
...@@ -321,7 +321,7 @@ $(function () { ...@@ -321,7 +321,7 @@ $(function () {
.appendTo('#qunit-fixture') .appendTo('#qunit-fixture')
.on('shown.bs.collapse', function () { .on('shown.bs.collapse', function () {
ok(!$target.hasClass('collapsed')) ok(!$target.hasClass('collapsed'))
start() done()
}) })
.bootstrapCollapse('show') .bootstrapCollapse('show')
}) })
......
...@@ -157,7 +157,7 @@ $(function () { ...@@ -157,7 +157,7 @@ $(function () {
strictEqual($('#qunit-fixture .open').length, 0, '"open" class removed') 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">' var dropdownHTML = '<ul class="tabs">'
+ '<li class="dropdown">' + '<li class="dropdown">'
+ '<a href="#" class="dropdown-toggle" data-toggle="dropdown">Dropdown</a>' + '<a href="#" class="dropdown-toggle" data-toggle="dropdown">Dropdown</a>'
...@@ -174,7 +174,7 @@ $(function () { ...@@ -174,7 +174,7 @@ $(function () {
.find('[data-toggle="dropdown"]') .find('[data-toggle="dropdown"]')
.bootstrapDropdown() .bootstrapDropdown()
stop() var done = assert.async()
$dropdown $dropdown
.parent('.dropdown') .parent('.dropdown')
...@@ -183,7 +183,7 @@ $(function () { ...@@ -183,7 +183,7 @@ $(function () {
}) })
.on('hide.bs.dropdown', function () { .on('hide.bs.dropdown', function () {
ok(true, 'hide was fired') ok(true, 'hide was fired')
start() done()
}) })
$dropdown.click() $dropdown.click()
...@@ -191,7 +191,7 @@ $(function () { ...@@ -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">' var dropdownHTML = '<ul class="tabs">'
+ '<li class="dropdown">' + '<li class="dropdown">'
+ '<a href="#" class="dropdown-toggle" data-toggle="dropdown">Dropdown</a>' + '<a href="#" class="dropdown-toggle" data-toggle="dropdown">Dropdown</a>'
...@@ -208,7 +208,7 @@ $(function () { ...@@ -208,7 +208,7 @@ $(function () {
.find('[data-toggle="dropdown"]') .find('[data-toggle="dropdown"]')
.bootstrapDropdown() .bootstrapDropdown()
stop() var done = assert.async()
$dropdown $dropdown
.parent('.dropdown') .parent('.dropdown')
...@@ -217,15 +217,15 @@ $(function () { ...@@ -217,15 +217,15 @@ $(function () {
}) })
.on('hidden.bs.dropdown', function () { .on('hidden.bs.dropdown', function () {
ok(true, 'hidden was fired') ok(true, 'hidden was fired')
start() done()
}) })
$dropdown.click() $dropdown.click()
$(document.body).click() $(document.body).click()
}) })
test('should ignore keyboard events within <input>s and <textarea>s', function () { test('should ignore keyboard events within <input>s and <textarea>s', function (assert) {
stop() var done = assert.async()
var dropdownHTML = '<ul class="tabs">' var dropdownHTML = '<ul class="tabs">'
+ '<li class="dropdown">' + '<li class="dropdown">'
...@@ -259,7 +259,7 @@ $(function () { ...@@ -259,7 +259,7 @@ $(function () {
$textarea.focus().trigger($.Event('keydown', { which: 38 })) $textarea.focus().trigger($.Event('keydown', { which: 38 }))
ok($(document.activeElement).is($textarea), 'textarea still focused') ok($(document.activeElement).is($textarea), 'textarea still focused')
start() done()
}) })
$dropdown.click() $dropdown.click()
......
...@@ -33,36 +33,36 @@ $(function () { ...@@ -33,36 +33,36 @@ $(function () {
ok($.fn.bootstrapModal.Constructor.DEFAULTS, 'default object exposed') ok($.fn.bootstrapModal.Constructor.DEFAULTS, 'default object exposed')
}) })
test('should insert into dom when show method is called', function () { test('should insert into dom when show method is called', function (assert) {
stop() var done = assert.async()
$('<div id="modal-test"/>') $('<div id="modal-test"/>')
.on('shown.bs.modal', function () { .on('shown.bs.modal', function () {
notEqual($('#modal-test').length, 0, 'modal inserted into dom') notEqual($('#modal-test').length, 0, 'modal inserted into dom')
start() done()
}) })
.bootstrapModal('show') .bootstrapModal('show')
}) })
test('should fire show event', function () { test('should fire show event', function (assert) {
stop() var done = assert.async()
$('<div id="modal-test"/>') $('<div id="modal-test"/>')
.on('show.bs.modal', function () { .on('show.bs.modal', function () {
ok(true, 'show event fired') ok(true, 'show event fired')
start() done()
}) })
.bootstrapModal('show') .bootstrapModal('show')
}) })
test('should not fire shown when show was prevented', function () { test('should not fire shown when show was prevented', function (assert) {
stop() var done = assert.async()
$('<div id="modal-test"/>') $('<div id="modal-test"/>')
.on('show.bs.modal', function (e) { .on('show.bs.modal', function (e) {
e.preventDefault() e.preventDefault()
ok(true, 'show event fired') ok(true, 'show event fired')
start() done()
}) })
.on('shown.bs.modal', function () { .on('shown.bs.modal', function () {
ok(false, 'shown event fired') ok(false, 'shown event fired')
...@@ -70,8 +70,8 @@ $(function () { ...@@ -70,8 +70,8 @@ $(function () {
.bootstrapModal('show') .bootstrapModal('show')
}) })
test('should hide modal when hide is called', function () { test('should hide modal when hide is called', function (assert) {
stop() var done = assert.async()
$('<div id="modal-test"/>') $('<div id="modal-test"/>')
.on('shown.bs.modal', function () { .on('shown.bs.modal', function () {
...@@ -81,13 +81,13 @@ $(function () { ...@@ -81,13 +81,13 @@ $(function () {
}) })
.on('hidden.bs.modal', function () { .on('hidden.bs.modal', function () {
ok(!$('#modal-test').is(':visible'), 'modal hidden') ok(!$('#modal-test').is(':visible'), 'modal hidden')
start() done()
}) })
.bootstrapModal('show') .bootstrapModal('show')
}) })
test('should toggle when toggle is called', function () { test('should toggle when toggle is called', function (assert) {
stop() var done = assert.async()
$('<div id="modal-test"/>') $('<div id="modal-test"/>')
.on('shown.bs.modal', function () { .on('shown.bs.modal', function () {
...@@ -97,13 +97,13 @@ $(function () { ...@@ -97,13 +97,13 @@ $(function () {
}) })
.on('hidden.bs.modal', function () { .on('hidden.bs.modal', function () {
ok(!$('#modal-test').is(':visible'), 'modal hidden') ok(!$('#modal-test').is(':visible'), 'modal hidden')
start() done()
}) })
.bootstrapModal('toggle') .bootstrapModal('toggle')
}) })
test('should remove from dom when click [data-dismiss="modal"]', function () { test('should remove from dom when click [data-dismiss="modal"]', function (assert) {
stop() var done = assert.async()
$('<div id="modal-test"><span class="close" data-dismiss="modal"/></div>') $('<div id="modal-test"><span class="close" data-dismiss="modal"/></div>')
.on('shown.bs.modal', function () { .on('shown.bs.modal', function () {
...@@ -113,13 +113,13 @@ $(function () { ...@@ -113,13 +113,13 @@ $(function () {
}) })
.on('hidden.bs.modal', function () { .on('hidden.bs.modal', function () {
ok(!$('#modal-test').is(':visible'), 'modal hidden') ok(!$('#modal-test').is(':visible'), 'modal hidden')
start() done()
}) })
.bootstrapModal('toggle') .bootstrapModal('toggle')
}) })
test('should allow modal close with "backdrop:false"', function () { test('should allow modal close with "backdrop:false"', function (assert) {
stop() var done = assert.async()
$('<div id="modal-test" data-backdrop="false"/>') $('<div id="modal-test" data-backdrop="false"/>')
.on('shown.bs.modal', function () { .on('shown.bs.modal', function () {
...@@ -128,13 +128,13 @@ $(function () { ...@@ -128,13 +128,13 @@ $(function () {
}) })
.on('hidden.bs.modal', function () { .on('hidden.bs.modal', function () {
ok(!$('#modal-test').is(':visible'), 'modal hidden') ok(!$('#modal-test').is(':visible'), 'modal hidden')
start() done()
}) })
.bootstrapModal('show') .bootstrapModal('show')
}) })
test('should close modal when clicking outside of modal-content', function () { test('should close modal when clicking outside of modal-content', function (assert) {
stop() var done = assert.async()
$('<div id="modal-test"><div class="contents"/></div>') $('<div id="modal-test"><div class="contents"/></div>')
.on('shown.bs.modal', function () { .on('shown.bs.modal', function () {
...@@ -145,13 +145,13 @@ $(function () { ...@@ -145,13 +145,13 @@ $(function () {
}) })
.on('hidden.bs.modal', function () { .on('hidden.bs.modal', function () {
ok(!$('#modal-test').is(':visible'), 'modal hidden') ok(!$('#modal-test').is(':visible'), 'modal hidden')
start() done()
}) })
.bootstrapModal('show') .bootstrapModal('show')
}) })
test('should close modal when escape key is pressed via keydown', function () { test('should close modal when escape key is pressed via keydown', function (assert) {
stop() var done = assert.async()
var div = $('<div id="modal-test"/>') var div = $('<div id="modal-test"/>')
div div
...@@ -163,14 +163,14 @@ $(function () { ...@@ -163,14 +163,14 @@ $(function () {
setTimeout(function () { setTimeout(function () {
ok(!$('#modal-test').is(':visible'), 'modal hidden') ok(!$('#modal-test').is(':visible'), 'modal hidden')
div.remove() div.remove()
start() done()
}, 0) }, 0)
}) })
.bootstrapModal('show') .bootstrapModal('show')
}) })
test('should not close modal when escape key is pressed via keyup', function () { test('should not close modal when escape key is pressed via keyup', function (assert) {
stop() var done = assert.async()
var div = $('<div id="modal-test"/>') var div = $('<div id="modal-test"/>')
div div
...@@ -182,14 +182,14 @@ $(function () { ...@@ -182,14 +182,14 @@ $(function () {
setTimeout(function () { setTimeout(function () {
ok($('#modal-test').is(':visible'), 'modal still visible') ok($('#modal-test').is(':visible'), 'modal still visible')
div.remove() div.remove()
start() done()
}, 0) }, 0)
}) })
.bootstrapModal('show') .bootstrapModal('show')
}) })
test('should trigger hide event once when clicking outside of modal-content', function () { test('should trigger hide event once when clicking outside of modal-content', function (assert) {
stop() var done = assert.async()
var triggered var triggered
...@@ -201,31 +201,36 @@ $(function () { ...@@ -201,31 +201,36 @@ $(function () {
.on('hide.bs.modal', function () { .on('hide.bs.modal', function () {
triggered += 1 triggered += 1
strictEqual(triggered, 1, 'modal hide triggered once') strictEqual(triggered, 1, 'modal hide triggered once')
start() done()
}) })
.bootstrapModal('show') .bootstrapModal('show')
}) })
test('should close reopened modal with [data-dismiss="modal"] click', function () { test('should close reopened modal with [data-dismiss="modal"] click', function (assert) {
stop() var done = assert.async()
$('<div id="modal-test"><div class="contents"><div id="close" data-dismiss="modal"/></div></div>') $('<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() $('#close').click()
ok(!$('#modal-test').is(':visible'), 'modal hidden')
}) })
.one('hidden.bs.modal', function () { .one('hidden.bs.modal', function () {
// after one open-close cycle
ok(!$('#modal-test').is(':visible'), 'modal hidden')
$(this) $(this)
.one('shown.bs.modal', function () {
$('#close').click()
})
.one('hidden.bs.modal', function () { .one('hidden.bs.modal', function () {
start() ok(!$('#modal-test').is(':visible'), 'modal hidden')
done()
}) })
.bootstrapModal('show') .bootstrapModal('show')
}) })
.bootstrapModal('show') .bootstrapModal('show')
}) })
test('should restore focus to toggling element when modal is hidden after having been opened via data-api', function () { test('should restore focus to toggling element when modal is hidden after having been opened via data-api', function (assert) {
stop() var done = assert.async()
var $toggleBtn = $('<button data-toggle="modal" data-target="#modal-test"/>').appendTo('#qunit-fixture') var $toggleBtn = $('<button data-toggle="modal" data-target="#modal-test"/>').appendTo('#qunit-fixture')
...@@ -233,7 +238,7 @@ $(function () { ...@@ -233,7 +238,7 @@ $(function () {
.on('hidden.bs.modal', function () { .on('hidden.bs.modal', function () {
setTimeout(function () { setTimeout(function () {
ok($(document.activeElement).is($toggleBtn), 'toggling element is once again focused') ok($(document.activeElement).is($toggleBtn), 'toggling element is once again focused')
start() done()
}, 0) }, 0)
}) })
.on('shown.bs.modal', function () { .on('shown.bs.modal', function () {
...@@ -244,8 +249,8 @@ $(function () { ...@@ -244,8 +249,8 @@ $(function () {
$toggleBtn.click() $toggleBtn.click()
}) })
test('should not restore focus to toggling element if the associated show event gets prevented', function () { test('should not restore focus to toggling element if the associated show event gets prevented', function (assert) {
stop() var done = assert.async()
var $toggleBtn = $('<button data-toggle="modal" data-target="#modal-test"/>').appendTo('#qunit-fixture') var $toggleBtn = $('<button data-toggle="modal" data-target="#modal-test"/>').appendTo('#qunit-fixture')
var $otherBtn = $('<button id="other-btn"/>').appendTo('#qunit-fixture') var $otherBtn = $('<button id="other-btn"/>').appendTo('#qunit-fixture')
...@@ -260,7 +265,7 @@ $(function () { ...@@ -260,7 +265,7 @@ $(function () {
.on('hidden.bs.modal', function () { .on('hidden.bs.modal', function () {
setTimeout(function () { setTimeout(function () {
ok($(document.activeElement).is($otherBtn), 'focus returned to toggling element') ok($(document.activeElement).is($otherBtn), 'focus returned to toggling element')
start() done()
}, 0) }, 0)
}) })
.on('shown.bs.modal', function () { .on('shown.bs.modal', function () {
......
...@@ -188,7 +188,7 @@ $(function () { ...@@ -188,7 +188,7 @@ $(function () {
equal($('.popover').length, 0, 'popover was removed') 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 $content = $('<div class="content-with-handler"><a class="btn btn-warning">Button with event handler</a></div>').appendTo('#qunit-fixture')
var handlerCalled = false var handlerCalled = false
...@@ -207,7 +207,7 @@ $(function () { ...@@ -207,7 +207,7 @@ $(function () {
} }
}) })
stop() var done = assert.async()
$div $div
.one('shown.bs.popover', function () { .one('shown.bs.popover', function () {
$div $div
...@@ -217,7 +217,7 @@ $(function () { ...@@ -217,7 +217,7 @@ $(function () {
$('.content-with-handler .btn').click() $('.content-with-handler .btn').click()
$div.bootstrapPopover('destroy') $div.bootstrapPopover('destroy')
ok(handlerCalled, 'content\'s event handler still present') ok(handlerCalled, 'content\'s event handler still present')
start() done()
}) })
.bootstrapPopover('show') .bootstrapPopover('show')
}) })
......
...@@ -29,8 +29,8 @@ $(function () { ...@@ -29,8 +29,8 @@ $(function () {
strictEqual($scrollspy[0], $el[0], 'collection contains element') strictEqual($scrollspy[0], $el[0], 'collection contains element')
}) })
test('should only switch "active" class on current target', function () { test('should only switch "active" class on current target', function (assert) {
stop() var done = assert.async()
var sectionHTML = '<div id="root" class="active">' var sectionHTML = '<div id="root" class="active">'
+ '<div class="topbar">' + '<div class="topbar">'
...@@ -67,14 +67,14 @@ $(function () { ...@@ -67,14 +67,14 @@ $(function () {
$scrollspy.on('scroll.bs.scrollspy', function () { $scrollspy.on('scroll.bs.scrollspy', function () {
ok($section.hasClass('active'), '"active" class still on root node') ok($section.hasClass('active'), '"active" class still on root node')
start() done()
}) })
$scrollspy.scrollTop(350) $scrollspy.scrollTop(350)
}) })
test('should correctly select middle navigation option when large offset is used', function () { test('should correctly select middle navigation option when large offset is used', function (assert) {
stop() var done = assert.async()
var sectionHTML = '<div id="header" style="height: 500px;"></div>' var sectionHTML = '<div id="header" style="height: 500px;"></div>'
+ '<nav id="navigation" class="navbar">' + '<nav id="navigation" class="navbar">'
...@@ -100,13 +100,13 @@ $(function () { ...@@ -100,13 +100,13 @@ $(function () {
ok(!$section.find('#one-link').parent().hasClass('active'), '"active" class removed from first section') 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('#two-link').parent().hasClass('active'), '"active" class on middle section')
ok(!$section.find('#three-link').parent().hasClass('active'), '"active" class not on last section') ok(!$section.find('#three-link').parent().hasClass('active'), '"active" class not on last section')
start() done()
}) })
$scrollspy.scrollTop(550) $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 = var navbarHtml =
'<nav class="navbar">' '<nav class="navbar">'
+ '<ul class="nav">' + '<ul class="nav">'
...@@ -128,10 +128,10 @@ $(function () { ...@@ -128,10 +128,10 @@ $(function () {
var testElementIsActiveAfterScroll = function (element, target) { var testElementIsActiveAfterScroll = function (element, target) {
var deferred = $.Deferred() var deferred = $.Deferred()
var scrollHeight = Math.ceil($content.scrollTop() + $(target).position().top) var scrollHeight = Math.ceil($content.scrollTop() + $(target).position().top)
stop() var done = assert.async()
$content.one('scroll', function () { $content.one('scroll', function () {
ok($(element).hasClass('active'), 'target:' + target + ', element' + element) ok($(element).hasClass('active'), 'target:' + target + ', element' + element)
start() done()
deferred.resolve() deferred.resolve()
}) })
$content.scrollTop(scrollHeight) $content.scrollTop(scrollHeight)
...@@ -142,8 +142,8 @@ $(function () { ...@@ -142,8 +142,8 @@ $(function () {
.then(function () { return testElementIsActiveAfterScroll('#li-2', '#div-2') }) .then(function () { return testElementIsActiveAfterScroll('#li-2', '#div-2') })
}) })
test('should clear selection if above the first section', function () { test('should clear selection if above the first section', function (assert) {
stop() var done = assert.async()
var sectionHTML = '<div id="header" style="height: 500px;"></div>' var sectionHTML = '<div id="header" style="height: 500px;"></div>'
+ '<nav id="navigation" class="navbar">' + '<nav id="navigation" class="navbar">'
...@@ -176,7 +176,7 @@ $(function () { ...@@ -176,7 +176,7 @@ $(function () {
$scrollspy $scrollspy
.one('scroll.bs.scrollspy', function () { .one('scroll.bs.scrollspy', function () {
strictEqual($('.active').length, 0, 'selection cleared') strictEqual($('.active').length, 0, 'selection cleared')
start() done()
}) })
.scrollTop(0) .scrollTop(0)
}) })
......
...@@ -59,14 +59,14 @@ $(function () { ...@@ -59,14 +59,14 @@ $(function () {
equal($('#qunit-fixture').find('.active').attr('id'), 'home') equal($('#qunit-fixture').find('.active').attr('id'), 'home')
}) })
test('should not fire shown when show is prevented', function () { test('should not fire shown when show is prevented', function (assert) {
stop() var done = assert.async()
$('<div class="tab"/>') $('<div class="tab"/>')
.on('show.bs.tab', function (e) { .on('show.bs.tab', function (e) {
e.preventDefault() e.preventDefault()
ok(true, 'show event fired') ok(true, 'show event fired')
start() done()
}) })
.on('shown.bs.tab', function () { .on('shown.bs.tab', function () {
ok(false, 'shown event fired') ok(false, 'shown event fired')
...@@ -74,8 +74,8 @@ $(function () { ...@@ -74,8 +74,8 @@ $(function () {
.bootstrapTab('show') .bootstrapTab('show')
}) })
test('show and shown events should reference correct relatedTarget', function () { test('show and shown events should reference correct relatedTarget', function (assert) {
stop() var done = assert.async()
var dropHTML = '<ul class="drop">' var dropHTML = '<ul class="drop">'
+ '<li class="dropdown"><a data-toggle="dropdown" href="#">1</a>' + '<li class="dropdown"><a data-toggle="dropdown" href="#">1</a>'
...@@ -93,16 +93,16 @@ $(function () { ...@@ -93,16 +93,16 @@ $(function () {
.find('ul > li:last a') .find('ul > li:last a')
.on('show.bs.tab', function (e) { .on('show.bs.tab', function (e) {
equal(e.relatedTarget.hash, '#1-1', 'references correct element as relatedTarget') equal(e.relatedTarget.hash, '#1-1', 'references correct element as relatedTarget')
start()
}) })
.on('shown.bs.tab', function (e) { .on('shown.bs.tab', function (e) {
equal(e.relatedTarget.hash, '#1-1', 'references correct element as relatedTarget') equal(e.relatedTarget.hash, '#1-1', 'references correct element as relatedTarget')
done()
}) })
.bootstrapTab('show') .bootstrapTab('show')
}) })
test('should fire hide and hidden events', function () { test('should fire hide and hidden events', function (assert) {
stop() var done = assert.async()
var tabsHTML = '<ul class="tabs">' var tabsHTML = '<ul class="tabs">'
+ '<li><a href="#home">Home</a></li>' + '<li><a href="#home">Home</a></li>'
...@@ -123,7 +123,7 @@ $(function () { ...@@ -123,7 +123,7 @@ $(function () {
.find('li:first a') .find('li:first a')
.on('hidden.bs.tab', function () { .on('hidden.bs.tab', function () {
ok(true, 'hidden event fired') ok(true, 'hidden event fired')
start() done()
}) })
.bootstrapTab('show') .bootstrapTab('show')
.end() .end()
...@@ -131,8 +131,8 @@ $(function () { ...@@ -131,8 +131,8 @@ $(function () {
.bootstrapTab('show') .bootstrapTab('show')
}) })
test('should not fire hidden when hide is prevented', function () { test('should not fire hidden when hide is prevented', function (assert) {
stop() var done = assert.async()
var tabsHTML = '<ul class="tabs">' var tabsHTML = '<ul class="tabs">'
+ '<li><a href="#home">Home</a></li>' + '<li><a href="#home">Home</a></li>'
...@@ -144,7 +144,7 @@ $(function () { ...@@ -144,7 +144,7 @@ $(function () {
.on('hide.bs.tab', function (e) { .on('hide.bs.tab', function (e) {
e.preventDefault() e.preventDefault()
ok(true, 'hide event fired') ok(true, 'hide event fired')
start() done()
}) })
.on('hidden.bs.tab', function () { .on('hidden.bs.tab', function () {
ok(false, 'hidden event fired') ok(false, 'hidden event fired')
...@@ -155,8 +155,8 @@ $(function () { ...@@ -155,8 +155,8 @@ $(function () {
.bootstrapTab('show') .bootstrapTab('show')
}) })
test('hide and hidden events contain correct relatedTarget', function () { test('hide and hidden events contain correct relatedTarget', function (assert) {
stop() var done = assert.async()
var tabsHTML = '<ul class="tabs">' var tabsHTML = '<ul class="tabs">'
+ '<li><a href="#home">Home</a></li>' + '<li><a href="#home">Home</a></li>'
...@@ -170,7 +170,7 @@ $(function () { ...@@ -170,7 +170,7 @@ $(function () {
}) })
.on('hidden.bs.tab', function (e) { .on('hidden.bs.tab', function (e) {
equal(e.relatedTarget.hash, '#profile', 'references correct element as relatedTarget') equal(e.relatedTarget.hash, '#profile', 'references correct element as relatedTarget')
start() done()
}) })
.bootstrapTab('show') .bootstrapTab('show')
.end() .end()
......
...@@ -115,37 +115,37 @@ $(function () { ...@@ -115,37 +115,37 @@ $(function () {
equal($('.tooltip').length, 0, 'tooltip removed') equal($('.tooltip').length, 0, 'tooltip removed')
}) })
test('should fire show event', function () { test('should fire show event', function (assert) {
stop() var done = assert.async()
$('<div title="tooltip title"/>') $('<div title="tooltip title"/>')
.on('show.bs.tooltip', function () { .on('show.bs.tooltip', function () {
ok(true, 'show event fired') ok(true, 'show event fired')
start() done()
}) })
.bootstrapTooltip('show') .bootstrapTooltip('show')
}) })
test('should fire shown event', function () { test('should fire shown event', function (assert) {
stop() var done = assert.async()
$('<div title="tooltip title"></div>') $('<div title="tooltip title"></div>')
.appendTo('#qunit-fixture') .appendTo('#qunit-fixture')
.on('shown.bs.tooltip', function () { .on('shown.bs.tooltip', function () {
ok(true, 'shown was called') ok(true, 'shown was called')
start() done()
}) })
.bootstrapTooltip('show') .bootstrapTooltip('show')
}) })
test('should not fire shown event when show was prevented', function () { test('should not fire shown event when show was prevented', function (assert) {
stop() var done = assert.async()
$('<div title="tooltip title"/>') $('<div title="tooltip title"/>')
.on('show.bs.tooltip', function (e) { .on('show.bs.tooltip', function (e) {
e.preventDefault() e.preventDefault()
ok(true, 'show event fired') ok(true, 'show event fired')
start() done()
}) })
.on('shown.bs.tooltip', function () { .on('shown.bs.tooltip', function () {
ok(false, 'shown event fired') ok(false, 'shown event fired')
...@@ -153,8 +153,8 @@ $(function () { ...@@ -153,8 +153,8 @@ $(function () {
.bootstrapTooltip('show') .bootstrapTooltip('show')
}) })
test('should fire hide event', function () { test('should fire hide event', function (assert) {
stop() var done = assert.async()
$('<div title="tooltip title"/>') $('<div title="tooltip title"/>')
.appendTo('#qunit-fixture') .appendTo('#qunit-fixture')
...@@ -163,13 +163,13 @@ $(function () { ...@@ -163,13 +163,13 @@ $(function () {
}) })
.on('hide.bs.tooltip', function () { .on('hide.bs.tooltip', function () {
ok(true, 'hide event fired') ok(true, 'hide event fired')
start() done()
}) })
.bootstrapTooltip('show') .bootstrapTooltip('show')
}) })
test('should fire hidden event', function () { test('should fire hidden event', function (assert) {
stop() var done = assert.async()
$('<div title="tooltip title"/>') $('<div title="tooltip title"/>')
.appendTo('#qunit-fixture') .appendTo('#qunit-fixture')
...@@ -178,13 +178,13 @@ $(function () { ...@@ -178,13 +178,13 @@ $(function () {
}) })
.on('hidden.bs.tooltip', function () { .on('hidden.bs.tooltip', function () {
ok(true, 'hidden event fired') ok(true, 'hidden event fired')
start() done()
}) })
.bootstrapTooltip('show') .bootstrapTooltip('show')
}) })
test('should not fire hidden event when hide was prevented', function () { test('should not fire hidden event when hide was prevented', function (assert) {
stop() var done = assert.async()
$('<div title="tooltip title"/>') $('<div title="tooltip title"/>')
.appendTo('#qunit-fixture') .appendTo('#qunit-fixture')
...@@ -194,7 +194,7 @@ $(function () { ...@@ -194,7 +194,7 @@ $(function () {
.on('hide.bs.tooltip', function (e) { .on('hide.bs.tooltip', function (e) {
e.preventDefault() e.preventDefault()
ok(true, 'hide event fired') ok(true, 'hide event fired')
start() done()
}) })
.on('hidden.bs.tooltip', function () { .on('hidden.bs.tooltip', function () {
ok(false, 'hidden event fired') ok(false, 'hidden event fired')
...@@ -699,8 +699,8 @@ $(function () { ...@@ -699,8 +699,8 @@ $(function () {
ok(passed, '.tooltip(\'show\') should not throw an error if element no longer is in dom') 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 () { test('should place tooltip on top of element', function (assert) {
stop() var done = assert.async()
var containerHTML = '<div>' var containerHTML = '<div>'
+ '<p style="margin-top: 200px">' + '<p style="margin-top: 200px">'
...@@ -732,12 +732,12 @@ $(function () { ...@@ -732,12 +732,12 @@ $(function () {
setTimeout(function () { setTimeout(function () {
ok(Math.round($tooltip.offset().top + $tooltip.outerHeight()) <= Math.round($trigger.offset().top)) ok(Math.round($tooltip.offset().top + $tooltip.outerHeight()) <= Math.round($trigger.offset().top))
start() done()
}, 0) }, 0)
}) })
test('should place tooltip inside viewport', function () { test('should place tooltip inside viewport', function (assert) {
stop() var done = assert.async()
var $container = $('<div/>') var $container = $('<div/>')
.css({ .css({
...@@ -763,12 +763,12 @@ $(function () { ...@@ -763,12 +763,12 @@ $(function () {
setTimeout(function () { setTimeout(function () {
ok($('.tooltip').offset().left >= 0) ok($('.tooltip').offset().left >= 0)
start() done()
}, 0) }, 0)
}) })
test('should show tooltip if leave event hasn\'t occurred before delay expires', function () { test('should show tooltip if leave event hasn\'t occurred before delay expires', function (assert) {
stop() var done = assert.async()
var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"/>') var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"/>')
.appendTo('#qunit-fixture') .appendTo('#qunit-fixture')
...@@ -780,14 +780,14 @@ $(function () { ...@@ -780,14 +780,14 @@ $(function () {
setTimeout(function () { setTimeout(function () {
ok($('.tooltip').is('.fade.in'), '200ms: tooltip is faded in') ok($('.tooltip').is('.fade.in'), '200ms: tooltip is faded in')
start() done()
}, 200) }, 200)
$tooltip.trigger('mouseenter') $tooltip.trigger('mouseenter')
}) })
test('should not show tooltip if leave event occurs before delay expires', function () { test('should not show tooltip if leave event occurs before delay expires', function (assert) {
stop() var done = assert.async()
var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"/>') var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"/>')
.appendTo('#qunit-fixture') .appendTo('#qunit-fixture')
...@@ -800,14 +800,14 @@ $(function () { ...@@ -800,14 +800,14 @@ $(function () {
setTimeout(function () { setTimeout(function () {
ok(!$('.tooltip').is('.fade.in'), '200ms: tooltip not faded in') ok(!$('.tooltip').is('.fade.in'), '200ms: tooltip not faded in')
start() done()
}, 200) }, 200)
$tooltip.trigger('mouseenter') $tooltip.trigger('mouseenter')
}) })
test('should not hide tooltip if leave event occurs and enter event occurs within the hide delay', function () { test('should not hide tooltip if leave event occurs and enter event occurs within the hide delay', function (assert) {
stop() var done = assert.async()
var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"/>') var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"/>')
.appendTo('#qunit-fixture') .appendTo('#qunit-fixture')
...@@ -824,15 +824,15 @@ $(function () { ...@@ -824,15 +824,15 @@ $(function () {
setTimeout(function () { setTimeout(function () {
ok($('.tooltip').is('.fade.in'), '200ms: tooltip still faded in') ok($('.tooltip').is('.fade.in'), '200ms: tooltip still faded in')
start() done()
}, 200) }, 200)
}, 0) }, 0)
$tooltip.trigger('mouseenter') $tooltip.trigger('mouseenter')
}) })
test('should not show tooltip if leave event occurs before delay expires', function () { test('should not show tooltip if leave event occurs before delay expires', function (assert) {
stop() var done = assert.async()
var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"/>') var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"/>')
.appendTo('#qunit-fixture') .appendTo('#qunit-fixture')
...@@ -845,14 +845,14 @@ $(function () { ...@@ -845,14 +845,14 @@ $(function () {
setTimeout(function () { setTimeout(function () {
ok(!$('.tooltip').is('.fade.in'), '200ms: tooltip not faded in') ok(!$('.tooltip').is('.fade.in'), '200ms: tooltip not faded in')
start() done()
}, 200) }, 200)
$tooltip.trigger('mouseenter') $tooltip.trigger('mouseenter')
}) })
test('should not show tooltip if leave event occurs before delay expires, even if hide delay is 0', function () { test('should not show tooltip if leave event occurs before delay expires, even if hide delay is 0', function (assert) {
stop() var done = assert.async()
var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"/>') var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"/>')
.appendTo('#qunit-fixture') .appendTo('#qunit-fixture')
...@@ -865,14 +865,14 @@ $(function () { ...@@ -865,14 +865,14 @@ $(function () {
setTimeout(function () { setTimeout(function () {
ok(!$('.tooltip').is('.fade.in'), '250ms: tooltip not faded in') ok(!$('.tooltip').is('.fade.in'), '250ms: tooltip not faded in')
start() done()
}, 250) }, 250)
$tooltip.trigger('mouseenter') $tooltip.trigger('mouseenter')
}) })
test('should wait 200ms before hiding the tooltip', function () { test('should wait 200ms before hiding the tooltip', function (assert) {
stop() var done = assert.async()
var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"/>') var $tooltip = $('<a href="#" rel="tooltip" title="Another tooltip"/>')
.appendTo('#qunit-fixture') .appendTo('#qunit-fixture')
...@@ -889,7 +889,7 @@ $(function () { ...@@ -889,7 +889,7 @@ $(function () {
setTimeout(function () { setTimeout(function () {
ok(!$tooltip.data('bs.tooltip').$tip.is('.in'), '200ms: tooltip removed') ok(!$tooltip.data('bs.tooltip').$tip.is('.in'), '200ms: tooltip removed')
start() done()
}, 200) }, 200)
}, 0) }, 0)
...@@ -897,14 +897,14 @@ $(function () { ...@@ -897,14 +897,14 @@ $(function () {
$tooltip.trigger('mouseenter') $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) { if (!window.SVGElement) {
// Skip IE8 since it doesn't support SVG // Skip IE8 since it doesn't support SVG
expect(0) expect(0)
return return
} }
stop() var done = assert.async()
var styles = '<style>' var styles = '<style>'
+ '.tooltip, .tooltip *, .tooltip *:before, .tooltip *:after { box-sizing: border-box; }' + '.tooltip, .tooltip *, .tooltip *:before, .tooltip *:after { box-sizing: border-box; }'
...@@ -928,15 +928,15 @@ $(function () { ...@@ -928,15 +928,15 @@ $(function () {
ok(Math.abs(offset.left - 88) <= 1, 'tooltip has correct horizontal location') ok(Math.abs(offset.left - 88) <= 1, 'tooltip has correct horizontal location')
$circle.bootstrapTooltip('hide') $circle.bootstrapTooltip('hide')
equal($('.tooltip').length, 0, 'tooltip removed from dom') equal($('.tooltip').length, 0, 'tooltip removed from dom')
start() done()
}) })
.bootstrapTooltip({ container: 'body', placement: 'top', trigger: 'manual' }) .bootstrapTooltip({ container: 'body', placement: 'top', trigger: 'manual' })
$circle.bootstrapTooltip('show') $circle.bootstrapTooltip('show')
}) })
test('should correctly determine auto placement based on container rather than parent', function () { test('should correctly determine auto placement based on container rather than parent', function (assert) {
stop() var done = assert.async()
var styles = '<style>' var styles = '<style>'
+ '.tooltip, .tooltip *, .tooltip *:before, .tooltip *:after { box-sizing: border-box; }' + '.tooltip, .tooltip *, .tooltip *:before, .tooltip *:after { box-sizing: border-box; }'
...@@ -965,7 +965,7 @@ $(function () { ...@@ -965,7 +965,7 @@ $(function () {
$styles.remove() $styles.remove()
$(this).remove() $(this).remove()
equal($('.tooltip').length, 0, 'tooltip removed from dom') equal($('.tooltip').length, 0, 'tooltip removed from dom')
start() done()
}) })
.bootstrapTooltip({ .bootstrapTooltip({
container: 'body', container: 'body',
...@@ -1037,8 +1037,8 @@ $(function () { ...@@ -1037,8 +1037,8 @@ $(function () {
equal(currentUid, $('#tt-content').text()) equal(currentUid, $('#tt-content').text())
}) })
test('should position arrow correctly when tooltip is moved to not appear offscreen', function () { test('should position arrow correctly when tooltip is moved to not appear offscreen', function (assert) {
stop() var done = assert.async()
var styles = '<style>' var styles = '<style>'
+ '.tooltip, .tooltip *, .tooltip *:before, .tooltip *:after { box-sizing: border-box; }' + '.tooltip, .tooltip *, .tooltip *:before, .tooltip *:after { box-sizing: border-box; }'
...@@ -1059,7 +1059,7 @@ $(function () { ...@@ -1059,7 +1059,7 @@ $(function () {
$styles.remove() $styles.remove()
$(this).remove() $(this).remove()
equal($('.tooltip').length, 0, 'tooltip removed from dom') equal($('.tooltip').length, 0, 'tooltip removed from dom')
start() done()
}) })
.bootstrapTooltip({ .bootstrapTooltip({
container: 'body', container: 'body',
...@@ -1069,14 +1069,14 @@ $(function () { ...@@ -1069,14 +1069,14 @@ $(function () {
.bootstrapTooltip('show') .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 var styleProps = document.documentElement.style
if (!('transform' in styleProps) && !('webkitTransform' in styleProps) && !('msTransform' in styleProps)) { if (!('transform' in styleProps) && !('webkitTransform' in styleProps) && !('msTransform' in styleProps)) {
expect(0) expect(0)
return return
} }
stop() var done = assert.async()
var styles = '<style>' var styles = '<style>'
+ '#qunit-fixture { top: 0; left: 0; }' + '#qunit-fixture { top: 0; left: 0; }'
...@@ -1096,7 +1096,7 @@ $(function () { ...@@ -1096,7 +1096,7 @@ $(function () {
ok(Math.abs(offset.left - 88) <= 1, 'tooltip has correct horizontal location') ok(Math.abs(offset.left - 88) <= 1, 'tooltip has correct horizontal location')
ok(Math.abs(offset.top - 126) <= 1, 'tooltip has correct vertical location') ok(Math.abs(offset.top - 126) <= 1, 'tooltip has correct vertical location')
$element.bootstrapTooltip('hide') $element.bootstrapTooltip('hide')
start() done()
}) })
.bootstrapTooltip({ .bootstrapTooltip({
container: 'body', container: 'body',
......
/*! /*!
* QUnit 1.15.0 * QUnit 1.17.0
* http://qunitjs.com/ * http://qunitjs.com/
* *
* Copyright 2014 jQuery Foundation and other contributors * Copyright jQuery Foundation and other contributors
* Released under the MIT license * Released under the MIT license
* http://jquery.org/license * http://jquery.org/license
* *
* Date: 2014-08-08T16:00Z * Date: 2015-01-19T11:58Z
*/ */
/** Font Family and Sizes */ /** Font Family and Sizes */
...@@ -77,6 +77,18 @@ ...@@ -77,6 +77,18 @@
#qunit-modulefilter-container { #qunit-modulefilter-container {
float: right; 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 */ /** Tests: Pass/Fail */
...@@ -91,7 +103,19 @@ ...@@ -91,7 +103,19 @@
list-style-position: inside; 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; display: none;
} }
...@@ -99,6 +123,10 @@ ...@@ -99,6 +123,10 @@
cursor: pointer; cursor: pointer;
} }
#qunit-tests li.skipped strong {
cursor: default;
}
#qunit-tests li a { #qunit-tests li a {
padding: 0.5em; padding: 0.5em;
color: #C2CCD1; color: #C2CCD1;
...@@ -211,6 +239,21 @@ ...@@ -211,6 +239,21 @@
#qunit-banner.qunit-fail { background-color: #EE5757; } #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 */ /** Result */
......
/*! /*!
* QUnit 1.15.0 * QUnit 1.17.0
* http://qunitjs.com/ * http://qunitjs.com/
* *
* Copyright 2014 jQuery Foundation and other contributors * Copyright jQuery Foundation and other contributors
* Released under the MIT license * Released under the MIT license
* http://jquery.org/license * http://jquery.org/license
* *
* Date: 2014-08-08T16:00Z * Date: 2015-01-19T11:58Z
*/ */
(function( window ) { (function( window ) {
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
var QUnit, var QUnit,
config, config,
onErrorFnPrev, onErrorFnPrev,
loggingCallbacks = {},
fileName = ( sourceFromStacktrace( 0 ) || "" ).replace( /(:\d+)+\)?/, "" ).replace( /.+\//, "" ), fileName = ( sourceFromStacktrace( 0 ) || "" ).replace( /(:\d+)+\)?/, "" ).replace( /.+\//, "" ),
toString = Object.prototype.toString, toString = Object.prototype.toString,
hasOwn = Object.prototype.hasOwnProperty, hasOwn = Object.prototype.hasOwnProperty,
...@@ -22,11 +23,13 @@ var QUnit, ...@@ -22,11 +23,13 @@ var QUnit,
now = Date.now || function() { now = Date.now || function() {
return new Date().getTime(); return new Date().getTime();
}, },
globalStartCalled = false,
runStarted = false,
setTimeout = window.setTimeout, setTimeout = window.setTimeout,
clearTimeout = window.clearTimeout, clearTimeout = window.clearTimeout,
defined = { defined = {
document: typeof window.document !== "undefined", document: window.document !== undefined,
setTimeout: typeof window.setTimeout !== "undefined", setTimeout: window.setTimeout !== undefined,
sessionStorage: (function() { sessionStorage: (function() {
var x = "qunit-test-string"; var x = "qunit-test-string";
try { try {
...@@ -86,132 +89,7 @@ var QUnit, ...@@ -86,132 +89,7 @@ var QUnit,
return vals; return vals;
}; };
// Root QUnit object. QUnit = {};
// `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;
}());
/** /**
* Config object: Maintain internal state * Config object: Maintain internal state
...@@ -225,10 +103,6 @@ config = { ...@@ -225,10 +103,6 @@ config = {
// block until document ready // block until document ready
blocking: true, 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 // by default, run previously failed tests first
// very useful in combination with "Hide passed tests" checked // very useful in combination with "Hide passed tests" checked
reorder: true, reorder: true,
...@@ -245,24 +119,40 @@ config = { ...@@ -245,24 +119,40 @@ config = {
// add checkboxes that are persisted in the query-string // add checkboxes that are persisted in the query-string
// when enabled, the id is set to `true` as a `QUnit.config` property // when enabled, the id is set to `true` as a `QUnit.config` property
urlConfig: [ urlConfig: [
{
id: "hidepassed",
label: "Hide passed tests",
tooltip: "Only show tests and assertions that fail. Stored as query-strings."
},
{ {
id: "noglobals", id: "noglobals",
label: "Check for Globals", 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", id: "notrycatch",
label: "No try-catch", 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. // Set of all modules.
modules: {}, modules: [],
// The first unnamed module
currentModule: {
name: "",
tests: []
},
callbacks: {} callbacks: {}
}; };
// Push a loose unnamed module to the modules collection
config.modules.push( config.currentModule );
// Initialize more QUnit.config and QUnit.urlParams // Initialize more QUnit.config and QUnit.urlParams
(function() { (function() {
var i, current, var i, current,
...@@ -286,22 +176,22 @@ config = { ...@@ -286,22 +176,22 @@ config = {
} }
} }
if ( urlParams.filter === true ) {
delete urlParams.filter;
}
QUnit.urlParams = urlParams; QUnit.urlParams = urlParams;
// String search anywhere in moduleName+testName // String search anywhere in moduleName+testName
config.filter = urlParams.filter; config.filter = urlParams.filter;
// Exact match of the module name config.testId = [];
config.module = urlParams.module; if ( urlParams.testId ) {
config.testNumber = [];
if ( urlParams.testNumber ) {
// Ensure that urlParams.testNumber is an array // Ensure that urlParams.testId is an array
urlParams.testNumber = [].concat( urlParams.testNumber ); urlParams.testId = [].concat( urlParams.testId );
for ( i = 0; i < urlParams.testNumber.length; i++ ) { for ( i = 0; i < urlParams.testId.length; i++ ) {
current = urlParams.testNumber[ i ]; config.testId.push( urlParams.testId[ i ] );
config.testNumber.push( parseInt( current, 10 ) );
} }
} }
...@@ -309,8 +199,130 @@ config = { ...@@ -309,8 +199,130 @@ config = {
QUnit.isLocal = location.protocol === "file:"; QUnit.isLocal = location.protocol === "file:";
}()); }());
// Root QUnit object.
// `QUnit` initialized at top of scope
extend( QUnit, { 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, config: config,
// Safe object type checking // Safe object type checking
...@@ -351,60 +363,10 @@ extend( QUnit, { ...@@ -351,60 +363,10 @@ extend( QUnit, {
return undefined; return undefined;
}, },
url: function( params ) { extend: extend,
params = extend( extend( {}, QUnit.urlParams ), params );
var key,
querystring = "?";
for ( key in params ) {
if ( hasOwn.call( params, key ) ) {
querystring += encodeURIComponent( key ) + "=" +
encodeURIComponent( params[ key ] ) + "&";
}
}
return window.location.protocol + "//" + window.location.host +
window.location.pathname + querystring.slice( 0, -1 );
},
extend: extend
});
/** load: function() {
* @deprecated: Created for backwards compatibility with test runner that set the hook function config.pageLoaded = true;
* 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 // Initialize the configuration options
extend( config, { extend( config, {
...@@ -413,16 +375,53 @@ QUnit.load = function() { ...@@ -413,16 +375,53 @@ QUnit.load = function() {
started: 0, started: 0,
updateRate: 1000, updateRate: 1000,
autostart: true, autostart: true,
filter: "", filter: ""
semaphore: 1
}, true ); }, true );
config.blocking = false; config.blocking = false;
if ( config.autostart ) { if ( config.autostart ) {
QUnit.start(); resumeProcessing();
} }
}; }
});
// 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 // `onErrorFnPrev` initialized at top of scope
// Preserve other handlers // Preserve other handlers
...@@ -448,7 +447,7 @@ window.onerror = function( error, filePath, linerNr ) { ...@@ -448,7 +447,7 @@ window.onerror = function( error, filePath, linerNr ) {
} else { } else {
QUnit.test( "global failure", extend(function() { QUnit.test( "global failure", extend(function() {
QUnit.pushFailure( error, filePath + ":" + linerNr ); QUnit.pushFailure( error, filePath + ":" + linerNr );
}, { validTest: validTest } ) ); }, { validTest: true } ) );
} }
return false; return false;
} }
...@@ -457,20 +456,24 @@ window.onerror = function( error, filePath, linerNr ) { ...@@ -457,20 +456,24 @@ window.onerror = function( error, filePath, linerNr ) {
}; };
function done() { function done() {
var runtime, passed;
config.autorun = true; config.autorun = true;
// Log the last module results // Log the last module results
if ( config.previousModule ) { if ( config.previousModule ) {
runLoggingCallbacks( "moduleDone", { runLoggingCallbacks( "moduleDone", {
name: config.previousModule, name: config.previousModule.name,
tests: config.previousModule.tests,
failed: config.moduleStats.bad, failed: config.moduleStats.bad,
passed: config.moduleStats.all - 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; delete config.previousModule;
var runtime = now() - config.started, runtime = now() - config.started;
passed = config.stats.all - config.stats.bad; passed = config.stats.all - config.stats.bad;
runLoggingCallbacks( "done", { runLoggingCallbacks( "done", {
...@@ -481,47 +484,6 @@ function done() { ...@@ -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 // Doesn't support IE6 to IE9
// See also https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error/Stack // See also https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error/Stack
function extractStacktrace( e, offset ) { function extractStacktrace( e, offset ) {
...@@ -565,15 +527,27 @@ function extractStacktrace( e, offset ) { ...@@ -565,15 +527,27 @@ function extractStacktrace( e, offset ) {
return e.sourceURL + ":" + e.line; return e.sourceURL + ":" + e.line;
} }
} }
function sourceFromStacktrace( offset ) { function sourceFromStacktrace( offset ) {
var e = new Error();
if ( !e.stack ) {
try { try {
throw new Error(); throw e;
} catch ( e ) { } catch ( err ) {
return extractStacktrace( e, offset ); // This should already be true in most browsers
e = err;
} }
}
return extractStacktrace( e, offset );
} }
function synchronize( callback, last ) { function synchronize( callback, last ) {
if ( QUnit.objectType( callback ) === "array" ) {
while ( callback.length ) {
synchronize( callback.shift() );
}
return;
}
config.queue.push( callback ); config.queue.push( callback );
if ( config.autorun && !config.blocking ) { if ( config.autorun && !config.blocking ) {
...@@ -586,10 +560,16 @@ function process( last ) { ...@@ -586,10 +560,16 @@ function process( last ) {
process( last ); process( last );
} }
var start = now(); var start = now();
config.depth = config.depth ? config.depth + 1 : 1; config.depth = ( config.depth || 0 ) + 1;
while ( config.queue.length && !config.blocking ) { 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()(); config.queue.shift()();
} else { } else {
setTimeout( next, 13 ); setTimeout( next, 13 );
...@@ -602,6 +582,79 @@ function process( last ) { ...@@ -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() { function saveGlobal() {
config.pollution = []; config.pollution = [];
...@@ -671,18 +724,6 @@ function extend( a, b, undefOnly ) { ...@@ -671,18 +724,6 @@ function extend( a, b, undefOnly ) {
return a; 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 ) { function runLoggingCallbacks( key, args ) {
var i, l, callbacks; var i, l, callbacks;
...@@ -692,6 +733,34 @@ function runLoggingCallbacks( key, args ) { ...@@ -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 // from jquery.js
function inArray( elem, array ) { function inArray( elem, array ) {
if ( array.indexOf ) { if ( array.indexOf ) {
...@@ -708,16 +777,46 @@ function inArray( elem, array ) { ...@@ -708,16 +777,46 @@ function inArray( elem, array ) {
} }
function Test( settings ) { function Test( settings ) {
var i, l;
++Test.count;
extend( this, settings ); extend( this, settings );
this.assert = new Assert( this );
this.assertions = []; 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.count = 0;
Test.prototype = { Test.prototype = {
setup: function() { before: function() {
if ( if (
// Emit moduleStart when we're switching from one module to another // Emit moduleStart when we're switching from one module to another
...@@ -731,47 +830,43 @@ Test.prototype = { ...@@ -731,47 +830,43 @@ Test.prototype = {
) { ) {
if ( hasOwn.call( config, "previousModule" ) ) { if ( hasOwn.call( config, "previousModule" ) ) {
runLoggingCallbacks( "moduleDone", { runLoggingCallbacks( "moduleDone", {
name: config.previousModule, name: config.previousModule.name,
tests: config.previousModule.tests,
failed: config.moduleStats.bad, failed: config.moduleStats.bad,
passed: config.moduleStats.all - 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.previousModule = this.module;
config.moduleStats = { all: 0, bad: 0 }; config.moduleStats = { all: 0, bad: 0, started: now() };
runLoggingCallbacks( "moduleStart", { runLoggingCallbacks( "moduleStart", {
name: this.module name: this.module.name,
tests: this.module.tests
}); });
} }
config.current = this; config.current = this;
this.testEnvironment = extend({ this.testEnvironment = extend( {}, this.module.testEnvironment );
setup: function() {}, delete this.testEnvironment.beforeEach;
teardown: function() {} delete this.testEnvironment.afterEach;
}, this.moduleTestEnvironment );
this.started = now(); this.started = now();
runLoggingCallbacks( "testStart", { runLoggingCallbacks( "testStart", {
name: this.testName, name: this.testName,
module: this.module, module: this.module.name,
testNumber: this.testNumber testId: this.testId
}); });
if ( !config.pollution ) { if ( !config.pollution ) {
saveGlobal(); 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() { run: function() {
var promise;
config.current = this; config.current = this;
if ( this.async ) { if ( this.async ) {
...@@ -781,18 +876,17 @@ Test.prototype = { ...@@ -781,18 +876,17 @@ Test.prototype = {
this.callbackStarted = now(); this.callbackStarted = now();
if ( config.notrycatch ) { if ( config.notrycatch ) {
this.callback.call( this.testEnvironment, this.assert ); promise = this.callback.call( this.testEnvironment, this.assert );
this.callbackRuntime = now() - this.callbackStarted; this.resolvePromise( promise );
return; return;
} }
try { try {
this.callback.call( this.testEnvironment, this.assert ); promise = this.callback.call( this.testEnvironment, this.assert );
this.callbackRuntime = now() - this.callbackStarted; this.resolvePromise( promise );
} catch ( e ) { } 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 // else next test will carry the responsibility
saveGlobal(); saveGlobal();
...@@ -803,31 +897,59 @@ Test.prototype = { ...@@ -803,31 +897,59 @@ Test.prototype = {
} }
} }
}, },
teardown: function() {
config.current = this; after: function() {
checkPollution();
},
queueHook: function( hook, hookName ) {
var promise,
test = this;
return function runHook() {
config.current = test;
if ( config.notrycatch ) { if ( config.notrycatch ) {
if ( typeof this.callbackRuntime === "undefined" ) { promise = hook.call( test.testEnvironment, test.assert );
this.callbackRuntime = now() - this.callbackStarted; test.resolvePromise( promise, hookName );
}
this.testEnvironment.teardown.call( this.testEnvironment, this.assert );
return; return;
} else { }
try { try {
this.testEnvironment.teardown.call( this.testEnvironment, this.assert ); promise = hook.call( test.testEnvironment, test.assert );
} catch ( e ) { test.resolvePromise( promise, hookName );
this.pushFailure( "Teardown failed on " + this.testName + ": " + ( e.message || e ), extractStacktrace( e, 0 ) ); } 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;
} }
checkPollution();
if ( this.module.testEnvironment &&
QUnit.objectType( this.module.testEnvironment[ handler ] ) === "function" ) {
hooks.push( this.queueHook( this.module.testEnvironment[ handler ], handler ) );
}
return hooks;
}, },
finish: function() { finish: function() {
config.current = this; config.current = this;
if ( config.requireExpects && this.expected === null ) { 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 ) { } 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 ) { } 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, var i,
...@@ -847,7 +969,8 @@ Test.prototype = { ...@@ -847,7 +969,8 @@ Test.prototype = {
runLoggingCallbacks( "testDone", { runLoggingCallbacks( "testDone", {
name: this.testName, name: this.testName,
module: this.module, module: this.module.name,
skipped: !!this.skip,
failed: bad, failed: bad,
passed: this.assertions.length - bad, passed: this.assertions.length - bad,
total: this.assertions.length, total: this.assertions.length,
...@@ -855,12 +978,17 @@ Test.prototype = { ...@@ -855,12 +978,17 @@ Test.prototype = {
// HTML Reporter use // HTML Reporter use
assertions: this.assertions, assertions: this.assertions,
testNumber: this.testNumber, testId: this.testId,
// DEPRECATED: this property will be removed in 2.0.0, use runtime instead // DEPRECATED: this property will be removed in 2.0.0, use runtime instead
duration: this.runtime 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; config.current = undefined;
}, },
...@@ -868,26 +996,39 @@ Test.prototype = { ...@@ -868,26 +996,39 @@ Test.prototype = {
var bad, var bad,
test = this; test = this;
if ( !this.valid() ) {
return;
}
function run() { function run() {
// each of these can by async // each of these can by async
synchronize(function() { synchronize([
test.setup(); function() {
}); test.before();
synchronize(function() { },
test.hooks( "beforeEach" ),
function() {
test.run(); test.run();
}); },
synchronize(function() {
test.teardown(); test.hooks( "afterEach" ).reverse(),
});
synchronize(function() { function() {
test.after();
},
function() {
test.finish(); test.finish();
}); }
]);
} }
// `bad` initialized at top of scope // `bad` initialized at top of scope
// defer when previous test run passed, if storage is available // defer when previous test run passed, if storage is available
bad = QUnit.config.reorder && defined.sessionStorage && 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 ) { if ( bad ) {
run(); run();
...@@ -899,13 +1040,14 @@ Test.prototype = { ...@@ -899,13 +1040,14 @@ Test.prototype = {
push: function( result, actual, expected, message ) { push: function( result, actual, expected, message ) {
var source, var source,
details = { details = {
module: this.module, module: this.module.name,
name: this.testName, name: this.testName,
result: result, result: result,
message: message, message: message,
actual: actual, actual: actual,
expected: expected, expected: expected,
testNumber: this.testNumber testId: this.testId,
runtime: now() - this.started
}; };
if ( !result ) { if ( !result ) {
...@@ -926,16 +1068,18 @@ Test.prototype = { ...@@ -926,16 +1068,18 @@ Test.prototype = {
pushFailure: function( message, source, actual ) { pushFailure: function( message, source, actual ) {
if ( !this instanceof Test ) { 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 = { var details = {
module: this.module, module: this.module.name,
name: this.testName, name: this.testName,
result: false, result: false,
message: message || "error", message: message || "error",
actual: actual || null, actual: actual || null,
testNumber: this.testNumber testId: this.testId,
runtime: now() - this.started
}; };
if ( source ) { if ( source ) {
...@@ -948,20 +1092,132 @@ Test.prototype = { ...@@ -948,20 +1092,132 @@ Test.prototype = {
result: false, result: false,
message: message 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() { QUnit.pushFailure = function() {
if ( !QUnit.config.current ) { 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 // Gets current test obj
var currentTest = QUnit.config.current.assert.test; var currentTest = QUnit.config.current;
return currentTest.pushFailure.apply( currentTest, arguments ); 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 ) { function Assert( testContext ) {
this.test = testContext; this.test = testContext;
} }
...@@ -969,7 +1225,8 @@ function Assert( testContext ) { ...@@ -969,7 +1225,8 @@ function Assert( testContext ) {
// Assert helpers // Assert helpers
QUnit.assert = Assert.prototype = { 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 ) { expect: function( asserts ) {
if ( arguments.length === 1 ) { if ( arguments.length === 1 ) {
this.test.expected = asserts; this.test.expected = asserts;
...@@ -978,20 +1235,51 @@ QUnit.assert = Assert.prototype = { ...@@ -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 // Exports test.push() to the user API
push: function() { push: function( /* result, actual, expected, message */ ) {
var assert = this; var assert = this,
currentTest = ( assert instanceof Assert && assert.test ) || QUnit.config.current;
// Backwards compatibility fix. // Backwards compatibility fix.
// Allows the direct use of global exported assertions and QUnit.assert.* // Allows the direct use of global exported assertions and QUnit.assert.*
// Although, it's use is not recommended as it can leak assertions // 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, // 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. // 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 ) ); 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 ) ) { if ( !( assert instanceof Assert ) ) {
assert = QUnit.config.current.assert; assert = currentTest.assert;
} }
return assert.test.push.apply( assert.test, arguments ); return assert.test.push.apply( assert.test, arguments );
}, },
...@@ -1005,11 +1293,7 @@ QUnit.assert = Assert.prototype = { ...@@ -1005,11 +1293,7 @@ QUnit.assert = Assert.prototype = {
ok: function( result, message ) { ok: function( result, message ) {
message = message || ( result ? "okay" : "failed, expected argument to be truthy, was: " + message = message || ( result ? "okay" : "failed, expected argument to be truthy, was: " +
QUnit.dump.parse( result ) ); QUnit.dump.parse( result ) );
if ( !!result ) { this.push( !!result, result, true, message );
this.push( true, result, true, message );
} else {
this.test.pushFailure( message, null, result );
}
}, },
/** /**
...@@ -1017,7 +1301,7 @@ QUnit.assert = Assert.prototype = { ...@@ -1017,7 +1301,7 @@ QUnit.assert = Assert.prototype = {
* Prints out both actual and expected values. * Prints out both actual and expected values.
* @name equal * @name equal
* @function * @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 ) { equal: function( actual, expected, message ) {
/*jshint eqeqeq:false */ /*jshint eqeqeq:false */
...@@ -1143,6 +1427,13 @@ QUnit.assert = Assert.prototype = { ...@@ -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. // Test for equality any JavaScript type.
// Author: Philippe Rathé <prathe@gmail.com> // Author: Philippe Rathé <prathe@gmail.com>
QUnit.equiv = (function() { QUnit.equiv = (function() {
...@@ -1356,7 +1647,8 @@ QUnit.equiv = (function() { ...@@ -1356,7 +1647,8 @@ QUnit.equiv = (function() {
} }
// apply transition with (1..n) arguments // 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; return innerEquiv;
...@@ -1386,6 +1678,11 @@ QUnit.dump = (function() { ...@@ -1386,6 +1678,11 @@ QUnit.dump = (function() {
function array( arr, stack ) { function array( arr, stack ) {
var i = arr.length, var i = arr.length,
ret = new Array( i ); ret = new Array( i );
if ( dump.maxDepth && dump.depth > dump.maxDepth ) {
return "[object Array]";
}
this.up(); this.up();
while ( i-- ) { while ( i-- ) {
ret[ i ] = this.parse( arr[ i ], undefined, stack ); ret[ i ] = this.parse( arr[ i ], undefined, stack );
...@@ -1396,25 +1693,28 @@ QUnit.dump = (function() { ...@@ -1396,25 +1693,28 @@ QUnit.dump = (function() {
var reName = /^function (\w+)/, var reName = /^function (\w+)/,
dump = { 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; // 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 ); inStack = inArray( obj, stack );
if ( inStack !== -1 ) { if ( inStack !== -1 ) {
return "recursion(" + ( inStack - stack.length ) + ")"; 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 ); stack.push( obj );
res = parser.call( this, obj, stack ); res = parser.call( this, obj, stack );
stack.pop(); stack.pop();
return res; return res;
} }
return ( type === "string" ) ? parser : this.parsers.error; return ( parserType === "string" ) ? parser : this.parsers.error;
}, },
typeOf: function( obj ) { typeOf: function( obj ) {
var type; var type;
...@@ -1428,7 +1728,9 @@ QUnit.dump = (function() { ...@@ -1428,7 +1728,9 @@ QUnit.dump = (function() {
type = "date"; type = "date";
} else if ( QUnit.is( "function", obj ) ) { } else if ( QUnit.is( "function", obj ) ) {
type = "function"; 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"; type = "window";
} else if ( obj.nodeType === 9 ) { } else if ( obj.nodeType === 9 ) {
type = "document"; type = "document";
...@@ -1440,7 +1742,9 @@ QUnit.dump = (function() { ...@@ -1440,7 +1742,9 @@ QUnit.dump = (function() {
toString.call( obj ) === "[object Array]" || toString.call( obj ) === "[object Array]" ||
// NodeList objects // 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"; type = "array";
} else if ( obj.constructor === Error.prototype.constructor ) { } else if ( obj.constructor === Error.prototype.constructor ) {
...@@ -1451,7 +1755,7 @@ QUnit.dump = (function() { ...@@ -1451,7 +1755,7 @@ QUnit.dump = (function() {
return type; return type;
}, },
separator: function() { separator: function() {
return this.multiline ? this.HTML ? "<br />" : "\n" : this.HTML ? "&nbsp;" : " "; return this.multiline ? this.HTML ? "<br />" : "\n" : this.HTML ? "&#160;" : " ";
}, },
// extra can be a number, shortcut for increasing-calling-decreasing // extra can be a number, shortcut for increasing-calling-decreasing
indent: function( extra ) { indent: function( extra ) {
...@@ -1460,7 +1764,7 @@ QUnit.dump = (function() { ...@@ -1460,7 +1764,7 @@ QUnit.dump = (function() {
} }
var chr = this.indentChar; var chr = this.indentChar;
if ( this.HTML ) { if ( this.HTML ) {
chr = chr.replace( /\t/g, " " ).replace( / /g, "&nbsp;" ); chr = chr.replace( /\t/g, " " ).replace( / /g, "&#160;" );
} }
return new Array( this.depth + ( extra || 0 ) ).join( chr ); return new Array( this.depth + ( extra || 0 ) ).join( chr );
}, },
...@@ -1479,6 +1783,8 @@ QUnit.dump = (function() { ...@@ -1479,6 +1783,8 @@ QUnit.dump = (function() {
join: join, join: join,
// //
depth: 1, depth: 1,
maxDepth: 5,
// This is the list of parsers, to modify them, use dump.setParser // This is the list of parsers, to modify them, use dump.setParser
parsers: { parsers: {
window: "[Window]", window: "[Window]",
...@@ -1491,6 +1797,7 @@ QUnit.dump = (function() { ...@@ -1491,6 +1797,7 @@ QUnit.dump = (function() {
"undefined": "undefined", "undefined": "undefined",
"function": function( fn ) { "function": function( fn ) {
var ret = "function", var ret = "function",
// functions never have name in IE // functions never have name in IE
name = "name" in fn ? fn.name : ( reName.exec( fn ) || [] )[ 1 ]; name = "name" in fn ? fn.name : ( reName.exec( fn ) || [] )[ 1 ];
...@@ -1506,8 +1813,13 @@ QUnit.dump = (function() { ...@@ -1506,8 +1813,13 @@ QUnit.dump = (function() {
nodelist: array, nodelist: array,
"arguments": array, "arguments": array,
object: function( map, stack ) { object: function( map, stack ) {
/*jshint forin:false */ var keys, key, val, i, nonEnumerableProperties,
var ret = [], keys, key, val, i, nonEnumerableProperties; ret = [];
if ( dump.maxDepth && dump.depth > dump.maxDepth ) {
return "[object Object]";
}
dump.up(); dump.up();
keys = []; keys = [];
for ( key in map ) { for ( key in map ) {
...@@ -1526,7 +1838,8 @@ QUnit.dump = (function() { ...@@ -1526,7 +1838,8 @@ QUnit.dump = (function() {
for ( i = 0; i < keys.length; i++ ) { for ( i = 0; i < keys.length; i++ ) {
key = keys[ i ]; key = keys[ i ];
val = map[ key ]; 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(); dump.down();
return join( "{", ret, "}" ); return join( "{", ret, "}" );
...@@ -1543,10 +1856,12 @@ QUnit.dump = (function() { ...@@ -1543,10 +1856,12 @@ QUnit.dump = (function() {
for ( i = 0, len = attrs.length; i < len; i++ ) { for ( i = 0, len = attrs.length; i < len; i++ ) {
val = attrs[ i ].nodeValue; val = attrs[ i ].nodeValue;
// IE6 includes all attributes in .attributes, even ones not explicitly set. // IE6 includes all attributes in .attributes, even ones not explicitly
// Those have values like undefined, null, 0, false, "" or "inherit". // set. Those have values like undefined, null, 0, false, "" or
// "inherit".
if ( val && val !== "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" ) { ...@@ -1653,9 +1968,17 @@ if ( typeof window !== "undefined" ) {
window.QUnit = QUnit; window.QUnit = QUnit;
} }
// For CommonJS environments, export everything // For nodejs
if ( typeof module !== "undefined" && module.exports ) { if ( typeof module !== "undefined" && module && module.exports ) {
module.exports = QUnit; 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 // Get a reference to the global object, like window in browsers
...@@ -1664,6 +1987,7 @@ if ( typeof module !== "undefined" && module.exports ) { ...@@ -1664,6 +1987,7 @@ if ( typeof module !== "undefined" && module.exports ) {
})() )); })() ));
/*istanbul ignore next */ /*istanbul ignore next */
// jscs:disable maximumLineLength
/* /*
* Javascript Diff Algorithm * Javascript Diff Algorithm
* By John Resig (http://ejohn.org/) * By John Resig (http://ejohn.org/)
...@@ -1810,6 +2134,7 @@ QUnit.diff = (function() { ...@@ -1810,6 +2134,7 @@ QUnit.diff = (function() {
return str; return str;
}; };
}()); }());
// jscs:enable
(function() { (function() {
...@@ -1828,7 +2153,6 @@ QUnit.init = function() { ...@@ -1828,7 +2153,6 @@ QUnit.init = function() {
config.autorun = false; config.autorun = false;
config.filter = ""; config.filter = "";
config.queue = []; config.queue = [];
config.semaphore = 1;
// Return on non-browser environments // Return on non-browser environments
// This is necessary to not break on node tests // This is necessary to not break on node tests
...@@ -1867,27 +2191,7 @@ QUnit.init = function() { ...@@ -1867,27 +2191,7 @@ QUnit.init = function() {
result.id = "qunit-testresult"; result.id = "qunit-testresult";
result.className = "result"; result.className = "result";
tests.parentNode.insertBefore( result, tests ); tests.parentNode.insertBefore( result, tests );
result.innerHTML = "Running...<br/>&nbsp;"; result.innerHTML = "Running...<br />&#160;";
}
};
// 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;
} }
}; };
...@@ -1899,7 +2203,7 @@ if ( typeof window === "undefined" ) { ...@@ -1899,7 +2203,7 @@ if ( typeof window === "undefined" ) {
var config = QUnit.config, var config = QUnit.config,
hasOwn = Object.prototype.hasOwnProperty, hasOwn = Object.prototype.hasOwnProperty,
defined = { defined = {
document: typeof window.document !== "undefined", document: window.document !== undefined,
sessionStorage: (function() { sessionStorage: (function() {
var x = "qunit-test-string"; var x = "qunit-test-string";
try { try {
...@@ -1910,7 +2214,8 @@ var config = QUnit.config, ...@@ -1910,7 +2214,8 @@ var config = QUnit.config,
return false; return false;
} }
}()) }())
}; },
modulesList = [];
/** /**
* Escape text for attribute or text content. * Escape text for attribute or text content.
...@@ -2020,13 +2325,16 @@ function getUrlConfigHtml() { ...@@ -2020,13 +2325,16 @@ function getUrlConfigHtml() {
escaped = escapeText( val.id ); escaped = escapeText( val.id );
escapedTooltip = escapeText( val.tooltip ); escapedTooltip = escapeText( val.tooltip );
if ( config[ val.id ] === undefined ) {
config[ val.id ] = QUnit.urlParams[ val.id ]; config[ val.id ] = QUnit.urlParams[ val.id ];
}
if ( !val.value || typeof val.value === "string" ) { if ( !val.value || typeof val.value === "string" ) {
urlConfigHtml += "<input id='qunit-urlconfig-" + escaped + urlConfigHtml += "<input id='qunit-urlconfig-" + escaped +
"' name='" + escaped + "' type='checkbox'" + "' name='" + escaped + "' type='checkbox'" +
( val.value ? " value='" + escapeText( val.value ) + "'" : "" ) + ( val.value ? " value='" + escapeText( val.value ) + "'" : "" ) +
( config[ val.id ] ? " checked='checked'" : "" ) + ( config[ val.id ] ? " checked='checked'" : "" ) +
" title='" + escapedTooltip + "'><label for='qunit-urlconfig-" + escaped + " title='" + escapedTooltip + "' /><label for='qunit-urlconfig-" + escaped +
"' title='" + escapedTooltip + "'>" + val.label + "</label>"; "' title='" + escapedTooltip + "'>" + val.label + "</label>";
} else { } else {
urlConfigHtml += "<label for='qunit-urlconfig-" + escaped + urlConfigHtml += "<label for='qunit-urlconfig-" + escaped +
...@@ -2064,69 +2372,145 @@ function getUrlConfigHtml() { ...@@ -2064,69 +2372,145 @@ function getUrlConfigHtml() {
return urlConfigHtml; 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() { function toolbarUrlConfigContainer() {
var urlConfigContainer = document.createElement( "span" ); var urlConfigContainer = document.createElement( "span" );
urlConfigContainer.innerHTML = getUrlConfigHtml(); urlConfigContainer.innerHTML = getUrlConfigHtml();
addClass( urlConfigContainer, "qunit-url-config" );
// For oldIE support: // For oldIE support:
// * Add handlers to the individual elements instead of the container // * Add handlers to the individual elements instead of the container
// * Use "click" instead of "change" for checkboxes // * Use "click" instead of "change" for checkboxes
// * Fallback from event.target to event.srcElement addEvents( urlConfigContainer.getElementsByTagName( "input" ), "click", toolbarChanged );
addEvents( urlConfigContainer.getElementsByTagName( "input" ), "click", function( event ) { addEvents( urlConfigContainer.getElementsByTagName( "select" ), "change", toolbarChanged );
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 );
});
return urlConfigContainer; return urlConfigContainer;
} }
function getModuleNames() { function toolbarLooseFilter() {
var i, var filter = document.createElement( "form" ),
moduleNames = []; label = document.createElement( "label" ),
input = document.createElement( "input" ),
button = document.createElement( "button" );
for ( i in config.modules ) { addClass( filter, "qunit-filter" );
if ( config.modules.hasOwnProperty( i ) ) {
moduleNames.push( i ); 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 false;
return a.localeCompare( b );
}); });
return moduleNames; return filter;
} }
function toolbarModuleFilterHtml() { function toolbarModuleFilterHtml() {
var i, var i,
moduleFilterHtml = "", moduleFilterHtml = "";
moduleNames = getModuleNames();
if ( moduleNames.length <= 1 ) { if ( !modulesList.length ) {
return false; return false;
} }
modulesList.sort(function( a, b ) {
return a.localeCompare( b );
});
moduleFilterHtml += "<label for='qunit-modulefilter'>Module: </label>" + moduleFilterHtml += "<label for='qunit-modulefilter'>Module: </label>" +
"<select id='qunit-modulefilter' name='modulefilter'><option value='' " + "<select id='qunit-modulefilter' name='modulefilter'><option value='' " +
( config.module === undefined ? "selected='selected'" : "" ) + ( QUnit.urlParams.module === undefined ? "selected='selected'" : "" ) +
">< All Modules ></option>"; ">< All Modules ></option>";
for ( i = 0; i < moduleNames.length; i++ ) { for ( i = 0; i < modulesList.length; i++ ) {
moduleFilterHtml += "<option value='" + moduleFilterHtml += "<option value='" +
escapeText( encodeURIComponent( moduleNames[ i ] ) ) + "' " + escapeText( encodeURIComponent( modulesList[ i ] ) ) + "' " +
( config.module === moduleNames[ i ] ? "selected='selected'" : "" ) + ( QUnit.urlParams.module === modulesList[ i ] ? "selected='selected'" : "" ) +
">" + escapeText( moduleNames[ i ] ) + "</option>"; ">" + escapeText( modulesList[ i ] ) + "</option>";
} }
moduleFilterHtml += "</select>"; moduleFilterHtml += "</select>";
...@@ -2134,7 +2518,8 @@ function toolbarModuleFilterHtml() { ...@@ -2134,7 +2518,8 @@ function toolbarModuleFilterHtml() {
} }
function toolbarModuleFilter() { function toolbarModuleFilter() {
var moduleFilter = document.createElement( "span" ), var toolbar = id( "qunit-testrunner-toolbar" ),
moduleFilter = document.createElement( "span" ),
moduleFilterHtml = toolbarModuleFilterHtml(); moduleFilterHtml = toolbarModuleFilterHtml();
if ( !moduleFilterHtml ) { if ( !moduleFilterHtml ) {
...@@ -2144,75 +2529,27 @@ function toolbarModuleFilter() { ...@@ -2144,75 +2529,27 @@ function toolbarModuleFilter() {
moduleFilter.setAttribute( "id", "qunit-modulefilter-container" ); moduleFilter.setAttribute( "id", "qunit-modulefilter-container" );
moduleFilter.innerHTML = moduleFilterHtml; moduleFilter.innerHTML = moduleFilterHtml;
addEvent( moduleFilter.lastChild, "change", function() { addEvent( moduleFilter.lastChild, "change", applyUrlParams );
var selectBox = moduleFilter.getElementsByTagName( "select" )[ 0 ],
selectedModule = decodeURIComponent( selectBox.options[ selectBox.selectedIndex ].value );
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() { toolbar.appendChild( moduleFilter );
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;
} }
function appendToolbar() { function appendToolbar() {
var moduleFilter, var toolbar = id( "qunit-testrunner-toolbar" );
toolbar = id( "qunit-testrunner-toolbar" );
if ( toolbar ) { if ( toolbar ) {
toolbar.appendChild( toolbarFilter() );
toolbar.appendChild( toolbarLabel() );
toolbar.appendChild( toolbarUrlConfigContainer() ); 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() { ...@@ -2221,9 +2558,6 @@ function appendBanner() {
if ( banner ) { if ( banner ) {
banner.className = ""; banner.className = "";
banner.innerHTML = "<a href='" +
QUnit.url({ filter: undefined, module: undefined, testNumber: undefined }) +
"'>" + banner.innerHTML + "</a> ";
} }
} }
...@@ -2241,7 +2575,7 @@ function appendTestResults() { ...@@ -2241,7 +2575,7 @@ function appendTestResults() {
result.id = "qunit-testresult"; result.id = "qunit-testresult";
result.className = "result"; result.className = "result";
tests.parentNode.insertBefore( result, tests ); tests.parentNode.insertBefore( result, tests );
result.innerHTML = "Running...<br>&nbsp;"; result.innerHTML = "Running...<br />&#160;";
} }
} }
...@@ -2255,14 +2589,64 @@ function storeFixture() { ...@@ -2255,14 +2589,64 @@ function storeFixture() {
function appendUserAgent() { function appendUserAgent() {
var userAgent = id( "qunit-userAgent" ); var userAgent = id( "qunit-userAgent" );
if ( 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 // HTML Reporter initialization and load
QUnit.begin(function() { QUnit.begin(function( details ) {
var qunit = id( "qunit" ); var qunit = id( "qunit" );
// Fixture is the only one necessary to run without the #qunit element
storeFixture();
if ( qunit ) { if ( qunit ) {
qunit.innerHTML = qunit.innerHTML =
"<h1 id='qunit-header'>" + escapeText( document.title ) + "</h1>" + "<h1 id='qunit-header'>" + escapeText( document.title ) + "</h1>" +
...@@ -2272,11 +2656,17 @@ QUnit.begin(function() { ...@@ -2272,11 +2656,17 @@ QUnit.begin(function() {
"<ol id='qunit-tests'></ol>"; "<ol id='qunit-tests'></ol>";
} }
appendHeader();
appendBanner(); appendBanner();
appendTestResults(); appendTestResults();
appendUserAgent(); appendUserAgent();
appendToolbar(); appendToolbar();
storeFixture(); appendTestsList( details.modules );
toolbarModuleFilter();
if ( qunit && config.hidepassed ) {
addClass( qunit.lastChild, "hidepass" );
}
}); });
QUnit.done(function( details ) { QUnit.done(function( details ) {
...@@ -2286,7 +2676,7 @@ QUnit.done(function( details ) { ...@@ -2286,7 +2676,7 @@ QUnit.done(function( details ) {
html = [ html = [
"Tests completed in ", "Tests completed in ",
details.runtime, details.runtime,
" milliseconds.<br>", " milliseconds.<br />",
"<span class='passed'>", "<span class='passed'>",
details.passed, details.passed,
"</span> assertions of <span class='total'>", "</span> assertions of <span class='total'>",
...@@ -2343,35 +2733,20 @@ function getNameHtml( name, module ) { ...@@ -2343,35 +2733,20 @@ function getNameHtml( name, module ) {
} }
QUnit.testStart(function( details ) { QUnit.testStart(function( details ) {
var a, b, li, running, assertList, var running, testBlock;
name = getNameHtml( details.name, details.module ),
tests = id( "qunit-tests" );
if ( tests ) {
b = document.createElement( "strong" );
b.innerHTML = name;
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 ); testBlock = id( "qunit-test-output-" + details.testId );
if ( testBlock ) {
testBlock.className = "running";
} else {
tests.appendChild( li ); // Report later registered tests
appendTest( details.name, details.testId, details.module );
} }
running = id( "qunit-testresult" ); running = id( "qunit-testresult" );
if ( running ) { if ( running ) {
running.innerHTML = "Running: <br>" + name; running.innerHTML = "Running: <br />" + getNameHtml( details.name, details.module );
} }
}); });
...@@ -2379,7 +2754,7 @@ QUnit.testStart(function( details ) { ...@@ -2379,7 +2754,7 @@ QUnit.testStart(function( details ) {
QUnit.log(function( details ) { QUnit.log(function( details ) {
var assertList, assertLi, var assertList, assertLi,
message, expected, actual, message, expected, actual,
testItem = id( "qunit-test-output" + details.testNumber ); testItem = id( "qunit-test-output-" + details.testId );
if ( !testItem ) { if ( !testItem ) {
return; return;
...@@ -2387,6 +2762,7 @@ QUnit.log(function( details ) { ...@@ -2387,6 +2762,7 @@ QUnit.log(function( details ) {
message = escapeText( details.message ) || ( details.result ? "okay" : "failed" ); message = escapeText( details.message ) || ( details.result ? "okay" : "failed" );
message = "<span class='test-message'>" + message + "</span>"; message = "<span class='test-message'>" + message + "</span>";
message += "<span class='runtime'>@ " + details.runtime + " ms</span>";
// pushFailure doesn't provide details.expected // pushFailure doesn't provide details.expected
// when it calls, it's implicit to also not show expected and diff stuff // when it calls, it's implicit to also not show expected and diff stuff
...@@ -2430,19 +2806,15 @@ QUnit.log(function( details ) { ...@@ -2430,19 +2806,15 @@ QUnit.log(function( details ) {
QUnit.testDone(function( details ) { QUnit.testDone(function( details ) {
var testTitle, time, testItem, assertList, var testTitle, time, testItem, assertList,
good, bad, testCounts, good, bad, testCounts, skipped,
tests = id( "qunit-tests" ); 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 ) { if ( !tests ) {
return; return;
} }
testItem = id( "qunit-test-output" + details.testNumber ); testItem = id( "qunit-test-output-" + details.testId );
assertList = testItem.getElementsByTagName( "ol" )[ 0 ]; assertList = testItem.getElementsByTagName( "ol" )[ 0 ];
good = details.passed; good = details.passed;
...@@ -2471,20 +2843,28 @@ QUnit.testDone(function( details ) { ...@@ -2471,20 +2843,28 @@ QUnit.testDone(function( details ) {
testTitle.innerHTML += " <b class='counts'>(" + testCounts + testTitle.innerHTML += " <b class='counts'>(" + testCounts +
details.assertions.length + ")</b>"; details.assertions.length + ")</b>";
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() { addEvent( testTitle, "click", function() {
toggleClass( assertList, "qunit-collapsed" ); toggleClass( assertList, "qunit-collapsed" );
}); });
testItem.className = bad ? "fail" : "pass";
time = document.createElement( "span" ); time = document.createElement( "span" );
time.className = "runtime"; time.className = "runtime";
time.innerHTML = details.runtime + " ms"; time.innerHTML = details.runtime + " ms";
testItem.className = bad ? "fail" : "pass";
testItem.insertBefore( time, assertList ); testItem.insertBefore( time, assertList );
}
}); });
if ( !defined.document || document.readyState === "complete" ) { if ( !defined.document || document.readyState === "complete" ) {
config.pageLoaded = true;
config.autorun = true; config.autorun = true;
} }
......
...@@ -256,12 +256,12 @@ ...@@ -256,12 +256,12 @@
"resolved": "https://registry.npmjs.org/autoprefixer-core/-/autoprefixer-core-5.0.0.tgz", "resolved": "https://registry.npmjs.org/autoprefixer-core/-/autoprefixer-core-5.0.0.tgz",
"dependencies": { "dependencies": {
"browserslist": { "browserslist": {
"version": "0.1.1", "version": "0.1.2",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-0.1.1.tgz" "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-0.1.2.tgz"
}, },
"caniuse-db": { "caniuse-db": {
"version": "1.0.30000048", "version": "1.0.30000049",
"resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000048.tgz" "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000049.tgz"
}, },
"postcss": { "postcss": {
"version": "4.0.1", "version": "4.0.1",
...@@ -394,32 +394,8 @@ ...@@ -394,32 +394,8 @@
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.1.2.tgz", "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.1.2.tgz",
"dependencies": { "dependencies": {
"bl": { "bl": {
"version": "0.9.3", "version": "0.9.4",
"resolved": "https://registry.npmjs.org/bl/-/bl-0.9.3.tgz", "resolved": "https://registry.npmjs.org/bl/-/bl-0.9.4.tgz"
"dependencies": {
"readable-stream": {
"version": "1.0.33",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.33.tgz",
"dependencies": {
"core-util-is": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz"
},
"inherits": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
},
"isarray": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz"
},
"string_decoder": {
"version": "0.10.31",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz"
}
}
}
}
}, },
"end-of-stream": { "end-of-stream": {
"version": "1.1.0", "version": "1.1.0",
...@@ -1692,8 +1668,8 @@ ...@@ -1692,8 +1668,8 @@
"resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.5.0.tgz" "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.5.0.tgz"
}, },
"bl": { "bl": {
"version": "0.9.3", "version": "0.9.4",
"resolved": "https://registry.npmjs.org/bl/-/bl-0.9.3.tgz", "resolved": "https://registry.npmjs.org/bl/-/bl-0.9.4.tgz",
"dependencies": { "dependencies": {
"readable-stream": { "readable-stream": {
"version": "1.0.33", "version": "1.0.33",
...@@ -1958,8 +1934,8 @@ ...@@ -1958,8 +1934,8 @@
"resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.5.0.tgz" "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.5.0.tgz"
}, },
"bl": { "bl": {
"version": "0.9.3", "version": "0.9.4",
"resolved": "https://registry.npmjs.org/bl/-/bl-0.9.3.tgz", "resolved": "https://registry.npmjs.org/bl/-/bl-0.9.4.tgz",
"dependencies": { "dependencies": {
"readable-stream": { "readable-stream": {
"version": "1.0.33", "version": "1.0.33",
...@@ -2894,18 +2870,18 @@ ...@@ -2894,18 +2870,18 @@
"resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.1.tgz" "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.1.tgz"
}, },
"vow": { "vow": {
"version": "0.4.7", "version": "0.4.8",
"resolved": "https://registry.npmjs.org/vow/-/vow-0.4.7.tgz" "resolved": "https://registry.npmjs.org/vow/-/vow-0.4.8.tgz"
} }
} }
}, },
"grunt-saucelabs": { "grunt-saucelabs": {
"version": "8.4.1", "version": "8.5.0",
"resolved": "https://registry.npmjs.org/grunt-saucelabs/-/grunt-saucelabs-8.4.1.tgz", "resolved": "https://registry.npmjs.org/grunt-saucelabs/-/grunt-saucelabs-8.5.0.tgz",
"dependencies": { "dependencies": {
"colors": { "colors": {
"version": "0.6.2", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/colors/-/colors-0.6.2.tgz" "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz"
}, },
"lodash": { "lodash": {
"version": "2.4.1", "version": "2.4.1",
...@@ -2915,41 +2891,97 @@ ...@@ -2915,41 +2891,97 @@
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/q/-/q-1.0.1.tgz" "resolved": "https://registry.npmjs.org/q/-/q-1.0.1.tgz"
}, },
"requestretry": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/requestretry/-/requestretry-1.2.2.tgz",
"dependencies": {
"fg-lodash": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/fg-lodash/-/fg-lodash-0.0.2.tgz",
"dependencies": {
"underscore.string": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.3.3.tgz"
}
}
},
"request": { "request": {
"version": "2.35.0", "version": "2.51.0",
"resolved": "https://registry.npmjs.org/request/-/request-2.35.0.tgz", "resolved": "https://registry.npmjs.org/request/-/request-2.51.0.tgz",
"dependencies": { "dependencies": {
"aws-sign2": { "aws-sign2": {
"version": "0.5.0", "version": "0.5.0",
"resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.5.0.tgz" "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.5.0.tgz"
}, },
"bl": {
"version": "0.9.4",
"resolved": "https://registry.npmjs.org/bl/-/bl-0.9.4.tgz",
"dependencies": {
"readable-stream": {
"version": "1.0.33",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.33.tgz",
"dependencies": {
"core-util-is": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz"
},
"inherits": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
},
"isarray": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz"
},
"string_decoder": {
"version": "0.10.31",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz"
}
}
}
}
},
"caseless": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/caseless/-/caseless-0.8.0.tgz"
},
"combined-stream": {
"version": "0.0.7",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-0.0.7.tgz",
"dependencies": {
"delayed-stream": {
"version": "0.0.5",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-0.0.5.tgz"
}
}
},
"forever-agent": { "forever-agent": {
"version": "0.5.2", "version": "0.5.2",
"resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.5.2.tgz" "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.5.2.tgz"
}, },
"form-data": { "form-data": {
"version": "0.1.4", "version": "0.2.0",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-0.1.4.tgz", "resolved": "https://registry.npmjs.org/form-data/-/form-data-0.2.0.tgz",
"dependencies": { "dependencies": {
"async": { "async": {
"version": "0.9.0", "version": "0.9.0",
"resolved": "https://registry.npmjs.org/async/-/async-0.9.0.tgz" "resolved": "https://registry.npmjs.org/async/-/async-0.9.0.tgz"
}, },
"combined-stream": { "mime-types": {
"version": "0.0.7", "version": "2.0.7",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-0.0.7.tgz", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.0.7.tgz",
"dependencies": { "dependencies": {
"delayed-stream": { "mime-db": {
"version": "0.0.5", "version": "1.5.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-0.0.5.tgz" "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.5.0.tgz"
} }
} }
} }
} }
}, },
"hawk": { "hawk": {
"version": "1.0.0", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/hawk/-/hawk-1.0.0.tgz", "resolved": "https://registry.npmjs.org/hawk/-/hawk-1.1.1.tgz",
"dependencies": { "dependencies": {
"boom": { "boom": {
"version": "0.4.2", "version": "0.4.2",
...@@ -2991,229 +3023,25 @@ ...@@ -2991,229 +3023,25 @@
"version": "5.0.0", "version": "5.0.0",
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.0.tgz" "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.0.tgz"
}, },
"lodash.merge": { "mime-types": {
"version": "2.4.1", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-2.4.1.tgz", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-1.0.2.tgz"
"dependencies": {
"lodash._basecreatecallback": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/lodash._basecreatecallback/-/lodash._basecreatecallback-2.4.1.tgz",
"dependencies": {
"lodash._setbinddata": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/lodash._setbinddata/-/lodash._setbinddata-2.4.1.tgz",
"dependencies": {
"lodash._isnative": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/lodash._isnative/-/lodash._isnative-2.4.1.tgz"
},
"lodash.noop": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/lodash.noop/-/lodash.noop-2.4.1.tgz"
}
}
},
"lodash.bind": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/lodash.bind/-/lodash.bind-2.4.1.tgz",
"dependencies": {
"lodash._createwrapper": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/lodash._createwrapper/-/lodash._createwrapper-2.4.1.tgz",
"dependencies": {
"lodash._basebind": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/lodash._basebind/-/lodash._basebind-2.4.1.tgz",
"dependencies": {
"lodash._basecreate": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/lodash._basecreate/-/lodash._basecreate-2.4.1.tgz",
"dependencies": {
"lodash._isnative": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/lodash._isnative/-/lodash._isnative-2.4.1.tgz"
},
"lodash.noop": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/lodash.noop/-/lodash.noop-2.4.1.tgz"
}
}
}
}
},
"lodash._basecreatewrapper": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/lodash._basecreatewrapper/-/lodash._basecreatewrapper-2.4.1.tgz",
"dependencies": {
"lodash._basecreate": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/lodash._basecreate/-/lodash._basecreate-2.4.1.tgz",
"dependencies": {
"lodash._isnative": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/lodash._isnative/-/lodash._isnative-2.4.1.tgz"
},
"lodash.noop": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/lodash.noop/-/lodash.noop-2.4.1.tgz"
}
}
}
}
},
"lodash.isfunction": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/lodash.isfunction/-/lodash.isfunction-2.4.1.tgz"
}
}
}
}
},
"lodash.identity": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/lodash.identity/-/lodash.identity-2.4.1.tgz"
},
"lodash.support": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/lodash.support/-/lodash.support-2.4.1.tgz",
"dependencies": {
"lodash._isnative": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/lodash._isnative/-/lodash._isnative-2.4.1.tgz"
}
}
}
}
},
"lodash._basemerge": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/lodash._basemerge/-/lodash._basemerge-2.4.1.tgz",
"dependencies": {
"lodash.foreach": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/lodash.foreach/-/lodash.foreach-2.4.1.tgz"
},
"lodash.forown": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/lodash.forown/-/lodash.forown-2.4.1.tgz",
"dependencies": {
"lodash._objecttypes": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/lodash._objecttypes/-/lodash._objecttypes-2.4.1.tgz"
},
"lodash.keys": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-2.4.1.tgz",
"dependencies": {
"lodash._isnative": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/lodash._isnative/-/lodash._isnative-2.4.1.tgz"
},
"lodash._shimkeys": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/lodash._shimkeys/-/lodash._shimkeys-2.4.1.tgz"
}
}
}
}
},
"lodash.isarray": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-2.4.1.tgz",
"dependencies": {
"lodash._isnative": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/lodash._isnative/-/lodash._isnative-2.4.1.tgz"
}
}
},
"lodash.isplainobject": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-2.4.1.tgz",
"dependencies": {
"lodash._isnative": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/lodash._isnative/-/lodash._isnative-2.4.1.tgz"
},
"lodash._shimisplainobject": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/lodash._shimisplainobject/-/lodash._shimisplainobject-2.4.1.tgz",
"dependencies": {
"lodash.forin": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/lodash.forin/-/lodash.forin-2.4.1.tgz",
"dependencies": {
"lodash._objecttypes": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/lodash._objecttypes/-/lodash._objecttypes-2.4.1.tgz"
}
}
},
"lodash.isfunction": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/lodash.isfunction/-/lodash.isfunction-2.4.1.tgz"
}
}
}
}
}
}
},
"lodash._getarray": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/lodash._getarray/-/lodash._getarray-2.4.1.tgz",
"dependencies": {
"lodash._arraypool": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/lodash._arraypool/-/lodash._arraypool-2.4.1.tgz"
}
}
},
"lodash._releasearray": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/lodash._releasearray/-/lodash._releasearray-2.4.1.tgz",
"dependencies": {
"lodash._arraypool": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/lodash._arraypool/-/lodash._arraypool-2.4.1.tgz"
},
"lodash._maxpoolsize": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/lodash._maxpoolsize/-/lodash._maxpoolsize-2.4.1.tgz"
}
}
},
"lodash._slice": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/lodash._slice/-/lodash._slice-2.4.1.tgz"
},
"lodash.isobject": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-2.4.1.tgz",
"dependencies": {
"lodash._objecttypes": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/lodash._objecttypes/-/lodash._objecttypes-2.4.1.tgz"
}
}
}
}
},
"mime": {
"version": "1.2.11",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz"
}, },
"node-uuid": { "node-uuid": {
"version": "1.4.2", "version": "1.4.2",
"resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.2.tgz" "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.2.tgz"
}, },
"oauth-sign": { "oauth-sign": {
"version": "0.3.0", "version": "0.5.0",
"resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.3.0.tgz" "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.5.0.tgz"
}, },
"qs": { "qs": {
"version": "0.6.6", "version": "2.3.3",
"resolved": "https://registry.npmjs.org/qs/-/qs-0.6.6.tgz" "resolved": "https://registry.npmjs.org/qs/-/qs-2.3.3.tgz"
},
"stringstream": {
"version": "0.0.4",
"resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.4.tgz"
}, },
"tough-cookie": { "tough-cookie": {
"version": "0.12.1", "version": "0.12.1",
...@@ -3230,6 +3058,8 @@ ...@@ -3230,6 +3058,8 @@
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.0.tgz" "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.0.tgz"
} }
} }
}
}
}, },
"sauce-tunnel": { "sauce-tunnel": {
"version": "2.1.1", "version": "2.1.1",
...@@ -3465,6 +3295,30 @@ ...@@ -3465,6 +3295,30 @@
} }
} }
}, },
"markdown-it": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-3.0.4.tgz",
"dependencies": {
"argparse": {
"version": "0.1.16",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-0.1.16.tgz",
"dependencies": {
"underscore": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz"
},
"underscore.string": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.4.0.tgz"
}
}
},
"autolinker": {
"version": "0.15.2",
"resolved": "https://registry.npmjs.org/autolinker/-/autolinker-0.15.2.tgz"
}
}
},
"npm-shrinkwrap": { "npm-shrinkwrap": {
"version": "200.0.0", "version": "200.0.0",
"resolved": "https://registry.npmjs.org/npm-shrinkwrap/-/npm-shrinkwrap-200.0.0.tgz", "resolved": "https://registry.npmjs.org/npm-shrinkwrap/-/npm-shrinkwrap-200.0.0.tgz",
...@@ -4193,30 +4047,6 @@ ...@@ -4193,30 +4047,6 @@
} }
} }
}, },
"remarkable": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/remarkable/-/remarkable-1.6.0.tgz",
"dependencies": {
"argparse": {
"version": "0.1.16",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-0.1.16.tgz",
"dependencies": {
"underscore": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz"
},
"underscore.string": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.4.0.tgz"
}
}
},
"autolinker": {
"version": "0.15.2",
"resolved": "https://registry.npmjs.org/autolinker/-/autolinker-0.15.2.tgz"
}
}
},
"time-grunt": { "time-grunt": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/time-grunt/-/time-grunt-1.0.0.tgz", "resolved": "https://registry.npmjs.org/time-grunt/-/time-grunt-1.0.0.tgz",
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment