/*
* Copyright (c) 2013, 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.html.angular;
import com.google.dart.engine.ast.Expression;
import com.google.dart.engine.ast.SimpleIdentifier;
import com.google.dart.engine.context.ChangeSet;
import com.google.dart.engine.element.Element;
import com.google.dart.engine.element.ElementKind;
import com.google.dart.engine.element.FunctionElement;
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.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.error.AnalysisError;
import com.google.dart.engine.error.AngularCode;
import com.google.dart.engine.html.ast.HtmlUnitUtils;
import com.google.dart.engine.html.ast.XmlAttributeNode;
import com.google.dart.engine.html.ast.XmlTagNode;
import com.google.dart.engine.internal.element.CompilationUnitElementImpl;
import com.google.dart.engine.internal.element.FunctionElementImpl;
import com.google.dart.engine.internal.element.LocalVariableElementImpl;
import com.google.dart.engine.internal.element.angular.AngularControllerElementImpl;
import com.google.dart.engine.source.Source;
import static com.google.dart.engine.element.ElementFactory.classElement;
import static com.google.dart.engine.element.ElementFactory.compilationUnit;
import static com.google.dart.engine.element.ElementFactory.functionElement;
import static com.google.dart.engine.element.ElementFactory.localVariableElement;
public class AngularHtmlUnitResolverTest extends AngularTest {
public void test_analysisContext_changeDart_invalidateApplication() throws Exception {
addMainSource(createSource("",//
"import 'angular.dart';",
"",
"@Component(",
" templateUrl: 'my_template.html', cssUrl: 'my_styles.css',",
" publishAs: 'ctrl',",
" selector: 'myComponent')",
"class MyComponent {",
"}"));
contextHelper.addSource("/entry-point.html", createHtmlWithAngular());
addIndexSource("/my_template.html", createSource(//
" <div>",
" {{ctrl.noMethod()}}",
" </div>"));
contextHelper.addSource("/my_styles.css", "");
contextHelper.runTasks();
// there are some errors in my_template.html
{
AnalysisError[] errors = context.getErrors(indexSource).getErrors();
assertTrue(errors.length != 0);
}
// change main.dart, there are no MyComponent anymore
context.setContents(mainSource, "");
// ...errors in my_template.html should be removed
{
AnalysisError[] errors = context.getErrors(indexSource).getErrors();
assertTrue(errors.length == 0);
}
}
public void test_analysisContext_changeEntryPoint_clearAngularErrors_inDart() throws Exception {
addMainSource(createSource("",//
"import 'angular.dart';",
"",
"@Component(",
" templateUrl: 'no-such-template.html', cssUrl: 'my_styles.css',",
" publishAs: 'ctrl',",
" selector: 'myComponent')",
"class MyComponent {",
"}"));
Source entrySource = contextHelper.addSource("/entry-point.html", createHtmlWithAngular());
contextHelper.addSource("/my_styles.css", "");
contextHelper.runTasks();
// there are some errors in MyComponent
{
AnalysisError[] errors = context.getErrors(mainSource).getErrors();
assertTrue(errors.length != 0);
}
// make entry-point.html non-Angular
context.setContents(entrySource, "<html/>");
// ...errors in MyComponent should be removed
{
AnalysisError[] errors = context.getErrors(mainSource).getErrors();
assertTrue(errors.length == 0);
}
}
public void test_analysisContext_changeEntryPoint_clearAngularErrors_inTemplate()
throws Exception {
addMainSource(createSource("",//
"import 'angular.dart';",
"",
"@Component(",
" templateUrl: 'my_template.html', cssUrl: 'my_styles.css',",
" publishAs: 'ctrl',",
" selector: 'myComponent')",
"class MyComponent {",
"}"));
Source entrySource = contextHelper.addSource("/entry-point.html", createHtmlWithAngular());
addIndexSource("/my_template.html", createSource(//
" <div>",
" {{ctrl.noMethod()}}",
" </div>"));
contextHelper.addSource("/my_styles.css", "");
contextHelper.runTasks();
// there are some errors in my_template.html
{
AnalysisError[] errors = context.getErrors(indexSource).getErrors();
assertTrue(errors.length != 0);
}
// make entry-point.html non-Angular
context.setContents(entrySource, "<html/>");
// ...errors in my_template.html should be removed
{
AnalysisError[] errors = context.getErrors(indexSource).getErrors();
assertTrue(errors.length == 0);
}
}
public void test_analysisContext_removeEntryPoint_clearAngularErrors_inDart() throws Exception {
addMainSource(createSource("",//
"import 'angular.dart';",
"",
"@Component(",
" templateUrl: 'no-such-template.html', cssUrl: 'my_styles.css',",
" publishAs: 'ctrl',",
" selector: 'myComponent')",
"class MyComponent {",
"}"));
Source entrySource = contextHelper.addSource("/entry-point.html", createHtmlWithAngular());
contextHelper.addSource("/my_styles.css", "");
contextHelper.runTasks();
// there are some errors in MyComponent
{
AnalysisError[] errors = context.getErrors(mainSource).getErrors();
assertTrue(errors.length != 0);
}
// remove entry-point.html
{
ChangeSet changeSet = new ChangeSet();
changeSet.removedSource(entrySource);
context.applyChanges(changeSet);
}
// ...errors in MyComponent should be removed
{
AnalysisError[] errors = context.getErrors(mainSource).getErrors();
assertTrue(errors.length == 0);
}
}
public void test_contextProperties() throws Exception {
addMyController();
resolveIndexNoErrors(createHtmlWithAngular(//
"<div>",
" {{$id}}",
" {{$parent}}",
" {{$root}}",
"</div>"));
assertResolvedIdentifier("$id");
assertResolvedIdentifier("$parent");
assertResolvedIdentifier("$root");
}
public void test_getAngularElement_isAngular() throws Exception {
// prepare local variable "name" in compilation unit
CompilationUnitElementImpl unit = compilationUnit("test.dart");
FunctionElementImpl function = functionElement("main");
unit.setFunctions(new FunctionElement[] {function});
LocalVariableElementImpl local = localVariableElement("name");
function.setLocalVariables(new LocalVariableElement[] {local});
// set AngularElement
AngularElement angularElement = new AngularControllerElementImpl("ctrl", 0);
local.setToolkitObjects(new AngularElement[] {angularElement});
assertSame(angularElement, AngularHtmlUnitResolver.getAngularElement(local));
}
public void test_getAngularElement_notAngular() throws Exception {
Element element = localVariableElement("name");
assertNull(AngularHtmlUnitResolver.getAngularElement(element));
}
public void test_getAngularElement_notLocal() throws Exception {
Element element = classElement("Test");
assertNull(AngularHtmlUnitResolver.getAngularElement(element));
}
/**
* Test that we resolve "ng-click" expression.
*/
public void test_ngClick() throws Exception {
addMyController();
resolveIndexNoErrors(createHtmlWithMyController("<button ng-click='ctrl.doSomething($event)'/>"));
assertResolvedIdentifier("doSomething");
}
public void test_NgComponent_resolveTemplateFile() throws Exception {
addMainSource(createSource("",//
"import 'angular.dart';",
"",
"@Component(",
" templateUrl: 'my_template.html', cssUrl: 'my_styles.css',",
" publishAs: 'ctrl',",
" selector: 'myComponent')",
"class MyComponent {",
" String field;",
"}"));
contextHelper.addSource("/entry-point.html", createHtmlWithAngular());
addIndexSource("/my_template.html", createSource(//
" <div>",
" {{ctrl.field}}",
" </div>"));
contextHelper.addSource("/my_styles.css", "");
contextHelper.runTasks();
resolveIndex();
assertNoErrors();
assertResolvedIdentifier("ctrl.", "MyComponent");
assertResolvedIdentifier("field}}", "String");
}
public void test_NgComponent_updateDartFile() throws Exception {
Source componentSource = contextHelper.addSource("/my_component.dart", createSource(//
"library my.component;",
"import 'angular.dart';",
"@Component(selector: 'myComponent')",
"class MyComponent {",
"}"));
contextHelper.addSource(
"/my_module.dart",
createSource("library my.module;", "import 'my_component.dart';"));
addMainSource(createSource("library main;", "import 'my_module.dart';"));
resolveIndexNoErrors(createHtmlWithMyController("<myComponent/>"));
// "myComponent" tag was resolved
{
XmlTagNode tagNode = HtmlUnitUtils.getTagNode(indexUnit, findOffset("myComponent"));
AngularSelectorElement tagElement = (AngularSelectorElement) tagNode.getElement();
assertNotNull(tagElement);
assertEquals("myComponent", tagElement.getName());
}
// replace "myComponent" with "myComponent2" in my_component.dart and index.html
{
context.setContents(
componentSource,
getSourceContent(componentSource).replace("myComponent", "myComponent2"));
indexContent = getSourceContent(indexSource).replace("myComponent", "myComponent2");
context.setContents(indexSource, indexContent);
}
contextHelper.runTasks();
resolveIndex();
// "myComponent2" tag should be resolved
{
XmlTagNode tagNode = HtmlUnitUtils.getTagNode(indexUnit, findOffset("myComponent2"));
AngularSelectorElement tagElement = (AngularSelectorElement) tagNode.getElement();
assertNotNull(tagElement);
assertEquals("myComponent2", tagElement.getName());
}
}
public void test_NgComponent_use_resolveAttributes() throws Exception {
contextHelper.addSource("/my_template.html", createSource(//
" <div>",
" {{ctrl.field}}",
" </div>"));
addMainSource(createSource("",//
"import 'angular.dart';",
"",
"@Component(",
" templateUrl: 'my_template.html', cssUrl: 'my_styles.css',",
" publishAs: 'ctrl',",
" selector: 'myComponent', // selector",
" map: const {'attrA' : '=>setA', 'attrB' : '@setB'})",
"class MyComponent {",
" set setA(value) {}",
" set setB(value) {}",
"}"));
resolveIndexNoErrors(createHtmlWithMyController(//
"<input type='text' ng-model='someModel'/>",
"<myComponent attrA='someModel' attrB='bbb'/>"));
// "attrA" attribute expression was resolved
assertNotNull(findIdentifier("someModel"));
// "myComponent" tag was resolved
XmlTagNode tagNode = HtmlUnitUtils.getTagNode(indexUnit, findOffset("myComponent"));
AngularSelectorElement tagElement = (AngularSelectorElement) tagNode.getElement();
assertNotNull(tagElement);
assertEquals("myComponent", tagElement.getName());
assertEquals(findMainOffset("myComponent', // selector"), tagElement.getNameOffset());
// "attrA" attribute was resolved
{
XmlAttributeNode node = HtmlUnitUtils.getAttributeNode(indexUnit, findOffset("attrA='"));
AngularPropertyElement element = (AngularPropertyElement) node.getElement();
assertNotNull(element);
assertEquals("attrA", element.getName());
assertEquals("setA", element.getField().getName());
}
// "attrB" attribute was resolved, even if it @binding
{
XmlAttributeNode node = HtmlUnitUtils.getAttributeNode(indexUnit, findOffset("attrB='"));
AngularPropertyElement element = (AngularPropertyElement) node.getElement();
assertNotNull(element);
assertEquals("attrB", element.getName());
assertEquals("setB", element.getField().getName());
}
}
public void test_NgDirective_noAttribute() throws Exception {
addMainSource(createSource("",//
"import 'angular.dart';",
"",
"@NgDirective(selector: '[my-directive]', map: const {'foo': '=>input'})",
"class MyDirective {",
" set input(value) {}",
"}"));
resolveIndexNoErrors(createHtmlWithMyController(//
"<div my-directive>",
"</div>"));
// no "foo" attribute, but it is OK
}
public void test_NgDirective_noExpression() throws Exception {
addMainSource(createSource("",//
"import 'angular.dart';",
"",
"@NgDirective(selector: '[my-directive]', map: const {'.': '=>input'})",
"class MyDirective {",
" set input(value) {}",
"}"));
resolveIndexNoErrors(createHtmlWithMyController(//
"<div my-directive>",
"</div>"));
}
public void test_NgDirective_resolvedExpression() throws Exception {
addMainSource(createSource("",//
"import 'angular.dart';",
"",
"@Decorator(selector: '[my-directive]')",
"class MyDirective {",
" @NgOneWay('my-property')",
" String condition;",
"}"));
resolveIndexNoErrors(createHtmlWithMyController(//
"<input type='text' ng-model='name'>",
"<div my-directive my-property='name != null'>",
"</div>"));
resolveMainNoErrors();
// "my-directive" attribute was resolved
{
AngularSelectorElement selector = findMainElement(
ElementKind.ANGULAR_SELECTOR,
"my-directive");
XmlAttributeNode attrNodeSelector = HtmlUnitUtils.getAttributeNode(
indexUnit,
findOffset("my-directive"));
assertNotNull(attrNodeSelector);
assertSame(selector, attrNodeSelector.getElement());
}
// "my-property" attribute was resolved
{
XmlAttributeNode attrNodeProperty = HtmlUnitUtils.getAttributeNode(
indexUnit,
findOffset("my-property='"));
AngularPropertyElement propertyElement = (AngularPropertyElement) attrNodeProperty.getElement();
assertNotNull(propertyElement);
assertSame(AngularPropertyKind.ONE_WAY, propertyElement.getPropertyKind());
assertEquals("condition", propertyElement.getField().getName());
}
// "name" expression was resolved
assertNotNull(findIdentifier("name != null"));
}
public void test_NgDirective_resolvedExpression_attrString() throws Exception {
addMainSource(createSource("",//
"import 'angular.dart';",
"",
"@NgDirective(selector: '[my-directive])",
"class MyDirective {",
" @NgAttr('my-property')",
" String property;",
"}"));
resolveIndexNoErrors(createHtmlWithMyController(//
"<input type='text' ng-model='name'>",
"<div my-directive my-property='name != null'>",
"</div>"));
resolveMain();
// @NgAttr means "string attribute", which we don't parse
assertNull(findIdentifierMaybe("name != null"));
}
public void test_NgDirective_resolvedExpression_dotAsName() throws Exception {
addMainSource(createSource("",//
"import 'angular.dart';",
"",
"@Decorator(",
" selector: '[my-directive]',",
" map: const {'.' : '=>condition'})",
"class MyDirective {",
" set condition(value) {}",
"}"));
resolveIndexNoErrors(createHtmlWithMyController(//
"<input type='text' ng-model='name'>",
"<div my-directive='name != null'>",
"</div>"));
// "name" attribute was resolved
assertNotNull(findIdentifier("name != null"));
}
/**
* Test that we resolve "ng-if" expression.
*/
public void test_ngIf() throws Exception {
addMyController();
resolveIndexNoErrors(createHtmlWithMyController("<div ng-if='ctrl.field != null'/>"));
assertResolvedIdentifier("field");
}
public void test_ngModel_modelAfterUsage() throws Exception {
addMyController();
resolveIndexNoErrors(createHtmlWithMyController(//
"<h3>Hello {{name}}!</h3>",
"<input type='text' ng-model='name'>"));
assertResolvedIdentifier("name}}!", "String");
assertResolvedIdentifier("name'>", "String");
}
public void test_ngModel_modelBeforeUsage() throws Exception {
addMyController();
resolveIndexNoErrors(createHtmlWithMyController(//
"<input type='text' ng-model='name'>",
"<h3>Hello {{name}}!</h3>"));
assertResolvedIdentifier("name}}!", "String");
Element element = assertResolvedIdentifier("name'>", "String");
assertEquals("name", element.getName());
assertEquals(findOffset("name'>"), element.getNameOffset());
}
public void test_ngModel_notIdentifier() throws Exception {
addMyController();
resolveIndexNoErrors(createHtmlWithMyController("<input type='text' ng-model='ctrl.field'>"));
assertResolvedIdentifier("field'>", "String");
}
/**
* Test that we resolve "ng-mouseout" expression.
*/
public void test_ngMouseOut() throws Exception {
addMyController();
resolveIndexNoErrors(createHtmlWithMyController("<button ng-mouseout='ctrl.doSomething($event)'/>"));
assertResolvedIdentifier("doSomething");
}
public void test_ngRepeat_additionalVariables() throws Exception {
addMyController();
resolveIndexNoErrors(createHtmlWithMyController(//
"<li ng-repeat='name in ctrl.names'>",
" {{$index}} {{$first}} {{$middle}} {{$last}} {{$even}} {{$odd}}",
"</li>"));
assertResolvedIdentifier("$index", "int");
assertResolvedIdentifier("$first", "bool");
assertResolvedIdentifier("$middle", "bool");
assertResolvedIdentifier("$last", "bool");
assertResolvedIdentifier("$even", "bool");
assertResolvedIdentifier("$odd", "bool");
}
public void test_ngRepeat_bad_expectedIdentifier() throws Exception {
addMyController();
resolveIndex(createHtmlWithMyController(//
"<li ng-repeat='name + 42 in ctrl.names'>",
"</li>"));
assertErrors(indexSource, AngularCode.INVALID_REPEAT_ITEM_SYNTAX);
}
public void test_ngRepeat_bad_expectedIn() throws Exception {
addMyController();
resolveIndex(createHtmlWithMyController(//
"<li ng-repeat='name : ctrl.names'>",
"</li>"));
assertErrors(indexSource, AngularCode.INVALID_REPEAT_SYNTAX);
}
public void test_ngRepeat_filters_filter_literal() throws Exception {
addMyController();
resolveIndexNoErrors(createHtmlWithMyController(//
"<li ng-repeat='item in ctrl.items | filter:42:null'/>",
"</li>"));
// filter "filter" is resolved
Element filterElement = assertResolvedIdentifier("filter");
assertInstanceOf(AngularFormatterElement.class, filterElement);
}
public void test_ngRepeat_filters_filter_propertyMap() throws Exception {
addMyController();
resolveIndexNoErrors(createHtmlWithMyController(//
"<li ng-repeat='item in ctrl.items | filter:{name:null, done:false}'/>",
"</li>"));
assertResolvedIdentifier("name:", "String");
assertResolvedIdentifier("done:", "bool");
}
public void test_ngRepeat_filters_missingColon() throws Exception {
addMyController();
resolveIndex(createHtmlWithMyController(//
"<li ng-repeat=\"item in ctrl.items | orderBy:'' true\"/>",
"</li>"));
assertErrors(indexSource, AngularCode.MISSING_FORMATTER_COLON);
}
public void test_ngRepeat_filters_noArgs() throws Exception {
addMyController();
resolveIndexNoErrors(createHtmlWithMyController(//
"<li ng-repeat=\"item in ctrl.items | orderBy\"/>",
"</li>"));
// filter "orderBy" is resolved
Element filterElement = assertResolvedIdentifier("orderBy");
assertInstanceOf(AngularFormatterElement.class, filterElement);
}
public void test_ngRepeat_filters_orderBy_emptyString() throws Exception {
addMyController();
resolveIndexNoErrors(createHtmlWithMyController(//
"<li ng-repeat=\"item in ctrl.items | orderBy:'':true\"/>",
"</li>"));
// filter "orderBy" is resolved
Element filterElement = assertResolvedIdentifier("orderBy");
assertInstanceOf(AngularFormatterElement.class, filterElement);
}
public void test_ngRepeat_filters_orderBy_propertyList() throws Exception {
addMyController();
resolveIndexNoErrors(createHtmlWithMyController(//
"<li ng-repeat=\"item in ctrl.items | orderBy:['name', 'done']\"/>",
"</li>"));
assertResolvedIdentifier("name'", "String");
assertResolvedIdentifier("done'", "bool");
}
public void test_ngRepeat_filters_orderBy_propertyName() throws Exception {
addMyController();
resolveIndexNoErrors(createHtmlWithMyController(//
"<li ng-repeat=\"item in ctrl.items | orderBy:'name'\"/>",
"</li>"));
assertResolvedIdentifier("name'", "String");
}
public void test_ngRepeat_filters_orderBy_propertyName_minus() throws Exception {
addMyController();
resolveIndexNoErrors(createHtmlWithMyController(//
"<li ng-repeat=\"item in ctrl.items | orderBy:'-name'\"/>",
"</li>"));
assertResolvedIdentifier("name'", "String");
}
public void test_ngRepeat_filters_orderBy_propertyName_plus() throws Exception {
addMyController();
resolveIndexNoErrors(createHtmlWithMyController(//
"<li ng-repeat=\"item in ctrl.items | orderBy:'+name'\"/>",
"</li>"));
assertResolvedIdentifier("name'", "String");
}
public void test_ngRepeat_filters_orderBy_propertyName_untypedItems() throws Exception {
addMyController();
resolveIndexNoErrors(createHtmlWithMyController(//
"<li ng-repeat=\"item in ctrl.untypedItems | orderBy:'name'\"/>",
"</li>"));
assertResolvedIdentifier("name'", "dynamic");
}
public void test_ngRepeat_filters_two() throws Exception {
addMyController();
resolveIndexNoErrors(createHtmlWithMyController(//
"<li ng-repeat=\"item in ctrl.items | orderBy:'+' | orderBy:'-'\"/>",
"</li>"));
assertInstanceOf(AngularFormatterElement.class, assertResolvedIdentifier("orderBy:'+'"));
assertInstanceOf(AngularFormatterElement.class, assertResolvedIdentifier("orderBy:'-'"));
}
public void test_ngRepeat_resolvedExpressions() throws Exception {
addMyController();
resolveIndexNoErrors(createHtmlWithMyController(//
"<li ng-repeat='name in ctrl.names'>",
" {{name}}",
"</li>"));
assertResolvedIdentifier("name in", "String");
assertResolvedIdentifier("ctrl.", "MyController");
assertResolvedIdentifier("names'", "List<String>");
assertResolvedIdentifier("name}}", "String");
}
public void test_ngRepeat_trackBy() throws Exception {
addMyController();
resolveIndexNoErrors(createHtmlWithMyController(//
"<li ng-repeat='name in ctrl.names track by name.length'/>",
"</li>"));
assertResolvedIdentifier("length'", "int");
}
/**
* Test that we resolve "ng-show" expression.
*/
public void test_ngShow() throws Exception {
addMyController();
resolveIndexNoErrors(createHtmlWithMyController("<div ng-show='ctrl.field != null'/>"));
assertResolvedIdentifier("field");
}
public void test_notResolved_noDartScript() throws Exception {
resolveIndex(createSource(//
"<html ng-app>",
" <body>",
" <div my-marker>",
" {{ctrl.field}}",
" </div>",
" </body>",
"</html>"));
assertNoErrors();
// Angular is not initialized, so "ctrl" is not parsed
Expression expression = HtmlUnitUtils.getExpression(indexUnit, findOffset("ctrl"));
assertNull(expression);
}
public void test_notResolved_notAngular() throws Exception {
resolveIndex(createSource(//
"<html no-ng-app>",
" <body>",
" <div my-marker>",
" {{ctrl.field}}",
" </div>",
" </body>",
"</html>"));
assertNoErrors();
// Angular is not initialized, so "ctrl" is not parsed
Expression expression = HtmlUnitUtils.getExpression(indexUnit, findOffset("ctrl"));
assertNull(expression);
}
public void test_notResolved_wrongControllerMarker() throws Exception {
addMyController();
addIndexSource(createSource(//
"<html ng-app>",
" <body>",
" <div not-my-marker>",
" {{ctrl.field}}",
" </div>",
" <script type='application/dart' src='main.dart'></script>",
" </body>",
"</html>"));
contextHelper.runTasks();
resolveIndex();
// no errors, because we decided to ignore them at the moment
assertNoErrors();
// "ctrl" is not resolved
SimpleIdentifier identifier = findIdentifier("ctrl");
assertNull(identifier.getBestElement());
}
public void test_resolveExpression_evenWithout_ngBootstrap() throws Exception {
resolveMainSource(createSource("",//
"import 'angular.dart';",
"",
"@Controller(",
" selector: '[my-controller]',",
" publishAs: 'ctrl')",
"class MyController {",
" String field;",
"}"));
resolveIndexNoErrors(createSource(//
"<html ng-app>",
" <body>",
" <div my-controller>",
" {{ctrl.field}}",
" </div>",
" <script type='application/dart' src='main.dart'></script>",
" </body>",
"</html>"));
assertResolvedIdentifier("ctrl.", "MyController");
}
public void test_resolveExpression_ignoreUnresolved() throws Exception {
resolveMainSource(createSource("",//
"import 'angular.dart';",
"",
"@Controller(",
" selector: '[my-controller]',",
" publishAs: 'ctrl')",
"class MyController {",
" Map map;",
" Object obj;",
"}"));
resolveIndex(createSource(//
"<html ng-app>",
" <body>",
" <div my-controller>",
" {{ctrl.map.property}}",
" {{ctrl.obj.property}}",
" {{invisibleScopeProperty}}",
" </div>",
" <script type='application/dart' src='main.dart'></script>",
" </body>",
"</html>"));
assertNoErrors();
// "ctrl.map" and "ctrl.obj" are resolved
assertResolvedIdentifier("map", "Map<dynamic, dynamic>");
assertResolvedIdentifier("obj", "Object");
// ...but not "invisibleScopeProperty"
{
SimpleIdentifier identifier = findIdentifier("invisibleScopeProperty");
assertNull(identifier.getBestElement());
}
}
public void test_resolveExpression_inAttribute() throws Exception {
addMyController();
resolveIndexNoErrors(createHtmlWithMyController("<button title='{{ctrl.field}}'></button>"));
assertResolvedIdentifier("ctrl", "MyController");
}
public void test_resolveExpression_ngApp_onBody() throws Exception {
addMyController();
resolveIndexNoErrors(createSource(//
"<html>",
" <body ng-app>",
" <div my-controller>",
" {{ctrl.field}}",
" </div>",
" <script type='application/dart' src='main.dart'></script>",
" </body>",
"</html>"));
assertResolvedIdentifier("ctrl", "MyController");
}
public void test_resolveExpression_withFilter() throws Exception {
addMyController();
resolveIndexNoErrors(createHtmlWithMyController("{{ctrl.field | uppercase}}"));
assertResolvedIdentifier("ctrl", "MyController");
assertResolvedIdentifier("uppercase");
}
public void test_resolveExpression_withFilter_missingColon() throws Exception {
addMyController();
resolveIndex(createHtmlWithMyController("{{ctrl.field | uppercase, lowercase}}"));
assertErrors(indexSource, AngularCode.MISSING_FORMATTER_COLON);
}
public void test_resolveExpression_withFilter_notSimpleIdentifier() throws Exception {
addMyController();
resolveIndex(createHtmlWithMyController("{{ctrl.field | not.supported}}"));
assertErrors(indexSource, AngularCode.INVALID_FORMATTER_NAME);
}
public void test_scopeProperties() throws Exception {
addMainSource(createSource("",//
"import 'angular.dart';",
"",
"@Component(",
" templateUrl: 'my_template.html', cssUrl: 'my_styles.css',",
" publishAs: 'ctrl',",
" selector: 'myComponent')",
"class MyComponent {",
" String field;",
" MyComponent(Scope scope) {",
" scope.context['scopeProperty'] = 'abc';",
" }",
"}",
""));
contextHelper.addSource("/entry-point.html", createHtmlWithAngular());
addIndexSource("/my_template.html", createSource(//
" <div>",
" {{scopeProperty}}",
" </div>"));
contextHelper.addSource("/my_styles.css", "");
contextHelper.runTasks();
resolveIndex();
assertNoErrors();
// "scopeProperty" is resolved
Element element = assertResolvedIdentifier("scopeProperty}}", "String");
assertInstanceOf(
AngularScopePropertyElement.class,
AngularHtmlUnitResolver.getAngularElement(element));
}
public void test_scopeProperties_hideWithComponent() throws Exception {
addMainSource(createSource("",//
"import 'angular.dart';",
"",
"@Component(",
" templateUrl: 'my_template.html', cssUrl: 'my_styles.css',",
" publishAs: 'ctrl',",
" selector: 'myComponent')",
"class MyComponent {",
"}",
"",
"void setScopeProperties(Scope scope) {",
" scope.context['ctrl'] = 1;",
"}",
""));
contextHelper.addSource("/entry-point.html", createHtmlWithAngular());
addIndexSource("/my_template.html", createSource(//
" <div>",
" {{ctrl}}",
" </div>"));
contextHelper.addSource("/my_styles.css", "");
contextHelper.runTasks();
resolveIndex();
assertNoErrors();
// "ctrl" is resolved
LocalVariableElement element = (LocalVariableElement) assertResolvedIdentifier("ctrl}}");
ToolkitObjectElement[] toolkitObjects = element.getToolkitObjects();
assertInstanceOf(AngularComponentElement.class, toolkitObjects[0]);
}
public void test_view_resolveTemplateFile() throws Exception {
addMainSource(createSource("",//
"import 'angular.dart';",
"",
"@Controller(",
" selector: '[my-controller]',",
" publishAs: 'ctrl')",
"class MyController {",
" String field;",
"}",
"",
"class MyRouteInitializer {",
" init(ViewFactory view) {",
" view('my_template.html');",
" }",
"}"));
contextHelper.addSource("/entry-point.html", createHtmlWithAngular());
addIndexSource("/my_template.html", createSource(//
" <div my-controller>",
" {{ctrl.field}}",
" </div>"));
contextHelper.addSource("/my_styles.css", "");
contextHelper.runTasks();
resolveIndex();
assertNoErrors();
assertResolvedIdentifier("ctrl.", "MyController");
assertResolvedIdentifier("field}}", "String");
}
private String getSourceContent(Source source) throws Exception {
return context.getContents(source).getData().toString();
}
private void resolveIndexNoErrors(String content) throws Exception {
resolveIndex(content);
assertNoErrors();
verify(indexSource);
}
}