1<!doctype html>
2<!--
3@license
4Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
5This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
6The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
7The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
8Code distributed by Google as part of the polymer project is also
9subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
10-->
11<html>
12<head>
13
14  <meta charset="utf-8">
15  <script>
16  WCT = {waitFor: function (cb) {HTMLImports.whenReady(cb)}}
17  </script>
18  <script src="./test-flags.js"></script>
19  <script src="../node_modules/wct-browser-legacy/browser.js"></script>
20  <script src="../node_modules/@webcomponents/webcomponents-platform/webcomponents-platform.js"></script>
21  <script src="../node_modules/es6-promise/dist/es6-promise.auto.min.js"></script>
22  <script src="../node_modules/@webcomponents/template/template.js"></script>
23  <script src="../node_modules/@webcomponents/html-imports/html-imports.min.js"></script>
24  <script src="../node_modules/@webcomponents/shadydom/shadydom.min.js"></script>
25  <script src="../node_modules/@webcomponents/custom-elements/custom-elements.min.js"></script>
26  <script src="../scoping-shim.min.js"></script>
27  <script src="../apply-shim.min.js"></script>
28  <script>
29  if (customElements.polyfillWrapFlushCallback) {
30    // delay definition of custom-style until after template polyfill loads
31    customElements.polyfillWrapFlushCallback(function(cb) {
32      HTMLImports.whenReady(cb);
33    });
34  }
35  </script>
36  <script src="../custom-style-interface.min.js"></script>
37  <script src="module/generated/custom-style-element.js"></script>
38  <script src="module/generated/make-element.js"></script>
39  <title>Apply Shim</title>
40
41</head>
42<body>
43  <template id="basic">
44    <style>
45    :host {
46      --mixin: {
47        border: 2px solid black;
48      };
49    }
50    div {
51      @apply --mixin;
52    }
53    </style>
54  </template>
55
56  <template id="defaults">
57    <style>
58    :host {
59      --mixin: {
60        border: 2px solid black;
61      }
62    }
63    div {
64      border: 1px dotted orange;
65      @apply --mixin;
66    }
67    span {
68      border: inherit;
69      @apply --mixin;
70    }
71    span {
72      border: initial;
73      @apply --mixin;
74    }
75    </style>
76  </template>
77
78  <template id="override">
79    <style>
80    :host {
81      --override: {
82        padding: 2px;
83      };
84    }
85    :host([override]) {
86      --override: {
87        border: 2px solid black;
88      };
89    }
90    div {
91      @apply --override;
92    }
93    </style>
94  </template>
95
96  <template id="override-with-property">
97    <style>
98    :root {
99      --prop-mixin: {
100        border: 2px solid black;
101      };
102    }
103    x-foo {
104      --prop-mixin: blue;
105      color: var(--prop-mixin);
106    }
107    div {
108      @apply --prop-mixin;
109    }
110    </style>
111  </template>
112
113  <template id="define-with-var">
114    <style>
115    :root {
116      --mixin-var: {
117        border: 2px solid black;
118      };
119    }
120    div {
121      --mixin-var2: var(--mixin-var);
122    }
123    span {
124      --mixin-var: 20px;
125      --variable: var(--mixin-var);
126    }
127    </style>
128  </template>
129
130  <template id="x-element">
131    <style>
132    :host {
133      @apply --my-mixin;
134    }
135    </style>
136  </template>
137
138  <template id="x-element2">
139    <custom-style>
140    <style>
141    html {
142      --my-mixin: {
143        border: 2px solid black;
144      };
145    }
146    </style>
147    </custom-style>
148  </template>
149  <template id="important">
150    <style>
151    :host {
152      --mixin-important: {
153        background-color: white;
154        border: 2px solid black !important;
155        color: white !important;
156      };
157      --mixin: {
158        background-color: red;
159        border: 1px dotted orange;
160        color: black !important;
161      };
162    }
163    div {
164      @apply --mixin-important;
165      @apply --mixin;
166    }
167    </style>
168  </template>
169  <script>
170  suite('Apply Shim', function() {
171    function copy(name) {
172      var template = document.querySelector('template#' + name);
173      return template.content.cloneNode(true);
174    }
175
176    function prep(templateName, elementName) {
177      var style = copy(templateName).querySelector('style');
178      var ast = window.ShadyCSS.ApplyShim.transformStyle(style, elementName);
179      return {style: style, ast: ast};
180    }
181
182    suite('Basic', function() {
183      var style, ast;
184      suiteSetup(function() {
185        var info = prep('basic');
186        style = info.style;
187        ast = info.ast;
188        style.textContent = window.ShadyCSS.ScopingShim.styleAstToString(ast);
189      });
190
191      test('style is transformed', function() {
192        var orig = copy('basic').querySelector('style');
193        assert.notEqual(style.textContent, orig.textContent);
194      });
195
196      test('mixin became custom properties', function() {
197        var definition = ast.rules[0];
198        var application = ast.rules[1];
199        assert.match(definition.cssText, /--mixin_-_border:\s*2px solid black/);
200        assert.match(application.cssText, /border:\s*var\(--mixin_-_border\)/);
201      });
202    });
203    suite('Defaults', function() {
204      var style, ast; // eslint-disable-line no-unused-vars
205      suiteSetup(function() {
206        var info = prep('defaults');
207        style = info.style;
208        ast = info.ast;
209      });
210
211      test('properties defined before mixin are used as defaults', function() {
212        var application = ast.rules[1];
213        assert.match(application.cssText, /border:\s*var\(--mixin_-_border,\s*1px dotted orange\)/);
214      });
215
216      test('inherit and initial default values are preserved', function() {
217        var application = ast.rules[2];
218        assert.match(application.cssText, /border:\s*var\(--mixin_-_border,\s*inherit\)/);
219        application = ast.rules[3];
220        assert.match(application.cssText, /border:\s*var\(--mixin_-_border,\s*initial\)/);
221      });
222    });
223
224    suite('override', function() {
225      var style, ast; // eslint-disable-line no-unused-vars
226      suiteSetup(function() {
227        var info = prep('override');
228        style = info.style;
229        ast = info.ast;
230      });
231
232      test('mixin redefinition sets unused properties to initial', function() {
233        var def1 = ast.rules[0];
234        assert.match(def1.cssText, /--override_-_padding:\s*2px/);
235        var def2 = ast.rules[1];
236        assert.match(def2.cssText, /--override_-_padding:\s*initial/);
237        assert.match(def2.cssText, /--override_-_border:\s*2px solid black/);
238      });
239
240      test('mixin application includes all values', function() {
241        var application = ast.rules[2];
242        assert.match(application.cssText, /padding:\s*var\(--override_-_padding\)/);
243        assert.match(application.cssText, /border:\s*var\(--override_-_border\)/);
244      });
245    });
246
247    suite('override with property', function() {
248      var style, ast; // eslint-disable-line no-unused-vars
249      suiteSetup(function() {
250        var info = prep('override-with-property');
251        style = info.style;
252        ast = info.ast;
253      });
254
255      test('mixin definition defers to property definition', function() {
256        var def = ast.rules[1];
257        assert.notMatch(def.cssText, /border:\s*var\(--prop-mixin_-_border\)/);
258      });
259
260      test('mixin can still be used by other parts of the page', function() {
261        var def = ast.rules[2];
262        assert.match(def.cssText, /border:\s*var\(--prop-mixin_-_border\)/);
263      });
264    });
265
266    suite('define with var()', function() {
267      var style, ast; // eslint-disable-line no-unused-vars
268      suiteSetup(function() {
269        var info = prep('define-with-var');
270        style = info.style;
271        ast = info.ast;
272      });
273
274      test('mixin-var2 is defined with mixin-var\'s values', function() {
275        var def = ast.rules[1];
276        assert.match(def.cssText, /--mixin-var2_-_border:\s*var\(--mixin-var_-_border\)/);
277      });
278
279      test('var usage of mixin is not removed, preserving override functionality', function() {
280        var def = ast.rules[2];
281        assert.match(def.cssText, /--variable:\s*var\(--mixin-var\)/);
282      });
283    });
284
285    suite('invalidation on new definitions', function() {
286      var style, ast, element;
287      suiteSetup(function() {
288        makeElement('x-element');
289        element = document.createElement('x-element');
290        document.body.appendChild(element);
291        style = element.shadowRoot ? element.shadowRoot.querySelector('style') : document.head.querySelector('style[scope=x-element]');
292      });
293
294      test('element initially has no definition', function() {
295        var ast = window.ShadyCSS.ScopingShim._styleInfoForNode(element)._getStyleRules();
296        assert.equal(ast.rules[0].cssText, ';');
297      });
298
299      test('Revalidating Apply Shim on element template fills in properties', function() {
300        var nodes = copy('x-element2');
301        document.body.appendChild(nodes);
302        window.ShadyCSS.styleDocument();
303        var ast = window.ShadyCSS.ScopingShim._styleInfoForNode(element)._getStyleRules();
304        if (window.ShadyCSS.nativeCss) {
305          assert.match(ast.rules[0].cssText, /border:\s*var\(--my-mixin_-_border\)/);
306        } else {
307          assert.match(ast.rules[0].cssText, /border:\s*2px solid black/);
308        }
309      });
310    });
311    suite('!important', function() {
312      var ast;
313      suiteSetup(function() {
314        var info = prep('important');
315        ast = info.ast;
316      });
317
318      test('!important in mixin correctly translates to !important in resulting custom property', function() {
319        var application = ast.rules[1];
320        assert.match(application.cssText, /border:\s*var\(--mixin-important_-_border\)\s*!important/);
321      });
322      test("Fallback of related property without !important is kept without !important in resulting custom property", function() {
323        var application = ast.rules[1];
324        assert.match(application.cssText, /border:\s*var\(--mixin_-_border,\s*var\(--mixin-important_-_border\)\)/);
325      });
326      test('Two mixins with both !important are treated in correct order while also preserving !important in resulting custom property', function () {
327        var application = ast.rules[1];
328        assert.match(application.cssText, /color:\s*var\(--mixin-important_-_color\)\s*!important/);
329        assert.match(application.cssText, /color:\s*var\(--mixin_-_color,\s*var\(--mixin-important_-_color\)\)\s*!important/);
330      })
331      test('Properties without !important in a mixin with !important are treated independently', function() {
332        var application = ast.rules[1];
333        assert.match(application.cssText, /background-color:\s*var\(--mixin_-_background-color,\s*var\(--mixin-important_-_background-color\)\)/);
334        assert.notMatch(application.cssText, /background-color:\s*var\(--mixin_-_background-color,\s*var\(--mixin-important_-_background-color\)\)\s*!important/);
335
336        assert.match(application.cssText, /background-color:\s*var\(--mixin-important_-_background-color\)/);
337        assert.notMatch(application.cssText, /background-color:\s*var\(--mixin-important_-_background-color\)\s*!important/);
338      });
339    });
340  });
341  </script>
342</body>
343</html>
344