mirror of
https://github.com/twbs/bootstrap.git
synced 2025-08-08 06:36:33 +02:00
Add a template factory helper to handle all template cases (#34519)
Co-authored-by: XhmikosR <xhmikosr@gmail.com>
This commit is contained in:
@@ -162,8 +162,8 @@ describe('Popover', () => {
|
||||
const popover = new Popover(popoverEl, {
|
||||
content: 'Popover content'
|
||||
})
|
||||
|
||||
const spy = spyOn(popover, 'setContent').and.callThrough()
|
||||
expect(popover._templateFactory).toBeNull()
|
||||
let spy = null
|
||||
let times = 1
|
||||
|
||||
popoverEl.addEventListener('hidden.bs.popover', () => {
|
||||
@@ -171,11 +171,12 @@ describe('Popover', () => {
|
||||
})
|
||||
|
||||
popoverEl.addEventListener('shown.bs.popover', () => {
|
||||
spy = spy || spyOn(popover._templateFactory, 'constructor').and.callThrough()
|
||||
const popoverDisplayed = document.querySelector('.popover')
|
||||
|
||||
expect(popoverDisplayed).not.toBeNull()
|
||||
expect(popoverDisplayed.querySelector('.popover-body').textContent).toEqual('Popover content')
|
||||
expect(spy).toHaveBeenCalledTimes(1)
|
||||
expect(spy).toHaveBeenCalledTimes(0)
|
||||
if (times > 1) {
|
||||
done()
|
||||
}
|
||||
|
@@ -1041,7 +1041,7 @@ describe('Tooltip', () => {
|
||||
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
|
||||
|
||||
const tooltipEl = fixtureEl.querySelector('a')
|
||||
const tooltip = new Tooltip(tooltipEl)
|
||||
const tooltip = new Tooltip(tooltipEl, { animation: false })
|
||||
|
||||
const tip = tooltip.getTipElement()
|
||||
|
||||
@@ -1051,6 +1051,35 @@ describe('Tooltip', () => {
|
||||
expect(tip.classList.contains('fade')).toEqual(false)
|
||||
expect(tip.querySelector('.tooltip-inner').textContent).toEqual('Another tooltip')
|
||||
})
|
||||
|
||||
it('should re-show tip if it was already shown', () => {
|
||||
fixtureEl.innerHTML = '<a href="#" rel="tooltip" data-bs-title="Another tooltip">'
|
||||
|
||||
const tooltipEl = fixtureEl.querySelector('a')
|
||||
const tooltip = new Tooltip(tooltipEl)
|
||||
tooltip.show()
|
||||
const tip = () => tooltip.getTipElement()
|
||||
|
||||
expect(tip().classList.contains('show')).toEqual(true)
|
||||
tooltip.setContent({ '.tooltip-inner': 'foo' })
|
||||
|
||||
expect(tip().classList.contains('show')).toEqual(true)
|
||||
expect(tip().querySelector('.tooltip-inner').textContent).toEqual('foo')
|
||||
})
|
||||
|
||||
it('should keep tip hidden, if it was already hidden before', () => {
|
||||
fixtureEl.innerHTML = '<a href="#" rel="tooltip" data-bs-title="Another tooltip">'
|
||||
|
||||
const tooltipEl = fixtureEl.querySelector('a')
|
||||
const tooltip = new Tooltip(tooltipEl)
|
||||
const tip = () => tooltip.getTipElement()
|
||||
|
||||
expect(tip().classList.contains('show')).toEqual(false)
|
||||
tooltip.setContent({ '.tooltip-inner': 'foo' })
|
||||
|
||||
expect(tip().classList.contains('show')).toEqual(false)
|
||||
expect(tip().querySelector('.tooltip-inner').textContent).toEqual('foo')
|
||||
})
|
||||
})
|
||||
|
||||
describe('updateAttachment', () => {
|
||||
@@ -1087,34 +1116,17 @@ describe('Tooltip', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('setElementContent', () => {
|
||||
describe('setContent', () => {
|
||||
it('should do nothing if the element is null', () => {
|
||||
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
|
||||
|
||||
const tooltipEl = fixtureEl.querySelector('a')
|
||||
const tooltip = new Tooltip(tooltipEl)
|
||||
|
||||
tooltip.setElementContent(null, null)
|
||||
tooltip.setContent({ '.tooltip': null })
|
||||
expect().nothing()
|
||||
})
|
||||
|
||||
it('should add the content as a child of the element', () => {
|
||||
fixtureEl.innerHTML = [
|
||||
'<a href="#" rel="tooltip" title="Another tooltip">',
|
||||
'<div id="childContent"></div>'
|
||||
].join('')
|
||||
|
||||
const tooltipEl = fixtureEl.querySelector('a')
|
||||
const childContent = fixtureEl.querySelector('div')
|
||||
const tooltip = new Tooltip(tooltipEl, {
|
||||
html: true
|
||||
})
|
||||
|
||||
tooltip.setElementContent(tooltip.getTipElement(), childContent)
|
||||
|
||||
expect(childContent.parentNode).toEqual(tooltip.getTipElement())
|
||||
})
|
||||
|
||||
it('should do nothing if the content is a child of the element', () => {
|
||||
fixtureEl.innerHTML = [
|
||||
'<a href="#" rel="tooltip" title="Another tooltip">',
|
||||
@@ -1128,7 +1140,7 @@ describe('Tooltip', () => {
|
||||
})
|
||||
|
||||
tooltip.getTipElement().append(childContent)
|
||||
tooltip.setElementContent(tooltip.getTipElement(), childContent)
|
||||
tooltip.setContent({ '.tooltip': childContent })
|
||||
|
||||
expect().nothing()
|
||||
})
|
||||
@@ -1145,7 +1157,7 @@ describe('Tooltip', () => {
|
||||
html: true
|
||||
})
|
||||
|
||||
tooltip.setElementContent(tooltip.getTipElement(), { 0: childContent, jquery: 'jQuery' })
|
||||
tooltip.setContent({ '.tooltip': { 0: childContent, jquery: 'jQuery' } })
|
||||
|
||||
expect(childContent.parentNode).toEqual(tooltip.getTipElement())
|
||||
})
|
||||
@@ -1160,7 +1172,7 @@ describe('Tooltip', () => {
|
||||
const childContent = fixtureEl.querySelector('div')
|
||||
const tooltip = new Tooltip(tooltipEl)
|
||||
|
||||
tooltip.setElementContent(tooltip.getTipElement(), childContent)
|
||||
tooltip.setContent({ '.tooltip': childContent })
|
||||
|
||||
expect(childContent.textContent).toEqual(tooltip.getTipElement().textContent)
|
||||
})
|
||||
@@ -1174,7 +1186,7 @@ describe('Tooltip', () => {
|
||||
html: true
|
||||
})
|
||||
|
||||
tooltip.setElementContent(tooltip.getTipElement(), '<div id="childContent">Tooltip</div>')
|
||||
tooltip.setContent({ '.tooltip': '<div id="childContent">Tooltip</div>' })
|
||||
|
||||
expect(tooltip.getTipElement().querySelector('div').id).toEqual('childContent')
|
||||
})
|
||||
@@ -1187,12 +1199,13 @@ describe('Tooltip', () => {
|
||||
html: true
|
||||
})
|
||||
|
||||
tooltip.setElementContent(tooltip.getTipElement(), [
|
||||
const content = [
|
||||
'<div id="childContent">',
|
||||
' <button type="button">test btn</button>',
|
||||
'</div>'
|
||||
].join(''))
|
||||
].join('')
|
||||
|
||||
tooltip.setContent({ '.tooltip': content })
|
||||
expect(tooltip.getTipElement().querySelector('div').id).toEqual('childContent')
|
||||
expect(tooltip.getTipElement().querySelector('button')).toEqual(null)
|
||||
})
|
||||
@@ -1203,7 +1216,7 @@ describe('Tooltip', () => {
|
||||
const tooltipEl = fixtureEl.querySelector('a')
|
||||
const tooltip = new Tooltip(tooltipEl)
|
||||
|
||||
tooltip.setElementContent(tooltip.getTipElement(), 'test')
|
||||
tooltip.setContent({ '.tooltip': 'test' })
|
||||
|
||||
expect(tooltip.getTipElement().textContent).toEqual('test')
|
||||
})
|
||||
|
305
js/tests/unit/util/template-factory.spec.js
Normal file
305
js/tests/unit/util/template-factory.spec.js
Normal file
@@ -0,0 +1,305 @@
|
||||
import { clearFixture, getFixture } from '../../helpers/fixture'
|
||||
import TemplateFactory from '../../../src/util/template-factory'
|
||||
|
||||
describe('TemplateFactory', () => {
|
||||
let fixtureEl
|
||||
|
||||
beforeAll(() => {
|
||||
fixtureEl = getFixture()
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
clearFixture()
|
||||
})
|
||||
|
||||
describe('NAME', () => {
|
||||
it('should return plugin NAME', () => {
|
||||
expect(TemplateFactory.NAME).toEqual('TemplateFactory')
|
||||
})
|
||||
})
|
||||
|
||||
describe('Default', () => {
|
||||
it('should return plugin default config', () => {
|
||||
expect(TemplateFactory.Default).toEqual(jasmine.any(Object))
|
||||
})
|
||||
})
|
||||
|
||||
describe('toHtml', () => {
|
||||
describe('Sanitization', () => {
|
||||
it('should use "sanitizeHtml" to sanitize template', () => {
|
||||
const factory = new TemplateFactory({
|
||||
sanitize: true,
|
||||
template: '<div><a href="javascript:alert(7)">Click me</a></div>'
|
||||
})
|
||||
const spy = spyOn(factory, '_maybeSanitize').and.callThrough()
|
||||
|
||||
expect(factory.toHtml().innerHTML).not.toContain('href="javascript:alert(7)')
|
||||
expect(spy).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should not sanitize template', () => {
|
||||
const factory = new TemplateFactory({
|
||||
sanitize: false,
|
||||
template: '<div><a href="javascript:alert(7)">Click me</a></div>'
|
||||
})
|
||||
const spy = spyOn(factory, '_maybeSanitize').and.callThrough()
|
||||
|
||||
expect(factory.toHtml().innerHTML).toContain('href="javascript:alert(7)')
|
||||
expect(spy).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should use "sanitizeHtml" to sanitize content', () => {
|
||||
const factory = new TemplateFactory({
|
||||
sanitize: true,
|
||||
html: true,
|
||||
template: '<div id="foo"></div>',
|
||||
content: { '#foo': '<a href="javascript:alert(7)">Click me</a>' }
|
||||
})
|
||||
expect(factory.toHtml().innerHTML).not.toContain('href="javascript:alert(7)')
|
||||
})
|
||||
|
||||
it('should not sanitize content', () => {
|
||||
const factory = new TemplateFactory({
|
||||
sanitize: false,
|
||||
html: true,
|
||||
template: '<div id="foo"></div>',
|
||||
content: { '#foo': '<a href="javascript:alert(7)">Click me</a>' }
|
||||
})
|
||||
expect(factory.toHtml().innerHTML).toContain('href="javascript:alert(7)')
|
||||
})
|
||||
|
||||
it('should sanitize content only if "config.html" is enabled', () => {
|
||||
const factory = new TemplateFactory({
|
||||
sanitize: true,
|
||||
html: false,
|
||||
template: '<div id="foo"></div>',
|
||||
content: { '#foo': '<a href="javascript:alert(7)">Click me</a>' }
|
||||
})
|
||||
const spy = spyOn(factory, '_maybeSanitize').and.callThrough()
|
||||
|
||||
expect(spy).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
describe('Extra Class', () => {
|
||||
it('should add extra class', () => {
|
||||
const factory = new TemplateFactory({
|
||||
extraClass: 'testClass'
|
||||
})
|
||||
expect(factory.toHtml().classList.contains('testClass')).toBeTrue()
|
||||
})
|
||||
|
||||
it('should add extra classes', () => {
|
||||
const factory = new TemplateFactory({
|
||||
extraClass: 'testClass testClass2'
|
||||
})
|
||||
expect(factory.toHtml().classList.contains('testClass')).toBeTrue()
|
||||
expect(factory.toHtml().classList.contains('testClass2')).toBeTrue()
|
||||
})
|
||||
|
||||
it('should resolve class if function is given', () => {
|
||||
const factory = new TemplateFactory({
|
||||
extraClass: arg => {
|
||||
expect(arg).toEqual(factory)
|
||||
return 'testClass'
|
||||
}
|
||||
})
|
||||
|
||||
expect(factory.toHtml().classList.contains('testClass')).toBeTrue()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('Content', () => {
|
||||
it('add simple text content', () => {
|
||||
const template = [
|
||||
'<div>' +
|
||||
'<div class="foo"></div>' +
|
||||
'<div class="foo2"></div>' +
|
||||
'</div>'
|
||||
].join(' ')
|
||||
|
||||
const factory = new TemplateFactory({
|
||||
template,
|
||||
content: {
|
||||
'.foo': 'bar',
|
||||
'.foo2': 'bar2'
|
||||
}
|
||||
})
|
||||
|
||||
const html = factory.toHtml()
|
||||
expect(html.querySelector('.foo').textContent).toBe('bar')
|
||||
expect(html.querySelector('.foo2').textContent).toBe('bar2')
|
||||
})
|
||||
|
||||
it('should not fill template if selector not exists', () => {
|
||||
const factory = new TemplateFactory({
|
||||
sanitize: true,
|
||||
html: true,
|
||||
template: '<div id="foo"></div>',
|
||||
content: { '#bar': 'test' }
|
||||
})
|
||||
|
||||
expect(factory.toHtml().outerHTML).toBe('<div id="foo"></div>')
|
||||
})
|
||||
|
||||
it('should remove template selector, if content is null', () => {
|
||||
const factory = new TemplateFactory({
|
||||
sanitize: true,
|
||||
html: true,
|
||||
template: '<div><div id="foo"></div></div>',
|
||||
content: { '#foo': null }
|
||||
})
|
||||
|
||||
expect(factory.toHtml().outerHTML).toBe('<div></div>')
|
||||
})
|
||||
|
||||
it('should resolve content if is function', () => {
|
||||
const factory = new TemplateFactory({
|
||||
sanitize: true,
|
||||
html: true,
|
||||
template: '<div><div id="foo"></div></div>',
|
||||
content: { '#foo': () => null }
|
||||
})
|
||||
|
||||
expect(factory.toHtml().outerHTML).toBe('<div></div>')
|
||||
})
|
||||
|
||||
it('if content is element and "config.html=false", should put content\'s textContent', () => {
|
||||
fixtureEl.innerHTML = '<div>foo<span>bar</span></div>'
|
||||
const contentElement = fixtureEl.querySelector('div')
|
||||
|
||||
const factory = new TemplateFactory({
|
||||
html: false,
|
||||
template: '<div><div id="foo"></div></div>',
|
||||
content: { '#foo': contentElement }
|
||||
})
|
||||
|
||||
const fooEl = factory.toHtml().querySelector('#foo')
|
||||
expect(fooEl.innerHTML).not.toBe(contentElement.innerHTML)
|
||||
expect(fooEl.textContent).toBe(contentElement.textContent)
|
||||
expect(fooEl.textContent).toBe('foobar')
|
||||
})
|
||||
|
||||
it('if content is element and "config.html=true", should put content\'s outerHtml as child', () => {
|
||||
fixtureEl.innerHTML = '<div>foo<span>bar</span></div>'
|
||||
const contentElement = fixtureEl.querySelector('div')
|
||||
|
||||
const factory = new TemplateFactory({
|
||||
html: true,
|
||||
template: '<div><div id="foo"></div></div>',
|
||||
content: { '#foo': contentElement }
|
||||
})
|
||||
|
||||
const fooEl = factory.toHtml().querySelector('#foo')
|
||||
expect(fooEl.innerHTML).toBe(contentElement.outerHTML)
|
||||
expect(fooEl.textContent).toBe(contentElement.textContent)
|
||||
})
|
||||
})
|
||||
|
||||
describe('getContent', () => {
|
||||
it('should get content as array', () => {
|
||||
const factory = new TemplateFactory({
|
||||
content: {
|
||||
'.foo': 'bar',
|
||||
'.foo2': 'bar2'
|
||||
}
|
||||
})
|
||||
expect(factory.getContent()).toEqual(['bar', 'bar2'])
|
||||
})
|
||||
|
||||
it('should filter empties', () => {
|
||||
const factory = new TemplateFactory({
|
||||
content: {
|
||||
'.foo': 'bar',
|
||||
'.foo2': '',
|
||||
'.foo3': null,
|
||||
'.foo4': () => 2,
|
||||
'.foo5': () => null
|
||||
}
|
||||
})
|
||||
expect(factory.getContent()).toEqual(['bar', 2])
|
||||
})
|
||||
})
|
||||
|
||||
describe('hasContent', () => {
|
||||
it('should return true, if it has', () => {
|
||||
const factory = new TemplateFactory({
|
||||
content: {
|
||||
'.foo': 'bar',
|
||||
'.foo2': 'bar2',
|
||||
'.foo3': ''
|
||||
}
|
||||
})
|
||||
expect(factory.hasContent()).toBeTrue()
|
||||
})
|
||||
|
||||
it('should return false, if filtered content is empty', () => {
|
||||
const factory = new TemplateFactory({
|
||||
content: {
|
||||
'.foo2': '',
|
||||
'.foo3': null,
|
||||
'.foo4': () => null
|
||||
}
|
||||
})
|
||||
expect(factory.hasContent()).toBeFalse()
|
||||
})
|
||||
})
|
||||
describe('changeContent', () => {
|
||||
it('should change Content', () => {
|
||||
const template = [
|
||||
'<div>' +
|
||||
'<div class="foo"></div>' +
|
||||
'<div class="foo2"></div>' +
|
||||
'</div>'
|
||||
].join(' ')
|
||||
|
||||
const factory = new TemplateFactory({
|
||||
template,
|
||||
content: {
|
||||
'.foo': 'bar',
|
||||
'.foo2': 'bar2'
|
||||
}
|
||||
})
|
||||
|
||||
const html = selector => factory.toHtml().querySelector(selector).textContent
|
||||
expect(html('.foo')).toEqual('bar')
|
||||
expect(html('.foo2')).toEqual('bar2')
|
||||
factory.changeContent({
|
||||
'.foo': 'test',
|
||||
'.foo2': 'test2'
|
||||
})
|
||||
|
||||
expect(html('.foo')).toEqual('test')
|
||||
expect(html('.foo2')).toEqual('test2')
|
||||
})
|
||||
|
||||
it('should change only the given, content', () => {
|
||||
const template = [
|
||||
'<div>' +
|
||||
'<div class="foo"></div>' +
|
||||
'<div class="foo2"></div>' +
|
||||
'</div>'
|
||||
].join(' ')
|
||||
|
||||
const factory = new TemplateFactory({
|
||||
template,
|
||||
content: {
|
||||
'.foo': 'bar',
|
||||
'.foo2': 'bar2'
|
||||
}
|
||||
})
|
||||
|
||||
const html = selector => factory.toHtml().querySelector(selector).textContent
|
||||
expect(html('.foo')).toEqual('bar')
|
||||
expect(html('.foo2')).toEqual('bar2')
|
||||
factory.changeContent({
|
||||
'.foo': 'test',
|
||||
'.wrong': 'wrong'
|
||||
})
|
||||
|
||||
expect(html('.foo')).toEqual('test')
|
||||
expect(html('.foo2')).toEqual('bar2')
|
||||
})
|
||||
})
|
||||
})
|
Reference in New Issue
Block a user