refactor(compiler): use the new compiler everywhere

Closes #3605

BREAKING CHANGE:
- we don't mark an element as bound any more if it only contains text bindings
  E.g. <div>{{hello}}</div>
  This changes the indices when using `DebugElement.componentViewChildren` / `DebugElement.children`.
- `@Directive.compileChildren` was removed,
  `ng-non-bindable` is now builtin and not a directive any more
- angular no more adds the `ng-binding` class to elements with bindings
- directives are now ordered as they are listed in the View.directives regarding change detection.
  Previously they had an undefined order.
- the `Renderer` interface has new methods `createProtoView` and `registerComponentTemplate`. See `DomRenderer` for default implementations.
- reprojection with `ng-content` is now all or nothing per `ng-content` element
- angular2 transformer can't be used in tests that modify directive metadata.
  Use `angular2/src/transform/inliner_for_test` transformer instead.
This commit is contained in:
Tobias Bosch
2015-10-01 10:07:49 -07:00
parent 30ca0434a2
commit 76247b7097
124 changed files with 2013 additions and 3451 deletions

View File

@ -33,7 +33,7 @@ import {
} from 'angular2/src/core/change_detection/change_detection';
import {Pipes} from 'angular2/src/core/change_detection/pipes';
import {createChangeDetectorDefinitions} from 'angular2/src/compiler/change_definition_factory';
import {TestContext, TestDirective, TestDispatcher, TestPipes} from './change_detector_mocks';
import {TestDirective, TestDispatcher, TestPipes} from './change_detector_mocks';
import {TEST_BINDINGS} from './test_bindings';
@ -186,3 +186,11 @@ export function main() {
});
});
}
class TestContext {
eventLog: string[] = [];
someProp: string;
someProp2: string;
onEvent(value: string) { this.eventLog.push(value); }
}

View File

@ -14,7 +14,7 @@ import {
} from 'angular2/test_lib';
import {bind} from 'angular2/src/core/di';
import {CONST_EXPR} from 'angular2/src/core/facade/lang';
import {CONST_EXPR, stringify} from 'angular2/src/core/facade/lang';
import {MapWrapper} from 'angular2/src/core/facade/collection';
import {Promise} from 'angular2/src/core/facade/async';
@ -46,12 +46,13 @@ import {
import {evalModule} from './eval_module';
import {TEST_BINDINGS} from './test_bindings';
import {TestContext, TestDispatcher, TestPipes} from './change_detector_mocks';
import {codeGenValueFn, codeGenExportVariable} from 'angular2/src/compiler/util';
import {TestDispatcher, TestPipes} from './change_detector_mocks';
import {codeGenValueFn, codeGenExportVariable, MODULE_SUFFIX} from 'angular2/src/compiler/util';
// Attention: These module names have to correspond to real modules!
const THIS_MODULE = 'angular2/test/compiler/change_detector_compiler_spec';
var THIS_MODULE_REF = moduleRef(THIS_MODULE);
var THIS_MODULE_ID = 'angular2/test/compiler/change_detector_compiler_spec';
var THIS_MODULE_URL = `package:${THIS_MODULE_ID}${MODULE_SUFFIX}`;
var THIS_MODULE_REF = moduleRef(THIS_MODULE_URL);
export function main() {
describe('ChangeDetectorCompiler', () => {
@ -68,7 +69,8 @@ export function main() {
describe('compileComponentRuntime', () => {
function detectChanges(compiler: ChangeDetectionCompiler, template: string,
directives: CompileDirectiveMetadata[] = CONST_EXPR([])): string[] {
var type = new CompileTypeMetadata({name: 'SomeComp'});
var type =
new CompileTypeMetadata({name: stringify(SomeComponent), moduleUrl: THIS_MODULE_URL});
var parsedTemplate = parser.parse(template, directives, 'TestComp');
var factories =
compiler.compileComponentRuntime(type, ChangeDetectionStrategy.Default, parsedTemplate);
@ -105,7 +107,8 @@ export function main() {
function detectChanges(compiler: ChangeDetectionCompiler, template: string,
directives: CompileDirectiveMetadata[] = CONST_EXPR([])):
Promise<string[]> {
var type = new CompileTypeMetadata({name: 'SomeComp'});
var type =
new CompileTypeMetadata({name: stringify(SomeComponent), moduleUrl: THIS_MODULE_URL});
var parsedTemplate = parser.parse(template, directives, 'TestComp');
var sourceExpressions =
compiler.compileComponentCodeGen(type, ChangeDetectionStrategy.Default, parsedTemplate);
@ -138,10 +141,14 @@ function createTestableModule(source: SourceExpressions, changeDetectorIndex: nu
export function testChangeDetector(changeDetectorFactory: Function): string[] {
var dispatcher = new TestDispatcher([], []);
var cd = changeDetectorFactory(dispatcher);
var ctx = new TestContext();
var ctx = new SomeComponent();
ctx.someProp = 'someValue';
var locals = new Locals(null, MapWrapper.createFromStringMap({'someVar': null}));
cd.hydrate(ctx, locals, dispatcher, new TestPipes());
cd.detectChanges();
return dispatcher.log;
}
class SomeComponent {
someProp: string;
}

View File

@ -7,14 +7,6 @@ import {
BindingTarget
} from 'angular2/src/core/change_detection/change_detection';
export class TestContext {
eventLog: string[] = [];
someProp: string;
someProp2: string;
onEvent(value: string) { this.eventLog.push(value); }
}
export class TestDirective {
eventLog: string[] = [];
dirProp: string;

View File

@ -40,7 +40,8 @@ import {evalModule} from './eval_module';
import {
escapeSingleQuoteString,
codeGenValueFn,
codeGenExportVariable
codeGenExportVariable,
MODULE_SUFFIX
} from 'angular2/src/compiler/util';
import {TEST_BINDINGS} from './test_bindings';
@ -53,9 +54,10 @@ const NG_CONTENT = 'NG_CONTENT';
const EMBEDDED_TEMPLATE = 'EMBEDDED_TEMPLATE';
// Attention: These module names have to correspond to real modules!
const THIS_MODULE_NAME = 'angular2/test/compiler/command_compiler_spec';
var THIS_MODULE_REF = moduleRef(THIS_MODULE_NAME);
var TEMPLATE_COMMANDS_MODULE_REF = moduleRef('angular2/src/core/compiler/template_commands');
var THIS_MODULE_URL = `package:angular2/test/compiler/command_compiler_spec${MODULE_SUFFIX}`;
var THIS_MODULE_REF = moduleRef(THIS_MODULE_URL);
var TEMPLATE_COMMANDS_MODULE_REF =
moduleRef(`package:angular2/src/core/compiler/template_commands${MODULE_SUFFIX}`);
// Attention: read by eval!
export class RootComp {}
@ -63,11 +65,11 @@ export class SomeDir {}
export class AComp {}
var RootCompTypeMeta =
new CompileTypeMetadata({name: 'RootComp', runtime: RootComp, moduleId: THIS_MODULE_NAME});
new CompileTypeMetadata({name: 'RootComp', runtime: RootComp, moduleUrl: THIS_MODULE_URL});
var SomeDirTypeMeta =
new CompileTypeMetadata({name: 'SomeDir', runtime: SomeDir, moduleId: THIS_MODULE_NAME});
new CompileTypeMetadata({name: 'SomeDir', runtime: SomeDir, moduleUrl: THIS_MODULE_URL});
var ACompTypeMeta =
new CompileTypeMetadata({name: 'AComp', runtime: AComp, moduleId: THIS_MODULE_NAME});
new CompileTypeMetadata({name: 'AComp', runtime: AComp, moduleUrl: THIS_MODULE_URL});
var compTypeTemplateId: Map<CompileTypeMetadata, number> =
MapWrapper.createFromPairs([[RootCompTypeMeta, 1], [SomeDirTypeMeta, 2], [ACompTypeMeta, 3]]);
const APP_ID = 'app1';

View File

@ -28,7 +28,8 @@ export function main() {
var fullDirectiveMeta: CompileDirectiveMetadata;
beforeEach(() => {
fullTypeMeta = new CompileTypeMetadata({name: 'SomeType', moduleId: 'someUrl'});
fullTypeMeta =
new CompileTypeMetadata({name: 'SomeType', moduleUrl: 'someUrl', isHost: true});
fullTemplateMeta = new CompileTemplateMetadata({
encapsulation: ViewEncapsulation.Emulated,
template: '<a></a>',

View File

@ -11,7 +11,7 @@ createIsolateSource(String moduleSource, List<List<String>> moduleImports) {
moduleImports.forEach((sourceImport) {
String modName = sourceImport[0];
String modAlias = sourceImport[1];
moduleSourceParts.add("import 'package:${modName}.dart' as ${modAlias};");
moduleSourceParts.add("import '${modName}' as ${modAlias};");
});
moduleSourceParts.add(moduleSource);
moduleSourceParts.add("""

View File

@ -2,6 +2,7 @@ import {Promise, PromiseWrapper} from 'angular2/src/core/facade/async';
import {isPresent, global, StringWrapper} from 'angular2/src/core/facade/lang';
var evalCounter = 0;
var MODULE_URL_REGEX = /^package:(.*)\.js/;
function nextModuleId() {
return `evalScript${evalCounter++}`;
@ -10,24 +11,30 @@ function nextModuleId() {
export function evalModule(moduleSource: string, imports: string[][], args: any[]): Promise<any> {
var moduleId = nextModuleId();
var moduleSourceWithImports = [];
var importModuleNames = [];
var importModuleIds = [];
imports.forEach(sourceImport => {
var modName = sourceImport[0];
var modUrl = sourceImport[0];
var modMatch = MODULE_URL_REGEX.exec(modUrl);
if (!modMatch) {
throw new Error(`Module url ${modUrl} does not match the pattern ${MODULE_URL_REGEX}`);
}
var modId = modMatch[1];
var modAlias = sourceImport[1];
importModuleNames.push(modName);
importModuleIds.push(modId);
// Note: After transpilation to commonJS and loading this file in a browser
// using SystemJS, the loader might get confused by the presence of require,
// and attempt to load "+ modName +.js" !?!
// A simple string concat manages to prevent that, but that is one compiler
// optimaztion away from breaking again. Proceed with caution!
moduleSourceWithImports.push(`var ${modAlias} = require` + `('${modName}');`);
moduleSourceWithImports.push(`var ${modAlias} = require` + `('${modId}');`);
});
moduleSourceWithImports.push(moduleSource);
var moduleBody = new Function('require', 'exports', 'module', moduleSourceWithImports.join('\n'));
var System = global['System'];
if (isPresent(System) && isPresent(System.registerDynamic)) {
System.registerDynamic(moduleId, importModuleNames, false, moduleBody);
System.registerDynamic(moduleId, importModuleIds, false, moduleBody);
return <Promise<any>>System.import(moduleId).then((module) => module.run(args));
} else {
var exports = {};

View File

@ -19,14 +19,14 @@ import {evalModule} from './eval_module';
// when evaling the test module!
export var TEST_VALUE = 23;
const THIS_MODULE = 'angular2/test/compiler/eval_module_spec';
const THIS_MODULE_URL = `package:angular2/test/compiler/eval_module_spec${IS_DART?'.dart':'.js'}`;
export function main() {
describe('evalModule', () => {
it('should call the "run" function and allow to use imports',
inject([AsyncTestCompleter], (async) => {
var moduleSource = IS_DART ? testDartModule : testJsModule;
evalModule(moduleSource, [[THIS_MODULE, 'tst']], [1])
evalModule(moduleSource, [[THIS_MODULE_URL, 'tst']], [1])
.then((value) => {
expect(value).toEqual([1, 23]);
async.done();

View File

@ -0,0 +1,92 @@
import {
ddescribe,
describe,
xdescribe,
it,
iit,
xit,
expect,
beforeEach,
afterEach,
AsyncTestCompleter,
inject,
beforeEachBindings
} from 'angular2/test_lib';
import {Component, View, bind} from 'angular2/core';
import {PromiseWrapper} from 'angular2/src/core/facade/async';
import {SpyProtoViewFactory} from '../core/spies';
import {
CompiledHostTemplate,
CompiledTemplate,
BeginComponentCmd
} from 'angular2/src/core/compiler/template_commands';
import {RuntimeCompiler} from 'angular2/src/compiler/runtime_compiler';
import {ProtoViewFactory} from 'angular2/src/core/compiler/proto_view_factory';
import {AppProtoView} from 'angular2/src/core/compiler/view';
export function main() {
describe('RuntimeCompiler', () => {
var compiler: RuntimeCompiler;
beforeEach(inject([RuntimeCompiler], (_compiler) => { compiler = _compiler; }));
describe('compileInHost', () => {
var protoViewFactorySpy;
var someProtoView;
beforeEachBindings(() => {
protoViewFactorySpy = new SpyProtoViewFactory();
someProtoView = new AppProtoView(null, null, null, null, null, null);
protoViewFactorySpy.spy('createHost').andReturn(someProtoView);
return [bind(ProtoViewFactory).toValue(protoViewFactorySpy)];
});
it('should compile the template via TemplateCompiler',
inject([AsyncTestCompleter], (async) => {
var cht: CompiledHostTemplate;
protoViewFactorySpy.spy('createHost')
.andCallFake((_cht) => {
cht = _cht;
return someProtoView;
});
compiler.compileInHost(SomeComponent)
.then((_) => {
var beginComponentCmd =
<BeginComponentCmd>cht.getTemplate().getData('app1').commands[0];
expect(beginComponentCmd.name).toEqual('some-comp');
async.done();
});
}));
});
it('should cache the result', inject([AsyncTestCompleter], (async) => {
PromiseWrapper
.all([compiler.compileInHost(SomeComponent), compiler.compileInHost(SomeComponent)])
.then((protoViewRefs) => {
expect(protoViewRefs[0]).toBe(protoViewRefs[1]);
async.done();
});
}));
it('should clear the cache',
inject([AsyncTestCompleter], (async) => {compiler.compileInHost(SomeComponent)
.then((protoViewRef1) => {
compiler.clearCache();
compiler.compileInHost(SomeComponent)
.then((protoViewRef2) => {
expect(protoViewRef1)
.not.toBe(protoViewRef2);
async.done();
});
})}));
});
}
@Component({selector: 'some-comp'})
@View({template: ''})
class SomeComponent {
}

View File

@ -34,7 +34,7 @@ import {
} from 'angular2/core';
import {TEST_BINDINGS} from './test_bindings';
import {IS_DART} from '../platform';
import {MODULE_SUFFIX, IS_DART} from 'angular2/src/compiler/util';
export function main() {
describe('RuntimeMetadataResolver', () => {
@ -50,7 +50,7 @@ export function main() {
expect(meta.dynamicLoadable).toBe(true);
expect(meta.type.runtime).toBe(ComponentWithEverything);
expect(meta.type.name).toEqual(stringify(ComponentWithEverything));
expect(meta.type.moduleId).toEqual('someModuleId');
expect(meta.type.moduleUrl).toEqual(`package:someModuleId${MODULE_SUFFIX}`);
expect(meta.lifecycleHooks).toEqual(LIFECYCLE_HOOKS_VALUES);
expect(meta.changeDetection).toBe(ChangeDetectionStrategy.CheckAlways);
expect(meta.inputs).toEqual({'someProp': 'someProp'});
@ -65,12 +65,12 @@ export function main() {
expect(meta.template.templateUrl).toEqual('someTemplateUrl');
}));
it('should use the moduleId from the reflector if none is given',
it('should use the moduleUrl from the reflector if none is given',
inject([RuntimeMetadataResolver], (resolver: RuntimeMetadataResolver) => {
var expectedValue =
IS_DART ? 'base/dist/dart/angular2/test/compiler/runtime_metadata_spec' : './';
expect(resolver.getMetadata(DirectiveWithoutModuleId).type.moduleId)
.toEqual(expectedValue);
var value: string = resolver.getMetadata(DirectiveWithoutModuleId).type.moduleUrl;
var expectedEndValue =
IS_DART ? 'base/dist/dart/angular2/test/compiler/runtime_metadata_spec.dart' : './';
expect((<any>value).endsWith(expectedEndValue)).toBe(true);
}));
});

View File

@ -19,22 +19,26 @@ export function main() {
describe('getSourceWithImports', () => {
it('should generate named imports for modules', () => {
var sourceWithImports =
new SourceModule('some/moda', `${moduleRef('some/modb')}A`).getSourceWithImports();
new SourceModule('package:some/moda', `${moduleRef('package:some/modb')}A`)
.getSourceWithImports();
expect(sourceWithImports.source).toEqual('import0.A');
expect(sourceWithImports.imports).toEqual([['some/modb', 'import0']]);
expect(sourceWithImports.imports).toEqual([['package:some/modb', 'import0']]);
});
it('should dedupe imports', () => {
var sourceWithImports =
new SourceModule('some/moda', `${moduleRef('some/modb')}A + ${moduleRef('some/modb')}B`)
new SourceModule(
'package:some/moda',
`${moduleRef('package:some/modb')}A + ${moduleRef('package:some/modb')}B`)
.getSourceWithImports();
expect(sourceWithImports.source).toEqual('import0.A + import0.B');
expect(sourceWithImports.imports).toEqual([['some/modb', 'import0']]);
expect(sourceWithImports.imports).toEqual([['package:some/modb', 'import0']]);
});
it('should not use an import for the moduleId of the SourceModule', () => {
it('should not use an import for the moduleUrl of the SourceModule', () => {
var sourceWithImports =
new SourceModule('some/moda', `${moduleRef('some/moda')}A`).getSourceWithImports();
new SourceModule('package:some/moda', `${moduleRef('package:some/moda')}A`)
.getSourceWithImports();
expect(sourceWithImports.source).toEqual('A');
expect(sourceWithImports.imports).toEqual([]);
});

View File

@ -29,15 +29,15 @@ import {
import {SourceExpression, SourceModule} from 'angular2/src/compiler/source_module';
import {ViewEncapsulation} from 'angular2/src/core/render/api';
import {TEST_BINDINGS} from './test_bindings';
import {codeGenValueFn, codeGenExportVariable} from 'angular2/src/compiler/util';
import {codeGenValueFn, codeGenExportVariable, MODULE_SUFFIX} from 'angular2/src/compiler/util';
// Attention: These module names have to correspond to real modules!
const MODULE_NAME = 'angular2/test/compiler/style_compiler_spec';
const IMPORT_ABS_MODULE_NAME = 'angular2/test/compiler/style_compiler_import';
const IMPORT_REL_MODULE_NAME = './style_compiler_import';
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.
const IMPORT_ABS_MODULE_NAME_WITH_IMPORT =
'angular2/test/compiler/style_compiler_transitive_import';
var IMPORT_ABS_STYLESHEET_URL_WITH_IMPORT =
`package:angular2/test/compiler/style_compiler_transitive_import.css`;
export function main() {
describe('StyleCompiler', () => {
@ -65,9 +65,9 @@ export function main() {
beforeEach(() => {
xhrCount = 0;
xhrUrlResults = {};
xhrUrlResults[IMPORT_ABS_MODULE_NAME] = 'span {color: blue}';
xhrUrlResults[IMPORT_ABS_MODULE_NAME_WITH_IMPORT] =
`a {color: green}@import ${IMPORT_REL_MODULE_NAME};`;
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):
@ -100,7 +100,7 @@ export function main() {
}));
it('should allow to import rules', inject([AsyncTestCompleter], (async) => {
compile(['div {color: red}'], [IMPORT_ABS_MODULE_NAME], encapsulation)
compile(['div {color: red}'], [IMPORT_ABS_STYLESHEET_URL], encapsulation)
.then(styles => {
expect(styles).toEqual(['div {color: red}', 'span {color: blue}']);
async.done();
@ -108,7 +108,7 @@ export function main() {
}));
it('should allow to import rules transitively', inject([AsyncTestCompleter], (async) => {
compile(['div {color: red}'], [IMPORT_ABS_MODULE_NAME_WITH_IMPORT], encapsulation)
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}']);
@ -132,7 +132,7 @@ export function main() {
}));
it('should allow to import rules', inject([AsyncTestCompleter], (async) => {
compile(['div {\ncolor: red;\n}'], [IMPORT_ABS_MODULE_NAME], encapsulation)
compile(['div {\ncolor: red;\n}'], [IMPORT_ABS_STYLESHEET_URL], encapsulation)
.then(styles => {
compareStyles(styles, [
'div[_ngcontent-app1-23] {\ncolor: red;\n}',
@ -143,7 +143,8 @@ export function main() {
}));
it('should allow to import rules transitively', inject([AsyncTestCompleter], (async) => {
compile(['div {\ncolor: red;\n}'], [IMPORT_ABS_MODULE_NAME_WITH_IMPORT], encapsulation)
compile(['div {\ncolor: red;\n}'], [IMPORT_ABS_STYLESHEET_URL_WITH_IMPORT],
encapsulation)
.then(styles => {
compareStyles(styles, [
'div[_ngcontent-app1-23] {\ncolor: red;\n}',
@ -157,8 +158,8 @@ export function main() {
it('should cache stylesheets for parallel requests', inject([AsyncTestCompleter], (async) => {
PromiseWrapper.all([
compile([], [IMPORT_ABS_MODULE_NAME], ViewEncapsulation.None),
compile([], [IMPORT_ABS_MODULE_NAME], ViewEncapsulation.None)
compile([], [IMPORT_ABS_STYLESHEET_URL], ViewEncapsulation.None),
compile([], [IMPORT_ABS_STYLESHEET_URL], ViewEncapsulation.None)
])
.then((styleArrays) => {
expect(styleArrays[0]).toEqual(['span {color: blue}']);
@ -169,10 +170,10 @@ export function main() {
}));
it('should cache stylesheets for serial requests', inject([AsyncTestCompleter], (async) => {
compile([], [IMPORT_ABS_MODULE_NAME], ViewEncapsulation.None)
compile([], [IMPORT_ABS_STYLESHEET_URL], ViewEncapsulation.None)
.then((styles0) => {
xhrUrlResults[IMPORT_ABS_MODULE_NAME] = 'span {color: black}';
return compile([], [IMPORT_ABS_MODULE_NAME], ViewEncapsulation.None)
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}']);
@ -183,11 +184,11 @@ export function main() {
}));
it('should allow to clear the cache', inject([AsyncTestCompleter], (async) => {
compile([], [IMPORT_ABS_MODULE_NAME], ViewEncapsulation.None)
compile([], [IMPORT_ABS_STYLESHEET_URL], ViewEncapsulation.None)
.then((_) => {
compiler.clearCache();
xhrUrlResults[IMPORT_ABS_MODULE_NAME] = 'span {color: black}';
return compile([], [IMPORT_ABS_MODULE_NAME], ViewEncapsulation.None);
xhrUrlResults[IMPORT_ABS_STYLESHEET_URL] = 'span {color: black}';
return compile([], [IMPORT_ABS_STYLESHEET_URL], ViewEncapsulation.None);
})
.then((styles) => {
expect(xhrCount).toBe(2);
@ -229,7 +230,7 @@ export function main() {
}));
it('should allow to import rules', inject([AsyncTestCompleter], (async) => {
compile(['div {color: red}'], [IMPORT_ABS_MODULE_NAME], encapsulation)
compile(['div {color: red}'], [IMPORT_ABS_STYLESHEET_URL], encapsulation)
.then(styles => {
expect(styles).toEqual(['div {color: red}', 'span {color: blue}']);
async.done();
@ -252,7 +253,7 @@ export function main() {
}));
it('should allow to import rules', inject([AsyncTestCompleter], (async) => {
compile(['div {color: red}'], [IMPORT_ABS_MODULE_NAME], encapsulation)
compile(['div {color: red}'], [IMPORT_ABS_STYLESHEET_URL], encapsulation)
.then(styles => {
compareStyles(styles, [
'div[_ngcontent-app1-23] {\ncolor: red;\n}',
@ -266,7 +267,7 @@ export function main() {
describe('compileStylesheetCodeGen', () => {
function compile(style: string): Promise<string[][]> {
var sourceModules = compiler.compileStylesheetCodeGen(MODULE_NAME, style);
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);
@ -286,7 +287,7 @@ export function main() {
it('should allow to import rules with relative paths',
inject([AsyncTestCompleter], (async) => {
compile(`div {color: red}@import ${IMPORT_REL_MODULE_NAME};`)
compile(`div {color: red}@import ${IMPORT_REL_STYLESHEET_URL};`)
.then(stylesAndShimStyles => {
var expected = [
['div {color: red}', 'span {color: blue}'],
@ -314,7 +315,7 @@ function testableExpression(source: SourceExpression): SourceModule {
function testableModule(sourceModule: SourceModule): SourceModule {
var testableSource = `${sourceModule.sourceWithModuleRefs}
${codeGenExportVariable('run')}${codeGenValueFn(['_'], 'STYLES')};`;
return new SourceModule(sourceModule.moduleId, testableSource);
return new SourceModule(sourceModule.moduleUrl, testableSource);
}
// Needed for Android browsers which add an extra space at the end of some lines

View File

@ -44,13 +44,13 @@ import {
import {Component, View, Directive, bind} from 'angular2/core';
import {TEST_BINDINGS} from './test_bindings';
import {TestContext, TestDispatcher, TestPipes} from './change_detector_mocks';
import {codeGenValueFn, codeGenExportVariable} from 'angular2/src/compiler/util';
import {TestDispatcher, TestPipes} from './change_detector_mocks';
import {codeGenValueFn, codeGenExportVariable, MODULE_SUFFIX} from 'angular2/src/compiler/util';
import {APP_ID} from 'angular2/src/core/render/dom/dom_tokens';
// Attention: This path has to point to this test file!
const THIS_MODULE = 'angular2/test/compiler/template_compiler_spec';
var THIS_MODULE_REF = moduleRef(THIS_MODULE);
const THIS_MODULE_ID = 'angular2/test/compiler/template_compiler_spec';
var THIS_MODULE_REF = moduleRef(`package:${THIS_MODULE_ID}${MODULE_SUFFIX}`);
const APP_ID_VALUE = 'app1';
@ -136,7 +136,7 @@ export function main() {
it('should cache components for parallel requests',
inject([AsyncTestCompleter, XHR], (async, xhr: MockXHR) => {
xhr.expect('angular2/test/compiler/compUrl.html', 'a');
xhr.expect('package:angular2/test/compiler/compUrl.html', 'a');
PromiseWrapper.all([compile([CompWithTemplateUrl]), compile([CompWithTemplateUrl])])
.then((humanizedTemplates) => {
expect(humanizedTemplates[0]['commands'][1]['commands']).toEqual(['#text(a)']);
@ -149,7 +149,7 @@ export function main() {
it('should cache components for sequential requests',
inject([AsyncTestCompleter, XHR], (async, xhr: MockXHR) => {
xhr.expect('angular2/test/compiler/compUrl.html', 'a');
xhr.expect('package:angular2/test/compiler/compUrl.html', 'a');
compile([CompWithTemplateUrl])
.then((humanizedTemplate0) => {
return compile([CompWithTemplateUrl])
@ -166,11 +166,11 @@ export function main() {
it('should allow to clear the cache',
inject([AsyncTestCompleter, XHR], (async, xhr: MockXHR) => {
xhr.expect('angular2/test/compiler/compUrl.html', 'a');
xhr.expect('package:angular2/test/compiler/compUrl.html', 'a');
compile([CompWithTemplateUrl])
.then((humanizedTemplate) => {
compiler.clearCache();
xhr.expect('angular2/test/compiler/compUrl.html', 'b');
xhr.expect('package:angular2/test/compiler/compUrl.html', 'b');
var result = compile([CompWithTemplateUrl]);
xhr.flush();
return result;
@ -200,8 +200,7 @@ export function main() {
function compile(components: Type[]): Promise<any[]> {
return PromiseWrapper.all(components.map(normalizeComponent))
.then((normalizedCompWithViewDirMetas: NormalizedComponentWithViewDirectives[]) => {
var sourceModule =
compiler.compileTemplatesCodeGen(THIS_MODULE, normalizedCompWithViewDirMetas);
var sourceModule = compiler.compileTemplatesCodeGen(normalizedCompWithViewDirMetas);
var sourceWithImports =
testableTemplateModule(sourceModule,
normalizedCompWithViewDirMetas[0].component)
@ -227,7 +226,7 @@ export function main() {
it('should normalize the template',
inject([AsyncTestCompleter, XHR], (async, xhr: MockXHR) => {
xhr.expect('angular2/test/compiler/compUrl.html', 'loadedTemplate');
xhr.expect('package:angular2/test/compiler/compUrl.html', 'loadedTemplate');
compiler.normalizeDirectiveMetadata(
runtimeMetadataResolver.getMetadata(CompWithTemplateUrl))
.then((meta: CompileDirectiveMetadata) => {
@ -260,7 +259,8 @@ export function main() {
describe('compileStylesheetCodeGen', () => {
it('should compile stylesheets into code', inject([AsyncTestCompleter], (async) => {
var cssText = 'div {color: red}';
var sourceModule = compiler.compileStylesheetCodeGen('someModuleId', cssText)[0];
var sourceModule =
compiler.compileStylesheetCodeGen('package:someModuleUrl', cssText)[0];
var sourceWithImports = testableStylesModule(sourceModule).getSourceWithImports();
evalModule(sourceWithImports.source, sourceWithImports.imports, null)
.then(loadedCssText => {
@ -276,52 +276,47 @@ export function main() {
@Component({
selector: 'comp-a',
host: {'[title]': 'someProp'},
moduleId: THIS_MODULE,
moduleId: THIS_MODULE_ID,
exportAs: 'someExportAs'
})
@View({template: '<a [href]="someProp"></a>', styles: ['div {color: red}']})
class CompWithBindingsAndStyles {
}
@Component({selector: 'tree', moduleId: THIS_MODULE})
@Component({selector: 'tree', moduleId: THIS_MODULE_ID})
@View({template: '<tree></tree>', directives: [TreeComp]})
class TreeComp {
}
@Component({selector: 'comp-url', moduleId: THIS_MODULE})
@Component({selector: 'comp-url', moduleId: THIS_MODULE_ID})
@View({templateUrl: 'compUrl.html'})
class CompWithTemplateUrl {
}
@Component({selector: 'comp-tpl', moduleId: THIS_MODULE})
@Component({selector: 'comp-tpl', moduleId: THIS_MODULE_ID})
@View({template: '<template><a [href]="someProp"></a></template>'})
class CompWithEmbeddedTemplate {
}
@Directive({selector: 'plain', moduleId: THIS_MODULE})
@Directive({selector: 'plain', moduleId: THIS_MODULE_ID})
@View({template: ''})
class NonComponent {
}
@Component({selector: 'comp', moduleId: THIS_MODULE})
@View({template: ''})
class CompWithoutHost {
}
function testableTemplateModule(sourceModule: SourceModule, normComp: CompileDirectiveMetadata):
SourceModule {
var resultExpression =
`${THIS_MODULE_REF}humanizeTemplate(Host${normComp.type.name}Template.getTemplate())`;
var testableSource = `${sourceModule.sourceWithModuleRefs}
${codeGenExportVariable('run')}${codeGenValueFn(['_'], resultExpression)};`;
return new SourceModule(sourceModule.moduleId, testableSource);
return new SourceModule(sourceModule.moduleUrl, testableSource);
}
function testableStylesModule(sourceModule: SourceModule): SourceModule {
var testableSource = `${sourceModule.sourceWithModuleRefs}
${codeGenExportVariable('run')}${codeGenValueFn(['_'], 'STYLES')};`;
return new SourceModule(sourceModule.moduleId, testableSource);
return new SourceModule(sourceModule.moduleUrl, testableSource);
}
// Attention: read by eval!
@ -347,6 +342,11 @@ export function humanizeTemplate(template: CompiledTemplate,
return result;
}
class TestContext implements CompWithBindingsAndStyles, TreeComp, CompWithTemplateUrl,
CompWithEmbeddedTemplate {
someProp: string;
}
function testChangeDetector(changeDetectorFactory: Function): string[] {
var ctx = new TestContext();

View File

@ -31,7 +31,7 @@ export function main() {
beforeEachBindings(() => TEST_BINDINGS);
beforeEach(() => {
dirType = new CompileTypeMetadata({moduleId: 'some/module/id', name: 'SomeComp'});
dirType = new CompileTypeMetadata({moduleUrl: 'package:some/module/a.js', name: 'SomeComp'});
});
describe('loadTemplate', () => {
@ -48,12 +48,12 @@ export function main() {
}))
.then((template: CompileTemplateMetadata) => {
expect(template.template).toEqual('a');
expect(template.templateUrl).toEqual('some/module/id');
expect(template.templateUrl).toEqual('package:some/module/a.js');
async.done();
});
}));
it('should resolve styles on the annotation against the moduleId',
it('should resolve styles on the annotation against the moduleUrl',
inject([AsyncTestCompleter, TemplateNormalizer],
(async, normalizer: TemplateNormalizer) => {
normalizer.normalizeTemplate(dirType, new CompileTemplateMetadata({
@ -64,12 +64,12 @@ export function main() {
styleUrls: ['test.css']
}))
.then((template: CompileTemplateMetadata) => {
expect(template.styleUrls).toEqual(['some/module/test.css']);
expect(template.styleUrls).toEqual(['package:some/module/test.css']);
async.done();
});
}));
it('should resolve styles in the template against the moduleId',
it('should resolve styles in the template against the moduleUrl',
inject([AsyncTestCompleter, TemplateNormalizer],
(async, normalizer: TemplateNormalizer) => {
normalizer.normalizeTemplate(dirType, new CompileTemplateMetadata({
@ -80,7 +80,7 @@ export function main() {
styleUrls: []
}))
.then((template: CompileTemplateMetadata) => {
expect(template.styleUrls).toEqual(['some/module/test.css']);
expect(template.styleUrls).toEqual(['package:some/module/test.css']);
async.done();
});
}));
@ -88,38 +88,39 @@ export function main() {
describe('templateUrl', () => {
it('should load a template from a url that is resolved against moduleId',
it('should load a template from a url that is resolved against moduleUrl',
inject([AsyncTestCompleter, TemplateNormalizer, XHR],
(async, normalizer: TemplateNormalizer, xhr: MockXHR) => {
xhr.expect('some/module/sometplurl', 'a');
xhr.expect('package:some/module/sometplurl.html', 'a');
normalizer.normalizeTemplate(dirType, new CompileTemplateMetadata({
encapsulation: null,
template: null,
templateUrl: 'sometplurl',
templateUrl: 'sometplurl.html',
styles: [],
styleUrls: ['test.css']
}))
.then((template: CompileTemplateMetadata) => {
expect(template.template).toEqual('a');
expect(template.templateUrl).toEqual('some/module/sometplurl');
expect(template.templateUrl)
.toEqual('package:some/module/sometplurl.html');
async.done();
});
xhr.flush();
}));
it('should resolve styles on the annotation against the moduleId',
it('should resolve styles on the annotation against the moduleUrl',
inject([AsyncTestCompleter, TemplateNormalizer, XHR],
(async, normalizer: TemplateNormalizer, xhr: MockXHR) => {
xhr.expect('some/module/tpl/sometplurl', '');
xhr.expect('package:some/module/tpl/sometplurl.html', '');
normalizer.normalizeTemplate(dirType, new CompileTemplateMetadata({
encapsulation: null,
template: null,
templateUrl: 'tpl/sometplurl',
templateUrl: 'tpl/sometplurl.html',
styles: [],
styleUrls: ['test.css']
}))
.then((template: CompileTemplateMetadata) => {
expect(template.styleUrls).toEqual(['some/module/test.css']);
expect(template.styleUrls).toEqual(['package:some/module/test.css']);
async.done();
});
xhr.flush();
@ -128,16 +129,17 @@ export function main() {
it('should resolve styles in the template against the templateUrl',
inject([AsyncTestCompleter, TemplateNormalizer, XHR],
(async, normalizer: TemplateNormalizer, xhr: MockXHR) => {
xhr.expect('some/module/tpl/sometplurl', '<style>@import test.css</style>');
xhr.expect('package:some/module/tpl/sometplurl.html',
'<style>@import test.css</style>');
normalizer.normalizeTemplate(dirType, new CompileTemplateMetadata({
encapsulation: null,
template: null,
templateUrl: 'tpl/sometplurl',
templateUrl: 'tpl/sometplurl.html',
styles: [],
styleUrls: []
}))
.then((template: CompileTemplateMetadata) => {
expect(template.styleUrls).toEqual(['some/module/tpl/test.css']);
expect(template.styleUrls).toEqual(['package:some/module/tpl/test.css']);
async.done();
});
xhr.flush();
@ -163,7 +165,7 @@ export function main() {
var template = normalizer.normalizeLoadedTemplate(
dirType, new CompileTemplateMetadata(
{encapsulation: viewEncapsulation, styles: [], styleUrls: []}),
'', 'some/module/');
'', 'package:some/module/');
expect(template.encapsulation).toBe(viewEncapsulation);
}));
@ -172,7 +174,7 @@ export function main() {
var template = normalizer.normalizeLoadedTemplate(
dirType,
new CompileTemplateMetadata({encapsulation: null, styles: [], styleUrls: []}), 'a',
'some/module/');
'package:some/module/');
expect(template.template).toEqual('a')
}));
@ -181,7 +183,7 @@ export function main() {
var template = normalizer.normalizeLoadedTemplate(
dirType,
new CompileTemplateMetadata({encapsulation: null, styles: [], styleUrls: []}),
'<ng-content select="a"></ng-content>', 'some/module/');
'<ng-content select="a"></ng-content>', 'package:some/module/');
expect(template.ngContentSelectors).toEqual(['a']);
}));
@ -191,7 +193,7 @@ export function main() {
dirType,
new CompileTemplateMetadata({encapsulation: null, styles: [], styleUrls: []}),
'<ng-content></ng-content><ng-content select></ng-content><ng-content select="*"></ng-content>',
'some/module/');
'package:some/module/');
expect(template.ngContentSelectors).toEqual(['*', '*', '*']);
}));
@ -200,7 +202,7 @@ export function main() {
var template = normalizer.normalizeLoadedTemplate(
dirType,
new CompileTemplateMetadata({encapsulation: null, styles: [], styleUrls: []}),
'<style>a</style>', 'some/module/');
'<style>a</style>', 'package:some/module/');
expect(template.styles).toEqual(['a']);
}));
@ -209,7 +211,7 @@ export function main() {
var template = normalizer.normalizeLoadedTemplate(
dirType,
new CompileTemplateMetadata({encapsulation: null, styles: [], styleUrls: []}),
'<div><style>a</style></div>', 'some/module/');
'<div><style>a</style></div>', 'package:some/module/');
expect(template.styles).toEqual(['a']);
}));
@ -218,8 +220,8 @@ export function main() {
var template = normalizer.normalizeLoadedTemplate(
dirType,
new CompileTemplateMetadata({encapsulation: null, styles: [], styleUrls: []}),
'<link rel="stylesheet" href="aUrl">', 'some/module/');
expect(template.styleUrls).toEqual(['some/module/aUrl']);
'<link rel="stylesheet" href="aUrl">', 'package:some/module/');
expect(template.styleUrls).toEqual(['package:some/module/aUrl']);
}));
it('should collect styleUrls in elements',
@ -227,8 +229,8 @@ export function main() {
var template = normalizer.normalizeLoadedTemplate(
dirType,
new CompileTemplateMetadata({encapsulation: null, styles: [], styleUrls: []}),
'<div><link rel="stylesheet" href="aUrl"></div>', 'some/module/');
expect(template.styleUrls).toEqual(['some/module/aUrl']);
'<div><link rel="stylesheet" href="aUrl"></div>', 'package:some/module/');
expect(template.styleUrls).toEqual(['package:some/module/aUrl']);
}));
it('should ignore link elements with non stylesheet rel attribute',
@ -236,7 +238,7 @@ export function main() {
var template = normalizer.normalizeLoadedTemplate(
dirType,
new CompileTemplateMetadata({encapsulation: null, styles: [], styleUrls: []}),
'<link href="b" rel="a"></link>', 'some/module/');
'<link href="b" rel="a"></link>', 'package:some/module/');
expect(template.styleUrls).toEqual([]);
}));
@ -245,9 +247,9 @@ export function main() {
var template = normalizer.normalizeLoadedTemplate(
dirType, new CompileTemplateMetadata(
{encapsulation: null, styles: ['@import "test.css";'], styleUrls: []}),
'', 'some/module/id');
'', 'package:some/module/id');
expect(template.styles).toEqual(['']);
expect(template.styleUrls).toEqual(['some/module/test.css']);
expect(template.styleUrls).toEqual(['package:some/module/test.css']);
}));
it('should resolve relative urls in inline styles',
@ -258,9 +260,9 @@ export function main() {
styles: ['.foo{background-image: url(\'double.jpg\');'],
styleUrls: []
}),
'', 'some/module/id');
'', 'package:some/module/id');
expect(template.styles)
.toEqual(['.foo{background-image: url(\'some/module/double.jpg\');']);
.toEqual(['.foo{background-image: url(\'package:some/module/double.jpg\');']);
}));
it('should resolve relative style urls in styleUrls',
@ -268,9 +270,9 @@ export function main() {
var template = normalizer.normalizeLoadedTemplate(
dirType, new CompileTemplateMetadata(
{encapsulation: null, styles: [], styleUrls: ['test.css']}),
'', 'some/module/id');
'', 'package:some/module/id');
expect(template.styles).toEqual([]);
expect(template.styleUrls).toEqual(['some/module/test.css']);
expect(template.styleUrls).toEqual(['package:some/module/test.css']);
}));
it('should normalize ViewEncapsulation.Emulated to ViewEncapsulation.None if there are no stlyes nor stylesheets',
@ -278,7 +280,7 @@ export function main() {
var template = normalizer.normalizeLoadedTemplate(
dirType, new CompileTemplateMetadata(
{encapsulation: ViewEncapsulation.Emulated, styles: [], styleUrls: []}),
'', 'some/module/id');
'', 'package:some/module/id');
expect(template.encapsulation).toEqual(ViewEncapsulation.None);
}));
@ -287,7 +289,8 @@ export function main() {
var template = normalizer.normalizeLoadedTemplate(
dirType,
new CompileTemplateMetadata({encapsulation: null, styles: [], styleUrls: []}),
'<div ng-non-bindable><ng-content select="a"></ng-content></div>', 'some/module/');
'<div ng-non-bindable><ng-content select="a"></ng-content></div>',
'package:some/module/');
expect(template.ngContentSelectors).toEqual([]);
}));
@ -296,7 +299,7 @@ export function main() {
var template = normalizer.normalizeLoadedTemplate(
dirType,
new CompileTemplateMetadata({encapsulation: null, styles: [], styleUrls: []}),
'<div ng-non-bindable><style>div {color:red}</style></div>', 'some/module/');
'<div ng-non-bindable><style>div {color:red}</style></div>', 'package:some/module/');
expect(template.styles).toEqual(['div {color:red}']);
}));
});

View File

@ -101,8 +101,8 @@ export function main() {
PromiseWrapper.then(refPromise, null, (exception) => {
expect(exception).toContainError(
`Could not load '${stringify(HelloRootDirectiveIsNotCmp)}' because it is not a component.`);
expect(logger.res.join("")).toContain("Could not load");
`Could not compile '${stringify(HelloRootDirectiveIsNotCmp)}' because it is not a component.`);
expect(logger.res.join("")).toContain("Could not compile");
async.done();
return null;
});

View File

@ -1,4 +1,4 @@
library angular2.src.transform.di_transformer;
library angular2.test.core.change_detection.generator;
import 'dart:convert';
import 'dart:io';

View File

@ -1,752 +1,63 @@
import {
AsyncTestCompleter,
beforeEach,
xdescribe,
ddescribe,
describe,
el,
expect,
xdescribe,
it,
iit,
xit,
expect,
beforeEach,
afterEach,
AsyncTestCompleter,
inject,
it
beforeEachBindings
} from 'angular2/test_lib';
import {SpyRenderCompiler, SpyDirectiveResolver} from '../spies';
import {ListWrapper, Map, MapWrapper, StringMapWrapper} from 'angular2/src/core/facade/collection';
import {Type, isBlank, stringify, isPresent, isArray} from 'angular2/src/core/facade/lang';
import {PromiseCompleter, PromiseWrapper, Promise} from 'angular2/src/core/facade/async';
import {Compiler, CompilerCache} from 'angular2/src/core/compiler/compiler';
import {AppProtoView} from 'angular2/src/core/compiler/view';
import {ElementBinder} from 'angular2/src/core/compiler/element_binder';
import {DirectiveResolver} from 'angular2/src/core/compiler/directive_resolver';
import {PipeResolver} from 'angular2/src/core/compiler/pipe_resolver';
import {Attribute, ViewMetadata, Component, Directive, Pipe} from 'angular2/src/core/metadata';
import {internalProtoView} from 'angular2/src/core/compiler/view_ref';
import {DirectiveBinding} from 'angular2/src/core/compiler/element_injector';
import {ViewResolver} from 'angular2/src/core/compiler/view_resolver';
import {Component, View, bind} from 'angular2/core';
import {SpyProtoViewFactory} from '../spies';
import {
ComponentUrlMapper,
RuntimeComponentUrlMapper
} from 'angular2/src/core/compiler/component_url_mapper';
CompiledHostTemplate,
CompiledTemplate,
BeginComponentCmd
} from 'angular2/src/core/compiler/template_commands';
import {Compiler} from 'angular2/src/core/compiler/compiler';
import {ProtoViewFactory} from 'angular2/src/core/compiler/proto_view_factory';
import {UrlResolver} from 'angular2/src/core/services/url_resolver';
import {AppRootUrl} from 'angular2/src/core/services/app_root_url';
import {
ProtoViewDto,
ViewType,
RenderProtoViewRef,
ViewDefinition,
RenderProtoViewMergeMapping,
RenderDirectiveMetadata,
DirectiveBinder,
RenderElementBinder
} from 'angular2/src/core/render/api';
// TODO(tbosch): Spys don't support named modules...
import {PipeBinding} from 'angular2/src/core/pipes/pipe_binding';
import {reflector, ReflectionInfo} from 'angular2/src/core/reflection/reflection';
import {AppProtoView} from 'angular2/src/core/compiler/view';
export function main() {
describe('compiler', function() {
var directiveResolver, pipeResolver, tplResolver, renderCompiler, protoViewFactory,
cmpUrlMapper, rootProtoView;
var renderCompileRequests: any[];
describe('Compiler', () => {
var compiler: Compiler;
var protoViewFactorySpy;
var someProtoView;
var cht: CompiledHostTemplate;
function createCompiler(renderCompileResults: Array<ProtoViewDto | Promise<ProtoViewDto>>,
protoViewFactoryResults: AppProtoView[]) {
var urlResolver = new UrlResolver();
renderCompileRequests = [];
renderCompileResults = ListWrapper.clone(renderCompileResults);
renderCompiler.spy('compile').andCallFake((view) => {
renderCompileRequests.push(view);
return PromiseWrapper.resolve(ListWrapper.removeAt(renderCompileResults, 0));
});
protoViewFactory = new FakeProtoViewFactory(protoViewFactoryResults);
return new Compiler(directiveResolver, pipeResolver, [SomeDefaultPipe], new CompilerCache(),
tplResolver, cmpUrlMapper, urlResolver, renderCompiler, protoViewFactory,
new AppRootUrl("http://www.app.com"));
}
beforeEach(() => {
directiveResolver = new DirectiveResolver();
pipeResolver = new PipeResolver();
tplResolver = new FakeViewResolver();
cmpUrlMapper = new RuntimeComponentUrlMapper();
renderCompiler = new SpyRenderCompiler();
renderCompiler.spy('compileHost')
.andCallFake((componentId) => {
return PromiseWrapper.resolve(
createRenderProtoView([createRenderComponentElementBinder(0)], ViewType.HOST));
});
renderCompiler.spy('mergeProtoViewsRecursively')
.andCallFake((protoViewRefs: Array<RenderProtoViewRef | any[]>) => {
return PromiseWrapper.resolve(new RenderProtoViewMergeMapping(
new MergedRenderProtoViewRef(protoViewRefs), 1, [], 0, [], [], [null]));
});
// TODO spy on .compile and return RenderProtoViewRef, same for compileHost
rootProtoView = createRootProtoView(directiveResolver, MainComponent);
beforeEachBindings(() => {
protoViewFactorySpy = new SpyProtoViewFactory();
someProtoView = new AppProtoView(null, null, null, null, null, null);
protoViewFactorySpy.spy('createHost').andReturn(someProtoView);
return [bind(ProtoViewFactory).toValue(protoViewFactorySpy), Compiler];
});
describe('serialize template', () => {
beforeEach(inject([Compiler], (_compiler) => {
compiler = _compiler;
cht = new CompiledHostTemplate(() => new CompiledTemplate(23, null));
reflector.registerType(SomeComponent, new ReflectionInfo([cht]));
}));
function captureTemplate(template: ViewMetadata): Promise<ViewDefinition> {
tplResolver.setView(MainComponent, template);
var compiler =
createCompiler([createRenderProtoView()], [rootProtoView, createProtoView()]);
return compiler.compileInHost(MainComponent)
.then((_) => {
expect(renderCompileRequests.length).toBe(1);
return renderCompileRequests[0];
});
}
function captureDirective(directive): Promise<RenderDirectiveMetadata> {
return captureTemplate(new ViewMetadata({template: '<div></div>', directives: [directive]}))
.then((renderTpl) => {
expect(renderTpl.directives.length).toBe(1);
return renderTpl.directives[0];
});
}
it('should fill the componentId', inject([AsyncTestCompleter], (async) => {
captureTemplate(new ViewMetadata({template: '<div></div>'}))
.then((renderTpl) => {
expect(renderTpl.componentId).toEqual(stringify(MainComponent));
async.done();
});
}));
it('should fill inline template', inject([AsyncTestCompleter], (async) => {
captureTemplate(new ViewMetadata({template: '<div></div>'}))
.then((renderTpl) => {
expect(renderTpl.template).toEqual('<div></div>');
async.done();
});
}));
it('should fill templateAbsUrl given inline templates',
inject([AsyncTestCompleter], (async) => {
cmpUrlMapper.setComponentUrl(MainComponent, '/cmp/main.js');
captureTemplate(new ViewMetadata({template: '<div></div>'}))
.then((renderTpl) => {
expect(renderTpl.templateAbsUrl).toEqual('http://www.app.com/cmp/main.js');
async.done();
});
}));
it('should not fill templateAbsUrl given no inline template or template url',
inject([AsyncTestCompleter], (async) => {
cmpUrlMapper.setComponentUrl(MainComponent, '/cmp/main.js');
captureTemplate(new ViewMetadata({template: null, templateUrl: null}))
.then((renderTpl) => {
expect(renderTpl.templateAbsUrl).toBe(null);
async.done();
});
}));
it('should not fill templateAbsUrl given template url with empty string',
inject([AsyncTestCompleter], (async) => {
cmpUrlMapper.setComponentUrl(MainComponent, '/cmp/main.js');
captureTemplate(new ViewMetadata({template: null, templateUrl: ''}))
.then((renderTpl) => {
expect(renderTpl.templateAbsUrl).toBe(null);
async.done();
});
}));
it('should not fill templateAbsUrl given template url with blank string',
inject([AsyncTestCompleter], (async) => {
cmpUrlMapper.setComponentUrl(MainComponent, '/cmp/main.js');
captureTemplate(new ViewMetadata({template: null, templateUrl: ' '}))
.then((renderTpl) => {
expect(renderTpl.templateAbsUrl).toBe(null);
async.done();
});
}));
it('should fill templateAbsUrl given url template', inject([AsyncTestCompleter], (async) => {
cmpUrlMapper.setComponentUrl(MainComponent, '/cmp/main.js');
captureTemplate(new ViewMetadata({templateUrl: 'tpl/main.html'}))
.then((renderTpl) => {
expect(renderTpl.templateAbsUrl).toEqual('http://www.app.com/cmp/tpl/main.html');
async.done();
});
}));
it('should fill styleAbsUrls given styleUrls', inject([AsyncTestCompleter], (async) => {
cmpUrlMapper.setComponentUrl(MainComponent, '/cmp/main.js');
captureTemplate(new ViewMetadata({styleUrls: ['css/1.css', 'css/2.css']}))
.then((renderTpl) => {
expect(renderTpl.styleAbsUrls)
.toEqual(
['http://www.app.com/cmp/css/1.css', 'http://www.app.com/cmp/css/2.css']);
async.done();
});
}));
it('should fill directive.id', inject([AsyncTestCompleter], (async) => {
captureDirective(MainComponent)
.then((renderDir) => {
expect(renderDir.id).toEqual(stringify(MainComponent));
async.done();
});
}));
it('should fill directive.selector', inject([AsyncTestCompleter], (async) => {
captureDirective(MainComponent)
.then((renderDir) => {
expect(renderDir.selector).toEqual('main-comp');
async.done();
});
}));
it('should fill directive.type for components', inject([AsyncTestCompleter], (async) => {
captureDirective(MainComponent)
.then((renderDir) => {
expect(renderDir.type).toEqual(RenderDirectiveMetadata.COMPONENT_TYPE);
async.done();
});
}));
it('should fill directive.type for dynamic components',
inject([AsyncTestCompleter], (async) => {
captureDirective(SomeDynamicComponentDirective)
.then((renderDir) => {
expect(renderDir.type).toEqual(RenderDirectiveMetadata.COMPONENT_TYPE);
async.done();
});
}));
it('should fill directive.type for decorator directives',
inject([AsyncTestCompleter], (async) => {
captureDirective(SomeDirective)
.then((renderDir) => {
expect(renderDir.type).toEqual(RenderDirectiveMetadata.DIRECTIVE_TYPE);
async.done();
});
}));
it('should set directive.compileChildren to false for other directives',
inject([AsyncTestCompleter], (async) => {
captureDirective(MainComponent)
.then((renderDir) => {
expect(renderDir.compileChildren).toEqual(true);
async.done();
});
}));
it('should set directive.compileChildren to true for decorator directives',
inject([AsyncTestCompleter], (async) => {
captureDirective(SomeDirective)
.then((renderDir) => {
expect(renderDir.compileChildren).toEqual(true);
async.done();
});
}));
it('should set directive.compileChildren to false for decorator directives',
inject([AsyncTestCompleter], (async) => {
captureDirective(IgnoreChildrenDirective)
.then((renderDir) => {
expect(renderDir.compileChildren).toEqual(false);
async.done();
});
}));
it('should set directive.hostListeners', inject([AsyncTestCompleter], (async) => {
captureDirective(DirectiveWithEvents)
.then((renderDir) => {
expect(renderDir.hostListeners)
.toEqual(MapWrapper.createFromStringMap({'someEvent': 'someAction'}));
async.done();
});
}));
it('should set directive.hostProperties', inject([AsyncTestCompleter], (async) => {
captureDirective(DirectiveWithProperties)
.then((renderDir) => {
expect(renderDir.hostProperties)
.toEqual(MapWrapper.createFromStringMap({'someProp': 'someExp'}));
async.done();
});
}));
it('should set directive.bind', inject([AsyncTestCompleter], (async) => {
captureDirective(DirectiveWithBind)
.then((renderDir) => {
expect(renderDir.inputs).toEqual(['a: b']);
async.done();
});
}));
it('should read @Attribute', inject([AsyncTestCompleter], (async) => {
captureDirective(DirectiveWithAttributes)
.then((renderDir) => {
expect(renderDir.readAttributes).toEqual(['someAttr']);
async.done();
});
}));
});
describe('call ProtoViewFactory', () => {
it('should pass the ProtoViewDto', inject([AsyncTestCompleter], (async) => {
tplResolver.setView(MainComponent, new ViewMetadata({template: '<div></div>'}));
var renderProtoView = createRenderProtoView();
var expectedProtoView = createProtoView();
var compiler = createCompiler([renderProtoView], [rootProtoView, expectedProtoView]);
compiler.compileInHost(MainComponent)
.then((_) => {
var request = protoViewFactory.requests[1];
expect(request[1]).toBe(renderProtoView);
async.done();
});
}));
it('should pass the component binding', inject([AsyncTestCompleter], (async) => {
tplResolver.setView(MainComponent, new ViewMetadata({template: '<div></div>'}));
var compiler =
createCompiler([createRenderProtoView()], [rootProtoView, createProtoView()]);
compiler.compileInHost(MainComponent)
.then((_) => {
var request = protoViewFactory.requests[1];
expect(request[0].key.token).toBe(MainComponent);
async.done();
});
}));
it('should pass the directive bindings', inject([AsyncTestCompleter], (async) => {
tplResolver.setView(
MainComponent,
new ViewMetadata({template: '<div></div>', directives: [SomeDirective]}));
var compiler =
createCompiler([createRenderProtoView()], [rootProtoView, createProtoView()]);
compiler.compileInHost(MainComponent)
.then((_) => {
var request = protoViewFactory.requests[1];
var binding = request[2][0];
expect(binding.key.token).toBe(SomeDirective);
async.done();
});
}));
it('should pass the pipe bindings', inject([AsyncTestCompleter], (async) => {
tplResolver.setView(MainComponent,
new ViewMetadata({template: '<div></div>', pipes: [SomePipe]}));
var compiler =
createCompiler([createRenderProtoView()], [rootProtoView, createProtoView()]);
compiler.compileInHost(MainComponent)
.then((_) => {
var request = protoViewFactory.requests[1];
expect(request[3][0].key.token).toBe(SomeDefaultPipe);
expect(request[3][1].key.token).toBe(SomePipe);
async.done();
});
}));
it('should use the protoView of the ProtoViewFactory',
inject([AsyncTestCompleter], (async) => {
tplResolver.setView(MainComponent, new ViewMetadata({template: '<div></div>'}));
var compiler =
createCompiler([createRenderProtoView()], [rootProtoView, createProtoView()]);
compiler.compileInHost(MainComponent)
.then((protoViewRef) => {
expect(internalProtoView(protoViewRef)).toBe(rootProtoView);
async.done();
});
}));
});
it('should load nested components', inject([AsyncTestCompleter], (async) => {
tplResolver.setView(MainComponent, new ViewMetadata({template: '<div></div>'}));
tplResolver.setView(NestedComponent, new ViewMetadata({template: '<div></div>'}));
var mainProtoView =
createProtoView([createComponentElementBinder(directiveResolver, NestedComponent)]);
var nestedProtoView = createProtoView();
var renderPvDtos = [
createRenderProtoView([createRenderComponentElementBinder(0)]),
createRenderProtoView()
];
var compiler =
createCompiler(renderPvDtos, [rootProtoView, mainProtoView, nestedProtoView]);
compiler.compileInHost(MainComponent)
.then((protoViewRef) => {
expect(originalRenderProtoViewRefs(internalProtoView(protoViewRef)))
.toEqual(
[rootProtoView.render, [mainProtoView.render, [nestedProtoView.render]]]);
expect(internalProtoView(protoViewRef).elementBinders[0].nestedProtoView)
.toBe(mainProtoView);
expect(mainProtoView.elementBinders[0].nestedProtoView).toBe(nestedProtoView);
it('should read the template from an annotation', inject([AsyncTestCompleter], (async) => {
compiler.compileInHost(SomeComponent)
.then((_) => {
expect(protoViewFactorySpy.spy('createHost')).toHaveBeenCalledWith(cht);
async.done();
});
}));
it('should load nested components in viewcontainers', inject([AsyncTestCompleter], (async) => {
tplResolver.setView(MainComponent, new ViewMetadata({template: '<div></div>'}));
tplResolver.setView(NestedComponent, new ViewMetadata({template: '<div></div>'}));
var viewportProtoView = createProtoView(
[createComponentElementBinder(directiveResolver, NestedComponent)], ViewType.EMBEDDED);
var mainProtoView = createProtoView([createViewportElementBinder(viewportProtoView)]);
var nestedProtoView = createProtoView();
var renderPvDtos = [
createRenderProtoView([
createRenderViewportElementBinder(
createRenderProtoView([createRenderComponentElementBinder(0)], ViewType.EMBEDDED))
]),
createRenderProtoView()
];
var compiler =
createCompiler(renderPvDtos, [rootProtoView, mainProtoView, nestedProtoView]);
compiler.compileInHost(MainComponent)
.then((protoViewRef) => {
expect(internalProtoView(protoViewRef).elementBinders[0].nestedProtoView)
.toBe(mainProtoView);
expect(originalRenderProtoViewRefs(internalProtoView(protoViewRef)))
.toEqual([rootProtoView.render, [mainProtoView.render, null]]);
expect(viewportProtoView.elementBinders[0].nestedProtoView).toBe(nestedProtoView);
expect(originalRenderProtoViewRefs(viewportProtoView))
.toEqual([viewportProtoView.render, [nestedProtoView.render]]);
async.done();
});
}));
it('should cache compiled host components', inject([AsyncTestCompleter], (async) => {
tplResolver.setView(MainComponent, new ViewMetadata({template: '<div></div>'}));
var mainPv = createProtoView();
var compiler = createCompiler([createRenderProtoView([])], [rootProtoView, mainPv]);
compiler.compileInHost(MainComponent)
.then((protoViewRef) => {
expect(internalProtoView(protoViewRef).elementBinders[0].nestedProtoView)
.toBe(mainPv);
return compiler.compileInHost(MainComponent);
})
.then((protoViewRef) => {
expect(internalProtoView(protoViewRef).elementBinders[0].nestedProtoView)
.toBe(mainPv);
async.done();
});
}));
it('should not bind directives for cached components', inject([AsyncTestCompleter], (async) => {
// set up the cache with the test proto view
var mainPv: AppProtoView = createProtoView();
var cache: CompilerCache = new CompilerCache();
cache.setHost(MainComponent, mainPv);
// create the spy resolver
var reader: any = new SpyDirectiveResolver();
// create the compiler
var compiler = new Compiler(reader, pipeResolver, [], cache, tplResolver, cmpUrlMapper,
new UrlResolver(), renderCompiler, protoViewFactory,
new AppRootUrl("http://www.app.com"));
compiler.compileInHost(MainComponent)
.then((protoViewRef) => {
// the test should have failed if the resolver was called, so we're good
async.done();
});
}));
it('should cache compiled nested components', inject([AsyncTestCompleter], (async) => {
tplResolver.setView(MainComponent, new ViewMetadata({template: '<div></div>'}));
tplResolver.setView(MainComponent2, new ViewMetadata({template: '<div></div>'}));
tplResolver.setView(NestedComponent, new ViewMetadata({template: '<div></div>'}));
var rootProtoView2 = createRootProtoView(directiveResolver, MainComponent2);
var mainPv =
createProtoView([createComponentElementBinder(directiveResolver, NestedComponent)]);
var nestedPv = createProtoView([]);
var compiler = createCompiler(
[createRenderProtoView(), createRenderProtoView(), createRenderProtoView()],
[rootProtoView, mainPv, nestedPv, rootProtoView2, mainPv]);
compiler.compileInHost(MainComponent)
.then((protoViewRef) => {
expect(internalProtoView(protoViewRef)
.elementBinders[0]
.nestedProtoView.elementBinders[0]
.nestedProtoView)
.toBe(nestedPv);
return compiler.compileInHost(MainComponent2);
})
.then((protoViewRef) => {
expect(internalProtoView(protoViewRef)
.elementBinders[0]
.nestedProtoView.elementBinders[0]
.nestedProtoView)
.toBe(nestedPv);
async.done();
});
}));
it('should re-use components being compiled', inject([AsyncTestCompleter], (async) => {
tplResolver.setView(MainComponent, new ViewMetadata({template: '<div></div>'}));
var renderProtoViewCompleter: PromiseCompleter<ProtoViewDto> = PromiseWrapper.completer();
var expectedProtoView = createProtoView();
var compiler = createCompiler([renderProtoViewCompleter.promise],
[rootProtoView, rootProtoView, expectedProtoView]);
var result = PromiseWrapper.all([
compiler.compileInHost(MainComponent),
compiler.compileInHost(MainComponent),
renderProtoViewCompleter.promise
]);
renderProtoViewCompleter.resolve(createRenderProtoView());
result.then((protoViewRefs) => {
expect(internalProtoView(protoViewRefs[0]).elementBinders[0].nestedProtoView)
.toBe(expectedProtoView);
expect(internalProtoView(protoViewRefs[1]).elementBinders[0].nestedProtoView)
.toBe(expectedProtoView);
async.done();
});
}));
it('should throw on unconditional recursive components',
inject([AsyncTestCompleter], (async) => {
tplResolver.setView(MainComponent, new ViewMetadata({template: '<div></div>'}));
var mainProtoView =
createProtoView([createComponentElementBinder(directiveResolver, MainComponent)]);
var compiler =
createCompiler([createRenderProtoView([createRenderComponentElementBinder(0)])],
[rootProtoView, mainProtoView]);
PromiseWrapper.catchError(compiler.compileInHost(MainComponent), (e) => {
expect(() => { throw e; })
.toThrowError(`Unconditional component cycle in ${stringify(MainComponent)}`);
async.done();
return null;
});
}));
it('should allow recursive components that are connected via an embedded ProtoView',
inject([AsyncTestCompleter], (async) => {
tplResolver.setView(MainComponent, new ViewMetadata({template: '<div></div>'}));
var viewportProtoView = createProtoView(
[createComponentElementBinder(directiveResolver, MainComponent)], ViewType.EMBEDDED);
var mainProtoView = createProtoView([createViewportElementBinder(viewportProtoView)]);
var renderPvDtos = [
createRenderProtoView([
createRenderViewportElementBinder(
createRenderProtoView([createRenderComponentElementBinder(0)], ViewType.EMBEDDED))
]),
createRenderProtoView()
];
var compiler = createCompiler(renderPvDtos, [rootProtoView, mainProtoView]);
compiler.compileInHost(MainComponent)
.then((protoViewRef) => {
expect(internalProtoView(protoViewRef).elementBinders[0].nestedProtoView)
.toBe(mainProtoView);
expect(mainProtoView.elementBinders[0]
.nestedProtoView.elementBinders[0]
.nestedProtoView)
.toBe(mainProtoView);
// In case of a cycle, don't merge the embedded proto views into the component!
expect(originalRenderProtoViewRefs(internalProtoView(protoViewRef)))
.toEqual([rootProtoView.render, [mainProtoView.render, null]]);
expect(originalRenderProtoViewRefs(viewportProtoView))
.toEqual([viewportProtoView.render, [mainProtoView.render, null]]);
async.done();
});
}));
it('should throw on recursive components that are connected via an embedded ProtoView with <ng-content>',
inject([AsyncTestCompleter], (async) => {
tplResolver.setView(MainComponent, new ViewMetadata({template: '<div></div>'}));
var viewportProtoView =
createProtoView([createComponentElementBinder(directiveResolver, MainComponent)],
ViewType.EMBEDDED, true);
var mainProtoView = createProtoView([createViewportElementBinder(viewportProtoView)]);
var renderPvDtos = [
createRenderProtoView([
createRenderViewportElementBinder(
createRenderProtoView([createRenderComponentElementBinder(0)], ViewType.EMBEDDED))
]),
createRenderProtoView()
];
var compiler = createCompiler(renderPvDtos, [rootProtoView, mainProtoView]);
PromiseWrapper.catchError(compiler.compileInHost(MainComponent), (e) => {
expect(() => { throw e; })
.toThrowError(
`<ng-content> is used within the recursive path of ${stringify(MainComponent)}`);
async.done();
return null;
});
}));
it('should create host proto views', inject([AsyncTestCompleter], (async) => {
tplResolver.setView(MainComponent, new ViewMetadata({template: '<div></div>'}));
var rootProtoView = createProtoView(
[createComponentElementBinder(directiveResolver, MainComponent)], ViewType.HOST);
var mainProtoView = createProtoView();
var compiler = createCompiler([createRenderProtoView()], [rootProtoView, mainProtoView]);
compiler.compileInHost(MainComponent)
.then((protoViewRef) => {
expect(internalProtoView(protoViewRef)).toBe(rootProtoView);
expect(rootProtoView.elementBinders[0].nestedProtoView).toBe(mainProtoView);
async.done();
});
}));
it('should throw for non component types', () => {
var compiler = createCompiler([], []);
expect(() => compiler.compileInHost(SomeDirective))
.toThrowError(
`Could not load '${stringify(SomeDirective)}' because it is not a component.`);
it('should clear the cache', () => {
compiler.clearCache();
expect(protoViewFactorySpy.spy('clearCache')).toHaveBeenCalled();
});
});
}
function createDirectiveBinding(directiveResolver, type): DirectiveBinding {
var annotation = directiveResolver.resolve(type);
return DirectiveBinding.createFromType(type, annotation);
}
function createProtoView(elementBinders = null, type: ViewType = null,
isEmbeddedFragment: boolean = false): AppProtoView {
if (isBlank(type)) {
type = ViewType.COMPONENT;
}
var pv = new AppProtoView(type, isEmbeddedFragment, new RenderProtoViewRef(), null, null,
new Map<string, number>(), null, null);
if (isBlank(elementBinders)) {
elementBinders = [];
}
pv.elementBinders = elementBinders;
return pv;
}
function createComponentElementBinder(directiveResolver, type): ElementBinder {
var binding = createDirectiveBinding(directiveResolver, type);
return new ElementBinder(0, null, 0, null, binding);
}
function createViewportElementBinder(nestedProtoView): ElementBinder {
var elBinder = new ElementBinder(0, null, 0, null, null);
elBinder.nestedProtoView = nestedProtoView;
return elBinder;
}
function createRenderProtoView(elementBinders = null, type: ViewType = null): ProtoViewDto {
if (isBlank(type)) {
type = ViewType.COMPONENT;
}
if (isBlank(elementBinders)) {
elementBinders = [];
}
return new ProtoViewDto(
{elementBinders: elementBinders, type: type, render: new RenderProtoViewRef()});
}
function createRenderComponentElementBinder(directiveIndex): RenderElementBinder {
return new RenderElementBinder(
{directives: [new DirectiveBinder({directiveIndex: directiveIndex})]});
}
function createRenderViewportElementBinder(nestedProtoView): RenderElementBinder {
return new RenderElementBinder({nestedProtoView: nestedProtoView});
}
function createRootProtoView(directiveResolver, type): AppProtoView {
return createProtoView([createComponentElementBinder(directiveResolver, type)], ViewType.HOST);
}
@Component({selector: 'main-comp'})
class MainComponent {
}
@Component({selector: 'main-comp2'})
class MainComponent2 {
}
@Component({selector: 'nested'})
class NestedComponent {
}
class RecursiveComponent {}
@Component({selector: 'some-dynamic'})
class SomeDynamicComponentDirective {
}
@Directive({selector: 'some'})
class SomeDirective {
}
@Directive({compileChildren: false})
class IgnoreChildrenDirective {
}
@Directive({host: {'(someEvent)': 'someAction'}})
class DirectiveWithEvents {
}
@Directive({host: {'[someProp]': 'someExp'}})
class DirectiveWithProperties {
}
@Directive({inputs: ['a: b']})
class DirectiveWithBind {
}
@Pipe({name: 'some-default-pipe'})
class SomeDefaultPipe {
}
@Pipe({name: 'some-pipe'})
class SomePipe {
}
@Directive({selector: 'directive-with-accts'})
class DirectiveWithAttributes {
constructor(@Attribute('someAttr') someAttr: String) {}
}
class FakeViewResolver extends ViewResolver {
_cmpViews = new Map<Type, ViewMetadata>();
constructor() { super(); }
resolve(component: Type): ViewMetadata {
// returns null for dynamic components
return this._cmpViews.has(component) ? this._cmpViews.get(component) : null;
}
setView(component: Type, view: ViewMetadata): void { this._cmpViews.set(component, view); }
}
class FakeProtoViewFactory extends ProtoViewFactory {
requests: any[][];
constructor(public results: AppProtoView[]) {
super(null);
this.requests = [];
}
createAppProtoViews(componentBinding: DirectiveBinding, renderProtoView: ProtoViewDto,
directives: DirectiveBinding[], pipes: PipeBinding[]): AppProtoView[] {
this.requests.push([componentBinding, renderProtoView, directives, pipes]);
return collectEmbeddedPvs(ListWrapper.removeAt(this.results, 0));
}
}
class MergedRenderProtoViewRef extends RenderProtoViewRef {
constructor(public originals: RenderProtoViewRef[]) { super(); }
}
function originalRenderProtoViewRefs(appProtoView: AppProtoView) {
return (<MergedRenderProtoViewRef>appProtoView.mergeMapping.renderProtoViewRef).originals;
}
function collectEmbeddedPvs(pv: AppProtoView, target: AppProtoView[] = null): AppProtoView[] {
if (isBlank(target)) {
target = [];
}
target.push(pv);
pv.elementBinders.forEach(elementBinder => {
if (elementBinder.hasEmbeddedProtoView()) {
collectEmbeddedPvs(elementBinder.nestedProtoView, target);
}
});
return target;
}
class SomeComponent {}

View File

@ -111,7 +111,6 @@ export function main() {
rootTC.detectChanges();
expect(rootTC.debugElement.nativeElement).toHaveText('Hello World!');
async.done();
});
}));
@ -431,7 +430,7 @@ export function main() {
tcb.overrideView(
MyComp, new ViewMetadata({
template:
'<div><template some-viewport var-greeting="some-tmpl"><copy-me>{{greeting}}</copy-me></template></div>',
'<template some-viewport var-greeting="some-tmpl"><copy-me>{{greeting}}</copy-me></template>',
directives: [SomeViewport]
}))
@ -440,11 +439,11 @@ export function main() {
rootTC.detectChanges();
var childNodesOfWrapper = rootTC.debugElement.componentViewChildren;
var childNodesOfWrapper = DOM.childNodes(rootTC.debugElement.nativeElement);
// 1 template + 2 copies.
expect(childNodesOfWrapper.length).toBe(3);
expect(childNodesOfWrapper[1].nativeElement).toHaveText('hello');
expect(childNodesOfWrapper[2].nativeElement).toHaveText('again');
expect(childNodesOfWrapper[1]).toHaveText('hello');
expect(childNodesOfWrapper[2]).toHaveText('again');
async.done();
});
}));
@ -454,7 +453,7 @@ export function main() {
tcb.overrideView(
MyComp, new ViewMetadata({
template:
'<div><copy-me template="some-viewport: var greeting=some-tmpl">{{greeting}}</copy-me></div>',
'<copy-me template="some-viewport: var greeting=some-tmpl">{{greeting}}</copy-me>',
directives: [SomeViewport]
}))
@ -462,11 +461,11 @@ export function main() {
.then((rootTC) => {
rootTC.detectChanges();
var childNodesOfWrapper = rootTC.debugElement.componentViewChildren;
var childNodesOfWrapper = DOM.childNodes(rootTC.debugElement.nativeElement);
// 1 template + 2 copies.
expect(childNodesOfWrapper.length).toBe(3);
expect(childNodesOfWrapper[1].nativeElement).toHaveText('hello');
expect(childNodesOfWrapper[2].nativeElement).toHaveText('again');
expect(childNodesOfWrapper[1]).toHaveText('hello');
expect(childNodesOfWrapper[2]).toHaveText('again');
async.done();
});
}));
@ -629,7 +628,7 @@ export function main() {
tcb.overrideView(
MyComp, new ViewMetadata({
template:
'<div><div *ng-for="var i of [1]"><child-cmp-no-template #cmp></child-cmp-no-template>{{i}}-{{cmp.ctxProp}}</div></div>',
'<template ng-for [ng-for-of]="[1]" var-i><child-cmp-no-template #cmp></child-cmp-no-template>{{i}}-{{cmp.ctxProp}}</template>',
directives: [ChildCompNoTemplate, NgFor]
}))
@ -637,8 +636,8 @@ export function main() {
.then((rootTC) => {
rootTC.detectChanges();
// Get the element at index 1, since index 0 is the <template>.
expect(rootTC.debugElement.componentViewChildren[1].nativeElement)
// Get the element at index 2, since index 0 is the <template>.
expect(DOM.childNodes(rootTC.debugElement.nativeElement)[2])
.toHaveText("1-hello");
async.done();
@ -1217,8 +1216,9 @@ export function main() {
describe("error handling", () => {
it('should report a meaningful error when a directive is missing annotation',
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
tcb = tcb.overrideView(MyComp,
new ViewMetadata({directives: [SomeDirectiveMissingAnnotation]}));
tcb = tcb.overrideView(
MyComp,
new ViewMetadata({template: '', directives: [SomeDirectiveMissingAnnotation]}));
PromiseWrapper.catchError(tcb.createAsync(MyComp), (e) => {
expect(e.message).toEqual(
@ -1229,19 +1229,20 @@ export function main() {
}));
it('should report a meaningful error when a component is missing view annotation',
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
PromiseWrapper.catchError(tcb.createAsync(ComponentWithoutView), (e) => {
inject([TestComponentBuilder], (tcb: TestComponentBuilder) => {
try {
tcb.createAsync(ComponentWithoutView);
} catch (e) {
expect(e.message).toEqual(
`No View annotation found on component ${stringify(ComponentWithoutView)}`);
async.done();
return null;
});
}
}));
it('should report a meaningful error when a directive is null',
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
tcb = tcb.overrideView(MyComp, new ViewMetadata({directives: [[null]]}));
tcb = tcb.overrideView(MyComp, new ViewMetadata({directives: [[null]], template: ''}));
PromiseWrapper.catchError(tcb.createAsync(MyComp), (e) => {
expect(e.message).toEqual(
@ -1353,7 +1354,8 @@ export function main() {
var undefinedValue;
tcb = tcb.overrideView(MyComp, new ViewMetadata({directives: [undefinedValue]}));
tcb = tcb.overrideView(MyComp,
new ViewMetadata({directives: [undefinedValue], template: ''}));
PromiseWrapper.catchError(tcb.createAsync(MyComp), (e) => {
expect(e.message).toEqual(
@ -1457,7 +1459,7 @@ export function main() {
PromiseWrapper.catchError(tcb.createAsync(MyComp), (e) => {
expect(e.message).toEqual(
`Can't bind to 'unknown' since it isn't a known property of the '<div>' element and there are no matching directives with a corresponding property`);
`Template parse errors:\nCan't bind to 'unknown' since it isn't a known native property in MyComp > div:nth-child(0)[unknown={{ctxProp}}]`);
async.done();
return null;
});
@ -1516,9 +1518,8 @@ export function main() {
describe('logging property updates', () => {
beforeEachBindings(() => [
bind(ChangeDetection)
.toValue(
new DynamicChangeDetection(new ChangeDetectorGenConfig(true, true, true, false)))
bind(ChangeDetectorGenConfig)
.toValue(new ChangeDetectorGenConfig(true, true, true, false))
]);
it('should reflect property values as attributes',

View File

@ -522,7 +522,8 @@ class OuterWithIndirectNestedComponent {
@Component({selector: 'outer'})
@View({
template: 'OUTER(<inner><ng-content></ng-content></inner>)',
template:
'OUTER(<inner><ng-content select=".left" class="left"></ng-content><ng-content></ng-content></inner>)',
directives: [forwardRef(() => InnerComponent)]
})
class OuterComponent {
@ -530,7 +531,8 @@ class OuterComponent {
@Component({selector: 'inner'})
@View({
template: 'INNER(<innerinner><ng-content></ng-content></innerinner>)',
template:
'INNER(<innerinner><ng-content select=".left" class="left"></ng-content><ng-content></ng-content></innerinner>)',
directives: [forwardRef(() => InnerInnerComponent)]
})
class InnerComponent {

View File

@ -13,7 +13,6 @@ import {
import {SpyChangeDetection} from '../spies';
import {isBlank, stringify} from 'angular2/src/core/facade/lang';
import {MapWrapper} from 'angular2/src/core/facade/collection';
import {
ChangeDetection,
@ -24,10 +23,7 @@ import {
} from 'angular2/src/core/change_detection/change_detection';
import {
BindingRecordsCreator,
ProtoViewFactory,
getChangeDetectorDefinitions,
createDirectiveVariableBindings,
createVariableLocations
getChangeDetectorDefinitions
} from 'angular2/src/core/compiler/proto_view_factory';
import {Component, Directive} from 'angular2/src/core/metadata';
import {Key, Binding} from 'angular2/core';
@ -43,18 +39,14 @@ import {
} from 'angular2/src/core/render/api';
export function main() {
// TODO(tbosch): add missing tests
describe('ProtoViewFactory', () => {
var changeDetection;
var protoViewFactory: ProtoViewFactory;
var directiveResolver;
beforeEach(() => {
directiveResolver = new DirectiveResolver();
changeDetection = new SpyChangeDetection();
changeDetection.prop("generateDetectors", true);
protoViewFactory = new ProtoViewFactory(changeDetection);
});
function bindDirective(type) {
@ -73,107 +65,6 @@ export function main() {
});
describe('createAppProtoViews', () => {
it('should create an AppProtoView for the root render proto view', () => {
var varBindings = new Map();
varBindings.set('a', 'b');
var renderPv = createRenderProtoView([], null, varBindings);
var appPvs =
protoViewFactory.createAppProtoViews(bindDirective(MainComponent), renderPv, [], []);
expect(appPvs[0].variableBindings.get('a')).toEqual('b');
expect(appPvs.length).toBe(1);
});
});
describe("createDirectiveVariableBindings", () => {
it("should calculate directive variable bindings", () => {
var dvbs = createDirectiveVariableBindings(
new RenderElementBinder({
variableBindings:
MapWrapper.createFromStringMap<string>({"exportName": "templateName"})
}),
[
directiveBinding(
{metadata: RenderDirectiveMetadata.create({exportAs: 'exportName'})}),
directiveBinding(
{metadata: RenderDirectiveMetadata.create({exportAs: 'otherName'})})
]);
expect(dvbs).toEqual(MapWrapper.createFromStringMap<number>({"templateName": 0}));
});
it("should set exportAs to $implicit for component with exportAs = null", () => {
var dvbs = createDirectiveVariableBindings(
new RenderElementBinder({
variableBindings:
MapWrapper.createFromStringMap<string>({"$implicit": "templateName"})
}),
[
directiveBinding({
metadata: RenderDirectiveMetadata.create(
{exportAs: null, type: RenderDirectiveMetadata.COMPONENT_TYPE})
})
]);
expect(dvbs).toEqual(MapWrapper.createFromStringMap<number>({"templateName": 0}));
});
it("should throw we no directive exported with this name", () => {
expect(() => {
createDirectiveVariableBindings(
new RenderElementBinder({
variableBindings:
MapWrapper.createFromStringMap<string>({"someInvalidName": "templateName"})
}),
[
directiveBinding(
{metadata: RenderDirectiveMetadata.create({exportAs: 'exportName'})})
]);
}).toThrowError(new RegExp("Cannot find directive with exportAs = 'someInvalidName'"));
});
it("should throw when binding to a name exported by two directives", () => {
expect(() => {
createDirectiveVariableBindings(
new RenderElementBinder({
variableBindings:
MapWrapper.createFromStringMap<string>({"exportName": "templateName"})
}),
[
directiveBinding(
{metadata: RenderDirectiveMetadata.create({exportAs: 'exportName'})}),
directiveBinding(
{metadata: RenderDirectiveMetadata.create({exportAs: 'exportName'})})
]);
}).toThrowError(new RegExp("More than one directive have exportAs = 'exportName'"));
});
it("should not throw when not binding to a name exported by two directives", () => {
expect(() => {
createDirectiveVariableBindings(
new RenderElementBinder({variableBindings: new Map<string, string>()}), [
directiveBinding(
{metadata: RenderDirectiveMetadata.create({exportAs: 'exportName'})}),
directiveBinding(
{metadata: RenderDirectiveMetadata.create({exportAs: 'exportName'})})
]);
}).not.toThrow();
});
});
describe('createVariableLocations', () => {
it('should merge the names in the template for all ElementBinders', () => {
expect(createVariableLocations([
new RenderElementBinder(
{variableBindings: MapWrapper.createFromStringMap<string>({"x": "a"})}),
new RenderElementBinder(
{variableBindings: MapWrapper.createFromStringMap<string>({"y": "b"})})
])).toEqual(MapWrapper.createFromStringMap<number>({'a': 0, 'b': 1}));
});
});
describe('BindingRecordsCreator', () => {
var creator: BindingRecordsCreator;
@ -247,15 +138,6 @@ function createRenderProtoView(elementBinders = null, type: ViewType = null,
});
}
function createRenderComponentElementBinder(directiveIndex) {
return new RenderElementBinder(
{directives: [new DirectiveBinder({directiveIndex: directiveIndex})]});
}
function createRenderViewportElementBinder(nestedProtoView) {
return new RenderElementBinder({nestedProtoView: nestedProtoView});
}
@Component({selector: 'main-comp'})
class MainComponent {
}

View File

@ -34,7 +34,7 @@ export function main() {
viewManager = new SpyAppViewManager();
view = new SpyView();
view.prop("viewContainers", [null]);
location = new ElementRef(new ViewRef(view), 0, 0, null);
location = new ElementRef(new ViewRef(view), 0, null);
});
describe('length', () => {

View File

@ -13,15 +13,10 @@ import {
it,
xit
} from 'angular2/test_lib';
import {SpyRenderer, SpyAppViewPool, SpyAppViewListener} from '../spies';
import {SpyRenderer, SpyAppViewPool, SpyAppViewListener, SpyProtoViewFactory} from '../spies';
import {Injector, bind} from 'angular2/core';
import {
AppProtoView,
AppView,
AppViewContainer,
AppProtoViewMergeMapping
} from 'angular2/src/core/compiler/view';
import {AppProtoView, AppView, AppViewContainer} from 'angular2/src/core/compiler/view';
import {ProtoViewRef, ViewRef, internalView} from 'angular2/src/core/compiler/view_ref';
import {ElementRef} from 'angular2/src/core/compiler/element_ref';
import {TemplateRef} from 'angular2/src/core/compiler/template_ref';
@ -54,6 +49,7 @@ export function main() {
var utils: AppViewManagerUtils;
var viewListener;
var viewPool;
var linker;
var manager: AppViewManager;
var createdRenderViews: RenderViewWithFragments[];
@ -78,7 +74,8 @@ export function main() {
utils = new AppViewManagerUtils();
viewListener = new SpyAppViewListener();
viewPool = new SpyAppViewPool();
manager = new AppViewManager(viewPool, viewListener, utils, renderer);
linker = new SpyProtoViewFactory();
manager = new AppViewManager(viewPool, viewListener, utils, renderer, linker);
createdRenderViews = [];
renderer.spy('createRootHostView')
@ -110,6 +107,11 @@ export function main() {
beforeEach(
() => { hostProtoView = createHostPv([createNestedElBinder(createComponentPv())]); });
it('should initialize the ProtoView', () => {
manager.createRootHostView(wrapPv(hostProtoView), null, null);
expect(linker.spy('initializeProtoViewIfNeeded')).toHaveBeenCalledWith(hostProtoView);
});
it('should create the view', () => {
var rootView =
internalView(<ViewRef>manager.createRootHostView(wrapPv(hostProtoView), null, null));
@ -129,8 +131,8 @@ export function main() {
var rootView =
internalView(<ViewRef>manager.createRootHostView(wrapPv(hostProtoView), null, null));
expect(renderer.spy('createRootHostView'))
.toHaveBeenCalledWith(hostProtoView.mergeMapping.renderProtoViewRef,
hostProtoView.mergeMapping.renderFragmentCount, 'someComponent');
.toHaveBeenCalledWith(hostProtoView.render,
hostProtoView.mergeInfo.embeddedViewCount + 1, 'someComponent');
expect(rootView.render).toBe(createdRenderViews[0].viewRef);
expect(rootView.renderFragment).toBe(createdRenderViews[0].fragmentRefs[0]);
});
@ -139,8 +141,8 @@ export function main() {
var selector = 'someOtherSelector';
internalView(<ViewRef>manager.createRootHostView(wrapPv(hostProtoView), selector, null));
expect(renderer.spy('createRootHostView'))
.toHaveBeenCalledWith(hostProtoView.mergeMapping.renderProtoViewRef,
hostProtoView.mergeMapping.renderFragmentCount, selector);
.toHaveBeenCalledWith(hostProtoView.render,
hostProtoView.mergeInfo.embeddedViewCount + 1, selector);
});
it('should set the event dispatcher', () => {
@ -182,7 +184,7 @@ export function main() {
});
describe('createViewInContainer', () => {
describe('createEmbeddedViewInContainer', () => {
describe('basic functionality', () => {
var hostView: AppView;
@ -200,6 +202,11 @@ export function main() {
resetSpies();
});
it('should initialize the ProtoView', () => {
manager.createEmbeddedViewInContainer(vcRef, 0, templateRef);
expect(linker.spy('initializeProtoViewIfNeeded')).toHaveBeenCalledWith(childProtoView);
});
describe('create the first view', () => {
it('should create an AppViewContainer if not yet existing', () => {
@ -255,8 +262,8 @@ export function main() {
expect(childView).not.toBe(firstChildView);
expect(viewListener.spy('viewCreated')).toHaveBeenCalledWith(childView);
expect(renderer.spy('createView'))
.toHaveBeenCalledWith(childProtoView.mergeMapping.renderProtoViewRef,
childProtoView.mergeMapping.renderFragmentCount);
.toHaveBeenCalledWith(childProtoView.render,
childProtoView.mergeInfo.embeddedViewCount + 1);
expect(childView.render).toBe(createdRenderViews[1].viewRef);
expect(childView.renderFragment).toBe(createdRenderViews[1].fragmentRefs[0]);
});
@ -306,6 +313,12 @@ export function main() {
describe('create a host view', () => {
it('should initialize the ProtoView', () => {
var newHostPv = createHostPv([createNestedElBinder(createComponentPv())]);
manager.createHostViewInContainer(vcRef, 0, wrapPv(newHostPv), null);
expect(linker.spy('initializeProtoViewIfNeeded')).toHaveBeenCalledWith(newHostPv);
});
it('should always create a new view and not use the embedded view', () => {
var newHostPv = createHostPv([createNestedElBinder(createComponentPv())]);
var newHostView = internalView(
@ -314,8 +327,7 @@ export function main() {
expect(newHostView).not.toBe(hostView.views[2]);
expect(viewListener.spy('viewCreated')).toHaveBeenCalledWith(newHostView);
expect(renderer.spy('createView'))
.toHaveBeenCalledWith(newHostPv.mergeMapping.renderProtoViewRef,
newHostPv.mergeMapping.renderFragmentCount);
.toHaveBeenCalledWith(newHostPv.render, newHostPv.mergeInfo.embeddedViewCount + 1);
});
});

View File

@ -18,7 +18,6 @@ import {
import {
SpyChangeDetector,
SpyProtoChangeDetector,
SpyProtoElementInjector,
SpyElementInjector,
SpyPreBuiltObjects
@ -26,9 +25,8 @@ import {
import {Injector, bind} from 'angular2/core';
import {isBlank, isPresent} from 'angular2/src/core/facade/lang';
import {MapWrapper, ListWrapper, StringMapWrapper} from 'angular2/src/core/facade/collection';
import {AppProtoView, AppView, AppProtoViewMergeMapping} from 'angular2/src/core/compiler/view';
import {AppProtoView, AppView, AppProtoViewMergeInfo} from 'angular2/src/core/compiler/view';
import {ElementBinder} from 'angular2/src/core/compiler/element_binder';
import {
DirectiveBinding,
@ -208,16 +206,19 @@ export function createInjector() {
function createElementInjector(parent = null) {
var host = new SpyElementInjector();
var elementInjector = new SpyElementInjector();
var res = SpyObject.stub(elementInjector,
{
'isExportingComponent': false,
'isExportingElement': false,
'getEventEmitterAccessors': [],
'getHostActionAccessors': [],
'getComponent': new Object(),
'getHost': host
},
{});
var _preBuiltObjects = null;
var res = SpyObject.stub(elementInjector, {
'isExportingComponent': false,
'isExportingElement': false,
'getEventEmitterAccessors': [],
'getHostActionAccessors': [],
'getComponent': new Object(),
'getHost': host
});
res.spy('getNestedView').andCallFake(() => _preBuiltObjects.nestedView);
res.spy('hydrate')
.andCallFake((mperativelyCreatedInjector: Injector, host: ElementInjector,
preBuiltObjects: PreBuiltObjects) => { _preBuiltObjects = preBuiltObjects; });
res.prop('parent', parent);
return res;
}
@ -232,7 +233,7 @@ export function createProtoElInjector(parent: ProtoElementInjector = null): Prot
export function createEmptyElBinder(parent: ElementBinder = null) {
var parentPeli = isPresent(parent) ? parent.protoElementInjector : null;
return new ElementBinder(0, null, 0, createProtoElInjector(parentPeli), null);
return new ElementBinder(0, null, 0, createProtoElInjector(parentPeli), null, null);
}
export function createNestedElBinder(nestedProtoView: AppProtoView) {
@ -241,78 +242,35 @@ export function createNestedElBinder(nestedProtoView: AppProtoView) {
var annotation = new DirectiveResolver().resolve(SomeComponent);
componentBinding = DirectiveBinding.createFromType(SomeComponent, annotation);
}
var binder = new ElementBinder(0, null, 0, createProtoElInjector(), componentBinding);
binder.nestedProtoView = nestedProtoView;
return binder;
}
function countNestedElementBinders(pv: AppProtoView): number {
var result = pv.elementBinders.length;
pv.elementBinders.forEach(binder => {
if (isPresent(binder.nestedProtoView)) {
result += countNestedElementBinders(binder.nestedProtoView);
}
});
return result;
}
function calcHostElementIndicesByViewIndex(pv: AppProtoView, elementOffset = 0,
target: number[] = null): number[] {
if (isBlank(target)) {
target = [null];
}
for (var binderIdx = 0; binderIdx < pv.elementBinders.length; binderIdx++) {
var binder = pv.elementBinders[binderIdx];
if (isPresent(binder.nestedProtoView)) {
target.push(elementOffset + binderIdx);
calcHostElementIndicesByViewIndex(binder.nestedProtoView,
elementOffset + pv.elementBinders.length, target);
elementOffset += countNestedElementBinders(binder.nestedProtoView);
}
}
return target;
}
function countNestedProtoViews(pv: AppProtoView, target: number[] = null): number[] {
if (isBlank(target)) {
target = [];
}
target.push(null);
var resultIndex = target.length - 1;
var count = 0;
for (var binderIdx = 0; binderIdx < pv.elementBinders.length; binderIdx++) {
var binder = pv.elementBinders[binderIdx];
if (isPresent(binder.nestedProtoView)) {
var nextResultIndex = target.length;
countNestedProtoViews(binder.nestedProtoView, target);
count += target[nextResultIndex] + 1;
}
}
target[resultIndex] = count;
return target;
return new ElementBinder(0, null, 0, createProtoElInjector(), componentBinding, nestedProtoView);
}
function _createProtoView(type: ViewType, binders: ElementBinder[] = null) {
if (isBlank(binders)) {
binders = [];
}
var protoChangeDetector = <any>new SpyProtoChangeDetector();
protoChangeDetector.spy('instantiate').andReturn(new SpyChangeDetector());
var res = new AppProtoView(type, null, null, protoChangeDetector, null, null, 0, null);
res.elementBinders = binders;
var mappedElementIndices = ListWrapper.createFixedSize(countNestedElementBinders(res));
var res = new AppProtoView([], type, true, (_) => new SpyChangeDetector(), new Map<string, any>(),
null);
var mergedElementCount = 0;
var mergedEmbeddedViewCount = 0;
var mergedViewCount = 1;
for (var i = 0; i < binders.length; i++) {
var binder = binders[i];
mappedElementIndices[i] = i;
binder.protoElementInjector.index = i;
mergedElementCount++;
var nestedPv = binder.nestedProtoView;
if (isPresent(nestedPv)) {
mergedElementCount += nestedPv.mergeInfo.elementCount;
mergedEmbeddedViewCount += nestedPv.mergeInfo.embeddedViewCount;
mergedViewCount += nestedPv.mergeInfo.viewCount;
if (nestedPv.type === ViewType.EMBEDDED) {
mergedEmbeddedViewCount++;
}
}
}
var hostElementIndicesByViewIndex = calcHostElementIndicesByViewIndex(res);
if (type === ViewType.EMBEDDED || type === ViewType.HOST) {
res.mergeMapping = new AppProtoViewMergeMapping(
new RenderProtoViewMergeMapping(null, hostElementIndicesByViewIndex.length,
mappedElementIndices, mappedElementIndices.length, [],
hostElementIndicesByViewIndex, countNestedProtoViews(res)));
}
var mergeInfo =
new AppProtoViewMergeInfo(mergedEmbeddedViewCount, mergedElementCount, mergedViewCount);
res.init(null, binders, 0, mergeInfo, new Map<string, number>());
return res;
}

View File

@ -24,12 +24,10 @@ export function main() {
function createViewPool({capacity}): AppViewPool { return new AppViewPool(capacity); }
function createProtoView() {
return new AppProtoView(null, null, null, null, null, null, null, null);
}
function createProtoView() { return new AppProtoView(null, null, null, null, null, null); }
function createView(pv) {
return new AppView(null, pv, null, null, null, null, new Map<string, any>(), null, null);
return new AppView(null, pv, null, null, null, new Map<string, any>(), null, null, null);
}
it('should support multiple AppProtoViews', () => {

View File

@ -51,7 +51,7 @@ class MessageDir {
template: `<div class="child" message="child">
<span class="childnested" message="nestedchild">Child</span>
</div>
<span class="child">{{childBinding}}</span>`,
<span class="child" [inner-html]="childBinding"></span>`,
directives: [MessageDir]
})
@Injectable()
@ -66,7 +66,7 @@ class ChildComp {
template: `<div class="parent" message="parent">
<span class="parentnested" message="nestedparent">Parent</span>
</div>
<span class="parent">{{parentBinding}}</span>
<span class="parent" [inner-html]="parentBinding"></span>
<child-comp class="child-comp-class"></child-comp>`,
directives: [ChildComp, MessageDir]
})
@ -107,9 +107,9 @@ class EventsComp {
@Component({selector: 'using-for', viewBindings: [Logger]})
@View({
template: `<span *ng-for="#thing of stuff">{{thing}}</span>
template: `<span *ng-for="#thing of stuff" [inner-html]="thing"></span>
<ul message="list">
<li *ng-for="#item of stuff">{{item}}</li>
<li *ng-for="#item of stuff" [inner-html]="item"></li>
</ul>`,
directives: [NgFor, MessageDir]
})

View File

@ -41,7 +41,7 @@ export function main() {
rootTC.detectChanges();
rootTC.debugElement.componentInstance.items = [['1']];
detectChangesAndCheck(rootTC, 'ng-binding 1', 1);
detectChangesAndCheck(rootTC, '1', 1);
async.done();
});
@ -58,7 +58,7 @@ export function main() {
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((rootTC) => {
detectChangesAndCheck(rootTC, 'ng-binding foo');
detectChangesAndCheck(rootTC, 'foo');
async.done();
});
}));
@ -71,7 +71,7 @@ export function main() {
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((rootTC) => {
detectChangesAndCheck(rootTC, 'ng-binding foo-bar fooBar');
detectChangesAndCheck(rootTC, 'foo-bar fooBar');
async.done();
});
}));
@ -83,10 +83,10 @@ export function main() {
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((rootTC) => {
detectChangesAndCheck(rootTC, 'ng-binding foo');
detectChangesAndCheck(rootTC, 'foo');
rootTC.debugElement.componentInstance.condition = false;
detectChangesAndCheck(rootTC, 'ng-binding bar');
detectChangesAndCheck(rootTC, 'bar');
async.done();
});
@ -99,16 +99,16 @@ export function main() {
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((rootTC) => {
detectChangesAndCheck(rootTC, 'ng-binding foo');
detectChangesAndCheck(rootTC, 'foo');
StringMapWrapper.set(rootTC.debugElement.componentInstance.objExpr, 'bar', true);
detectChangesAndCheck(rootTC, 'ng-binding foo bar');
detectChangesAndCheck(rootTC, 'foo bar');
StringMapWrapper.set(rootTC.debugElement.componentInstance.objExpr, 'baz', true);
detectChangesAndCheck(rootTC, 'ng-binding foo bar baz');
detectChangesAndCheck(rootTC, 'foo bar baz');
StringMapWrapper.delete(rootTC.debugElement.componentInstance.objExpr, 'bar');
detectChangesAndCheck(rootTC, 'ng-binding foo baz');
detectChangesAndCheck(rootTC, 'foo baz');
async.done();
});
@ -121,13 +121,13 @@ export function main() {
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((rootTC) => {
detectChangesAndCheck(rootTC, 'ng-binding foo');
detectChangesAndCheck(rootTC, 'foo');
rootTC.debugElement.componentInstance.objExpr = {foo: true, bar: true};
detectChangesAndCheck(rootTC, 'ng-binding foo bar');
detectChangesAndCheck(rootTC, 'foo bar');
rootTC.debugElement.componentInstance.objExpr = {baz: true};
detectChangesAndCheck(rootTC, 'ng-binding baz');
detectChangesAndCheck(rootTC, 'baz');
async.done();
});
@ -140,13 +140,13 @@ export function main() {
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((rootTC) => {
detectChangesAndCheck(rootTC, 'ng-binding foo');
detectChangesAndCheck(rootTC, 'foo');
rootTC.debugElement.componentInstance.objExpr = null;
detectChangesAndCheck(rootTC, 'ng-binding');
detectChangesAndCheck(rootTC, '');
rootTC.debugElement.componentInstance.objExpr = {'foo': false, 'bar': true};
detectChangesAndCheck(rootTC, 'ng-binding bar');
detectChangesAndCheck(rootTC, 'bar');
async.done();
});
@ -162,7 +162,7 @@ export function main() {
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((rootTC) => {
detectChangesAndCheck(rootTC, 'ng-binding foo bar foo-bar fooBar');
detectChangesAndCheck(rootTC, 'foo bar foo-bar fooBar');
async.done();
});
}));
@ -175,16 +175,16 @@ export function main() {
.createAsync(TestComponent)
.then((rootTC) => {
var arrExpr: string[] = rootTC.debugElement.componentInstance.arrExpr;
detectChangesAndCheck(rootTC, 'ng-binding foo');
detectChangesAndCheck(rootTC, 'foo');
arrExpr.push('bar');
detectChangesAndCheck(rootTC, 'ng-binding foo bar');
detectChangesAndCheck(rootTC, 'foo bar');
arrExpr[1] = 'baz';
detectChangesAndCheck(rootTC, 'ng-binding foo baz');
detectChangesAndCheck(rootTC, 'foo baz');
ListWrapper.remove(rootTC.debugElement.componentInstance.arrExpr, 'baz');
detectChangesAndCheck(rootTC, 'ng-binding foo');
detectChangesAndCheck(rootTC, 'foo');
async.done();
});
@ -197,10 +197,10 @@ export function main() {
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((rootTC) => {
detectChangesAndCheck(rootTC, 'ng-binding foo');
detectChangesAndCheck(rootTC, 'foo');
rootTC.debugElement.componentInstance.arrExpr = ['bar'];
detectChangesAndCheck(rootTC, 'ng-binding bar');
detectChangesAndCheck(rootTC, 'bar');
async.done();
});
@ -213,10 +213,10 @@ export function main() {
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((rootTC) => {
detectChangesAndCheck(rootTC, 'foo ng-binding');
detectChangesAndCheck(rootTC, 'foo');
rootTC.debugElement.componentInstance.arrExpr = ['bar'];
detectChangesAndCheck(rootTC, 'ng-binding foo bar');
detectChangesAndCheck(rootTC, 'foo bar');
async.done();
});
@ -231,7 +231,7 @@ export function main() {
.then((rootTC) => {
rootTC.debugElement.componentInstance.arrExpr = ['', ' '];
detectChangesAndCheck(rootTC, 'foo ng-binding');
detectChangesAndCheck(rootTC, 'foo');
async.done();
});
@ -246,7 +246,7 @@ export function main() {
.then((rootTC) => {
rootTC.debugElement.componentInstance.arrExpr = [' bar '];
detectChangesAndCheck(rootTC, 'foo ng-binding bar');
detectChangesAndCheck(rootTC, 'foo bar');
async.done();
});
@ -262,7 +262,7 @@ export function main() {
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((rootTC) => {
detectChangesAndCheck(rootTC, 'ng-binding foo bar foo-bar fooBar');
detectChangesAndCheck(rootTC, 'foo bar foo-bar fooBar');
async.done();
});
}));
@ -274,14 +274,14 @@ export function main() {
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((rootTC) => {
detectChangesAndCheck(rootTC, 'ng-binding foo');
detectChangesAndCheck(rootTC, 'foo');
rootTC.debugElement.componentInstance.strExpr = 'foo bar';
detectChangesAndCheck(rootTC, 'ng-binding foo bar');
detectChangesAndCheck(rootTC, 'foo bar');
rootTC.debugElement.componentInstance.strExpr = 'baz';
detectChangesAndCheck(rootTC, 'ng-binding baz');
detectChangesAndCheck(rootTC, 'baz');
async.done();
});
@ -294,10 +294,10 @@ export function main() {
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((rootTC) => {
detectChangesAndCheck(rootTC, 'ng-binding foo');
detectChangesAndCheck(rootTC, 'foo');
rootTC.debugElement.componentInstance.strExpr = null;
detectChangesAndCheck(rootTC, 'ng-binding');
detectChangesAndCheck(rootTC, '');
async.done();
});
@ -310,10 +310,10 @@ export function main() {
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((rootTC) => {
detectChangesAndCheck(rootTC, 'foo ng-binding');
detectChangesAndCheck(rootTC, 'foo');
rootTC.debugElement.componentInstance.strExpr = null;
detectChangesAndCheck(rootTC, 'ng-binding foo');
detectChangesAndCheck(rootTC, 'foo');
async.done();
});
@ -327,7 +327,7 @@ export function main() {
.createAsync(TestComponent)
.then((rootTC) => {
rootTC.debugElement.componentInstance.strExpr = '';
detectChangesAndCheck(rootTC, 'foo ng-binding');
detectChangesAndCheck(rootTC, 'foo');
async.done();
});
@ -345,13 +345,13 @@ export function main() {
.createAsync(TestComponent)
.then((rootTC) => {
StringMapWrapper.set(rootTC.debugElement.componentInstance.objExpr, 'bar', true);
detectChangesAndCheck(rootTC, 'init foo ng-binding bar');
detectChangesAndCheck(rootTC, 'init foo bar');
StringMapWrapper.set(rootTC.debugElement.componentInstance.objExpr, 'foo', false);
detectChangesAndCheck(rootTC, 'init ng-binding bar');
detectChangesAndCheck(rootTC, 'init bar');
rootTC.debugElement.componentInstance.objExpr = null;
detectChangesAndCheck(rootTC, 'init ng-binding foo');
detectChangesAndCheck(rootTC, 'init foo');
async.done();
});
@ -365,13 +365,13 @@ export function main() {
.createAsync(TestComponent)
.then((rootTC) => {
StringMapWrapper.set(rootTC.debugElement.componentInstance.objExpr, 'bar', true);
detectChangesAndCheck(rootTC, `{{'init foo'}} ng-binding init foo bar`);
detectChangesAndCheck(rootTC, `init foo bar`);
StringMapWrapper.set(rootTC.debugElement.componentInstance.objExpr, 'foo', false);
detectChangesAndCheck(rootTC, `{{'init foo'}} ng-binding init bar`);
detectChangesAndCheck(rootTC, `init bar`);
rootTC.debugElement.componentInstance.objExpr = null;
detectChangesAndCheck(rootTC, `{{'init foo'}} ng-binding init foo`);
detectChangesAndCheck(rootTC, `init foo`);
async.done();
});
@ -385,13 +385,13 @@ export function main() {
.createAsync(TestComponent)
.then((rootTC) => {
StringMapWrapper.set(rootTC.debugElement.componentInstance.objExpr, 'bar', true);
detectChangesAndCheck(rootTC, `init ng-binding foo bar`);
detectChangesAndCheck(rootTC, `init foo bar`);
StringMapWrapper.set(rootTC.debugElement.componentInstance.objExpr, 'foo', false);
detectChangesAndCheck(rootTC, `init ng-binding bar`);
detectChangesAndCheck(rootTC, `init bar`);
rootTC.debugElement.componentInstance.objExpr = null;
detectChangesAndCheck(rootTC, `init ng-binding foo`);
detectChangesAndCheck(rootTC, `init foo`);
async.done();
});
@ -405,16 +405,16 @@ export function main() {
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((rootTC) => {
detectChangesAndCheck(rootTC, 'init foo ng-binding baz');
detectChangesAndCheck(rootTC, 'init foo baz');
StringMapWrapper.set(rootTC.debugElement.componentInstance.objExpr, 'bar', true);
detectChangesAndCheck(rootTC, 'init foo ng-binding baz bar');
detectChangesAndCheck(rootTC, 'init foo baz bar');
StringMapWrapper.set(rootTC.debugElement.componentInstance.objExpr, 'foo', false);
detectChangesAndCheck(rootTC, 'init ng-binding baz bar');
detectChangesAndCheck(rootTC, 'init baz bar');
rootTC.debugElement.componentInstance.condition = false;
detectChangesAndCheck(rootTC, 'init ng-binding bar');
detectChangesAndCheck(rootTC, 'init bar');
async.done();
});
@ -427,16 +427,16 @@ export function main() {
tcb.overrideTemplate(TestComponent, template)
.createAsync(TestComponent)
.then((rootTC) => {
detectChangesAndCheck(rootTC, 'init ng-binding foo');
detectChangesAndCheck(rootTC, 'init foo');
StringMapWrapper.set(rootTC.debugElement.componentInstance.objExpr, 'bar', true);
detectChangesAndCheck(rootTC, 'init ng-binding foo bar');
detectChangesAndCheck(rootTC, 'init foo bar');
rootTC.debugElement.componentInstance.strExpr = 'baz';
detectChangesAndCheck(rootTC, 'init ng-binding bar baz foo');
detectChangesAndCheck(rootTC, 'init bar baz foo');
rootTC.debugElement.componentInstance.objExpr = null;
detectChangesAndCheck(rootTC, 'init ng-binding baz');
detectChangesAndCheck(rootTC, 'init baz');
async.done();
});

View File

@ -12,7 +12,7 @@ import {
xit,
} from 'angular2/test_lib';
import {DOM} from 'angular2/src/core/dom/dom_adapter';
import {Component, Directive, NgNonBindable, View} from 'angular2/core';
import {Component, Directive, View} from 'angular2/core';
import {ElementRef} from 'angular2/src/core/compiler/element_ref';
export function main() {
@ -66,7 +66,7 @@ class TestDirective {
}
@Component({selector: 'test-cmp'})
@View({directives: [NgNonBindable, TestDirective]})
@View({directives: [TestDirective]})
class TestComponent {
text: string;
constructor() { this.text = 'foo'; }

View File

@ -31,6 +31,7 @@ import {
Validators,
} from 'angular2/core';
import {By} from 'angular2/src/core/debug';
import {ListWrapper} from 'angular2/src/core/facade/collection';
export function main() {
describe("integration tests", () => {
@ -660,21 +661,18 @@ export function main() {
rootTC.detectChanges();
var input = rootTC.debugElement.query(By.css("input")).nativeElement;
expect(DOM.classList(input))
.toEqual(['ng-binding', 'ng-invalid', 'ng-pristine', 'ng-untouched']);
expect(sortedClassList(input)).toEqual(['ng-invalid', 'ng-pristine', 'ng-untouched']);
dispatchEvent(input, "blur");
rootTC.detectChanges();
expect(DOM.classList(input))
.toEqual(["ng-binding", "ng-invalid", "ng-pristine", "ng-touched"]);
expect(sortedClassList(input)).toEqual(["ng-invalid", "ng-pristine", "ng-touched"]);
input.value = "updatedValue";
dispatchEvent(input, "change");
rootTC.detectChanges();
expect(DOM.classList(input))
.toEqual(["ng-binding", "ng-touched", "ng-dirty", "ng-valid"]);
expect(sortedClassList(input)).toEqual(["ng-dirty", "ng-touched", "ng-valid"]);
async.done();
});
}));
@ -690,21 +688,18 @@ export function main() {
rootTC.detectChanges();
var input = rootTC.debugElement.query(By.css("input")).nativeElement;
expect(DOM.classList(input))
.toEqual(["ng-binding", "ng-invalid", "ng-pristine", "ng-untouched"]);
expect(sortedClassList(input)).toEqual(["ng-invalid", "ng-pristine", "ng-untouched"]);
dispatchEvent(input, "blur");
rootTC.detectChanges();
expect(DOM.classList(input))
.toEqual(["ng-binding", "ng-invalid", "ng-pristine", "ng-touched"]);
expect(sortedClassList(input)).toEqual(["ng-invalid", "ng-pristine", "ng-touched"]);
input.value = "updatedValue";
dispatchEvent(input, "change");
rootTC.detectChanges();
expect(DOM.classList(input))
.toEqual(["ng-binding", "ng-touched", "ng-dirty", "ng-valid"]);
expect(sortedClassList(input)).toEqual(["ng-dirty", "ng-touched", "ng-valid"]);
async.done();
});
}));
@ -718,21 +713,18 @@ export function main() {
rootTC.detectChanges();
var input = rootTC.debugElement.query(By.css("input")).nativeElement;
expect(DOM.classList(input))
.toEqual(["ng-binding", "ng-invalid", "ng-pristine", "ng-untouched"]);
expect(sortedClassList(input)).toEqual(["ng-invalid", "ng-pristine", "ng-untouched"]);
dispatchEvent(input, "blur");
rootTC.detectChanges();
expect(DOM.classList(input))
.toEqual(["ng-binding", "ng-invalid", "ng-pristine", "ng-touched"]);
expect(sortedClassList(input)).toEqual(["ng-invalid", "ng-pristine", "ng-touched"]);
input.value = "updatedValue";
dispatchEvent(input, "change");
rootTC.detectChanges();
expect(DOM.classList(input))
.toEqual(["ng-binding", "ng-touched", "ng-dirty", "ng-valid"]);
expect(sortedClassList(input)).toEqual(["ng-dirty", "ng-touched", "ng-valid"]);
async.done();
});
}));
@ -845,3 +837,9 @@ class MyComp {
name: string;
data: any;
}
function sortedClassList(el) {
var l = DOM.classList(el);
ListWrapper.sort(l);
return l;
}

View File

@ -34,27 +34,29 @@ export function main() {
() => { injector = Injector.resolveAndCreate([bind('dep').toValue('dependency')]); });
it('should instantiate a pipe', () => {
var proto = new ProtoPipes([PipeBinding.createFromType(PipeA, new Pipe({name: 'a'}))]);
var proto =
ProtoPipes.fromBindings([PipeBinding.createFromType(PipeA, new Pipe({name: 'a'}))]);
var pipes = new Pipes(proto, injector);
expect(pipes.get("a").pipe).toBeAnInstanceOf(PipeA);
});
it('should throw when no pipe found', () => {
var proto = new ProtoPipes([]);
var proto = ProtoPipes.fromBindings([]);
var pipes = new Pipes(proto, injector);
expect(() => pipes.get("invalid")).toThrowErrorWith("Cannot find pipe 'invalid'");
});
it('should inject dependencies from the provided injector', () => {
var proto = new ProtoPipes([PipeBinding.createFromType(PipeB, new Pipe({name: 'b'}))]);
var proto =
ProtoPipes.fromBindings([PipeBinding.createFromType(PipeB, new Pipe({name: 'b'}))]);
var pipes = new Pipes(proto, injector);
expect((<any>pipes.get("b").pipe).dep).toEqual("dependency");
});
it('should cache pure pipes', () => {
var proto =
new ProtoPipes([PipeBinding.createFromType(PipeA, new Pipe({name: 'a', pure: true}))]);
var proto = ProtoPipes.fromBindings(
[PipeBinding.createFromType(PipeA, new Pipe({name: 'a', pure: true}))]);
var pipes = new Pipes(proto, injector);
expect(pipes.get("a").pure).toEqual(true);
@ -62,8 +64,8 @@ export function main() {
});
it('should NOT cache impure pipes', () => {
var proto =
new ProtoPipes([PipeBinding.createFromType(PipeA, new Pipe({name: 'a', pure: false}))]);
var proto = ProtoPipes.fromBindings(
[PipeBinding.createFromType(PipeA, new Pipe({name: 'a', pure: false}))]);
var pipes = new Pipes(proto, injector);
expect(pipes.get("a").pure).toEqual(false);

View File

@ -248,15 +248,11 @@ export function main() {
});
if (IS_DART) {
describe("moduleId", () => {
it("should return the moduleId for a type", () => {
expect(reflector.moduleId(TestObjWith00Args))
.toEqual('base/dist/dart/angular2/test/core/reflection/reflector_spec');
});
it("should return an empty array otherwise", () => {
var p = reflector.interfaces(ClassWithDecorators);
expect(p).toEqual([]);
describe("importUri", () => {
it("should return the importUri for a type", () => {
expect(reflector.importUri(TestObjWith00Args)
.endsWith('base/dist/dart/angular2/test/core/reflection/reflector_spec.dart'))
.toBe(true);
});
});
}

View File

@ -13,266 +13,22 @@ import {
SpyObject,
} from 'angular2/test_lib';
import {MapWrapper} from 'angular2/src/core/facade/collection';
import {DOM} from 'angular2/src/core/dom/dom_adapter';
// import {MapWrapper} from 'angular2/src/core/facade/collection';
// import {DOM} from 'angular2/src/core/dom/dom_adapter';
import {DomTestbed, TestRootView, elRef} from './dom_testbed';
// import {DomTestbed, TestRootView, elRef} from './dom_testbed';
import {
ViewDefinition,
RenderDirectiveMetadata,
RenderViewRef,
ViewEncapsulation
} from 'angular2/src/core/render/api';
// import {
// ViewDefinition,
// RenderDirectiveMetadata,
// RenderViewRef,
// ViewEncapsulation
// } from 'angular2/src/core/render/api';
export function main() {
describe('DomRenderer integration', () => {
beforeEachBindings(() => [DomTestbed]);
it('should create and destroy root host views while using the given elements in place',
inject([AsyncTestCompleter, DomTestbed], (async, tb: DomTestbed) => {
tb.compiler.compileHost(someComponent)
.then((hostProtoViewDto) => {
var view = new TestRootView(
tb.renderer.createRootHostView(hostProtoViewDto.render, 0, '#root'));
expect(tb.rootEl.parentNode).toBeTruthy();
expect(view.hostElement).toEqual(tb.rootEl);
tb.renderer.detachFragment(view.fragments[0]);
tb.renderer.destroyView(view.viewRef);
expect(tb.rootEl.parentNode).toBeFalsy();
async.done();
});
}));
it('should update text nodes',
inject([AsyncTestCompleter, DomTestbed], (async, tb: DomTestbed) => {
tb.compileAndMerge(
someComponent,
[
new ViewDefinition(
{componentId: 'someComponent', template: '{{a}}', directives: []})
])
.then((protoViewMergeMappings) => {
var rootView = tb.createView(protoViewMergeMappings);
tb.renderer.setText(rootView.viewRef, 0, 'hello');
expect(rootView.hostElement).toHaveText('hello');
async.done();
});
}));
it('should update any element property/attributes/class/style independent of the compilation on the root element and other elements',
inject([AsyncTestCompleter, DomTestbed], (async, tb: DomTestbed) => {
tb.compileAndMerge(someComponent,
[
new ViewDefinition({
componentId: 'someComponent',
template: '<input [title]="y" style="position:absolute">',
directives: []
})
])
.then((protoViewMergeMappings) => {
var checkSetters = (elr, el) => {
tb.renderer.setElementProperty(elr, 'tabIndex', 1);
expect((<HTMLInputElement>el).tabIndex).toEqual(1);
tb.renderer.setElementClass(elr, 'a', true);
expect(DOM.hasClass(el, 'a')).toBe(true);
tb.renderer.setElementClass(elr, 'a', false);
expect(DOM.hasClass(el, 'a')).toBe(false);
tb.renderer.setElementStyle(elr, 'width', '10px');
expect(DOM.getStyle(el, 'width')).toEqual('10px');
tb.renderer.setElementStyle(elr, 'width', null);
expect(DOM.getStyle(el, 'width')).toEqual('');
tb.renderer.setElementAttribute(elr, 'someAttr', 'someValue');
expect(DOM.getAttribute(el, 'some-attr')).toEqual('someValue');
};
var rootView = tb.createView(protoViewMergeMappings);
// root element
checkSetters(elRef(rootView.viewRef, 0), rootView.hostElement);
// nested elements
checkSetters(elRef(rootView.viewRef, 1), DOM.firstChild(rootView.hostElement));
async.done();
});
}));
if (DOM.supportsDOMEvents()) {
it('should call actions on the element independent of the compilation',
inject([AsyncTestCompleter, DomTestbed], (async, tb: DomTestbed) => {
tb.compileAndMerge(someComponent,
[
new ViewDefinition({
componentId: 'someComponent',
template: '<input [title]="y"></input>',
directives: []
})
])
.then((protoViewMergeMappings) => {
var rootView = tb.createView(protoViewMergeMappings);
tb.renderer.invokeElementMethod(elRef(rootView.viewRef, 1), 'setAttribute',
['a', 'b']);
expect(DOM.getAttribute(DOM.childNodes(rootView.hostElement)[0], 'a'))
.toEqual('b');
async.done();
});
}));
}
it('should add and remove fragments',
inject([AsyncTestCompleter, DomTestbed], (async, tb: DomTestbed) => {
tb.compileAndMerge(someComponent,
[
new ViewDefinition({
componentId: 'someComponent',
template: '<template>hello</template>',
directives: []
})
])
.then((protoViewMergeMappings) => {
var rootView = tb.createView(protoViewMergeMappings);
var elr = elRef(rootView.viewRef, 1);
expect(rootView.hostElement).toHaveText('');
var fragment = rootView.fragments[1];
tb.renderer.attachFragmentAfterElement(elr, fragment);
expect(rootView.hostElement).toHaveText('hello');
tb.renderer.detachFragment(fragment);
expect(rootView.hostElement).toHaveText('');
async.done();
});
}));
it('should add and remove empty fragments',
inject([AsyncTestCompleter, DomTestbed], (async, tb: DomTestbed) => {
tb.compileAndMerge(someComponent,
[
new ViewDefinition({
componentId: 'someComponent',
template: '<template></template><template></template>',
directives: []
})
])
.then((protoViewMergeMappings) => {
var rootView = tb.createView(protoViewMergeMappings);
var elr = elRef(rootView.viewRef, 1);
expect(rootView.hostElement).toHaveText('');
var fragment = rootView.fragments[1];
var fragment2 = rootView.fragments[2];
tb.renderer.attachFragmentAfterElement(elr, fragment);
tb.renderer.attachFragmentAfterFragment(fragment, fragment2);
tb.renderer.detachFragment(fragment);
tb.renderer.detachFragment(fragment2);
expect(rootView.hostElement).toHaveText('');
async.done();
});
}));
it('should handle events', inject([AsyncTestCompleter, DomTestbed], (async, tb: DomTestbed) => {
tb.compileAndMerge(someComponent,
[
new ViewDefinition({
componentId: 'someComponent',
template: '<input (change)="doSomething()">',
directives: []
})
])
.then((protoViewMergeMappings) => {
var rootView = tb.createView(protoViewMergeMappings);
tb.triggerEvent(elRef(rootView.viewRef, 1), 'change');
var eventEntry = rootView.events[0];
// bound element index
expect(eventEntry[0]).toEqual(1);
// event type
expect(eventEntry[1]).toEqual('change');
// actual event
expect((<Map<any, any>>eventEntry[2]).get('$event').type).toEqual('change');
async.done();
});
}));
if (DOM.supportsNativeShadowDOM()) {
describe('native shadow dom support', () => {
it('should put the template into a shadow root',
inject([AsyncTestCompleter, DomTestbed], (async, tb: DomTestbed) => {
tb.compileAndMerge(someComponent,
[
new ViewDefinition({
componentId: 'someComponent',
template: 'hello',
directives: [],
encapsulation: ViewEncapsulation.Native
})
])
.then((protoViewMergeMappings) => {
var rootView = tb.createView(protoViewMergeMappings);
expect(DOM.getShadowRoot(rootView.hostElement)).toHaveText('hello');
async.done();
});
}));
it('should add styles from non native components to shadow roots while the view is not destroyed',
inject([AsyncTestCompleter, DomTestbed], (async, tb: DomTestbed) => {
tb.compileAndMerge(someComponent,
[
new ViewDefinition({
componentId: 'someComponent',
template: '',
directives: [],
encapsulation: ViewEncapsulation.Native,
styles: ['a {};']
})
])
.then((protoViewMergeMappings) => {
var rootView = tb.createView(protoViewMergeMappings);
tb.compiler.compile(new ViewDefinition({
componentId: 'someComponent',
template: '',
directives: [],
encapsulation: ViewEncapsulation.None,
styles: ['b {};']
}))
.then(_ => {
expect(DOM.getShadowRoot(rootView.hostElement)).toHaveText('a {};b {};');
tb.renderer.destroyView(rootView.viewRef);
tb.compiler.compile(new ViewDefinition({
componentId: 'someComponent',
template: '',
directives: [],
encapsulation: ViewEncapsulation.None,
styles: ['c {};']
}))
.then(_ => {
expect(DOM.getShadowRoot(rootView.hostElement))
.toHaveText('a {};b {};');
async.done();
});
});
});
}));
});
}
it('should work', () => {
// TODO
});
});
}
export var someComponent = RenderDirectiveMetadata.create(
{id: 'someComponent', type: RenderDirectiveMetadata.COMPONENT_TYPE, selector: 'some-comp'});

View File

@ -8,6 +8,7 @@ import 'package:angular2/src/core/compiler/directive_resolver.dart';
import 'package:angular2/src/core/compiler/view.dart';
import 'package:angular2/src/core/compiler/element_ref.dart';
import 'package:angular2/src/core/compiler/view_manager.dart';
import 'package:angular2/src/core/compiler/proto_view_factory.dart';
import 'package:angular2/src/core/compiler/view_pool.dart';
import 'package:angular2/src/core/compiler/view_listener.dart';
import 'package:angular2/src/core/compiler/element_injector.dart';
@ -91,6 +92,11 @@ class SpyAppViewListener extends SpyObject implements AppViewListener {
noSuchMethod(m) => super.noSuchMethod(m);
}
@proxy
class SpyProtoViewFactory extends SpyObject implements ProtoViewFactory {
noSuchMethod(m) => super.noSuchMethod(m);
}
@proxy
class SpyProtoElementInjector extends SpyObject implements ProtoElementInjector {
noSuchMethod(m) => super.noSuchMethod(m);

View File

@ -14,6 +14,7 @@ import {ElementRef} from 'angular2/src/core/compiler/element_ref';
import {AppViewManager} from 'angular2/src/core/compiler/view_manager';
import {AppViewPool} from 'angular2/src/core/compiler/view_pool';
import {AppViewListener} from 'angular2/src/core/compiler/view_listener';
import {ProtoViewFactory} from 'angular2/src/core/compiler/proto_view_factory';
import {DomAdapter} from 'angular2/src/core/dom/dom_adapter';
import {ClientMessageBroker} from 'angular2/src/web_workers/shared/client_message_broker';
import {XHR} from 'angular2/src/core/render/xhr';
@ -76,6 +77,10 @@ export class SpyAppViewListener extends SpyObject {
constructor() { super(AppViewListener); }
}
export class SpyProtoViewFactory extends SpyObject {
constructor() { super(ProtoViewFactory); }
}
export class SpyProtoElementInjector extends SpyObject {
constructor() { super(ProtoElementInjector); }
}

View File

@ -152,10 +152,10 @@ var NG_API = [
'Class:js',
'Compiler',
'Compiler.compileInHost()',
'Compiler.clearCache()',
'Component',
'Component.bindings',
'Component.changeDetection',
'Component.compileChildren',
'Component.outputs',
'Component.exportAs',
'Component.host',
@ -167,7 +167,6 @@ var NG_API = [
'ComponentMetadata',
'ComponentMetadata.bindings',
'ComponentMetadata.changeDetection',
'ComponentMetadata.compileChildren',
'ComponentMetadata.outputs',
'ComponentMetadata.exportAs',
'ComponentMetadata.host',
@ -372,7 +371,6 @@ var NG_API = [
'DependencyMetadata.token',
'Directive',
'Directive.bindings',
'Directive.compileChildren',
'Directive.outputs',
'Directive.exportAs',
'Directive.host',
@ -382,7 +380,6 @@ var NG_API = [
'Directive.selector',
'DirectiveMetadata',
'DirectiveMetadata.bindings',
'DirectiveMetadata.compileChildren',
'DirectiveMetadata.outputs',
'DirectiveMetadata.exportAs',
'DirectiveMetadata.host',
@ -720,7 +717,6 @@ var NG_API = [
'NgModel.viewModel',
'NgModel.viewModel=',
'NgModel.viewToModelUpdate()',
'NgNonBindable',
'NgSelectOption',
'NgStyle',
'NgStyle.doCheck()',
@ -899,6 +895,8 @@ var NG_API = [
'Renderer',
'Renderer.attachFragmentAfterElement()',
'Renderer.attachFragmentAfterFragment()',
'Renderer.createProtoView()',
'Renderer.registerComponentTemplate()',
'Renderer.createRootHostView()',
'Renderer.createView()',
'Renderer.dehydrateView()',
@ -967,7 +965,6 @@ var NG_API = [
'UpperCasePipe',
'UpperCasePipe.transform()',
'UrlResolver',
'UrlResolver.packagePrefix',
'UrlResolver.resolve()',
'Validators#array()',
'Validators#compose()',
@ -1129,6 +1126,36 @@ var NG_API = [
'{OnInit}',
'{PipeOnDestroy}',
'{PipeTransform}',
'{RenderBeginCmd}',
'{RenderBeginCmd}.isBound',
'{RenderBeginCmd}.isBound=',
'{RenderBeginCmd}.ngContentIndex',
'{RenderBeginCmd}.ngContentIndex=',
'{RenderBeginComponentCmd}',
'{RenderBeginComponentCmd}.nativeShadow',
'{RenderBeginComponentCmd}.nativeShadow=',
'{RenderBeginComponentCmd}.templateId',
'{RenderBeginComponentCmd}.templateId=',
'{RenderBeginElementCmd}',
'{RenderBeginElementCmd}.attrNameAndValues',
'{RenderBeginElementCmd}.attrNameAndValues=',
'{RenderBeginElementCmd}.eventTargetAndNames',
'{RenderBeginElementCmd}.eventTargetAndNames=',
'{RenderBeginElementCmd}.name',
'{RenderBeginElementCmd}.name=',
'{RenderCommandVisitor}',
'{RenderEmbeddedTemplateCmd}',
'{RenderEmbeddedTemplateCmd}.children',
'{RenderEmbeddedTemplateCmd}.children=',
'{RenderEmbeddedTemplateCmd}.isMerged',
'{RenderEmbeddedTemplateCmd}.isMerged=',
'{RenderNgContentCmd}',
'{RenderNgContentCmd}.ngContentIndex',
'{RenderNgContentCmd}.ngContentIndex=',
'{RenderTemplateCmd}',
'{RenderTextCmd}',
'{RenderTextCmd}.value',
'{RenderTextCmd}.value=',
'{RenderElementRef}',
'{RenderElementRef}.renderBoundElementIndex',
'{RenderElementRef}.renderBoundElementIndex=',

View File

@ -1,24 +1,56 @@
import {AsyncTestCompleter, inject, describe, it, expect} from "angular2/test_lib";
import {
AsyncTestCompleter,
inject,
describe,
ddescribe,
beforeEach,
it,
expect
} from "angular2/test_lib";
import {RenderProtoViewRef} from "angular2/src/core/render/api";
import {RenderProtoViewRefStore} from "angular2/src/web_workers/shared/render_proto_view_ref_store";
import {
WebWorkerRenderProtoViewRef
} from "angular2/src/web_workers/shared/render_proto_view_ref_store";
export function main() {
describe("RenderProtoViewRefStore", () => {
it("should store and return the correct reference", () => {
var store = new RenderProtoViewRefStore(true);
var ref1 = new RenderProtoViewRef();
var index1 = store.storeRenderProtoViewRef(ref1);
expect(store.retreiveRenderProtoViewRef(index1)).toBe(ref1);
var ref2 = new RenderProtoViewRef();
var index2 = store.storeRenderProtoViewRef(ref2);
expect(store.retreiveRenderProtoViewRef(index2)).toBe(ref2);
describe("on WebWorker", () => {
var store: RenderProtoViewRefStore;
beforeEach(() => { store = new RenderProtoViewRefStore(true); });
it("should allocate refs", () => {
expect((<WebWorkerRenderProtoViewRef>store.allocate()).refNumber).toBe(0);
expect((<WebWorkerRenderProtoViewRef>store.allocate()).refNumber).toBe(1);
});
it("should be serializable", () => {
var protoView = store.allocate();
expect(store.deserialize(store.serialize(protoView))).toEqual(protoView);
});
});
it("should cache index numbers", () => {
var store = new RenderProtoViewRefStore(true);
var ref = new RenderProtoViewRef();
var index = store.storeRenderProtoViewRef(ref);
expect(store.storeRenderProtoViewRef(ref)).toEqual(index);
describe("on UI", () => {
var store: RenderProtoViewRefStore;
beforeEach(() => { store = new RenderProtoViewRefStore(false); });
it("should associate views with the correct references", () => {
var renderProtoViewRef = new RenderProtoViewRef();
store.store(renderProtoViewRef, 100);
expect(store.deserialize(100)).toBe(renderProtoViewRef);
});
it("should be serializable", () => {
var renderProtoViewRef = new RenderProtoViewRef();
store.store(renderProtoViewRef, 0);
var deserialized = store.deserialize(store.serialize(renderProtoViewRef));
expect(deserialized).toBe(renderProtoViewRef);
});
});
});
}

View File

@ -1,35 +1,44 @@
import {
AsyncTestCompleter,
inject,
ddescribe,
describe,
it,
iit,
expect,
beforeEach,
createTestInjector,
beforeEachBindings
beforeEachBindings,
TestComponentBuilder
} from "angular2/test_lib";
import {DOM} from 'angular2/src/core/dom/dom_adapter';
import {DomTestbed, TestRootView, elRef} from '../../core/render/dom/dom_testbed';
import {bind} from 'angular2/core';
import {WebWorkerCompiler, WebWorkerRenderer} from "angular2/src/web_workers/worker/renderer";
import {
bind,
Binding,
Injector,
ViewMetadata,
Component,
View,
Injectable,
ElementRef,
NgIf
} from 'angular2/core';
import {WebWorkerRenderer} from "angular2/src/web_workers/worker/renderer";
import {
ClientMessageBrokerFactory,
UiArguments,
FnArg
} from "angular2/src/web_workers/shared/client_message_broker";
import {Serializer} from "angular2/src/web_workers/shared/serializer";
import {isPresent, isBlank, Type} from "angular2/src/core/facade/lang";
import {MapWrapper, ListWrapper} from "angular2/src/core/facade/collection";
import {
RenderDirectiveMetadata,
ProtoViewDto,
RenderProtoViewRef,
RenderViewWithFragments,
ViewDefinition,
RenderProtoViewMergeMapping,
RenderViewRef,
RenderFragmentRef
RenderFragmentRef,
Renderer
} from "angular2/src/core/render/api";
import {DomRenderer} from 'angular2/src/core/render/dom/dom_renderer';
import {DefaultRenderView} from 'angular2/src/core/render/view';
import {
RenderProtoViewRefStore,
WebWorkerRenderProtoViewRef
@ -38,23 +47,18 @@ import {
RenderViewWithFragmentsStore,
WebWorkerRenderViewRef
} from 'angular2/src/web_workers/shared/render_view_with_fragments_store';
import {
resolveInternalDomProtoView,
DomProtoView
} from 'angular2/src/core/render/dom/view/proto_view';
import {someComponent} from '../../core/render/dom/dom_renderer_integration_spec';
import {WebWorkerApplication} from 'angular2/src/web_workers/ui/impl';
import {MessageBasedRenderCompiler} from 'angular2/src/web_workers/ui/render_compiler';
import {MessageBasedRenderer} from 'angular2/src/web_workers/ui/renderer';
import {createPairedMessageBuses} from '../shared/web_worker_test_util';
import {createPairedMessageBuses, PairedMessageBuses} from '../shared/web_worker_test_util';
import {ServiceMessageBrokerFactory} from 'angular2/src/web_workers/shared/service_message_broker';
import {WebWorkerEventDispatcher} from 'angular2/src/web_workers/worker/event_dispatcher';
export function main() {
function createWebWorkerBrokerFactory(
workerSerializer: Serializer, uiSerializer: Serializer, tb: DomTestbed,
uiRenderViewStore: RenderViewWithFragmentsStore,
workerRenderViewStore: RenderViewWithFragmentsStore): ClientMessageBrokerFactory {
var messageBuses = createPairedMessageBuses();
messageBuses: PairedMessageBuses, workerSerializer: Serializer, uiSerializer: Serializer,
domRenderer: DomRenderer, uiRenderProtoViewStore: RenderProtoViewRefStore,
uiRenderViewStore: RenderViewWithFragmentsStore): ClientMessageBrokerFactory {
var uiMessageBus = messageBuses.ui;
var workerMessageBus = messageBuses.worker;
@ -63,152 +67,91 @@ export function main() {
// set up the ui side
var uiMessageBrokerFactory = new ServiceMessageBrokerFactory(uiMessageBus, uiSerializer);
var renderCompiler = new MessageBasedRenderCompiler(uiMessageBrokerFactory, tb.compiler);
renderCompiler.start();
var renderer = new MessageBasedRenderer(uiMessageBrokerFactory, uiMessageBus, uiSerializer,
uiRenderViewStore, tb.renderer);
uiRenderProtoViewStore, uiRenderViewStore, domRenderer);
renderer.start();
new WebWorkerApplication(null, null);
return webWorkerBrokerFactory;
}
function createWorkerRenderer(workerSerializer: Serializer, uiSerializer: Serializer,
tb: DomTestbed, uiRenderViewStore: RenderViewWithFragmentsStore,
workerRenderViewStore: RenderViewWithFragmentsStore):
WebWorkerRenderer {
var brokerFactory = createWebWorkerBrokerFactory(workerSerializer, uiSerializer, tb,
uiRenderViewStore, workerRenderViewStore);
return new WebWorkerRenderer(brokerFactory, workerRenderViewStore, null);
}
function createWorkerCompiler(workerSerializer: Serializer, uiSerializer: Serializer,
tb: DomTestbed): WebWorkerCompiler {
function createWorkerRenderer(
workerSerializer: Serializer, uiSerializer: Serializer, domRenderer: DomRenderer,
uiRenderProtoViewStore: RenderProtoViewRefStore,
uiRenderViewStore: RenderViewWithFragmentsStore,
workerRenderProtoViewStore: RenderProtoViewRefStore,
workerRenderViewStore: RenderViewWithFragmentsStore): WebWorkerRenderer {
var messageBuses = createPairedMessageBuses();
var brokerFactory =
createWebWorkerBrokerFactory(workerSerializer, uiSerializer, tb, null, null);
return new WebWorkerCompiler(brokerFactory);
createWebWorkerBrokerFactory(messageBuses, workerSerializer, uiSerializer, domRenderer,
uiRenderProtoViewStore, uiRenderViewStore);
var workerEventDispatcher = new WebWorkerEventDispatcher(messageBuses.worker, workerSerializer);
return new WebWorkerRenderer(brokerFactory, workerRenderProtoViewStore, workerRenderViewStore,
workerEventDispatcher);
}
describe("Web Worker Compiler", function() {
var workerSerializer: Serializer;
var uiSerializer: Serializer;
var workerRenderProtoViewRefStore: RenderProtoViewRefStore;
var uiRenderProtoViewRefStore: RenderProtoViewRefStore;
var tb: DomTestbed;
beforeEach(() => {
workerRenderProtoViewRefStore = new RenderProtoViewRefStore(true);
uiRenderProtoViewRefStore = new RenderProtoViewRefStore(false);
workerSerializer = createSerializer(workerRenderProtoViewRefStore, null);
uiSerializer = createSerializer(uiRenderProtoViewRefStore, null);
tb = createTestInjector([DomTestbed]).get(DomTestbed);
});
function resolveWebWorkerRef(ref: RenderProtoViewRef) {
var refNumber = (<WebWorkerRenderProtoViewRef>ref).refNumber;
return resolveInternalDomProtoView(uiRenderProtoViewRefStore.deserialize(refNumber));
}
it('should build the proto view', inject([AsyncTestCompleter], (async) => {
var compiler: WebWorkerCompiler = createWorkerCompiler(workerSerializer, uiSerializer, tb);
var dirMetadata = RenderDirectiveMetadata.create(
{id: 'id', selector: 'custom', type: RenderDirectiveMetadata.COMPONENT_TYPE});
compiler.compileHost(dirMetadata)
.then((protoView) => {
expect(DOM.tagName(DOM.firstChild(DOM.content(
templateRoot(resolveWebWorkerRef(protoView.render)))))
.toLowerCase())
.toEqual('custom');
expect(protoView).not.toBeNull();
async.done();
});
}));
});
describe("Web Worker Renderer", () => {
var renderer: WebWorkerRenderer;
var workerSerializer: Serializer;
var workerRenderViewStore: RenderViewWithFragmentsStore;
var uiInjector: Injector;
var uiRenderViewStore: RenderViewWithFragmentsStore;
var uiSerializer: Serializer;
var tb: DomTestbed;
/**
* Seriliazes the given obj with the uiSerializer and then returns the version that
* the worker would deserialize
*/
function serialize(obj: any, type: Type): any {
var serialized = uiSerializer.serialize(obj, type);
return workerSerializer.deserialize(serialized, type);
}
beforeEach(() => {
workerRenderViewStore = new RenderViewWithFragmentsStore(true);
tb = createTestInjector([DomTestbed]).get(DomTestbed);
beforeEachBindings(() => {
var uiRenderProtoViewStore = new RenderProtoViewRefStore(false);
uiRenderViewStore = new RenderViewWithFragmentsStore(false);
workerSerializer = createSerializer(new RenderProtoViewRefStore(true), workerRenderViewStore);
uiSerializer = createSerializer(new RenderProtoViewRefStore(false), uiRenderViewStore);
renderer = createWorkerRenderer(workerSerializer, uiSerializer, tb, uiRenderViewStore,
workerRenderViewStore);
uiInjector = createTestInjector([
bind(RenderProtoViewRefStore)
.toValue(uiRenderProtoViewStore),
bind(RenderViewWithFragmentsStore).toValue(uiRenderViewStore),
bind(Renderer).toClass(DomRenderer)
]);
var uiSerializer = uiInjector.get(Serializer);
var domRenderer = uiInjector.get(DomRenderer);
var workerRenderProtoViewStore = new RenderProtoViewRefStore(true);
var workerRenderViewStore = new RenderViewWithFragmentsStore(true);
return [
bind(RenderProtoViewRefStore)
.toValue(workerRenderProtoViewStore),
bind(RenderViewWithFragmentsStore).toValue(workerRenderViewStore),
bind(Renderer).toFactory(
(workerSerializer) => {
return createWorkerRenderer(workerSerializer, uiSerializer, domRenderer,
uiRenderProtoViewStore, uiRenderViewStore,
workerRenderProtoViewStore, workerRenderViewStore);
},
[Serializer])
];
});
it('should create and destroy root host views while using the given elements in place',
inject([AsyncTestCompleter], (async) => {
tb.compiler.compileHost(someComponent)
.then((hostProtoViewDto: any) => {
hostProtoViewDto = serialize(hostProtoViewDto, ProtoViewDto);
var viewWithFragments =
renderer.createRootHostView(hostProtoViewDto.render, 1, '#root');
var view = new WorkerTestRootView(viewWithFragments, uiRenderViewStore);
function getRenderElement(elementRef: ElementRef) {
var renderView = <DefaultRenderView<Node>>uiRenderViewStore.deserializeRenderViewRef(
(<WebWorkerRenderViewRef>elementRef.renderView).refNumber);
return renderView.boundElements[elementRef.boundElementIndex];
}
expect(tb.rootEl.parentNode).toBeTruthy();
expect(view.hostElement).toBe(tb.rootEl);
renderer.detachFragment(viewWithFragments.fragmentRefs[0]);
renderer.destroyView(viewWithFragments.viewRef);
expect(tb.rootEl.parentNode).toBeFalsy();
it('should update text nodes',
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
tcb.overrideView(MyComp, new ViewMetadata({template: '<div>{{ctxProp}}</div>'}))
.createAsync(MyComp)
.then((rootTC) => {
var renderEl = getRenderElement(rootTC.debugElement.elementRef);
expect(renderEl).toHaveText('');
rootTC.debugElement.componentInstance.ctxProp = 'Hello World!';
rootTC.detectChanges();
expect(renderEl).toHaveText('Hello World!');
async.done();
});
}));
it('should update text nodes', inject([AsyncTestCompleter], (async) => {
tb.compileAndMerge(
someComponent,
[
new ViewDefinition(
{componentId: 'someComponent', template: '{{a}}', directives: []})
])
.then((protoViewMergeMappings) => {
protoViewMergeMappings =
serialize(protoViewMergeMappings, RenderProtoViewMergeMapping);
var rootView = renderer.createView(protoViewMergeMappings.mergedProtoViewRef, 1);
renderer.hydrateView(rootView.viewRef);
renderer.setText(rootView.viewRef, 0, 'hello');
var view = new WorkerTestRootView(rootView, uiRenderViewStore);
expect(view.hostElement).toHaveText('hello');
async.done();
});
}));
it('should update any element property/attributes/class/style independent of the compilation on the root element and other elements',
inject([AsyncTestCompleter], (async) => {
tb.compileAndMerge(someComponent,
[
new ViewDefinition({
componentId: 'someComponent',
template: '<input [title]="y" style="position:absolute">',
directives: []
})
])
.then((protoViewMergeMappings) => {
protoViewMergeMappings =
serialize(protoViewMergeMappings, RenderProtoViewMergeMapping);
var checkSetters = (elr, el) => {
inject([TestComponentBuilder, Renderer, AsyncTestCompleter], (tcb: TestComponentBuilder,
renderer: Renderer, async) => {
tcb.overrideView(MyComp, new ViewMetadata(
{template: '<input [title]="y" style="position:absolute">'}))
.createAsync(MyComp)
.then((rootTC) => {
var checkSetters = (elr) => {
var el = getRenderElement(elr);
renderer.setElementProperty(elr, 'tabIndex', 1);
expect((<HTMLInputElement>el).tabIndex).toEqual(1);
@ -226,74 +169,34 @@ export function main() {
expect(DOM.getAttribute(el, 'some-attr')).toEqual('someValue');
};
var rootViewWithFragments =
renderer.createView(protoViewMergeMappings.mergedProtoViewRef, 1);
renderer.hydrateView(rootViewWithFragments.viewRef);
var rootView = new WorkerTestRootView(rootViewWithFragments, uiRenderViewStore);
// root element
checkSetters(elRef(rootViewWithFragments.viewRef, 0), rootView.hostElement);
checkSetters(rootTC.debugElement.elementRef);
// nested elements
checkSetters(elRef(rootViewWithFragments.viewRef, 1),
DOM.firstChild(rootView.hostElement));
checkSetters(rootTC.debugElement.componentViewChildren[0].elementRef);
async.done();
});
}));
it('should add and remove fragments', inject([AsyncTestCompleter], (async) => {
tb.compileAndMerge(someComponent,
[
new ViewDefinition({
componentId: 'someComponent',
template: '<template>hello</template>',
directives: []
})
])
.then((protoViewMergeMappings) => {
protoViewMergeMappings =
serialize(protoViewMergeMappings, RenderProtoViewMergeMapping);
var rootViewWithFragments =
renderer.createView(protoViewMergeMappings.mergedProtoViewRef, 2);
it('should add and remove fragments',
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
tcb.overrideView(MyComp, new ViewMetadata({
template: '<template [ng-if]="ctxBoolProp">hello</template>',
directives: [NgIf]
}))
.createAsync(MyComp)
.then((rootTC) => {
var elr = elRef(rootViewWithFragments.viewRef, 1);
var rootView = new WorkerTestRootView(rootViewWithFragments, uiRenderViewStore);
expect(rootView.hostElement).toHaveText('');
var fragment = rootViewWithFragments.fragmentRefs[1];
renderer.attachFragmentAfterElement(elr, fragment);
expect(rootView.hostElement).toHaveText('hello');
renderer.detachFragment(fragment);
expect(rootView.hostElement).toHaveText('');
var rootEl = getRenderElement(rootTC.debugElement.elementRef);
expect(rootEl).toHaveText('');
async.done();
});
}));
rootTC.debugElement.componentInstance.ctxBoolProp = true;
rootTC.detectChanges();
expect(rootEl).toHaveText('hello');
it('should add and remove empty fragments', inject([AsyncTestCompleter], (async) => {
tb.compileAndMerge(someComponent,
[
new ViewDefinition({
componentId: 'someComponent',
template: '<template></template><template></template>',
directives: []
})
])
.then((protoViewMergeMappings) => {
protoViewMergeMappings =
serialize(protoViewMergeMappings, RenderProtoViewMergeMapping);
var rootViewWithFragments =
renderer.createView(protoViewMergeMappings.mergedProtoViewRef, 3);
var elr = elRef(rootViewWithFragments.viewRef, 1);
var rootView = new WorkerTestRootView(rootViewWithFragments, uiRenderViewStore);
expect(rootView.hostElement).toHaveText('');
var fragment = rootViewWithFragments.fragmentRefs[1];
var fragment2 = rootViewWithFragments.fragmentRefs[2];
renderer.attachFragmentAfterElement(elr, fragment);
renderer.attachFragmentAfterFragment(fragment, fragment2);
renderer.detachFragment(fragment);
renderer.detachFragment(fragment2);
expect(rootView.hostElement).toHaveText('');
rootTC.debugElement.componentInstance.ctxBoolProp = false;
rootTC.detectChanges();
expect(rootEl).toHaveText('');
async.done();
});
@ -301,56 +204,36 @@ export function main() {
if (DOM.supportsDOMEvents()) {
it('should call actions on the element independent of the compilation',
inject([AsyncTestCompleter], (async) => {
tb.compileAndMerge(someComponent,
[
new ViewDefinition({
componentId: 'someComponent',
template: '<input [title]="y"></input>',
directives: []
})
])
.then((protoViewMergeMappings) => {
protoViewMergeMappings =
serialize(protoViewMergeMappings, RenderProtoViewMergeMapping);
var rootViewWithFragments =
renderer.createView(protoViewMergeMappings.mergedProtoViewRef, 1);
var rootView = new WorkerTestRootView(rootViewWithFragments, uiRenderViewStore);
inject([TestComponentBuilder, Renderer, AsyncTestCompleter],
(tcb: TestComponentBuilder, renderer: Renderer, async) => {
tcb.overrideView(MyComp,
new ViewMetadata({template: '<input [title]="y"></input>'}))
.createAsync(MyComp)
.then((rootTC) => {
var elRef = rootTC.debugElement.componentViewChildren[0].elementRef;
renderer.invokeElementMethod(elRef, 'setAttribute', ['a', 'b']);
renderer.invokeElementMethod(elRef(rootViewWithFragments.viewRef, 1),
'setAttribute', ['a', 'b']);
expect(DOM.getAttribute(DOM.childNodes(rootView.hostElement)[0], 'a'))
.toEqual('b');
async.done();
});
}));
expect(DOM.getAttribute(getRenderElement(elRef), 'a')).toEqual('b');
async.done();
});
}));
}
});
}
class WorkerTestRootView extends TestRootView {
constructor(workerViewWithFragments: RenderViewWithFragments,
uiRenderViewStore: RenderViewWithFragmentsStore) {
super(new RenderViewWithFragments(
uiRenderViewStore.deserializeRenderViewRef(
(<WebWorkerRenderViewRef>workerViewWithFragments.viewRef).refNumber),
ListWrapper.map(workerViewWithFragments.fragmentRefs, (val) => {
return uiRenderViewStore.deserializeRenderFragmentRef(val.refNumber);
})));
@Component({selector: 'my-comp'})
@View({directives: []})
@Injectable()
class MyComp {
ctxProp: string;
ctxNumProp;
ctxBoolProp;
constructor() {
this.ctxProp = 'initial value';
this.ctxNumProp = 0;
this.ctxBoolProp = false;
}
}
function templateRoot(pv: DomProtoView) {
return <Element>pv.cloneableTemplate;
}
function createSerializer(protoViewRefStore: RenderProtoViewRefStore,
renderViewStore: RenderViewWithFragmentsStore): Serializer {
var injector = createTestInjector([
bind(RenderProtoViewRefStore)
.toValue(protoViewRefStore),
bind(RenderViewWithFragmentsStore).toValue(renderViewStore)
]);
return injector.get(Serializer);
throwError() { throw 'boom'; }
}