chore(core): move compiler out of core
BREAKING CHANGE All imports from 'angular2/core/compiler' should be changed to 'angular2/compiler'.
This commit is contained in:
210
modules/angular2/test/compiler/change_definition_factory_spec.ts
Normal file
210
modules/angular2/test/compiler/change_definition_factory_spec.ts
Normal file
@ -0,0 +1,210 @@
|
||||
import {
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
describe,
|
||||
el,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
it,
|
||||
xit,
|
||||
TestComponentBuilder,
|
||||
beforeEachBindings
|
||||
} from 'angular2/testing_internal';
|
||||
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_PROVIDERS} from './test_bindings';
|
||||
|
||||
export function main() {
|
||||
describe('ChangeDefinitionFactory', () => {
|
||||
beforeEachBindings(() => TEST_PROVIDERS);
|
||||
|
||||
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 on regular elements', () => {
|
||||
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 on template elements', () => {
|
||||
var dirMeta = CompileDirectiveMetadata.create({
|
||||
type: new CompileTypeMetadata({name: 'SomeDir'}),
|
||||
selector: 'template',
|
||||
outputs: ['click']
|
||||
});
|
||||
var changeDetector =
|
||||
createChangeDetector('<template on-click="onEvent($event)">', [dirMeta], 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); }
|
||||
}
|
154
modules/angular2/test/compiler/change_detector_compiler_spec.ts
Normal file
154
modules/angular2/test/compiler/change_detector_compiler_spec.ts
Normal file
@ -0,0 +1,154 @@
|
||||
import {
|
||||
ddescribe,
|
||||
describe,
|
||||
xdescribe,
|
||||
it,
|
||||
iit,
|
||||
xit,
|
||||
expect,
|
||||
beforeEach,
|
||||
afterEach,
|
||||
AsyncTestCompleter,
|
||||
inject,
|
||||
beforeEachBindings
|
||||
} from 'angular2/testing_internal';
|
||||
import {provide} from 'angular2/src/core/di';
|
||||
|
||||
import {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_PROVIDERS} 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_PROVIDERS);
|
||||
|
||||
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(() => [
|
||||
provide(ChangeDetectorGenConfig,
|
||||
{useValue: 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(() => [
|
||||
provide(ChangeDetectorGenConfig,
|
||||
{useValue: 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;
|
||||
}
|
44
modules/angular2/test/compiler/change_detector_mocks.ts
Normal file
44
modules/angular2/test/compiler/change_detector_mocks.ts
Normal 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; }
|
||||
}
|
602
modules/angular2/test/compiler/command_compiler_spec.ts
Normal file
602
modules/angular2/test/compiler/command_compiler_spec.ts
Normal file
@ -0,0 +1,602 @@
|
||||
import {
|
||||
ddescribe,
|
||||
describe,
|
||||
xdescribe,
|
||||
it,
|
||||
iit,
|
||||
xit,
|
||||
expect,
|
||||
beforeEach,
|
||||
afterEach,
|
||||
AsyncTestCompleter,
|
||||
inject,
|
||||
beforeEachBindings
|
||||
} from 'angular2/testing_internal';
|
||||
|
||||
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/metadata/view';
|
||||
import {evalModule} from './eval_module';
|
||||
import {
|
||||
escapeSingleQuoteString,
|
||||
codeGenValueFn,
|
||||
codeGenExportVariable,
|
||||
MODULE_SUFFIX
|
||||
} from 'angular2/src/compiler/util';
|
||||
import {TEST_PROVIDERS} 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_PROVIDERS);
|
||||
|
||||
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 merge element attributes with host attributes',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
var rootComp = createComp({
|
||||
type: RootCompTypeMeta,
|
||||
template: '<div class="origclass" style="color: red;" role="origrole" attr1>'
|
||||
});
|
||||
var dir = CompileDirectiveMetadata.create({
|
||||
selector: 'div',
|
||||
isComponent: false,
|
||||
type: SomeDirTypeMeta,
|
||||
host: {'class': 'newclass', 'style': 'newstyle', 'role': 'newrole', 'attr2': ''}
|
||||
});
|
||||
run(rootComp, [dir])
|
||||
.then((data) => {
|
||||
expect(data).toEqual([
|
||||
[
|
||||
BEGIN_ELEMENT,
|
||||
'div',
|
||||
[
|
||||
'attr1',
|
||||
'',
|
||||
'attr2',
|
||||
'',
|
||||
'class',
|
||||
'origclass newclass',
|
||||
'role',
|
||||
'newrole',
|
||||
'style',
|
||||
'color: red; newstyle'
|
||||
],
|
||||
[],
|
||||
[],
|
||||
['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);
|
||||
}
|
96
modules/angular2/test/compiler/directive_metadata_spec.ts
Normal file
96
modules/angular2/test/compiler/directive_metadata_spec.ts
Normal file
@ -0,0 +1,96 @@
|
||||
import {
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
describe,
|
||||
el,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
it,
|
||||
xit,
|
||||
TestComponentBuilder
|
||||
} from 'angular2/testing_internal';
|
||||
|
||||
import {
|
||||
CompileDirectiveMetadata,
|
||||
CompileTypeMetadata,
|
||||
CompileTemplateMetadata
|
||||
} from 'angular2/src/compiler/directive_metadata';
|
||||
import {ViewEncapsulation} from 'angular2/src/core/metadata/view';
|
||||
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 use ViewEncapsulation.Emulated by default', () => {
|
||||
expect(new CompileTemplateMetadata({encapsulation: null}).encapsulation)
|
||||
.toBe(ViewEncapsulation.Emulated);
|
||||
});
|
||||
|
||||
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);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
55
modules/angular2/test/compiler/eval_module.dart
Normal file
55
modules/angular2/test/compiler/eval_module.dart
Normal file
@ -0,0 +1,55 @@
|
||||
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;
|
||||
});
|
||||
}
|
44
modules/angular2/test/compiler/eval_module.ts
Normal file
44
modules/angular2/test/compiler/eval_module.ts
Normal 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));
|
||||
}
|
||||
}
|
50
modules/angular2/test/compiler/eval_module_spec.ts
Normal file
50
modules/angular2/test/compiler/eval_module_spec.ts
Normal file
@ -0,0 +1,50 @@
|
||||
import {
|
||||
ddescribe,
|
||||
describe,
|
||||
xdescribe,
|
||||
it,
|
||||
iit,
|
||||
xit,
|
||||
expect,
|
||||
beforeEach,
|
||||
afterEach,
|
||||
AsyncTestCompleter,
|
||||
inject
|
||||
} from 'angular2/testing_internal';
|
||||
import {IS_DART} from 'angular2/src/core/facade/lang';
|
||||
|
||||
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;
|
||||
}
|
||||
`;
|
148
modules/angular2/test/compiler/html_parser_spec.ts
Normal file
148
modules/angular2/test/compiler/html_parser_spec.ts
Normal file
@ -0,0 +1,148 @@
|
||||
import {
|
||||
ddescribe,
|
||||
describe,
|
||||
it,
|
||||
iit,
|
||||
xit,
|
||||
expect,
|
||||
beforeEach,
|
||||
afterEach
|
||||
} from 'angular2/testing_internal';
|
||||
|
||||
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 and lower case attributes on regular elements', () => {
|
||||
expect(humanizeDom(parser.parse('<div FoO="bar"></div>', 'TestComp')))
|
||||
.toEqual([
|
||||
[HtmlElementAst, 'div', 'TestComp > div:nth-child(0)'],
|
||||
[HtmlAttrAst, 'foo', 'bar', 'TestComp > div:nth-child(0)[foo=bar]']
|
||||
]);
|
||||
});
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
92
modules/angular2/test/compiler/runtime_compiler_spec.ts
Normal file
92
modules/angular2/test/compiler/runtime_compiler_spec.ts
Normal file
@ -0,0 +1,92 @@
|
||||
import {
|
||||
ddescribe,
|
||||
describe,
|
||||
xdescribe,
|
||||
it,
|
||||
iit,
|
||||
xit,
|
||||
expect,
|
||||
beforeEach,
|
||||
afterEach,
|
||||
AsyncTestCompleter,
|
||||
inject,
|
||||
beforeEachBindings
|
||||
} from 'angular2/testing_internal';
|
||||
|
||||
import {Component, View, provide} 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 [provide(ProtoViewFactory, {useValue: 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 {
|
||||
}
|
147
modules/angular2/test/compiler/runtime_metadata_spec.ts
Normal file
147
modules/angular2/test/compiler/runtime_metadata_spec.ts
Normal file
@ -0,0 +1,147 @@
|
||||
import {
|
||||
ddescribe,
|
||||
describe,
|
||||
xdescribe,
|
||||
it,
|
||||
iit,
|
||||
xit,
|
||||
expect,
|
||||
beforeEach,
|
||||
afterEach,
|
||||
AsyncTestCompleter,
|
||||
inject,
|
||||
beforeEachProviders
|
||||
} from 'angular2/testing_internal';
|
||||
|
||||
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,
|
||||
provide
|
||||
} from 'angular2/core';
|
||||
|
||||
import {TEST_PROVIDERS} from './test_bindings';
|
||||
import {MODULE_SUFFIX} from 'angular2/src/compiler/util';
|
||||
import {IS_DART} from 'angular2/src/core/facade/lang';
|
||||
import {AMBIENT_DIRECTIVES} from 'angular2/src/core/ambient';
|
||||
|
||||
export function main() {
|
||||
describe('RuntimeMetadataResolver', () => {
|
||||
beforeEachProviders(() => TEST_PROVIDERS);
|
||||
|
||||
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)]);
|
||||
}));
|
||||
|
||||
describe("ambient directives", () => {
|
||||
beforeEachProviders(() => [provide(AMBIENT_DIRECTIVES, {useValue: [ADirective]})]);
|
||||
|
||||
it('should include ambient directives when available',
|
||||
inject([RuntimeMetadataResolver], (resolver: RuntimeMetadataResolver) => {
|
||||
expect(resolver.getViewDirectivesMetadata(ComponentWithEverything))
|
||||
.toEqual([
|
||||
resolver.getMetadata(ADirective),
|
||||
resolver.getMetadata(DirectiveWithoutModuleId)
|
||||
]);
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Directive({selector: 'a-directive'})
|
||||
class ADirective {
|
||||
}
|
||||
|
||||
@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: {[key: string]: SimpleChange}): void {}
|
||||
onInit(): void {}
|
||||
doCheck(): void {}
|
||||
onDestroy(): void {}
|
||||
afterContentInit(): void {}
|
||||
afterContentChecked(): void {}
|
||||
afterViewInit(): void {}
|
||||
afterViewChecked(): void {}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
import {
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
xdescribe,
|
||||
describe,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
it,
|
||||
xit
|
||||
} from 'angular2/testing_internal';
|
||||
import {IS_DART} from 'angular2/src/core/facade/lang';
|
||||
|
||||
import {DomElementSchemaRegistry} from 'angular2/src/compiler/schema/dom_element_schema_registry';
|
||||
|
||||
export function main() {
|
||||
// DOMElementSchema can only be used on the JS side where we can safely
|
||||
// use reflection for DOM elements
|
||||
if (IS_DART) return;
|
||||
|
||||
var registry: DomElementSchemaRegistry;
|
||||
|
||||
beforeEach(() => { registry = new DomElementSchemaRegistry(); });
|
||||
|
||||
describe('DOMElementSchema', () => {
|
||||
|
||||
it('should detect properties on regular elements', () => {
|
||||
expect(registry.hasProperty('div', 'id')).toBeTruthy();
|
||||
expect(registry.hasProperty('div', 'title')).toBeTruthy();
|
||||
expect(registry.hasProperty('div', 'unknown')).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should return true for custom-like elements',
|
||||
() => { expect(registry.hasProperty('custom-like', 'unknown')).toBeTruthy(); });
|
||||
|
||||
it('should not re-map property names that are not specified in DOM facade',
|
||||
() => { expect(registry.getMappedPropName('readonly')).toEqual('readOnly'); });
|
||||
|
||||
it('should not re-map property names that are not specified in DOM facade', () => {
|
||||
expect(registry.getMappedPropName('title')).toEqual('title');
|
||||
expect(registry.getMappedPropName('exotic-unknown')).toEqual('exotic-unknown');
|
||||
});
|
||||
});
|
||||
}
|
16
modules/angular2/test/compiler/schema_registry_mock.ts
Normal file
16
modules/angular2/test/compiler/schema_registry_mock.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import {ElementSchemaRegistry} from 'angular2/src/compiler/schema/element_schema_registry';
|
||||
import {isPresent} from 'angular2/src/core/facade/lang';
|
||||
|
||||
export class MockSchemaRegistry implements ElementSchemaRegistry {
|
||||
constructor(public existingProperties: {[key: string]: boolean},
|
||||
public attrPropMapping: {[key: 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;
|
||||
}
|
||||
}
|
361
modules/angular2/test/compiler/selector_spec.ts
Normal file
361
modules/angular2/test/compiler/selector_spec.ts
Normal file
@ -0,0 +1,361 @@
|
||||
import {describe, it, expect, beforeEach, ddescribe, iit, xit, el} from 'angular2/testing_internal';
|
||||
import {DOM} from 'angular2/src/core/dom/dom_adapter';
|
||||
import {SelectorMatcher} from 'angular2/src/compiler/selector';
|
||||
import {CssSelector} from 'angular2/src/compiler/selector';
|
||||
import {ListWrapper, MapWrapper} from 'angular2/src/core/facade/collection';
|
||||
|
||||
export function main() {
|
||||
describe('SelectorMatcher', () => {
|
||||
var matcher, selectableCollector, s1, s2, s3, s4;
|
||||
var matched: any[];
|
||||
|
||||
function reset() { matched = []; }
|
||||
|
||||
beforeEach(() => {
|
||||
reset();
|
||||
s1 = s2 = s3 = s4 = null;
|
||||
selectableCollector = (selector, context) => {
|
||||
matched.push(selector);
|
||||
matched.push(context);
|
||||
};
|
||||
matcher = new SelectorMatcher();
|
||||
});
|
||||
|
||||
it('should select by element name case insensitive', () => {
|
||||
matcher.addSelectables(s1 = CssSelector.parse('someTag'), 1);
|
||||
|
||||
expect(matcher.match(CssSelector.parse('SOMEOTHERTAG')[0], selectableCollector))
|
||||
.toEqual(false);
|
||||
expect(matched).toEqual([]);
|
||||
|
||||
expect(matcher.match(CssSelector.parse('SOMETAG')[0], selectableCollector)).toEqual(true);
|
||||
expect(matched).toEqual([s1[0], 1]);
|
||||
});
|
||||
|
||||
it('should select by class name case insensitive', () => {
|
||||
matcher.addSelectables(s1 = CssSelector.parse('.someClass'), 1);
|
||||
matcher.addSelectables(s2 = CssSelector.parse('.someClass.class2'), 2);
|
||||
|
||||
expect(matcher.match(CssSelector.parse('.SOMEOTHERCLASS')[0], selectableCollector))
|
||||
.toEqual(false);
|
||||
expect(matched).toEqual([]);
|
||||
|
||||
expect(matcher.match(CssSelector.parse('.SOMECLASS')[0], selectableCollector)).toEqual(true);
|
||||
expect(matched).toEqual([s1[0], 1]);
|
||||
|
||||
reset();
|
||||
expect(matcher.match(CssSelector.parse('.someClass.class2')[0], selectableCollector))
|
||||
.toEqual(true);
|
||||
expect(matched).toEqual([s1[0], 1, s2[0], 2]);
|
||||
});
|
||||
|
||||
it('should select by attr name case insensitive independent of the value', () => {
|
||||
matcher.addSelectables(s1 = CssSelector.parse('[someAttr]'), 1);
|
||||
matcher.addSelectables(s2 = CssSelector.parse('[someAttr][someAttr2]'), 2);
|
||||
|
||||
expect(matcher.match(CssSelector.parse('[SOMEOTHERATTR]')[0], selectableCollector))
|
||||
.toEqual(false);
|
||||
expect(matched).toEqual([]);
|
||||
|
||||
expect(matcher.match(CssSelector.parse('[SOMEATTR]')[0], selectableCollector)).toEqual(true);
|
||||
expect(matched).toEqual([s1[0], 1]);
|
||||
|
||||
reset();
|
||||
expect(matcher.match(CssSelector.parse('[SOMEATTR=someValue]')[0], selectableCollector))
|
||||
.toEqual(true);
|
||||
expect(matched).toEqual([s1[0], 1]);
|
||||
|
||||
reset();
|
||||
expect(matcher.match(CssSelector.parse('[someAttr][someAttr2]')[0], selectableCollector))
|
||||
.toEqual(true);
|
||||
expect(matched).toEqual([s1[0], 1, s2[0], 2]);
|
||||
|
||||
reset();
|
||||
expect(matcher.match(CssSelector.parse('[someAttr=someValue][someAttr2]')[0],
|
||||
selectableCollector))
|
||||
.toEqual(true);
|
||||
expect(matched).toEqual([s1[0], 1, s2[0], 2]);
|
||||
|
||||
reset();
|
||||
expect(matcher.match(CssSelector.parse('[someAttr2][someAttr=someValue]')[0],
|
||||
selectableCollector))
|
||||
.toEqual(true);
|
||||
expect(matched).toEqual([s1[0], 1, s2[0], 2]);
|
||||
|
||||
reset();
|
||||
expect(matcher.match(CssSelector.parse('[someAttr2=someValue][someAttr]')[0],
|
||||
selectableCollector))
|
||||
.toEqual(true);
|
||||
expect(matched).toEqual([s1[0], 1, s2[0], 2]);
|
||||
});
|
||||
|
||||
it('should select by attr name only once if the value is from the DOM', () => {
|
||||
matcher.addSelectables(s1 = CssSelector.parse('[some-decor]'), 1);
|
||||
|
||||
var elementSelector = new CssSelector();
|
||||
var element = el('<div attr></div>');
|
||||
var empty = DOM.getAttribute(element, 'attr');
|
||||
elementSelector.addAttribute('some-decor', empty);
|
||||
matcher.match(elementSelector, selectableCollector);
|
||||
expect(matched).toEqual([s1[0], 1]);
|
||||
});
|
||||
|
||||
it('should select by attr name and value case insensitive', () => {
|
||||
matcher.addSelectables(s1 = CssSelector.parse('[someAttr=someValue]'), 1);
|
||||
|
||||
expect(matcher.match(CssSelector.parse('[SOMEATTR=SOMEOTHERATTR]')[0], selectableCollector))
|
||||
.toEqual(false);
|
||||
expect(matched).toEqual([]);
|
||||
|
||||
expect(matcher.match(CssSelector.parse('[SOMEATTR=SOMEVALUE]')[0], selectableCollector))
|
||||
.toEqual(true);
|
||||
expect(matched).toEqual([s1[0], 1]);
|
||||
});
|
||||
|
||||
it('should select by element name, class name and attribute name with value', () => {
|
||||
matcher.addSelectables(s1 = CssSelector.parse('someTag.someClass[someAttr=someValue]'), 1);
|
||||
|
||||
expect(matcher.match(CssSelector.parse('someOtherTag.someOtherClass[someOtherAttr]')[0],
|
||||
selectableCollector))
|
||||
.toEqual(false);
|
||||
expect(matched).toEqual([]);
|
||||
|
||||
expect(matcher.match(CssSelector.parse('someTag.someOtherClass[someOtherAttr]')[0],
|
||||
selectableCollector))
|
||||
.toEqual(false);
|
||||
expect(matched).toEqual([]);
|
||||
|
||||
expect(matcher.match(CssSelector.parse('someTag.someClass[someOtherAttr]')[0],
|
||||
selectableCollector))
|
||||
.toEqual(false);
|
||||
expect(matched).toEqual([]);
|
||||
|
||||
expect(
|
||||
matcher.match(CssSelector.parse('someTag.someClass[someAttr]')[0], selectableCollector))
|
||||
.toEqual(false);
|
||||
expect(matched).toEqual([]);
|
||||
|
||||
expect(matcher.match(CssSelector.parse('someTag.someClass[someAttr=someValue]')[0],
|
||||
selectableCollector))
|
||||
.toEqual(true);
|
||||
expect(matched).toEqual([s1[0], 1]);
|
||||
});
|
||||
|
||||
it('should select by many attributes and independent of the value', () => {
|
||||
matcher.addSelectables(s1 = CssSelector.parse('input[type=text][control]'), 1);
|
||||
|
||||
var cssSelector = new CssSelector();
|
||||
cssSelector.setElement('input');
|
||||
cssSelector.addAttribute('type', 'text');
|
||||
cssSelector.addAttribute('control', 'one');
|
||||
|
||||
expect(matcher.match(cssSelector, selectableCollector)).toEqual(true);
|
||||
expect(matched).toEqual([s1[0], 1]);
|
||||
});
|
||||
|
||||
it('should select independent of the order in the css selector', () => {
|
||||
matcher.addSelectables(s1 = CssSelector.parse('[someAttr].someClass'), 1);
|
||||
matcher.addSelectables(s2 = CssSelector.parse('.someClass[someAttr]'), 2);
|
||||
matcher.addSelectables(s3 = CssSelector.parse('.class1.class2'), 3);
|
||||
matcher.addSelectables(s4 = CssSelector.parse('.class2.class1'), 4);
|
||||
|
||||
expect(matcher.match(CssSelector.parse('[someAttr].someClass')[0], selectableCollector))
|
||||
.toEqual(true);
|
||||
expect(matched).toEqual([s1[0], 1, s2[0], 2]);
|
||||
|
||||
reset();
|
||||
expect(matcher.match(CssSelector.parse('.someClass[someAttr]')[0], selectableCollector))
|
||||
.toEqual(true);
|
||||
expect(matched).toEqual([s1[0], 1, s2[0], 2]);
|
||||
|
||||
reset();
|
||||
expect(matcher.match(CssSelector.parse('.class1.class2')[0], selectableCollector))
|
||||
.toEqual(true);
|
||||
expect(matched).toEqual([s3[0], 3, s4[0], 4]);
|
||||
|
||||
reset();
|
||||
expect(matcher.match(CssSelector.parse('.class2.class1')[0], selectableCollector))
|
||||
.toEqual(true);
|
||||
expect(matched).toEqual([s4[0], 4, s3[0], 3]);
|
||||
});
|
||||
|
||||
it('should not select with a matching :not selector', () => {
|
||||
matcher.addSelectables(CssSelector.parse('p:not(.someClass)'), 1);
|
||||
matcher.addSelectables(CssSelector.parse('p:not([someAttr])'), 2);
|
||||
matcher.addSelectables(CssSelector.parse(':not(.someClass)'), 3);
|
||||
matcher.addSelectables(CssSelector.parse(':not(p)'), 4);
|
||||
matcher.addSelectables(CssSelector.parse(':not(p[someAttr])'), 5);
|
||||
|
||||
expect(matcher.match(CssSelector.parse('p.someClass[someAttr]')[0], selectableCollector))
|
||||
.toEqual(false);
|
||||
expect(matched).toEqual([]);
|
||||
});
|
||||
|
||||
it('should select with a non matching :not selector', () => {
|
||||
matcher.addSelectables(s1 = CssSelector.parse('p:not(.someClass)'), 1);
|
||||
matcher.addSelectables(s2 = CssSelector.parse('p:not(.someOtherClass[someAttr])'), 2);
|
||||
matcher.addSelectables(s3 = CssSelector.parse(':not(.someClass)'), 3);
|
||||
matcher.addSelectables(s4 = CssSelector.parse(':not(.someOtherClass[someAttr])'), 4);
|
||||
|
||||
expect(matcher.match(CssSelector.parse('p[someOtherAttr].someOtherClass')[0],
|
||||
selectableCollector))
|
||||
.toEqual(true);
|
||||
expect(matched).toEqual([s1[0], 1, s2[0], 2, s3[0], 3, s4[0], 4]);
|
||||
});
|
||||
|
||||
it('should match with multiple :not selectors', () => {
|
||||
matcher.addSelectables(s1 = CssSelector.parse('div:not([a]):not([b])'), 1);
|
||||
expect(matcher.match(CssSelector.parse('div[a]')[0], selectableCollector)).toBe(false);
|
||||
expect(matcher.match(CssSelector.parse('div[b]')[0], selectableCollector)).toBe(false);
|
||||
expect(matcher.match(CssSelector.parse('div[c]')[0], selectableCollector)).toBe(true);
|
||||
});
|
||||
|
||||
it('should select with one match in a list', () => {
|
||||
matcher.addSelectables(s1 = CssSelector.parse('input[type=text], textbox'), 1);
|
||||
|
||||
expect(matcher.match(CssSelector.parse('textbox')[0], selectableCollector)).toEqual(true);
|
||||
expect(matched).toEqual([s1[1], 1]);
|
||||
|
||||
reset();
|
||||
expect(matcher.match(CssSelector.parse('input[type=text]')[0], selectableCollector))
|
||||
.toEqual(true);
|
||||
expect(matched).toEqual([s1[0], 1]);
|
||||
});
|
||||
|
||||
it('should not select twice with two matches in a list', () => {
|
||||
matcher.addSelectables(s1 = CssSelector.parse('input, .someClass'), 1);
|
||||
|
||||
expect(matcher.match(CssSelector.parse('input.someclass')[0], selectableCollector))
|
||||
.toEqual(true);
|
||||
expect(matched.length).toEqual(2);
|
||||
expect(matched).toEqual([s1[0], 1]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('CssSelector.parse', () => {
|
||||
it('should detect element names', () => {
|
||||
var cssSelector = CssSelector.parse('sometag')[0];
|
||||
expect(cssSelector.element).toEqual('sometag');
|
||||
expect(cssSelector.toString()).toEqual('sometag');
|
||||
});
|
||||
|
||||
it('should detect class names', () => {
|
||||
var cssSelector = CssSelector.parse('.someClass')[0];
|
||||
expect(cssSelector.classNames).toEqual(['someclass']);
|
||||
|
||||
expect(cssSelector.toString()).toEqual('.someclass');
|
||||
});
|
||||
|
||||
it('should detect attr names', () => {
|
||||
var cssSelector = CssSelector.parse('[attrname]')[0];
|
||||
expect(cssSelector.attrs).toEqual(['attrname', '']);
|
||||
|
||||
expect(cssSelector.toString()).toEqual('[attrname]');
|
||||
});
|
||||
|
||||
it('should detect attr values', () => {
|
||||
var cssSelector = CssSelector.parse('[attrname=attrvalue]')[0];
|
||||
expect(cssSelector.attrs).toEqual(['attrname', 'attrvalue']);
|
||||
expect(cssSelector.toString()).toEqual('[attrname=attrvalue]');
|
||||
});
|
||||
|
||||
it('should detect multiple parts', () => {
|
||||
var cssSelector = CssSelector.parse('sometag[attrname=attrvalue].someclass')[0];
|
||||
expect(cssSelector.element).toEqual('sometag');
|
||||
expect(cssSelector.attrs).toEqual(['attrname', 'attrvalue']);
|
||||
expect(cssSelector.classNames).toEqual(['someclass']);
|
||||
|
||||
expect(cssSelector.toString()).toEqual('sometag.someclass[attrname=attrvalue]');
|
||||
});
|
||||
|
||||
it('should detect multiple attributes', () => {
|
||||
var cssSelector = CssSelector.parse('input[type=text][control]')[0];
|
||||
expect(cssSelector.element).toEqual('input');
|
||||
expect(cssSelector.attrs).toEqual(['type', 'text', 'control', '']);
|
||||
|
||||
expect(cssSelector.toString()).toEqual('input[type=text][control]');
|
||||
});
|
||||
|
||||
it('should detect :not', () => {
|
||||
var cssSelector = CssSelector.parse('sometag:not([attrname=attrvalue].someclass)')[0];
|
||||
expect(cssSelector.element).toEqual('sometag');
|
||||
expect(cssSelector.attrs.length).toEqual(0);
|
||||
expect(cssSelector.classNames.length).toEqual(0);
|
||||
|
||||
var notSelector = cssSelector.notSelectors[0];
|
||||
expect(notSelector.element).toEqual(null);
|
||||
expect(notSelector.attrs).toEqual(['attrname', 'attrvalue']);
|
||||
expect(notSelector.classNames).toEqual(['someclass']);
|
||||
|
||||
expect(cssSelector.toString()).toEqual('sometag:not(.someclass[attrname=attrvalue])');
|
||||
});
|
||||
|
||||
it('should detect :not without truthy', () => {
|
||||
var cssSelector = CssSelector.parse(':not([attrname=attrvalue].someclass)')[0];
|
||||
expect(cssSelector.element).toEqual("*");
|
||||
|
||||
var notSelector = cssSelector.notSelectors[0];
|
||||
expect(notSelector.attrs).toEqual(['attrname', 'attrvalue']);
|
||||
expect(notSelector.classNames).toEqual(['someclass']);
|
||||
|
||||
expect(cssSelector.toString()).toEqual('*:not(.someclass[attrname=attrvalue])');
|
||||
});
|
||||
|
||||
it('should throw when nested :not', () => {
|
||||
expect(() => { CssSelector.parse('sometag:not(:not([attrname=attrvalue].someclass))')[0]; })
|
||||
.toThrowError('Nesting :not is not allowed in a selector');
|
||||
});
|
||||
|
||||
it('should throw when multiple selectors in :not', () => {
|
||||
expect(() => { CssSelector.parse('sometag:not(a,b)'); })
|
||||
.toThrowError('Multiple selectors in :not are not supported');
|
||||
});
|
||||
|
||||
it('should detect lists of selectors', () => {
|
||||
var cssSelectors = CssSelector.parse('.someclass,[attrname=attrvalue], sometag');
|
||||
expect(cssSelectors.length).toEqual(3);
|
||||
|
||||
expect(cssSelectors[0].classNames).toEqual(['someclass']);
|
||||
expect(cssSelectors[1].attrs).toEqual(['attrname', 'attrvalue']);
|
||||
expect(cssSelectors[2].element).toEqual('sometag');
|
||||
});
|
||||
|
||||
it('should detect lists of selectors with :not', () => {
|
||||
var cssSelectors =
|
||||
CssSelector.parse('input[type=text], :not(textarea), textbox:not(.special)');
|
||||
expect(cssSelectors.length).toEqual(3);
|
||||
|
||||
expect(cssSelectors[0].element).toEqual('input');
|
||||
expect(cssSelectors[0].attrs).toEqual(['type', 'text']);
|
||||
|
||||
expect(cssSelectors[1].element).toEqual('*');
|
||||
expect(cssSelectors[1].notSelectors[0].element).toEqual('textarea');
|
||||
|
||||
expect(cssSelectors[2].element).toEqual('textbox');
|
||||
expect(cssSelectors[2].notSelectors[0].classNames).toEqual(['special']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('CssSelector.getMatchingElementTemplate', () => {
|
||||
it('should create an element with a tagName, classes, and attributes', () => {
|
||||
let selector = CssSelector.parse('blink.neon.hotpink[sweet][dismissable=false]')[0];
|
||||
let template = selector.getMatchingElementTemplate();
|
||||
|
||||
expect(template).toEqual('<blink class="neon hotpink" sweet dismissable="false"></blink>');
|
||||
});
|
||||
|
||||
it('should create an element without a tag name', () => {
|
||||
let selector = CssSelector.parse('[fancy]')[0];
|
||||
let template = selector.getMatchingElementTemplate();
|
||||
|
||||
expect(template).toEqual('<div fancy></div>');
|
||||
});
|
||||
|
||||
it('should ignore :not selectors', () => {
|
||||
let selector = CssSelector.parse('grape:not(.red)')[0];
|
||||
let template = selector.getMatchingElementTemplate();
|
||||
|
||||
expect(template).toEqual('<grape></grape>');
|
||||
});
|
||||
});
|
||||
}
|
226
modules/angular2/test/compiler/shadow_css_spec.ts
Normal file
226
modules/angular2/test/compiler/shadow_css_spec.ts
Normal file
@ -0,0 +1,226 @@
|
||||
import {
|
||||
describe,
|
||||
beforeEach,
|
||||
it,
|
||||
expect,
|
||||
ddescribe,
|
||||
iit,
|
||||
SpyObject,
|
||||
el,
|
||||
normalizeCSS
|
||||
} from 'angular2/testing_internal';
|
||||
import {ShadowCss, processRules, CssRule} from 'angular2/src/compiler/shadow_css';
|
||||
|
||||
import {RegExpWrapper, StringWrapper, isPresent} from 'angular2/src/core/facade/lang';
|
||||
|
||||
export function main() {
|
||||
describe('ShadowCss', function() {
|
||||
|
||||
function s(css: string, contentAttr: string, hostAttr: string = '') {
|
||||
var shadowCss = new ShadowCss();
|
||||
var shim = shadowCss.shimCssText(css, contentAttr, hostAttr);
|
||||
var nlRegexp = /\n/g;
|
||||
return normalizeCSS(StringWrapper.replaceAll(shim, nlRegexp, ''));
|
||||
}
|
||||
|
||||
it('should handle empty string', () => { expect(s('', 'a')).toEqual(''); });
|
||||
|
||||
it('should add an attribute to every rule', () => {
|
||||
var css = 'one {color: red;}two {color: red;}';
|
||||
var expected = 'one[a] {color:red;}two[a] {color:red;}';
|
||||
expect(s(css, 'a')).toEqual(expected);
|
||||
});
|
||||
|
||||
it('should handle invalid css', () => {
|
||||
var css = 'one {color: red;}garbage';
|
||||
var expected = 'one[a] {color:red;}garbage';
|
||||
expect(s(css, 'a')).toEqual(expected);
|
||||
});
|
||||
|
||||
it('should add an attribute to every selector', () => {
|
||||
var css = 'one, two {color: red;}';
|
||||
var expected = 'one[a], two[a] {color:red;}';
|
||||
expect(s(css, 'a')).toEqual(expected);
|
||||
});
|
||||
|
||||
it('should support newlines in the selector and content ', () => {
|
||||
var css = 'one, \ntwo {\ncolor: red;}';
|
||||
var expected = 'one[a], two[a] {color:red;}';
|
||||
expect(s(css, 'a')).toEqual(expected);
|
||||
});
|
||||
|
||||
it('should handle media rules', () => {
|
||||
var css = '@media screen and (max-width:800px, max-height:100%) {div {font-size:50px;}}';
|
||||
var expected =
|
||||
'@media screen and (max-width:800px, max-height:100%) {div[a] {font-size:50px;}}';
|
||||
expect(s(css, 'a')).toEqual(expected);
|
||||
});
|
||||
|
||||
it('should handle media rules with simple rules', () => {
|
||||
var css = '@media screen and (max-width: 800px) {div {font-size: 50px;}} div {}';
|
||||
var expected = '@media screen and (max-width:800px) {div[a] {font-size:50px;}} div[a] {}';
|
||||
expect(s(css, 'a')).toEqual(expected);
|
||||
});
|
||||
|
||||
// Check that the browser supports unprefixed CSS animation
|
||||
it('should handle keyframes rules', () => {
|
||||
var css = '@keyframes foo {0% {transform:translate(-50%) scaleX(0);}}';
|
||||
expect(s(css, 'a')).toEqual(css);
|
||||
});
|
||||
|
||||
it('should handle -webkit-keyframes rules', () => {
|
||||
var css = '@-webkit-keyframes foo {0% {-webkit-transform:translate(-50%) scaleX(0);}}';
|
||||
expect(s(css, 'a')).toEqual(css);
|
||||
});
|
||||
|
||||
it('should handle complicated selectors', () => {
|
||||
expect(s('one::before {}', 'a')).toEqual('one[a]::before {}');
|
||||
expect(s('one two {}', 'a')).toEqual('one[a] two[a] {}');
|
||||
expect(s('one > two {}', 'a')).toEqual('one[a] > two[a] {}');
|
||||
expect(s('one + two {}', 'a')).toEqual('one[a] + two[a] {}');
|
||||
expect(s('one ~ two {}', 'a')).toEqual('one[a] ~ two[a] {}');
|
||||
var res = s('.one.two > three {}', 'a'); // IE swap classes
|
||||
expect(res == '.one.two[a] > three[a] {}' || res == '.two.one[a] > three[a] {}')
|
||||
.toEqual(true);
|
||||
expect(s('one[attr="value"] {}', 'a')).toEqual('one[attr="value"][a] {}');
|
||||
expect(s('one[attr=value] {}', 'a')).toEqual('one[attr="value"][a] {}');
|
||||
expect(s('one[attr^="value"] {}', 'a')).toEqual('one[attr^="value"][a] {}');
|
||||
expect(s('one[attr$="value"] {}', 'a')).toEqual('one[attr$="value"][a] {}');
|
||||
expect(s('one[attr*="value"] {}', 'a')).toEqual('one[attr*="value"][a] {}');
|
||||
expect(s('one[attr|="value"] {}', 'a')).toEqual('one[attr|="value"][a] {}');
|
||||
expect(s('one[attr] {}', 'a')).toEqual('one[attr][a] {}');
|
||||
expect(s('[is="one"] {}', 'a')).toEqual('[is="one"][a] {}');
|
||||
});
|
||||
|
||||
it('should handle :host', () => {
|
||||
expect(s(':host {}', 'a', 'a-host')).toEqual('[a-host] {}');
|
||||
expect(s(':host(.x,.y) {}', 'a', 'a-host')).toEqual('[a-host].x, [a-host].y {}');
|
||||
expect(s(':host(.x,.y) > .z {}', 'a', 'a-host'))
|
||||
.toEqual('[a-host].x > .z, [a-host].y > .z {}');
|
||||
});
|
||||
|
||||
it('should handle :host-context', () => {
|
||||
expect(s(':host-context(.x) {}', 'a', 'a-host')).toEqual('[a-host].x, .x [a-host] {}');
|
||||
expect(s(':host-context(.x) > .y {}', 'a', 'a-host'))
|
||||
.toEqual('[a-host].x > .y, .x [a-host] > .y {}');
|
||||
});
|
||||
|
||||
it('should support polyfill-next-selector', () => {
|
||||
var css = s("polyfill-next-selector {content: 'x > y'} z {}", 'a');
|
||||
expect(css).toEqual('x[a] > y[a]{}');
|
||||
|
||||
css = s('polyfill-next-selector {content: "x > y"} z {}', 'a');
|
||||
expect(css).toEqual('x[a] > y[a]{}');
|
||||
});
|
||||
|
||||
it('should support polyfill-unscoped-rule', () => {
|
||||
var css = s("polyfill-unscoped-rule {content: '#menu > .bar';color: blue;}", 'a');
|
||||
expect(StringWrapper.contains(css, '#menu > .bar {;color:blue;}')).toBeTruthy();
|
||||
|
||||
css = s('polyfill-unscoped-rule {content: "#menu > .bar";color: blue;}', 'a');
|
||||
expect(StringWrapper.contains(css, '#menu > .bar {;color:blue;}')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should support multiple instances polyfill-unscoped-rule', () => {
|
||||
var css = s("polyfill-unscoped-rule {content: 'foo';color: blue;}" +
|
||||
"polyfill-unscoped-rule {content: 'bar';color: blue;}",
|
||||
'a');
|
||||
expect(StringWrapper.contains(css, 'foo {;color:blue;}')).toBeTruthy();
|
||||
expect(StringWrapper.contains(css, 'bar {;color:blue;}')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should support polyfill-rule', () => {
|
||||
var css = s("polyfill-rule {content: ':host.foo .bar';color: blue;}", 'a', 'a-host');
|
||||
expect(css).toEqual('[a-host].foo .bar {;color:blue;}');
|
||||
|
||||
css = s('polyfill-rule {content: ":host.foo .bar";color:blue;}', 'a', 'a-host');
|
||||
expect(css).toEqual('[a-host].foo .bar {;color:blue;}');
|
||||
});
|
||||
|
||||
it('should handle ::shadow', () => {
|
||||
var css = s('x::shadow > y {}', 'a');
|
||||
expect(css).toEqual('x[a] > y[a] {}');
|
||||
});
|
||||
|
||||
it('should handle /deep/', () => {
|
||||
var css = s('x /deep/ y {}', 'a');
|
||||
expect(css).toEqual('x[a] y[a] {}');
|
||||
});
|
||||
|
||||
it('should handle >>>', () => {
|
||||
var css = s('x >>> y {}', 'a');
|
||||
expect(css).toEqual('x[a] y[a] {}');
|
||||
});
|
||||
|
||||
it('should pass through @import directives', () => {
|
||||
var styleStr = '@import url("https://fonts.googleapis.com/css?family=Roboto");';
|
||||
var css = s(styleStr, 'a');
|
||||
expect(css).toEqual(styleStr);
|
||||
});
|
||||
|
||||
it('should shim rules after @import', () => {
|
||||
var styleStr = '@import url("a"); div {}';
|
||||
var css = s(styleStr, 'a');
|
||||
expect(css).toEqual('@import url("a"); div[a] {}');
|
||||
});
|
||||
|
||||
it('should leave calc() unchanged', () => {
|
||||
var styleStr = 'div {height:calc(100% - 55px);}';
|
||||
var css = s(styleStr, 'a');
|
||||
expect(css).toEqual('div[a] {height:calc(100% - 55px);}');
|
||||
});
|
||||
|
||||
it('should strip comments', () => { expect(s('/* x */b {c}', 'a')).toEqual('b[a] {c}'); });
|
||||
|
||||
it('should ignore special characters in comments',
|
||||
() => { expect(s('/* {;, */b {c}', 'a')).toEqual('b[a] {c}'); });
|
||||
|
||||
it('should support multiline comments',
|
||||
() => { expect(s('/* \n */b {c}', 'a')).toEqual('b[a] {c}'); });
|
||||
});
|
||||
|
||||
describe('processRules', () => {
|
||||
describe('parse rules', () => {
|
||||
function captureRules(input: string): CssRule[] {
|
||||
var result = [];
|
||||
processRules(input, (cssRule) => {
|
||||
result.push(cssRule);
|
||||
return cssRule;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
it('should work with empty css', () => { expect(captureRules('')).toEqual([]); });
|
||||
|
||||
it('should capture a rule without body',
|
||||
() => { expect(captureRules('a;')).toEqual([new CssRule('a', '')]); });
|
||||
|
||||
it('should capture css rules with body',
|
||||
() => { expect(captureRules('a {b}')).toEqual([new CssRule('a', 'b')]); });
|
||||
|
||||
it('should capture css rules with nested rules', () => {
|
||||
expect(captureRules('a {b {c}} d {e}'))
|
||||
.toEqual([new CssRule('a', 'b {c}'), new CssRule('d', 'e')]);
|
||||
});
|
||||
|
||||
it('should capture mutiple rules where some have no body', () => {
|
||||
expect(captureRules('@import a ; b {c}'))
|
||||
.toEqual([new CssRule('@import a', ''), new CssRule('b', 'c')]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('modify rules', () => {
|
||||
it('should allow to change the selector while preserving whitespaces', () => {
|
||||
expect(processRules('@import a; b {c {d}} e {f}',
|
||||
(cssRule) => new CssRule(cssRule.selector + '2', cssRule.content)))
|
||||
.toEqual('@import a2; b2 {c {d}} e2 {f}');
|
||||
});
|
||||
|
||||
it('should allow to change the content', () => {
|
||||
expect(processRules('a {b}',
|
||||
(cssRule) => new CssRule(cssRule.selector, cssRule.content + '2')))
|
||||
.toEqual('a {b2}');
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
47
modules/angular2/test/compiler/source_module_spec.ts
Normal file
47
modules/angular2/test/compiler/source_module_spec.ts
Normal file
@ -0,0 +1,47 @@
|
||||
import {
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
describe,
|
||||
el,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
it,
|
||||
xit,
|
||||
TestComponentBuilder
|
||||
} from 'angular2/testing_internal';
|
||||
|
||||
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([]);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
9
modules/angular2/test/compiler/spies.dart
Normal file
9
modules/angular2/test/compiler/spies.dart
Normal file
@ -0,0 +1,9 @@
|
||||
library core.spies;
|
||||
|
||||
import 'package:angular2/src/compiler/xhr.dart';
|
||||
import 'package:angular2/testing_internal.dart';
|
||||
|
||||
@proxy
|
||||
class SpyXHR extends SpyObject implements XHR {
|
||||
noSuchMethod(m) => super.noSuchMethod(m);
|
||||
}
|
7
modules/angular2/test/compiler/spies.ts
Normal file
7
modules/angular2/test/compiler/spies.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import {XHR} from 'angular2/src/compiler/xhr';
|
||||
|
||||
import {SpyObject, proxy} from 'angular2/testing_internal';
|
||||
|
||||
export class SpyXHR extends SpyObject {
|
||||
constructor() { super(XHR); }
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
// used by style_compiler_spec.ts
|
||||
export var STYLES = ['span[_ngcontent-%COMP%] {\ncolor: blue;\n}'];
|
@ -0,0 +1,2 @@
|
||||
// used by style_compiler_spec.ts
|
||||
export var STYLES = ['span {color: blue}'];
|
326
modules/angular2/test/compiler/style_compiler_spec.ts
Normal file
326
modules/angular2/test/compiler/style_compiler_spec.ts
Normal file
@ -0,0 +1,326 @@
|
||||
import {
|
||||
ddescribe,
|
||||
describe,
|
||||
xdescribe,
|
||||
it,
|
||||
iit,
|
||||
xit,
|
||||
expect,
|
||||
beforeEach,
|
||||
afterEach,
|
||||
AsyncTestCompleter,
|
||||
inject,
|
||||
beforeEachBindings
|
||||
} from 'angular2/testing_internal';
|
||||
import {provide} from 'angular2/src/core/di';
|
||||
import {SpyXHR} from './spies';
|
||||
import {XHR} from 'angular2/src/compiler/xhr';
|
||||
import {BaseException, WrappedException} from 'angular2/src/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/metadata/view';
|
||||
import {TEST_PROVIDERS} from './test_bindings';
|
||||
import {codeGenValueFn, codeGenExportVariable, MODULE_SUFFIX} from 'angular2/src/compiler/util';
|
||||
|
||||
// Attention: These module names have to correspond to real modules!
|
||||
var MODULE_URL = `package:angular2/test/compiler/style_compiler_spec${MODULE_SUFFIX}`;
|
||||
var IMPORT_ABS_STYLESHEET_URL = `package:angular2/test/compiler/style_compiler_import.css`;
|
||||
var IMPORT_REL_STYLESHEET_URL = './style_compiler_import.css';
|
||||
// Note: Not a real module, only used via mocks.
|
||||
var IMPORT_ABS_STYLESHEET_URL_WITH_IMPORT =
|
||||
`package:angular2/test/compiler/style_compiler_transitive_import.css`;
|
||||
|
||||
export function main() {
|
||||
describe('StyleCompiler', () => {
|
||||
var xhr: SpyXHR;
|
||||
var templateId;
|
||||
var appId;
|
||||
|
||||
beforeEachBindings(() => {
|
||||
xhr = <any>new SpyXHR();
|
||||
return [TEST_PROVIDERS, provide(XHR, {useValue: 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] {color: blue}'
|
||||
]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should allow to import rules transitively', inject([AsyncTestCompleter], (async) => {
|
||||
compile(['div {\ncolor: red;\n}'], [IMPORT_ABS_STYLESHEET_URL_WITH_IMPORT],
|
||||
encapsulation)
|
||||
.then(styles => {
|
||||
compareStyles(styles, [
|
||||
'div[_ngcontent-app1-23] {\ncolor: red;\n}',
|
||||
'a[_ngcontent-app1-23] {color: green}',
|
||||
'span[_ngcontent-app1-23] {color: blue}'
|
||||
]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
||||
it('should cache stylesheets for parallel requests', inject([AsyncTestCompleter], (async) => {
|
||||
PromiseWrapper.all([
|
||||
compile([], [IMPORT_ABS_STYLESHEET_URL], ViewEncapsulation.None),
|
||||
compile([], [IMPORT_ABS_STYLESHEET_URL], ViewEncapsulation.None)
|
||||
])
|
||||
.then((styleArrays) => {
|
||||
expect(styleArrays[0]).toEqual(['span {color: blue}']);
|
||||
expect(styleArrays[1]).toEqual(['span {color: blue}']);
|
||||
expect(xhrCount).toBe(1);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should cache stylesheets for serial requests', inject([AsyncTestCompleter], (async) => {
|
||||
compile([], [IMPORT_ABS_STYLESHEET_URL], ViewEncapsulation.None)
|
||||
.then((styles0) => {
|
||||
xhrUrlResults[IMPORT_ABS_STYLESHEET_URL] = 'span {color: black}';
|
||||
return compile([], [IMPORT_ABS_STYLESHEET_URL], ViewEncapsulation.None)
|
||||
.then((styles1) => {
|
||||
expect(styles0).toEqual(['span {color: blue}']);
|
||||
expect(styles1).toEqual(['span {color: blue}']);
|
||||
expect(xhrCount).toBe(1);
|
||||
async.done();
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
it('should allow to clear the cache', inject([AsyncTestCompleter], (async) => {
|
||||
compile([], [IMPORT_ABS_STYLESHEET_URL], ViewEncapsulation.None)
|
||||
.then((_) => {
|
||||
compiler.clearCache();
|
||||
xhrUrlResults[IMPORT_ABS_STYLESHEET_URL] = 'span {color: black}';
|
||||
return compile([], [IMPORT_ABS_STYLESHEET_URL], ViewEncapsulation.None);
|
||||
})
|
||||
.then((styles) => {
|
||||
expect(xhrCount).toBe(2);
|
||||
expect(styles).toEqual(['span {color: black}']);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
||||
describe('compileComponentCodeGen', () => {
|
||||
function compile(styles: string[], styleAbsUrls: string[],
|
||||
encapsulation: ViewEncapsulation): Promise<string[]> {
|
||||
var sourceExpression = compiler.compileComponentCodeGen(
|
||||
`'${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] {color: red}',
|
||||
'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%] {color: red;}']];
|
||||
compareStyles(stylesAndShimStyles[0], expected[0]);
|
||||
compareStyles(stylesAndShimStyles[1], expected[1]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should allow to import rules with relative paths',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
compile(`div {color: red}@import ${IMPORT_REL_STYLESHEET_URL};`)
|
||||
.then(stylesAndShimStyles => {
|
||||
var expected = [
|
||||
['div {color: red}', 'span {color: blue}'],
|
||||
[
|
||||
'div[_ngcontent-%COMP%] {color: red}',
|
||||
'span[_ngcontent-%COMP%] {\ncolor: blue;\n}'
|
||||
]
|
||||
];
|
||||
compareStyles(stylesAndShimStyles[0], expected[0]);
|
||||
compareStyles(stylesAndShimStyles[1], expected[1]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function testableExpression(source: SourceExpression): SourceModule {
|
||||
var testableSource = `${source.declarations.join('\n')}
|
||||
${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]);
|
||||
}
|
||||
}
|
109
modules/angular2/test/compiler/style_url_resolver_spec.ts
Normal file
109
modules/angular2/test/compiler/style_url_resolver_spec.ts
Normal file
@ -0,0 +1,109 @@
|
||||
import {describe, it, expect, beforeEach, ddescribe, iit, xit, el} from 'angular2/testing_internal';
|
||||
import {extractStyleUrls, isStyleUrlResolvable} from 'angular2/src/compiler/style_url_resolver';
|
||||
|
||||
import {UrlResolver} from 'angular2/src/compiler/url_resolver';
|
||||
|
||||
export function main() {
|
||||
describe('extractStyleUrls', () => {
|
||||
var urlResolver;
|
||||
|
||||
beforeEach(() => { urlResolver = new UrlResolver(); });
|
||||
|
||||
it('should not resolve "url()" urls', () => {
|
||||
var css = `
|
||||
.foo {
|
||||
background-image: url("double.jpg");
|
||||
background-image: url('simple.jpg');
|
||||
background-image: url(noquote.jpg);
|
||||
}`;
|
||||
var resolvedCss = extractStyleUrls(urlResolver, 'http://ng.io', css).style;
|
||||
expect(resolvedCss).toEqual(css);
|
||||
});
|
||||
|
||||
it('should extract "@import" urls', () => {
|
||||
var css = `
|
||||
@import '1.css';
|
||||
@import "2.css";
|
||||
`;
|
||||
var styleWithImports = extractStyleUrls(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 = extractStyleUrls(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 = extractStyleUrls(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 = extractStyleUrls(urlResolver, 'http://ng.io', css);
|
||||
expect(styleWithImports.style.trim()).toEqual('');
|
||||
expect(styleWithImports.styleUrls)
|
||||
.toEqual(['http://ng.io/print1.css', 'http://ng.io/print2.css']);
|
||||
});
|
||||
|
||||
it('should leave absolute non-package @import urls intact', () => {
|
||||
var css = `@import url('http://server.com/some.css');`;
|
||||
var styleWithImports = extractStyleUrls(urlResolver, 'http://ng.io', css);
|
||||
expect(styleWithImports.style.trim()).toEqual(`@import url('http://server.com/some.css');`);
|
||||
expect(styleWithImports.styleUrls).toEqual([]);
|
||||
});
|
||||
|
||||
it('should resolve package @import urls', () => {
|
||||
var css = `@import url('package:a/b/some.css');`;
|
||||
var styleWithImports = extractStyleUrls(new FakeUrlResolver(), 'http://ng.io', css);
|
||||
expect(styleWithImports.style.trim()).toEqual(``);
|
||||
expect(styleWithImports.styleUrls).toEqual(['fake_resolved_url']);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('isStyleUrlResolvable', () => {
|
||||
it('should resolve relative urls',
|
||||
() => { expect(isStyleUrlResolvable('someUrl.css')).toBe(true); });
|
||||
|
||||
it('should resolve package: urls',
|
||||
() => { expect(isStyleUrlResolvable('package:someUrl.css')).toBe(true); });
|
||||
|
||||
it('should resolve asset: urls',
|
||||
() => { expect(isStyleUrlResolvable('asset:someUrl.css')).toBe(true); });
|
||||
|
||||
it('should not resolve empty urls', () => {
|
||||
expect(isStyleUrlResolvable(null)).toBe(false);
|
||||
expect(isStyleUrlResolvable('')).toBe(false);
|
||||
});
|
||||
|
||||
it('should not resolve urls with other schema',
|
||||
() => { expect(isStyleUrlResolvable('http://otherurl')).toBe(false); });
|
||||
|
||||
it('should not resolve urls with absolute paths', () => {
|
||||
expect(isStyleUrlResolvable('/otherurl')).toBe(false);
|
||||
expect(isStyleUrlResolvable('//otherurl')).toBe(false);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/// The real thing behaves differently between Dart and JS for package URIs.
|
||||
class FakeUrlResolver extends UrlResolver {
|
||||
constructor() { super(); }
|
||||
|
||||
resolve(baseUrl: string, url: string): string { return 'fake_resolved_url'; }
|
||||
}
|
402
modules/angular2/test/compiler/template_compiler_spec.ts
Normal file
402
modules/angular2/test/compiler/template_compiler_spec.ts
Normal file
@ -0,0 +1,402 @@
|
||||
import {
|
||||
ddescribe,
|
||||
describe,
|
||||
xdescribe,
|
||||
it,
|
||||
iit,
|
||||
xit,
|
||||
expect,
|
||||
beforeEach,
|
||||
afterEach,
|
||||
AsyncTestCompleter,
|
||||
inject,
|
||||
beforeEachBindings
|
||||
} from 'angular2/testing_internal';
|
||||
|
||||
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/compiler/xhr';
|
||||
import {MockXHR} from 'angular2/src/compiler/xhr_mock';
|
||||
import {ViewEncapsulation} from 'angular2/src/core/metadata/view';
|
||||
|
||||
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, provide} from 'angular2/core';
|
||||
|
||||
import {TEST_PROVIDERS} 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/application_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(() => [provide(APP_ID, {useValue: APP_ID_VALUE}), TEST_PROVIDERS]);
|
||||
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}'],
|
||||
encapsulation: ViewEncapsulation.None
|
||||
})
|
||||
class CompWithBindingsAndStyles {
|
||||
}
|
||||
|
||||
@Component({selector: 'tree', moduleId: THIS_MODULE_ID})
|
||||
@View({template: '<tree></tree>', directives: [TreeComp], encapsulation: ViewEncapsulation.None})
|
||||
class TreeComp {
|
||||
}
|
||||
|
||||
@Component({selector: 'comp-url', moduleId: THIS_MODULE_ID})
|
||||
@View({templateUrl: 'compUrl.html', encapsulation: ViewEncapsulation.None})
|
||||
class CompWithTemplateUrl {
|
||||
}
|
||||
|
||||
@Component({selector: 'comp-tpl', moduleId: THIS_MODULE_ID})
|
||||
@View({
|
||||
template: '<template><a [href]="someProp"></a></template>',
|
||||
encapsulation: ViewEncapsulation.None
|
||||
})
|
||||
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, {[key: string]: any}> = null): {[key: string]: any} {
|
||||
if (isBlank(humanizedTemplates)) {
|
||||
humanizedTemplates = new Map<number, {[key: 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, {[key: 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;
|
||||
}
|
||||
}
|
329
modules/angular2/test/compiler/template_normalizer_spec.ts
Normal file
329
modules/angular2/test/compiler/template_normalizer_spec.ts
Normal file
@ -0,0 +1,329 @@
|
||||
import {
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
describe,
|
||||
el,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
it,
|
||||
xit,
|
||||
TestComponentBuilder,
|
||||
beforeEachBindings
|
||||
} from 'angular2/testing_internal';
|
||||
|
||||
import {
|
||||
CompileTypeMetadata,
|
||||
CompileTemplateMetadata
|
||||
} from 'angular2/src/compiler/directive_metadata';
|
||||
import {ViewEncapsulation} from 'angular2/src/core/metadata/view';
|
||||
|
||||
import {TemplateNormalizer} from 'angular2/src/compiler/template_normalizer';
|
||||
import {XHR} from 'angular2/src/compiler/xhr';
|
||||
import {MockXHR} from 'angular2/src/compiler/xhr_mock';
|
||||
import {TEST_PROVIDERS} from './test_bindings';
|
||||
|
||||
export function main() {
|
||||
describe('TemplateNormalizer', () => {
|
||||
var dirType: CompileTypeMetadata;
|
||||
var dirTypeWithHttpUrl: CompileTypeMetadata;
|
||||
|
||||
beforeEachBindings(() => TEST_PROVIDERS);
|
||||
|
||||
beforeEach(() => {
|
||||
dirType = new CompileTypeMetadata({moduleUrl: 'package:some/module/a.js', name: 'SomeComp'});
|
||||
dirTypeWithHttpUrl =
|
||||
new CompileTypeMetadata({moduleUrl: 'http://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 ignore link elements with absolute urls but non package: scheme',
|
||||
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
|
||||
var template = normalizer.normalizeLoadedTemplate(
|
||||
dirType,
|
||||
new CompileTemplateMetadata({encapsulation: null, styles: [], styleUrls: []}),
|
||||
'<link href="http://some/external.css" rel="stylesheet"></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 not 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(\'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 resolve relative style urls in styleUrls with http directive url',
|
||||
inject([TemplateNormalizer], (normalizer: TemplateNormalizer) => {
|
||||
var template = normalizer.normalizeLoadedTemplate(
|
||||
dirTypeWithHttpUrl, new CompileTemplateMetadata(
|
||||
{encapsulation: null, styles: [], styleUrls: ['test.css']}),
|
||||
'', 'http://some/module/id');
|
||||
expect(template.styles).toEqual([]);
|
||||
expect(template.styleUrls).toEqual(['http://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}']);
|
||||
}));
|
||||
});
|
||||
});
|
||||
}
|
1108
modules/angular2/test/compiler/template_parser_spec.ts
Normal file
1108
modules/angular2/test/compiler/template_parser_spec.ts
Normal file
File diff suppressed because it is too large
Load Diff
58
modules/angular2/test/compiler/template_preparser_spec.ts
Normal file
58
modules/angular2/test/compiler/template_preparser_spec.ts
Normal file
@ -0,0 +1,58 @@
|
||||
import {
|
||||
ddescribe,
|
||||
describe,
|
||||
xdescribe,
|
||||
it,
|
||||
iit,
|
||||
xit,
|
||||
expect,
|
||||
beforeEach,
|
||||
afterEach,
|
||||
AsyncTestCompleter,
|
||||
inject,
|
||||
beforeEachBindings
|
||||
} from 'angular2/testing_internal';
|
||||
|
||||
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('*');
|
||||
}));
|
||||
|
||||
});
|
||||
}
|
12
modules/angular2/test/compiler/test_bindings.ts
Normal file
12
modules/angular2/test/compiler/test_bindings.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import {provide, Provider} from 'angular2/src/core/di';
|
||||
import {MockSchemaRegistry} from './schema_registry_mock';
|
||||
import {ElementSchemaRegistry} from 'angular2/src/compiler/schema/element_schema_registry';
|
||||
import {MockXHR} from 'angular2/src/compiler/xhr_mock';
|
||||
import {XHR} from 'angular2/src/compiler/xhr';
|
||||
import {UrlResolver, createWithoutPackagePrefix} from 'angular2/src/compiler/url_resolver';
|
||||
|
||||
export var TEST_PROVIDERS = [
|
||||
provide(ElementSchemaRegistry, {useValue: new MockSchemaRegistry({}, {})}),
|
||||
provide(XHR, {useClass: MockXHR}),
|
||||
provide(UrlResolver, {useFactory: createWithoutPackagePrefix})
|
||||
];
|
82
modules/angular2/test/compiler/url_resolver_spec.ts
Normal file
82
modules/angular2/test/compiler/url_resolver_spec.ts
Normal file
@ -0,0 +1,82 @@
|
||||
import {describe, it, expect, beforeEach, ddescribe, iit, xit, el} from 'angular2/testing_internal';
|
||||
import {UrlResolver} from 'angular2/src/compiler/url_resolver';
|
||||
|
||||
export function main() {
|
||||
describe('UrlResolver', () => {
|
||||
var resolver = new UrlResolver();
|
||||
|
||||
describe('absolute base url', () => {
|
||||
it('should add a relative path to the base url', () => {
|
||||
expect(resolver.resolve('http://www.foo.com', 'bar')).toEqual('http://www.foo.com/bar');
|
||||
expect(resolver.resolve('http://www.foo.com/', 'bar')).toEqual('http://www.foo.com/bar');
|
||||
expect(resolver.resolve('http://www.foo.com', './bar')).toEqual('http://www.foo.com/bar');
|
||||
expect(resolver.resolve('http://www.foo.com/', './bar')).toEqual('http://www.foo.com/bar');
|
||||
});
|
||||
|
||||
it('should replace the base path', () => {
|
||||
expect(resolver.resolve('http://www.foo.com/baz', 'bar')).toEqual('http://www.foo.com/bar');
|
||||
expect(resolver.resolve('http://www.foo.com/baz', './bar'))
|
||||
.toEqual('http://www.foo.com/bar');
|
||||
});
|
||||
|
||||
it('should append to the base path', () => {
|
||||
expect(resolver.resolve('http://www.foo.com/baz/', 'bar'))
|
||||
.toEqual('http://www.foo.com/baz/bar');
|
||||
expect(resolver.resolve('http://www.foo.com/baz/', './bar'))
|
||||
.toEqual('http://www.foo.com/baz/bar');
|
||||
});
|
||||
|
||||
it('should support ".." in the path', () => {
|
||||
expect(resolver.resolve('http://www.foo.com/baz/', '../bar'))
|
||||
.toEqual('http://www.foo.com/bar');
|
||||
expect(resolver.resolve('http://www.foo.com/1/2/3/', '../../bar'))
|
||||
.toEqual('http://www.foo.com/1/bar');
|
||||
expect(resolver.resolve('http://www.foo.com/1/2/3/', '../biz/bar'))
|
||||
.toEqual('http://www.foo.com/1/2/biz/bar');
|
||||
expect(resolver.resolve('http://www.foo.com/1/2/baz', '../../bar'))
|
||||
.toEqual('http://www.foo.com/bar');
|
||||
});
|
||||
|
||||
it('should ignore the base path when the url has a scheme', () => {
|
||||
expect(resolver.resolve('http://www.foo.com', 'http://www.bar.com'))
|
||||
.toEqual('http://www.bar.com');
|
||||
});
|
||||
|
||||
it('should support absolute urls', () => {
|
||||
expect(resolver.resolve('http://www.foo.com', '/bar')).toEqual('http://www.foo.com/bar');
|
||||
expect(resolver.resolve('http://www.foo.com/', '/bar')).toEqual('http://www.foo.com/bar');
|
||||
expect(resolver.resolve('http://www.foo.com/baz', '/bar'))
|
||||
.toEqual('http://www.foo.com/bar');
|
||||
expect(resolver.resolve('http://www.foo.com/baz/', '/bar'))
|
||||
.toEqual('http://www.foo.com/bar');
|
||||
});
|
||||
});
|
||||
|
||||
describe('relative base url', () => {
|
||||
it('should add a relative path to the base url', () => {
|
||||
expect(resolver.resolve('foo/', './bar')).toEqual('foo/bar');
|
||||
expect(resolver.resolve('foo/baz', './bar')).toEqual('foo/bar');
|
||||
expect(resolver.resolve('foo/baz', 'bar')).toEqual('foo/bar');
|
||||
|
||||
});
|
||||
|
||||
it('should support ".." in the path', () => {
|
||||
expect(resolver.resolve('foo/baz', '../bar')).toEqual('bar');
|
||||
expect(resolver.resolve('foo/baz', '../biz/bar')).toEqual('biz/bar');
|
||||
});
|
||||
|
||||
it('should support absolute urls', () => {
|
||||
expect(resolver.resolve('foo/baz', '/bar')).toEqual('/bar');
|
||||
expect(resolver.resolve('foo/baz/', '/bar')).toEqual('/bar');
|
||||
});
|
||||
});
|
||||
|
||||
describe('corner and error cases', () => {
|
||||
it('should encode URLs before resolving', () => {
|
||||
expect(resolver.resolve('foo/baz', `<p #p>Hello
|
||||
</p>`))
|
||||
.toEqual('foo/%3Cp%20#p%3EHello%0A%20%20%20%20%20%20%20%20%3C/p%3E');
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
55
modules/angular2/test/compiler/util_spec.ts
Normal file
55
modules/angular2/test/compiler/util_spec.ts
Normal file
@ -0,0 +1,55 @@
|
||||
import {
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
describe,
|
||||
el,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
it,
|
||||
xit,
|
||||
TestComponentBuilder
|
||||
} from 'angular2/testing_internal';
|
||||
|
||||
import {IS_DART} from 'angular2/src/core/facade/lang';
|
||||
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(`"$"`); });
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
}
|
40
modules/angular2/test/compiler/xhr_impl_spec.ts
Normal file
40
modules/angular2/test/compiler/xhr_impl_spec.ts
Normal file
@ -0,0 +1,40 @@
|
||||
import {
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
describe,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
it,
|
||||
xit
|
||||
} from 'angular2/testing_internal';
|
||||
|
||||
import {XHRImpl} from 'angular2/src/compiler/xhr_impl';
|
||||
import {PromiseWrapper} from 'angular2/src/core/facade/async';
|
||||
|
||||
export function main() {
|
||||
describe('XHRImpl', () => {
|
||||
var xhr: XHRImpl;
|
||||
var url200 = '/base/modules/angular2/test/core/services/static_assets/200.html';
|
||||
var url404 = '/base/modules/angular2/test/core/services/static_assets/404.html';
|
||||
|
||||
beforeEach(() => { xhr = new XHRImpl(); });
|
||||
|
||||
it('should resolve the Promise with the file content on success',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
xhr.get(url200).then((text) => {
|
||||
expect(text.trim()).toEqual('<p>hey</p>');
|
||||
async.done();
|
||||
});
|
||||
}), 10000);
|
||||
|
||||
it('should reject the Promise on failure', inject([AsyncTestCompleter], (async) => {
|
||||
PromiseWrapper.catchError(xhr.get(url404), (e) => {
|
||||
expect(e).toEqual(`Failed to load ${url404}`);
|
||||
async.done();
|
||||
return null;
|
||||
});
|
||||
}), 10000);
|
||||
});
|
||||
}
|
111
modules/angular2/test/compiler/xhr_mock_spec.ts
Normal file
111
modules/angular2/test/compiler/xhr_mock_spec.ts
Normal file
@ -0,0 +1,111 @@
|
||||
import {
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
describe,
|
||||
el,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
it,
|
||||
} from 'angular2/testing_internal';
|
||||
import {MockXHR} from 'angular2/src/compiler/xhr_mock';
|
||||
import {PromiseWrapper, Promise} from 'angular2/src/core/facade/async';
|
||||
import {isPresent} from 'angular2/src/core/facade/lang';
|
||||
|
||||
export function main() {
|
||||
describe('MockXHR', () => {
|
||||
var xhr: MockXHR;
|
||||
|
||||
beforeEach(() => { xhr = new MockXHR(); });
|
||||
|
||||
function expectResponse(request: Promise<string>, url: string, response: string, done = null) {
|
||||
function onResponse(text: string): string {
|
||||
if (response === null) {
|
||||
throw `Unexpected response ${url} -> ${text}`;
|
||||
} else {
|
||||
expect(text).toEqual(response);
|
||||
if (isPresent(done)) done();
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
function onError(error: string): string {
|
||||
if (response !== null) {
|
||||
throw `Unexpected error ${url}`;
|
||||
} else {
|
||||
expect(error).toEqual(`Failed to load ${url}`);
|
||||
if (isPresent(done)) done();
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
PromiseWrapper.then(request, onResponse, onError);
|
||||
}
|
||||
|
||||
it('should return a response from the definitions', inject([AsyncTestCompleter], (async) => {
|
||||
var url = '/foo';
|
||||
var response = 'bar';
|
||||
xhr.when(url, response);
|
||||
expectResponse(xhr.get(url), url, response, () => async.done());
|
||||
xhr.flush();
|
||||
}));
|
||||
|
||||
it('should return an error from the definitions', inject([AsyncTestCompleter], (async) => {
|
||||
var url = '/foo';
|
||||
var response = null;
|
||||
xhr.when(url, response);
|
||||
expectResponse(xhr.get(url), url, response, () => async.done());
|
||||
xhr.flush();
|
||||
}));
|
||||
|
||||
it('should return a response from the expectations', inject([AsyncTestCompleter], (async) => {
|
||||
var url = '/foo';
|
||||
var response = 'bar';
|
||||
xhr.expect(url, response);
|
||||
expectResponse(xhr.get(url), url, response, () => async.done());
|
||||
xhr.flush();
|
||||
}));
|
||||
|
||||
it('should return an error from the expectations', inject([AsyncTestCompleter], (async) => {
|
||||
var url = '/foo';
|
||||
var response = null;
|
||||
xhr.expect(url, response);
|
||||
expectResponse(xhr.get(url), url, response, () => async.done());
|
||||
xhr.flush();
|
||||
}));
|
||||
|
||||
it('should not reuse expectations', () => {
|
||||
var url = '/foo';
|
||||
var response = 'bar';
|
||||
xhr.expect(url, response);
|
||||
xhr.get(url);
|
||||
xhr.get(url);
|
||||
expect(() => { xhr.flush(); }).toThrowError('Unexpected request /foo');
|
||||
});
|
||||
|
||||
it('should return expectations before definitions', inject([AsyncTestCompleter], (async) => {
|
||||
var url = '/foo';
|
||||
xhr.when(url, 'when');
|
||||
xhr.expect(url, 'expect');
|
||||
expectResponse(xhr.get(url), url, 'expect');
|
||||
expectResponse(xhr.get(url), url, 'when', () => async.done());
|
||||
xhr.flush();
|
||||
}));
|
||||
|
||||
it('should throw when there is no definitions or expectations', () => {
|
||||
xhr.get('/foo');
|
||||
expect(() => { xhr.flush(); }).toThrowError('Unexpected request /foo');
|
||||
});
|
||||
|
||||
it('should throw when flush is called without any pending requests',
|
||||
() => { expect(() => { xhr.flush(); }).toThrowError('No pending requests to flush'); });
|
||||
|
||||
it('should throw on unstatisfied expectations', () => {
|
||||
xhr.expect('/foo', 'bar');
|
||||
xhr.when('/bar', 'foo');
|
||||
xhr.get('/bar');
|
||||
expect(() => { xhr.flush(); }).toThrowError('Unsatisfied requests: /foo');
|
||||
});
|
||||
});
|
||||
}
|
Reference in New Issue
Block a user