aboutsummaryrefslogtreecommitdiff
path: root/js/tests
diff options
context:
space:
mode:
authorGeoSot <[email protected]>2021-11-25 19:14:02 +0200
committerGitHub <[email protected]>2021-11-25 19:14:02 +0200
commit94a596fbcb1011ba990da2078ba7e20b39dba2d9 (patch)
tree26af41580d5cae017e32e29cfef96178e897afa6 /js/tests
parentfa33e83f25faf8c378b99126fbd69977e667ad9a (diff)
downloadbootstrap-94a596fbcb1011ba990da2078ba7e20b39dba2d9.tar.xz
bootstrap-94a596fbcb1011ba990da2078ba7e20b39dba2d9.zip
Add a template factory helper to handle all template cases (#34519)
Co-authored-by: XhmikosR <[email protected]>
Diffstat (limited to 'js/tests')
-rw-r--r--js/tests/unit/popover.spec.js7
-rw-r--r--js/tests/unit/tooltip.spec.js67
-rw-r--r--js/tests/unit/util/template-factory.spec.js305
3 files changed, 349 insertions, 30 deletions
diff --git a/js/tests/unit/popover.spec.js b/js/tests/unit/popover.spec.js
index 4452a132d..b3bba3180 100644
--- a/js/tests/unit/popover.spec.js
+++ b/js/tests/unit/popover.spec.js
@@ -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()
}
diff --git a/js/tests/unit/tooltip.spec.js b/js/tests/unit/tooltip.spec.js
index 0cca4acff..3c28cd837 100644
--- a/js/tests/unit/tooltip.spec.js
+++ b/js/tests/unit/tooltip.spec.js
@@ -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')
})
diff --git a/js/tests/unit/util/template-factory.spec.js b/js/tests/unit/util/template-factory.spec.js
new file mode 100644
index 000000000..842c480c2
--- /dev/null
+++ b/js/tests/unit/util/template-factory.spec.js
@@ -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')
+ })
+ })
+})