diff options
| author | GeoSot <[email protected]> | 2021-11-25 19:14:02 +0200 |
|---|---|---|
| committer | GitHub <[email protected]> | 2021-11-25 19:14:02 +0200 |
| commit | 94a596fbcb1011ba990da2078ba7e20b39dba2d9 (patch) | |
| tree | 26af41580d5cae017e32e29cfef96178e897afa6 /js/tests | |
| parent | fa33e83f25faf8c378b99126fbd69977e667ad9a (diff) | |
| download | bootstrap-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.js | 7 | ||||
| -rw-r--r-- | js/tests/unit/tooltip.spec.js | 67 | ||||
| -rw-r--r-- | js/tests/unit/util/template-factory.spec.js | 305 |
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') + }) + }) +}) |
