| <!doctype html> |
| <!-- |
| @license |
| Copyright (c) 2014 The Polymer Project Authors. All rights reserved. |
| This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt |
| The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt |
| The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt |
| Code distributed by Google as part of the polymer project is also |
| subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt |
| --> |
| <html> |
| <head> |
| <meta charset="utf-8"> |
| <script> |
| WCT = {waitFor: function (cb) {HTMLImports.whenReady(cb)}} |
| </script> |
| <script src="./test-flags.js"></script> |
| <script src="../node_modules/wct-browser-legacy/browser.js"></script> |
| <script src="../node_modules/@webcomponents/webcomponents-platform/webcomponents-platform.js"></script> |
| <script src="../node_modules/es6-promise/dist/es6-promise.auto.min.js"></script> |
| <script src="../node_modules/@webcomponents/template/template.js"></script> |
| <script src="../node_modules/@webcomponents/html-imports/html-imports.min.js"></script> |
| <script src="../node_modules/@webcomponents/shadydom/shadydom.min.js"></script> |
| <script src="../node_modules/@webcomponents/custom-elements/custom-elements.min.js"></script> |
| <script src="../scoping-shim.min.js"></script> |
| <script src="../apply-shim.min.js"></script> |
| <script src="../custom-style-interface.min.js"></script> |
| <script src="module/generated/make-element.js"></script> |
| |
| <custom-style> |
| <style> |
| div#priority { |
| border: 1px solid black; |
| } |
| </style> |
| </custom-style> |
| </head> |
| <body> |
| |
| <template id="x-gchild"> |
| <style> |
| </style> |
| <div id="target">x-gchild</div> |
| </template> |
| |
| <template id="x-child"> |
| <div id="simple">simple</div> |
| <div id="complex1" class="scoped">complex1</div> |
| <div id="complex2" selected>complex2</div> |
| <div id="media">media</div> |
| <div id="shadow" class="shadowTarget">shadowTarget</div> |
| <div id="deep" class="deepTarget">deepTarget</div> |
| <x-gchild id="gchild1"></x-gchild> |
| <x-gchild id="gchild2" class="wide"></x-gchild> |
| </template> |
| |
| <template id="x-child2"> |
| <style> |
| :host(.wide) #target{ |
| border: none; |
| } |
| </style> |
| <div id="target">x-child2</div> |
| </template> |
| |
| <template id="x-scope-class"> |
| <div id="scope">Trivial</div> |
| </template> |
| |
| <template id="x-scoped"> |
| <style> |
| :host { |
| display: block; |
| border: 1px solid orange; |
| --keyframes100: 100px; |
| } |
| |
| :host(.wide) { |
| border-width: 2px; |
| } |
| |
| :host(.wide)::after { |
| content: '-content-'; |
| }; |
| |
| #keyframes2.special { |
| --keyframes100: 200px; |
| } |
| |
| #simple { |
| border: 3px solid orange; |
| } |
| |
| .scoped, [selected] { |
| border: 4px solid pink; |
| } |
| |
| @media(max-width: 10000px) { |
| .media { |
| border: 5px solid brown; |
| } |
| } |
| |
| .container ::slotted(*) { |
| border: 6px solid navy; |
| } |
| |
| #priority { |
| border: 9px solid orange; |
| } |
| |
| .container1 > ::slotted([slot=content1]) { |
| border: 13px solid navy; |
| } |
| |
| .container2 > ::slotted([slot=content2]) { |
| border: 14px solid navy; |
| } |
| |
| .computed { |
| border: 15px solid orange; |
| } |
| |
| .computeda { |
| border: 20px solid orange; |
| } |
| |
| #child { |
| border: 16px solid tomato; |
| display: block; |
| } |
| |
| svg { |
| margin-top: 20px; |
| } |
| |
| #circle { |
| fill: seagreen; |
| stroke-width: 1px; |
| stroke: tomato; |
| } |
| </style> |
| <slot name="blank"></slot> |
| <div id="simple">simple</div> |
| <div id="complex1" class="scoped">complex1</div> |
| <div id="complex2" selected>complex2</div> |
| <div id="media" class="media">media</div> |
| <div class="container1"> |
| <slot name="content1"></slot> |
| </div> |
| <div class="container2"> |
| <slot name="content2"></slot> |
| </div> |
| <div class="container"> |
| <slot></slot> |
| </div> |
| <x-child id="child"></x-child> |
| <div id="priority">priority</div> |
| <x-child2 class="wide" id="child2"></x-child2> |
| <div id="computed">Computed</div> |
| <svg height="25" width="25"> |
| <circle id="circle" cx="12" cy="12" r="10"></circle> |
| </svg> |
| <x-scope-class id="scopeClass"></x-scope-class> |
| <x-keyframes id="keyframes"></x-keyframes> |
| <x-keyframes id="keyframes2"></x-keyframes> |
| </template> |
| |
| <template id="x-slotted"> |
| <style> |
| ::slotted(.auto-content) { |
| border: 2px solid orange; |
| } |
| .bar, ::slotted(.complex-child) { |
| border: 6px solid navy; |
| } |
| #container ::slotted(*) { |
| border: 8px solid green; |
| } |
| </style> |
| <slot></slot> |
| <div id="container"> |
| <slot name="container"></slot> |
| </div> |
| </template> |
| |
| <template id="dynamic"> |
| <div class="added"> |
| Added |
| <div class="sub-added"> |
| Sub-added |
| </div> |
| </div> |
| </div> |
| </template> |
| |
| <template id="x-dynamic-scope"> |
| <style> |
| .added { |
| border: 17px solid beige; |
| } |
| .sub-added { |
| border: 18px solid #fafafa; |
| } |
| </style> |
| <div id="container"></div> |
| </template> |
| |
| <template id="x-keyframes"> |
| <style> |
| :host { |
| display: block; |
| position: relative; |
| border: 10px solid blue; |
| left: 0px; |
| /* Prefix required by Safari <= 8 */ |
| -webkit-animation-duration: 0.3s; |
| animation-duration: 0.3s; |
| -webkit-animation-fill-mode: forwards; |
| animation-fill-mode: forwards; |
| } |
| |
| :host([animated]) { |
| /* Prefix required by Safari <= 8 */ |
| -webkit-animation-name: x-keyframes-animation; |
| animation-name: x-keyframes-animation; |
| } |
| |
| /* Prefix required by Safari <= 8 */ |
| @-webkit-keyframes x-keyframes-animation { |
| 0% { |
| left: var(--keyframes0, 0px); |
| } |
| |
| 100% { |
| left: var(--keyframes100, 10px); |
| } |
| } |
| @keyframes x-keyframes-animation { |
| 0% { |
| left: var(--keyframes0, 0px); |
| } |
| |
| 100% { |
| left: var(--keyframes100, 10px); |
| } |
| } |
| </style> |
| x-keyframes |
| </template> |
| |
| <template id="x-attr-selector"> |
| <style> |
| #foo1 ~ #bar1 { |
| border: 2px solid red; |
| } |
| |
| #foo1 ~ #bar1 ~ #foo2[attr~=foo2] ~ #bar2[attr~=bar2] { |
| border: 4px solid red; |
| } |
| |
| #foo1 ~ #bar1 ~ #foo2[attr~=foo2] ~ #bar2[attr~=bar2] ~ #foo3[attr~=foo3][a~=a] ~ #bar3[attr~=bar3][a~=a] { |
| border: 6px solid red; |
| } |
| </style> |
| <div id="foo1"></div> |
| <div id="bar1">bar1</div> |
| <div id="foo2" attr="foo2"></div> |
| <div id="bar2" attr="bar2">bar2</div> |
| <div id="foo3" attr="foo3" a="a"></div> |
| <div id="bar3" attr="bar3" a="a">bar3</div> |
| </template> |
| |
| <template id="x-adjacent-sibling"> |
| <style> |
| div { |
| border: 20px solid black; |
| } |
| #foo2 + #foo1 { |
| border: 2px solid black; |
| } |
| #foo1 + #foo2 { |
| border: 4px solid black; |
| } |
| #foo2 + #foo3 { |
| border: 6px solid black; |
| } |
| </style> |
| <div id="foo1"></div> |
| <div id="foo2"></div> |
| <div id="foo3"></div> |
| </template> |
| |
| <template id="svg"> |
| <svg class="svg" viewBox="0 0 24 24"> |
| <circle id="circle" r="12" cx="12" cy="12" /> |
| </svg> |
| </template> |
| |
| <template id="x-dynamic-svg"> |
| <style> |
| .svg { |
| height: 24px; |
| width: 24px; |
| } |
| #circle { |
| fill: red; |
| fill-opacity: 0.5; |
| } |
| </style> |
| <div id="container"></div> |
| </template> |
| |
| <template id="x-specificity"> |
| <style> |
| :host { |
| border-top: 1px solid red; |
| } |
| :host(.bar) { |
| border-top: 2px solid red; |
| } |
| </style> |
| <slot></slot> |
| </template> |
| |
| <template id="self-test"> |
| <style> |
| :host { |
| --border: 10px solid rgb(123, 123, 123); |
| } |
| |
| a { |
| border: var(--border); |
| } |
| </style> |
| <a>I should be red.</a> |
| </template> |
| |
| <template id="nth-plus-one"> |
| <style> |
| .foo.bar { |
| color: rgb(255, 0, 0); |
| } |
| div:nth-child(n+1) { |
| color: rgb(0, 255, 0); |
| } |
| </style> |
| <div>1</div> |
| <div class="foo bar">2</div> |
| </template> |
| |
| <template id="shady-unscoped"> |
| <style shady-unscoped> |
| .unscoped { |
| color: rgb(255, 0, 0); |
| } |
| </style> |
| <div class="unscoped"></div> |
| </template> |
| |
| <template id="shady-unscoped-2"> |
| <style shady-unscoped> |
| .unscoped { |
| color: rgb(255, 0, 0); |
| } |
| </style> |
| <span class="unscoped"></span> |
| </template> |
| |
| <template id="unscoped-apply-user"> |
| <style> |
| div { |
| @apply --unscoped-foo; |
| } |
| </style> |
| <div></div> |
| </template> |
| |
| <template id="unscoped-apply"> |
| <style shady-unscoped> |
| html, :host > * { |
| --unscoped-foo: {border: 10px solid black}; |
| } |
| </style> |
| <unscoped-apply-user></unscoped-apply-user> |
| </template> |
| |
| <template id="any-selector"> |
| <style> |
| :-webkit-any(div, span) { |
| color: rgb(123, 123, 123); |
| } |
| :-moz-any(div, span) { |
| color: rgb(123, 123, 123); |
| } |
| </style> |
| <div>a</div> |
| <span>b</span> |
| </template> |
| |
| <template id="scoped-keyframes"> |
| <style> |
| :host { |
| --time: 0.1s; |
| } |
| |
| div { |
| /* prefix for older chrome and safari */ |
| -webkit-animation-duration: var(--time); |
| animation-duration: var(--time); |
| -webkit-animation-fill-mode: forwards; |
| animation-fill-mode: forwards; |
| border: 0px solid black; |
| } |
| |
| :host([animate]) div { |
| /* prefix for older chrome and safari */ |
| -webkit-animation-name: border-grow; |
| animation-name: border-grow; |
| } |
| |
| /* prefix for older chrome and safari */ |
| @-webkit-keyframes border {} |
| @-webkit-keyframes border-grow { |
| to { |
| border-top-width: 10px; |
| } |
| } |
| @keyframes border {} |
| @keyframes border-grow { |
| to { |
| border-top-width: 10px; |
| } |
| } |
| </style> |
| |
| <div id="target">Hello world</div> |
| </template> |
| |
| <template id="nested-templates"> |
| <style> |
| * { |
| opacity: 0.5; |
| } |
| </style> |
| <div id="a"></div> |
| <template id="t1"> |
| <div id="b"> |
| <div id="c"> |
| <template id="t2"> |
| <div id="d"></div> |
| </template> |
| </div> |
| </div> |
| </template> |
| <svg> |
| <template id="t3"> |
| <g id="g"> |
| <circle id="circle"></circle> |
| </g> |
| </template> |
| </svg> |
| </template> |
| |
| <template id="bad-mixin"> |
| <style> |
| :host(.nomatch) { |
| --div-border: { |
| border: 2px solid black; |
| } |
| } |
| div { |
| @apply --div-border; |
| } |
| </style> |
| <div></div> |
| </template> |
| |
| <template id="x-parent-skip"> |
| <style> |
| :host { |
| --foo: 10px solid black; |
| } |
| </style> |
| <x-skip></x-skip> |
| </template> |
| |
| <template id="x-skip"> |
| <x-child-skip></x-child-skip> |
| </template> |
| |
| <template id="x-child-skip"> |
| <style> |
| div { |
| border: var(--foo); |
| } |
| </style> |
| <div></div> |
| </template> |
| |
| <script> |
| (function() { |
| function assertComputed(element, value, property, pseudo) { |
| var computed = getComputedStyle(element, pseudo); |
| property = property || 'border-top-width'; |
| if (Array.isArray(value)) { |
| assert.oneOf(computed[property], value, 'computed style incorrect for ' + property); |
| } else { |
| assert.equal(computed[property], value, 'computed style incorrect for ' + property); |
| } |
| } |
| |
| function findNode(desc) { |
| var parts = desc.split('.'); |
| var root = document; |
| var node; |
| for (var i=0, p; i < parts.length; i++) { |
| p = parts[i]; |
| if (p == '$') { |
| root = node.shadowRoot; |
| } else { |
| node = root.querySelector('#' + p); |
| } |
| } |
| return node; |
| } |
| |
| function flush() { |
| if (window.ShadyDOM) { |
| window.ShadyDOM.flush(); |
| } |
| window.ShadyCSS.ScopingShim.flush(); |
| } |
| |
| suite('scoped-styling', function() { |
| |
| suiteSetup(function() { |
| makeElement('x-gchild'); |
| makeElement('x-child', function() { |
| this.classList.add('nug'); |
| }); |
| makeElement('x-child2'); |
| makeElement('x-scope-class'); |
| makeElement('x-scoped'); |
| makeElement('x-slotted'); |
| (function() { |
| var dynamic = document.querySelector('template#dynamic'); |
| |
| makeElement('x-dynamic-scope', |
| function() { |
| // simulate 3rd party action by using normal dom to add to element. |
| var dom = document.importNode(dynamic.content, true); |
| this.shadowRoot.querySelector('#container').appendChild(dom); |
| }); |
| })(); |
| makeElement('x-keyframes'); |
| makeElement('x-attr-selector'); |
| (function() { |
| var template = document.querySelector('template#svg'); |
| |
| makeElement('x-dynamic-svg', function() { |
| var dom = document.importNode(template.content, true); |
| this.shadowRoot.querySelector('#container').appendChild(dom); |
| }); |
| })(); |
| makeElement('x-specificity'); |
| makeElement('nested-templates'); |
| }); |
| |
| var el; |
| setup(function() { |
| el = document.createElement('x-scoped'); |
| el.id = 'el'; |
| document.body.appendChild(el); |
| flush(); |
| }); |
| |
| teardown(function() { |
| document.body.removeChild(el); |
| }); |
| |
| test(':host', function() { |
| assertComputed(el, '1px'); |
| assertComputed(el, ['', 'none'], 'content', '::after'); |
| }); |
| |
| test(':host(...)', function() { |
| var el2 = document.createElement('x-scoped'); |
| el2.classList.add('wide'); |
| document.body.appendChild(el2); |
| flush(); |
| assertComputed(el2, '2px'); |
| assertComputed(el2, ['"-content-"', '-content-'], 'content', '::after'); |
| document.body.removeChild(el2); |
| }); |
| |
| test('scoped selectors, simple and complex', function() { |
| assertComputed(findNode('el.$.simple'), '3px'); |
| assertComputed(findNode('el.$.complex1'), '4px'); |
| assertComputed(findNode('el.$.complex2'), '4px'); |
| }); |
| |
| test('media query scoped selectors', function() { |
| assertComputed(findNode('el.$.media'), '5px'); |
| }); |
| |
| test('upper bound encapsulation', function() { |
| var d = document.createElement('div'); |
| d.classList.add('scoped'); |
| document.body.appendChild(d); |
| assertComputed(d, '0px'); |
| document.body.removeChild(d); |
| }); |
| |
| test('lower bound encapsulation', function() { |
| assertComputed(findNode('el.$.child.$.simple'), '0px'); |
| assertComputed(findNode('el.$.child.$.complex1'), '0px'); |
| assertComputed(findNode('el.$.child.$.complex2'), '0px'); |
| assertComputed(findNode('el.$.child.$.media'), '0px'); |
| }); |
| |
| test('nested templates', function() { |
| var el = document.createElement('nested-templates'); |
| document.body.appendChild(el); |
| // Append nested template content. Note the <template> in <svg> is not |
| // an HTML template with .content at this point; it is just an unknown |
| // SVGElement so we don't have to stamp it |
| var t1 = el.shadowRoot.querySelector('#t1'); |
| el.shadowRoot.appendChild(t1.content.cloneNode(true)); |
| var t2 = el.shadowRoot.querySelector('#t2'); |
| el.shadowRoot.appendChild(t2.content.cloneNode(true)); |
| // Everything should now have 'opacity: 0.5' |
| var els = Array.from(el.shadowRoot.querySelectorAll('[id]')); |
| assert.deepEqual(els.map(e => e.getAttribute('id')), ['a', 't1', 't3', 'g', 'circle', 'b', 'c', 't2', 'd']); |
| els.forEach(e => { |
| assert.equal(getComputedStyle(e).opacity, '0.5', `Element with id "${e.id}" does not have the correct opacity`); |
| }); |
| document.body.removeChild(el); |
| }); |
| |
| }); |
| |
| suite('slotted', function() { |
| |
| test('::slotted selectors', function() { |
| var el = document.createElement('x-scoped'); |
| document.body.appendChild(el); |
| var content1 = document.createElement('div'); |
| content1.slot = 'content1'; |
| var content2 = document.createElement('div'); |
| content2.slot = 'content2'; |
| var content = document.createElement('div'); |
| content.className = 'content'; |
| el.appendChild(content1); |
| el.appendChild(content2); |
| el.appendChild(content); |
| flush(); |
| |
| assertComputed(content, '6px'); |
| assertComputed(content1, '13px'); |
| assertComputed(content2, '14px'); |
| document.body.removeChild(el); |
| }); |
| |
| test('auto ::slotted selector', function() { |
| var x = document.createElement('x-slotted'); |
| var d1 = document.createElement('div'); |
| d1.classList.add('auto-content'); |
| d1.textContent = 'auto-content'; |
| document.body.appendChild(x); |
| x.appendChild(d1); |
| flush(); |
| assertComputed(d1, '2px'); |
| document.body.removeChild(x); |
| }); |
| |
| test('::slotted + child in complex selector', function() { |
| var x = document.createElement('x-slotted'); |
| var d1 = document.createElement('div'); |
| d1.classList.add('complex-child'); |
| d1.textContent = 'complex-child'; |
| document.body.appendChild(x); |
| x.appendChild(d1); |
| flush(); |
| assertComputed(d1, '6px'); |
| document.body.removeChild(x); |
| }); |
| |
| test('::slotted + named slot', function() { |
| var x = document.createElement('x-slotted'); |
| var d1 = document.createElement('div'); |
| d1.setAttribute('slot', 'container') |
| d1.textContent = 'named slot child'; |
| document.body.appendChild(x); |
| x.appendChild(d1); |
| flush(); |
| assertComputed(d1, '8px'); |
| document.body.removeChild(x); |
| }); |
| |
| }); |
| |
| suite('dynamic changes', function() { |
| |
| test('elements dynamically added/removed from root', function() { |
| var el = document.createElement('x-scoped'); |
| document.body.appendChild(el); |
| flush(); |
| var d = document.createElement('div'); |
| d.classList.add('scoped'); |
| d.textContent = 'Dynamically... Scoped!'; |
| el.shadowRoot.appendChild(d); |
| flush(); |
| assertComputed(d, '4px'); |
| document.body.appendChild(d); |
| flush(); |
| assert.notInclude(d.getAttribute('style-scoped') || '', el.is, 'scoping attribute not removed when added to other root'); |
| assert.notInclude(d.className, el.is, 'scoping class not removed when added to other root'); |
| el.shadowRoot.appendChild(d); |
| flush(); |
| assertComputed(d, '4px'); |
| el.shadowRoot.removeChild(d); |
| flush(); |
| assert.notInclude(d.getAttribute('style-scoped') || '', el.is, 'scoping attribute not removed when removed from root'); |
| assert.notInclude(d.className, el.is, 'scoping class not removed when removed from root'); |
| el.shadowRoot.appendChild(d); |
| flush(); |
| assertComputed(d, '4px'); |
| document.body.removeChild(el); |
| }); |
| |
| test('elements dynamically added/removed from host', function() { |
| var el = document.createElement('x-scoped'); |
| document.body.appendChild(el); |
| var d = document.createElement('div'); |
| d.classList.add('scoped'); |
| d.slot = 'blank'; |
| d.textContent = 'Dynamically... unScoped!'; |
| el.appendChild(d); |
| flush(); |
| assertComputed(d, '0px'); |
| el.removeChild(d); |
| flush(); |
| assert.notInclude(d.getAttribute('style-scoped') || '', el.is, 'scoping attribute not removed when added to other root'); |
| assert.notInclude(d.className, el.is, 'scoping class not removed when added to other root'); |
| el.appendChild(d); |
| flush(); |
| assertComputed(d, '0px'); |
| el.removeChild(d); |
| flush(); |
| assert.notInclude(d.getAttribute('style-scoped') || '', el.is, 'scoping attribute not removed when removed from root'); |
| assert.notInclude(d.className, el.is, 'scoping class not removed when removed from root'); |
| el.appendChild(d); |
| flush(); |
| assertComputed(d, '0px'); |
| document.body.removeChild(el); |
| }); |
| |
| test('element subtree added via dom api', function() { |
| var el = document.createElement('x-dynamic-scope'); |
| document.body.appendChild(el); |
| flush(); |
| var container = el.shadowRoot.querySelector('#container'); |
| var a = container.querySelector('.added'); |
| assertComputed(a, '17px'); |
| var b = container.querySelector('.sub-added'); |
| assertComputed(b, '18px'); |
| document.body.removeChild(el); |
| }); |
| |
| test('changes to class attribute', function() { |
| var el = document.createElement('x-scoped'); |
| el.id = 'el' |
| document.body.appendChild(el); |
| flush(); |
| var d = findNode('el.$.computed'); |
| assertComputed(d, '0px'); |
| d.setAttribute('class', 'computed'); |
| assertComputed(d, '15px'); |
| d.setAttribute('class', '', 'empty class attr does not remove class'); |
| assertComputed(d, '0px'); |
| d.setAttribute('class', 'computed ', 'class attr with space does not apply'); |
| assertComputed(d, '15px'); |
| document.body.removeChild(el); |
| }); |
| |
| }); |
| |
| suite('misc', function() { |
| |
| var el; |
| setup(function() { |
| el = document.createElement('x-scoped'); |
| el.id = 'el'; |
| document.body.appendChild(el); |
| flush(); |
| }); |
| |
| teardown(function() { |
| document.body.removeChild(el); |
| }); |
| |
| test('keyframes change scope', function(done) { |
| var xKeyframes = findNode('el.$.keyframes'); |
| // Edge 16 does not support CSS Custom Properties in keyframes |
| if (window.ShadyCSS.nativeCss && navigator.userAgent.match(/Edge/)) { |
| this.skip(); |
| } |
| var onAnimationEnd = function() { |
| xKeyframes.removeEventListener('animationend', onAnimationEnd); |
| xKeyframes.removeEventListener('webkitAnimationEnd', onAnimationEnd); |
| assertComputed(xKeyframes, '100px', 'left'); |
| |
| xKeyframes = findNode('el.$.keyframes2'); |
| |
| onAnimationEnd = function() { |
| xKeyframes.removeEventListener('animationend', onAnimationEnd); |
| xKeyframes.removeEventListener('webkitAnimationEnd', onAnimationEnd); |
| assertComputed(xKeyframes, '200px', 'left'); |
| done(); |
| }; |
| |
| xKeyframes.addEventListener('animationend', onAnimationEnd); |
| xKeyframes.addEventListener('webkitAnimationEnd', onAnimationEnd); |
| |
| xKeyframes.classList.add('special'); |
| xKeyframes.setAttribute('animated', ''); |
| window.ShadyCSS.ScopingShim.styleElement(xKeyframes); |
| }; |
| xKeyframes.addEventListener('animationend', onAnimationEnd); |
| xKeyframes.addEventListener('webkitAnimationEnd', onAnimationEnd); |
| xKeyframes.setAttribute('animated', ''); |
| assertComputed(xKeyframes, '0px', 'left'); |
| }); |
| |
| test('keyframe names are transformed correctly', function(done) { |
| makeElement('scoped-keyframes'); |
| var e = document.createElement('scoped-keyframes'); |
| document.body.appendChild(e); |
| flush(); |
| var target = e.shadowRoot.querySelector('#target'); |
| var onAnimationEnd = function() { |
| assertComputed(target, '10px'); |
| target.removeEventListener('animationend', onAnimationEnd); |
| target.removeEventListener('webkitAnimationEnd', onAnimationEnd); |
| document.body.removeChild(e); |
| done(); |
| }; |
| target.addEventListener('animationend', onAnimationEnd); |
| target.addEventListener('webkitAnimationEnd', onAnimationEnd); |
| e.setAttribute('animate', ''); |
| assertComputed(target, '0px'); |
| }); |
| |
| test('attribute inclusive selector and general sibling selectors', function() { |
| var x = document.createElement('x-attr-selector'); |
| x.id = 'x'; |
| document.body.appendChild(x); |
| flush(); |
| assertComputed(findNode('x.$.bar1'), '2px'); |
| assertComputed(findNode('x.$.bar2'), '4px'); |
| assertComputed(findNode('x.$.bar3'), '6px'); |
| document.body.removeChild(x); |
| }); |
| |
| test('adjacent sibling selectors', function() { |
| makeElement('x-adjacent-sibling'); |
| var x = document.createElement('x-adjacent-sibling'); |
| x.id = 'x'; |
| document.body.appendChild(x); |
| flush(); |
| assertComputed(findNode('x.$.foo1'), '20px'); |
| assertComputed(findNode('x.$.foo2'), '4px'); |
| assertComputed(findNode('x.$.foo3'), '6px'); |
| document.body.removeChild(x); |
| }) |
| |
| test('svg classes are dynamically scoped correctly', function() { |
| var x = document.createElement('x-dynamic-svg'); |
| x.id = 'x'; |
| document.body.appendChild(x); |
| flush(); |
| var container = findNode('x.$.container'); |
| var svg = container.querySelector('.svg'); |
| var computed = getComputedStyle(svg); |
| assert.equal(computed.height, '24px'); |
| assert.equal(computed.width, '24px'); |
| var circle = container.querySelector('#circle'); |
| computed = getComputedStyle(circle); |
| assert.equal(computed['fill-opacity'], '0.5'); |
| document.body.removeChild(x); |
| }); |
| |
| test(':host selectors always lowest priority', function() { |
| var priority = findNode('el.$.priority'); |
| assertComputed(priority, '9px'); |
| el.setAttribute('class', 'wide'); |
| assertComputed(priority, '9px'); |
| }); |
| |
| test('svg elements properly scoped', function() { |
| if (window.ShadyCSS.nativeShadow) { |
| this.skip(); |
| } |
| var circle = findNode('el.$.circle'); |
| var classes = (circle.getAttribute('class') || '').split(/\s+/); |
| assert.include(classes, 'x-scoped'); |
| assert.include(classes, 'style-scope'); |
| assert.notInclude(classes, 'null'); |
| assertComputed(circle, '1px', 'strokeWidth'); |
| }); |
| |
| test('set attribute class has style scoping selectors', function() { |
| if (window.ShadyCSS.nativeShadow) { |
| this.skip(); |
| } |
| var s = findNode('el.$.scopeClass'); |
| var scope = findNode('el.$.scopeClass.$.scope'); |
| assert.isTrue(s.classList.contains('style-scope')); |
| assert.isTrue(s.classList.contains('x-scoped')); |
| s.setAttribute('class', 'foo'); |
| assert.isTrue(s.classList.contains('foo')); |
| assert.isTrue(s.classList.contains('style-scope')); |
| assert.isTrue(s.classList.contains('x-scoped')); |
| // |
| assert.isTrue(scope.classList.contains('style-scope')); |
| assert.isTrue(scope.classList.contains('x-scope-class')); |
| scope.setAttribute('class', 'foo'); |
| assert.isTrue(scope.classList.contains('foo')); |
| assert.isTrue(scope.classList.contains('style-scope')); |
| assert.isTrue(scope.classList.contains('x-scope-class')); |
| }); |
| |
| test('specificity of :host selector with class', function() { |
| var e1 = document.createElement('x-specificity'); |
| document.body.appendChild(e1); |
| flush(); |
| assertComputed(e1, '1px'); |
| document.body.removeChild(e1); |
| var e2 = document.createElement('x-specificity'); |
| e2.setAttribute('class', 'bar'); |
| document.body.appendChild(e2); |
| flush(); |
| assertComputed(e2, '2px'); |
| document.body.removeChild(e2); |
| }); |
| |
| test('self-use is supported', function() { |
| makeElement('self-test'); |
| var e = document.createElement('self-test'); |
| document.body.appendChild(e); |
| flush(); |
| assertComputed(e.shadowRoot.querySelector('a'), '10px'); |
| document.body.removeChild(e); |
| }); |
| |
| test('nth-child selectors work correctly with plusses', function() { |
| makeElement('nth-plus-one'); |
| var e = document.createElement('nth-plus-one'); |
| document.body.appendChild(e); |
| flush(); |
| assertComputed(e.shadowRoot.querySelector('.foo'), 'rgb(255, 0, 0)', 'color'); |
| document.body.removeChild(e); |
| }); |
| |
| test(':-webkit-any and :-moz-any selectors are supported', function() { |
| if (navigator.userAgent.match(/Trident|Edge/)) { |
| this.skip(); |
| } |
| makeElement('any-selector'); |
| var e = document.createElement('any-selector'); |
| document.body.appendChild(e); |
| flush(); |
| assertComputed(e.shadowRoot.querySelector('div'), 'rgb(123, 123, 123)', 'color'); |
| assertComputed(e.shadowRoot.querySelector('span'), 'rgb(123, 123, 123)', 'color'); |
| document.body.removeChild(e); |
| }); |
| |
| test(':host() sets mixin definitions correctly', function() { |
| makeElement('bad-mixin'); |
| var e = document.createElement('bad-mixin'); |
| document.body.appendChild(e); |
| flush(); |
| assertComputed(e.shadowRoot.querySelector('div'), '0px'); |
| document.body.removeChild(e); |
| }); |
| |
| test('trees with elements missing styles render correctly', function() { |
| makeElement('x-parent-skip'); |
| makeElement('x-skip'); |
| makeElement('x-child-skip'); |
| const p = document.createElement('x-parent-skip'); |
| document.body.appendChild(p); |
| flush(); |
| const inner = p.shadowRoot.querySelector('x-skip').shadowRoot.querySelector('x-child-skip').shadowRoot.querySelector('div'); |
| assertComputed(inner, '10px'); |
| document.body.removeChild(p); |
| }); |
| |
| test('trees with elements missing templates render correctly', function() { |
| makeElement('no-shadow'); |
| const p = document.createElement('x-parent-skip'); |
| const n = document.createElement('no-shadow'); |
| const c = document.createElement('x-child-skip'); |
| document.body.appendChild(p); |
| p.shadowRoot.appendChild(n); |
| n.shadowRoot.appendChild(c); |
| flush(); |
| const inner = c.shadowRoot.querySelector('div'); |
| assertComputed(inner, '10px'); |
| document.body.removeChild(p); |
| }) |
| |
| }); |
| |
| suite('unscoping', function() { |
| suiteSetup(function() { |
| makeElement('shady-unscoped'); |
| }); |
| test('styles with "shady-unscoped" attr work in Shady and Shadow', function() { |
| var el = document.createElement('shady-unscoped'); |
| document.body.appendChild(el); |
| flush(); |
| var div = el.shadowRoot.querySelector('div'); |
| assertComputed(div, 'rgb(255, 0, 0)', 'color'); |
| document.body.removeChild(el); |
| }); |
| test('styles with "shady-unscoped" attr deduplicate', function(){ |
| if (window.ShadyCSS.nativeShadow) { |
| this.skip(); |
| } |
| makeElement('shady-unscoped-2'); |
| var el1 = document.createElement('shady-unscoped'); |
| var el2 = document.createElement('shady-unscoped-2'); |
| document.body.appendChild(el1); |
| document.body.appendChild(el2); |
| flush(); |
| assert.equal(document.querySelectorAll('style[shady-unscoped]').length, 1); |
| document.body.removeChild(el1); |
| document.body.removeChild(el2); |
| }); |
| test('@apply does not work in shady-unscoped', function() { |
| makeElement('unscoped-apply-user'); |
| makeElement('unscoped-apply'); |
| var el = document.createElement('unscoped-apply'); |
| document.body.appendChild(el); |
| flush(); |
| var inner = el.shadowRoot.querySelector('unscoped-apply-user'); |
| var target = inner.shadowRoot.querySelector('div'); |
| assertComputed(target, '0px'); |
| document.body.removeChild(el); |
| }); |
| }); |
| |
| })(); |
| </script> |
| </body> |
| </html> |