326 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			326 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| import {
 | |
|   ddescribe,
 | |
|   describe,
 | |
|   xdescribe,
 | |
|   it,
 | |
|   iit,
 | |
|   xit,
 | |
|   expect,
 | |
|   beforeEach,
 | |
|   afterEach,
 | |
|   AsyncTestCompleter,
 | |
|   inject,
 | |
|   beforeEachBindings
 | |
| } from 'angular2/testing_internal';
 | |
| import {provide} from 'angular2/src/core/di';
 | |
| import {SpyXHR} from './spies';
 | |
| import {XHR} from 'angular2/src/compiler/xhr';
 | |
| import {BaseException, WrappedException} from 'angular2/src/facade/exceptions';
 | |
| 
 | |
| import {CONST_EXPR, isPresent, isBlank, StringWrapper, isArray} from 'angular2/src/facade/lang';
 | |
| import {PromiseWrapper, Promise} from 'angular2/src/facade/async';
 | |
| import {evalModule} from './eval_module';
 | |
| import {StyleCompiler} from 'angular2/src/compiler/style_compiler';
 | |
| import {
 | |
|   CompileDirectiveMetadata,
 | |
|   CompileTemplateMetadata,
 | |
|   CompileTypeMetadata
 | |
| } from 'angular2/src/compiler/directive_metadata';
 | |
| import {SourceExpression, SourceModule} from 'angular2/src/compiler/source_module';
 | |
| import {ViewEncapsulation} from 'angular2/src/core/metadata/view';
 | |
| import {TEST_PROVIDERS} from './test_bindings';
 | |
| import {codeGenValueFn, codeGenExportVariable, MODULE_SUFFIX} from 'angular2/src/compiler/util';
 | |
| 
 | |
| // Attention: These module names have to correspond to real modules!
 | |
| var MODULE_URL = `package:angular2/test/compiler/style_compiler_spec${MODULE_SUFFIX}`;
 | |
| var IMPORT_ABS_STYLESHEET_URL = `package:angular2/test/compiler/style_compiler_import.css`;
 | |
| var IMPORT_REL_STYLESHEET_URL = './style_compiler_import.css';
 | |
| // Note: Not a real module, only used via mocks.
 | |
| var IMPORT_ABS_STYLESHEET_URL_WITH_IMPORT =
 | |
|     `package:angular2/test/compiler/style_compiler_transitive_import.css`;
 | |
| 
 | |
| export function main() {
 | |
|   describe('StyleCompiler', () => {
 | |
|     var xhr: SpyXHR;
 | |
| 
 | |
|     beforeEachBindings(() => {
 | |
|       xhr = <any>new SpyXHR();
 | |
|       return [TEST_PROVIDERS, provide(XHR, {useValue: xhr})];
 | |
|     });
 | |
| 
 | |
|     var compiler: StyleCompiler;
 | |
| 
 | |
|     beforeEach(inject([StyleCompiler], (_compiler) => { compiler = _compiler; }));
 | |
| 
 | |
|     describe('compileComponentRuntime', () => {
 | |
|       var xhrUrlResults;
 | |
|       var xhrCount;
 | |
| 
 | |
|       beforeEach(() => {
 | |
|         xhrCount = 0;
 | |
|         xhrUrlResults = {};
 | |
|         xhrUrlResults[IMPORT_ABS_STYLESHEET_URL] = 'span {color: blue}';
 | |
|         xhrUrlResults[IMPORT_ABS_STYLESHEET_URL_WITH_IMPORT] =
 | |
|             `a {color: green}@import ${IMPORT_REL_STYLESHEET_URL};`;
 | |
|       });
 | |
| 
 | |
|       function compile(styles: string[], styleAbsUrls: string[],
 | |
|                        encapsulation: ViewEncapsulation): Promise<string[]> {
 | |
|         // Note: Can't use MockXHR as the xhr is called recursively,
 | |
|         // so we can't trigger flush.
 | |
|         xhr.spy('get').andCallFake((url) => {
 | |
|           var response = xhrUrlResults[url];
 | |
|           xhrCount++;
 | |
|           if (isBlank(response)) {
 | |
|             throw new BaseException(`Unexpected url ${url}`);
 | |
|           }
 | |
|           return PromiseWrapper.resolve(response);
 | |
|         });
 | |
|         return compiler.compileComponentRuntime(new CompileTemplateMetadata(
 | |
|             {styles: styles, styleUrls: styleAbsUrls, encapsulation: encapsulation}));
 | |
|       }
 | |
| 
 | |
|       describe('no shim', () => {
 | |
|         var encapsulation = ViewEncapsulation.None;
 | |
| 
 | |
|         it('should compile plain css rules', inject([AsyncTestCompleter], (async) => {
 | |
|              compile(['div {color: red}', 'span {color: blue}'], [], encapsulation)
 | |
|                  .then(styles => {
 | |
|                    expect(styles).toEqual(['div {color: red}', 'span {color: blue}']);
 | |
|                    async.done();
 | |
|                  });
 | |
|            }));
 | |
| 
 | |
|         it('should allow to import rules', inject([AsyncTestCompleter], (async) => {
 | |
|              compile(['div {color: red}'], [IMPORT_ABS_STYLESHEET_URL], encapsulation)
 | |
|                  .then(styles => {
 | |
|                    expect(styles).toEqual(['div {color: red}', ['span {color: blue}']]);
 | |
|                    async.done();
 | |
|                  });
 | |
|            }));
 | |
| 
 | |
|         it('should allow to import rules transitively', inject([AsyncTestCompleter], (async) => {
 | |
|              compile(['div {color: red}'], [IMPORT_ABS_STYLESHEET_URL_WITH_IMPORT], encapsulation)
 | |
|                  .then(styles => {
 | |
|                    expect(styles)
 | |
|                        .toEqual(['div {color: red}', ['a {color: green}', ['span {color: blue}']]]);
 | |
|                    async.done();
 | |
|                  });
 | |
|            }));
 | |
|       });
 | |
| 
 | |
|       describe('with shim', () => {
 | |
|         var encapsulation = ViewEncapsulation.Emulated;
 | |
| 
 | |
|         it('should compile plain css rules', inject([AsyncTestCompleter], (async) => {
 | |
|              compile(['div {\ncolor: red;\n}', 'span {\ncolor: blue;\n}'], [], encapsulation)
 | |
|                  .then(styles => {
 | |
|                    compareStyles(styles, [
 | |
|                      'div[_ngcontent-%COMP%] {\ncolor: red;\n}',
 | |
|                      'span[_ngcontent-%COMP%] {\ncolor: blue;\n}'
 | |
|                    ]);
 | |
|                    async.done();
 | |
|                  });
 | |
|            }));
 | |
| 
 | |
|         it('should allow to import rules', inject([AsyncTestCompleter], (async) => {
 | |
|              compile(['div {\ncolor: red;\n}'], [IMPORT_ABS_STYLESHEET_URL], encapsulation)
 | |
|                  .then(styles => {
 | |
|                    compareStyles(styles, [
 | |
|                      'div[_ngcontent-%COMP%] {\ncolor: red;\n}',
 | |
|                      ['span[_ngcontent-%COMP%] {color: blue}']
 | |
|                    ]);
 | |
|                    async.done();
 | |
|                  });
 | |
|            }));
 | |
| 
 | |
|         it('should allow to import rules transitively', inject([AsyncTestCompleter], (async) => {
 | |
|              compile(['div {\ncolor: red;\n}'], [IMPORT_ABS_STYLESHEET_URL_WITH_IMPORT],
 | |
|                      encapsulation)
 | |
|                  .then(styles => {
 | |
|                    compareStyles(styles, [
 | |
|                      'div[_ngcontent-%COMP%] {\ncolor: red;\n}',
 | |
|                      [
 | |
|                        'a[_ngcontent-%COMP%] {color: green}',
 | |
|                        ['span[_ngcontent-%COMP%] {color: blue}']
 | |
|                      ]
 | |
|                    ]);
 | |
|                    async.done();
 | |
|                  });
 | |
|            }));
 | |
|       });
 | |
| 
 | |
|       it('should cache stylesheets for parallel requests', inject([AsyncTestCompleter], (async) => {
 | |
|            PromiseWrapper.all([
 | |
|                            compile([], [IMPORT_ABS_STYLESHEET_URL], ViewEncapsulation.None),
 | |
|                            compile([], [IMPORT_ABS_STYLESHEET_URL], ViewEncapsulation.None)
 | |
|                          ])
 | |
|                .then((styleArrays) => {
 | |
|                  expect(styleArrays[0]).toEqual([['span {color: blue}']]);
 | |
|                  expect(styleArrays[1]).toEqual([['span {color: blue}']]);
 | |
|                  expect(xhrCount).toBe(1);
 | |
|                  async.done();
 | |
|                });
 | |
|          }));
 | |
| 
 | |
|       it('should cache stylesheets for serial requests', inject([AsyncTestCompleter], (async) => {
 | |
|            compile([], [IMPORT_ABS_STYLESHEET_URL], ViewEncapsulation.None)
 | |
|                .then((styles0) => {
 | |
|                  xhrUrlResults[IMPORT_ABS_STYLESHEET_URL] = 'span {color: black}';
 | |
|                  return compile([], [IMPORT_ABS_STYLESHEET_URL], ViewEncapsulation.None)
 | |
|                      .then((styles1) => {
 | |
|                        expect(styles0).toEqual([['span {color: blue}']]);
 | |
|                        expect(styles1).toEqual([['span {color: blue}']]);
 | |
|                        expect(xhrCount).toBe(1);
 | |
|                        async.done();
 | |
|                      });
 | |
|                });
 | |
|          }));
 | |
| 
 | |
|       it('should allow to clear the cache', inject([AsyncTestCompleter], (async) => {
 | |
|            compile([], [IMPORT_ABS_STYLESHEET_URL], ViewEncapsulation.None)
 | |
|                .then((_) => {
 | |
|                  compiler.clearCache();
 | |
|                  xhrUrlResults[IMPORT_ABS_STYLESHEET_URL] = 'span {color: black}';
 | |
|                  return compile([], [IMPORT_ABS_STYLESHEET_URL], ViewEncapsulation.None);
 | |
|                })
 | |
|                .then((styles) => {
 | |
|                  expect(xhrCount).toBe(2);
 | |
|                  expect(styles).toEqual([['span {color: black}']]);
 | |
|                  async.done();
 | |
|                });
 | |
|          }));
 | |
|     });
 | |
| 
 | |
|     describe('compileComponentCodeGen', () => {
 | |
|       function compile(styles: string[], styleAbsUrls: string[],
 | |
|                        encapsulation: ViewEncapsulation): Promise<string[]> {
 | |
|         var sourceExpression = compiler.compileComponentCodeGen(new CompileTemplateMetadata(
 | |
|             {styles: styles, styleUrls: styleAbsUrls, encapsulation: encapsulation}));
 | |
|         var sourceWithImports = testableExpression(sourceExpression).getSourceWithImports();
 | |
|         return evalModule(sourceWithImports.source, sourceWithImports.imports, null);
 | |
|       };
 | |
| 
 | |
|       describe('no shim', () => {
 | |
|         var encapsulation = ViewEncapsulation.None;
 | |
| 
 | |
|         it('should compile plain css rules', inject([AsyncTestCompleter], (async) => {
 | |
|              compile(['div {color: red}', 'span {color: blue}'], [], encapsulation)
 | |
|                  .then(styles => {
 | |
|                    expect(styles).toEqual(['div {color: red}', 'span {color: blue}']);
 | |
|                    async.done();
 | |
|                  });
 | |
|            }));
 | |
| 
 | |
|         it('should compile css rules with newlines and quotes',
 | |
|            inject([AsyncTestCompleter], (async) => {
 | |
|              compile(['div\n{"color": \'red\'}'], [], encapsulation)
 | |
|                  .then(styles => {
 | |
|                    expect(styles).toEqual(['div\n{"color": \'red\'}']);
 | |
|                    async.done();
 | |
|                  });
 | |
|            }));
 | |
| 
 | |
|         it('should allow to import rules', inject([AsyncTestCompleter], (async) => {
 | |
|              compile(['div {color: red}'], [IMPORT_ABS_STYLESHEET_URL], encapsulation)
 | |
|                  .then(styles => {
 | |
|                    expect(styles).toEqual(['div {color: red}', ['span {color: blue}']]);
 | |
|                    async.done();
 | |
|                  });
 | |
|            }), 1000);
 | |
|       });
 | |
| 
 | |
|       describe('with shim', () => {
 | |
|         var encapsulation = ViewEncapsulation.Emulated;
 | |
| 
 | |
|         it('should compile plain css ruless', inject([AsyncTestCompleter], (async) => {
 | |
|              compile(['div {\ncolor: red;\n}', 'span {\ncolor: blue;\n}'], [], encapsulation)
 | |
|                  .then(styles => {
 | |
|                    compareStyles(styles, [
 | |
|                      'div[_ngcontent-%COMP%] {\ncolor: red;\n}',
 | |
|                      'span[_ngcontent-%COMP%] {\ncolor: blue;\n}'
 | |
|                    ]);
 | |
|                    async.done();
 | |
|                  });
 | |
|            }));
 | |
| 
 | |
|         it('should allow to import rules', inject([AsyncTestCompleter], (async) => {
 | |
|              compile(['div {color: red}'], [IMPORT_ABS_STYLESHEET_URL], encapsulation)
 | |
|                  .then(styles => {
 | |
|                    compareStyles(styles, [
 | |
|                      'div[_ngcontent-%COMP%] {color: red}',
 | |
|                      ['span[_ngcontent-%COMP%] {\ncolor: blue;\n}']
 | |
|                    ]);
 | |
|                    async.done();
 | |
|                  });
 | |
|            }), 1000);
 | |
|       });
 | |
|     });
 | |
| 
 | |
|     describe('compileStylesheetCodeGen', () => {
 | |
|       function compile(style: string): Promise<string[][]> {
 | |
|         var sourceModules = compiler.compileStylesheetCodeGen(MODULE_URL, style);
 | |
|         return PromiseWrapper.all(sourceModules.map(sourceModule => {
 | |
|           var sourceWithImports = testableModule(sourceModule).getSourceWithImports();
 | |
|           return evalModule(sourceWithImports.source, sourceWithImports.imports, null);
 | |
|         }));
 | |
|       }
 | |
| 
 | |
|       it('should compile plain css rules', inject([AsyncTestCompleter], (async) => {
 | |
|            compile('div {color: red;}')
 | |
|                .then(stylesAndShimStyles => {
 | |
|                  var expected = [['div {color: red;}'], ['div[_ngcontent-%COMP%] {color: red;}']];
 | |
|                  compareStyles(stylesAndShimStyles[0], expected[0]);
 | |
|                  compareStyles(stylesAndShimStyles[1], expected[1]);
 | |
|                  async.done();
 | |
|                });
 | |
|          }));
 | |
| 
 | |
|       it('should allow to import rules with relative paths',
 | |
|          inject([AsyncTestCompleter], (async) => {
 | |
|            compile(`div {color: red}@import ${IMPORT_REL_STYLESHEET_URL};`)
 | |
|                .then(stylesAndShimStyles => {
 | |
|                  var expected = [
 | |
|                    ['div {color: red}', ['span {color: blue}']],
 | |
|                    [
 | |
|                      'div[_ngcontent-%COMP%] {color: red}',
 | |
|                      ['span[_ngcontent-%COMP%] {\ncolor: blue;\n}']
 | |
|                    ]
 | |
|                  ];
 | |
|                  compareStyles(stylesAndShimStyles[0], expected[0]);
 | |
|                  compareStyles(stylesAndShimStyles[1], expected[1]);
 | |
|                  async.done();
 | |
|                });
 | |
|          }));
 | |
|     });
 | |
|   });
 | |
| }
 | |
| 
 | |
| 
 | |
| function testableExpression(source: SourceExpression): SourceModule {
 | |
|   var testableSource = `${source.declarations.join('\n')}
 | |
|   ${codeGenValueFn(['_'], source.expression, '_run')};
 | |
|   ${codeGenExportVariable('run')}_run;`;
 | |
|   return new SourceModule(null, testableSource);
 | |
| }
 | |
| 
 | |
| function testableModule(sourceModule: SourceModule): SourceModule {
 | |
|   var testableSource = `${sourceModule.sourceWithModuleRefs}
 | |
|   ${codeGenValueFn(['_'], 'STYLES', '_run')};
 | |
|   ${codeGenExportVariable('run')}_run;`;
 | |
|   return new SourceModule(sourceModule.moduleUrl, testableSource);
 | |
| }
 | |
| 
 | |
| // Needed for Android browsers which add an extra space at the end of some lines
 | |
| function compareStyles(styles: Array<string | any[]>, expectedStyles: Array<string | any[]>) {
 | |
|   expect(styles.length).toEqual(expectedStyles.length);
 | |
|   for (var i = 0; i < styles.length; i++) {
 | |
|     var style = styles[i];
 | |
|     if (isArray(style)) {
 | |
|       compareStyles(<any[]>style, <any[]>expectedStyles[i]);
 | |
|     } else {
 | |
|       expect(StringWrapper.replaceAll(<string>style, /\s+\n/g, '\n')).toEqual(expectedStyles[i]);
 | |
|     }
 | |
|   }
 | |
| }
 |