Commit 8a37045b authored by Johann-S's avatar Johann-S Committed by XhmikosR
Browse files

move util in a util folder with the sanitizer

parent 8affe84c
Showing with 98 additions and 206 deletions
+98 -206
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Bootstrap Plugin Test Suite</title>
<!-- jQuery -->
<script src="../../node_modules/jquery/dist/jquery.slim.min.js"></script>
<script src="../../node_modules/popper.js/dist/umd/popper.min.js"></script>
<!-- QUnit -->
<link rel="stylesheet" href="../../node_modules/qunit/qunit/qunit.css" media="screen">
<script src="../../node_modules/qunit/qunit/qunit.js"></script>
<!-- Sinon -->
<script src="../../node_modules/sinon/pkg/sinon-no-sourcemaps.js"></script>
<!-- Hammer simulator -->
<script src="../../node_modules/hammer-simulator/index.js"></script>
<script>
// Disable jQuery event aliases to ensure we don't accidentally use any of them
[
'blur',
'focus',
'focusin',
'focusout',
'resize',
'scroll',
'click',
'dblclick',
'mousedown',
'mouseup',
'mousemove',
'mouseover',
'mouseout',
'mouseenter',
'mouseleave',
'change',
'select',
'submit',
'keydown',
'keypress',
'keyup',
'contextmenu'
].forEach(function(eventAlias) {
$.fn[eventAlias] = function() {
throw new Error('Using the ".' + eventAlias + '()" method is not allowed, so that Bootstrap can be compatible with custom jQuery builds which exclude the "event aliases" module that defines said method. See https://github.com/twbs/bootstrap/blob/master/CONTRIBUTING.md#js')
}
})
// Require assert.expect in each test
QUnit.config.requireExpects = true
// See https://github.com/axemclion/grunt-saucelabs#test-result-details-with-qunit
var log = []
var testName
QUnit.done(function(testResults) {
var tests = []
for (var i = 0; i < log.length; i++) {
var details = log[i]
tests.push({
name: details.name,
result: details.result,
expected: details.expected,
actual: details.actual,
source: details.source
})
}
testResults.tests = tests
window.global_test_results = testResults
})
QUnit.testStart(function(testDetails) {
QUnit.log(function(details) {
if (!details.result) {
details.name = testDetails.name
log.push(details)
}
})
})
// Display fixture on-screen on iOS to avoid false positives
// See https://github.com/twbs/bootstrap/pull/15955
if (/iPhone|iPad|iPod/.test(navigator.userAgent)) {
QUnit.begin(function() {
$('#qunit-fixture').css({ top: 0, left: 0 })
})
QUnit.done(function() {
$('#qunit-fixture').css({ top: '', left: '' })
})
}
</script>
<!-- Transpiled Plugins -->
<script src="../dist/util.js"></script>
<script src="../dist/dom/polyfill.js"></script>
<script src="../dist/dom/manipulator.js"></script>
<script src="../dist/dom/eventHandler.js"></script>
<script src="../dist/dom/selectorEngine.js"></script>
<script src="../dist/dom/data.js"></script>
<script src="../dist/alert.js"></script>
<script src="../dist/button.js"></script>
<script src="../dist/carousel.js"></script>
<script src="../dist/collapse.js"></script>
<script src="../dist/dropdown.js"></script>
<script src="../dist/modal.js"></script>
<script src="../dist/scrollspy.js"></script>
<script src="../dist/tab.js"></script>
<script src="../dist/tooltip.js"></script>
<script src="../dist/popover.js"></script>
<script src="../dist/toast.js"></script>
<!-- Unit Tests -->
<script src="unit/dom/eventHandler.js"></script>
<script src="unit/dom/manipulator.js"></script>
<script src="unit/dom/data.js"></script>
<script src="unit/dom/selectorEngine.js"></script>
<script src="unit/alert.js"></script>
<script src="unit/button.js"></script>
<script src="unit/carousel.js"></script>
<script src="unit/collapse.js"></script>
<script src="unit/dropdown.js"></script>
<script src="unit/modal.js"></script>
<script src="unit/scrollspy.js"></script>
<script src="unit/tab.js"></script>
<script src="unit/tooltip.js"></script>
<script src="unit/popover.js"></script>
<script src="unit/util.js"></script>
<script src="unit/toast.js"></script>
</head>
<body>
<div id="qunit-container">
<div id="qunit"></div>
<div id="qunit-fixture"></div>
</div>
</body>
</html>
......@@ -2,8 +2,6 @@ import 'popper.js'
import bootstrap from '../../../dist/js/bootstrap'
window.addEventListener('load', () => {
document.getElementById('resultUID').innerHTML = bootstrap.Util.getUID('bs')
bootstrap.Util.makeArray(document.querySelectorAll('[data-toggle="tooltip"]'))
Array.from(document.querySelectorAll('[data-toggle="tooltip"]'))
.map((tooltipNode) => new bootstrap.Tooltip(tooltipNode))
})
......@@ -13,11 +13,7 @@
<body>
<div class="container">
<h1>Hello, world!</h1>
<div class="col-12">
<div class="mt-5 mb-3">
<span>Util.getUID: </span>
<span id="resultUID"></span>
</div>
<div class="col-12 mt-5">
<button type="button" class="btn btn-secondary" data-toggle="tooltip" data-placement="top" title="Tooltip on top">
Tooltip on top
</button>
......
......@@ -11,6 +11,7 @@ const {
const jqueryFile = process.env.USE_OLD_JQUERY ? 'https://code.jquery.com/jquery-1.9.1.min.js' : 'node_modules/jquery/dist/jquery.slim.min.js'
const bundle = process.env.BUNDLE === 'true'
const browserStack = process.env.BROWSER === 'true'
const debug = process.env.DEBUG === 'true'
const frameworks = [
'qunit',
......@@ -28,11 +29,11 @@ const detectBrowsers = {
usePhantomJS: false,
postDetection(availableBrowser) {
if (typeof process.env.TRAVIS_JOB_ID !== 'undefined' || availableBrowser.includes('Chrome')) {
return ['ChromeHeadless']
return debug ? ['Chrome'] : ['ChromeHeadless']
}
if (availableBrowser.includes('Firefox')) {
return ['FirefoxHeadless']
return debug ? ['Firefox'] : ['FirefoxHeadless']
}
throw new Error('Please install Firefox or Chrome')
......@@ -76,7 +77,8 @@ if (bundle) {
conf.detectBrowsers = detectBrowsers
files = files.concat([
jqueryFile,
'dist/js/bootstrap.js'
'dist/js/bootstrap.js',
'js/tests/unit/*.js'
])
} else if (browserStack) {
conf.hostname = ip.address()
......@@ -93,7 +95,8 @@ if (bundle) {
reporters.push('BrowserStack')
files = files.concat([
'node_modules/jquery/dist/jquery.slim.min.js',
'js/coverage/dist/util.js',
'js/coverage/dist/util/util.js',
'js/coverage/dist/util/sanitizer.js',
'js/coverage/dist/dom/polyfill.js',
'js/coverage/dist/dom/eventHandler.js',
'js/coverage/dist/dom/selectorEngine.js',
......@@ -103,7 +106,8 @@ if (bundle) {
'js/coverage/dist/tooltip.js',
'js/coverage/dist/!(util|index|tooltip).js', // include all of our js/dist files except util.js, index.js and tooltip.js
'js/tests/unit/*.js',
'js/tests/unit/dom/*.js'
'js/tests/unit/dom/*.js',
'js/tests/unit/util/*.js'
])
} else {
frameworks.push('detectBrowsers')
......@@ -115,7 +119,8 @@ if (bundle) {
)
files = files.concat([
jqueryFile,
'js/coverage/dist/util.js',
'js/coverage/dist/util/util.js',
'js/coverage/dist/util/sanitizer.js',
'js/coverage/dist/dom/polyfill.js',
'js/coverage/dist/dom/eventHandler.js',
'js/coverage/dist/dom/selectorEngine.js',
......@@ -125,7 +130,8 @@ if (bundle) {
'js/coverage/dist/tooltip.js',
'js/coverage/dist/!(util|index|tooltip).js', // include all of our js/dist files except util.js, index.js and tooltip.js
'js/tests/unit/*.js',
'js/tests/unit/dom/*.js'
'js/tests/unit/dom/*.js',
'js/tests/unit/util/*.js'
])
reporters.push('coverage-istanbul')
conf.customLaunchers = customLaunchers
......@@ -153,9 +159,12 @@ if (bundle) {
}
}
}
}
files.push('js/tests/unit/*.js')
if (debug) {
conf.singleRun = false
conf.autoWatch = true
}
}
conf.frameworks = frameworks
conf.plugins = plugins
......
......@@ -8,6 +8,7 @@
"bootstrap": false,
"sinon": false,
"Util": false,
"Sanitizer": false,
"Data": false,
"Alert": false,
"Button": false,
......
......@@ -695,13 +695,10 @@ $(function () {
].join('')
var $modal = $(modalHTML).appendTo('#qunit-fixture')
var expectedTransitionDuration = 300
var spy = sinon.spy(Util, 'getTransitionDurationFromElement')
$modal.on('shown.bs.modal', function () {
assert.ok(spy.returned(expectedTransitionDuration))
$style.remove()
spy.restore()
assert.ok(true)
done()
})
.bootstrapModal('show')
......
......@@ -722,8 +722,10 @@ $(function () {
QUnit.test('should not reload the tooltip on subsequent mouseenter events', function (assert) {
assert.expect(1)
var fakeId = 1
var titleHtml = function () {
var uid = Util.getUID('tooltip')
var uid = fakeId
fakeId++
return '<p id="tt-content">' + uid + '</p><p>' + uid + '</p><p>' + uid + '</p>'
}
......@@ -753,8 +755,10 @@ $(function () {
QUnit.test('should not reload the tooltip if the mouse leaves and re-enters before hiding', function (assert) {
assert.expect(4)
var fakeId = 1
var titleHtml = function () {
var uid = Util.getUID('tooltip')
var uid = 'tooltip' + fakeId
fakeId++
return '<p id="tt-content">' + uid + '</p><p>' + uid + '</p><p>' + uid + '</p>'
}
......@@ -1152,24 +1156,6 @@ $(function () {
assert.strictEqual(tooltip.config.template.indexOf('onError'), -1)
})
QUnit.test('should sanitize template by removing tags with XSS', function (assert) {
assert.expect(1)
var $trigger = $('<a href="#" rel="tooltip" data-trigger="click" title="Another tooltip"/>')
.appendTo('#qunit-fixture')
.bootstrapTooltip({
template: [
'<div>',
' <a href="javascript:alert(7)">Click me</a>',
' <span>Some content</span>',
'</div>'
].join('')
})
var tooltip = Tooltip._getInstance($trigger[0])
assert.strictEqual(tooltip.config.template.indexOf('script'), -1)
})
QUnit.test('should allow custom sanitization rules', function (assert) {
assert.expect(2)
......
$(function () {
'use strict'
window.Util = typeof bootstrap !== 'undefined' ? bootstrap.Util : Util
QUnit.module('util', {
afterEach: function () {
$('#qunit-fixture').html('')
......
$(function () {
'use strict'
QUnit.module('sanitizer', {
afterEach: function () {
$('#qunit-fixture').html('')
}
})
QUnit.test('should export a default white list', function (assert) {
assert.expect(1)
assert.ok(Sanitizer.DefaultWhitelist)
})
QUnit.test('should sanitize template by removing tags with XSS', function (assert) {
assert.expect(1)
var template = [
'<div>',
' <a href="javascript:alert(7)">Click me</a>',
' <span>Some content</span>',
'</div>'
].join('')
var result = Sanitizer.sanitizeHtml(template, Sanitizer.DefaultWhitelist, null)
assert.strictEqual(result.indexOf('script'), -1)
})
QUnit.test('should not use native api to sanitize if a custom function passed', function (assert) {
assert.expect(2)
var template = [
'<div>',
' <span>Some content</span>',
'</div>'
].join('')
function mySanitize(htmlUnsafe) {
return htmlUnsafe
}
var spy = sinon.spy(DOMParser.prototype, 'parseFromString')
var result = Sanitizer.sanitizeHtml(template, Sanitizer.DefaultWhitelist, mySanitize)
assert.strictEqual(result, template)
assert.strictEqual(spy.called, false)
spy.restore()
})
})
......@@ -60,13 +60,13 @@
<script src="../../dist/toast.js"></script>
<script>
window.addEventListener('load', function () {
Util.makeArray(document.querySelectorAll('.toast'))
Array.from(document.querySelectorAll('.toast'))
.forEach(function (toastNode) {
new Toast(toastNode)
})
document.getElementById('btnShowToast').addEventListener('click', function () {
Util.makeArray(document.querySelectorAll('.toast'))
Array.from(document.querySelectorAll('.toast'))
.forEach(function (toastNode) {
var toast = Toast._getInstance(toastNode)
toast.show()
......@@ -74,7 +74,7 @@
})
document.getElementById('btnHideToast').addEventListener('click', function () {
Util.makeArray(document.querySelectorAll('.toast'))
Array.from(document.querySelectorAll('.toast'))
.forEach(function (toastNode) {
var toast = Toast._getInstance(toastNode)
toast.hide()
......
......@@ -56,6 +56,7 @@
"js-minify-bundle": "terser --compress --mangle --comments \"/^!/\" --source-map \"content=dist/js/bootstrap.bundle.js.map,includeSources,url=bootstrap.bundle.min.js.map\" --output dist/js/bootstrap.bundle.min.js dist/js/bootstrap.bundle.js",
"js-minify-docs": "cross-env-shell terser --mangle --comments \\\"/^!/\\\" --output site/docs/$npm_package_version_short/assets/js/docs.min.js site/docs/$npm_package_version_short/assets/js/vendor/anchor.min.js site/docs/$npm_package_version_short/assets/js/vendor/clipboard.min.js site/docs/$npm_package_version_short/assets/js/vendor/bs-custom-file-input.min.js \"site/docs/$npm_package_version_short/assets/js/src/*.js\"",
"js-test": "npm-run-all js-test-karma* js-test-integration",
"js-debug": "cross-env DEBUG=true karma start js/tests/karma.conf.js",
"js-test-karma": "karma start js/tests/karma.conf.js",
"js-test-karma-old": "cross-env USE_OLD_JQUERY=true npm run js-test-karma",
"js-test-karma-bundle": "cross-env BUNDLE=true npm run js-test-karma",
......
......@@ -15,20 +15,24 @@
(function () {
'use strict'
function makeArray(list) {
return [].slice.call(list)
}
// Tooltip and popover demos
bootstrap.Util.makeArray(document.querySelectorAll('.tooltip-demo'))
makeArray(document.querySelectorAll('.tooltip-demo'))
.forEach(function (tooltip) {
new bootstrap.Tooltip(tooltip, {
selector: '[data-toggle="tooltip"]'
})
})
bootstrap.Util.makeArray(document.querySelectorAll('[data-toggle="popover"]'))
makeArray(document.querySelectorAll('[data-toggle="popover"]'))
.forEach(function (popover) {
new bootstrap.Popover(popover)
})
bootstrap.Util.makeArray(document.querySelectorAll('.toast'))
makeArray(document.querySelectorAll('.toast'))
.forEach(function (toastNode) {
var toast = new bootstrap.Toast(toastNode, {
autohide: false
......@@ -38,24 +42,24 @@
})
// Demos within modals
bootstrap.Util.makeArray(document.querySelectorAll('.tooltip-test'))
makeArray(document.querySelectorAll('.tooltip-test'))
.forEach(function (tooltip) {
new bootstrap.Tooltip(tooltip)
})
bootstrap.Util.makeArray(document.querySelectorAll('.popover-test'))
makeArray(document.querySelectorAll('.popover-test'))
.forEach(function (popover) {
new bootstrap.Popover(popover)
})
// Indeterminate checkbox example
bootstrap.Util.makeArray(document.querySelectorAll('.bd-example-indeterminate [type="checkbox"]'))
makeArray(document.querySelectorAll('.bd-example-indeterminate [type="checkbox"]'))
.forEach(function (checkbox) {
checkbox.indeterminate = true
})
// Disable empty links in docs examples
bootstrap.Util.makeArray(document.querySelectorAll('.bd-content [href="#"]'))
makeArray(document.querySelectorAll('.bd-content [href="#"]'))
.forEach(function (link) {
link.addEventListener('click', function (e) {
e.preventDefault()
......@@ -79,7 +83,7 @@
}
// Activate animated progress bar
bootstrap.Util.makeArray(document.querySelectorAll('.bd-toggle-animated-progress > .progress-bar-striped'))
makeArray(document.querySelectorAll('.bd-toggle-animated-progress > .progress-bar-striped'))
.forEach(function (progressBar) {
progressBar.addEventListener('click', function () {
if (progressBar.classList.contains('progress-bar-animated')) {
......@@ -92,12 +96,12 @@
// Insert copy to clipboard button before .highlight
var btnHtml = '<div class="bd-clipboard"><button type="button" class="btn-clipboard" title="Copy to clipboard">Copy</button></div>'
bootstrap.Util.makeArray(document.querySelectorAll('figure.highlight, div.highlight'))
makeArray(document.querySelectorAll('figure.highlight, div.highlight'))
.forEach(function (element) {
element.insertAdjacentHTML('beforebegin', btnHtml)
})
bootstrap.Util.makeArray(document.querySelectorAll('.btn-clipboard'))
makeArray(document.querySelectorAll('.btn-clipboard'))
.forEach(function (btn) {
var tooltipBtn = new bootstrap.Tooltip(btn)
......@@ -146,7 +150,7 @@
anchors.add('.bd-content > h2, .bd-content > h3, .bd-content > h4, .bd-content > h5')
// Wrap inner
bootstrap.Util.makeArray(document.querySelectorAll('.bd-content > h2, .bd-content > h3, .bd-content > h4, .bd-content > h5'))
makeArray(document.querySelectorAll('.bd-content > h2, .bd-content > h3, .bd-content > h4, .bd-content > h5'))
.forEach(function (hEl) {
hEl.innerHTML = '<span class="bd-content-title">' + hEl.innerHTML + '</span>'
})
......
......@@ -134,12 +134,6 @@ Bootstrap's plugins don't fall back particularly gracefully when JavaScript is d
{% endcapture %}
{% include callout.html content=callout type="warning" %}
## Util
All Bootstrap's JavaScript files depend on `util.js` and it has to be included alongside the other JavaScript files. If you're using the compiled (or minified) `bootstrap.js`, there is no need to include this—it's already there.
`util.js` includes utility functions and a basic helper for `transitionEnd` events as well as a CSS transition emulator. It's used by the other plugins to check for CSS transition support and to catch hanging transitions.
## Sanitizer
Tooltips and Popovers use our built-in sanitizer to sanitize options which accept HTML.
......
......@@ -21,7 +21,6 @@ import 'bootstrap';
Alternatively, you may **import plugins individually** as needed:
{% highlight js %}
import 'bootstrap/js/dist/util';
import 'bootstrap/js/dist/alert';
...
{% endhighlight %}
......
Supports Markdown
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