feat(DirectiveParser): throw errors when expected directives are not present
closes #527 Closes #570
This commit is contained in:

committed by
Misko Hevery

parent
715ee14ced
commit
94e203b9df
@ -1,6 +1,8 @@
|
||||
import {describe, xit, it, expect, beforeEach, ddescribe, iit, el} from 'angular2/test_lib';
|
||||
|
||||
import {DOM} from 'angular2/src/facade/dom';
|
||||
import {Type, isPresent, BaseException} from 'angular2/src/facade/lang';
|
||||
import {assertionsEnabled, isJsObject} from 'angular2/src/facade/lang';
|
||||
|
||||
import {Injector} from 'angular2/di';
|
||||
import {Lexer, Parser, ChangeDetector, dynamicChangeDetection,
|
||||
@ -50,7 +52,6 @@ export function main() {
|
||||
|
||||
it('should consume text node changes', (done) => {
|
||||
tplResolver.setTemplate(MyComp, new Template({inline: '<div>{{ctxProp}}</div>'}));
|
||||
|
||||
compiler.compile(MyComp).then((pv) => {
|
||||
createView(pv);
|
||||
ctx.ctxProp = 'Hello World!';
|
||||
@ -365,6 +366,60 @@ export function main() {
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
// TODO support these tests with DART e.g. with Promise.catch (JS) transpiled to Future.catchError (DART)
|
||||
if (assertionsEnabled() && isJsObject({})) {
|
||||
|
||||
function expectCompileError(inlineTpl, errMessage, done) {
|
||||
tplResolver.setTemplate(MyComp, new Template({inline: inlineTpl}));
|
||||
compiler.compile(MyComp).then(() => {
|
||||
throw new BaseException("Test failure: should not have come here as an exception was expected");
|
||||
},(err) => {
|
||||
expect(err.message).toBe(errMessage);
|
||||
done();
|
||||
});
|
||||
}
|
||||
|
||||
it('should raise an error if no directive is registered for an unsupported DOM property', (done) => {
|
||||
expectCompileError(
|
||||
'<div [some-prop]="foo"></div>',
|
||||
'Missing directive to handle \'some-prop\' in MyComp: <div [some-prop]="foo">',
|
||||
done
|
||||
);
|
||||
});
|
||||
|
||||
it('should raise an error if no directive is registered for a template with template bindings', (done) => {
|
||||
expectCompileError(
|
||||
'<div><div template="if: foo"></div></div>',
|
||||
'Missing directive to handle \'if\' in <div template="if: foo">',
|
||||
done
|
||||
);
|
||||
});
|
||||
|
||||
it('should raise an error for missing template directive (1)', (done) => {
|
||||
expectCompileError(
|
||||
'<div><template foo></template></div>',
|
||||
'Missing directive to handle: <template foo>',
|
||||
done
|
||||
);
|
||||
});
|
||||
|
||||
it('should raise an error for missing template directive (2)', (done) => {
|
||||
expectCompileError(
|
||||
'<div><template *if="condition"></template></div>',
|
||||
'Missing directive to handle: <template *if="condition">',
|
||||
done
|
||||
);
|
||||
});
|
||||
|
||||
it('should raise an error for missing template directive (3)', (done) => {
|
||||
expectCompileError(
|
||||
'<div *if="condition"></div>',
|
||||
'Missing directive to handle \'if\' in MyComp: <div *if="condition">',
|
||||
done
|
||||
);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -473,6 +528,19 @@ class CompWithAncestor {
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: '[child-cmp2]',
|
||||
componentServices: [MyService]
|
||||
})
|
||||
class ChildComp2 {
|
||||
ctxProp:string;
|
||||
dirProp:string;
|
||||
constructor(service: MyService) {
|
||||
this.ctxProp = service.greeting;
|
||||
this.dirProp = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Viewport({
|
||||
selector: '[some-viewport]'
|
||||
})
|
||||
|
@ -1,5 +1,5 @@
|
||||
import {describe, beforeEach, it, expect, iit, ddescribe, el} from 'angular2/test_lib';
|
||||
import {isPresent} from 'angular2/src/facade/lang';
|
||||
import {isPresent, assertionsEnabled} from 'angular2/src/facade/lang';
|
||||
import {ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
|
||||
import {DirectiveParser} from 'angular2/src/core/compiler/pipeline/directive_parser';
|
||||
import {CompilePipeline} from 'angular2/src/core/compiler/pipeline/compile_pipeline';
|
||||
@ -85,20 +85,20 @@ export function main() {
|
||||
});
|
||||
|
||||
it('should not allow multiple component directives on the same element', () => {
|
||||
expect( () => {
|
||||
createPipeline().process(
|
||||
el('<div some-comp some-comp2></div>')
|
||||
);
|
||||
}).toThrowError('Only one component directive per element is allowed!');
|
||||
expect( () => {
|
||||
createPipeline().process(
|
||||
el('<div some-comp some-comp2></div>')
|
||||
);
|
||||
}).toThrowError('Multiple component directives not allowed on the same element - check <div some-comp some-comp2>');
|
||||
});
|
||||
|
||||
it('should not allow component directives on <template> elements', () => {
|
||||
expect( () => {
|
||||
createPipeline().process(
|
||||
el('<template some-comp></template>')
|
||||
);
|
||||
}).toThrowError('Only template directives are allowed on <template> elements!');
|
||||
});
|
||||
expect( () => {
|
||||
createPipeline().process(
|
||||
el('<template some-comp></template>')
|
||||
);
|
||||
}).toThrowError('Only template directives are allowed on template elements - check <template some-comp>');
|
||||
});
|
||||
});
|
||||
|
||||
describe('viewport directives', () => {
|
||||
@ -128,7 +128,7 @@ export function main() {
|
||||
createPipeline().process(
|
||||
el('<template some-templ some-templ2></template>')
|
||||
);
|
||||
}).toThrowError('Only one template directive per element is allowed!');
|
||||
}).toThrowError('Only one viewport directive can be used per element - check <template some-templ some-templ2>');
|
||||
});
|
||||
|
||||
it('should not allow viewport directives on non <template> elements', () => {
|
||||
@ -136,7 +136,8 @@ export function main() {
|
||||
createPipeline().process(
|
||||
el('<div some-templ></div>')
|
||||
);
|
||||
}).toThrowError('Viewport directives need to be placed on <template> elements or elements with template attribute!');
|
||||
|
||||
}).toThrowError('Viewport directives need to be placed on <template> elements or elements with template attribute - check <div some-templ>');
|
||||
});
|
||||
});
|
||||
|
||||
@ -172,14 +173,6 @@ export function main() {
|
||||
expect(results[0].decoratorDirectives).toEqual([reader.read(SomeDecorator)]);
|
||||
});
|
||||
|
||||
it('should not allow decorator directives on <template> elements', () => {
|
||||
expect( () => {
|
||||
createPipeline().process(
|
||||
el('<template some-decor></template>')
|
||||
);
|
||||
}).toThrowError('Only template directives are allowed on <template> elements!');
|
||||
});
|
||||
|
||||
it('should not instantiate decorator directive twice', () => {
|
||||
var pipeline = createPipeline({propertyBindings: {
|
||||
'some-decor-with-binding': 'someExpr'
|
||||
|
@ -76,7 +76,7 @@ export function main() {
|
||||
} else if (isPresent(parent)) {
|
||||
current.inheritedProtoView = parent.inheritedProtoView;
|
||||
}
|
||||
}), new ElementBinderBuilder(parser, null)
|
||||
}), new ElementBinderBuilder(parser)
|
||||
]);
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,7 @@ export function main() {
|
||||
function createPipeline(ignoreBindings = false) {
|
||||
return new CompilePipeline([
|
||||
new MockStep((parent, current, control) => { current.ignoreBindings = ignoreBindings; }),
|
||||
new PropertyBindingParser(new Parser(new Lexer()), null)]);
|
||||
new PropertyBindingParser(new Parser(new Lexer()))]);
|
||||
}
|
||||
|
||||
it('should not parse bindings when ignoreBindings is true', () => {
|
||||
|
@ -14,7 +14,7 @@ export function main() {
|
||||
return new CompilePipeline([
|
||||
new MockStep((parent, current, control) => { current.ignoreBindings = ignoreBindings; }),
|
||||
new IgnoreChildrenStep(),
|
||||
new TextInterpolationParser(new Parser(new Lexer()), null)
|
||||
new TextInterpolationParser(new Parser(new Lexer()))
|
||||
]);
|
||||
}
|
||||
|
||||
|
@ -11,7 +11,7 @@ export function main() {
|
||||
describe('ViewSplitter', () => {
|
||||
|
||||
function createPipeline() {
|
||||
return new CompilePipeline([new ViewSplitter(new Parser(new Lexer()), null)]);
|
||||
return new CompilePipeline([new ViewSplitter(new Parser(new Lexer()))]);
|
||||
}
|
||||
|
||||
it('should mark root elements as viewRoot', () => {
|
||||
@ -160,14 +160,14 @@ export function main() {
|
||||
expect( () => {
|
||||
var rootElement = el('<div><div *foo *bar="blah"></div></div>');
|
||||
createPipeline().process(rootElement);
|
||||
}).toThrowError('Only one template directive per element is allowed: foo and bar cannot be used simultaneously!');
|
||||
}).toThrowError('Only one template directive per element is allowed: foo and bar cannot be used simultaneously in <div *foo *bar="blah">');
|
||||
});
|
||||
|
||||
it('should not allow template and bang directives on the same element', () => {
|
||||
it('should not allow template and star directives on the same element', () => {
|
||||
expect( () => {
|
||||
var rootElement = el('<div><div *foo template="blah"></div></div>');
|
||||
var rootElement = el('<div><div *foo template="bar"></div></div>');
|
||||
createPipeline().process(rootElement);
|
||||
}).toThrowError('Only one template directive per element is allowed: blah and foo cannot be used simultaneously!');
|
||||
}).toThrowError('Only one template directive per element is allowed: bar and foo cannot be used simultaneously in <div *foo template="bar">');
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -5,7 +5,7 @@ import {List, ListWrapper, MapWrapper} from 'angular2/src/facade/collection';
|
||||
|
||||
export function main() {
|
||||
describe('SelectorMatcher', () => {
|
||||
var matcher, matched, selectableCollector;
|
||||
var matcher, matched, selectableCollector, s1, s2, s3, s4;
|
||||
|
||||
function reset() {
|
||||
matched = ListWrapper.create();
|
||||
@ -13,79 +13,81 @@ export function main() {
|
||||
|
||||
beforeEach(() => {
|
||||
reset();
|
||||
selectableCollector = (selectable) => {
|
||||
ListWrapper.push(matched, selectable);
|
||||
s1 = s2 = s3 = s4 = null;
|
||||
selectableCollector = (selector, context) => {
|
||||
ListWrapper.push(matched, selector);
|
||||
ListWrapper.push(matched, context);
|
||||
}
|
||||
matcher = new SelectorMatcher();
|
||||
});
|
||||
|
||||
it('should select by element name case insensitive', () => {
|
||||
matcher.addSelectable(CssSelector.parse('someTag'), 1);
|
||||
matcher.addSelectable(s1 = CssSelector.parse('someTag'), 1);
|
||||
|
||||
matcher.match(CssSelector.parse('SOMEOTHERTAG'), selectableCollector);
|
||||
expect(matched).toEqual([]);
|
||||
|
||||
matcher.match(CssSelector.parse('SOMETAG'), selectableCollector);
|
||||
expect(matched).toEqual([1]);
|
||||
expect(matched).toEqual([s1,1]);
|
||||
});
|
||||
|
||||
it('should select by class name case insensitive', () => {
|
||||
matcher.addSelectable(CssSelector.parse('.someClass'), 1);
|
||||
matcher.addSelectable(CssSelector.parse('.someClass.class2'), 2);
|
||||
matcher.addSelectable(s1 = CssSelector.parse('.someClass'), 1);
|
||||
matcher.addSelectable(s2 = CssSelector.parse('.someClass.class2'), 2);
|
||||
|
||||
matcher.match(CssSelector.parse('.SOMEOTHERCLASS'), selectableCollector);
|
||||
expect(matched).toEqual([]);
|
||||
|
||||
matcher.match(CssSelector.parse('.SOMECLASS'), selectableCollector);
|
||||
expect(matched).toEqual([1]);
|
||||
expect(matched).toEqual([s1,1]);
|
||||
|
||||
reset();
|
||||
matcher.match(CssSelector.parse('.someClass.class2'), selectableCollector);
|
||||
expect(matched).toEqual([1,2]);
|
||||
expect(matched).toEqual([s1,1,s2,2]);
|
||||
});
|
||||
|
||||
it('should select by attr name case insensitive independent of the value', () => {
|
||||
matcher.addSelectable(CssSelector.parse('[someAttr]'), 1);
|
||||
matcher.addSelectable(CssSelector.parse('[someAttr][someAttr2]'), 2);
|
||||
matcher.addSelectable(s1 = CssSelector.parse('[someAttr]'), 1);
|
||||
matcher.addSelectable(s2 = CssSelector.parse('[someAttr][someAttr2]'), 2);
|
||||
|
||||
matcher.match(CssSelector.parse('[SOMEOTHERATTR]'), selectableCollector);
|
||||
expect(matched).toEqual([]);
|
||||
|
||||
matcher.match(CssSelector.parse('[SOMEATTR]'), selectableCollector);
|
||||
expect(matched).toEqual([1]);
|
||||
expect(matched).toEqual([s1,1]);
|
||||
|
||||
reset();
|
||||
matcher.match(CssSelector.parse('[SOMEATTR=someValue]'), selectableCollector);
|
||||
expect(matched).toEqual([1]);
|
||||
expect(matched).toEqual([s1,1]);
|
||||
|
||||
reset();
|
||||
matcher.match(CssSelector.parse('[someAttr][someAttr2]'), selectableCollector);
|
||||
expect(matched).toEqual([1,2]);
|
||||
expect(matched).toEqual([s1,1,s2,2]);
|
||||
});
|
||||
|
||||
it('should select by attr name only once if the value is from the DOM', () => {
|
||||
matcher.addSelectable(CssSelector.parse('[some-decor]'), 1);
|
||||
matcher.addSelectable(s1 = CssSelector.parse('[some-decor]'), 1);
|
||||
|
||||
var elementSelector = new CssSelector();
|
||||
var element = el('<div attr></div>');
|
||||
var empty = element.getAttribute('attr');
|
||||
elementSelector.addAttribute('some-decor', empty);
|
||||
matcher.match(elementSelector, selectableCollector);
|
||||
expect(matched).toEqual([1]);
|
||||
expect(matched).toEqual([s1,1]);
|
||||
});
|
||||
|
||||
it('should select by attr name and value case insensitive', () => {
|
||||
matcher.addSelectable(CssSelector.parse('[someAttr=someValue]'), 1);
|
||||
matcher.addSelectable(s1 = CssSelector.parse('[someAttr=someValue]'), 1);
|
||||
|
||||
matcher.match(CssSelector.parse('[SOMEATTR=SOMEOTHERATTR]'), selectableCollector);
|
||||
expect(matched).toEqual([]);
|
||||
|
||||
matcher.match(CssSelector.parse('[SOMEATTR=SOMEVALUE]'), selectableCollector);
|
||||
expect(matched).toEqual([1]);
|
||||
expect(matched).toEqual([s1,1]);
|
||||
});
|
||||
|
||||
it('should select by element name, class name and attribute name with value', () => {
|
||||
matcher.addSelectable(CssSelector.parse('someTag.someClass[someAttr=someValue]'), 1);
|
||||
matcher.addSelectable(s1 = CssSelector.parse('someTag.someClass[someAttr=someValue]'), 1);
|
||||
|
||||
matcher.match(CssSelector.parse('someOtherTag.someOtherClass[someOtherAttr]'), selectableCollector);
|
||||
expect(matched).toEqual([]);
|
||||
@ -100,29 +102,29 @@ export function main() {
|
||||
expect(matched).toEqual([]);
|
||||
|
||||
matcher.match(CssSelector.parse('someTag.someClass[someAttr=someValue]'), selectableCollector);
|
||||
expect(matched).toEqual([1]);
|
||||
expect(matched).toEqual([s1,1]);
|
||||
});
|
||||
|
||||
it('should select independent of the order in the css selector', () => {
|
||||
matcher.addSelectable(CssSelector.parse('[someAttr].someClass'), 1);
|
||||
matcher.addSelectable(CssSelector.parse('.someClass[someAttr]'), 2);
|
||||
matcher.addSelectable(CssSelector.parse('.class1.class2'), 3);
|
||||
matcher.addSelectable(CssSelector.parse('.class2.class1'), 4);
|
||||
matcher.addSelectable(s1 = CssSelector.parse('[someAttr].someClass'), 1);
|
||||
matcher.addSelectable(s2 = CssSelector.parse('.someClass[someAttr]'), 2);
|
||||
matcher.addSelectable(s3 = CssSelector.parse('.class1.class2'), 3);
|
||||
matcher.addSelectable(s4 = CssSelector.parse('.class2.class1'), 4);
|
||||
|
||||
matcher.match(CssSelector.parse('[someAttr].someClass'), selectableCollector);
|
||||
expect(matched).toEqual([1,2]);
|
||||
expect(matched).toEqual([s1,1,s2,2]);
|
||||
|
||||
reset();
|
||||
matcher.match(CssSelector.parse('.someClass[someAttr]'), selectableCollector);
|
||||
expect(matched).toEqual([1,2]);
|
||||
expect(matched).toEqual([s1,1,s2,2]);
|
||||
|
||||
reset();
|
||||
matcher.match(CssSelector.parse('.class1.class2'), selectableCollector);
|
||||
expect(matched).toEqual([3,4]);
|
||||
expect(matched).toEqual([s3,3,s4,4]);
|
||||
|
||||
reset();
|
||||
matcher.match(CssSelector.parse('.class2.class1'), selectableCollector);
|
||||
expect(matched).toEqual([4,3]);
|
||||
expect(matched).toEqual([s4,4,s3,3]);
|
||||
});
|
||||
});
|
||||
|
||||
|
Reference in New Issue
Block a user