/* * 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.services.internal.refactoring; import com.google.dart.engine.ast.CompilationUnit; import com.google.dart.engine.context.AnalysisContext; import com.google.dart.engine.element.Element; import com.google.dart.engine.element.ElementKind; import com.google.dart.engine.element.angular.AngularComponentElement; import com.google.dart.engine.element.angular.AngularControllerElement; import com.google.dart.engine.element.angular.AngularFormatterElement; import com.google.dart.engine.element.angular.AngularHasAttributeSelectorElement; import com.google.dart.engine.element.angular.AngularPropertyElement; import com.google.dart.engine.element.angular.AngularScopePropertyElement; import com.google.dart.engine.element.angular.AngularTagSelectorElement; import com.google.dart.engine.html.ast.HtmlUnit; import com.google.dart.engine.index.Index; import com.google.dart.engine.index.IndexFactory; import com.google.dart.engine.internal.html.angular.AngularTest; import com.google.dart.engine.internal.index.file.MemoryNodeManager; import com.google.dart.engine.search.SearchEngine; import com.google.dart.engine.search.SearchEngineFactory; import com.google.dart.engine.services.change.Change; import com.google.dart.engine.services.refactoring.NullProgressMonitor; import com.google.dart.engine.services.refactoring.ProgressMonitor; import com.google.dart.engine.services.refactoring.RefactoringFactory; import com.google.dart.engine.services.refactoring.RenameRefactoring; import com.google.dart.engine.services.status.RefactoringStatus; import com.google.dart.engine.services.status.RefactoringStatusSeverity; import static com.google.dart.engine.services.internal.correction.AbstractDartTest.assertRefactoringStatus; import static com.google.dart.engine.services.internal.correction.AbstractDartTest.assertRefactoringStatusOK; import static com.google.dart.engine.services.internal.refactoring.RefactoringImplTest.assertChangeResult; import static com.google.dart.engine.services.internal.refactoring.RefactoringImplTest.assertRefactoringStatusOK; /** * Test for Angular related rename refactorings - for both Dart and Angular elements. */ public class AngularRenameRefactoringTest extends AngularTest { private static final ProgressMonitor PM = new NullProgressMonitor(); private Index index; private SearchEngine searchEngine; private RenameRefactoring refactoring; private Change refactoringChange; public void test_angular_renameAttributeSelector() throws Exception { contextHelper.addSource("/my_template.html", createSource(// " <div>", " {{ctrl.field}}", " </div>")); addMainSource(createSource("",// "import 'angular.dart';", "", "@Decorator(", " selector: '[my-dir]',", " map: const {'my-dir' : '@field'})", "class MyDirective {", " String field;", "}")); addIndexSource(createHtmlWithAngular("<div my-dir/>")); contextHelper.runTasks(); resolveMain(); resolveIndex(); indexUnit(indexUnit); // prepare refactoring AngularHasAttributeSelectorElement selector = findMainElement("my-dir"); prepareRenameChange(selector, "new-name"); // check results assertIndexChangeResult(createHtmlWithAngular("<div new-name/>")); assertMainChangeResult(mainContent.replace("my-dir", "new-name")); } public void test_angular_renameAttributeSelector_whenRenameProperty() throws Exception { resolveMainSource(createSource(// "import 'angular.dart';", "", "@Decorator(", " selector: '[test]',", " map: const {'test' : '@field'})", "class MyDirective {", " set field(value) {}", "}")); resolveIndex(createHtmlWithAngular("<div test='null'/>")); indexUnit(indexUnit); // prepare refactoring AngularPropertyElement property = findMainElement(ElementKind.ANGULAR_PROPERTY, "test"); prepareRenameChange(property, "newName"); // check results assertIndexChangeResult(createHtmlWithAngular("<div newName='null'/>")); assertMainChangeResult(mainContent.replace("test", "newName")); } public void test_angular_renameComponentDecl() throws Exception { contextHelper.addSource("/entry-point.html", createHtmlWithAngular()); addIndexSource("/my_template.html", createSource("<div> {{ctrl.field}} </div>")); prepareMyComponent(); contextHelper.runTasks(); resolveIndex(); indexUnit(indexUnit); // prepare refactoring AngularComponentElement component = findMainElement("ctrl"); prepareRenameChange(component, "newName"); // check results assertIndexChangeResult(createSource("<div> {{newName.field}} </div>")); assertMainChangeResult(mainContent.replace("'ctrl',", "'newName',")); } public void test_angular_renameComponentDecl_checkNewName() throws Exception { addIndexSource("/my_template.html", createSource("<div> {{ctrl.field}} </div>")); prepareMyComponent(); contextHelper.runTasks(); resolveIndex(); indexUnit(indexUnit); // prepare refactoring AngularComponentElement component = findMainElement("ctrl"); createRenameRefactoring(component); // "newName" { RefactoringStatus status = refactoring.checkNewName("newName"); assertRefactoringStatusOK(status); } // "new-name" - bad { RefactoringStatus status = refactoring.checkNewName("new-name"); assertRefactoringStatus( status, RefactoringStatusSeverity.ERROR, "Component name must not contain '-'."); } // "new.name" - bad { RefactoringStatus status = refactoring.checkNewName("new.name"); assertRefactoringStatus( status, RefactoringStatusSeverity.ERROR, "Component name must not contain '.'."); } } public void test_angular_renameController() throws Exception { prepareMyController(); resolveIndex(createHtmlWithMyController("<div> {{test.name}} </div>")); indexUnit(indexUnit); // prepare refactoring AngularControllerElement controller = findMainElement("test"); prepareRenameChange(controller, "newName"); // check results assertIndexChangeResult(createHtmlWithMyController("<div> {{newName.name}} </div>")); assertMainChangeResult(mainContent.replace("'test')", "'newName')")); } public void test_angular_renameController_checkNewName() throws Exception { prepareMyController(); resolveIndex(createHtmlWithMyController("<div> {{test.name}} </div>")); indexUnit(indexUnit); // prepare refactoring AngularControllerElement controller = findMainElement("test"); createRenameRefactoring(controller); // "newName" { RefactoringStatus status = refactoring.checkNewName("newName"); assertRefactoringStatusOK(status); } // "new-name" - bad { RefactoringStatus status = refactoring.checkNewName("new-name"); assertRefactoringStatus( status, RefactoringStatusSeverity.ERROR, "Controller name must not contain '-'."); } // "new.name" - bad { RefactoringStatus status = refactoring.checkNewName("new.name"); assertRefactoringStatus( status, RefactoringStatusSeverity.ERROR, "Controller name must not contain '.'."); } // there is already "otherController", but that's OK { RefactoringStatus status = refactoring.checkNewName("otherController"); assertRefactoringStatusOK(status); } } public void test_angular_renameFormatter() throws Exception { prepareMyFormatter(); resolveIndex(createHtmlWithMyController(// " <li ng-repeat=\"item in ctrl.items | test:true\">", " </li>", "")); indexUnit(indexUnit); // prepare refactoring AngularFormatterElement formatter = findMainElement("test"); prepareRenameChange(formatter, "newName"); // check results assertIndexChangeResult(createHtmlWithMyController(// " <li ng-repeat=\"item in ctrl.items | newName:true\">", " </li>", "")); assertMainChangeResult(mainContent.replace("'test')", "'newName')")); } public void test_angular_renameFormatter_checkNewName() throws Exception { contextHelper.addSource("/entry-point.html", createHtmlWithAngular()); prepareMyFormatter(); resolveIndex(createHtmlWithMyController(// " <li ng-repeat=\"item in ctrl.items | test:true\">", " </li>", "")); indexUnit(indexUnit); // prepare refactoring AngularFormatterElement formatter = findMainElement("test"); createRenameRefactoring(formatter); // "newName" { RefactoringStatus status = refactoring.checkNewName("newName"); assertRefactoringStatusOK(status); } // "new-name" - bad { RefactoringStatus status = refactoring.checkNewName("new-name"); assertRefactoringStatus( status, RefactoringStatusSeverity.ERROR, "Formatter name must not contain '-'."); } // "new.name" - bad { RefactoringStatus status = refactoring.checkNewName("new.name"); assertRefactoringStatus( status, RefactoringStatusSeverity.ERROR, "Formatter name must not contain '.'."); } // there is already "existingFormatter" formatter { RefactoringStatus status = refactoring.checkNewName("existingFormatter"); assertRefactoringStatus( status, RefactoringStatusSeverity.ERROR, "Application already defines formatter with name 'existingFormatter'."); } } public void test_angular_renameProperty_checkNewName() throws Exception { prepareMyComponent(); resolveIndex(createHtmlWithAngular("<myComponent test='null'/>")); indexUnit(indexUnit); // prepare refactoring AngularPropertyElement property = findMainElement("test"); createRenameRefactoring(property); // "newName" { RefactoringStatus status = refactoring.checkNewName("newName"); assertRefactoringStatusOK(status); } // "new-name" { RefactoringStatus status = refactoring.checkNewName("new-name"); assertRefactoringStatusOK(status); } // "new.name" - bad { RefactoringStatus status = refactoring.checkNewName("new.name"); assertRefactoringStatus( status, RefactoringStatusSeverity.ERROR, "Property name must not contain '.'."); } // there is already "other" property { RefactoringStatus status = refactoring.checkNewName("other"); assertRefactoringStatus( status, RefactoringStatusSeverity.ERROR, "Component already defines property with name 'other'."); } } public void test_angular_renameProperty_inComponent() throws Exception { prepareMyComponent(); resolveIndex(createHtmlWithAngular("<myComponent test='null'/>")); indexUnit(indexUnit); // prepare refactoring AngularPropertyElement property = findMainElement("test"); prepareRenameChange(property, "newName"); // check results assertIndexChangeResult(createHtmlWithAngular("<myComponent newName='null'/>")); assertMainChangeResult(mainContent.replace("'test' :", "'newName' :")); } public void test_angular_renameScopeProperty() 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['test'] = 'abc';", " }", "}")); contextHelper.addSource("/entry-point.html", createHtmlWithAngular()); addIndexSource("/my_template.html", "<div>{{test}}</div>"); contextHelper.addSource("/my_styles.css", ""); contextHelper.runTasks(); resolveMain(); resolveIndex(); indexUnit(mainUnit); indexUnit(indexUnit); // prepare refactoring AngularScopePropertyElement property = findMainElement("test"); prepareRenameChange(property, "newName"); // check results assertIndexChangeResult("<div>{{newName}}</div>"); assertMainChangeResult(mainContent.replace("'test'] =", "'newName'] =")); } public void test_angular_renameScopeProperty_checkNewName() 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['existingScopeProperty'] = 42;", " scope.context['test'] = 'abc';", " }", "}")); contextHelper.addSource("/entry-point.html", createHtmlWithAngular()); addIndexSource("/my_template.html", "<div>{{test}}</div>"); contextHelper.addSource("/my_styles.css", ""); contextHelper.runTasks(); resolveMain(); resolveIndex(); indexUnit(mainUnit); indexUnit(indexUnit); // prepare refactoring AngularScopePropertyElement property = findMainElement("test"); createRenameRefactoring(property); // "newName" { RefactoringStatus status = refactoring.checkNewName("newName"); assertRefactoringStatusOK(status); } // "new-name" - bad { RefactoringStatus status = refactoring.checkNewName("new-name"); assertRefactoringStatus( status, RefactoringStatusSeverity.ERROR, "Scope property name must not contain '-'."); } // "new.name" - bad { RefactoringStatus status = refactoring.checkNewName("new.name"); assertRefactoringStatus( status, RefactoringStatusSeverity.ERROR, "Scope property name must not contain '.'."); } // there is already "existingScopeProperty" property { RefactoringStatus status = refactoring.checkNewName("existingScopeProperty"); assertRefactoringStatus( status, RefactoringStatusSeverity.ERROR, "Component already defines scope property with name 'existingScopeProperty'."); } } public void test_angular_renameTagSelector() 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)", "class MyComponent {", "}")); addIndexSource(createHtmlWithAngular("<myComponent>abcd</myComponent>")); contextHelper.runTasks(); resolveMain(); resolveIndex(); indexUnit(indexUnit); // prepare refactoring AngularTagSelectorElement selector = findMainElement("myComponent"); prepareRenameChange(selector, "newName"); // check results assertIndexChangeResult(createHtmlWithAngular("<newName>abcd</newName>")); assertMainChangeResult(mainContent.replace("'myComponent'", "'newName'")); } public void test_angular_renameTagSelector_checkNewName() throws Exception { contextHelper.addSource("/my_template.html", createSource(// " <div>", " {{ctrl.field}}", " </div>")); addMainSource(createSource("",// "import 'angular.dart';", "", "@Component(selector: 'existingSelector')", "class OtherComponent {}", "", "@Component(", " templateUrl: 'my_template.html', cssUrl: 'my_styles.css',", " publishAs: 'ctrl',", " selector: 'myComponent' // selector)", "class MyComponent {", "}")); addIndexSource(createHtmlWithAngular("<myComponent>abcd</myComponent>")); contextHelper.runTasks(); resolveMain(); resolveIndex(); indexUnit(indexUnit); // prepare refactoring AngularTagSelectorElement selector = findMainElement("myComponent"); createRenameRefactoring(selector); // "new-name" { RefactoringStatus status = refactoring.checkNewName("new-name"); assertRefactoringStatusOK(status); } // "new name" - bad { RefactoringStatus status = refactoring.checkNewName("new name"); assertRefactoringStatus( status, RefactoringStatusSeverity.ERROR, "Tag selector name must not contain ' '."); } // "new.name" - bad { RefactoringStatus status = refactoring.checkNewName("new.name"); assertRefactoringStatus( status, RefactoringStatusSeverity.ERROR, "Tag selector name must not contain '.'."); } // there is already "existingSelector" selector { RefactoringStatus status = refactoring.checkNewName("existingSelector"); assertRefactoringStatus( status, RefactoringStatusSeverity.ERROR, "Application already defines component with tag selector 'existingSelector'."); } } public void test_dart_renameField_updateFormatterArg_orderBy() throws Exception { addMyController(); resolveIndex(createHtmlWithMyController(// "<li ng-repeat=\"item in ctrl.items | orderBy:'-name'\"/>", "</li>")); indexUnit(mainUnit); indexUnit(indexUnit); // prepare refactoring Element field = findMainElement("name"); prepareRenameChange(field, "newName"); // check results assertIndexChangeResult(createHtmlWithMyController(// "<li ng-repeat=\"item in ctrl.items | orderBy:'-newName'\"/>", "</li>")); } public void test_dart_renameField_updateHtmlExpression() throws Exception { addMyController(); resolveIndex(createHtmlWithMyController(// "<button title='{{ctrl.field}}'> {{ctrl.field}} </button>", "")); indexUnit(mainUnit); indexUnit(indexUnit); // prepare refactoring Element field = findMainElement("field"); prepareRenameChange(field, "newName"); // check results assertIndexChangeResult(createHtmlWithMyController(// "<button title='{{ctrl.newName}}'> {{ctrl.newName}} </button>", "")); } @Override protected void setUp() throws Exception { super.setUp(); // run Index index = IndexFactory.newIndex(IndexFactory.newSplitIndexStore(new MemoryNodeManager())); new Thread() { @Override public void run() { index.run(); } }.start(); searchEngine = SearchEngineFactory.createSearchEngine(index); // search for something, ensure that Index is running before we will try to stop it searchEngine.searchDeclarations("no-such-name", null, null); } @Override protected void tearDown() throws Exception { index.stop(); index = null; searchEngine = null; refactoring = null; refactoringChange = null; super.tearDown(); } private void assertIndexChangeResult(String expected) throws Exception { assertChangeResult(context, refactoringChange, indexSource, expected); } private void assertMainChangeResult(String expected) throws Exception { assertChangeResult(context, refactoringChange, mainSource, expected); } private void createRenameRefactoring(Element element) { refactoring = RefactoringFactory.createRenameRefactoring(searchEngine, element); } /** * Index the given {@link CompilationUnit}. */ private void indexUnit(CompilationUnit unit) { AnalysisContext context = unit.getElement().getContext(); index.indexUnit(context, unit); } /** * Index the given {@link HtmlUnit}. */ private void indexUnit(HtmlUnit unit) { AnalysisContext context = unit.getElement().getContext(); index.indexHtmlUnit(context, unit); } private void prepareMyComponent() throws Exception { resolveMainSource(createSource(// "import 'angular.dart';", "", "@Component(", " templateUrl: 'my_template.html', cssUrl: 'my_styles.css',", " publishAs: 'ctrl',", " selector: 'myComponent', // selector", " map: const {'test' : '=>field', 'other' : '=>field'})", "class MyComponent {", " set field(value) {}", "}")); } private void prepareMyController() throws Exception { resolveMainSource(createSource("",// "import 'angular.dart';", "", "@Controller(", " selector: '[other-controller]',", " publishAs: 'otherController')", "class OtherController {", " String name;", "}", "", "@Controller(", " selector: '[my-controller]',", " publishAs: 'test')", "class MyController {", " String name;", "}")); } private void prepareMyFormatter() throws Exception { resolveMainSource(createSource("",// "import 'angular.dart';", "", "@Formatter(name: 'test')", "class MyFormatter {", "}", "", "@Formatter(name: 'existingFormatter')", "class ExistingFormatter {", "}", "", "class Item {", " String name;", " bool done;", "}", "", "@Controller(", " selector: '[my-controller]',", " publishAs: 'ctrl')", "class MyController {", " List<Item> items;", "}")); } private void prepareRenameChange(Element element, String newName) throws Exception { createRenameRefactoring(element); refactoring.setNewName(newName); // validate status assertRefactoringStatusOK(refactoring); // prepare result refactoringChange = refactoring.createChange(PM); } }