2016-06-23 12:47:54 -04:00
|
|
|
/**
|
|
|
|
* @license
|
|
|
|
* Copyright Google Inc. All Rights Reserved.
|
|
|
|
*
|
|
|
|
* Use of this source code is governed by an MIT-style license that can be
|
|
|
|
* found in the LICENSE file at https://angular.io/license
|
|
|
|
*/
|
|
|
|
|
2016-08-02 18:53:34 -04:00
|
|
|
import {CssRule, ShadowCss, processRules} from '@angular/compiler/src/shadow_css';
|
|
|
|
import {beforeEach, ddescribe, describe, expect, iit, it} from '@angular/core/testing/testing_internal';
|
|
|
|
import {normalizeCSS} from '@angular/platform-browser/testing/browser_util';
|
2015-03-23 17:10:55 -04:00
|
|
|
|
2016-08-05 12:50:49 -04:00
|
|
|
import {StringWrapper, isPresent} from '../src/facade/lang';
|
2015-03-23 17:10:55 -04:00
|
|
|
|
|
|
|
export function main() {
|
|
|
|
describe('ShadowCss', function() {
|
|
|
|
|
2015-05-26 12:25:39 -04:00
|
|
|
function s(css: string, contentAttr: string, hostAttr: string = '') {
|
2015-03-23 17:10:55 -04:00
|
|
|
var shadowCss = new ShadowCss();
|
|
|
|
var shim = shadowCss.shimCssText(css, contentAttr, hostAttr);
|
2015-06-23 06:46:38 -04:00
|
|
|
var nlRegexp = /\n/g;
|
2015-05-27 04:22:30 -04:00
|
|
|
return normalizeCSS(StringWrapper.replaceAll(shim, nlRegexp, ''));
|
2015-03-23 17:10:55 -04:00
|
|
|
}
|
|
|
|
|
2015-05-26 12:25:39 -04:00
|
|
|
it('should handle empty string', () => { expect(s('', 'a')).toEqual(''); });
|
2015-03-23 17:10:55 -04:00
|
|
|
|
|
|
|
it('should add an attribute to every rule', () => {
|
|
|
|
var css = 'one {color: red;}two {color: red;}';
|
2015-05-27 04:22:30 -04:00
|
|
|
var expected = 'one[a] {color:red;}two[a] {color:red;}';
|
2015-03-23 17:10:55 -04:00
|
|
|
expect(s(css, 'a')).toEqual(expected);
|
|
|
|
});
|
|
|
|
|
2015-09-29 20:27:44 -04:00
|
|
|
it('should handle invalid css', () => {
|
2015-03-23 17:10:55 -04:00
|
|
|
var css = 'one {color: red;}garbage';
|
2015-10-29 13:57:54 -04:00
|
|
|
var expected = 'one[a] {color:red;}garbage';
|
2015-03-23 17:10:55 -04:00
|
|
|
expect(s(css, 'a')).toEqual(expected);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should add an attribute to every selector', () => {
|
|
|
|
var css = 'one, two {color: red;}';
|
2015-05-27 04:22:30 -04:00
|
|
|
var expected = 'one[a], two[a] {color:red;}';
|
2015-03-23 17:10:55 -04:00
|
|
|
expect(s(css, 'a')).toEqual(expected);
|
|
|
|
});
|
|
|
|
|
2015-10-30 18:09:04 -04:00
|
|
|
it('should support newlines in the selector and content ', () => {
|
|
|
|
var css = 'one, \ntwo {\ncolor: red;}';
|
|
|
|
var expected = 'one[a], two[a] {color:red;}';
|
|
|
|
expect(s(css, 'a')).toEqual(expected);
|
|
|
|
});
|
|
|
|
|
2015-03-23 17:10:55 -04:00
|
|
|
it('should handle media rules', () => {
|
2015-10-30 18:09:04 -04:00
|
|
|
var css = '@media screen and (max-width:800px, max-height:100%) {div {font-size:50px;}}';
|
|
|
|
var expected =
|
|
|
|
'@media screen and (max-width:800px, max-height:100%) {div[a] {font-size:50px;}}';
|
2015-03-23 17:10:55 -04:00
|
|
|
expect(s(css, 'a')).toEqual(expected);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should handle media rules with simple rules', () => {
|
|
|
|
var css = '@media screen and (max-width: 800px) {div {font-size: 50px;}} div {}';
|
2015-10-29 13:57:54 -04:00
|
|
|
var expected = '@media screen and (max-width:800px) {div[a] {font-size:50px;}} div[a] {}';
|
2015-03-23 17:10:55 -04:00
|
|
|
expect(s(css, 'a')).toEqual(expected);
|
|
|
|
});
|
|
|
|
|
2016-04-17 21:18:11 -04:00
|
|
|
it('should handle support rules', () => {
|
|
|
|
var css = '@supports (display: flex) {section {display: flex;}}';
|
|
|
|
var expected = '@supports (display:flex) {section[a] {display:flex;}}';
|
|
|
|
expect(s(css, 'a')).toEqual(expected);
|
|
|
|
});
|
|
|
|
|
2015-06-02 08:59:35 -04:00
|
|
|
// Check that the browser supports unprefixed CSS animation
|
2015-10-29 13:57:54 -04:00
|
|
|
it('should handle keyframes rules', () => {
|
|
|
|
var css = '@keyframes foo {0% {transform:translate(-50%) scaleX(0);}}';
|
|
|
|
expect(s(css, 'a')).toEqual(css);
|
|
|
|
});
|
2015-05-26 06:30:06 -04:00
|
|
|
|
2015-10-29 13:57:54 -04:00
|
|
|
it('should handle -webkit-keyframes rules', () => {
|
|
|
|
var css = '@-webkit-keyframes foo {0% {-webkit-transform:translate(-50%) scaleX(0);}}';
|
|
|
|
expect(s(css, 'a')).toEqual(css);
|
|
|
|
});
|
2015-05-26 06:30:06 -04:00
|
|
|
|
2015-03-23 17:10:55 -04:00
|
|
|
it('should handle complicated selectors', () => {
|
|
|
|
expect(s('one::before {}', 'a')).toEqual('one[a]::before {}');
|
|
|
|
expect(s('one two {}', 'a')).toEqual('one[a] two[a] {}');
|
2015-09-29 20:27:44 -04:00
|
|
|
expect(s('one > two {}', 'a')).toEqual('one[a] > two[a] {}');
|
|
|
|
expect(s('one + two {}', 'a')).toEqual('one[a] + two[a] {}');
|
|
|
|
expect(s('one ~ two {}', 'a')).toEqual('one[a] ~ two[a] {}');
|
2015-05-27 04:22:30 -04:00
|
|
|
var res = s('.one.two > three {}', 'a'); // IE swap classes
|
|
|
|
expect(res == '.one.two[a] > three[a] {}' || res == '.two.one[a] > three[a] {}')
|
|
|
|
.toEqual(true);
|
2015-03-23 17:10:55 -04:00
|
|
|
expect(s('one[attr="value"] {}', 'a')).toEqual('one[attr="value"][a] {}');
|
|
|
|
expect(s('one[attr=value] {}', 'a')).toEqual('one[attr="value"][a] {}');
|
|
|
|
expect(s('one[attr^="value"] {}', 'a')).toEqual('one[attr^="value"][a] {}');
|
|
|
|
expect(s('one[attr$="value"] {}', 'a')).toEqual('one[attr$="value"][a] {}');
|
|
|
|
expect(s('one[attr*="value"] {}', 'a')).toEqual('one[attr*="value"][a] {}');
|
|
|
|
expect(s('one[attr|="value"] {}', 'a')).toEqual('one[attr|="value"][a] {}');
|
|
|
|
expect(s('one[attr] {}', 'a')).toEqual('one[attr][a] {}');
|
|
|
|
expect(s('[is="one"] {}', 'a')).toEqual('[is="one"][a] {}');
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should handle :host', () => {
|
|
|
|
expect(s(':host {}', 'a', 'a-host')).toEqual('[a-host] {}');
|
|
|
|
expect(s(':host(.x,.y) {}', 'a', 'a-host')).toEqual('[a-host].x, [a-host].y {}');
|
2015-05-26 12:25:39 -04:00
|
|
|
expect(s(':host(.x,.y) > .z {}', 'a', 'a-host'))
|
|
|
|
.toEqual('[a-host].x > .z, [a-host].y > .z {}');
|
2015-03-23 17:10:55 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
it('should handle :host-context', () => {
|
|
|
|
expect(s(':host-context(.x) {}', 'a', 'a-host')).toEqual('[a-host].x, .x [a-host] {}');
|
2015-05-26 12:25:39 -04:00
|
|
|
expect(s(':host-context(.x) > .y {}', 'a', 'a-host'))
|
|
|
|
.toEqual('[a-host].x > .y, .x [a-host] > .y {}');
|
2015-03-23 17:10:55 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
it('should support polyfill-next-selector', () => {
|
2016-06-08 19:38:52 -04:00
|
|
|
var css = s('polyfill-next-selector {content: \'x > y\'} z {}', 'a');
|
2015-10-29 13:57:54 -04:00
|
|
|
expect(css).toEqual('x[a] > y[a]{}');
|
2015-03-23 17:10:55 -04:00
|
|
|
|
|
|
|
css = s('polyfill-next-selector {content: "x > y"} z {}', 'a');
|
2015-10-29 13:57:54 -04:00
|
|
|
expect(css).toEqual('x[a] > y[a]{}');
|
2015-03-23 17:10:55 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
it('should support polyfill-unscoped-rule', () => {
|
2016-06-08 19:38:52 -04:00
|
|
|
var css = s('polyfill-unscoped-rule {content: \'#menu > .bar\';color: blue;}', 'a');
|
2015-05-27 04:22:30 -04:00
|
|
|
expect(StringWrapper.contains(css, '#menu > .bar {;color:blue;}')).toBeTruthy();
|
2015-03-23 17:10:55 -04:00
|
|
|
|
2015-05-21 11:12:10 -04:00
|
|
|
css = s('polyfill-unscoped-rule {content: "#menu > .bar";color: blue;}', 'a');
|
2015-05-27 04:22:30 -04:00
|
|
|
expect(StringWrapper.contains(css, '#menu > .bar {;color:blue;}')).toBeTruthy();
|
2015-03-23 17:10:55 -04:00
|
|
|
});
|
|
|
|
|
2015-07-10 13:38:24 -04:00
|
|
|
it('should support multiple instances polyfill-unscoped-rule', () => {
|
2016-06-08 19:38:52 -04:00
|
|
|
var css =
|
|
|
|
s('polyfill-unscoped-rule {content: \'foo\';color: blue;}' +
|
|
|
|
'polyfill-unscoped-rule {content: \'bar\';color: blue;}',
|
|
|
|
'a');
|
2015-07-10 13:38:24 -04:00
|
|
|
expect(StringWrapper.contains(css, 'foo {;color:blue;}')).toBeTruthy();
|
|
|
|
expect(StringWrapper.contains(css, 'bar {;color:blue;}')).toBeTruthy();
|
|
|
|
});
|
|
|
|
|
2015-03-23 17:10:55 -04:00
|
|
|
it('should support polyfill-rule', () => {
|
2016-06-08 19:38:52 -04:00
|
|
|
var css = s('polyfill-rule {content: \':host.foo .bar\';color: blue;}', 'a', 'a-host');
|
2015-10-29 13:57:54 -04:00
|
|
|
expect(css).toEqual('[a-host].foo .bar {;color:blue;}');
|
2015-03-23 17:10:55 -04:00
|
|
|
|
2015-05-27 04:22:30 -04:00
|
|
|
css = s('polyfill-rule {content: ":host.foo .bar";color:blue;}', 'a', 'a-host');
|
2015-10-29 13:57:54 -04:00
|
|
|
expect(css).toEqual('[a-host].foo .bar {;color:blue;}');
|
2015-03-23 17:10:55 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
it('should handle ::shadow', () => {
|
|
|
|
var css = s('x::shadow > y {}', 'a');
|
|
|
|
expect(css).toEqual('x[a] > y[a] {}');
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should handle /deep/', () => {
|
|
|
|
var css = s('x /deep/ y {}', 'a');
|
2016-03-11 16:35:28 -05:00
|
|
|
expect(css).toEqual('x[a] y {}');
|
2015-03-23 17:10:55 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
it('should handle >>>', () => {
|
|
|
|
var css = s('x >>> y {}', 'a');
|
2016-03-11 16:35:28 -05:00
|
|
|
expect(css).toEqual('x[a] y {}');
|
2015-03-23 17:10:55 -04:00
|
|
|
});
|
2015-10-07 16:37:56 -04:00
|
|
|
|
2015-10-29 13:57:54 -04:00
|
|
|
it('should pass through @import directives', () => {
|
|
|
|
var styleStr = '@import url("https://fonts.googleapis.com/css?family=Roboto");';
|
|
|
|
var css = s(styleStr, 'a');
|
|
|
|
expect(css).toEqual(styleStr);
|
|
|
|
});
|
2015-10-15 15:15:42 -04:00
|
|
|
|
2015-10-29 13:57:54 -04:00
|
|
|
it('should shim rules after @import', () => {
|
|
|
|
var styleStr = '@import url("a"); div {}';
|
|
|
|
var css = s(styleStr, 'a');
|
|
|
|
expect(css).toEqual('@import url("a"); div[a] {}');
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should leave calc() unchanged', () => {
|
|
|
|
var styleStr = 'div {height:calc(100% - 55px);}';
|
|
|
|
var css = s(styleStr, 'a');
|
|
|
|
expect(css).toEqual('div[a] {height:calc(100% - 55px);}');
|
|
|
|
});
|
|
|
|
|
2015-10-30 18:09:04 -04:00
|
|
|
it('should strip comments', () => { expect(s('/* x */b {c}', 'a')).toEqual('b[a] {c}'); });
|
2015-10-29 13:57:54 -04:00
|
|
|
|
2015-10-30 18:09:04 -04:00
|
|
|
it('should ignore special characters in comments',
|
|
|
|
() => { expect(s('/* {;, */b {c}', 'a')).toEqual('b[a] {c}'); });
|
2015-10-29 13:57:54 -04:00
|
|
|
|
2015-10-30 18:09:04 -04:00
|
|
|
it('should support multiline comments',
|
|
|
|
() => { expect(s('/* \n */b {c}', 'a')).toEqual('b[a] {c}'); });
|
2016-08-12 04:39:43 -04:00
|
|
|
|
|
|
|
it('should keep sourceMappingURL comments', () => {
|
|
|
|
expect(s('b {c}/*# sourceMappingURL=data:x */', 'a'))
|
|
|
|
.toEqual('b[a] {c}/*# sourceMappingURL=data:x */');
|
|
|
|
expect(s('b {c}/* #sourceMappingURL=data:x */', 'a'))
|
|
|
|
.toEqual('b[a] {c}/* #sourceMappingURL=data:x */');
|
|
|
|
});
|
2015-10-30 18:09:04 -04:00
|
|
|
});
|
2015-10-29 13:57:54 -04:00
|
|
|
|
2015-10-30 18:09:04 -04:00
|
|
|
describe('processRules', () => {
|
|
|
|
describe('parse rules', () => {
|
|
|
|
function captureRules(input: string): CssRule[] {
|
2016-06-08 18:45:15 -04:00
|
|
|
var result: any[] /** TODO #9100 */ = [];
|
|
|
|
processRules(input, (cssRule: any /** TODO #9100 */) => {
|
2015-10-30 18:09:04 -04:00
|
|
|
result.push(cssRule);
|
|
|
|
return cssRule;
|
|
|
|
});
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
it('should work with empty css', () => { expect(captureRules('')).toEqual([]); });
|
|
|
|
|
|
|
|
it('should capture a rule without body',
|
|
|
|
() => { expect(captureRules('a;')).toEqual([new CssRule('a', '')]); });
|
|
|
|
|
|
|
|
it('should capture css rules with body',
|
|
|
|
() => { expect(captureRules('a {b}')).toEqual([new CssRule('a', 'b')]); });
|
|
|
|
|
|
|
|
it('should capture css rules with nested rules', () => {
|
2016-06-08 19:38:52 -04:00
|
|
|
expect(captureRules('a {b {c}} d {e}')).toEqual([
|
|
|
|
new CssRule('a', 'b {c}'), new CssRule('d', 'e')
|
|
|
|
]);
|
2015-10-30 18:09:04 -04:00
|
|
|
});
|
|
|
|
|
2015-12-16 02:47:48 -05:00
|
|
|
it('should capture multiple rules where some have no body', () => {
|
2016-06-08 19:38:52 -04:00
|
|
|
expect(captureRules('@import a ; b {c}')).toEqual([
|
|
|
|
new CssRule('@import a', ''), new CssRule('b', 'c')
|
|
|
|
]);
|
2015-10-30 18:09:04 -04:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('modify rules', () => {
|
|
|
|
it('should allow to change the selector while preserving whitespaces', () => {
|
2016-06-08 19:38:52 -04:00
|
|
|
expect(processRules(
|
|
|
|
'@import a; b {c {d}} e {f}', (cssRule: any /** TODO #9100 */) => new CssRule(
|
|
|
|
cssRule.selector + '2', cssRule.content)))
|
2015-10-30 18:09:04 -04:00
|
|
|
.toEqual('@import a2; b2 {c {d}} e2 {f}');
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should allow to change the content', () => {
|
2016-06-08 19:38:52 -04:00
|
|
|
expect(processRules(
|
|
|
|
'a {b}', (cssRule: any /** TODO #9100 */) =>
|
|
|
|
new CssRule(cssRule.selector, cssRule.content + '2')))
|
2015-10-30 18:09:04 -04:00
|
|
|
.toEqual('a {b2}');
|
|
|
|
});
|
2015-10-29 13:57:54 -04:00
|
|
|
});
|
2015-03-23 17:10:55 -04:00
|
|
|
});
|
|
|
|
}
|