/>`;
97 |
98 | expect(el.outerHTML).to.equal('
');
99 | });
100 |
101 | it('should support a child text node', () => {
102 | const el = dominate`
foo
`;
103 |
104 | expect(el.outerHTML).to.equal('
foo
');
105 | });
106 |
107 | it('should support a child element', () => {
108 | let el = dominate`
`;
109 | expect(el.outerHTML).to.equal('
');
110 |
111 | el = dominate`
`;
112 | expect(el.outerHTML).to.equal('
');
113 | });
114 |
115 | it('should support multiple element children', () => {
116 | const el = dominate`
`;
117 |
118 | expect(el.outerHTML).to.equal('
');
119 | });
120 |
121 | it('should support a dynamic text node', () => {
122 | const el = dominate`
${'foo'}
`;
123 |
124 | expect(el.outerHTML).to.equal('
foo
');
125 | });
126 |
127 | it('should support a dynamic element', () => {
128 | let el = dominate`
${document.createElement('span')}
`;
129 | expect(el.outerHTML).to.equal('
');
130 |
131 | el = dominate`
${dominate``}
`;
132 | expect(el.outerHTML).to.equal('
');
133 | });
134 |
135 | it('should support mixed type children', () => {
136 | let el = dominate`
${'foo'}bar
`;
137 | expect(el.outerHTML).to.equal('
foobar
');
138 |
139 | el = dominate`
before${'foo'}after
`;
140 | expect(el.outerHTML).to.equal('
beforefooafter
');
141 |
142 | el = dominate`
foo
`;
143 | expect(el.outerHTML).to.equal('
foo
');
144 |
145 | el = dominate`
foo
`;
146 | expect(el.outerHTML).to.equal('
foo
');
147 |
148 | el = dominate`
foobar
`;
149 | expect(el.outerHTML).to.equal('
foobar
');
150 |
151 | el = dominate`
foo
`;
152 | expect(el.outerHTML).to.equal('
foo
');
153 |
154 | el = dominate`
before${document.createElement('span')}
`;
155 | expect(el.outerHTML).to.equal('
before
');
156 |
157 | el = dominate`
${document.createElement('span')}after
`;
158 | expect(el.outerHTML).to.equal('
after
');
159 |
160 | el = dominate`
before${document.createElement('span')}after
`;
161 | expect(el.outerHTML).to.equal('
beforeafter
');
162 |
163 | el = dominate`
before${dominate``}
`;
164 | expect(el.outerHTML).to.equal('
before
');
165 |
166 | el = dominate`
${dominate``}after
`;
167 | expect(el.outerHTML).to.equal('
after
');
168 |
169 | el = dominate`
before${dominate``}after
`;
170 | expect(el.outerHTML).to.equal('
beforeafter
');
171 |
172 | el = dominate`
173 |
174 | foo
175 | ${'bar'}
176 |
177 | ${''}
178 | baz
179 | ${document.createElement('p')}
180 | ${dominate``}
181 | qux
182 |
183 | `;
184 | expect(el.outerHTML).to.equal('
foobar
<span></span>baz
qux
');
185 | });
186 |
187 | it('should support an array of children', () => {
188 | const children = [
189 | 'foo',
190 | dominate`
bar
`,
191 | document.createElement('span'),
192 | ];
193 |
194 | const el = dominate`
${children}
`;
195 | expect(el.outerHTML).to.equal('
');
196 | });
197 |
198 | it('should support a boolean attribute', () => {
199 | const el = dominate`
`;
200 |
201 | expect(el.disabled).to.equal(true);
202 | expect(el.outerHTML).to.equal('
');
203 | });
204 |
205 | it('should support an attribute with a static value', () => {
206 | const el = dominate`
`;
207 |
208 | expect(el.outerHTML).to.equal('
');
209 | });
210 |
211 | it('should support an attribute with an empty value', () => {
212 | const el = dominate`
`;
213 |
214 | expect(el.outerHTML).to.equal('
');
215 | });
216 |
217 | it('should support an attribute with a dynamic value', () => {
218 | const el = dominate`
`;
219 |
220 | expect(el.outerHTML).to.equal('
');
221 | });
222 |
223 | it('should support an attribute with a quoted dynamic value', () => {
224 | let el = dominate`
`;
225 | expect(el.outerHTML).to.equal('
');
226 | });
227 |
228 | it('should support attributes with no value', () => {
229 | const el = dominate`
`;
230 |
231 | expect(el.outerHTML).to.equal('
');
232 | });
233 |
234 | it('should support hyphens in attribute names', () => {
235 | const el = dominate`
`;
236 |
237 | expect(el.dataset.foo).to.equal('bar');
238 | expect(el.outerHTML).to.equal('
');
239 | });
240 |
241 | it('should set the class attribute with an array', () => {
242 | const el = dominate`
`;
243 |
244 | expect(el.className).to.equal('foo bar baz');
245 | });
246 |
247 | it('should set the class attribute with an object', () => {
248 | const el = dominate`
`;
249 |
250 | expect(el.className).to.equal('foo baz');
251 | });
252 |
253 | it('should alias className to class', () => {
254 | const el = dominate`
`;
255 |
256 | expect(el.className).to.equal('foo');
257 | });
258 |
259 | it('should support CSS styles as an object', () => {
260 | const styles = {
261 | width: '2em',
262 | gridRowStart: 1,
263 | 'padding-top': 5,
264 | 'padding-bottom': '0.7ex',
265 | top: 100,
266 | left: '100%'
267 | };
268 |
269 | const el = dominate`
`;
270 |
271 | expect(el.style.cssText).to.equal('width: 2em; grid-row-start: 1; padding-top: 5px; padding-bottom: 0.7ex; top: 100px; left: 100%;');
272 | });
273 |
274 | it('should support CSS styles as a string', () => {
275 | const el = dominate`
`;
276 |
277 | expect(el.outerHTML).to.equal('
');
278 | });
279 |
280 | it('should support CSS custom properties', () => {
281 | const el = dominate`
`;
282 | document.body.appendChild(el);
283 |
284 | expect(el.style.color).to.equal('var(--color)');
285 | expect(window.getComputedStyle(el).getPropertyValue('color')).to.equal('rgb(255, 0, 0)');
286 | expect(window.getComputedStyle(el).getPropertyValue('--color')).to.equal('red');
287 | expect(el.outerHTML).to.equal('
');
288 | document.body.removeChild(el);
289 | });
290 |
291 | it('should not add "px" suffix for custom properties', () => {
292 | const el = dominate`
test
`;
293 | document.body.appendChild(el);
294 |
295 | expect(el.style.width).to.equal('var(--foo)');
296 | expect(window.getComputedStyle(el).getPropertyValue('--foo')).to.equal('100px');
297 | document.body.removeChild(el);
298 | });
299 |
300 | it('should support DOM properties', () => {
301 | const el = dominate`
`;
302 |
303 | expect(el.value).to.equal('foo');
304 | });
305 |
306 | it('should support spread attributes', () => {
307 | let el = dominate`
`;
308 | expect(el.outerHTML).to.equal('
');
309 | });
310 |
311 | it('should support event listeners', (done) => {
312 | const event = new MouseEvent('click');
313 | const onClick = (e) => {
314 | expect(e).to.equal(event);
315 | document.body.removeChild(el);
316 | done();
317 | };
318 |
319 | const el = dominate`
`;
320 | document.body.appendChild(el);
321 | el.dispatchEvent(event);
322 | });
323 |
324 | it('should support camel-case event names', (done) => {
325 | const event = new MouseEvent('mousedown');
326 | const onMouseDown = (e) => {
327 | expect(e).to.equal(event);
328 | document.body.removeChild(el);
329 | done();
330 | };
331 |
332 | const el = dominate`
`;
333 | document.body.appendChild(el);
334 | el.dispatchEvent(event);
335 | });
336 |
337 | it('should support custom events', (done) => {
338 | let event = new CustomEvent('foo');
339 |
340 | const callback = sinon.spy((e) => {
341 | expect(e).to.equal(event);
342 | document.body.removeChild(el);
343 | done();
344 | });
345 |
346 | const el = dominate`
`;
347 | document.body.appendChild(el);
348 | el.dispatchEvent(event);
349 | });
350 |
351 | it('should support the form attribute', () => {
352 | const el = dominate`
353 |
354 |
355 |
356 |
357 |
358 | `;
359 |
360 | document.body.appendChild(el);
361 |
362 | const form = el.childNodes[0];
363 | const button = el.childNodes[1];
364 | const input = el.childNodes[2];
365 |
366 | expect(button).to.have.property('form', form);
367 | expect(input).to.have.property('form', form);
368 |
369 | document.body.removeChild(el);
370 | });
371 |
372 | it('should clear falsy input values', () => {
373 | const el = dominate`
374 |
375 |
376 |
377 |
378 |
379 |
380 | `;
381 |
382 | expect(el.children[0]).to.have.property('value', '0');
383 | expect(el.children[1]).to.have.property('value', 'false');
384 | expect(el.children[2]).to.have.property('value', '');
385 | expect(el.children[3]).to.have.property('value', '');
386 | });
387 |
388 | it('should support falsy DOM properties', () => {
389 | const el1 = dominate`
390 |
394 | `;
395 |
396 | expect(el1.innerHTML).to.equal('
');
397 |
398 | const el2 = dominate`
399 |
403 | `;
404 |
405 | expect(el2.innerHTML).to.equal('
');
406 |
407 | const el3 = dominate`
408 |
412 | `;
413 |
414 | expect(el3.innerHTML).to.equal('
');
415 | });
416 |
417 | it('should set an enumerable boolean attribute', () => {
418 | const el = dominate`
`;
419 |
420 | expect(el.spellcheck).to.equal(false);
421 | });
422 |
423 | it('should set the download attribute', () => {
424 | const el1 = dominate`
`;
425 |
426 | expect(el1.getAttribute('download')).to.equal('');
427 |
428 | const el2 = dominate`
`;
429 |
430 | expect(el2.getAttribute('download')).to.equal(null);
431 | });
432 |
433 | it('should support false string aria attributes', () => {
434 | const el = dominate`
`;
435 |
436 | expect(el.getAttribute('aria-checked')).to.equal('false');
437 | });
438 |
439 | it('should support false aria attributes', () => {
440 | const el = dominate`
`;
441 |
442 | expect(el.getAttribute('aria-checked')).to.equal('false');
443 | });
444 |
445 | it('should support false data attributes', () => {
446 | const el = dominate`
`;
447 |
448 | expect(el.getAttribute('data-checked')).to.equal('false');
449 | });
450 |
451 | it('should set checked attribute on custom elements without checked property', () => {
452 | const el = dominate`
`;
453 |
454 | expect(el.outerHTML).to.equal('
');
455 | });
456 |
457 | it('should set value attribute on custom elements without value property', () => {
458 | const el = dominate`
`;
459 |
460 | expect(el.outerHTML).to.equal('
');
461 | });
462 |
463 | it('should mask value on password input elements', () => {
464 | const el = dominate`
`;
465 |
466 | expect(el.outerHTML).to.equal('
');
467 | });
468 |
469 | it('should unset href if null or undefined', () => {
470 | const el = dominate`
471 |
477 | `;
478 |
479 | const links = el.querySelectorAll('a');
480 | expect(links[0].hasAttribute('href')).to.equal(true);
481 | expect(links[1].hasAttribute('href')).to.equal(false);
482 | expect(links[2].hasAttribute('href')).to.equal(false);
483 | expect(links[3].hasAttribute('href')).to.equal(true);
484 | });
485 |
486 | it('should not set tagName', () => {
487 | expect(() => dominate`
`).not.to.throw();
488 | });
489 |
490 | it('should not throw when setting size to an invalid value', () => {
491 | expect(() => dominate`
`).to.not.throw();
492 | expect(() => dominate`
`).to.not.throw();
493 | expect(() => dominate`
`).to.not.throw();
494 | });
495 |
496 | it('should escape HTML characters', () => {
497 | const el = dominate`
${'bar'}
`;
498 |
499 | expect(el.outerHTML).to.equal('
<i id="foo" class=\'bar\'>bar</i>
');
500 | });
501 |
502 | it('should support SVG', () => {
503 | const svg = dominate`
`;
504 |
505 | expect(svg.outerHTML).to.equal('
');
506 |
507 | expect(svg.nodeType).to.equal(1);
508 | expect(svg.namespaceURI).to.equal('http://www.w3.org/2000/svg');
509 | expect(svg).to.be.instanceof(SVGElement);
510 |
511 | const circle = svg.querySelector('circle');
512 | expect(circle.nodeType).to.equal(1);
513 | expect(circle.getAttribute('class')).to.equal('foo');
514 | expect(circle.namespaceURI).to.equal('http://www.w3.org/2000/svg');
515 | expect(circle).to.be.instanceof(SVGElement);
516 | });
517 |
518 | it('should execute scripts', () => {
519 | const el = dominate``;
520 | expect(window.foo).to.not.exist;
521 | document.body.appendChild(el);
522 | expect(window.foo).to.exist;
523 | delete window.foo;
524 | });
525 |
526 | it('should support custom elements', () => {
527 | const customElementSpy = sinon.spy();
528 |
529 | customElements.define('foo-bar', class FooBar extends HTMLElement {
530 | constructor() {
531 | super();
532 | customElementSpy();
533 | }
534 | });
535 |
536 | const el = dominate`
537 |
538 | `;
539 |
540 | expect(el.nodeType).to.equal(1);
541 | expect(el.nodeName).to.equal('FOO-BAR');
542 | expect(el.outerHTML).to.equal('
');
543 | expect(customElementSpy.callCount).to.equal(1);
544 | });
545 |
546 | it('should support functional components', () => {
547 | const Component = (attributes, children) => {
548 | expect(attributes).to.deep.equal({id: 'foo', class: 'bar'});
549 | expect(children).to.be.an('array');
550 | expect(children).to.have.length(1);
551 | expect(children[0].outerHTML).to.equal('
baz');
552 | return dominate`
${children[0]}
`;
553 | };
554 |
555 | const el = dominate`
556 | <${Component} id="foo" class="bar">
557 |
baz
558 | />
559 | `;
560 |
561 | expect(el.nodeType).to.equal(1);
562 | expect(el.outerHTML).to.equal('
baz
');
563 | });
564 |
565 | it('should support returning multiple elements via ref attributes', () => {
566 | const { foo, bar, baz } = dominate`
567 |
568 |
569 |
570 |
571 | `;
572 |
573 | expect(foo.outerHTML).to.equal('
');
574 | expect(bar.outerHTML).to.equal('
');
575 | expect(baz.outerHTML).to.equal('
');
576 | });
577 |
578 | it('should support refs for document fragments', () => {
579 | const { foo, bar } = dominate`
580 |
581 |
582 | `;
583 |
584 | expect(foo.outerHTML).to.equal('
');
585 | expect(bar.outerHTML).to.equal('
');
586 | });
587 |
588 | it('should support inner refs', () => {
589 | const Component = () => dominate`
`;
590 |
591 | const { foo, bar, baz } = dominate`
592 |
593 | ${dominate``}
594 | <${Component} />
595 |
596 | `;
597 |
598 | expect(foo.outerHTML).to.equal('
');
599 | expect(bar.outerHTML).to.equal('
');
600 | expect(baz.outerHTML).to.equal('
');
601 | });
602 |
603 | it('should not cache elements with identical markup', () => {
604 | const text = () => dominate`foo`;
605 | const element = () => dominate`
`;
606 | const frag = () => dominate`
`;
607 |
608 | expect(dominate`foo`).to.not.equal(dominate`foo`);
609 | expect(dominate`
`).to.not.equal(dominate`
`);
610 | expect(dominate`
`).to.not.equal(dominate`
`);
611 | expect(text()).to.not.equal(text());
612 | expect(element()).to.not.equal(element());
613 | expect(frag()).to.not.equal(frag());
614 | });
615 | });
616 |
--------------------------------------------------------------------------------