refactor(core): rename angular2/src/compiler -> angular2/src/core/compiler

This commit is contained in:
Tobias Bosch
2015-10-02 07:39:29 -07:00
parent 6248a5e846
commit aa3d94149f
41 changed files with 0 additions and 0 deletions

View File

@ -0,0 +1,196 @@
import {
AsyncTestCompleter,
beforeEach,
ddescribe,
describe,
el,
expect,
iit,
inject,
it,
xit,
TestComponentBuilder,
beforeEachBindings
} from 'angular2/test_lib';
import {MapWrapper} from 'angular2/src/core/facade/collection';
import {
CompileDirectiveMetadata,
CompileTypeMetadata
} from 'angular2/src/compiler/directive_metadata';
import {TemplateParser} from 'angular2/src/compiler/template_parser';
import {
Parser,
Lexer,
ChangeDetectorDefinition,
ChangeDetectorGenConfig,
DynamicProtoChangeDetector,
ChangeDetectionStrategy,
ChangeDispatcher,
DirectiveIndex,
Locals,
BindingTarget,
ChangeDetector
} 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 {TestDirective, TestDispatcher, TestPipes} from './change_detector_mocks';
import {TEST_BINDINGS} from './test_bindings';
export function main() {
describe('ChangeDefinitionFactory', () => {
beforeEachBindings(() => TEST_BINDINGS);
var parser: TemplateParser;
var dispatcher: TestDispatcher;
var context: TestContext;
var directive: TestDirective;
var locals: Locals;
var pipes: Pipes;
var eventLocals: Locals;
beforeEach(inject([TemplateParser], (_templateParser) => {
parser = _templateParser;
context = new TestContext();
directive = new TestDirective();
dispatcher = new TestDispatcher([directive], []);
locals = new Locals(null, MapWrapper.createFromStringMap({'someVar': null}));
eventLocals = new Locals(null, MapWrapper.createFromStringMap({'$event': null}));
pipes = new TestPipes();
}));
function createChangeDetector(template: string, directives: CompileDirectiveMetadata[],
protoViewIndex: number = 0): ChangeDetector {
var protoChangeDetectors =
createChangeDetectorDefinitions(new CompileTypeMetadata({name: 'SomeComp'}),
ChangeDetectionStrategy.Default,
new ChangeDetectorGenConfig(true, true, false, false),
parser.parse(template, directives, 'TestComp'))
.map(definition => new DynamicProtoChangeDetector(definition));
var changeDetector = protoChangeDetectors[protoViewIndex].instantiate(dispatcher);
changeDetector.hydrate(context, locals, dispatcher, pipes);
return changeDetector;
}
it('should watch element properties', () => {
var changeDetector = createChangeDetector('<div [el-prop]="someProp">', [], 0);
context.someProp = 'someValue';
changeDetector.detectChanges();
expect(dispatcher.log).toEqual(['elementProperty(elProp)=someValue']);
});
it('should watch text nodes', () => {
var changeDetector = createChangeDetector('{{someProp}}', [], 0);
context.someProp = 'someValue';
changeDetector.detectChanges();
expect(dispatcher.log).toEqual(['textNode(null)=someValue']);
});
it('should handle events', () => {
var changeDetector = createChangeDetector('<div on-click="onEvent($event)">', [], 0);
eventLocals.set('$event', 'click');
changeDetector.handleEvent('click', 0, eventLocals);
expect(context.eventLog).toEqual(['click']);
});
it('should handle events with targets', () => {
var changeDetector = createChangeDetector('<div (window:click)="onEvent($event)">', [], 0);
eventLocals.set('$event', 'click');
changeDetector.handleEvent('window:click', 0, eventLocals);
expect(context.eventLog).toEqual(['click']);
});
it('should watch variables', () => {
var changeDetector = createChangeDetector('<div #some-var [el-prop]="someVar">', [], 0);
locals.set('someVar', 'someValue');
changeDetector.detectChanges();
expect(dispatcher.log).toEqual(['elementProperty(elProp)=someValue']);
});
it('should write directive properties', () => {
var dirMeta = CompileDirectiveMetadata.create({
type: new CompileTypeMetadata({name: 'SomeDir'}),
selector: '[dir-prop]',
inputs: ['dirProp']
});
var changeDetector = createChangeDetector('<div [dir-prop]="someProp">', [dirMeta], 0);
context.someProp = 'someValue';
changeDetector.detectChanges();
expect(directive.dirProp).toEqual('someValue');
});
it('should write template directive properties', () => {
var dirMeta = CompileDirectiveMetadata.create({
type: new CompileTypeMetadata({name: 'SomeDir'}),
selector: '[dir-prop]',
inputs: ['dirProp']
});
var changeDetector = createChangeDetector('<template [dir-prop]="someProp">', [dirMeta], 0);
context.someProp = 'someValue';
changeDetector.detectChanges();
expect(directive.dirProp).toEqual('someValue');
});
it('should watch directive host properties', () => {
var dirMeta = CompileDirectiveMetadata.create({
type: new CompileTypeMetadata({name: 'SomeDir'}),
selector: 'div',
host: {'[elProp]': 'dirProp'}
});
var changeDetector = createChangeDetector('<div>', [dirMeta], 0);
directive.dirProp = 'someValue';
changeDetector.detectChanges();
expect(dispatcher.log).toEqual(['elementProperty(elProp)=someValue']);
});
it('should handle directive events', () => {
var dirMeta = CompileDirectiveMetadata.create({
type: new CompileTypeMetadata({name: 'SomeDir'}),
selector: 'div',
host: {'(click)': 'onEvent($event)'}
});
var changeDetector = createChangeDetector('<div>', [dirMeta], 0);
eventLocals.set('$event', 'click');
changeDetector.handleEvent('click', 0, eventLocals);
expect(directive.eventLog).toEqual(['click']);
});
it('should create change detectors for embedded templates', () => {
var changeDetector = createChangeDetector('<template>{{someProp}}<template>', [], 1);
context.someProp = 'someValue';
changeDetector.detectChanges();
expect(dispatcher.log).toEqual(['textNode(null)=someValue']);
});
it('should watch expressions after embedded templates', () => {
var changeDetector =
createChangeDetector('<template>{{someProp2}}</template>{{someProp}}', [], 0);
context.someProp = 'someValue';
changeDetector.detectChanges();
expect(dispatcher.log).toEqual(['textNode(null)=someValue']);
});
});
}
class TestContext {
eventLog: string[] = [];
someProp: string;
someProp2: string;
onEvent(value: string) { this.eventLog.push(value); }
}

View File

@ -0,0 +1,154 @@
import {
ddescribe,
describe,
xdescribe,
it,
iit,
xit,
expect,
beforeEach,
afterEach,
AsyncTestCompleter,
inject,
beforeEachBindings
} from 'angular2/test_lib';
import {bind} from 'angular2/src/core/di';
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';
import {ChangeDetectionCompiler} from 'angular2/src/compiler/change_detector_compiler';
import {
CompileDirectiveMetadata,
CompileTypeMetadata
} from 'angular2/src/compiler/directive_metadata';
import {
SourceModule,
SourceExpression,
SourceExpressions,
moduleRef
} from 'angular2/src/compiler/source_module';
import {TemplateParser} from 'angular2/src/compiler/template_parser';
import {
ChangeDetectorGenConfig,
ChangeDetectionStrategy,
ChangeDispatcher,
DirectiveIndex,
Locals,
BindingTarget,
ChangeDetector
} from 'angular2/src/core/change_detection/change_detection';
import {evalModule} from './eval_module';
import {TEST_BINDINGS} from './test_bindings';
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!
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', () => {
beforeEachBindings(() => TEST_BINDINGS);
var parser: TemplateParser;
var compiler: ChangeDetectionCompiler;
beforeEach(inject([TemplateParser, ChangeDetectionCompiler], (_parser, _compiler) => {
parser = _parser;
compiler = _compiler;
}));
describe('compileComponentRuntime', () => {
function detectChanges(compiler: ChangeDetectionCompiler, template: string,
directives: CompileDirectiveMetadata[] = CONST_EXPR([])): string[] {
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);
return testChangeDetector(factories[0]);
}
describe('no jit', () => {
beforeEachBindings(() => [
bind(ChangeDetectorGenConfig)
.toValue(new ChangeDetectorGenConfig(true, true, false, false))
]);
it('should watch element properties', () => {
expect(detectChanges(compiler, '<div [el-prop]="someProp">'))
.toEqual(['elementProperty(elProp)=someValue']);
});
});
describe('jit', () => {
beforeEachBindings(() => [
bind(ChangeDetectorGenConfig)
.toValue(new ChangeDetectorGenConfig(true, true, false, true))
]);
it('should watch element properties', () => {
expect(detectChanges(compiler, '<div [el-prop]="someProp">'))
.toEqual(['elementProperty(elProp)=someValue']);
});
});
});
describe('compileComponentCodeGen', () => {
function detectChanges(compiler: ChangeDetectionCompiler, template: string,
directives: CompileDirectiveMetadata[] = CONST_EXPR([])):
Promise<string[]> {
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);
var testableModule = createTestableModule(sourceExpressions, 0).getSourceWithImports();
return evalModule(testableModule.source, testableModule.imports, null);
}
it('should watch element properties', inject([AsyncTestCompleter], (async) => {
detectChanges(compiler, '<div [el-prop]="someProp">')
.then((value) => {
expect(value).toEqual(['elementProperty(elProp)=someValue']);
async.done();
});
}));
});
});
}
function createTestableModule(source: SourceExpressions, changeDetectorIndex: number):
SourceModule {
var resultExpression =
`${THIS_MODULE_REF}testChangeDetector(([${source.expressions.join(',')}])[${changeDetectorIndex}])`;
var testableSource = `${source.declarations.join('\n')}
${codeGenExportVariable('run')}${codeGenValueFn(['_'], resultExpression)};`;
return new SourceModule(null, testableSource);
}
export function testChangeDetector(changeDetectorFactory: Function): string[] {
var dispatcher = new TestDispatcher([], []);
var cd = changeDetectorFactory(dispatcher);
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

@ -0,0 +1,44 @@
import {isBlank} from 'angular2/src/core/facade/lang';
import {Pipes} from 'angular2/src/core/change_detection/pipes';
import {
ProtoChangeDetector,
ChangeDispatcher,
DirectiveIndex,
BindingTarget
} from 'angular2/src/core/change_detection/change_detection';
export class TestDirective {
eventLog: string[] = [];
dirProp: string;
onEvent(value: string) { this.eventLog.push(value); }
}
export class TestDispatcher implements ChangeDispatcher {
log: string[];
constructor(public directives: any[], public detectors: ProtoChangeDetector[]) { this.clear(); }
getDirectiveFor(di: DirectiveIndex) { return this.directives[di.directiveIndex]; }
getDetectorFor(di: DirectiveIndex) { return this.detectors[di.directiveIndex]; }
clear() { this.log = []; }
notifyOnBinding(target: BindingTarget, value) {
this.log.push(`${target.mode}(${target.name})=${this._asString(value)}`);
}
logBindingUpdate(target, value) {}
notifyAfterContentChecked() {}
notifyAfterViewChecked() {}
getDebugContext(a, b) { return null; }
_asString(value) { return (isBlank(value) ? 'null' : value.toString()); }
}
export class TestPipes implements Pipes {
get(type: string) { return null; }
}

View File

@ -0,0 +1,560 @@
import {
ddescribe,
describe,
xdescribe,
it,
iit,
xit,
expect,
beforeEach,
afterEach,
AsyncTestCompleter,
inject,
beforeEachBindings
} from 'angular2/test_lib';
import {CONST_EXPR, stringify, isType, Type, isBlank} from 'angular2/src/core/facade/lang';
import {MapWrapper} from 'angular2/src/core/facade/collection';
import {PromiseWrapper, Promise} from 'angular2/src/core/facade/async';
import {TemplateParser} from 'angular2/src/compiler/template_parser';
import {
CommandVisitor,
TextCmd,
NgContentCmd,
BeginElementCmd,
BeginComponentCmd,
EmbeddedTemplateCmd,
TemplateCmd,
visitAllCommands,
CompiledTemplate
} from 'angular2/src/core/linker/template_commands';
import {CommandCompiler} from 'angular2/src/compiler/command_compiler';
import {
CompileDirectiveMetadata,
CompileTypeMetadata,
CompileTemplateMetadata
} from 'angular2/src/compiler/directive_metadata';
import {SourceModule, SourceExpression, moduleRef} from 'angular2/src/compiler/source_module';
import {ViewEncapsulation} from 'angular2/src/core/render/api';
import {evalModule} from './eval_module';
import {
escapeSingleQuoteString,
codeGenValueFn,
codeGenExportVariable,
MODULE_SUFFIX
} from 'angular2/src/compiler/util';
import {TEST_BINDINGS} from './test_bindings';
const BEGIN_ELEMENT = 'BEGIN_ELEMENT';
const END_ELEMENT = 'END_ELEMENT';
const BEGIN_COMPONENT = 'BEGIN_COMPONENT';
const END_COMPONENT = 'END_COMPONENT';
const TEXT = 'TEXT';
const NG_CONTENT = 'NG_CONTENT';
const EMBEDDED_TEMPLATE = 'EMBEDDED_TEMPLATE';
// Attention: These module names have to correspond to real modules!
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/linker/template_commands${MODULE_SUFFIX}`);
// Attention: read by eval!
export class RootComp {}
export class SomeDir {}
export class AComp {}
var RootCompTypeMeta =
new CompileTypeMetadata({name: 'RootComp', runtime: RootComp, moduleUrl: THIS_MODULE_URL});
var SomeDirTypeMeta =
new CompileTypeMetadata({name: 'SomeDir', runtime: SomeDir, moduleUrl: THIS_MODULE_URL});
var ACompTypeMeta =
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';
var NESTED_COMPONENT = new CompiledTemplate(45, () => []);
export function main() {
describe('CommandCompiler', () => {
beforeEachBindings(() => TEST_BINDINGS);
var parser: TemplateParser;
var commandCompiler: CommandCompiler;
var componentTemplateFactory: Function;
beforeEach(inject([TemplateParser, CommandCompiler], (_templateParser, _commandCompiler) => {
parser = _templateParser;
commandCompiler = _commandCompiler;
}));
function createComp({type, selector, template, encapsulation, ngContentSelectors}: {
type?: CompileTypeMetadata,
selector?: string,
template?: string,
encapsulation?: ViewEncapsulation,
ngContentSelectors?: string[]
}): CompileDirectiveMetadata {
if (isBlank(encapsulation)) {
encapsulation = ViewEncapsulation.None;
}
if (isBlank(selector)) {
selector = 'root';
}
if (isBlank(ngContentSelectors)) {
ngContentSelectors = [];
}
if (isBlank(template)) {
template = '';
}
return CompileDirectiveMetadata.create({
selector: selector,
isComponent: true,
type: type,
template: new CompileTemplateMetadata({
template: template,
ngContentSelectors: ngContentSelectors,
encapsulation: encapsulation
})
});
}
function createDirective(type: CompileTypeMetadata, selector: string, exportAs: string = null):
CompileDirectiveMetadata {
return CompileDirectiveMetadata.create(
{selector: selector, exportAs: exportAs, isComponent: false, type: type});
}
function createTests(run: Function) {
describe('text', () => {
it('should create unbound text commands', inject([AsyncTestCompleter], (async) => {
var rootComp = createComp({type: RootCompTypeMeta, template: 'a'});
run(rootComp, [])
.then((data) => {
expect(data).toEqual([[TEXT, 'a', false, null]]);
async.done();
});
}));
it('should create bound text commands', inject([AsyncTestCompleter], (async) => {
var rootComp = createComp({type: RootCompTypeMeta, template: '{{a}}'});
run(rootComp, [])
.then((data) => {
expect(data).toEqual([[TEXT, null, true, null]]);
async.done();
});
}));
});
describe('elements', () => {
it('should create unbound element commands', inject([AsyncTestCompleter], (async) => {
var rootComp = createComp({type: RootCompTypeMeta, template: '<div a="b">'});
run(rootComp, [])
.then((data) => {
expect(data).toEqual([
[BEGIN_ELEMENT, 'div', ['a', 'b'], [], [], [], false, null],
[END_ELEMENT]
]);
async.done();
});
}));
it('should create bound element commands', inject([AsyncTestCompleter], (async) => {
var rootComp = createComp({
type: RootCompTypeMeta,
template: '<div a="b" #some-var (click)="someHandler" (window:scroll)="scrollTo()">'
});
run(rootComp, [])
.then((data) => {
expect(data).toEqual([
[
BEGIN_ELEMENT,
'div',
['a', 'b'],
[null, 'click', 'window', 'scroll'],
['someVar', null],
[],
true,
null
],
[END_ELEMENT]
]);
async.done();
});
}));
it('should create element commands with directives',
inject([AsyncTestCompleter], (async) => {
var rootComp =
createComp({type: RootCompTypeMeta, template: '<div a #some-var="someExport">'});
var dir = CompileDirectiveMetadata.create({
selector: '[a]',
exportAs: 'someExport',
isComponent: false,
type: SomeDirTypeMeta,
host: {'(click)': 'doIt()', '(window:scroll)': 'doIt()', 'role': 'button'}
});
run(rootComp, [dir])
.then((data) => {
expect(data).toEqual([
[
BEGIN_ELEMENT,
'div',
['a', '', 'role', 'button'],
[null, 'click', 'window', 'scroll'],
['someVar', 0],
['SomeDirType'],
true,
null
],
[END_ELEMENT]
]);
async.done();
});
}));
it('should emulate style encapsulation', inject([AsyncTestCompleter], (async) => {
var rootComp = createComp({
type: RootCompTypeMeta,
template: '<div>',
encapsulation: ViewEncapsulation.Emulated
});
run(rootComp, [])
.then((data) => {
expect(data).toEqual([
[BEGIN_ELEMENT, 'div', ['_ngcontent-app1-1', ''], [], [], [], false, null],
[END_ELEMENT]
]);
async.done();
});
}));
it('should create nested nodes', inject([AsyncTestCompleter], (async) => {
var rootComp = createComp({type: RootCompTypeMeta, template: '<div>a</div>'});
run(rootComp, [])
.then((data) => {
expect(data).toEqual([
[BEGIN_ELEMENT, 'div', [], [], [], [], false, null],
[TEXT, 'a', false, null],
[END_ELEMENT]
]);
async.done();
});
}));
});
describe('components', () => {
it('should create component commands', inject([AsyncTestCompleter], (async) => {
var rootComp = createComp(
{type: RootCompTypeMeta, template: '<a a="b" #some-var (click)="someHandler">'});
var comp = createComp({type: ACompTypeMeta, selector: 'a'});
run(rootComp, [comp])
.then((data) => {
expect(data).toEqual([
[
BEGIN_COMPONENT,
'a',
['a', 'b'],
[null, 'click'],
['someVar', 0],
['ACompType'],
false,
null,
3
],
[END_COMPONENT]
]);
async.done();
});
}));
it('should emulate style encapsulation on host elements',
inject([AsyncTestCompleter], (async) => {
var rootComp = createComp({
type: RootCompTypeMeta,
template: '<a></a>',
encapsulation: ViewEncapsulation.Emulated
});
var comp = createComp(
{type: ACompTypeMeta, selector: 'a', encapsulation: ViewEncapsulation.Emulated});
run(rootComp, [comp])
.then((data) => {
expect(data).toEqual([
[
BEGIN_COMPONENT,
'a',
['_nghost-app1-3', '', '_ngcontent-app1-1', ''],
[],
[],
['ACompType'],
false,
null,
3
],
[END_COMPONENT]
]);
async.done();
});
}));
it('should set nativeShadow flag', inject([AsyncTestCompleter], (async) => {
var rootComp = createComp({type: RootCompTypeMeta, template: '<a></a>'});
var comp = createComp(
{type: ACompTypeMeta, selector: 'a', encapsulation: ViewEncapsulation.Native});
run(rootComp, [comp])
.then((data) => {
expect(data).toEqual([
[BEGIN_COMPONENT, 'a', [], [], [], ['ACompType'], true, null, 3],
[END_COMPONENT]
]);
async.done();
});
}));
it('should create nested nodes and set ngContentIndex',
inject([AsyncTestCompleter], (async) => {
var rootComp = createComp({type: RootCompTypeMeta, template: '<a>t</a>'});
var comp = createComp({type: ACompTypeMeta, selector: 'a', ngContentSelectors: ['*']});
run(rootComp, [comp])
.then((data) => {
expect(data).toEqual([
[BEGIN_COMPONENT, 'a', [], [], [], ['ACompType'], false, null, 3],
[TEXT, 't', false, 0],
[END_COMPONENT]
]);
async.done();
});
}));
});
describe('embedded templates', () => {
it('should create embedded template commands', inject([AsyncTestCompleter], (async) => {
var rootComp =
createComp({type: RootCompTypeMeta, template: '<template a="b"></template>'});
var dir = createDirective(SomeDirTypeMeta, '[a]');
run(rootComp, [dir], 1)
.then((data) => {
expect(data).toEqual([
[EMBEDDED_TEMPLATE, ['a', 'b'], [], ['SomeDirType'], false, null, 'cd1', []]
]);
async.done();
});
}));
it('should keep variable name and value for <template> elements',
inject([AsyncTestCompleter], (async) => {
var rootComp = createComp({
type: RootCompTypeMeta,
template: '<template #some-var="someValue" #some-empty-var></template>'
});
var dir = createDirective(SomeDirTypeMeta, '[a]');
run(rootComp, [dir], 1)
.then((data) => {
expect(data[0][2])
.toEqual(['someEmptyVar', '$implicit', 'someVar', 'someValue']);
async.done();
});
}));
it('should keep variable name and value for template attributes',
inject([AsyncTestCompleter], (async) => {
var rootComp = createComp({
type: RootCompTypeMeta,
template: '<div template="var someVar=someValue; var someEmptyVar"></div>'
});
var dir = createDirective(SomeDirTypeMeta, '[a]');
run(rootComp, [dir], 1)
.then((data) => {
expect(data[0][2])
.toEqual(['someVar', 'someValue', 'someEmptyVar', '$implicit']);
async.done();
});
}));
it('should created nested nodes', inject([AsyncTestCompleter], (async) => {
var rootComp =
createComp({type: RootCompTypeMeta, template: '<template>t</template>'});
run(rootComp, [], 1)
.then((data) => {
expect(data).toEqual([
[
EMBEDDED_TEMPLATE,
[],
[],
[],
false,
null,
'cd1',
[[TEXT, 't', false, null]]
]
]);
async.done();
});
}));
it('should calculate wether the template is merged based on nested ng-content elements',
inject([AsyncTestCompleter], (async) => {
var rootComp = createComp({
type: RootCompTypeMeta,
template: '<template><ng-content></ng-content></template>'
});
run(rootComp, [], 1)
.then((data) => {
expect(data).toEqual(
[[EMBEDDED_TEMPLATE, [], [], [], true, null, 'cd1', [[NG_CONTENT, null]]]]);
async.done();
});
}));
});
describe('ngContent', () => {
it('should create ng-content commands', inject([AsyncTestCompleter], (async) => {
var rootComp =
createComp({type: RootCompTypeMeta, template: '<ng-content></ng-content>'});
run(rootComp, [])
.then((data) => {
expect(data).toEqual([[NG_CONTENT, null]]);
async.done();
});
}));
});
}
describe('compileComponentRuntime', () => {
beforeEach(() => {
componentTemplateFactory = (directive: CompileDirectiveMetadata) => {
return new CompiledTemplate(compTypeTemplateId.get(directive.type), () => []);
};
});
function run(component: CompileDirectiveMetadata, directives: CompileDirectiveMetadata[],
embeddedTemplateCount: number = 0): Promise<any[][]> {
var changeDetectorFactories = [];
for (var i = 0; i < embeddedTemplateCount + 1; i++) {
(function(i) { changeDetectorFactories.push((_) => `cd${i}`); })(i);
}
var parsedTemplate =
parser.parse(component.template.template, directives, component.type.name);
var commands = commandCompiler.compileComponentRuntime(
component, APP_ID, compTypeTemplateId.get(component.type), parsedTemplate,
changeDetectorFactories, componentTemplateFactory);
return PromiseWrapper.resolve(humanize(commands));
}
createTests(run);
});
describe('compileComponentCodeGen', () => {
beforeEach(() => {
componentTemplateFactory = (directive: CompileDirectiveMetadata) => {
return `new ${TEMPLATE_COMMANDS_MODULE_REF}CompiledTemplate(${compTypeTemplateId.get(directive.type)}, ${codeGenValueFn([], '{}')})`;
};
});
function run(component: CompileDirectiveMetadata, directives: CompileDirectiveMetadata[],
embeddedTemplateCount: number = 0): Promise<any[][]> {
var changeDetectorFactoryExpressions = [];
for (var i = 0; i < embeddedTemplateCount + 1; i++) {
changeDetectorFactoryExpressions.push(codeGenValueFn(['_'], `'cd${i}'`));
}
var parsedTemplate =
parser.parse(component.template.template, directives, component.type.name);
var sourceModule = commandCompiler.compileComponentCodeGen(
component, `'${APP_ID}'`, `${compTypeTemplateId.get(component.type)}`, parsedTemplate,
changeDetectorFactoryExpressions, componentTemplateFactory);
var testableModule = createTestableModule(sourceModule).getSourceWithImports();
return evalModule(testableModule.source, testableModule.imports, null);
}
createTests(run);
});
});
}
// Attention: read by eval!
export function humanize(cmds: TemplateCmd[]): any[][] {
var visitor = new CommandHumanizer();
visitAllCommands(visitor, cmds);
return visitor.result;
}
function checkAndStringifyType(type: Type): string {
expect(isType(type)).toBe(true);
return `${stringify(type)}Type`;
}
class CommandHumanizer implements CommandVisitor {
result: any[][] = [];
visitText(cmd: TextCmd, context: any): any {
this.result.push([TEXT, cmd.value, cmd.isBound, cmd.ngContentIndex]);
return null;
}
visitNgContent(cmd: NgContentCmd, context: any): any {
this.result.push([NG_CONTENT, cmd.ngContentIndex]);
return null;
}
visitBeginElement(cmd: BeginElementCmd, context: any): any {
this.result.push([
BEGIN_ELEMENT,
cmd.name,
cmd.attrNameAndValues,
cmd.eventTargetAndNames,
cmd.variableNameAndValues,
cmd.directives.map(checkAndStringifyType),
cmd.isBound,
cmd.ngContentIndex
]);
return null;
}
visitEndElement(context: any): any {
this.result.push([END_ELEMENT]);
return null;
}
visitBeginComponent(cmd: BeginComponentCmd, context: any): any {
this.result.push([
BEGIN_COMPONENT,
cmd.name,
cmd.attrNameAndValues,
cmd.eventTargetAndNames,
cmd.variableNameAndValues,
cmd.directives.map(checkAndStringifyType),
cmd.nativeShadow,
cmd.ngContentIndex,
cmd.template.id
]);
return null;
}
visitEndComponent(context: any): any {
this.result.push([END_COMPONENT]);
return null;
}
visitEmbeddedTemplate(cmd: EmbeddedTemplateCmd, context: any): any {
this.result.push([
EMBEDDED_TEMPLATE,
cmd.attrNameAndValues,
cmd.variableNameAndValues,
cmd.directives.map(checkAndStringifyType),
cmd.isMerged,
cmd.ngContentIndex,
cmd.changeDetectorFactory(null),
humanize(cmd.children)
]);
return null;
}
}
function createTestableModule(source: SourceExpression): SourceModule {
var resultExpression = `${THIS_MODULE_REF}humanize(${source.expression})`;
var testableSource = `${source.declarations.join('\n')}
${codeGenExportVariable('run')}${codeGenValueFn(['_'], resultExpression)};`;
return new SourceModule(null, testableSource);
}

View File

@ -0,0 +1,90 @@
import {
AsyncTestCompleter,
beforeEach,
ddescribe,
describe,
el,
expect,
iit,
inject,
it,
xit,
TestComponentBuilder
} from 'angular2/test_lib';
import {
CompileDirectiveMetadata,
CompileTypeMetadata,
CompileTemplateMetadata
} from 'angular2/src/compiler/directive_metadata';
import {ViewEncapsulation} from 'angular2/src/core/render/api';
import {ChangeDetectionStrategy} from 'angular2/src/core/change_detection';
import {LifecycleHooks} from 'angular2/src/core/linker/interfaces';
export function main() {
describe('DirectiveMetadata', () => {
var fullTypeMeta: CompileTypeMetadata;
var fullTemplateMeta: CompileTemplateMetadata;
var fullDirectiveMeta: CompileDirectiveMetadata;
beforeEach(() => {
fullTypeMeta =
new CompileTypeMetadata({name: 'SomeType', moduleUrl: 'someUrl', isHost: true});
fullTemplateMeta = new CompileTemplateMetadata({
encapsulation: ViewEncapsulation.Emulated,
template: '<a></a>',
templateUrl: 'someTemplateUrl',
styles: ['someStyle'],
styleUrls: ['someStyleUrl'],
ngContentSelectors: ['*']
});
fullDirectiveMeta = CompileDirectiveMetadata.create({
selector: 'someSelector',
isComponent: true,
dynamicLoadable: true,
type: fullTypeMeta, template: fullTemplateMeta,
changeDetection: ChangeDetectionStrategy.Default,
inputs: ['someProp'],
outputs: ['someEvent'],
host: {'(event1)': 'handler1', '[prop1]': 'expr1', 'attr1': 'attrValue2'},
lifecycleHooks: [LifecycleHooks.OnChanges]
});
});
describe('DirectiveMetadata', () => {
it('should serialize with full data', () => {
expect(CompileDirectiveMetadata.fromJson(fullDirectiveMeta.toJson()))
.toEqual(fullDirectiveMeta);
});
it('should serialize with no data', () => {
var empty = CompileDirectiveMetadata.create();
expect(CompileDirectiveMetadata.fromJson(empty.toJson())).toEqual(empty);
});
});
describe('TypeMetadata', () => {
it('should serialize with full data', () => {
expect(CompileTypeMetadata.fromJson(fullTypeMeta.toJson())).toEqual(fullTypeMeta);
});
it('should serialize with no data', () => {
var empty = new CompileTypeMetadata();
expect(CompileTypeMetadata.fromJson(empty.toJson())).toEqual(empty);
});
});
describe('TemplateMetadata', () => {
it('should serialize with full data', () => {
expect(CompileTemplateMetadata.fromJson(fullTemplateMeta.toJson()))
.toEqual(fullTemplateMeta);
});
it('should serialize with no data', () => {
var empty = new CompileTemplateMetadata();
expect(CompileTemplateMetadata.fromJson(empty.toJson())).toEqual(empty);
});
});
});
}

View File

@ -0,0 +1,50 @@
import "dart:isolate";
import "dart:async";
Uri toDartDataUri(String source) {
return Uri.parse("data:application/dart;charset=utf-8,"
"${Uri.encodeComponent(source)}");
}
createIsolateSource(String moduleSource, List<List<String>> moduleImports) {
var moduleSourceParts = ['import "dart:isolate";'];
moduleImports.forEach((sourceImport) {
String modName = sourceImport[0];
String modAlias = sourceImport[1];
moduleSourceParts.add("import '${modName}' as ${modAlias};");
});
moduleSourceParts.add(moduleSource);
moduleSourceParts.add("""
main(List args, SendPort replyPort) {
replyPort.send(run(args));
}
""");
return moduleSourceParts.join('\n');
}
var timeStamp = new DateTime.now().millisecondsSinceEpoch;
dynamic callModule(dynamic data) { return data.map( (a) => a+1); }
evalModule(String moduleSource, List<List<String>> imports, List args) {
String source = createIsolateSource(moduleSource, imports);
Completer completer = new Completer();
RawReceivePort receivePort;
receivePort = new RawReceivePort( (message) {
receivePort.close();
completer.complete(message);
});
// Note: we have a special karma plugin that sends files under
// urls like /package_1234 as permanently cached.
// With this, spawning multiple isolates gets faster as Darts does not
// reload the files from the server.
var packageRoot = Uri.parse('/packages_${timeStamp}');
return Isolate.spawnUri(toDartDataUri(source), args, receivePort.sendPort, packageRoot: packageRoot).then( (isolate) {
RawReceivePort errorPort;
errorPort = new RawReceivePort( (message) {
completer.completeError(message);
});
isolate.addErrorListener(errorPort.sendPort);
return completer.future;
});
}

View File

@ -0,0 +1,44 @@
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++}`;
}
export function evalModule(moduleSource: string, imports: string[][], args: any[]): Promise<any> {
var moduleId = nextModuleId();
var moduleSourceWithImports = [];
var importModuleIds = [];
imports.forEach(sourceImport => {
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];
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` + `('${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, importModuleIds, false, moduleBody);
return <Promise<any>>System.import(moduleId).then((module) => module.run(args));
} else {
var exports = {};
moduleBody(require, exports, {});
return PromiseWrapper.resolve(exports['run'](args));
}
}

View File

@ -0,0 +1,50 @@
import {
ddescribe,
describe,
xdescribe,
it,
iit,
xit,
expect,
beforeEach,
afterEach,
AsyncTestCompleter,
inject
} from 'angular2/test_lib';
import {IS_DART} from '../platform';
import {evalModule} from './eval_module';
// This export is used by this test code
// when evaling the test module!
export var TEST_VALUE = 23;
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_URL, 'tst']], [1])
.then((value) => {
expect(value).toEqual([1, 23]);
async.done();
});
}));
});
}
var testDartModule = `
run(data) {
data.add(tst.TEST_VALUE);
return data;
}
`;
var testJsModule = `
exports.run = function(data) {
data.push(tst.TEST_VALUE);
return data;
}
`;

View File

@ -0,0 +1,131 @@
import {ddescribe, describe, it, iit, xit, expect, beforeEach, afterEach} from 'angular2/test_lib';
import {HtmlParser} from 'angular2/src/compiler/html_parser';
import {
HtmlAst,
HtmlAstVisitor,
HtmlElementAst,
HtmlAttrAst,
HtmlTextAst,
htmlVisitAll
} from 'angular2/src/compiler/html_ast';
export function main() {
describe('DomParser', () => {
var parser: HtmlParser;
beforeEach(() => { parser = new HtmlParser(); });
describe('parse', () => {
describe('text nodes', () => {
it('should parse root level text nodes', () => {
expect(humanizeDom(parser.parse('a', 'TestComp')))
.toEqual([[HtmlTextAst, 'a', 'TestComp > #text(a):nth-child(0)']]);
});
it('should parse text nodes inside regular elements', () => {
expect(humanizeDom(parser.parse('<div>a</div>', 'TestComp')))
.toEqual([
[HtmlElementAst, 'div', 'TestComp > div:nth-child(0)'],
[HtmlTextAst, 'a', 'TestComp > div:nth-child(0) > #text(a):nth-child(0)']
]);
});
it('should parse text nodes inside template elements', () => {
expect(humanizeDom(parser.parse('<template>a</template>', 'TestComp')))
.toEqual([
[HtmlElementAst, 'template', 'TestComp > template:nth-child(0)'],
[HtmlTextAst, 'a', 'TestComp > template:nth-child(0) > #text(a):nth-child(0)']
]);
});
});
describe('elements', () => {
it('should parse root level elements', () => {
expect(humanizeDom(parser.parse('<div></div>', 'TestComp')))
.toEqual([[HtmlElementAst, 'div', 'TestComp > div:nth-child(0)']]);
});
it('should parse elements inside of regular elements', () => {
expect(humanizeDom(parser.parse('<div><span></span></div>', 'TestComp')))
.toEqual([
[HtmlElementAst, 'div', 'TestComp > div:nth-child(0)'],
[HtmlElementAst, 'span', 'TestComp > div:nth-child(0) > span:nth-child(0)']
]);
});
it('should parse elements inside of template elements', () => {
expect(humanizeDom(parser.parse('<template><span></span></template>', 'TestComp')))
.toEqual([
[HtmlElementAst, 'template', 'TestComp > template:nth-child(0)'],
[HtmlElementAst, 'span', 'TestComp > template:nth-child(0) > span:nth-child(0)']
]);
});
});
describe('attributes', () => {
it('should parse attributes on regular elements', () => {
expect(humanizeDom(parser.parse('<div k="v"></div>', 'TestComp')))
.toEqual([
[HtmlElementAst, 'div', 'TestComp > div:nth-child(0)'],
[HtmlAttrAst, 'k', 'v', 'TestComp > div:nth-child(0)[k=v]']
]);
});
it('should parse attributes on template elements', () => {
expect(humanizeDom(parser.parse('<template k="v"></template>', 'TestComp')))
.toEqual([
[HtmlElementAst, 'template', 'TestComp > template:nth-child(0)'],
[HtmlAttrAst, 'k', 'v', 'TestComp > template:nth-child(0)[k=v]']
]);
});
});
});
describe('unparse', () => {
it('should unparse text nodes',
() => { expect(parser.unparse(parser.parse('a', null))).toEqual('a'); });
it('should unparse elements',
() => { expect(parser.unparse(parser.parse('<a></a>', null))).toEqual('<a></a>'); });
it('should unparse attributes', () => {
expect(parser.unparse(parser.parse('<div a b="c"></div>', null)))
.toEqual('<div a="" b="c"></div>');
});
it('should unparse nested elements', () => {
expect(parser.unparse(parser.parse('<div><a></a></div>', null)))
.toEqual('<div><a></a></div>');
});
it('should unparse nested text nodes', () => {
expect(parser.unparse(parser.parse('<div>a</div>', null))).toEqual('<div>a</div>');
});
});
});
}
function humanizeDom(asts: HtmlAst[]): any[] {
var humanizer = new Humanizer();
htmlVisitAll(humanizer, asts);
return humanizer.result;
}
class Humanizer implements HtmlAstVisitor {
result: any[] = [];
visitElement(ast: HtmlElementAst, context: any): any {
this.result.push([HtmlElementAst, ast.name, ast.sourceInfo]);
htmlVisitAll(this, ast.attrs);
htmlVisitAll(this, ast.children);
return null;
}
visitAttr(ast: HtmlAttrAst, context: any): any {
this.result.push([HtmlAttrAst, ast.name, ast.value, ast.sourceInfo]);
return null;
}
visitText(ast: HtmlTextAst, context: any): any {
this.result.push([HtmlTextAst, ast.value, ast.sourceInfo]);
return null;
}
}

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/linker/template_commands';
import {RuntimeCompiler} from 'angular2/src/compiler/runtime_compiler';
import {ProtoViewFactory} from 'angular2/src/core/linker/proto_view_factory';
import {AppProtoView} from 'angular2/src/core/linker/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

@ -0,0 +1,127 @@
import {
ddescribe,
describe,
xdescribe,
it,
iit,
xit,
expect,
beforeEach,
afterEach,
AsyncTestCompleter,
inject,
beforeEachBindings
} from 'angular2/test_lib';
import {stringify} from 'angular2/src/core/facade/lang';
import {RuntimeMetadataResolver} from 'angular2/src/compiler/runtime_metadata';
import {LifecycleHooks, LIFECYCLE_HOOKS_VALUES} from 'angular2/src/core/linker/interfaces';
import {
Component,
View,
Directive,
ViewEncapsulation,
ChangeDetectionStrategy,
OnChanges,
OnInit,
DoCheck,
OnDestroy,
AfterContentInit,
AfterContentChecked,
AfterViewInit,
AfterViewChecked,
SimpleChange
} from 'angular2/core';
import {TEST_BINDINGS} from './test_bindings';
import {MODULE_SUFFIX, IS_DART} from 'angular2/src/compiler/util';
export function main() {
describe('RuntimeMetadataResolver', () => {
beforeEachBindings(() => TEST_BINDINGS);
describe('getMetadata', () => {
it('should read metadata',
inject([RuntimeMetadataResolver], (resolver: RuntimeMetadataResolver) => {
var meta = resolver.getMetadata(ComponentWithEverything);
expect(meta.selector).toEqual('someSelector');
expect(meta.exportAs).toEqual('someExportAs');
expect(meta.isComponent).toBe(true);
expect(meta.dynamicLoadable).toBe(true);
expect(meta.type.runtime).toBe(ComponentWithEverything);
expect(meta.type.name).toEqual(stringify(ComponentWithEverything));
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'});
expect(meta.outputs).toEqual({'someEvent': 'someEvent'});
expect(meta.hostListeners).toEqual({'someHostListener': 'someHostListenerExpr'});
expect(meta.hostProperties).toEqual({'someHostProp': 'someHostPropExpr'});
expect(meta.hostAttributes).toEqual({'someHostAttr': 'someHostAttrValue'});
expect(meta.template.encapsulation).toBe(ViewEncapsulation.Emulated);
expect(meta.template.styles).toEqual(['someStyle']);
expect(meta.template.styleUrls).toEqual(['someStyleUrl']);
expect(meta.template.template).toEqual('someTemplate');
expect(meta.template.templateUrl).toEqual('someTemplateUrl');
}));
it('should use the moduleUrl from the reflector if none is given',
inject([RuntimeMetadataResolver], (resolver: RuntimeMetadataResolver) => {
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);
}));
});
describe('getViewDirectivesMetadata', () => {
it('should return the directive metadatas',
inject([RuntimeMetadataResolver], (resolver: RuntimeMetadataResolver) => {
expect(resolver.getViewDirectivesMetadata(ComponentWithEverything))
.toEqual([resolver.getMetadata(DirectiveWithoutModuleId)]);
}));
});
});
}
@Directive({selector: 'someSelector'})
class DirectiveWithoutModuleId {
}
@Component({
selector: 'someSelector',
inputs: ['someProp'],
outputs: ['someEvent'],
host: {
'[someHostProp]': 'someHostPropExpr',
'(someHostListener)': 'someHostListenerExpr',
'someHostAttr': 'someHostAttrValue'
},
exportAs: 'someExportAs',
moduleId: 'someModuleId',
changeDetection: ChangeDetectionStrategy.CheckAlways
})
@View({
template: 'someTemplate',
templateUrl: 'someTemplateUrl',
encapsulation: ViewEncapsulation.Emulated,
styles: ['someStyle'],
styleUrls: ['someStyleUrl'],
directives: [DirectiveWithoutModuleId]
})
class ComponentWithEverything implements OnChanges,
OnInit, DoCheck, OnDestroy, AfterContentInit, AfterContentChecked, AfterViewInit,
AfterViewChecked {
onChanges(changes: StringMap<string, SimpleChange>): void {}
onInit(): void {}
doCheck(): void {}
onDestroy(): void {}
afterContentInit(): void {}
afterContentChecked(): void {}
afterViewInit(): void {}
afterViewChecked(): void {}
}

View File

@ -0,0 +1,17 @@
import {ElementSchemaRegistry} from 'angular2/src/core/render/dom/schema/element_schema_registry';
import {StringMap} from 'angular2/src/core/facade/collection';
import {isPresent} from 'angular2/src/core/facade/lang';
export class MockSchemaRegistry implements ElementSchemaRegistry {
constructor(public existingProperties: StringMap<string, boolean>,
public attrPropMapping: StringMap<string, string>) {}
hasProperty(tagName: string, property: string): boolean {
var result = this.existingProperties[property];
return isPresent(result) ? result : true;
}
getMappedPropName(attrName: string): string {
var result = this.attrPropMapping[attrName];
return isPresent(result) ? result : attrName;
}
}

View File

@ -0,0 +1,47 @@
import {
AsyncTestCompleter,
beforeEach,
ddescribe,
describe,
el,
expect,
iit,
inject,
it,
xit,
TestComponentBuilder
} from 'angular2/test_lib';
import {SourceModule, moduleRef} from 'angular2/src/compiler/source_module';
export function main() {
describe('SourceModule', () => {
describe('getSourceWithImports', () => {
it('should generate named imports for modules', () => {
var sourceWithImports =
new SourceModule('package:some/moda', `${moduleRef('package:some/modb')}A`)
.getSourceWithImports();
expect(sourceWithImports.source).toEqual('import0.A');
expect(sourceWithImports.imports).toEqual([['package:some/modb', 'import0']]);
});
it('should dedupe imports', () => {
var sourceWithImports =
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([['package:some/modb', 'import0']]);
});
it('should not use an import for the moduleUrl of the SourceModule', () => {
var sourceWithImports =
new SourceModule('package:some/moda', `${moduleRef('package:some/moda')}A`)
.getSourceWithImports();
expect(sourceWithImports.source).toEqual('A');
expect(sourceWithImports.imports).toEqual([]);
});
});
});
}

View File

@ -0,0 +1,2 @@
// used by style_compiler_spec.ts
export var STYLES = ['span[_ngcontent-%COMP%] {\ncolor: blue;\n}'];

View File

@ -0,0 +1,2 @@
// used by style_compiler_spec.ts
export var STYLES = ['span {color: blue}'];

View File

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

View File

@ -0,0 +1,92 @@
import {describe, it, expect, beforeEach, ddescribe, iit, xit, el} from 'angular2/test_lib';
import {resolveStyleUrls} from 'angular2/src/compiler/style_url_resolver';
import {UrlResolver} from 'angular2/src/core/services/url_resolver';
export function main() {
describe('StyleUrlResolver', () => {
var urlResolver;
beforeEach(() => { urlResolver = new UrlResolver(); });
it('should resolve "url()" urls', () => {
var css = `
.foo {
background-image: url("double.jpg");
background-image: url('simple.jpg');
background-image: url(noquote.jpg);
}`;
var expectedCss = `
.foo {
background-image: url('http://ng.io/double.jpg');
background-image: url('http://ng.io/simple.jpg');
background-image: url('http://ng.io/noquote.jpg');
}`;
var resolvedCss = resolveStyleUrls(urlResolver, 'http://ng.io', css).style;
expect(resolvedCss).toEqual(expectedCss);
});
it('should not strip quotes from inlined SVG styles', () => {
var css = `
.selector {
background:rgb(55,71,79) url('data:image/svg+xml;utf8,<?xml version="1.0"?>');
background:rgb(55,71,79) url("data:image/svg+xml;utf8,<?xml version='1.0'?>");
background:rgb(55,71,79) url("/some/data:image");
}
`;
var expectedCss = `
.selector {
background:rgb(55,71,79) url('data:image/svg+xml;utf8,<?xml version="1.0"?>');
background:rgb(55,71,79) url("data:image/svg+xml;utf8,<?xml version='1.0'?>");
background:rgb(55,71,79) url('http://ng.io/some/data:image');
}
`;
var resolvedCss = resolveStyleUrls(urlResolver, 'http://ng.io', css).style;
expect(resolvedCss).toEqual(expectedCss);
});
it('should extract "@import" urls', () => {
var css = `
@import '1.css';
@import "2.css";
`;
var styleWithImports = resolveStyleUrls(urlResolver, 'http://ng.io', css);
expect(styleWithImports.style.trim()).toEqual('');
expect(styleWithImports.styleUrls).toEqual(['http://ng.io/1.css', 'http://ng.io/2.css']);
});
it('should extract "@import url()" urls', () => {
var css = `
@import url('3.css');
@import url("4.css");
@import url(5.css);
`;
var styleWithImports = resolveStyleUrls(urlResolver, 'http://ng.io', css);
expect(styleWithImports.style.trim()).toEqual('');
expect(styleWithImports.styleUrls)
.toEqual(['http://ng.io/3.css', 'http://ng.io/4.css', 'http://ng.io/5.css']);
});
it('should extract "@import urls and keep rules in the same line', () => {
var css = `@import url('some.css');div {color: red};`;
var styleWithImports = resolveStyleUrls(urlResolver, 'http://ng.io', css);
expect(styleWithImports.style.trim()).toEqual('div {color: red};');
expect(styleWithImports.styleUrls).toEqual(['http://ng.io/some.css']);
});
it('should extract media query in "@import"', () => {
var css = `
@import 'print1.css' print;
@import url(print2.css) print;
`;
var styleWithImports = resolveStyleUrls(urlResolver, 'http://ng.io', css);
expect(styleWithImports.style.trim()).toEqual('');
expect(styleWithImports.styleUrls)
.toEqual(['http://ng.io/print1.css', 'http://ng.io/print2.css']);
});
});
}

View File

@ -0,0 +1,394 @@
import {
ddescribe,
describe,
xdescribe,
it,
iit,
xit,
expect,
beforeEach,
afterEach,
AsyncTestCompleter,
inject,
beforeEachBindings
} from 'angular2/test_lib';
import {Promise, PromiseWrapper} from 'angular2/src/core/facade/async';
import {Type, isPresent, isBlank, stringify, isString} from 'angular2/src/core/facade/lang';
import {MapWrapper, SetWrapper, ListWrapper} from 'angular2/src/core/facade/collection';
import {RuntimeMetadataResolver} from 'angular2/src/compiler/runtime_metadata';
import {
TemplateCompiler,
NormalizedComponentWithViewDirectives
} from 'angular2/src/compiler/template_compiler';
import {CompileDirectiveMetadata} from 'angular2/src/compiler/directive_metadata';
import {evalModule} from './eval_module';
import {SourceModule, moduleRef} from 'angular2/src/compiler/source_module';
import {XHR} from 'angular2/src/core/render/xhr';
import {MockXHR} from 'angular2/src/core/render/xhr_mock';
import {Locals} from 'angular2/src/core/change_detection/change_detection';
import {
CommandVisitor,
TextCmd,
NgContentCmd,
BeginElementCmd,
BeginComponentCmd,
EmbeddedTemplateCmd,
TemplateCmd,
visitAllCommands,
CompiledTemplate
} from 'angular2/src/core/linker/template_commands';
import {Component, View, Directive, bind} from 'angular2/core';
import {TEST_BINDINGS} from './test_bindings';
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_ID = 'angular2/test/compiler/template_compiler_spec';
var THIS_MODULE_REF = moduleRef(`package:${THIS_MODULE_ID}${MODULE_SUFFIX}`);
const APP_ID_VALUE = 'app1';
export function main() {
describe('TemplateCompiler', () => {
var compiler: TemplateCompiler;
var runtimeMetadataResolver: RuntimeMetadataResolver;
beforeEachBindings(() => [bind(APP_ID).toValue(APP_ID_VALUE), TEST_BINDINGS]);
beforeEach(inject([TemplateCompiler, RuntimeMetadataResolver],
(_compiler, _runtimeMetadataResolver) => {
compiler = _compiler;
runtimeMetadataResolver = _runtimeMetadataResolver;
}));
describe('compile templates', () => {
function runTests(compile) {
it('should throw for non components', inject([AsyncTestCompleter], (async) => {
PromiseWrapper.catchError(PromiseWrapper.wrap(() => compile([NonComponent])), (error) => {
expect(error.message)
.toEqual(
`Could not compile '${stringify(NonComponent)}' because it is not a component.`);
async.done();
});
}));
it('should compile host components', inject([AsyncTestCompleter], (async) => {
compile([CompWithBindingsAndStyles])
.then((humanizedTemplate) => {
expect(humanizedTemplate['styles']).toEqual([]);
expect(humanizedTemplate['commands'][0]).toEqual('<comp-a>');
expect(humanizedTemplate['cd']).toEqual(['elementProperty(title)=someDirValue']);
async.done();
});
}));
it('should compile nested components', inject([AsyncTestCompleter], (async) => {
compile([CompWithBindingsAndStyles])
.then((humanizedTemplate) => {
var nestedTemplate = humanizedTemplate['commands'][1];
expect(nestedTemplate['styles']).toEqual(['div {color: red}']);
expect(nestedTemplate['commands'][0]).toEqual('<a>');
expect(nestedTemplate['cd']).toEqual(['elementProperty(href)=someCtxValue']);
async.done();
});
}));
it('should compile recursive components', inject([AsyncTestCompleter], (async) => {
compile([TreeComp])
.then((humanizedTemplate) => {
expect(humanizedTemplate['commands'][0]).toEqual('<tree>');
expect(humanizedTemplate['commands'][1]['commands'][0]).toEqual('<tree>');
expect(humanizedTemplate['commands'][1]['commands'][1]['commands'][0])
.toEqual('<tree>');
async.done();
});
}));
it('should pass the right change detector to embedded templates',
inject([AsyncTestCompleter], (async) => {
compile([CompWithEmbeddedTemplate])
.then((humanizedTemplate) => {
expect(humanizedTemplate['commands'][1]['commands'][0]).toEqual('<template>');
expect(humanizedTemplate['commands'][1]['commands'][1]['cd'])
.toEqual(['elementProperty(href)=someCtxValue']);
async.done();
});
}));
}
describe('compileHostComponentRuntime', () => {
function compile(components: Type[]): Promise<any[]> {
return compiler.compileHostComponentRuntime(components[0])
.then((compiledHostTemplate) => humanizeTemplate(compiledHostTemplate.getTemplate()));
}
runTests(compile);
it('should cache components for parallel requests',
inject([AsyncTestCompleter, XHR], (async, xhr: MockXHR) => {
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)']);
expect(humanizedTemplates[1]['commands'][1]['commands']).toEqual(['#text(a)']);
async.done();
});
xhr.flush();
}));
it('should cache components for sequential requests',
inject([AsyncTestCompleter, XHR], (async, xhr: MockXHR) => {
xhr.expect('package:angular2/test/compiler/compUrl.html', 'a');
compile([CompWithTemplateUrl])
.then((humanizedTemplate0) => {
return compile([CompWithTemplateUrl])
.then((humanizedTemplate1) => {
expect(humanizedTemplate0['commands'][1]['commands'])
.toEqual(['#text(a)']);
expect(humanizedTemplate1['commands'][1]['commands'])
.toEqual(['#text(a)']);
async.done();
});
});
xhr.flush();
}));
it('should allow to clear the cache',
inject([AsyncTestCompleter, XHR], (async, xhr: MockXHR) => {
xhr.expect('package:angular2/test/compiler/compUrl.html', 'a');
compile([CompWithTemplateUrl])
.then((humanizedTemplate) => {
compiler.clearCache();
xhr.expect('package:angular2/test/compiler/compUrl.html', 'b');
var result = compile([CompWithTemplateUrl]);
xhr.flush();
return result;
})
.then((humanizedTemplate) => {
expect(humanizedTemplate['commands'][1]['commands']).toEqual(['#text(b)']);
async.done();
});
xhr.flush();
}));
});
describe('compileTemplatesCodeGen', () => {
function normalizeComponent(component: Type):
Promise<NormalizedComponentWithViewDirectives> {
var compAndViewDirMetas = [runtimeMetadataResolver.getMetadata(component)].concat(
runtimeMetadataResolver.getViewDirectivesMetadata(component));
return PromiseWrapper.all(compAndViewDirMetas.map(
meta => compiler.normalizeDirectiveMetadata(meta)))
.then((normalizedCompAndViewDirMetas: CompileDirectiveMetadata[]) =>
new NormalizedComponentWithViewDirectives(
normalizedCompAndViewDirMetas[0],
normalizedCompAndViewDirMetas.slice(1)));
}
function compile(components: Type[]): Promise<any[]> {
return PromiseWrapper.all(components.map(normalizeComponent))
.then((normalizedCompWithViewDirMetas: NormalizedComponentWithViewDirectives[]) => {
var sourceModule = compiler.compileTemplatesCodeGen(normalizedCompWithViewDirMetas);
var sourceWithImports =
testableTemplateModule(sourceModule,
normalizedCompWithViewDirMetas[0].component)
.getSourceWithImports();
return evalModule(sourceWithImports.source, sourceWithImports.imports, null);
});
}
runTests(compile);
});
});
describe('normalizeDirectiveMetadata', () => {
it('should return the given DirectiveMetadata for non components',
inject([AsyncTestCompleter], (async) => {
var meta = runtimeMetadataResolver.getMetadata(NonComponent);
compiler.normalizeDirectiveMetadata(meta).then(normMeta => {
expect(normMeta).toBe(meta);
async.done();
});
}));
it('should normalize the template',
inject([AsyncTestCompleter, XHR], (async, xhr: MockXHR) => {
xhr.expect('package:angular2/test/compiler/compUrl.html', 'loadedTemplate');
compiler.normalizeDirectiveMetadata(
runtimeMetadataResolver.getMetadata(CompWithTemplateUrl))
.then((meta: CompileDirectiveMetadata) => {
expect(meta.template.template).toEqual('loadedTemplate');
async.done();
});
xhr.flush();
}));
it('should copy all the other fields', inject([AsyncTestCompleter], (async) => {
var meta = runtimeMetadataResolver.getMetadata(CompWithBindingsAndStyles);
compiler.normalizeDirectiveMetadata(meta).then((normMeta: CompileDirectiveMetadata) => {
expect(normMeta.type).toEqual(meta.type);
expect(normMeta.isComponent).toEqual(meta.isComponent);
expect(normMeta.dynamicLoadable).toEqual(meta.dynamicLoadable);
expect(normMeta.selector).toEqual(meta.selector);
expect(normMeta.exportAs).toEqual(meta.exportAs);
expect(normMeta.changeDetection).toEqual(meta.changeDetection);
expect(normMeta.inputs).toEqual(meta.inputs);
expect(normMeta.outputs).toEqual(meta.outputs);
expect(normMeta.hostListeners).toEqual(meta.hostListeners);
expect(normMeta.hostProperties).toEqual(meta.hostProperties);
expect(normMeta.hostAttributes).toEqual(meta.hostAttributes);
expect(normMeta.lifecycleHooks).toEqual(meta.lifecycleHooks);
async.done();
});
}));
});
describe('compileStylesheetCodeGen', () => {
it('should compile stylesheets into code', inject([AsyncTestCompleter], (async) => {
var cssText = 'div {color: red}';
var sourceModule =
compiler.compileStylesheetCodeGen('package:someModuleUrl', cssText)[0];
var sourceWithImports = testableStylesModule(sourceModule).getSourceWithImports();
evalModule(sourceWithImports.source, sourceWithImports.imports, null)
.then(loadedCssText => {
expect(loadedCssText).toEqual([cssText]);
async.done();
});
}));
});
});
}
@Component({
selector: 'comp-a',
host: {'[title]': 'someProp'},
moduleId: THIS_MODULE_ID,
exportAs: 'someExportAs'
})
@View({template: '<a [href]="someProp"></a>', styles: ['div {color: red}']})
class CompWithBindingsAndStyles {
}
@Component({selector: 'tree', moduleId: THIS_MODULE_ID})
@View({template: '<tree></tree>', directives: [TreeComp]})
class TreeComp {
}
@Component({selector: 'comp-url', moduleId: THIS_MODULE_ID})
@View({templateUrl: 'compUrl.html'})
class CompWithTemplateUrl {
}
@Component({selector: 'comp-tpl', moduleId: THIS_MODULE_ID})
@View({template: '<template><a [href]="someProp"></a></template>'})
class CompWithEmbeddedTemplate {
}
@Directive({selector: 'plain', moduleId: THIS_MODULE_ID})
@View({template: ''})
class NonComponent {
}
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.moduleUrl, testableSource);
}
function testableStylesModule(sourceModule: SourceModule): SourceModule {
var testableSource = `${sourceModule.sourceWithModuleRefs}
${codeGenExportVariable('run')}${codeGenValueFn(['_'], 'STYLES')};`;
return new SourceModule(sourceModule.moduleUrl, testableSource);
}
// Attention: read by eval!
export function humanizeTemplate(template: CompiledTemplate,
humanizedTemplates: Map<number, StringMap<string, any>> = null):
StringMap<string, any> {
if (isBlank(humanizedTemplates)) {
humanizedTemplates = new Map<number, StringMap<string, any>>();
}
var result = humanizedTemplates.get(template.id);
if (isPresent(result)) {
return result;
}
var templateData = template.getData(APP_ID_VALUE);
var commands = [];
result = {
'styles': templateData.styles,
'commands': commands,
'cd': testChangeDetector(templateData.changeDetectorFactory)
};
humanizedTemplates.set(template.id, result);
visitAllCommands(new CommandHumanizer(commands, humanizedTemplates), templateData.commands);
return result;
}
class TestContext implements CompWithBindingsAndStyles, TreeComp, CompWithTemplateUrl,
CompWithEmbeddedTemplate {
someProp: string;
}
function testChangeDetector(changeDetectorFactory: Function): string[] {
var ctx = new TestContext();
ctx.someProp = 'someCtxValue';
var dir1 = new TestContext();
dir1.someProp = 'someDirValue';
var dispatcher = new TestDispatcher([dir1], []);
var cd = changeDetectorFactory(dispatcher);
var locals = new Locals(null, MapWrapper.createFromStringMap({'someVar': null}));
cd.hydrate(ctx, locals, dispatcher, new TestPipes());
cd.detectChanges();
return dispatcher.log;
}
class CommandHumanizer implements CommandVisitor {
constructor(private result: any[],
private humanizedTemplates: Map<number, StringMap<string, any>>) {}
visitText(cmd: TextCmd, context: any): any {
this.result.push(`#text(${cmd.value})`);
return null;
}
visitNgContent(cmd: NgContentCmd, context: any): any { return null; }
visitBeginElement(cmd: BeginElementCmd, context: any): any {
this.result.push(`<${cmd.name}>`);
return null;
}
visitEndElement(context: any): any {
this.result.push('</>');
return null;
}
visitBeginComponent(cmd: BeginComponentCmd, context: any): any {
this.result.push(`<${cmd.name}>`);
this.result.push(humanizeTemplate(cmd.template, this.humanizedTemplates));
return null;
}
visitEndComponent(context: any): any { return this.visitEndElement(context); }
visitEmbeddedTemplate(cmd: EmbeddedTemplateCmd, context: any): any {
this.result.push(`<template>`);
this.result.push({'cd': testChangeDetector(cmd.changeDetectorFactory)});
this.result.push(`</template>`);
return null;
}
}

View File

@ -0,0 +1,307 @@
import {
AsyncTestCompleter,
beforeEach,
ddescribe,
describe,
el,
expect,
iit,
inject,
it,
xit,
TestComponentBuilder,
beforeEachBindings
} from 'angular2/test_lib';
import {
CompileTypeMetadata,
CompileTemplateMetadata
} from 'angular2/src/compiler/directive_metadata';
import {ViewEncapsulation} from 'angular2/src/core/render/api';
import {TemplateNormalizer} from 'angular2/src/compiler/template_normalizer';
import {XHR} from 'angular2/src/core/render/xhr';
import {MockXHR} from 'angular2/src/core/render/xhr_mock';
import {TEST_BINDINGS} from './test_bindings';
export function main() {
describe('TemplateNormalizer', () => {
var dirType: CompileTypeMetadata;
beforeEachBindings(() => TEST_BINDINGS);
beforeEach(() => {
dirType = new CompileTypeMetadata({moduleUrl: 'package:some/module/a.js', name: 'SomeComp'});
});
describe('loadTemplate', () => {
describe('inline template', () => {
it('should store the template',
inject([AsyncTestCompleter, TemplateNormalizer],
(async, normalizer: TemplateNormalizer) => {
normalizer.normalizeTemplate(dirType, new CompileTemplateMetadata({
encapsulation: null,
template: 'a',
templateUrl: null,
styles: [],
styleUrls: ['test.css']
}))
.then((template: CompileTemplateMetadata) => {
expect(template.template).toEqual('a');
expect(template.templateUrl).toEqual('package:some/module/a.js');
async.done();
});
}));
it('should resolve styles on the annotation against the moduleUrl',
inject([AsyncTestCompleter, TemplateNormalizer],
(async, normalizer: TemplateNormalizer) => {
normalizer.normalizeTemplate(dirType, new CompileTemplateMetadata({
encapsulation: null,
template: '',
templateUrl: null,
styles: [],
styleUrls: ['test.css']
}))
.then((template: CompileTemplateMetadata) => {
expect(template.styleUrls).toEqual(['package:some/module/test.css']);
async.done();
});
}));
it('should resolve styles in the template against the moduleUrl',
inject([AsyncTestCompleter, TemplateNormalizer],
(async, normalizer: TemplateNormalizer) => {
normalizer.normalizeTemplate(dirType, new CompileTemplateMetadata({
encapsulation: null,
template: '<style>@import test.css</style>',
templateUrl: null,
styles: [],
styleUrls: []
}))
.then((template: CompileTemplateMetadata) => {
expect(template.styleUrls).toEqual(['package:some/module/test.css']);
async.done();
});
}));
});
describe('templateUrl', () => {
it('should load a template from a url that is resolved against moduleUrl',
inject([AsyncTestCompleter, TemplateNormalizer, XHR],
(async, normalizer: TemplateNormalizer, xhr: MockXHR) => {
xhr.expect('package:some/module/sometplurl.html', 'a');
normalizer.normalizeTemplate(dirType, new CompileTemplateMetadata({
encapsulation: null,
template: null,
templateUrl: 'sometplurl.html',
styles: [],
styleUrls: ['test.css']
}))
.then((template: CompileTemplateMetadata) => {
expect(template.template).toEqual('a');
expect(template.templateUrl)
.toEqual('package:some/module/sometplurl.html');
async.done();
});
xhr.flush();
}));
it('should resolve styles on the annotation against the moduleUrl',
inject([AsyncTestCompleter, TemplateNormalizer, XHR],
(async, normalizer: TemplateNormalizer, xhr: MockXHR) => {
xhr.expect('package:some/module/tpl/sometplurl.html', '');
normalizer.normalizeTemplate(dirType, new CompileTemplateMetadata({
encapsulation: null,
template: null,
templateUrl: 'tpl/sometplurl.html',
styles: [],
styleUrls: ['test.css']
}))
.then((template: CompileTemplateMetadata) => {
expect(template.styleUrls).toEqual(['package:some/module/test.css']);
async.done();
});
xhr.flush();
}));
it('should resolve styles in the template against the templateUrl',
inject([AsyncTestCompleter, TemplateNormalizer, XHR],
(async, normalizer: TemplateNormalizer, xhr: MockXHR) => {
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.html',
styles: [],
styleUrls: []
}))
.then((template: CompileTemplateMetadata) => {
expect(template.styleUrls).toEqual(['package:some/module/tpl/test.css']);
async.done();
});
xhr.flush();
}));
});
it('should throw if no template was specified',
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
expect(() => normalizer.normalizeTemplate(
dirType, new CompileTemplateMetadata(
{encapsulation: null, styles: [], styleUrls: []})))
.toThrowError('No template specified for component SomeComp');
}));
});
describe('normalizeLoadedTemplate', () => {
it('should store the viewEncapsulationin the result',
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
var viewEncapsulation = ViewEncapsulation.Native;
var template = normalizer.normalizeLoadedTemplate(
dirType, new CompileTemplateMetadata(
{encapsulation: viewEncapsulation, styles: [], styleUrls: []}),
'', 'package:some/module/');
expect(template.encapsulation).toBe(viewEncapsulation);
}));
it('should keep the template as html',
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
var template = normalizer.normalizeLoadedTemplate(
dirType,
new CompileTemplateMetadata({encapsulation: null, styles: [], styleUrls: []}), 'a',
'package:some/module/');
expect(template.template).toEqual('a')
}));
it('should collect ngContent',
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
var template = normalizer.normalizeLoadedTemplate(
dirType,
new CompileTemplateMetadata({encapsulation: null, styles: [], styleUrls: []}),
'<ng-content select="a"></ng-content>', 'package:some/module/');
expect(template.ngContentSelectors).toEqual(['a']);
}));
it('should normalize ngContent wildcard selector',
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
var template = normalizer.normalizeLoadedTemplate(
dirType,
new CompileTemplateMetadata({encapsulation: null, styles: [], styleUrls: []}),
'<ng-content></ng-content><ng-content select></ng-content><ng-content select="*"></ng-content>',
'package:some/module/');
expect(template.ngContentSelectors).toEqual(['*', '*', '*']);
}));
it('should collect top level styles in the template',
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
var template = normalizer.normalizeLoadedTemplate(
dirType,
new CompileTemplateMetadata({encapsulation: null, styles: [], styleUrls: []}),
'<style>a</style>', 'package:some/module/');
expect(template.styles).toEqual(['a']);
}));
it('should collect styles inside in elements',
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
var template = normalizer.normalizeLoadedTemplate(
dirType,
new CompileTemplateMetadata({encapsulation: null, styles: [], styleUrls: []}),
'<div><style>a</style></div>', 'package:some/module/');
expect(template.styles).toEqual(['a']);
}));
it('should collect styleUrls in the template',
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
var template = normalizer.normalizeLoadedTemplate(
dirType,
new CompileTemplateMetadata({encapsulation: null, styles: [], styleUrls: []}),
'<link rel="stylesheet" href="aUrl">', 'package:some/module/');
expect(template.styleUrls).toEqual(['package:some/module/aUrl']);
}));
it('should collect styleUrls in elements',
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
var template = normalizer.normalizeLoadedTemplate(
dirType,
new CompileTemplateMetadata({encapsulation: null, styles: [], styleUrls: []}),
'<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',
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
var template = normalizer.normalizeLoadedTemplate(
dirType,
new CompileTemplateMetadata({encapsulation: null, styles: [], styleUrls: []}),
'<link href="b" rel="a"></link>', 'package:some/module/');
expect(template.styleUrls).toEqual([]);
}));
it('should extract @import style urls into styleAbsUrl',
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
var template = normalizer.normalizeLoadedTemplate(
dirType, new CompileTemplateMetadata(
{encapsulation: null, styles: ['@import "test.css";'], styleUrls: []}),
'', 'package:some/module/id');
expect(template.styles).toEqual(['']);
expect(template.styleUrls).toEqual(['package:some/module/test.css']);
}));
it('should resolve relative urls in inline styles',
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
var template = normalizer.normalizeLoadedTemplate(
dirType, new CompileTemplateMetadata({
encapsulation: null,
styles: ['.foo{background-image: url(\'double.jpg\');'],
styleUrls: []
}),
'', 'package:some/module/id');
expect(template.styles)
.toEqual(['.foo{background-image: url(\'package:some/module/double.jpg\');']);
}));
it('should resolve relative style urls in styleUrls',
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
var template = normalizer.normalizeLoadedTemplate(
dirType, new CompileTemplateMetadata(
{encapsulation: null, styles: [], styleUrls: ['test.css']}),
'', 'package:some/module/id');
expect(template.styles).toEqual([]);
expect(template.styleUrls).toEqual(['package:some/module/test.css']);
}));
it('should normalize ViewEncapsulation.Emulated to ViewEncapsulation.None if there are no stlyes nor stylesheets',
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
var template = normalizer.normalizeLoadedTemplate(
dirType, new CompileTemplateMetadata(
{encapsulation: ViewEncapsulation.Emulated, styles: [], styleUrls: []}),
'', 'package:some/module/id');
expect(template.encapsulation).toEqual(ViewEncapsulation.None);
}));
it('should ignore ng-content in elements with ng-non-bindable',
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
var template = normalizer.normalizeLoadedTemplate(
dirType,
new CompileTemplateMetadata({encapsulation: null, styles: [], styleUrls: []}),
'<div ng-non-bindable><ng-content select="a"></ng-content></div>',
'package:some/module/');
expect(template.ngContentSelectors).toEqual([]);
}));
it('should still collect <style> in elements with ng-non-bindable',
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
var template = normalizer.normalizeLoadedTemplate(
dirType,
new CompileTemplateMetadata({encapsulation: null, styles: [], styleUrls: []}),
'<div ng-non-bindable><style>div {color:red}</style></div>', 'package:some/module/');
expect(template.styles).toEqual(['div {color:red}']);
}));
});
});
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,58 @@
import {
ddescribe,
describe,
xdescribe,
it,
iit,
xit,
expect,
beforeEach,
afterEach,
AsyncTestCompleter,
inject,
beforeEachBindings
} from 'angular2/test_lib';
import {HtmlParser} from 'angular2/src/compiler/html_parser';
import {
preparseElement,
PreparsedElementType,
PreparsedElement
} from 'angular2/src/compiler/template_preparser';
export function main() {
describe('preparseElement', () => {
var htmlParser;
beforeEach(inject([HtmlParser], (_htmlParser: HtmlParser) => { htmlParser = _htmlParser; }));
function preparse(html: string): PreparsedElement {
return preparseElement(htmlParser.parse(html, '')[0]);
}
it('should detect script elements', inject([HtmlParser], (htmlParser: HtmlParser) => {
expect(preparse('<script>').type).toBe(PreparsedElementType.SCRIPT);
}));
it('should detect style elements', inject([HtmlParser], (htmlParser: HtmlParser) => {
expect(preparse('<style>').type).toBe(PreparsedElementType.STYLE);
}));
it('should detect stylesheet elements', inject([HtmlParser], (htmlParser: HtmlParser) => {
expect(preparse('<link rel="stylesheet">').type).toBe(PreparsedElementType.STYLESHEET);
expect(preparse('<link rel="stylesheet" href="someUrl">').hrefAttr).toEqual('someUrl');
expect(preparse('<link rel="someRel">').type).toBe(PreparsedElementType.OTHER);
}));
it('should detect ng-content elements', inject([HtmlParser], (htmlParser: HtmlParser) => {
expect(preparse('<ng-content>').type).toBe(PreparsedElementType.NG_CONTENT);
}));
it('should normalize ng-content.select attribute',
inject([HtmlParser], (htmlParser: HtmlParser) => {
expect(preparse('<ng-content>').selectAttr).toEqual('*');
expect(preparse('<ng-content select>').selectAttr).toEqual('*');
expect(preparse('<ng-content select="*">').selectAttr).toEqual('*');
}));
});
}

View File

@ -0,0 +1,5 @@
import {bind, Binding} from 'angular2/src/core/di';
import {MockSchemaRegistry} from './schema_registry_mock';
import {ElementSchemaRegistry} from 'angular2/src/core/render/dom/schema/element_schema_registry';
export var TEST_BINDINGS = [bind(ElementSchemaRegistry).toValue(new MockSchemaRegistry({}, {}))];

View File

@ -0,0 +1,55 @@
import {
AsyncTestCompleter,
beforeEach,
ddescribe,
describe,
el,
expect,
iit,
inject,
it,
xit,
TestComponentBuilder
} from 'angular2/test_lib';
import {IS_DART} from '../platform';
import {escapeSingleQuoteString, escapeDoubleQuoteString} from 'angular2/src/compiler/util';
export function main() {
describe('util', () => {
describe('escapeSingleQuoteString', () => {
it('should escape single quotes',
() => { expect(escapeSingleQuoteString(`'`)).toEqual(`'\\''`); });
it('should escape backslash',
() => { expect(escapeSingleQuoteString('\\')).toEqual(`'\\\\'`); });
it('should escape newlines',
() => { expect(escapeSingleQuoteString('\n')).toEqual(`'\\n'`); });
if (IS_DART) {
it('should escape $', () => { expect(escapeSingleQuoteString('$')).toEqual(`'\\$'`); });
} else {
it('should not escape $', () => { expect(escapeSingleQuoteString('$')).toEqual(`'$'`); });
}
});
describe('escapeDoubleQuoteString', () => {
it('should escape double quotes',
() => { expect(escapeDoubleQuoteString(`"`)).toEqual(`"\\""`); });
it('should escape backslash',
() => { expect(escapeDoubleQuoteString('\\')).toEqual(`"\\\\"`); });
it('should escape newlines',
() => { expect(escapeDoubleQuoteString('\n')).toEqual(`"\\n"`); });
if (IS_DART) {
it('should escape $', () => { expect(escapeDoubleQuoteString('$')).toEqual(`"\\$"`); });
} else {
it('should not escape $', () => { expect(escapeDoubleQuoteString('$')).toEqual(`"$"`); });
}
});
});
}