Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -511,7 +511,7 @@ if (typeof jQuery === 'undefined') {
var $this = $(this)
var href = $this.attr('href')
if (href) {
href = href.replace(/.*(?=#[^\s]+$)/, '') // strip for ie7
href = href && href.indexOf('#') !== -1 ? href.slice(href.lastIndexOf('#')) : href
}

var target = $this.attr('data-target') || href
Expand Down Expand Up @@ -705,7 +705,7 @@ if (typeof jQuery === 'undefined') {
function getTargetFromTrigger($trigger) {
var href
var target = $trigger.attr('data-target')
|| (href = $trigger.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') // strip for ie7
|| ((href = $trigger.attr('href')) && (href.indexOf('#') !== -1 ? href.slice(href.lastIndexOf('#')) : href)) // strip for ie7 (safe)

return $(document).find(target)
}
Expand Down Expand Up @@ -1265,7 +1265,8 @@ if (typeof jQuery === 'undefined') {
var $this = $(this)
var href = $this.attr('href')
var target = $this.attr('data-target') ||
(href && href.replace(/.*(?=#[^\s]+$)/, '')) // strip for ie7
//(href && href.replace(/.*(?=#[^\s]+$)/, '')) // strip for ie7
(href && href.replace(/^[^#]*(?=#\S+$)/, '')) // strip for ie7 - safe

var $target = $(document).find(target)
var option = $target.data('bs.modal') ? 'toggle' : $.extend({ remote: !/#/.test(href) && href }, $target.data(), $this.data())
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"name": "bootstrap-sample-tests",
"private": true,
"type": "commonjs",
"scripts": {
"test": "node --test ./test/carousel-href.spec.js ./test/collapse-href.spec.js"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// Node built-in test – Carousel data-api href sanitize timing (fail if > LIMIT)
const test = require('node:test');
const assert = require('node:assert/strict');
const path = require('node:path');

const BOOTSTRAP_PATH =
process.env.BOOTSTRAP_PATH ||
path.resolve(__dirname, '../dist/js/bootstrap.js');

const N = parseInt(process.env.LENGTH || '100000', 10);
const LIMIT = parseInt(process.env.LIMIT || '2000', 10); // 超过即失败

test('Carousel data-api href sanitize timing (fail if > LIMIT)', () => {
// 极简 jQuery stub:按事件名保存 handler
const handlers = Object.create(null);

function wrap(raw) {
return {
on(event, selectorOrHandler, maybeHandler) {
const h = typeof maybeHandler === 'function'
? maybeHandler
: (typeof selectorOrHandler === 'function' ? selectorOrHandler : null);
if (event && h) handlers[event] = h;
return this; // 链式 .on().on()
},
find() { return { hasClass: () => false, data() { return {}; } }; },
data() { return {}; },
attr(name) { return raw && name === 'href' ? raw._href : null; },
};
}
function $(x) { return wrap(x); }
$.fn = { jquery: '3.4.1' };
$.extend = Object.assign;

// 满足 bootstrap 自检
global.window = global;
global.document = {};
global.jQuery = global.$ = $;

// 载入本地 bootstrap(未修复/修复后的都可以)
require(BOOTSTRAP_PATH);

// 精确拿到 Carousel 的 data-api 处理器
const click =
handlers['click.bs.carousel.data-api'] ||
handlers['click.bs.carousel'];
assert.equal(typeof click, 'function', 'failed to capture carousel handler');

const cases = [
{ name: 'nul', s: '\u0000'.repeat(N) + '\u0000' },
{ name: 'digits\\n@', s: '1'.repeat(N) + '\n@' },
];

let worst = 0;
for (const { name, s } of cases) {
const t0 = Date.now();
try { click.call({ _href: s }, { preventDefault(){} }); } catch {}
const ms = Date.now() - t0;
worst = Math.max(worst, ms);
console.log(`[carousel] ${name.padEnd(10)} len=${s.length} -> ${ms} ms`);
}
console.log(`[carousel] worst = ${worst} ms (limit=${LIMIT})`);

// 关键:超过 LIMIT 就判失败(红色 ✗)
if (worst > LIMIT) {
assert.fail(`too slow: ${worst}ms (> ${LIMIT}ms)`);
}
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// Node built-in test – Carousel data-api href sanitize timing (pass if < LIMIT)
const test = require('node:test');
const assert = require('node:assert/strict');
const path = require('node:path');

const BOOTSTRAP_PATH =
process.env.BOOTSTRAP_PATH ||
path.resolve(__dirname, '../dist/js/bootstrap.js');

const N = parseInt(process.env.LENGTH || '100000', 10);
const LIMIT = parseInt(process.env.LIMIT || '2000', 10);

test('Carousel data-api href sanitize timing (pass if < LIMIT)', () => {
const handlers = Object.create(null);

function wrap(raw) {
return {
on(event, selectorOrHandler, maybeHandler) {
const h = typeof maybeHandler === 'function'
? maybeHandler
: (typeof selectorOrHandler === 'function' ? selectorOrHandler : null);
if (event && h) handlers[event] = h;
return this;
},
find() { return { hasClass: () => false, data() { return {}; } }; },
data() { return {}; },
attr(name) { return raw && name === 'href' ? raw._href : null; },
};
}
function $(x) { return wrap(x); }
$.fn = { jquery: '3.4.1' };
$.extend = Object.assign;

global.window = global;
global.document = {};
global.jQuery = global.$ = $;

require(BOOTSTRAP_PATH);

const click =
handlers['click.bs.carousel.data-api'] ||
handlers['click.bs.carousel'];
assert.equal(typeof click, 'function', 'failed to capture carousel handler');

const cases = [
{ name: 'nul', s: '\u0000'.repeat(N) + '\u0000' },
{ name: 'digits\\n@', s: '1'.repeat(N) + '\n@' },
];

let worst = 0;
for (const { name, s } of cases) {
const t0 = Date.now();
try { click.call({ _href: s }, { preventDefault(){} }); } catch {}
const ms = Date.now() - t0;
worst = Math.max(worst, ms);
console.log(`[carousel] ${name.padEnd(10)} len=${s.length} -> ${ms} ms`);
}
console.log(`[carousel] worst = ${worst} ms (limit=${LIMIT})`);

// 修复后应当 < LIMIT;否则失败
assert.ok(worst < LIMIT, `too slow: ${worst}ms (>= ${LIMIT}ms)`);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Node built-in test – Collapse data-api href sanitize timing (fail if > LIMIT ms)
const test = require('node:test');
const assert = require('node:assert/strict');
const path = require('node:path');

const BOOTSTRAP_PATH =
process.env.BOOTSTRAP_PATH ||
path.resolve(__dirname, '../dist/js/bootstrap.js');

const N = parseInt(process.env.LENGTH || '100000', 10);
const LIMIT = parseInt(process.env.LIMIT || '2000', 10); // 超过就失败

test('Collapse data-api href sanitize timing', () => {
// 极简 jQuery stub:按事件名存 handler
const handlers = Object.create(null);

function wrap(raw) {
return {
on(event, selectorOrHandler, maybeHandler) {
const h = typeof maybeHandler === 'function'
? maybeHandler
: (typeof selectorOrHandler === 'function' ? selectorOrHandler : null);
if (event && h) handlers[event] = h;
return this;
},
find() { return { hasClass: () => false, data() { return {}; } }; },
data() { return {}; },
attr(name) { return raw && name === 'href' ? raw._href : null; },
};
}
function $(x) { return wrap(x); }
$.fn = { jquery: '3.4.1' };
$.extend = Object.assign;

global.window = global;
global.document = {};
global.jQuery = global.$ = $;

require(BOOTSTRAP_PATH);

const click =
handlers['click.bs.collapse.data-api'] ||
handlers['click.bs.collapse'];
assert.equal(typeof click, 'function', 'failed to capture collapse handler');

const cases = [
{ name: 'nul', s: '\u0000'.repeat(N) + '\u0000' },
{ name: 'digits\\n@', s: '1'.repeat(N) + '\n@' },
];

let worst = 0;
for (const { name, s } of cases) {
const t0 = Date.now();
try { click.call({ _href: s }, { preventDefault(){} }); } catch {}
const ms = Date.now() - t0;
worst = Math.max(worst, ms);
console.log(`[collapse] ${name.padEnd(10)} len=${s.length} -> ${ms} ms`);
}
console.log(`[collapse] worst = ${worst} ms (limit=${LIMIT})`);

// 关键:超过 LIMIT 就判失败(红色 ✗)
if (worst > LIMIT) {
assert.fail(`too slow: ${worst}ms (> ${LIMIT}ms)`);
}
});
Loading