| /* |
| * Copyright (c) 2014, the Dart project authors. |
| * |
| * Licensed under the Eclipse Public License v1.0 (the "License"); you may not use this file except |
| * in compliance with the License. You may obtain a copy of the License at |
| * |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Unless required by applicable law or agreed to in writing, software distributed under the License |
| * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
| * or implied. See the License for the specific language governing permissions and limitations under |
| * the License. |
| */ |
| package com.google.dart.engine.internal.builder; |
| |
| import com.google.dart.engine.ast.AstFactory; |
| import com.google.dart.engine.ast.AstNode; |
| import com.google.dart.engine.ast.ClassDeclaration; |
| import com.google.dart.engine.ast.SimpleStringLiteral; |
| import com.google.dart.engine.element.ClassElement; |
| import com.google.dart.engine.element.Element; |
| import com.google.dart.engine.element.FieldElement; |
| import com.google.dart.engine.element.LocalVariableElement; |
| import com.google.dart.engine.element.ToolkitObjectElement; |
| import com.google.dart.engine.element.angular.AngularComponentElement; |
| import com.google.dart.engine.element.angular.AngularControllerElement; |
| import com.google.dart.engine.element.angular.AngularDecoratorElement; |
| import com.google.dart.engine.element.angular.AngularElement; |
| import com.google.dart.engine.element.angular.AngularFormatterElement; |
| import com.google.dart.engine.element.angular.AngularPropertyElement; |
| import com.google.dart.engine.element.angular.AngularPropertyKind; |
| import com.google.dart.engine.element.angular.AngularScopePropertyElement; |
| import com.google.dart.engine.element.angular.AngularSelectorElement; |
| import com.google.dart.engine.element.angular.AngularViewElement; |
| import com.google.dart.engine.error.AngularCode; |
| import com.google.dart.engine.html.ast.XmlTagNode; |
| import com.google.dart.engine.internal.element.angular.AngularHasClassSelectorElementImpl; |
| import com.google.dart.engine.internal.element.angular.AngularScopePropertyElementImpl; |
| import com.google.dart.engine.internal.element.angular.AngularTagSelectorElementImpl; |
| import com.google.dart.engine.internal.element.angular.HasAttributeSelectorElementImpl; |
| import com.google.dart.engine.internal.element.angular.IsTagHasAttributeSelectorElementImpl; |
| import com.google.dart.engine.internal.html.angular.AngularTest; |
| |
| import static com.google.dart.engine.html.HtmlFactory.attribute; |
| import static com.google.dart.engine.html.HtmlFactory.tagNode; |
| |
| public class AngularCompilationUnitBuilderTest extends AngularTest { |
| @SuppressWarnings("unchecked") |
| protected static <T extends AngularElement> T getAngularElement(Element element, |
| Class<T> angularElementType) { |
| ToolkitObjectElement[] toolkitObjects = null; |
| if (element instanceof ClassElement) { |
| ClassElement classElement = (ClassElement) element; |
| toolkitObjects = classElement.getToolkitObjects(); |
| } |
| if (element instanceof LocalVariableElement) { |
| LocalVariableElement variableElement = (LocalVariableElement) element; |
| toolkitObjects = variableElement.getToolkitObjects(); |
| } |
| if (toolkitObjects != null) { |
| for (ToolkitObjectElement toolkitObject : toolkitObjects) { |
| if (angularElementType.isInstance(toolkitObject)) { |
| return (T) toolkitObject; |
| } |
| } |
| } |
| return null; |
| } |
| |
| private static void assertHasAttributeSelector(AngularSelectorElement selector, String name) { |
| assertInstanceOf(HasAttributeSelectorElementImpl.class, selector); |
| assertEquals(name, ((HasAttributeSelectorElementImpl) selector).getName()); |
| } |
| |
| private static void assertIsTagSelector(AngularSelectorElement selector, String name) { |
| assertInstanceOf(AngularTagSelectorElementImpl.class, selector); |
| assertEquals(name, ((AngularTagSelectorElementImpl) selector).getName()); |
| } |
| |
| private static String createAngularSource(String... lines) { |
| String source = "import 'angular.dart';\n"; |
| source += createSource(lines); |
| return source; |
| } |
| |
| public void test_bad_notConstructorAnnotation() throws Exception { |
| String mainContent = createSource(// |
| "const MY_ANNOTATION = null;", |
| "@MY_ANNOTATION()", |
| "class MyFilter {", |
| "}"); |
| resolveMainSource(mainContent); |
| // prepare AngularFilterElement |
| ClassElement classElement = mainUnitElement.getType("MyFilter"); |
| AngularFormatterElement filter = getAngularElement(classElement, AngularFormatterElement.class); |
| assertNull(filter); |
| } |
| |
| public void test_Decorator() throws Exception { |
| String mainContent = createAngularSource(// |
| "@Decorator(selector: '[my-dir]',", |
| " map: const {", |
| " 'my-dir' : '=>myPropA',", |
| " '.' : '&myPropB',", |
| " })", |
| "class MyDirective {", |
| " set myPropA(value) {}", |
| " set myPropB(value) {}", |
| " @NgTwoWay('my-prop-c')", |
| " String myPropC;", |
| "}"); |
| resolveMainSourceNoErrors(mainContent); |
| // prepare AngularDirectiveElement |
| ClassElement classElement = mainUnitElement.getType("MyDirective"); |
| AngularDecoratorElement directive = getAngularElement( |
| classElement, |
| AngularDecoratorElement.class); |
| assertNotNull(directive); |
| // verify |
| assertEquals(null, directive.getName()); |
| assertEquals(-1, directive.getNameOffset()); |
| assertHasAttributeSelector(directive.getSelector(), "my-dir"); |
| // verify properties |
| AngularPropertyElement[] properties = directive.getProperties(); |
| assertLength(3, properties); |
| assertProperty( |
| properties[0], |
| "my-dir", |
| findMainOffset("my-dir' :"), |
| AngularPropertyKind.ONE_WAY, |
| "myPropA", |
| findMainOffset("myPropA'")); |
| assertProperty( |
| properties[1], |
| ".", |
| findMainOffset(".' :"), |
| AngularPropertyKind.CALLBACK, |
| "myPropB", |
| findMainOffset("myPropB'")); |
| assertProperty( |
| properties[2], |
| "my-prop-c", |
| findMainOffset("my-prop-c'"), |
| AngularPropertyKind.TWO_WAY, |
| "myPropC", |
| -1); |
| } |
| |
| public void test_Decorator_bad_cannotParseSelector() throws Exception { |
| String mainContent = createAngularSource(// |
| "@Decorator(selector: '~bad-selector',", |
| " map: const {", |
| " 'my-dir' : '=>myPropA',", |
| " '.' : '&myPropB',", |
| " })", |
| "class MyDirective {", |
| " set myPropA(value) {}", |
| " set myPropB(value) {}", |
| "}"); |
| resolveMainSource(mainContent); |
| // has error |
| assertMainErrors(AngularCode.CANNOT_PARSE_SELECTOR); |
| } |
| |
| public void test_Decorator_bad_missingSelector() throws Exception { |
| String mainContent = createAngularSource(// |
| "@Decorator(/*selector: '[my-dir]',*/", |
| " map: const {", |
| " 'my-dir' : '=>myPropA',", |
| " '.' : '&myPropB',", |
| " })", |
| "class MyDirective {", |
| " set myPropA(value) {}", |
| " set myPropB(value) {}", |
| "}"); |
| resolveMainSource(mainContent); |
| // has error |
| assertMainErrors(AngularCode.MISSING_SELECTOR); |
| } |
| |
| public void test_Formatter() throws Exception { |
| String mainContent = createAngularSource(// |
| "@Formatter(name: 'myFilter')", |
| "class MyFilter {", |
| " call(p1, p2) {}", |
| "}"); |
| resolveMainSourceNoErrors(mainContent); |
| // prepare AngularFilterElement |
| ClassElement classElement = mainUnitElement.getType("MyFilter"); |
| AngularFormatterElement filter = getAngularElement(classElement, AngularFormatterElement.class); |
| assertNotNull(filter); |
| // verify |
| assertEquals("myFilter", filter.getName()); |
| assertEquals(findOffset(mainContent, "myFilter'"), filter.getNameOffset()); |
| } |
| |
| public void test_Formatter_missingName() throws Exception { |
| String mainContent = createAngularSource(// |
| "@Formatter()", |
| "class MyFilter {", |
| " call(p1, p2) {}", |
| "}"); |
| resolveMainSource(mainContent); |
| // has error |
| assertMainErrors(AngularCode.MISSING_NAME); |
| // no filter |
| ClassElement classElement = mainUnitElement.getType("MyFilter"); |
| AngularFormatterElement filter = getAngularElement(classElement, AngularFormatterElement.class); |
| assertNull(filter); |
| } |
| |
| public void test_getElement_component_name() throws Exception { |
| resolveMainSource(createAngularSource(// |
| "@Component(publishAs: 'ctrl', selector: 'myComp',", |
| " templateUrl: 'my_template.html', cssUrl: 'my_styles.css')", |
| "class MyComponent {}")); |
| SimpleStringLiteral node = findMainNode("ctrl'", SimpleStringLiteral.class); |
| int offset = node.getOffset(); |
| // find AngularComponentElement |
| Element element = AngularCompilationUnitBuilder.getElement(node, offset); |
| assertInstanceOf(AngularComponentElement.class, element); |
| } |
| |
| public void test_getElement_component_property_fromFieldAnnotation() throws Exception { |
| resolveMainSource(createAngularSource(// |
| "@Component(publishAs: 'ctrl', selector: 'myComp',", |
| " templateUrl: 'my_template.html', cssUrl: 'my_styles.css')", |
| "class MyComponent {", |
| " @NgOneWay('prop')", |
| " var field;", |
| "}")); |
| // prepare node |
| SimpleStringLiteral node = findMainNode("prop'", SimpleStringLiteral.class); |
| int offset = node.getOffset(); |
| // prepare Element |
| Element element = AngularCompilationUnitBuilder.getElement(node, offset); |
| assertNotNull(element); |
| // check AngularPropertyElement |
| AngularPropertyElement property = (AngularPropertyElement) element; |
| assertEquals("prop", property.getName()); |
| } |
| |
| public void test_getElement_component_property_fromMap() throws Exception { |
| resolveMainSource(createAngularSource(// |
| "@Component(publishAs: 'ctrl', selector: 'myComp',", |
| " templateUrl: 'my_template.html', cssUrl: 'my_styles.css',", |
| " map: const {", |
| " 'prop' : '@field',", |
| " })", |
| "class MyComponent {", |
| " var field;", |
| "}")); |
| // AngularPropertyElement |
| { |
| SimpleStringLiteral node = findMainNode("prop'", SimpleStringLiteral.class); |
| int offset = node.getOffset(); |
| // prepare Element |
| Element element = AngularCompilationUnitBuilder.getElement(node, offset); |
| assertNotNull(element); |
| // check AngularPropertyElement |
| AngularPropertyElement property = (AngularPropertyElement) element; |
| assertEquals("prop", property.getName()); |
| } |
| // FieldElement |
| { |
| SimpleStringLiteral node = findMainNode("@field'", SimpleStringLiteral.class); |
| int offset = node.getOffset(); |
| // prepare Element |
| Element element = AngularCompilationUnitBuilder.getElement(node, offset); |
| assertNotNull(element); |
| // check FieldElement |
| FieldElement field = (FieldElement) element; |
| assertEquals("field", field.getName()); |
| } |
| } |
| |
| public void test_getElement_component_selector() throws Exception { |
| resolveMainSource(createAngularSource(// |
| "@Component(publishAs: 'ctrl', selector: 'myComp',", |
| " templateUrl: 'my_template.html', cssUrl: 'my_styles.css')", |
| "class MyComponent {}")); |
| SimpleStringLiteral node = findMainNode("myComp'", SimpleStringLiteral.class); |
| int offset = node.getOffset(); |
| // find AngularSelectorElement |
| Element element = AngularCompilationUnitBuilder.getElement(node, offset); |
| assertInstanceOf(AngularSelectorElement.class, element); |
| } |
| |
| public void test_getElement_controller_name() throws Exception { |
| resolveMainSource(createAngularSource(// |
| "@Controller(publishAs: 'ctrl', selector: '[myApp]')", |
| "class MyController {", |
| "}")); |
| SimpleStringLiteral node = findMainNode("ctrl'", SimpleStringLiteral.class); |
| int offset = node.getOffset(); |
| // find AngularControllerElement |
| Element element = AngularCompilationUnitBuilder.getElement(node, offset); |
| assertInstanceOf(AngularControllerElement.class, element); |
| } |
| |
| public void test_getElement_directive_property() throws Exception { |
| resolveMainSource(createAngularSource(// |
| "@Decorator(selector: '[my-dir]',", |
| " map: const {", |
| " 'my-dir' : '=>field'", |
| " })", |
| "class MyDirective {", |
| " set field(value) {}", |
| "}")); |
| // prepare node |
| SimpleStringLiteral node = findMainNode("my-dir'", SimpleStringLiteral.class); |
| int offset = node.getOffset(); |
| // prepare Element |
| Element element = AngularCompilationUnitBuilder.getElement(node, offset); |
| assertNotNull(element); |
| // check AngularPropertyElement |
| AngularPropertyElement property = (AngularPropertyElement) element; |
| assertEquals("my-dir", property.getName()); |
| } |
| |
| public void test_getElement_directive_selector() throws Exception { |
| resolveMainSource(createAngularSource(// |
| "@Decorator(selector: '[my-dir]')", |
| "class MyDirective {}")); |
| SimpleStringLiteral node = findMainNode("my-dir]'", SimpleStringLiteral.class); |
| int offset = node.getOffset(); |
| // find AngularSelectorElement |
| Element element = AngularCompilationUnitBuilder.getElement(node, offset); |
| assertInstanceOf(AngularSelectorElement.class, element); |
| } |
| |
| public void test_getElement_filter_name() throws Exception { |
| resolveMainSource(createAngularSource(// |
| "@Formatter(name: 'myFilter')", |
| "class MyFilter {", |
| " call(p1, p2) {}", |
| "}")); |
| SimpleStringLiteral node = findMainNode("myFilter'", SimpleStringLiteral.class); |
| int offset = node.getOffset(); |
| // find FilterElement |
| Element element = AngularCompilationUnitBuilder.getElement(node, offset); |
| assertInstanceOf(AngularFormatterElement.class, element); |
| } |
| |
| public void test_getElement_noClassDeclaration() throws Exception { |
| resolveMainSource("var foo = 'bar';"); |
| SimpleStringLiteral node = findMainNode("bar'", SimpleStringLiteral.class); |
| Element element = AngularCompilationUnitBuilder.getElement(node, 0); |
| assertNull(element); |
| } |
| |
| public void test_getElement_noClassElement() throws Exception { |
| resolveMainSource(createSource(// |
| "class A {", |
| " const A(p);", |
| "}", |
| "", |
| "@A('bar')", |
| "class B {}")); |
| SimpleStringLiteral node = findMainNode("bar'", SimpleStringLiteral.class); |
| // reset B element |
| node.getAncestor(ClassDeclaration.class).getName().setStaticElement(null); |
| // class is not resolved - no element |
| Element element = AngularCompilationUnitBuilder.getElement(node, 0); |
| assertNull(element); |
| } |
| |
| public void test_getElement_noNode() throws Exception { |
| Element element = AngularCompilationUnitBuilder.getElement(null, 0); |
| assertNull(element); |
| } |
| |
| public void test_getElement_notFound() throws Exception { |
| resolveMainSource(createSource(// |
| "class MyComponent {", |
| " var str = 'some string';", |
| "}")); |
| // prepare node |
| SimpleStringLiteral node = findMainNode("some string'", SimpleStringLiteral.class); |
| int offset = node.getOffset(); |
| // no Element |
| Element element = AngularCompilationUnitBuilder.getElement(node, offset); |
| assertNull(element); |
| } |
| |
| public void test_getElement_SimpleStringLiteral_withToolkitElement() throws Exception { |
| SimpleStringLiteral literal = AstFactory.string("foo"); |
| Element element = new AngularScopePropertyElementImpl("foo", 0, null); |
| literal.setToolkitElement(element); |
| assertSame(element, AngularCompilationUnitBuilder.getElement(literal, -1)); |
| } |
| |
| public void test_NgComponent_bad_cannotParseSelector() throws Exception { |
| contextHelper.addSource("/my_template.html", ""); |
| contextHelper.addSource("/my_styles.css", ""); |
| String mainContent = createAngularSource(// |
| "@Component(publishAs: 'ctrl', selector: '~myComp',", |
| " templateUrl: 'my_template.html', cssUrl: 'my_styles.css')", |
| "class MyComponent {", |
| "}"); |
| resolveMainSource(mainContent); |
| // has error |
| assertMainErrors(AngularCode.CANNOT_PARSE_SELECTOR); |
| } |
| |
| public void test_NgComponent_bad_missingSelector() throws Exception { |
| contextHelper.addSource("/my_template.html", ""); |
| contextHelper.addSource("/my_styles.css", ""); |
| String mainContent = createAngularSource(// |
| "@Component(publishAs: 'ctrl', /*selector: 'myComp',*/", |
| " templateUrl: 'my_template.html', cssUrl: 'my_styles.css')", |
| "class MyComponent {", |
| "}"); |
| resolveMainSource(mainContent); |
| // has error |
| assertMainErrors(AngularCode.MISSING_SELECTOR); |
| } |
| |
| /** |
| * <p> |
| * https://code.google.com/p/dart/issues/detail?id=16346 |
| */ |
| public void test_NgComponent_bad_notHtmlTemplate() throws Exception { |
| contextHelper.addSource("/my_template", ""); |
| contextHelper.addSource("/my_styles.css", ""); |
| addMainSource(createAngularSource(// |
| "@NgComponent(publishAs: 'ctrl', selector: 'myComp',", |
| " templateUrl: 'my_template', cssUrl: 'my_styles.css')", |
| "class MyComponent {", |
| "}")); |
| contextHelper.runTasks(); |
| // no exceptions, even though "my_template" is not an HTML file |
| } |
| |
| public void test_NgComponent_bad_properties_invalidBinding() throws Exception { |
| contextHelper.addSource("/my_template.html", ""); |
| contextHelper.addSource("/my_styles.css", ""); |
| String mainContent = createAngularSource(// |
| "@Component(publishAs: 'ctrl', selector: 'myComp',", |
| " templateUrl: 'my_template.html', cssUrl: 'my_styles.css',", |
| " map: const {'name' : '?field'})", |
| "class MyComponent {", |
| "}"); |
| resolveMainSource(mainContent); |
| // has error |
| assertMainErrors(AngularCode.INVALID_PROPERTY_KIND); |
| } |
| |
| public void test_NgComponent_bad_properties_nameNotStringLiteral() throws Exception { |
| contextHelper.addSource("/my_template.html", ""); |
| contextHelper.addSource("/my_styles.css", ""); |
| String mainContent = createAngularSource(// |
| "@Component(publishAs: 'ctrl', selector: 'myComp',", |
| " templateUrl: 'my_template.html', cssUrl: 'my_styles.css',", |
| " map: const {null : 'field'})", |
| "class MyComponent {", |
| "}"); |
| resolveMainSource(mainContent); |
| // has error |
| assertMainErrors(AngularCode.INVALID_PROPERTY_NAME); |
| } |
| |
| public void test_NgComponent_bad_properties_noSuchField() throws Exception { |
| contextHelper.addSource("/my_template.html", ""); |
| contextHelper.addSource("/my_styles.css", ""); |
| String mainContent = createAngularSource(// |
| "@Component(publishAs: 'ctrl', selector: 'myComp',", |
| " templateUrl: 'my_template.html', cssUrl: 'my_styles.css',", |
| " map: const {'name' : '=>field'})", |
| "class MyComponent {", |
| "}"); |
| resolveMainSource(mainContent); |
| // has error |
| assertMainErrors(AngularCode.INVALID_PROPERTY_FIELD); |
| } |
| |
| public void test_NgComponent_bad_properties_notMapLiteral() throws Exception { |
| contextHelper.addSource("/my_template.html", ""); |
| contextHelper.addSource("/my_styles.css", ""); |
| String mainContent = createAngularSource(// |
| "@Component(publishAs: 'ctrl', selector: 'myComp',", |
| " templateUrl: 'my_template.html', cssUrl: 'my_styles.css',", |
| " map: null)", |
| "class MyComponent {", |
| "}"); |
| resolveMainSource(mainContent); |
| // has error |
| assertMainErrors(AngularCode.INVALID_PROPERTY_MAP); |
| } |
| |
| public void test_NgComponent_bad_properties_specNotStringLiteral() throws Exception { |
| contextHelper.addSource("/my_template.html", ""); |
| contextHelper.addSource("/my_styles.css", ""); |
| String mainContent = createAngularSource(// |
| "@Component(publishAs: 'ctrl', selector: 'myComp',", |
| " templateUrl: 'my_template.html', cssUrl: 'my_styles.css',", |
| " map: const {'name' : null})", |
| "class MyComponent {", |
| "}"); |
| resolveMainSource(mainContent); |
| // has error |
| assertMainErrors(AngularCode.INVALID_PROPERTY_SPEC); |
| } |
| |
| public void test_NgComponent_no_cssUrl() throws Exception { |
| contextHelper.addSource("/my_template.html", ""); |
| contextHelper.addSource("/my_styles.css", ""); |
| String mainContent = createAngularSource(// |
| "@Component(publishAs: 'ctrl', selector: 'myComp',", |
| " templateUrl: 'my_template.html'/*, cssUrl: 'my_styles.css'*/)", |
| "class MyComponent {", |
| "}"); |
| resolveMainSource(mainContent); |
| // prepare AngularComponentElement |
| ClassElement classElement = mainUnitElement.getType("MyComponent"); |
| AngularComponentElement component = getAngularElement( |
| classElement, |
| AngularComponentElement.class); |
| assertNotNull(component); |
| // no CSS |
| assertEquals(null, component.getStyleUri()); |
| assertEquals(-1, component.getStyleUriOffset()); |
| } |
| |
| public void test_NgComponent_no_publishAs() throws Exception { |
| contextHelper.addSource("/my_template.html", ""); |
| contextHelper.addSource("/my_styles.css", ""); |
| String mainContent = createAngularSource(// |
| "@Component(/*publishAs: 'ctrl',*/ selector: 'myComp',", |
| " templateUrl: 'my_template.html', cssUrl: 'my_styles.css')", |
| "class MyComponent {", |
| "}"); |
| resolveMainSource(mainContent); |
| // prepare AngularComponentElement |
| ClassElement classElement = mainUnitElement.getType("MyComponent"); |
| AngularComponentElement component = getAngularElement( |
| classElement, |
| AngularComponentElement.class); |
| assertNotNull(component); |
| // no name |
| assertEquals(null, component.getName()); |
| assertEquals(-1, component.getNameOffset()); |
| } |
| |
| public void test_NgComponent_no_templateUrl() throws Exception { |
| contextHelper.addSource("/my_template.html", ""); |
| contextHelper.addSource("/my_styles.css", ""); |
| String mainContent = createAngularSource(// |
| "@Component(publishAs: 'ctrl', selector: 'myComp',", |
| " /*templateUrl: 'my_template.html',*/ cssUrl: 'my_styles.css')", |
| "class MyComponent {", |
| "}"); |
| resolveMainSource(mainContent); |
| // prepare AngularComponentElement |
| ClassElement classElement = mainUnitElement.getType("MyComponent"); |
| AngularComponentElement component = getAngularElement( |
| classElement, |
| AngularComponentElement.class); |
| assertNotNull(component); |
| // no template |
| assertEquals(null, component.getTemplateUri()); |
| assertEquals(null, component.getTemplateSource()); |
| assertEquals(-1, component.getTemplateUriOffset()); |
| } |
| |
| /** |
| * <p> |
| * https://code.google.com/p/dart/issues/detail?id=19023 |
| */ |
| public void test_NgComponent_notAngular() throws Exception { |
| contextHelper.addSource("/my_template.html", ""); |
| contextHelper.addSource("/my_styles.css", ""); |
| String mainContent = createSource(// |
| "class Component {", |
| " const Component(a, b);", |
| "}", |
| "", |
| "@Component('foo', 42)", |
| "class MyComponent {", |
| "}"); |
| resolveMainSource(mainContent); |
| assertNoMainErrors(); |
| } |
| |
| public void test_NgComponent_properties_fieldFromSuper() throws Exception { |
| contextHelper.addSource("/my_template.html", ""); |
| contextHelper.addSource("/my_styles.css", ""); |
| resolveMainSourceNoErrors(createAngularSource(// |
| "class MySuper {", |
| " var myPropA;", |
| "}", |
| "", |
| "", |
| "", |
| "@Component(publishAs: 'ctrl', selector: 'myComp',", |
| " templateUrl: 'my_template.html', cssUrl: 'my_styles.css',", |
| " map: const {", |
| " 'prop-a' : '@myPropA'", |
| " })", |
| "class MyComponent extends MySuper {", |
| "}")); |
| // prepare AngularComponentElement |
| ClassElement classElement = mainUnitElement.getType("MyComponent"); |
| AngularComponentElement component = getAngularElement( |
| classElement, |
| AngularComponentElement.class); |
| assertNotNull(component); |
| // verify |
| AngularPropertyElement[] properties = component.getProperties(); |
| assertLength(1, properties); |
| assertProperty( |
| properties[0], |
| "prop-a", |
| findMainOffset("prop-a' :"), |
| AngularPropertyKind.ATTR, |
| "myPropA", |
| findMainOffset("myPropA'")); |
| } |
| |
| public void test_NgComponent_properties_fromFields() throws Exception { |
| contextHelper.addSource("/my_template.html", ""); |
| contextHelper.addSource("/my_styles.css", ""); |
| resolveMainSourceNoErrors(createAngularSource(// |
| "@Component(publishAs: 'ctrl', selector: 'myComp',", |
| " templateUrl: 'my_template.html', cssUrl: 'my_styles.css')", |
| "class MyComponent {", |
| " @NgAttr('prop-a')", |
| " var myPropA;", |
| " @NgCallback('prop-b')", |
| " var myPropB;", |
| " @NgOneWay('prop-c')", |
| " var myPropC;", |
| " @NgOneWayOneTime('prop-d')", |
| " var myPropD;", |
| " @NgTwoWay('prop-e')", |
| " var myPropE;", |
| "}")); |
| // prepare AngularComponentElement |
| ClassElement classElement = mainUnitElement.getType("MyComponent"); |
| AngularComponentElement component = getAngularElement( |
| classElement, |
| AngularComponentElement.class); |
| assertNotNull(component); |
| // verify |
| AngularPropertyElement[] properties = component.getProperties(); |
| assertLength(5, properties); |
| assertProperty( |
| properties[0], |
| "prop-a", |
| findMainOffset("prop-a')"), |
| AngularPropertyKind.ATTR, |
| "myPropA", |
| -1); |
| assertProperty( |
| properties[1], |
| "prop-b", |
| findMainOffset("prop-b')"), |
| AngularPropertyKind.CALLBACK, |
| "myPropB", |
| -1); |
| assertProperty( |
| properties[2], |
| "prop-c", |
| findMainOffset("prop-c')"), |
| AngularPropertyKind.ONE_WAY, |
| "myPropC", |
| -1); |
| assertProperty( |
| properties[3], |
| "prop-d", |
| findMainOffset("prop-d')"), |
| AngularPropertyKind.ONE_WAY_ONE_TIME, |
| "myPropD", |
| -1); |
| assertProperty( |
| properties[4], |
| "prop-e", |
| findMainOffset("prop-e')"), |
| AngularPropertyKind.TWO_WAY, |
| "myPropE", |
| -1); |
| } |
| |
| public void test_NgComponent_properties_fromMap() throws Exception { |
| contextHelper.addSource("/my_template.html", ""); |
| contextHelper.addSource("/my_styles.css", ""); |
| resolveMainSourceNoErrors(createAngularSource(// |
| "@Component(publishAs: 'ctrl', selector: 'myComp',", |
| " templateUrl: 'my_template.html', cssUrl: 'my_styles.css',", |
| " map: const {", |
| " 'prop-a' : '@myPropA',", |
| " 'prop-b' : '&myPropB',", |
| " 'prop-c' : '=>myPropC',", |
| " 'prop-d' : '=>!myPropD',", |
| " 'prop-e' : '<=>myPropE'", |
| " })", |
| "class MyComponent {", |
| " var myPropA;", |
| " var myPropB;", |
| " var myPropC;", |
| " var myPropD;", |
| " var myPropE;", |
| "}")); |
| // prepare AngularComponentElement |
| ClassElement classElement = mainUnitElement.getType("MyComponent"); |
| AngularComponentElement component = getAngularElement( |
| classElement, |
| AngularComponentElement.class); |
| assertNotNull(component); |
| // verify |
| AngularPropertyElement[] properties = component.getProperties(); |
| assertLength(5, properties); |
| assertProperty( |
| properties[0], |
| "prop-a", |
| findMainOffset("prop-a' :"), |
| AngularPropertyKind.ATTR, |
| "myPropA", |
| findMainOffset("myPropA'")); |
| assertProperty( |
| properties[1], |
| "prop-b", |
| findMainOffset("prop-b' :"), |
| AngularPropertyKind.CALLBACK, |
| "myPropB", |
| findMainOffset("myPropB'")); |
| assertProperty( |
| properties[2], |
| "prop-c", |
| findMainOffset("prop-c' :"), |
| AngularPropertyKind.ONE_WAY, |
| "myPropC", |
| findMainOffset("myPropC'")); |
| assertProperty( |
| properties[3], |
| "prop-d", |
| findMainOffset("prop-d' :"), |
| AngularPropertyKind.ONE_WAY_ONE_TIME, |
| "myPropD", |
| findMainOffset("myPropD'")); |
| assertProperty( |
| properties[4], |
| "prop-e", |
| findMainOffset("prop-e' :"), |
| AngularPropertyKind.TWO_WAY, |
| "myPropE", |
| findMainOffset("myPropE'")); |
| } |
| |
| public void test_NgComponent_properties_no() throws Exception { |
| contextHelper.addSource("/my_template.html", ""); |
| contextHelper.addSource("/my_styles.css", ""); |
| String mainContent = createAngularSource(// |
| "@Component(publishAs: 'ctrl', selector: 'myComp',", |
| " templateUrl: 'my_template.html', cssUrl: 'my_styles.css')", |
| "class MyComponent {", |
| "}"); |
| resolveMainSourceNoErrors(mainContent); |
| // prepare AngularComponentElement |
| ClassElement classElement = mainUnitElement.getType("MyComponent"); |
| AngularComponentElement component = getAngularElement( |
| classElement, |
| AngularComponentElement.class); |
| assertNotNull(component); |
| // verify |
| assertEquals("ctrl", component.getName()); |
| assertEquals(findOffset(mainContent, "ctrl'"), component.getNameOffset()); |
| assertIsTagSelector(component.getSelector(), "myComp"); |
| assertEquals("my_template.html", component.getTemplateUri()); |
| assertEquals(findOffset(mainContent, "my_template.html'"), component.getTemplateUriOffset()); |
| assertEquals("my_styles.css", component.getStyleUri()); |
| assertEquals(findOffset(mainContent, "my_styles.css'"), component.getStyleUriOffset()); |
| assertLength(0, component.getProperties()); |
| } |
| |
| public void test_NgComponent_scopeProperties() throws Exception { |
| contextHelper.addSource("/my_template.html", ""); |
| contextHelper.addSource("/my_styles.css", ""); |
| String mainContent = createAngularSource(// |
| "@Component(publishAs: 'ctrl', selector: 'myComp',", |
| " templateUrl: 'my_template.html', cssUrl: 'my_styles.css')", |
| "class MyComponent {", |
| " MyComponent(Scope scope) {", |
| " scope.context['boolProp'] = true;", |
| " scope.context['intProp'] = 42;", |
| " scope.context['stringProp'] = 'foo';", |
| " // duplicate is ignored", |
| " scope.context['boolProp'] = true;", |
| " // LHS is not an IndexExpression", |
| " var v1;", |
| " v1 = 1;", |
| " // LHS is not a Scope access", |
| " var v2;", |
| " v2['name'] = 2;", |
| " }", |
| "}"); |
| resolveMainSourceNoErrors(mainContent); |
| // prepare AngularComponentElement |
| ClassElement classElement = mainUnitElement.getType("MyComponent"); |
| AngularComponentElement component = getAngularElement( |
| classElement, |
| AngularComponentElement.class); |
| assertNotNull(component); |
| // verify |
| AngularScopePropertyElement[] scopeProperties = component.getScopeProperties(); |
| assertLength(3, scopeProperties); |
| { |
| AngularScopePropertyElement property = scopeProperties[0]; |
| assertSame(property, findMainElement("boolProp")); |
| assertEquals("boolProp", property.getName()); |
| assertEquals(findOffset(mainContent, "boolProp'"), property.getNameOffset()); |
| assertEquals("bool", property.getType().getName()); |
| } |
| { |
| AngularScopePropertyElement property = scopeProperties[1]; |
| assertSame(property, findMainElement("intProp")); |
| assertEquals("intProp", property.getName()); |
| assertEquals(findOffset(mainContent, "intProp'"), property.getNameOffset()); |
| assertEquals("int", property.getType().getName()); |
| } |
| { |
| AngularScopePropertyElement property = scopeProperties[2]; |
| assertSame(property, findMainElement("stringProp")); |
| assertEquals("stringProp", property.getName()); |
| assertEquals(findOffset(mainContent, "stringProp'"), property.getNameOffset()); |
| assertEquals("String", property.getType().getName()); |
| } |
| } |
| |
| public void test_NgController() throws Exception { |
| String mainContent = createAngularSource(// |
| "@Controller(publishAs: 'ctrl', selector: '[myApp]')", |
| "class MyController {", |
| "}"); |
| resolveMainSourceNoErrors(mainContent); |
| // prepare AngularControllerElement |
| ClassElement classElement = mainUnitElement.getType("MyController"); |
| AngularControllerElement controller = getAngularElement( |
| classElement, |
| AngularControllerElement.class); |
| assertNotNull(controller); |
| // verify |
| assertEquals("ctrl", controller.getName()); |
| assertEquals(findOffset(mainContent, "ctrl'"), controller.getNameOffset()); |
| assertHasAttributeSelector(controller.getSelector(), "myApp"); |
| } |
| |
| public void test_NgController_cannotParseSelector() throws Exception { |
| String mainContent = createAngularSource(// |
| "@Controller(publishAs: 'ctrl', selector: '~unknown')", |
| "class MyController {", |
| "}"); |
| resolveMainSource(mainContent); |
| // has error |
| assertMainErrors(AngularCode.CANNOT_PARSE_SELECTOR); |
| } |
| |
| public void test_NgController_missingPublishAs() throws Exception { |
| String mainContent = createAngularSource(// |
| "@Controller(selector: '[myApp]')", |
| "class MyController {", |
| "}"); |
| resolveMainSource(mainContent); |
| // has error |
| assertMainErrors(AngularCode.MISSING_PUBLISH_AS); |
| } |
| |
| public void test_NgController_missingSelector() throws Exception { |
| String mainContent = createAngularSource(// |
| "@Controller(publishAs: 'ctrl')", |
| "class MyController {", |
| "}"); |
| resolveMainSource(mainContent); |
| // has error |
| assertMainErrors(AngularCode.MISSING_SELECTOR); |
| } |
| |
| public void test_NgController_noAnnotationArguments() throws Exception { |
| String mainContent = createAngularSource(// |
| "@NgController", |
| "class MyController {", |
| "}"); |
| resolveMainSource(mainContent); |
| // ignore errors, but there should be no exceptions |
| } |
| |
| public void test_parseSelector_hasAttribute() throws Exception { |
| AngularSelectorElement selector = AngularCompilationUnitBuilder.parseSelector(42, "[name]"); |
| assertHasAttributeSelector(selector, "name"); |
| assertEquals(42 + 1, selector.getNameOffset()); |
| } |
| |
| public void test_parseSelector_hasClass() throws Exception { |
| AngularSelectorElement selector = AngularCompilationUnitBuilder.parseSelector(42, ".my-class"); |
| AngularHasClassSelectorElementImpl classSelector = (AngularHasClassSelectorElementImpl) selector; |
| assertEquals("my-class", classSelector.getName()); |
| assertEquals(".my-class", classSelector.toString()); |
| assertEquals(42 + 1, selector.getNameOffset()); |
| // test apply() |
| { |
| XmlTagNode node = tagNode("div", attribute("class", "one two")); |
| assertFalse(classSelector.apply(node)); |
| } |
| { |
| XmlTagNode node = tagNode("div", attribute("class", "one my-class two")); |
| assertTrue(classSelector.apply(node)); |
| } |
| } |
| |
| public void test_parseSelector_isTag() throws Exception { |
| AngularSelectorElement selector = AngularCompilationUnitBuilder.parseSelector(42, "name"); |
| assertIsTagSelector(selector, "name"); |
| assertEquals(42, selector.getNameOffset()); |
| } |
| |
| public void test_parseSelector_isTag_hasAttribute() throws Exception { |
| AngularSelectorElement selector = AngularCompilationUnitBuilder.parseSelector(42, "tag[attr]"); |
| assertInstanceOf(IsTagHasAttributeSelectorElementImpl.class, selector); |
| assertEquals("tag[attr]", selector.getName()); |
| assertEquals(-1, selector.getNameOffset()); |
| assertEquals("tag", ((IsTagHasAttributeSelectorElementImpl) selector).getTagName()); |
| assertEquals("attr", ((IsTagHasAttributeSelectorElementImpl) selector).getAttributeName()); |
| } |
| |
| public void test_parseSelector_unknown() throws Exception { |
| AngularSelectorElement selector = AngularCompilationUnitBuilder.parseSelector(0, "~unknown"); |
| assertNull(selector); |
| } |
| |
| public void test_view() throws Exception { |
| contextHelper.addSource("/wrong.html", ""); |
| contextHelper.addSource("/my_templateA.html", ""); |
| contextHelper.addSource("/my_templateB.html", ""); |
| String mainContent = createAngularSource(// |
| "class MyRouteInitializer {", |
| " init(ViewFactory view, foo) {", |
| " foo.view('wrong.html'); // has target", |
| " foo(); // less than one argument", |
| " foo('wrong.html', 'bar'); // more than one argument", |
| " foo('wrong' + '.html'); // not literal", |
| " foo('wrong.html'); // not ViewFactory", |
| " view('my_templateA.html');", |
| " view('my_templateB.html');", |
| " }", |
| "}"); |
| resolveMainSourceNoErrors(mainContent); |
| // prepare AngularViewElement(s) |
| AngularViewElement[] views = mainUnitElement.getAngularViews(); |
| assertLength(2, views); |
| { |
| AngularViewElement view = views[0]; |
| assertEquals("my_templateA.html", view.getTemplateUri()); |
| assertEquals(null, view.getName()); |
| assertEquals(-1, view.getNameOffset()); |
| assertEquals(findOffset(mainContent, "my_templateA.html'"), view.getTemplateUriOffset()); |
| } |
| { |
| AngularViewElement view = views[1]; |
| assertEquals("my_templateB.html", view.getTemplateUri()); |
| assertEquals(null, view.getName()); |
| assertEquals(-1, view.getNameOffset()); |
| assertEquals(findOffset(mainContent, "my_templateB.html'"), view.getTemplateUriOffset()); |
| } |
| } |
| |
| private void assertProperty(AngularPropertyElement property, String expectedName, |
| int expectedNameOffset, AngularPropertyKind expectedKind, String expectedFieldName, |
| int expectedFieldOffset) { |
| assertEquals(expectedName, property.getName()); |
| assertEquals(expectedNameOffset, property.getNameOffset()); |
| assertSame(expectedKind, property.getPropertyKind()); |
| assertEquals(expectedFieldName, property.getField().getName()); |
| assertEquals(expectedFieldOffset, property.getFieldNameOffset()); |
| } |
| |
| /** |
| * Find {@link AstNode} of the given type in {@link #mainUnit}. |
| */ |
| private <T extends AstNode> T findMainNode(String search, Class<T> clazz) { |
| return findNode(mainUnit, mainContent, search, clazz); |
| } |
| } |