@netflix/x-element
Advanced tools
Comparing version 1.0.0-rc.53 to 1.0.0-rc.54
@@ -5,3 +5,3 @@ { | ||
"description": "Custom Element base class.", | ||
"version": "1.0.0-rc.53", | ||
"version": "1.0.0-rc.54", | ||
"license": "SEE LICENSE IN LICENSE", | ||
@@ -32,5 +32,5 @@ "repository": "https://github.com/Netflix/x-element", | ||
"http-server": "^14.1.1", | ||
"puppeteer": "^13.4.0", | ||
"tap-parser": "^10.0.1" | ||
"puppeteer": "^19.7.5", | ||
"tap-parser": "^12.0.1" | ||
} | ||
} |
@@ -1,2 +0,2 @@ | ||
import { test, cover } from './x-test.js'; | ||
import { test, coverage } from './x-test.js'; | ||
@@ -7,3 +7,3 @@ // We import this here so we can see code coverage. | ||
// Set a high bar for code coverage! | ||
cover(new URL('../x-element.js', import.meta.url).href, 100); | ||
coverage(new URL('../x-element.js', import.meta.url).href, 100); | ||
@@ -10,0 +10,0 @@ test('./test-analysis-errors.html'); |
import XElement from '../x-element.js'; | ||
import { assert, it, skip } from './x-test.js'; | ||
import { assert, it } from './x-test.js'; | ||
@@ -223,3 +223,3 @@ class TestElement extends XElement { | ||
// seems like a bug in the browser, but we should look into it. | ||
(navigator.userAgent.includes('Firefox') ? skip : it)('test adoptedCallback', () => { | ||
(navigator.userAgent.includes('Firefox') ? it.skip : it)('test adoptedCallback', () => { | ||
const el = document.createElement('test-element'); | ||
@@ -226,0 +226,0 @@ el.prop1 = 'adopt me!'; |
import XElement from '../x-element.js'; | ||
import { assert, it } from './x-test.js'; | ||
import { assert, describe, it } from './x-test.js'; | ||
@@ -10,1512 +10,1570 @@ // Long-term interface. | ||
it('html: renders basic string', () => { | ||
const getTemplate = () => { | ||
return html`<div id="target">No interpolation.</div>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
render(container, getTemplate()); | ||
assert(container.querySelector('#target').textContent === 'No interpolation.'); | ||
container.remove(); | ||
}); | ||
describe('html rendering', () => { | ||
it('renders basic string', () => { | ||
const getTemplate = () => { | ||
return html`<div id="target">No interpolation.</div>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
render(container, getTemplate()); | ||
assert(container.querySelector('#target').textContent === 'No interpolation.'); | ||
container.remove(); | ||
}); | ||
it('html: refuses to interpolate within a style tag', () => { | ||
const getTemplate = ({ color }) => { | ||
return html` | ||
<style id="target"> | ||
#target { | ||
background-color: ${color}; | ||
} | ||
</style> | ||
`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
render(container, getTemplate({ color: 'url(evil-url)' })); | ||
const textContent = container.querySelector('#target').textContent; | ||
assert(textContent === '/* x-element: Interpolation is not allowed here. */', textContent); | ||
container.remove(); | ||
}); | ||
it('refuses to interpolate within a style tag', () => { | ||
const getTemplate = ({ color }) => { | ||
return html` | ||
<style id="target"> | ||
#target { | ||
background-color: ${color}; | ||
} | ||
</style> | ||
`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
render(container, getTemplate({ color: 'url(evil-url)' })); | ||
const textContent = container.querySelector('#target').textContent; | ||
assert(textContent === '/* x-element: Interpolation is not allowed here. */', textContent); | ||
container.remove(); | ||
}); | ||
it('html: refuses to interpolate within a script tag', () => { | ||
const getTemplate = ({ message }) => { | ||
return html` | ||
<script id="target"> | ||
console.log('${message}'); | ||
</script> | ||
`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
render(container, getTemplate({ message: '\' + prompt(\'evil\') + \'' })); | ||
const textContent = container.querySelector('#target').textContent; | ||
assert(textContent === '/* x-element: Interpolation is not allowed here. */', textContent); | ||
container.remove(); | ||
}); | ||
it('refuses to interpolate within a script tag', () => { | ||
const getTemplate = ({ message }) => { | ||
return html` | ||
<script id="target"> | ||
console.log('${message}'); | ||
</script> | ||
`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
render(container, getTemplate({ message: '\' + prompt(\'evil\') + \'' })); | ||
const textContent = container.querySelector('#target').textContent; | ||
assert(textContent === '/* x-element: Interpolation is not allowed here. */', textContent); | ||
container.remove(); | ||
}); | ||
it('html: renders interpolated content without parsing', () => { | ||
const userContent = '<a href="https://evil.com">Click Me!</a>'; | ||
const getTemplate = () => { | ||
return html`<div id="target">${userContent}</div>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
render(container, getTemplate()); | ||
assert(container.querySelector('#target').textContent === userContent); | ||
container.remove(); | ||
}); | ||
it('renders interpolated content without parsing', () => { | ||
const userContent = '<a href="https://evil.com">Click Me!</a>'; | ||
const getTemplate = () => { | ||
return html`<div id="target">${userContent}</div>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
render(container, getTemplate()); | ||
assert(container.querySelector('#target').textContent === userContent); | ||
container.remove(); | ||
}); | ||
it('html: does not recognize single-quoted attributes', () => { | ||
const getTemplate = () => { | ||
return html`<div id="target" ignore-me='${'foo'}'>Gotta double-quote those.</div>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
render(container, getTemplate()); | ||
assert(container.querySelector('#target').getAttribute('ignore-me') !== 'foo'); | ||
container.remove(); | ||
}); | ||
it('does not recognize single-quoted attributes', () => { | ||
const getTemplate = () => { | ||
return html`<div id="target" ignore-me='${'foo'}'>Gotta double-quote those.</div>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
render(container, getTemplate()); | ||
assert(container.querySelector('#target').getAttribute('ignore-me') !== 'foo'); | ||
container.remove(); | ||
}); | ||
it('html: does not recognize single-quoted properties', () => { | ||
const getTemplate = () => { | ||
return html`<div id="target" .ignoreMe='${'foo'}'>Gotta double-quote those.</div>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
render(container, getTemplate()); | ||
assert(container.querySelector('#target').ignoreMe !== 'foo'); | ||
container.remove(); | ||
}); | ||
it('does not recognize single-quoted properties', () => { | ||
const getTemplate = () => { | ||
return html`<div id="target" .ignoreMe='${'foo'}'>Gotta double-quote those.</div>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
render(container, getTemplate()); | ||
assert(container.querySelector('#target').ignoreMe !== 'foo'); | ||
container.remove(); | ||
}); | ||
it('html: refuses to reuse a template', () => { | ||
const templateResultReference = html`<div id="target"></div>`; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
render(container, templateResultReference); | ||
assert(!!container.querySelector('#target')); | ||
render(container, null); | ||
assert(!container.querySelector('#target')); | ||
let error; | ||
try { | ||
it('refuses to reuse a template', () => { | ||
const templateResultReference = html`<div id="target"></div>`; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
render(container, templateResultReference); | ||
} catch (e) { | ||
error = e; | ||
} | ||
assert(error?.message === 'Unexpected re-injection of template result.', error.message); | ||
container.remove(); | ||
}); | ||
assert(!!container.querySelector('#target')); | ||
render(container, null); | ||
assert(!container.querySelector('#target')); | ||
let error; | ||
try { | ||
render(container, templateResultReference); | ||
} catch (e) { | ||
error = e; | ||
} | ||
assert(error?.message === 'Unexpected re-injection of template result.', error.message); | ||
container.remove(); | ||
}); | ||
it('html: renders nullish templates', () => { | ||
const getTemplate = () => { | ||
return html`<div></div>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
render(container, getTemplate()); | ||
assert(!!container.childNodes.length); | ||
render(container, null); | ||
assert(!container.childNodes.length); | ||
render(container, getTemplate()); | ||
assert(!!container.childNodes.length); | ||
render(container, undefined); | ||
assert(!container.childNodes.length); | ||
container.remove(); | ||
}); | ||
it('renders nullish templates', () => { | ||
const getTemplate = () => { | ||
return html`<div></div>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
render(container, getTemplate()); | ||
assert(!!container.childNodes.length); | ||
render(container, null); | ||
assert(!container.childNodes.length); | ||
render(container, getTemplate()); | ||
assert(!!container.childNodes.length); | ||
render(container, undefined); | ||
assert(!container.childNodes.length); | ||
container.remove(); | ||
}); | ||
it('html: renders interpolated content', () => { | ||
const getTemplate = ({ content }) => { | ||
return html`<div id="target">${content}</div>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
render(container, getTemplate({ content: 'Interpolated.' })); | ||
assert(container.querySelector('#target').textContent === 'Interpolated.'); | ||
render(container, getTemplate({ content: 'Updated.' })); | ||
assert(container.querySelector('#target').textContent === 'Updated.'); | ||
container.remove(); | ||
}); | ||
it('renders interpolated content', () => { | ||
const getTemplate = ({ content }) => { | ||
return html`<div id="target">${content}</div>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
render(container, getTemplate({ content: 'Interpolated.' })); | ||
assert(container.querySelector('#target').textContent === 'Interpolated.'); | ||
render(container, getTemplate({ content: 'Updated.' })); | ||
assert(container.querySelector('#target').textContent === 'Updated.'); | ||
container.remove(); | ||
}); | ||
it('html: renders multiple, interpolated content', () => { | ||
const getTemplate = ({ one, two }) => { | ||
return html` | ||
<div id="target">one: ${one} / two: ${two}</div> | ||
`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
render(container, getTemplate({ one: 'ONE', two: 'TWO' })); | ||
assert(container.querySelector('#target').textContent === 'one: ONE / two: TWO'); | ||
container.remove(); | ||
}); | ||
it('renders multiple, interpolated content', () => { | ||
const getTemplate = ({ one, two }) => { | ||
return html` | ||
<div id="target">one: ${one} / two: ${two}</div> | ||
`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
render(container, getTemplate({ one: 'ONE', two: 'TWO' })); | ||
assert(container.querySelector('#target').textContent === 'one: ONE / two: TWO'); | ||
container.remove(); | ||
}); | ||
it('html: renders nested content', () => { | ||
const getTemplate = ({ show, nestedContent }) => { | ||
return html` | ||
<div id="target"> | ||
${show ? html`<span id="conditional">${nestedContent}</span>` : null} | ||
</div> | ||
`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
render(container, getTemplate({ show: true, nestedContent: 'oh hai' })); | ||
assert(!!container.querySelector('#conditional')); | ||
render(container, getTemplate({ show: false, nestedContent: 'oh hai' })); | ||
assert(!container.querySelector('#conditional')); | ||
render(container, getTemplate({ show: true, nestedContent: 'oh hai' })); | ||
assert(container.querySelector('#conditional').textContent === 'oh hai'); | ||
render(container, getTemplate({ show: true, nestedContent: 'k bye' })); | ||
assert(container.querySelector('#conditional').textContent === 'k bye'); | ||
container.remove(); | ||
}); | ||
it('renders nested content', () => { | ||
const getTemplate = ({ show, nestedContent }) => { | ||
return html` | ||
<div id="target"> | ||
${show ? html`<span id="conditional">${nestedContent}</span>` : null} | ||
</div> | ||
`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
render(container, getTemplate({ show: true, nestedContent: 'oh hai' })); | ||
assert(!!container.querySelector('#conditional')); | ||
render(container, getTemplate({ show: false, nestedContent: 'oh hai' })); | ||
assert(!container.querySelector('#conditional')); | ||
render(container, getTemplate({ show: true, nestedContent: 'oh hai' })); | ||
assert(container.querySelector('#conditional').textContent === 'oh hai'); | ||
render(container, getTemplate({ show: true, nestedContent: 'k bye' })); | ||
assert(container.querySelector('#conditional').textContent === 'k bye'); | ||
container.remove(); | ||
}); | ||
it('html: renders attributes', () => { | ||
const getTemplate = ({ attr }) => { | ||
return html`<div id="target" attr="${attr}"></div>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
render(container, getTemplate({ attr: 'foo' })); | ||
assert(container.querySelector('#target').getAttribute('attr') === 'foo'); | ||
render(container, getTemplate({ attr: 'bar' })); | ||
assert(container.querySelector('#target').getAttribute('attr') === 'bar'); | ||
container.remove(); | ||
}); | ||
it('renders attributes', () => { | ||
const getTemplate = ({ attr }) => { | ||
return html`<div id="target" attr="${attr}"></div>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
render(container, getTemplate({ attr: 'foo' })); | ||
assert(container.querySelector('#target').getAttribute('attr') === 'foo'); | ||
render(container, getTemplate({ attr: 'bar' })); | ||
assert(container.querySelector('#target').getAttribute('attr') === 'bar'); | ||
container.remove(); | ||
}); | ||
it('html: renders boolean attributes', () => { | ||
const getTemplate = ({ attr }) => { | ||
return html`<div id="target" ?attr="${attr}"></div>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
render(container, getTemplate({ attr: 'foo' })); | ||
assert(container.querySelector('#target').getAttribute('attr') === ''); | ||
render(container, getTemplate({ attr: '' })); | ||
assert(container.querySelector('#target').getAttribute('attr') === null); | ||
render(container, getTemplate({ attr: 'bar' })); | ||
assert(container.querySelector('#target').getAttribute('attr') === ''); | ||
render(container, getTemplate({ attr: undefined })); | ||
assert(container.querySelector('#target').getAttribute('attr') === null); | ||
render(container, getTemplate({ attr: 'baz' })); | ||
assert(container.querySelector('#target').getAttribute('attr') === ''); | ||
render(container, getTemplate({ attr: false })); | ||
assert(container.querySelector('#target').getAttribute('attr') === null); | ||
render(container, getTemplate({ attr: true })); | ||
assert(container.querySelector('#target').getAttribute('attr') === ''); | ||
container.remove(); | ||
}); | ||
it('renders boolean attributes', () => { | ||
const getTemplate = ({ attr }) => { | ||
return html`<div id="target" ?attr="${attr}"></div>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
render(container, getTemplate({ attr: 'foo' })); | ||
assert(container.querySelector('#target').getAttribute('attr') === ''); | ||
render(container, getTemplate({ attr: '' })); | ||
assert(container.querySelector('#target').getAttribute('attr') === null); | ||
render(container, getTemplate({ attr: 'bar' })); | ||
assert(container.querySelector('#target').getAttribute('attr') === ''); | ||
render(container, getTemplate({ attr: undefined })); | ||
assert(container.querySelector('#target').getAttribute('attr') === null); | ||
render(container, getTemplate({ attr: 'baz' })); | ||
assert(container.querySelector('#target').getAttribute('attr') === ''); | ||
render(container, getTemplate({ attr: false })); | ||
assert(container.querySelector('#target').getAttribute('attr') === null); | ||
render(container, getTemplate({ attr: true })); | ||
assert(container.querySelector('#target').getAttribute('attr') === ''); | ||
container.remove(); | ||
}); | ||
it('html: renders properties', () => { | ||
const getTemplate = ({ prop }) => { | ||
return html`<div id="target" .prop="${prop}"></div>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
render(container, getTemplate({ prop: 'foo' })); | ||
assert(container.querySelector('#target').prop === 'foo'); | ||
render(container, getTemplate({ prop: 'bar' })); | ||
assert(container.querySelector('#target').prop === 'bar'); | ||
container.remove(); | ||
}); | ||
it('renders properties', () => { | ||
const getTemplate = ({ prop }) => { | ||
return html`<div id="target" .prop="${prop}"></div>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
render(container, getTemplate({ prop: 'foo' })); | ||
assert(container.querySelector('#target').prop === 'foo'); | ||
render(container, getTemplate({ prop: 'bar' })); | ||
assert(container.querySelector('#target').prop === 'bar'); | ||
container.remove(); | ||
}); | ||
it('html: maintains DOM nodes', () => { | ||
const getTemplate = ({ content }) => { | ||
return html`<div>${content}</div>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
render(container, getTemplate({ content: 'foo' })); | ||
container.querySelector('div').classList.add('state'); | ||
assert(container.querySelector('div').textContent === 'foo'); | ||
assert(container.querySelector('div').classList.contains('state')); | ||
render(container, getTemplate({ content: 'bar' })); | ||
assert(container.querySelector('div').textContent === 'bar'); | ||
assert(container.querySelector('div').classList.contains('state')); | ||
container.remove(); | ||
}); | ||
it('maintains DOM nodes', () => { | ||
const getTemplate = ({ content }) => { | ||
return html`<div>${content}</div>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
render(container, getTemplate({ content: 'foo' })); | ||
container.querySelector('div').classList.add('state'); | ||
assert(container.querySelector('div').textContent === 'foo'); | ||
assert(container.querySelector('div').classList.contains('state')); | ||
render(container, getTemplate({ content: 'bar' })); | ||
assert(container.querySelector('div').textContent === 'bar'); | ||
assert(container.querySelector('div').classList.contains('state')); | ||
container.remove(); | ||
}); | ||
it('html: renders lists', () => { | ||
const getTemplate = ({ items }) => { | ||
return html` | ||
<div id="target"> | ||
${items.map(item => html`<div>${item}</div>`)} | ||
</div> | ||
`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
render(container, getTemplate({ items: ['foo', 'bar', 'baz'] })); | ||
assert(container.querySelector('#target').childElementCount === 3); | ||
assert(container.querySelector('#target').children[0].textContent === 'foo'); | ||
assert(container.querySelector('#target').children[1].textContent === 'bar'); | ||
assert(container.querySelector('#target').children[2].textContent === 'baz'); | ||
container.remove(); | ||
}); | ||
it('renders lists', () => { | ||
const getTemplate = ({ items }) => { | ||
return html` | ||
<div id="target"> | ||
${items.map(item => html`<div>${item}</div>`)} | ||
</div> | ||
`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
render(container, getTemplate({ items: ['foo', 'bar', 'baz'] })); | ||
assert(container.querySelector('#target').childElementCount === 3); | ||
assert(container.querySelector('#target').children[0].textContent === 'foo'); | ||
assert(container.querySelector('#target').children[1].textContent === 'bar'); | ||
assert(container.querySelector('#target').children[2].textContent === 'baz'); | ||
container.remove(); | ||
}); | ||
it('html: renders lists with multiple elements', () => { | ||
const getTemplate = ({ items }) => { | ||
return html` | ||
<div id="target"> | ||
${items.map(item => html`<div>${item}-</div><div>${item}</div>`)} | ||
</div> | ||
`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
render(container, getTemplate({ items: ['foo', 'bar', 'baz'] })); | ||
assert(container.querySelector('#target').childElementCount === 6); | ||
assert(container.querySelector('#target').children[0].textContent === 'foo-'); | ||
assert(container.querySelector('#target').children[1].textContent === 'foo'); | ||
assert(container.querySelector('#target').children[2].textContent === 'bar-'); | ||
assert(container.querySelector('#target').children[3].textContent === 'bar'); | ||
assert(container.querySelector('#target').children[4].textContent === 'baz-'); | ||
assert(container.querySelector('#target').children[5].textContent === 'baz'); | ||
container.remove(); | ||
}); | ||
it('renders lists with multiple elements', () => { | ||
const getTemplate = ({ items }) => { | ||
return html` | ||
<div id="target"> | ||
${items.map(item => html`<div>${item}-</div><div>${item}</div>`)} | ||
</div> | ||
`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
render(container, getTemplate({ items: ['foo', 'bar', 'baz'] })); | ||
assert(container.querySelector('#target').childElementCount === 6); | ||
assert(container.querySelector('#target').children[0].textContent === 'foo-'); | ||
assert(container.querySelector('#target').children[1].textContent === 'foo'); | ||
assert(container.querySelector('#target').children[2].textContent === 'bar-'); | ||
assert(container.querySelector('#target').children[3].textContent === 'bar'); | ||
assert(container.querySelector('#target').children[4].textContent === 'baz-'); | ||
assert(container.querySelector('#target').children[5].textContent === 'baz'); | ||
container.remove(); | ||
}); | ||
it('html: renders lists with changing templates', () => { | ||
const getTemplate = ({ items }) => { | ||
return html` | ||
<div id="target"> | ||
${items.map(item => item ? html`<div class="true"></div>` : html`<div class="false"></div>`)} | ||
</div> | ||
`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
render(container, getTemplate({ items: [true, true, true] })); | ||
assert(container.querySelector('#target').childElementCount === 3); | ||
assert(container.querySelector('#target').children[0].classList.contains('true')); | ||
assert(container.querySelector('#target').children[1].classList.contains('true')); | ||
assert(container.querySelector('#target').children[2].classList.contains('true')); | ||
render(container, getTemplate({ items: [true, false, true] })); | ||
assert(container.querySelector('#target').children[0].classList.contains('true')); | ||
assert(container.querySelector('#target').children[1].classList.contains('false')); | ||
assert(container.querySelector('#target').children[2].classList.contains('true')); | ||
container.remove(); | ||
}); | ||
it('renders lists with changing templates', () => { | ||
const getTemplate = ({ items }) => { | ||
return html` | ||
<div id="target"> | ||
${items.map(item => item ? html`<div class="true"></div>` : html`<div class="false"></div>`)} | ||
</div> | ||
`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
render(container, getTemplate({ items: [true, true, true] })); | ||
assert(container.querySelector('#target').childElementCount === 3); | ||
assert(container.querySelector('#target').children[0].classList.contains('true')); | ||
assert(container.querySelector('#target').children[1].classList.contains('true')); | ||
assert(container.querySelector('#target').children[2].classList.contains('true')); | ||
render(container, getTemplate({ items: [true, false, true] })); | ||
assert(container.querySelector('#target').children[0].classList.contains('true')); | ||
assert(container.querySelector('#target').children[1].classList.contains('false')); | ||
assert(container.querySelector('#target').children[2].classList.contains('true')); | ||
container.remove(); | ||
}); | ||
it('html: renders lists with changing length', () => { | ||
const getTemplate = ({ items }) => { | ||
return html` | ||
<div id="target"> | ||
${items.map(() => html`<div class="item"></div>`)} | ||
</div> | ||
`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
render(container, getTemplate({ items: [1, 2, 3] })); | ||
assert(container.querySelector('#target').childElementCount === 3); | ||
render(container, getTemplate({ items: [1, 2, 3, 4, 5] })); | ||
assert(container.querySelector('#target').childElementCount === 5); | ||
render(container, getTemplate({ items: [1, 2] })); | ||
assert(container.querySelector('#target').childElementCount === 2); | ||
container.remove(); | ||
}); | ||
it('renders lists with changing length', () => { | ||
const getTemplate = ({ items }) => { | ||
return html` | ||
<div id="target"> | ||
${items.map(() => html`<div class="item"></div>`)} | ||
</div> | ||
`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
render(container, getTemplate({ items: [1, 2, 3] })); | ||
assert(container.querySelector('#target').childElementCount === 3); | ||
render(container, getTemplate({ items: [1, 2, 3, 4, 5] })); | ||
assert(container.querySelector('#target').childElementCount === 5); | ||
render(container, getTemplate({ items: [1, 2] })); | ||
assert(container.querySelector('#target').childElementCount === 2); | ||
container.remove(); | ||
}); | ||
it('html: renders multiple templates', () => { | ||
const getTemplate = ({ content }) => { | ||
if (content) { | ||
return html`<div id="content">${content}</div>`; | ||
} else { | ||
return html`<div id="empty"></div>`; | ||
} | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
render(container, getTemplate({ content: 'oh hai' })); | ||
assert(!!container.querySelector('#content')); | ||
assert(!container.querySelector('#empty')); | ||
render(container, getTemplate({ content: '' })); | ||
assert(!container.querySelector('#content')); | ||
assert(!!container.querySelector('#empty')); | ||
container.remove(); | ||
}); | ||
it('renders multiple templates', () => { | ||
const getTemplate = ({ content }) => { | ||
if (content) { | ||
return html`<div id="content">${content}</div>`; | ||
} else { | ||
return html`<div id="empty"></div>`; | ||
} | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
render(container, getTemplate({ content: 'oh hai' })); | ||
assert(!!container.querySelector('#content')); | ||
assert(!container.querySelector('#empty')); | ||
render(container, getTemplate({ content: '' })); | ||
assert(!container.querySelector('#content')); | ||
assert(!!container.querySelector('#empty')); | ||
container.remove(); | ||
}); | ||
it('html: renders multiple templates (as content)', () => { | ||
const getTemplate = ({ content }) => { | ||
return html` | ||
<div id="target"> | ||
${content ? html`<div id="content">${content}</div>` : html`<div id="empty"></div>`} | ||
</div> | ||
`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
render(container, getTemplate({ content: 'oh hai' })); | ||
assert(!!container.querySelector('#content')); | ||
assert(!container.querySelector('#empty')); | ||
render(container, getTemplate({ content: '' })); | ||
assert(!container.querySelector('#content')); | ||
assert(!!container.querySelector('#empty')); | ||
container.remove(); | ||
}); | ||
it('renders multiple templates (as content)', () => { | ||
const getTemplate = ({ content }) => { | ||
return html` | ||
<div id="target"> | ||
${content ? html`<div id="content">${content}</div>` : html`<div id="empty"></div>`} | ||
</div> | ||
`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
render(container, getTemplate({ content: 'oh hai' })); | ||
assert(!!container.querySelector('#content')); | ||
assert(!container.querySelector('#empty')); | ||
render(container, getTemplate({ content: '' })); | ||
assert(!container.querySelector('#content')); | ||
assert(!!container.querySelector('#empty')); | ||
container.remove(); | ||
}); | ||
it('html: renders elements with special characters in attributes', () => { | ||
// Note the "/", "<", ">", and """ characters. | ||
const getTemplate = ({ width, height }) => { | ||
return html`\ | ||
<svg | ||
id="svg" | ||
class="<><>""</></>" | ||
xmlns="http://www.w3.org/2000/svg" | ||
viewBox="0 0 24 24" | ||
width="${ifDefined(width)}" | ||
height="${ifDefined(height)}"> | ||
<circle id="circle" r="${width / 2}" cx="${width / 2}" cy="${height / 2}"></circle> | ||
</svg>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
const width = 24; | ||
const height = 24; | ||
render(container, getTemplate({ width, height })); | ||
const svgBox = container.querySelector('#svg').getBoundingClientRect(); | ||
assert(svgBox.width === width); | ||
assert(svgBox.height === height); | ||
const circleBox = container.querySelector('#circle').getBoundingClientRect(); | ||
assert(circleBox.width === width); | ||
assert(circleBox.height === height); | ||
container.remove(); | ||
}); | ||
it('renders elements with "/" characters in attributes', () => { | ||
// Note the "/" character. | ||
const getTemplate = ({ width, height }) => { | ||
return html`\ | ||
<svg | ||
id="svg" | ||
xmlns="http://www.w3.org/2000/svg" | ||
viewBox="0 0 24 24" | ||
width="${ifDefined(width)}" | ||
height="${ifDefined(height)}"> | ||
<circle id="circle" r="${width / 2}" cx="${width / 2}" cy="${height / 2}"></circle> | ||
</svg>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
const width = 24; | ||
const height = 24; | ||
render(container, getTemplate({ width, height })); | ||
const svgBox = container.querySelector('#svg').getBoundingClientRect(); | ||
assert(svgBox.width === width); | ||
assert(svgBox.height === height); | ||
const circleBox = container.querySelector('#circle').getBoundingClientRect(); | ||
assert(circleBox.width === width); | ||
assert(circleBox.height === height); | ||
container.remove(); | ||
}); | ||
it('html: self-closing tags work', () => { | ||
const getTemplate = ({ type }) => { | ||
return html`<input type="${nullish(type)}"/>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
render(container, getTemplate({ type: null })); | ||
assert(container.querySelector('input').type === 'text'); | ||
render(container, getTemplate({ type: 'checkbox' })); | ||
assert(container.querySelector('input').type === 'checkbox'); | ||
container.remove(); | ||
}); | ||
// TODO: trying to find escaped values in strings is possible via a regex, but | ||
// making that performant is nuanced. I.e., it's easy to cause catastrophic | ||
// backtracking. | ||
it.todo('renders elements with "<" or ">" characters in attributes', () => { | ||
// Note the "/", "<", and ">" characters. | ||
const getTemplate = ({ width, height }) => { | ||
return html`\ | ||
<svg | ||
id="svg" | ||
class="<><></></>" | ||
xmlns="http://www.w3.org/2000/svg" | ||
viewBox="0 0 24 24" | ||
width="${ifDefined(width)}" | ||
height="${ifDefined(height)}"> | ||
<circle id="circle" r="${width / 2}" cx="${width / 2}" cy="${height / 2}"></circle> | ||
</svg>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
const width = 24; | ||
const height = 24; | ||
render(container, getTemplate({ width, height })); | ||
const svgBox = container.querySelector('#svg').getBoundingClientRect(); | ||
assert(svgBox.width === width); | ||
assert(svgBox.height === height); | ||
const circleBox = container.querySelector('#circle').getBoundingClientRect(); | ||
assert(circleBox.width === width); | ||
assert(circleBox.height === height); | ||
container.remove(); | ||
}); | ||
it('html: textarea element content', () => { | ||
const getTemplate = ({ defaultValue }) => { | ||
return html`<textarea>${defaultValue}</textarea>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
render(container, getTemplate({ defaultValue: 'foo' })); | ||
assert(container.querySelector('textarea').value === 'foo'); | ||
container.remove(); | ||
it('self-closing tags work', () => { | ||
const getTemplate = ({ type }) => { | ||
return html`<input type="${nullish(type)}"/>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
render(container, getTemplate({ type: null })); | ||
assert(container.querySelector('input').type === 'text'); | ||
render(container, getTemplate({ type: 'checkbox' })); | ||
assert(container.querySelector('input').type === 'checkbox'); | ||
container.remove(); | ||
}); | ||
it('textarea element content', () => { | ||
const getTemplate = ({ defaultValue }) => { | ||
return html`<textarea>${defaultValue}</textarea>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
render(container, getTemplate({ defaultValue: 'foo' })); | ||
assert(container.querySelector('textarea').value === 'foo'); | ||
container.remove(); | ||
}); | ||
}); | ||
// This is mainly for backwards compat, "nullish" is likely a better match. | ||
it('html: updaters: ifDefined', () => { | ||
const getTemplate = ({ maybe }) => { | ||
return html`<div id="target" maybe="${ifDefined(maybe)}"></div>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
render(container, getTemplate({ maybe: 'yes' })); | ||
assert(container.querySelector('#target').getAttribute('maybe') === 'yes'); | ||
render(container, getTemplate({ maybe: undefined })); | ||
assert(container.querySelector('#target').getAttribute('maybe') === null); | ||
render(container, getTemplate({ maybe: false })); | ||
assert(container.querySelector('#target').getAttribute('maybe') === 'false'); | ||
describe('html updaters', () => { | ||
// This is mainly for backwards compat, "nullish" is likely a better match. | ||
it('ifDefined', () => { | ||
const getTemplate = ({ maybe }) => { | ||
return html`<div id="target" maybe="${ifDefined(maybe)}"></div>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
render(container, getTemplate({ maybe: 'yes' })); | ||
assert(container.querySelector('#target').getAttribute('maybe') === 'yes'); | ||
render(container, getTemplate({ maybe: undefined })); | ||
assert(container.querySelector('#target').getAttribute('maybe') === null); | ||
render(container, getTemplate({ maybe: false })); | ||
assert(container.querySelector('#target').getAttribute('maybe') === 'false'); | ||
// This is correct, but perhaps unexpected. | ||
render(container, getTemplate({ maybe: null })); | ||
assert(container.querySelector('#target').getAttribute('maybe') === 'null'); | ||
container.remove(); | ||
}); | ||
// This is correct, but perhaps unexpected. | ||
render(container, getTemplate({ maybe: null })); | ||
assert(container.querySelector('#target').getAttribute('maybe') === 'null'); | ||
container.remove(); | ||
}); | ||
it('html: updaters: nullish', () => { | ||
const getTemplate = ({ maybe }) => { | ||
return html`<div id="target" maybe="${nullish(maybe)}"></div>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
render(container, getTemplate({ maybe: 'yes' })); | ||
assert(container.querySelector('#target').getAttribute('maybe') === 'yes'); | ||
render(container, getTemplate({ maybe: undefined })); | ||
assert(container.querySelector('#target').getAttribute('maybe') === null); | ||
render(container, getTemplate({ maybe: false })); | ||
assert(container.querySelector('#target').getAttribute('maybe') === 'false'); | ||
render(container, getTemplate({ maybe: null })); | ||
assert(container.querySelector('#target').getAttribute('maybe') === null); | ||
container.remove(); | ||
}); | ||
it('nullish', () => { | ||
const getTemplate = ({ maybe }) => { | ||
return html`<div id="target" maybe="${nullish(maybe)}"></div>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
render(container, getTemplate({ maybe: 'yes' })); | ||
assert(container.querySelector('#target').getAttribute('maybe') === 'yes'); | ||
render(container, getTemplate({ maybe: undefined })); | ||
assert(container.querySelector('#target').getAttribute('maybe') === null); | ||
render(container, getTemplate({ maybe: false })); | ||
assert(container.querySelector('#target').getAttribute('maybe') === 'false'); | ||
render(container, getTemplate({ maybe: null })); | ||
assert(container.querySelector('#target').getAttribute('maybe') === null); | ||
container.remove(); | ||
}); | ||
it('html: updaters: live', () => { | ||
const getTemplate = ({ alive, dead }) => { | ||
return html`<div id="target" .alive="${live(alive)}" .dead="${dead}"></div>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
render(container, getTemplate({ alive: 'lively', dead: 'deadly' })); | ||
assert(container.querySelector('#target').alive === 'lively'); | ||
assert(container.querySelector('#target').dead === 'deadly'); | ||
container.querySelector('#target').alive = 'changed'; | ||
container.querySelector('#target').dead = 'changed'; | ||
assert(container.querySelector('#target').alive === 'changed'); | ||
assert(container.querySelector('#target').dead === 'changed'); | ||
render(container, getTemplate({ alive: 'lively', dead: 'deadly' })); | ||
assert(container.querySelector('#target').alive === 'lively'); | ||
assert(container.querySelector('#target').dead === 'changed'); | ||
container.remove(); | ||
}); | ||
it('live', () => { | ||
const getTemplate = ({ alive, dead }) => { | ||
return html`<div id="target" .alive="${live(alive)}" .dead="${dead}"></div>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
render(container, getTemplate({ alive: 'lively', dead: 'deadly' })); | ||
assert(container.querySelector('#target').alive === 'lively'); | ||
assert(container.querySelector('#target').dead === 'deadly'); | ||
container.querySelector('#target').alive = 'changed'; | ||
container.querySelector('#target').dead = 'changed'; | ||
assert(container.querySelector('#target').alive === 'changed'); | ||
assert(container.querySelector('#target').dead === 'changed'); | ||
render(container, getTemplate({ alive: 'lively', dead: 'deadly' })); | ||
assert(container.querySelector('#target').alive === 'lively'); | ||
assert(container.querySelector('#target').dead === 'changed'); | ||
container.remove(); | ||
}); | ||
it('html: updaters: unsafeHTML', () => { | ||
const getTemplate = ({ content }) => { | ||
return html`<div id="target">${unsafeHTML(content)}</div>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
render(container, getTemplate({ content: '<div id="injected">oh hai</div>' })); | ||
assert(!!container.querySelector('#injected')); | ||
render(container, getTemplate({ content: '<div id="booster">oh hai, again</div>' })); | ||
assert(!!container.querySelector('#booster')); | ||
container.remove(); | ||
}); | ||
it('unsafeHTML', () => { | ||
const getTemplate = ({ content }) => { | ||
return html`<div id="target">${unsafeHTML(content)}</div>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
render(container, getTemplate({ content: '<div id="injected">oh hai</div>' })); | ||
assert(!!container.querySelector('#injected')); | ||
render(container, getTemplate({ content: '<div id="booster">oh hai, again</div>' })); | ||
assert(!!container.querySelector('#booster')); | ||
container.remove(); | ||
}); | ||
// This is mainly for backwards compat, TBD if we deprecate or not. | ||
it('html: updaters: repeat works when called with all arguments', () => { | ||
const getTemplate = ({ items }) => { | ||
return html` | ||
<div id="target"> | ||
${repeat(items, item => item.id, item => { | ||
return html`<div id="${item.id}" class="item">${item.id}</div>`; | ||
})} | ||
</div> | ||
`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
render(container, getTemplate({ items: [{ id: 'foo' }, { id: 'bar'}, { id: 'baz' }] })); | ||
const foo = container.querySelector('#foo'); | ||
const bar = container.querySelector('#bar'); | ||
const baz = container.querySelector('#baz'); | ||
assert(container.querySelector('#target').childElementCount === 3); | ||
assert(!!foo); | ||
assert(!!bar); | ||
assert(!!baz); | ||
assert(container.querySelector('#target').children[0] === foo); | ||
assert(container.querySelector('#target').children[1] === bar); | ||
assert(container.querySelector('#target').children[2] === baz); | ||
render(container, getTemplate({ items: [{ id: 'foo' }, { id: 'bar'}, { id: 'baz' }] })); | ||
assert(container.querySelector('#target').childElementCount === 3); | ||
assert(container.querySelector('#target').children[0] === foo); | ||
assert(container.querySelector('#target').children[1] === bar); | ||
assert(container.querySelector('#target').children[2] === baz); | ||
render(container, getTemplate({ items: [{ id: 'baz' }, { id: 'foo' }, { id: 'bar'}] })); | ||
assert(container.querySelector('#target').childElementCount === 3); | ||
assert(container.querySelector('#target').children[0] === baz); | ||
assert(container.querySelector('#target').children[1] === foo); | ||
assert(container.querySelector('#target').children[2] === bar); | ||
render(container, getTemplate({ items: [{ id: 'bar'}, { id: 'baz' }, { id: 'foo' }] })); | ||
assert(container.querySelector('#target').childElementCount === 3); | ||
assert(container.querySelector('#target').children[0] === bar); | ||
assert(container.querySelector('#target').children[1] === baz); | ||
assert(container.querySelector('#target').children[2] === foo); | ||
render(container, getTemplate({ items: [{ id: 'foo' }, { id: 'bar'}, { id: 'baz' }] })); | ||
assert(container.querySelector('#target').childElementCount === 3); | ||
assert(container.querySelector('#target').children[0] === foo); | ||
assert(container.querySelector('#target').children[1] === bar); | ||
assert(container.querySelector('#target').children[2] === baz); | ||
render(container, getTemplate({ items: [{ id: 'foo' }, { id: 'bar'}] })); | ||
assert(container.querySelector('#target').childElementCount === 2); | ||
assert(container.querySelector('#target').children[0] === foo); | ||
assert(container.querySelector('#target').children[1] === bar); | ||
render(container, getTemplate({ items: [{ id: 'foo' }] })); | ||
assert(container.querySelector('#target').childElementCount === 1); | ||
assert(container.querySelector('#target').children[0] === foo); | ||
render(container, getTemplate({ items: [] })); | ||
assert(container.querySelector('#target').childElementCount === 0); | ||
render(container, getTemplate({ items: [{ id: 'foo' }, { id: 'bar'}, { id: 'baz' }] })); | ||
assert(container.querySelector('#target').childElementCount === 3); | ||
assert(container.querySelector('#target').children[0] !== foo); | ||
assert(container.querySelector('#target').children[1] !== bar); | ||
assert(container.querySelector('#target').children[2] !== baz); | ||
container.remove(); | ||
}); | ||
// This is mainly for backwards compat, TBD if we deprecate or not. | ||
it('repeat works when called with all arguments', () => { | ||
const getTemplate = ({ items }) => { | ||
return html` | ||
<div id="target"> | ||
${repeat(items, item => item.id, item => { | ||
return html`<div id="${item.id}" class="item">${item.id}</div>`; | ||
})} | ||
</div> | ||
`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
render(container, getTemplate({ items: [{ id: 'foo' }, { id: 'bar'}, { id: 'baz' }] })); | ||
const foo = container.querySelector('#foo'); | ||
const bar = container.querySelector('#bar'); | ||
const baz = container.querySelector('#baz'); | ||
assert(container.querySelector('#target').childElementCount === 3); | ||
assert(!!foo); | ||
assert(!!bar); | ||
assert(!!baz); | ||
assert(container.querySelector('#target').children[0] === foo); | ||
assert(container.querySelector('#target').children[1] === bar); | ||
assert(container.querySelector('#target').children[2] === baz); | ||
render(container, getTemplate({ items: [{ id: 'foo' }, { id: 'bar'}, { id: 'baz' }] })); | ||
assert(container.querySelector('#target').childElementCount === 3); | ||
assert(container.querySelector('#target').children[0] === foo); | ||
assert(container.querySelector('#target').children[1] === bar); | ||
assert(container.querySelector('#target').children[2] === baz); | ||
render(container, getTemplate({ items: [{ id: 'baz' }, { id: 'foo' }, { id: 'bar'}] })); | ||
assert(container.querySelector('#target').childElementCount === 3); | ||
assert(container.querySelector('#target').children[0] === baz); | ||
assert(container.querySelector('#target').children[1] === foo); | ||
assert(container.querySelector('#target').children[2] === bar); | ||
render(container, getTemplate({ items: [{ id: 'bar'}, { id: 'baz' }, { id: 'foo' }] })); | ||
assert(container.querySelector('#target').childElementCount === 3); | ||
assert(container.querySelector('#target').children[0] === bar); | ||
assert(container.querySelector('#target').children[1] === baz); | ||
assert(container.querySelector('#target').children[2] === foo); | ||
render(container, getTemplate({ items: [{ id: 'foo' }, { id: 'bar'}, { id: 'baz' }] })); | ||
assert(container.querySelector('#target').childElementCount === 3); | ||
assert(container.querySelector('#target').children[0] === foo); | ||
assert(container.querySelector('#target').children[1] === bar); | ||
assert(container.querySelector('#target').children[2] === baz); | ||
render(container, getTemplate({ items: [{ id: 'foo' }, { id: 'bar'}] })); | ||
assert(container.querySelector('#target').childElementCount === 2); | ||
assert(container.querySelector('#target').children[0] === foo); | ||
assert(container.querySelector('#target').children[1] === bar); | ||
render(container, getTemplate({ items: [{ id: 'foo' }] })); | ||
assert(container.querySelector('#target').childElementCount === 1); | ||
assert(container.querySelector('#target').children[0] === foo); | ||
render(container, getTemplate({ items: [] })); | ||
assert(container.querySelector('#target').childElementCount === 0); | ||
render(container, getTemplate({ items: [{ id: 'foo' }, { id: 'bar'}, { id: 'baz' }] })); | ||
assert(container.querySelector('#target').childElementCount === 3); | ||
assert(container.querySelector('#target').children[0] !== foo); | ||
assert(container.querySelector('#target').children[1] !== bar); | ||
assert(container.querySelector('#target').children[2] !== baz); | ||
container.remove(); | ||
}); | ||
it('html: updaters: repeat works when called with omitted lookup', () => { | ||
const getTemplate = ({ items }) => { | ||
return html` | ||
<div id="target"> | ||
${repeat(items, item => { | ||
return html`<div id="${item.id}" class="item">${item.id}</div>`; | ||
})} | ||
</div> | ||
`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
render(container, getTemplate({ items: [{ id: 'foo' }, { id: 'bar'}, { id: 'baz' }] })); | ||
const foo = container.querySelector('#foo'); | ||
const bar = container.querySelector('#bar'); | ||
const baz = container.querySelector('#baz'); | ||
assert(container.querySelector('#target').childElementCount === 3); | ||
assert(!!foo); | ||
assert(!!bar); | ||
assert(!!baz); | ||
assert(container.querySelector('#target').children[0] === foo); | ||
assert(container.querySelector('#target').children[1] === bar); | ||
assert(container.querySelector('#target').children[2] === baz); | ||
render(container, getTemplate({ items: [{ id: 'foo' }, { id: 'bar'}, { id: 'baz' }] })); | ||
assert(container.querySelector('#target').childElementCount === 3); | ||
assert(container.querySelector('#target').children[0] === foo); | ||
assert(container.querySelector('#target').children[1] === bar); | ||
assert(container.querySelector('#target').children[2] === baz); | ||
it('repeat works when called with omitted lookup', () => { | ||
const getTemplate = ({ items }) => { | ||
return html` | ||
<div id="target"> | ||
${repeat(items, item => { | ||
return html`<div id="${item.id}" class="item">${item.id}</div>`; | ||
})} | ||
</div> | ||
`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
render(container, getTemplate({ items: [{ id: 'foo' }, { id: 'bar'}, { id: 'baz' }] })); | ||
const foo = container.querySelector('#foo'); | ||
const bar = container.querySelector('#bar'); | ||
const baz = container.querySelector('#baz'); | ||
assert(container.querySelector('#target').childElementCount === 3); | ||
assert(!!foo); | ||
assert(!!bar); | ||
assert(!!baz); | ||
assert(container.querySelector('#target').children[0] === foo); | ||
assert(container.querySelector('#target').children[1] === bar); | ||
assert(container.querySelector('#target').children[2] === baz); | ||
render(container, getTemplate({ items: [{ id: 'foo' }, { id: 'bar'}, { id: 'baz' }] })); | ||
assert(container.querySelector('#target').childElementCount === 3); | ||
assert(container.querySelector('#target').children[0] === foo); | ||
assert(container.querySelector('#target').children[1] === bar); | ||
assert(container.querySelector('#target').children[2] === baz); | ||
// Because "lookup" is omitted, we don't expect DOM nodes to remain after a shift. | ||
render(container, getTemplate({ items: [{ id: 'baz' }, { id: 'foo' }, { id: 'bar'}] })); | ||
assert(container.querySelector('#target').childElementCount === 3); | ||
assert(container.querySelector('#target').children[0] !== baz); | ||
assert(container.querySelector('#target').children[1] !== foo); | ||
assert(container.querySelector('#target').children[2] !== bar); | ||
container.remove(); | ||
}); | ||
// Because "lookup" is omitted, we don't expect DOM nodes to remain after a shift. | ||
render(container, getTemplate({ items: [{ id: 'baz' }, { id: 'foo' }, { id: 'bar'}] })); | ||
assert(container.querySelector('#target').childElementCount === 3); | ||
assert(container.querySelector('#target').children[0] !== baz); | ||
assert(container.querySelector('#target').children[1] !== foo); | ||
assert(container.querySelector('#target').children[2] !== bar); | ||
container.remove(); | ||
}); | ||
it('html: updaters: map', () => { | ||
const getTemplate = ({ items }) => { | ||
return html` | ||
<div id="target"> | ||
${map(items, item => item.id, item => { | ||
return html`<div id="${item.id}" class="item">${item.id}</div>`; | ||
})} | ||
</div> | ||
`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
render(container, getTemplate({ items: [{ id: 'foo' }, { id: 'bar'}, { id: 'baz' }] })); | ||
const foo = container.querySelector('#foo'); | ||
const bar = container.querySelector('#bar'); | ||
const baz = container.querySelector('#baz'); | ||
assert(container.querySelector('#target').childElementCount === 3); | ||
assert(!!foo); | ||
assert(!!bar); | ||
assert(!!baz); | ||
assert(container.querySelector('#target').children[0] === foo); | ||
assert(container.querySelector('#target').children[1] === bar); | ||
assert(container.querySelector('#target').children[2] === baz); | ||
render(container, getTemplate({ items: [{ id: 'foo' }, { id: 'bar'}, { id: 'baz' }] })); | ||
assert(container.querySelector('#target').childElementCount === 3); | ||
assert(container.querySelector('#target').children[0] === foo); | ||
assert(container.querySelector('#target').children[1] === bar); | ||
assert(container.querySelector('#target').children[2] === baz); | ||
render(container, getTemplate({ items: [{ id: 'baz' }, { id: 'foo' }, { id: 'bar'}] })); | ||
assert(container.querySelector('#target').childElementCount === 3); | ||
assert(container.querySelector('#target').children[0] === baz); | ||
assert(container.querySelector('#target').children[1] === foo); | ||
assert(container.querySelector('#target').children[2] === bar); | ||
render(container, getTemplate({ items: [{ id: 'bar'}, { id: 'baz' }, { id: 'foo' }] })); | ||
assert(container.querySelector('#target').childElementCount === 3); | ||
assert(container.querySelector('#target').children[0] === bar); | ||
assert(container.querySelector('#target').children[1] === baz); | ||
assert(container.querySelector('#target').children[2] === foo); | ||
render(container, getTemplate({ items: [{ id: 'foo' }, { id: 'bar'}, { id: 'baz' }] })); | ||
assert(container.querySelector('#target').childElementCount === 3); | ||
assert(container.querySelector('#target').children[0] === foo); | ||
assert(container.querySelector('#target').children[1] === bar); | ||
assert(container.querySelector('#target').children[2] === baz); | ||
render(container, getTemplate({ items: [{ id: 'foo' }, { id: 'bar'}] })); | ||
assert(container.querySelector('#target').childElementCount === 2); | ||
assert(container.querySelector('#target').children[0] === foo); | ||
assert(container.querySelector('#target').children[1] === bar); | ||
render(container, getTemplate({ items: [{ id: 'foo' }] })); | ||
assert(container.querySelector('#target').childElementCount === 1); | ||
assert(container.querySelector('#target').children[0] === foo); | ||
render(container, getTemplate({ items: [] })); | ||
assert(container.querySelector('#target').childElementCount === 0); | ||
render(container, getTemplate({ items: [{ id: 'foo' }, { id: 'bar'}, { id: 'baz' }] })); | ||
assert(container.querySelector('#target').childElementCount === 3); | ||
assert(container.querySelector('#target').children[0] !== foo); | ||
assert(container.querySelector('#target').children[1] !== bar); | ||
assert(container.querySelector('#target').children[2] !== baz); | ||
container.remove(); | ||
}); | ||
it('map', () => { | ||
const getTemplate = ({ items }) => { | ||
return html` | ||
<div id="target"> | ||
${map(items, item => item.id, item => { | ||
return html`<div id="${item.id}" class="item">${item.id}</div>`; | ||
})} | ||
</div> | ||
`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
render(container, getTemplate({ items: [{ id: 'foo' }, { id: 'bar'}, { id: 'baz' }] })); | ||
const foo = container.querySelector('#foo'); | ||
const bar = container.querySelector('#bar'); | ||
const baz = container.querySelector('#baz'); | ||
assert(container.querySelector('#target').childElementCount === 3); | ||
assert(!!foo); | ||
assert(!!bar); | ||
assert(!!baz); | ||
assert(container.querySelector('#target').children[0] === foo); | ||
assert(container.querySelector('#target').children[1] === bar); | ||
assert(container.querySelector('#target').children[2] === baz); | ||
render(container, getTemplate({ items: [{ id: 'foo' }, { id: 'bar'}, { id: 'baz' }] })); | ||
assert(container.querySelector('#target').childElementCount === 3); | ||
assert(container.querySelector('#target').children[0] === foo); | ||
assert(container.querySelector('#target').children[1] === bar); | ||
assert(container.querySelector('#target').children[2] === baz); | ||
render(container, getTemplate({ items: [{ id: 'baz' }, { id: 'foo' }, { id: 'bar'}] })); | ||
assert(container.querySelector('#target').childElementCount === 3); | ||
assert(container.querySelector('#target').children[0] === baz); | ||
assert(container.querySelector('#target').children[1] === foo); | ||
assert(container.querySelector('#target').children[2] === bar); | ||
render(container, getTemplate({ items: [{ id: 'bar'}, { id: 'baz' }, { id: 'foo' }] })); | ||
assert(container.querySelector('#target').childElementCount === 3); | ||
assert(container.querySelector('#target').children[0] === bar); | ||
assert(container.querySelector('#target').children[1] === baz); | ||
assert(container.querySelector('#target').children[2] === foo); | ||
render(container, getTemplate({ items: [{ id: 'foo' }, { id: 'bar'}, { id: 'baz' }] })); | ||
assert(container.querySelector('#target').childElementCount === 3); | ||
assert(container.querySelector('#target').children[0] === foo); | ||
assert(container.querySelector('#target').children[1] === bar); | ||
assert(container.querySelector('#target').children[2] === baz); | ||
render(container, getTemplate({ items: [{ id: 'foo' }, { id: 'bar'}] })); | ||
assert(container.querySelector('#target').childElementCount === 2); | ||
assert(container.querySelector('#target').children[0] === foo); | ||
assert(container.querySelector('#target').children[1] === bar); | ||
render(container, getTemplate({ items: [{ id: 'foo' }] })); | ||
assert(container.querySelector('#target').childElementCount === 1); | ||
assert(container.querySelector('#target').children[0] === foo); | ||
render(container, getTemplate({ items: [] })); | ||
assert(container.querySelector('#target').childElementCount === 0); | ||
render(container, getTemplate({ items: [{ id: 'foo' }, { id: 'bar'}, { id: 'baz' }] })); | ||
assert(container.querySelector('#target').childElementCount === 3); | ||
assert(container.querySelector('#target').children[0] !== foo); | ||
assert(container.querySelector('#target').children[1] !== bar); | ||
assert(container.querySelector('#target').children[2] !== baz); | ||
container.remove(); | ||
}); | ||
it('html: updaters: map: template changes', () => { | ||
const getTemplate = ({ items }) => { | ||
return html` | ||
<div id="target"> | ||
${map(items, item => item.id, item => { | ||
return item.show ? html`<div id="${item.id}" class="item">${item.id}</div>` : html`<div id="${item.id}" class="item"></div>`; | ||
})} | ||
</div> | ||
`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
render(container, getTemplate({ items: [{ id: 'foo', show: true }] })); | ||
const foo = container.querySelector('#foo'); | ||
assert(container.querySelector('#target').childElementCount === 1); | ||
assert(!!foo); | ||
assert(container.querySelector('#target').children[0] === foo); | ||
render(container, getTemplate({ items: [{ id: 'foo', show: false }] })); | ||
assert(container.querySelector('#target').childElementCount === 1); | ||
assert(!!container.querySelector('#foo')); | ||
assert(container.querySelector('#target').children[0] !== foo); | ||
container.remove(); | ||
it('map: template changes', () => { | ||
const getTemplate = ({ items }) => { | ||
return html` | ||
<div id="target"> | ||
${map(items, item => item.id, item => { | ||
return item.show ? html`<div id="${item.id}" class="item">${item.id}</div>` : html`<div id="${item.id}" class="item"></div>`; | ||
})} | ||
</div> | ||
`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
render(container, getTemplate({ items: [{ id: 'foo', show: true }] })); | ||
const foo = container.querySelector('#foo'); | ||
assert(container.querySelector('#target').childElementCount === 1); | ||
assert(!!foo); | ||
assert(container.querySelector('#target').children[0] === foo); | ||
render(container, getTemplate({ items: [{ id: 'foo', show: false }] })); | ||
assert(container.querySelector('#target').childElementCount === 1); | ||
assert(!!container.querySelector('#foo')); | ||
assert(container.querySelector('#target').children[0] !== foo); | ||
container.remove(); | ||
}); | ||
}); | ||
it('svg: renders a basic string', () => { | ||
const getTemplate = ({ r, cx, cy }) => { | ||
return svg`<circle id="target" r="${r}" cx="${cx}" cy="${cy}"/>`; | ||
}; | ||
const container = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); | ||
container.setAttribute('viewBox', '0 0 100 100'); | ||
container.setAttribute('style', 'height: 100px; width: 100px;'); | ||
document.body.append(container); | ||
render(container, getTemplate({ r: 10, cx: 50, cy: 50 })); | ||
assert(container.querySelector('#target').getBoundingClientRect().width === 20); | ||
assert(container.querySelector('#target').getBoundingClientRect().height === 20); | ||
render(container, getTemplate({ r: 5, cx: 50, cy: 50 })); | ||
assert(container.querySelector('#target').getBoundingClientRect().width === 10); | ||
assert(container.querySelector('#target').getBoundingClientRect().height === 10); | ||
container.remove(); | ||
}); | ||
describe('svg rendering', () => { | ||
it('renders a basic string', () => { | ||
const getTemplate = ({ r, cx, cy }) => { | ||
return svg`<circle id="target" r="${r}" cx="${cx}" cy="${cy}"/>`; | ||
}; | ||
const container = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); | ||
container.setAttribute('viewBox', '0 0 100 100'); | ||
container.setAttribute('style', 'height: 100px; width: 100px;'); | ||
document.body.append(container); | ||
render(container, getTemplate({ r: 10, cx: 50, cy: 50 })); | ||
assert(container.querySelector('#target').getBoundingClientRect().width === 20); | ||
assert(container.querySelector('#target').getBoundingClientRect().height === 20); | ||
render(container, getTemplate({ r: 5, cx: 50, cy: 50 })); | ||
assert(container.querySelector('#target').getBoundingClientRect().width === 10); | ||
assert(container.querySelector('#target').getBoundingClientRect().height === 10); | ||
container.remove(); | ||
}); | ||
it('svg: renders lists', () => { | ||
const getTemplate = ({ items }) => { | ||
return html` | ||
<svg id="target" | ||
xmlns="http://www.w3.org/2000/svg" | ||
viewBox="0 0 10 100" | ||
style="width: 10px; height: 100px;"> | ||
${items.map((item, index) => { | ||
return svg`<circle class="dot" r="${item.r}" cx="5" cy="${10 * (index + 1)}"></circle>`; | ||
})} | ||
</svg> | ||
`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
render(container, getTemplate({ items: [{ r: 1 }, { r: 2 }, { r: 3 }, { r: 4 }, { r: 5 }] })); | ||
assert(container.querySelector('#target').childElementCount === 5); | ||
assert(container.querySelector('#target').children[0].getBoundingClientRect().height = 2); | ||
assert(container.querySelector('#target').children[1].getBoundingClientRect().height = 4); | ||
assert(container.querySelector('#target').children[2].getBoundingClientRect().height = 6); | ||
assert(container.querySelector('#target').children[3].getBoundingClientRect().height = 8); | ||
assert(container.querySelector('#target').children[4].getBoundingClientRect().height = 10); | ||
container.remove(); | ||
it('renders lists', () => { | ||
const getTemplate = ({ items }) => { | ||
return html` | ||
<svg id="target" | ||
xmlns="http://www.w3.org/2000/svg" | ||
viewBox="0 0 10 100" | ||
style="width: 10px; height: 100px;"> | ||
${items.map((item, index) => { | ||
return svg`<circle class="dot" r="${item.r}" cx="5" cy="${10 * (index + 1)}"></circle>`; | ||
})} | ||
</svg> | ||
`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
render(container, getTemplate({ items: [{ r: 1 }, { r: 2 }, { r: 3 }, { r: 4 }, { r: 5 }] })); | ||
assert(container.querySelector('#target').childElementCount === 5); | ||
assert(container.querySelector('#target').children[0].getBoundingClientRect().height = 2); | ||
assert(container.querySelector('#target').children[1].getBoundingClientRect().height = 4); | ||
assert(container.querySelector('#target').children[2].getBoundingClientRect().height = 6); | ||
assert(container.querySelector('#target').children[3].getBoundingClientRect().height = 8); | ||
assert(container.querySelector('#target').children[4].getBoundingClientRect().height = 10); | ||
container.remove(); | ||
}); | ||
}); | ||
it('svg: updaters: unsafeSVG', () => { | ||
const getTemplate = ({ content }) => { | ||
return html` | ||
<svg | ||
id="target" | ||
xmlns="http://www.w3.org/2000/svg" | ||
viewBox="0 0 100 100" | ||
style="width: 100px; height: 100px;"> | ||
${unsafeSVG(content)} | ||
</svg> | ||
`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
render(container, getTemplate({ content: '<circle id="injected" r="10" cx="50" cy="50"></circle>' })); | ||
assert(!!container.querySelector('#injected')); | ||
assert(container.querySelector('#injected').getBoundingClientRect().height = 20); | ||
assert(container.querySelector('#injected').getBoundingClientRect().width = 20); | ||
render(container, getTemplate({ content: '<circle id="injected" r="5" cx="50" cy="50"></circle>' })); | ||
assert(!!container.querySelector('#injected')); | ||
assert(container.querySelector('#injected').getBoundingClientRect().height = 10); | ||
assert(container.querySelector('#injected').getBoundingClientRect().width = 10); | ||
container.remove(); | ||
describe('svg updaters', () => { | ||
it('unsafeSVG', () => { | ||
const getTemplate = ({ content }) => { | ||
return html` | ||
<svg | ||
id="target" | ||
xmlns="http://www.w3.org/2000/svg" | ||
viewBox="0 0 100 100" | ||
style="width: 100px; height: 100px;"> | ||
${unsafeSVG(content)} | ||
</svg> | ||
`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
render(container, getTemplate({ content: '<circle id="injected" r="10" cx="50" cy="50"></circle>' })); | ||
assert(!!container.querySelector('#injected')); | ||
assert(container.querySelector('#injected').getBoundingClientRect().height = 20); | ||
assert(container.querySelector('#injected').getBoundingClientRect().width = 20); | ||
render(container, getTemplate({ content: '<circle id="injected" r="5" cx="50" cy="50"></circle>' })); | ||
assert(!!container.querySelector('#injected')); | ||
assert(container.querySelector('#injected').getBoundingClientRect().height = 10); | ||
assert(container.querySelector('#injected').getBoundingClientRect().width = 10); | ||
container.remove(); | ||
}); | ||
}); | ||
it('render errors: ifDefined: throws if used on a "boolean-attribute"', () => { | ||
const expected = 'The ifDefined update must be used on an attribute, not on a boolean-attribute.'; | ||
const getTemplate = ({ maybe }) => { | ||
return html`<div id="target" ?maybe="${ifDefined(maybe)}"></div>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
let actual; | ||
try { | ||
render(container, getTemplate({ maybe: 'yes' })); | ||
} catch (error) { | ||
actual = error.message; | ||
} | ||
assert(!!actual, 'No error was thrown.'); | ||
assert(actual === expected, actual); | ||
container.remove(); | ||
}); | ||
describe('rendering errors', () => { | ||
describe('ifDefined', () => { | ||
it('throws if used on a "boolean-attribute"', () => { | ||
const expected = 'The ifDefined update must be used on an attribute, not on a boolean-attribute.'; | ||
const getTemplate = ({ maybe }) => { | ||
return html`<div id="target" ?maybe="${ifDefined(maybe)}"></div>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
let actual; | ||
try { | ||
render(container, getTemplate({ maybe: 'yes' })); | ||
} catch (error) { | ||
actual = error.message; | ||
} | ||
assert(!!actual, 'No error was thrown.'); | ||
assert(actual === expected, actual); | ||
container.remove(); | ||
}); | ||
it('render errors: ifDefined: throws if used on a "property"', () => { | ||
const expected = 'The ifDefined update must be used on an attribute, not on a property.'; | ||
const getTemplate = ({ maybe }) => { | ||
return html`<div id="target" .maybe="${ifDefined(maybe)}"></div>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
let actual; | ||
try { | ||
render(container, getTemplate({ maybe: 'yes' })); | ||
} catch (error) { | ||
actual = error.message; | ||
} | ||
assert(!!actual, 'No error was thrown.'); | ||
assert(actual === expected, actual); | ||
container.remove(); | ||
}); | ||
it('throws if used on a "property"', () => { | ||
const expected = 'The ifDefined update must be used on an attribute, not on a property.'; | ||
const getTemplate = ({ maybe }) => { | ||
return html`<div id="target" .maybe="${ifDefined(maybe)}"></div>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
let actual; | ||
try { | ||
render(container, getTemplate({ maybe: 'yes' })); | ||
} catch (error) { | ||
actual = error.message; | ||
} | ||
assert(!!actual, 'No error was thrown.'); | ||
assert(actual === expected, actual); | ||
container.remove(); | ||
}); | ||
it('render errors: ifDefined: throws if used with "content"', () => { | ||
const expected = 'The ifDefined update must be used on an attribute, not on content.'; | ||
const getTemplate = ({ maybe }) => { | ||
return html`<div id="target">${ifDefined(maybe)}</div>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
let actual; | ||
try { | ||
render(container, getTemplate({ maybe: 'yes' })); | ||
} catch (error) { | ||
actual = error.message; | ||
} | ||
assert(!!actual, 'No error was thrown.'); | ||
assert(actual === expected, actual); | ||
container.remove(); | ||
}); | ||
it('throws if used with "content"', () => { | ||
const expected = 'The ifDefined update must be used on an attribute, not on content.'; | ||
const getTemplate = ({ maybe }) => { | ||
return html`<div id="target">${ifDefined(maybe)}</div>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
let actual; | ||
try { | ||
render(container, getTemplate({ maybe: 'yes' })); | ||
} catch (error) { | ||
actual = error.message; | ||
} | ||
assert(!!actual, 'No error was thrown.'); | ||
assert(actual === expected, actual); | ||
container.remove(); | ||
}); | ||
it('render errors: ifDefined: throws if used with "text-content"', () => { | ||
const expected = 'The ifDefined update must be used on an attribute, not on text-content.'; | ||
const getTemplate = ({ maybe }) => { | ||
return html`<textarea id="target">${ifDefined(maybe)}</textarea>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
let actual; | ||
try { | ||
render(container, getTemplate({ maybe: 'yes' })); | ||
} catch (error) { | ||
actual = error.message; | ||
} | ||
assert(!!actual, 'No error was thrown.'); | ||
assert(actual === expected, actual); | ||
container.remove(); | ||
}); | ||
it('throws if used with "text-content"', () => { | ||
const expected = 'The ifDefined update must be used on an attribute, not on text-content.'; | ||
const getTemplate = ({ maybe }) => { | ||
return html`<textarea id="target">${ifDefined(maybe)}</textarea>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
let actual; | ||
try { | ||
render(container, getTemplate({ maybe: 'yes' })); | ||
} catch (error) { | ||
actual = error.message; | ||
} | ||
assert(!!actual, 'No error was thrown.'); | ||
assert(actual === expected, actual); | ||
container.remove(); | ||
}); | ||
}); | ||
it('render errors: nullish: throws if used on a "boolean-attribute"', () => { | ||
const expected = 'The nullish update must be used on an attribute, not on a boolean-attribute.'; | ||
const getTemplate = ({ maybe }) => { | ||
return html`<div id="target" ?maybe="${nullish(maybe)}"></div>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
let actual; | ||
try { | ||
render(container, getTemplate({ maybe: 'yes' })); | ||
} catch (error) { | ||
actual = error.message; | ||
} | ||
assert(!!actual, 'No error was thrown.'); | ||
assert(actual === expected, actual); | ||
container.remove(); | ||
}); | ||
describe('nullish', () => { | ||
it('throws if used on a "boolean-attribute"', () => { | ||
const expected = 'The nullish update must be used on an attribute, not on a boolean-attribute.'; | ||
const getTemplate = ({ maybe }) => { | ||
return html`<div id="target" ?maybe="${nullish(maybe)}"></div>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
let actual; | ||
try { | ||
render(container, getTemplate({ maybe: 'yes' })); | ||
} catch (error) { | ||
actual = error.message; | ||
} | ||
assert(!!actual, 'No error was thrown.'); | ||
assert(actual === expected, actual); | ||
container.remove(); | ||
}); | ||
it('render errors: nullish: throws if used on a "property"', () => { | ||
const expected = 'The nullish update must be used on an attribute, not on a property.'; | ||
const getTemplate = ({ maybe }) => { | ||
return html`<div id="target" .maybe="${nullish(maybe)}"></div>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
let actual; | ||
try { | ||
render(container, getTemplate({ maybe: 'yes' })); | ||
} catch (error) { | ||
actual = error.message; | ||
} | ||
assert(!!actual, 'No error was thrown.'); | ||
assert(actual === expected, actual); | ||
container.remove(); | ||
}); | ||
it('throws if used on a "property"', () => { | ||
const expected = 'The nullish update must be used on an attribute, not on a property.'; | ||
const getTemplate = ({ maybe }) => { | ||
return html`<div id="target" .maybe="${nullish(maybe)}"></div>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
let actual; | ||
try { | ||
render(container, getTemplate({ maybe: 'yes' })); | ||
} catch (error) { | ||
actual = error.message; | ||
} | ||
assert(!!actual, 'No error was thrown.'); | ||
assert(actual === expected, actual); | ||
container.remove(); | ||
}); | ||
it('render errors: nullish: throws if used with "content"', () => { | ||
const expected = 'The nullish update must be used on an attribute, not on content.'; | ||
const getTemplate = ({ maybe }) => { | ||
return html`<div id="target">${nullish(maybe)}</div>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
let actual; | ||
try { | ||
render(container, getTemplate({ maybe: 'yes' })); | ||
} catch (error) { | ||
actual = error.message; | ||
} | ||
assert(!!actual, 'No error was thrown.'); | ||
assert(actual === expected, actual); | ||
container.remove(); | ||
}); | ||
it('throws if used with "content"', () => { | ||
const expected = 'The nullish update must be used on an attribute, not on content.'; | ||
const getTemplate = ({ maybe }) => { | ||
return html`<div id="target">${nullish(maybe)}</div>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
let actual; | ||
try { | ||
render(container, getTemplate({ maybe: 'yes' })); | ||
} catch (error) { | ||
actual = error.message; | ||
} | ||
assert(!!actual, 'No error was thrown.'); | ||
assert(actual === expected, actual); | ||
container.remove(); | ||
}); | ||
it('render errors: nullish: throws if used with "text-content"', () => { | ||
const expected = 'The nullish update must be used on an attribute, not on text-content.'; | ||
const getTemplate = ({ maybe }) => { | ||
return html`<textarea id="target">${nullish(maybe)}</textarea>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
let actual; | ||
try { | ||
render(container, getTemplate({ maybe: 'yes' })); | ||
} catch (error) { | ||
actual = error.message; | ||
} | ||
assert(!!actual, 'No error was thrown.'); | ||
assert(actual === expected, actual); | ||
container.remove(); | ||
}); | ||
it('throws if used with "text-content"', () => { | ||
const expected = 'The nullish update must be used on an attribute, not on text-content.'; | ||
const getTemplate = ({ maybe }) => { | ||
return html`<textarea id="target">${nullish(maybe)}</textarea>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
let actual; | ||
try { | ||
render(container, getTemplate({ maybe: 'yes' })); | ||
} catch (error) { | ||
actual = error.message; | ||
} | ||
assert(!!actual, 'No error was thrown.'); | ||
assert(actual === expected, actual); | ||
container.remove(); | ||
}); | ||
}); | ||
it('render errors: live: throws if used on an "attribute"', () => { | ||
const expected = 'The live update must be used on a property, not on an attribute.'; | ||
const getTemplate = ({ maybe }) => { | ||
return html`<div id="target" maybe="${live(maybe)}"></div>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
let actual; | ||
try { | ||
render(container, getTemplate({ maybe: 'yes' })); | ||
} catch (error) { | ||
actual = error.message; | ||
} | ||
assert(!!actual, 'No error was thrown.'); | ||
assert(actual === expected, actual); | ||
container.remove(); | ||
}); | ||
describe('live', () => { | ||
it('throws if used on an "attribute"', () => { | ||
const expected = 'The live update must be used on a property, not on an attribute.'; | ||
const getTemplate = ({ maybe }) => { | ||
return html`<div id="target" maybe="${live(maybe)}"></div>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
let actual; | ||
try { | ||
render(container, getTemplate({ maybe: 'yes' })); | ||
} catch (error) { | ||
actual = error.message; | ||
} | ||
assert(!!actual, 'No error was thrown.'); | ||
assert(actual === expected, actual); | ||
container.remove(); | ||
}); | ||
it('render errors: live: throws if used on a "boolean-attribute"', () => { | ||
const expected = 'The live update must be used on a property, not on a boolean-attribute.'; | ||
const getTemplate = ({ maybe }) => { | ||
return html`<div id="target" ?maybe="${live(maybe)}"></div>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
let actual; | ||
try { | ||
render(container, getTemplate({ maybe: 'yes' })); | ||
} catch (error) { | ||
actual = error.message; | ||
} | ||
assert(!!actual, 'No error was thrown.'); | ||
assert(actual === expected, actual); | ||
container.remove(); | ||
}); | ||
it('throws if used on a "boolean-attribute"', () => { | ||
const expected = 'The live update must be used on a property, not on a boolean-attribute.'; | ||
const getTemplate = ({ maybe }) => { | ||
return html`<div id="target" ?maybe="${live(maybe)}"></div>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
let actual; | ||
try { | ||
render(container, getTemplate({ maybe: 'yes' })); | ||
} catch (error) { | ||
actual = error.message; | ||
} | ||
assert(!!actual, 'No error was thrown.'); | ||
assert(actual === expected, actual); | ||
container.remove(); | ||
}); | ||
it('render errors: live: throws if used with "content"', () => { | ||
const expected = 'The live update must be used on a property, not on content.'; | ||
const getTemplate = ({ maybe }) => { | ||
return html`<div id="target">${live(maybe)}</div>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
let actual; | ||
try { | ||
render(container, getTemplate({ maybe: 'yes' })); | ||
} catch (error) { | ||
actual = error.message; | ||
} | ||
assert(!!actual, 'No error was thrown.'); | ||
assert(actual === expected, actual); | ||
container.remove(); | ||
}); | ||
it('throws if used with "content"', () => { | ||
const expected = 'The live update must be used on a property, not on content.'; | ||
const getTemplate = ({ maybe }) => { | ||
return html`<div id="target">${live(maybe)}</div>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
let actual; | ||
try { | ||
render(container, getTemplate({ maybe: 'yes' })); | ||
} catch (error) { | ||
actual = error.message; | ||
} | ||
assert(!!actual, 'No error was thrown.'); | ||
assert(actual === expected, actual); | ||
container.remove(); | ||
}); | ||
it('render errors: live: throws if used with "text-content"', () => { | ||
const expected = 'The live update must be used on a property, not on text-content.'; | ||
const getTemplate = ({ maybe }) => { | ||
return html`<textarea id="target">${live(maybe)}</textarea>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
let actual; | ||
try { | ||
render(container, getTemplate({ maybe: 'yes' })); | ||
} catch (error) { | ||
actual = error.message; | ||
} | ||
assert(!!actual, 'No error was thrown.'); | ||
assert(actual === expected, actual); | ||
container.remove(); | ||
}); | ||
it('throws if used with "text-content"', () => { | ||
const expected = 'The live update must be used on a property, not on text-content.'; | ||
const getTemplate = ({ maybe }) => { | ||
return html`<textarea id="target">${live(maybe)}</textarea>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
let actual; | ||
try { | ||
render(container, getTemplate({ maybe: 'yes' })); | ||
} catch (error) { | ||
actual = error.message; | ||
} | ||
assert(!!actual, 'No error was thrown.'); | ||
assert(actual === expected, actual); | ||
container.remove(); | ||
}); | ||
}); | ||
it('render errors: unsafeHTML: throws if used on an "attribute"', () => { | ||
const expected = 'The unsafeHTML update must be used on content, not on an attribute.'; | ||
const getTemplate = ({ maybe }) => { | ||
return html`<div id="target" maybe="${unsafeHTML(maybe)}"></div>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
let actual; | ||
try { | ||
render(container, getTemplate({ maybe: 'yes' })); | ||
} catch (error) { | ||
actual = error.message; | ||
} | ||
assert(!!actual, 'No error was thrown.'); | ||
assert(actual === expected, actual); | ||
container.remove(); | ||
}); | ||
describe('unsafeHTML', () => { | ||
it('throws if used on an "attribute"', () => { | ||
const expected = 'The unsafeHTML update must be used on content, not on an attribute.'; | ||
const getTemplate = ({ maybe }) => { | ||
return html`<div id="target" maybe="${unsafeHTML(maybe)}"></div>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
let actual; | ||
try { | ||
render(container, getTemplate({ maybe: 'yes' })); | ||
} catch (error) { | ||
actual = error.message; | ||
} | ||
assert(!!actual, 'No error was thrown.'); | ||
assert(actual === expected, actual); | ||
container.remove(); | ||
}); | ||
it('render errors: unsafeHTML: throws if used on a "boolean-attribute"', () => { | ||
const expected = 'The unsafeHTML update must be used on content, not on a boolean-attribute.'; | ||
const getTemplate = ({ maybe }) => { | ||
return html`<div id="target" ?maybe="${unsafeHTML(maybe)}"></div>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
let actual; | ||
try { | ||
render(container, getTemplate({ maybe: 'yes' })); | ||
} catch (error) { | ||
actual = error.message; | ||
} | ||
assert(!!actual, 'No error was thrown.'); | ||
assert(actual === expected, actual); | ||
container.remove(); | ||
}); | ||
it('throws if used on a "boolean-attribute"', () => { | ||
const expected = 'The unsafeHTML update must be used on content, not on a boolean-attribute.'; | ||
const getTemplate = ({ maybe }) => { | ||
return html`<div id="target" ?maybe="${unsafeHTML(maybe)}"></div>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
let actual; | ||
try { | ||
render(container, getTemplate({ maybe: 'yes' })); | ||
} catch (error) { | ||
actual = error.message; | ||
} | ||
assert(!!actual, 'No error was thrown.'); | ||
assert(actual === expected, actual); | ||
container.remove(); | ||
}); | ||
it('render errors: unsafeHTML: throws if used with a "property"', () => { | ||
const expected = 'The unsafeHTML update must be used on content, not on a property.'; | ||
const getTemplate = ({ maybe }) => { | ||
return html`<div id="target" .maybe="${unsafeHTML(maybe)}"></div>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
let actual; | ||
try { | ||
render(container, getTemplate({ maybe: 'yes' })); | ||
} catch (error) { | ||
actual = error.message; | ||
} | ||
assert(!!actual, 'No error was thrown.'); | ||
assert(actual === expected, actual); | ||
container.remove(); | ||
}); | ||
it('throws if used with a "property"', () => { | ||
const expected = 'The unsafeHTML update must be used on content, not on a property.'; | ||
const getTemplate = ({ maybe }) => { | ||
return html`<div id="target" .maybe="${unsafeHTML(maybe)}"></div>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
let actual; | ||
try { | ||
render(container, getTemplate({ maybe: 'yes' })); | ||
} catch (error) { | ||
actual = error.message; | ||
} | ||
assert(!!actual, 'No error was thrown.'); | ||
assert(actual === expected, actual); | ||
container.remove(); | ||
}); | ||
it('render errors: unsafeHTML: throws if used with "text-content"', () => { | ||
const expected = 'The unsafeHTML update must be used on content, not on text-content.'; | ||
const getTemplate = ({ maybe }) => { | ||
return html`<textarea id="target">${unsafeHTML(maybe)}</textarea>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
let actual; | ||
try { | ||
render(container, getTemplate({ maybe: 'yes' })); | ||
} catch (error) { | ||
actual = error.message; | ||
} | ||
assert(!!actual, 'No error was thrown.'); | ||
assert(actual === expected, actual); | ||
container.remove(); | ||
}); | ||
it('throws if used with "text-content"', () => { | ||
const expected = 'The unsafeHTML update must be used on content, not on text-content.'; | ||
const getTemplate = ({ maybe }) => { | ||
return html`<textarea id="target">${unsafeHTML(maybe)}</textarea>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
let actual; | ||
try { | ||
render(container, getTemplate({ maybe: 'yes' })); | ||
} catch (error) { | ||
actual = error.message; | ||
} | ||
assert(!!actual, 'No error was thrown.'); | ||
assert(actual === expected, actual); | ||
container.remove(); | ||
}); | ||
it('render errors: unsafeSVG: throws if used on an "attribute"', () => { | ||
const expected = 'The unsafeSVG update must be used on content, not on an attribute.'; | ||
const getTemplate = ({ maybe }) => { | ||
return html`<div id="target" maybe="${unsafeSVG(maybe)}"></div>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
let actual; | ||
try { | ||
render(container, getTemplate({ maybe: 'yes' })); | ||
} catch (error) { | ||
actual = error.message; | ||
} | ||
assert(!!actual, 'No error was thrown.'); | ||
assert(actual === expected, actual); | ||
container.remove(); | ||
}); | ||
it('throws for non-string value', () => { | ||
const getTemplate = ({ content }) => { | ||
return html` | ||
<div id="target"> | ||
${unsafeHTML(content)} | ||
</div> | ||
`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
let error; | ||
try { | ||
render(container, getTemplate({ content: null })); | ||
} catch (e) { | ||
error = e; | ||
} | ||
assert(error?.message === 'Unexpected unsafeHTML value "null".', error?.message); | ||
container.remove(); | ||
}); | ||
}); | ||
it('render errors: unsafeSVG: throws if used on a "boolean-attribute"', () => { | ||
const expected = 'The unsafeSVG update must be used on content, not on a boolean-attribute.'; | ||
const getTemplate = ({ maybe }) => { | ||
return html`<div id="target" ?maybe="${unsafeSVG(maybe)}"></div>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
let actual; | ||
try { | ||
render(container, getTemplate({ maybe: 'yes' })); | ||
} catch (error) { | ||
actual = error.message; | ||
} | ||
assert(!!actual, 'No error was thrown.'); | ||
assert(actual === expected, actual); | ||
container.remove(); | ||
}); | ||
describe('unsafeSVG', () => { | ||
it('throws if used on an "attribute"', () => { | ||
const expected = 'The unsafeSVG update must be used on content, not on an attribute.'; | ||
const getTemplate = ({ maybe }) => { | ||
return html`<div id="target" maybe="${unsafeSVG(maybe)}"></div>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
let actual; | ||
try { | ||
render(container, getTemplate({ maybe: 'yes' })); | ||
} catch (error) { | ||
actual = error.message; | ||
} | ||
assert(!!actual, 'No error was thrown.'); | ||
assert(actual === expected, actual); | ||
container.remove(); | ||
}); | ||
it('render errors: unsafeSVG: throws if used with a "property"', () => { | ||
const expected = 'The unsafeSVG update must be used on content, not on a property.'; | ||
const getTemplate = ({ maybe }) => { | ||
return html`<div id="target" .maybe="${unsafeSVG(maybe)}"></div>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
let actual; | ||
try { | ||
render(container, getTemplate({ maybe: 'yes' })); | ||
} catch (error) { | ||
actual = error.message; | ||
} | ||
assert(!!actual, 'No error was thrown.'); | ||
assert(actual === expected, actual); | ||
container.remove(); | ||
}); | ||
it('throws if used on a "boolean-attribute"', () => { | ||
const expected = 'The unsafeSVG update must be used on content, not on a boolean-attribute.'; | ||
const getTemplate = ({ maybe }) => { | ||
return html`<div id="target" ?maybe="${unsafeSVG(maybe)}"></div>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
let actual; | ||
try { | ||
render(container, getTemplate({ maybe: 'yes' })); | ||
} catch (error) { | ||
actual = error.message; | ||
} | ||
assert(!!actual, 'No error was thrown.'); | ||
assert(actual === expected, actual); | ||
container.remove(); | ||
}); | ||
it('render errors: unsafeSVG: throws if used with "text-content"', () => { | ||
const expected = 'The unsafeSVG update must be used on content, not on text-content.'; | ||
const getTemplate = ({ maybe }) => { | ||
return html`<textarea id="target">${unsafeSVG(maybe)}</textarea>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
let actual; | ||
try { | ||
render(container, getTemplate({ maybe: 'yes' })); | ||
} catch (error) { | ||
actual = error.message; | ||
} | ||
assert(!!actual, 'No error was thrown.'); | ||
assert(actual === expected, actual); | ||
container.remove(); | ||
}); | ||
it('throws if used with a "property"', () => { | ||
const expected = 'The unsafeSVG update must be used on content, not on a property.'; | ||
const getTemplate = ({ maybe }) => { | ||
return html`<div id="target" .maybe="${unsafeSVG(maybe)}"></div>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
let actual; | ||
try { | ||
render(container, getTemplate({ maybe: 'yes' })); | ||
} catch (error) { | ||
actual = error.message; | ||
} | ||
assert(!!actual, 'No error was thrown.'); | ||
assert(actual === expected, actual); | ||
container.remove(); | ||
}); | ||
it('render errors: map: throws if identify is not a function', () => { | ||
const expected = 'Unexpected map identify "undefined" provided, expected a function.'; | ||
const getTemplate = ({ maybe }) => { | ||
return html`<div id="target" maybe="${map(maybe)}"></div>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
let actual; | ||
try { | ||
render(container, getTemplate({ maybe: 'yes' })); | ||
} catch (error) { | ||
actual = error.message; | ||
} | ||
assert(!!actual, 'No error was thrown.'); | ||
assert(actual === expected, actual); | ||
container.remove(); | ||
}); | ||
it('throws if used with "text-content"', () => { | ||
const expected = 'The unsafeSVG update must be used on content, not on text-content.'; | ||
const getTemplate = ({ maybe }) => { | ||
return html`<textarea id="target">${unsafeSVG(maybe)}</textarea>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
let actual; | ||
try { | ||
render(container, getTemplate({ maybe: 'yes' })); | ||
} catch (error) { | ||
actual = error.message; | ||
} | ||
assert(!!actual, 'No error was thrown.'); | ||
assert(actual === expected, actual); | ||
container.remove(); | ||
}); | ||
it('render errors: map: throws if callback is not a function', () => { | ||
const expected = 'Unexpected map callback "undefined" provided, expected a function.'; | ||
const getTemplate = ({ maybe }) => { | ||
return html`<div id="target" maybe="${map(maybe, () => {})}"></div>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
let actual; | ||
try { | ||
render(container, getTemplate({ maybe: 'yes' })); | ||
} catch (error) { | ||
actual = error.message; | ||
} | ||
assert(!!actual, 'No error was thrown.'); | ||
assert(actual === expected, actual); | ||
container.remove(); | ||
}); | ||
it('throws for non-string value', () => { | ||
const getTemplate = ({ content }) => { | ||
return html` | ||
<svg id="target" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"> | ||
${unsafeSVG(content)} | ||
</svg> | ||
`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
let error; | ||
try { | ||
render(container, getTemplate({ content: null })); | ||
} catch (e) { | ||
error = e; | ||
} | ||
assert(error?.message === 'Unexpected unsafeSVG value "null".', error?.message); | ||
container.remove(); | ||
}); | ||
}); | ||
it('render errors: map: throws if used on an "attribute"', () => { | ||
const expected = 'The map update must be used on content, not on an attribute.'; | ||
const getTemplate = ({ maybe }) => { | ||
return html`<div id="target" maybe="${map(maybe, () => {}, () => {})}"></div>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
let actual; | ||
try { | ||
render(container, getTemplate({ maybe: 'yes' })); | ||
} catch (error) { | ||
actual = error.message; | ||
} | ||
assert(!!actual, 'No error was thrown.'); | ||
assert(actual === expected, actual); | ||
container.remove(); | ||
}); | ||
describe('map', () => { | ||
it('throws if identify is not a function', () => { | ||
const expected = 'Unexpected map identify "undefined" provided, expected a function.'; | ||
const getTemplate = ({ maybe }) => { | ||
return html`<div id="target" maybe="${map(maybe)}"></div>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
let actual; | ||
try { | ||
render(container, getTemplate({ maybe: 'yes' })); | ||
} catch (error) { | ||
actual = error.message; | ||
} | ||
assert(!!actual, 'No error was thrown.'); | ||
assert(actual === expected, actual); | ||
container.remove(); | ||
}); | ||
it('render errors: map: throws if used on a "boolean-attribute"', () => { | ||
const expected = 'The map update must be used on content, not on a boolean-attribute.'; | ||
const getTemplate = ({ maybe }) => { | ||
return html`<div id="target" ?maybe="${map(maybe, () => {}, () => {})}"></div>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
let actual; | ||
try { | ||
render(container, getTemplate({ maybe: 'yes' })); | ||
} catch (error) { | ||
actual = error.message; | ||
} | ||
assert(!!actual, 'No error was thrown.'); | ||
assert(actual === expected, actual); | ||
container.remove(); | ||
}); | ||
it('throws if callback is not a function', () => { | ||
const expected = 'Unexpected map callback "undefined" provided, expected a function.'; | ||
const getTemplate = ({ maybe }) => { | ||
return html`<div id="target" maybe="${map(maybe, () => {})}"></div>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
let actual; | ||
try { | ||
render(container, getTemplate({ maybe: 'yes' })); | ||
} catch (error) { | ||
actual = error.message; | ||
} | ||
assert(!!actual, 'No error was thrown.'); | ||
assert(actual === expected, actual); | ||
container.remove(); | ||
}); | ||
it('render errors: map: throws if used with a "property"', () => { | ||
const expected = 'The map update must be used on content, not on a property.'; | ||
const getTemplate = ({ maybe }) => { | ||
return html`<div id="target" .maybe="${map(maybe, () => {}, () => {})}"></div>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
let actual; | ||
try { | ||
render(container, getTemplate({ maybe: 'yes' })); | ||
} catch (error) { | ||
actual = error.message; | ||
} | ||
assert(!!actual, 'No error was thrown.'); | ||
assert(actual === expected, actual); | ||
container.remove(); | ||
}); | ||
it('throws if used on an "attribute"', () => { | ||
const expected = 'The map update must be used on content, not on an attribute.'; | ||
const getTemplate = ({ maybe }) => { | ||
return html`<div id="target" maybe="${map(maybe, () => {}, () => {})}"></div>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
let actual; | ||
try { | ||
render(container, getTemplate({ maybe: 'yes' })); | ||
} catch (error) { | ||
actual = error.message; | ||
} | ||
assert(!!actual, 'No error was thrown.'); | ||
assert(actual === expected, actual); | ||
container.remove(); | ||
}); | ||
it('render errors: map: throws if used with "text-content"', () => { | ||
const expected = 'The map update must be used on content, not on text-content.'; | ||
const getTemplate = ({ maybe }) => { | ||
return html`<textarea id="target">${map(maybe, () => {}, () => {})}</textarea>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
let actual; | ||
try { | ||
render(container, getTemplate({ maybe: 'yes' })); | ||
} catch (error) { | ||
actual = error.message; | ||
} | ||
assert(!!actual, 'No error was thrown.'); | ||
assert(actual === expected, actual); | ||
container.remove(); | ||
}); | ||
it('throws if used on a "boolean-attribute"', () => { | ||
const expected = 'The map update must be used on content, not on a boolean-attribute.'; | ||
const getTemplate = ({ maybe }) => { | ||
return html`<div id="target" ?maybe="${map(maybe, () => {}, () => {})}"></div>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
let actual; | ||
try { | ||
render(container, getTemplate({ maybe: 'yes' })); | ||
} catch (error) { | ||
actual = error.message; | ||
} | ||
assert(!!actual, 'No error was thrown.'); | ||
assert(actual === expected, actual); | ||
container.remove(); | ||
}); | ||
it('render errors: repeat: throws if callback is not a function (1)', () => { | ||
const expected = 'Unexpected repeat identify "undefined" provided, expected a function.'; | ||
const getTemplate = ({ maybe }) => { | ||
return html`<div id="target" maybe="${repeat(maybe)}"></div>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
let actual; | ||
try { | ||
render(container, getTemplate({ maybe: 'yes' })); | ||
} catch (error) { | ||
actual = error.message; | ||
} | ||
assert(!!actual, 'No error was thrown.'); | ||
assert(actual === expected, actual); | ||
container.remove(); | ||
}); | ||
it('throws if used with a "property"', () => { | ||
const expected = 'The map update must be used on content, not on a property.'; | ||
const getTemplate = ({ maybe }) => { | ||
return html`<div id="target" .maybe="${map(maybe, () => {}, () => {})}"></div>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
let actual; | ||
try { | ||
render(container, getTemplate({ maybe: 'yes' })); | ||
} catch (error) { | ||
actual = error.message; | ||
} | ||
assert(!!actual, 'No error was thrown.'); | ||
assert(actual === expected, actual); | ||
container.remove(); | ||
}); | ||
it('render errors: repeat: throws if callback is not a function (2)', () => { | ||
const expected = 'Unexpected repeat callback "5" provided, expected a function.'; | ||
const getTemplate = ({ maybe }) => { | ||
return html`<div id="target" maybe="${repeat(maybe, 5)}"></div>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
let actual; | ||
try { | ||
render(container, getTemplate({ maybe: 'yes' })); | ||
} catch (error) { | ||
actual = error.message; | ||
} | ||
assert(!!actual, 'No error was thrown.'); | ||
assert(actual === expected, actual); | ||
container.remove(); | ||
}); | ||
it('throws if used with "text-content"', () => { | ||
const expected = 'The map update must be used on content, not on text-content.'; | ||
const getTemplate = ({ maybe }) => { | ||
return html`<textarea id="target">${map(maybe, () => {}, () => {})}</textarea>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
let actual; | ||
try { | ||
render(container, getTemplate({ maybe: 'yes' })); | ||
} catch (error) { | ||
actual = error.message; | ||
} | ||
assert(!!actual, 'No error was thrown.'); | ||
assert(actual === expected, actual); | ||
container.remove(); | ||
}); | ||
it('render errors: repeat: throws if used on an "attribute"', () => { | ||
const expected = 'The repeat update must be used on content, not on an attribute.'; | ||
const getTemplate = ({ maybe }) => { | ||
return html`<div id="target" maybe="${repeat(maybe, () => {})}"></div>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
let actual; | ||
try { | ||
render(container, getTemplate({ maybe: 'yes' })); | ||
} catch (error) { | ||
actual = error.message; | ||
} | ||
assert(!!actual, 'No error was thrown.'); | ||
assert(actual === expected, actual); | ||
container.remove(); | ||
}); | ||
it('throws for non-array value', () => { | ||
const getTemplate = ({ array }) => { | ||
return html` | ||
<div id="target"> | ||
${map(array, () => {}, () => html``)} | ||
</div> | ||
`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
let error; | ||
try { | ||
render(container, getTemplate({ array: 5 })); | ||
} catch (e) { | ||
error = e; | ||
} | ||
assert(error?.message === 'Unexpected map value "5".', error?.message); | ||
container.remove(); | ||
}); | ||
it('render errors: repeat: throws if used on a "boolean-attribute"', () => { | ||
const expected = 'The repeat update must be used on content, not on a boolean-attribute.'; | ||
const getTemplate = ({ maybe }) => { | ||
return html`<div id="target" ?maybe="${repeat(maybe, () => {})}"></div>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
let actual; | ||
try { | ||
render(container, getTemplate({ maybe: 'yes' })); | ||
} catch (error) { | ||
actual = error.message; | ||
} | ||
assert(!!actual, 'No error was thrown.'); | ||
assert(actual === expected, actual); | ||
container.remove(); | ||
}); | ||
it('throws for non-template callback value', () => { | ||
const getTemplate = ({ array }) => { | ||
return html` | ||
<div id="target"> | ||
${map(array, item => item.id, item => item.value ? html`<div>${item.value}</div>` : null)} | ||
</div> | ||
`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
let error; | ||
try { | ||
render(container, getTemplate({ array: [{ id: 'foo', value: null }] })); | ||
} catch (e) { | ||
error = e; | ||
} | ||
assert(error?.message === 'Unexpected map value "null" provided by callback.', error?.message); | ||
container.remove(); | ||
}); | ||
it('render errors: repeat: throws if used with a "property"', () => { | ||
const expected = 'The repeat update must be used on content, not on a property.'; | ||
const getTemplate = ({ maybe }) => { | ||
return html`<div id="target" .maybe="${repeat(maybe, () => {})}"></div>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
let actual; | ||
try { | ||
render(container, getTemplate({ maybe: 'yes' })); | ||
} catch (error) { | ||
actual = error.message; | ||
} | ||
assert(!!actual, 'No error was thrown.'); | ||
assert(actual === expected, actual); | ||
container.remove(); | ||
}); | ||
it('throws for non-template callback value (on re-render)', () => { | ||
const getTemplate = ({ array }) => { | ||
return html` | ||
<div id="target"> | ||
${map(array, item => item.id, item => item.value ? html`<div>${item.value}</div>` : null)} | ||
</div> | ||
`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
render(container, getTemplate({ array: [{ id: 'foo', value: 'oh hai' }] })); | ||
let error; | ||
try { | ||
render(container, getTemplate({ array: [{ id: 'foo', value: null }] })); | ||
} catch (e) { | ||
error = e; | ||
} | ||
assert(error?.message === 'Unexpected map value "null" provided by callback.', error?.message); | ||
container.remove(); | ||
}); | ||
}); | ||
it('render errors: repeat: throws if used with "text-content"', () => { | ||
const expected = 'The repeat update must be used on content, not on text-content.'; | ||
const getTemplate = ({ maybe }) => { | ||
return html`<textarea id="target">${repeat(maybe, () => {})}</textarea>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
let actual; | ||
try { | ||
render(container, getTemplate({ maybe: 'yes' })); | ||
} catch (error) { | ||
actual = error.message; | ||
} | ||
assert(!!actual, 'No error was thrown.'); | ||
assert(actual === expected, actual); | ||
container.remove(); | ||
}); | ||
describe('repeat', () => { | ||
it('throws if callback is not a function (1)', () => { | ||
const expected = 'Unexpected repeat identify "undefined" provided, expected a function.'; | ||
const getTemplate = ({ maybe }) => { | ||
return html`<div id="target" maybe="${repeat(maybe)}"></div>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
let actual; | ||
try { | ||
render(container, getTemplate({ maybe: 'yes' })); | ||
} catch (error) { | ||
actual = error.message; | ||
} | ||
assert(!!actual, 'No error was thrown.'); | ||
assert(actual === expected, actual); | ||
container.remove(); | ||
}); | ||
it('render errors: map: throws for non-array value', () => { | ||
const getTemplate = ({ array }) => { | ||
return html` | ||
<div id="target"> | ||
${map(array, () => {}, () => html``)} | ||
</div> | ||
`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
let error; | ||
try { | ||
render(container, getTemplate({ array: 5 })); | ||
} catch (e) { | ||
error = e; | ||
} | ||
assert(error?.message === 'Unexpected map value "5".', error?.message); | ||
container.remove(); | ||
}); | ||
it('throws if callback is not a function (2)', () => { | ||
const expected = 'Unexpected repeat callback "5" provided, expected a function.'; | ||
const getTemplate = ({ maybe }) => { | ||
return html`<div id="target" maybe="${repeat(maybe, 5)}"></div>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
let actual; | ||
try { | ||
render(container, getTemplate({ maybe: 'yes' })); | ||
} catch (error) { | ||
actual = error.message; | ||
} | ||
assert(!!actual, 'No error was thrown.'); | ||
assert(actual === expected, actual); | ||
container.remove(); | ||
}); | ||
it('render errors: map: throws for non-template callback value', () => { | ||
const getTemplate = ({ array }) => { | ||
return html` | ||
<div id="target"> | ||
${map(array, item => item.id, item => item.value ? html`<div>${item.value}</div>` : null)} | ||
</div> | ||
`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
let error; | ||
try { | ||
render(container, getTemplate({ array: [{ id: 'foo', value: null }] })); | ||
} catch (e) { | ||
error = e; | ||
} | ||
assert(error?.message === 'Unexpected map value "null" provided by callback.', error?.message); | ||
container.remove(); | ||
}); | ||
it('throws if used on an "attribute"', () => { | ||
const expected = 'The repeat update must be used on content, not on an attribute.'; | ||
const getTemplate = ({ maybe }) => { | ||
return html`<div id="target" maybe="${repeat(maybe, () => {})}"></div>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
let actual; | ||
try { | ||
render(container, getTemplate({ maybe: 'yes' })); | ||
} catch (error) { | ||
actual = error.message; | ||
} | ||
assert(!!actual, 'No error was thrown.'); | ||
assert(actual === expected, actual); | ||
container.remove(); | ||
}); | ||
it('render errors: map: throws for non-template callback value (on re-render)', () => { | ||
const getTemplate = ({ array }) => { | ||
return html` | ||
<div id="target"> | ||
${map(array, item => item.id, item => item.value ? html`<div>${item.value}</div>` : null)} | ||
</div> | ||
`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
render(container, getTemplate({ array: [{ id: 'foo', value: 'oh hai' }] })); | ||
let error; | ||
try { | ||
render(container, getTemplate({ array: [{ id: 'foo', value: null }] })); | ||
} catch (e) { | ||
error = e; | ||
} | ||
assert(error?.message === 'Unexpected map value "null" provided by callback.', error?.message); | ||
container.remove(); | ||
}); | ||
it('throws if used on a "boolean-attribute"', () => { | ||
const expected = 'The repeat update must be used on content, not on a boolean-attribute.'; | ||
const getTemplate = ({ maybe }) => { | ||
return html`<div id="target" ?maybe="${repeat(maybe, () => {})}"></div>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
let actual; | ||
try { | ||
render(container, getTemplate({ maybe: 'yes' })); | ||
} catch (error) { | ||
actual = error.message; | ||
} | ||
assert(!!actual, 'No error was thrown.'); | ||
assert(actual === expected, actual); | ||
container.remove(); | ||
}); | ||
it('render errors: repeat: throws for non-array value', () => { | ||
const getTemplate = ({ array }) => { | ||
return html` | ||
<div id="target"> | ||
${repeat(array, () => {}, () => html``)} | ||
</div> | ||
`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
let error; | ||
try { | ||
render(container, getTemplate({ array: 5 })); | ||
} catch (e) { | ||
error = e; | ||
} | ||
assert(error?.message === 'Unexpected repeat value "5".', error?.message); | ||
container.remove(); | ||
}); | ||
it('throws if used with a "property"', () => { | ||
const expected = 'The repeat update must be used on content, not on a property.'; | ||
const getTemplate = ({ maybe }) => { | ||
return html`<div id="target" .maybe="${repeat(maybe, () => {})}"></div>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
let actual; | ||
try { | ||
render(container, getTemplate({ maybe: 'yes' })); | ||
} catch (error) { | ||
actual = error.message; | ||
} | ||
assert(!!actual, 'No error was thrown.'); | ||
assert(actual === expected, actual); | ||
container.remove(); | ||
}); | ||
it('render errors: repeat: throws for non-template callback value', () => { | ||
const getTemplate = ({ array }) => { | ||
return html` | ||
<div id="target"> | ||
${repeat(array, item => item.id, item => item.value ? html`<div>${item.value}</div>` : null)} | ||
</div> | ||
`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
let error; | ||
try { | ||
render(container, getTemplate({ array: [{ id: 'foo', value: null }] })); | ||
} catch (e) { | ||
error = e; | ||
} | ||
assert(error?.message === 'Unexpected repeat value "null" provided by callback.', error?.message); | ||
container.remove(); | ||
}); | ||
it('throws if used with "text-content"', () => { | ||
const expected = 'The repeat update must be used on content, not on text-content.'; | ||
const getTemplate = ({ maybe }) => { | ||
return html`<textarea id="target">${repeat(maybe, () => {})}</textarea>`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
let actual; | ||
try { | ||
render(container, getTemplate({ maybe: 'yes' })); | ||
} catch (error) { | ||
actual = error.message; | ||
} | ||
assert(!!actual, 'No error was thrown.'); | ||
assert(actual === expected, actual); | ||
container.remove(); | ||
}); | ||
it('render errors: repeat: throws for non-template callback value (on re-render)', () => { | ||
const getTemplate = ({ array }) => { | ||
return html` | ||
<div id="target"> | ||
${repeat(array, item => item.id, item => item.value ? html`<div>${item.value}</div>` : null)} | ||
</div> | ||
`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
render(container, getTemplate({ array: [{ id: 'foo', value: 'oh hai' }] })); | ||
let error; | ||
try { | ||
render(container, getTemplate({ array: [{ id: 'foo', value: null }] })); | ||
} catch (e) { | ||
error = e; | ||
} | ||
assert(error?.message === 'Unexpected repeat value "null" provided by callback.', error?.message); | ||
container.remove(); | ||
}); | ||
it('throws for non-array value', () => { | ||
const getTemplate = ({ array }) => { | ||
return html` | ||
<div id="target"> | ||
${repeat(array, () => {}, () => html``)} | ||
</div> | ||
`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
let error; | ||
try { | ||
render(container, getTemplate({ array: 5 })); | ||
} catch (e) { | ||
error = e; | ||
} | ||
assert(error?.message === 'Unexpected repeat value "5".', error?.message); | ||
container.remove(); | ||
}); | ||
it('render errors: unsafeHTML: throws for non-string value', () => { | ||
const getTemplate = ({ content }) => { | ||
return html` | ||
<div id="target"> | ||
${unsafeHTML(content)} | ||
</div> | ||
`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
let error; | ||
try { | ||
render(container, getTemplate({ content: null })); | ||
} catch (e) { | ||
error = e; | ||
} | ||
assert(error?.message === 'Unexpected unsafeHTML value "null".', error?.message); | ||
container.remove(); | ||
}); | ||
it('throws for non-template callback value', () => { | ||
const getTemplate = ({ array }) => { | ||
return html` | ||
<div id="target"> | ||
${repeat(array, item => item.id, item => item.value ? html`<div>${item.value}</div>` : null)} | ||
</div> | ||
`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
let error; | ||
try { | ||
render(container, getTemplate({ array: [{ id: 'foo', value: null }] })); | ||
} catch (e) { | ||
error = e; | ||
} | ||
assert(error?.message === 'Unexpected repeat value "null" provided by callback.', error?.message); | ||
container.remove(); | ||
}); | ||
it('render errors: unsafeSVG: throws for non-string value', () => { | ||
const getTemplate = ({ content }) => { | ||
return html` | ||
<svg id="target" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"> | ||
${unsafeSVG(content)} | ||
</svg> | ||
`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
let error; | ||
try { | ||
render(container, getTemplate({ content: null })); | ||
} catch (e) { | ||
error = e; | ||
} | ||
assert(error?.message === 'Unexpected unsafeSVG value "null".', error?.message); | ||
container.remove(); | ||
}); | ||
it('throws for non-template callback value (on re-render)', () => { | ||
const getTemplate = ({ array }) => { | ||
return html` | ||
<div id="target"> | ||
${repeat(array, item => item.id, item => item.value ? html`<div>${item.value}</div>` : null)} | ||
</div> | ||
`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
render(container, getTemplate({ array: [{ id: 'foo', value: 'oh hai' }] })); | ||
let error; | ||
try { | ||
render(container, getTemplate({ array: [{ id: 'foo', value: null }] })); | ||
} catch (e) { | ||
error = e; | ||
} | ||
assert(error?.message === 'Unexpected repeat value "null" provided by callback.', error?.message); | ||
container.remove(); | ||
}); | ||
}); | ||
it('render errors: template array: throws for non-template value', () => { | ||
const getTemplate = ({ items }) => { | ||
return html` | ||
<svg id="target" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"> | ||
${items.map(item => item ? html`<div>${item}</div>` : null)} | ||
</svg> | ||
`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
let error; | ||
try { | ||
render(container, getTemplate({ items: [null] })); | ||
} catch (e) { | ||
error = e; | ||
} | ||
assert(error?.message === 'Unexpected array value "null" provided by callback.', error?.message); | ||
container.remove(); | ||
describe('native array', () => { | ||
it('throws for non-template value', () => { | ||
const getTemplate = ({ items }) => { | ||
return html` | ||
<svg id="target" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"> | ||
${items.map(item => item ? html`<div>${item}</div>` : null)} | ||
</svg> | ||
`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
let error; | ||
try { | ||
render(container, getTemplate({ items: [null] })); | ||
} catch (e) { | ||
error = e; | ||
} | ||
assert(error?.message === 'Unexpected array value "null" provided by callback.', error?.message); | ||
container.remove(); | ||
}); | ||
it('throws for non-template value on re-render', () => { | ||
const getTemplate = ({ items }) => { | ||
return html` | ||
<svg id="target" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"> | ||
${items.map(item => item ? html`<div>${item}</div>` : null)} | ||
</svg> | ||
`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
render(container, getTemplate({ items: ['foo'] })); | ||
let error; | ||
try { | ||
render(container, getTemplate({ items: [null] })); | ||
} catch (e) { | ||
error = e; | ||
} | ||
assert(error?.message === 'Unexpected array value "null" provided by callback.', error?.message); | ||
container.remove(); | ||
}); | ||
}); | ||
}); | ||
it('render errors: template array (on re-render): throws for non-template value', () => { | ||
const getTemplate = ({ items }) => { | ||
return html` | ||
<svg id="target" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"> | ||
${items.map(item => item ? html`<div>${item}</div>` : null)} | ||
</svg> | ||
`; | ||
}; | ||
const container = document.createElement('div'); | ||
document.body.append(container); | ||
render(container, getTemplate({ items: ['foo'] })); | ||
let error; | ||
try { | ||
render(container, getTemplate({ items: [null] })); | ||
} catch (e) { | ||
error = e; | ||
describe('interface migration errors', () => { | ||
const removedInterfaceNames = [ | ||
'asyncAppend', 'asyncReplace', 'cache', 'classMap', 'directive', 'guard', | ||
'styleMap', 'templateContent', 'until', | ||
]; | ||
for (const name of removedInterfaceNames) { | ||
it(`warns that "${name}" no longer exists.`, () => { | ||
const expected = `Removed "${name}" from default templating engine interface. Import and plug-in "lit-html" as your element's templating engine if you want this functionality.`; | ||
let actual; | ||
try { | ||
XElement.templateEngine[name](); | ||
} catch (error) { | ||
actual = error.message; | ||
} | ||
assert(!!actual, 'No error was thrown.'); | ||
assert(actual === expected, actual); | ||
}); | ||
} | ||
assert(error?.message === 'Unexpected array value "null" provided by callback.', error?.message); | ||
container.remove(); | ||
}); | ||
const removedInterfaceNames = [ | ||
'asyncAppend', 'asyncReplace', 'cache', 'classMap', 'directive', 'guard', | ||
'styleMap', 'templateContent', 'until', | ||
]; | ||
for (const name of removedInterfaceNames) { | ||
it(`interface migration errors: "${name}" no longer exists.`, () => { | ||
const expected = `Removed "${name}" from default templating engine interface. Import and plug-in "lit-html" as your element's templating engine if you want this functionality.`; | ||
let actual; | ||
try { | ||
XElement.templateEngine[name](); | ||
} catch (error) { | ||
actual = error.message; | ||
} | ||
assert(!!actual, 'No error was thrown.'); | ||
assert(actual === expected, actual); | ||
}); | ||
} |
// Simply import / export so that we pin this version in one spot. | ||
import { test, it, skip, todo, waitFor, assert, cover } from 'https://unpkg.com/@netflix/x-test@1.0.0-rc.16/x-test.js'; | ||
export { test, it, skip, todo, waitFor, assert, cover }; | ||
import { test, describe, it, waitFor, assert, coverage } from 'https://unpkg.com/@netflix/x-test@1.0.0-rc.20/x-test.js'; | ||
export { test, describe, it, waitFor, assert, coverage }; |
@@ -1065,5 +1065,5 @@ /** Base element class for creating custom elements. */ | ||
static #updaters = new WeakMap(); | ||
static #ATTRIBUTE = /<[a-zA-Z0-9-]+(?:[^/<>]|"[^"]*")* ([a-z][a-z-]*)="$/; | ||
static #BOOLEAN_ATTRIBUTE = /<[a-zA-Z0-9-]+(?:[^/<>]|"[^"]*")* \?([a-z][a-z-]*)="$/; | ||
static #PROPERTY = /<[a-zA-Z0-9-]+(?:[^/<>]|"[^"]*")* \.([a-z][a-zA-Z0-9_]*)="$/; | ||
static #ATTRIBUTE = /<[a-zA-Z0-9-]+[^>]* ([a-z][a-z-]*)="$/; | ||
static #BOOLEAN_ATTRIBUTE = /<[a-zA-Z0-9-]+[^>]* \?([a-z][a-z-]*)="$/; | ||
static #PROPERTY = /<[a-zA-Z0-9-]+[^>]* \.([a-z][a-zA-Z0-9_]*)="$/; | ||
@@ -1070,0 +1070,0 @@ #type = null; |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
262471
6807