package com.intellij.lang.javascript.flex.flexunit; import com.intellij.codeInsight.CodeInsightBundle; import com.intellij.ide.projectView.actions.MarkRootActionBase; import com.intellij.ide.util.PropertiesComponent; import com.intellij.javascript.flex.resolve.ActionScriptClassResolver; import com.intellij.lang.javascript.JSBundle; import com.intellij.lang.javascript.psi.JSFunction; import com.intellij.lang.javascript.psi.ecmal4.JSAttributeList; import com.intellij.lang.javascript.psi.ecmal4.JSAttributeListOwner; import com.intellij.lang.javascript.psi.ecmal4.JSClass; import com.intellij.lang.javascript.refactoring.ui.JSMemberSelectionPanel; import com.intellij.lang.javascript.refactoring.ui.JSReferenceEditor; import com.intellij.lang.javascript.refactoring.util.JSMemberInfo; import com.intellij.lang.javascript.refactoring.util.JSRefactoringUtil; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleUtil; import com.intellij.openapi.roots.ContentEntry; import com.intellij.openapi.roots.ModifiableRootModel; import com.intellij.openapi.roots.ModuleRootManager; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.ui.ValidationInfo; import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.Conditions; import com.intellij.openapi.util.NullableComputable; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiManager; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.refactoring.RefactoringBundle; import com.intellij.refactoring.classMembers.MemberInfoBase; import com.intellij.util.ThreeState; import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.IOException; import java.util.ArrayList; import java.util.List; public class CreateFlexUnitTestDialog extends DialogWrapper { private JPanel myMainPanel; private JTextField myTestClassNameTextField; private JSReferenceEditor myPackageCombo; private JCheckBox myCreateTestSourceFolderCheckBox; private JTextField myTestSourceFolderTextField; private JSReferenceEditor mySuperClassField; private JCheckBox mySetUpCheckBox; private JCheckBox myTearDownCheckBox; private JSMemberSelectionPanel myMemberSelectionPanel; private final Module myModule; private final JSClass myContextClass; private PsiDirectory myTargetDirectory; private JSClass mySuperClass; private final PsiDirectory myExistingTestSourceRoot; private static final String CREATE_TEST_SOURCE_FOLDER_KEY = "CreateTestSourceFolder"; public CreateFlexUnitTestDialog(final Module module, final JSClass contextClass) { super(module.getProject()); myModule = module; myContextClass = contextClass; myTestClassNameTextField.setText(myContextClass.getName() + "Test"); setTitle(CodeInsightBundle.message("intention.create.test")); myExistingTestSourceRoot = findExistingTestSourceRoot(module); myCreateTestSourceFolderCheckBox.setVisible(myExistingTestSourceRoot == null); myTestSourceFolderTextField.setVisible(myExistingTestSourceRoot == null); myTestSourceFolderTextField .setText(FileUtil.toSystemDependentName(suggestTestSourceRootPath(module, contextClass.getContainingFile().getVirtualFile()))); myCreateTestSourceFolderCheckBox.addActionListener(new ActionListener() { public void actionPerformed(final ActionEvent e) { myTestSourceFolderTextField.setEnabled(myCreateTestSourceFolderCheckBox.isSelected()); } }); myCreateTestSourceFolderCheckBox .setSelected(PropertiesComponent.getInstance(module.getProject()).getBoolean(CREATE_TEST_SOURCE_FOLDER_KEY, true)); myTestSourceFolderTextField.setEnabled(myCreateTestSourceFolderCheckBox.isSelected()); init(); } private static String suggestTestSourceRootPath(final Module module, final VirtualFile file) { if (file != null) { final ContentEntry contentEntry = MarkRootActionBase.findContentEntry(ModuleRootManager.getInstance(module), file); if (contentEntry != null) { boolean mavenStyle = false; for (VirtualFile srcRoot : contentEntry.getSourceFolderFiles()) { if (srcRoot.getUrl().equals(contentEntry.getUrl() + "/src/main/flex")) { mavenStyle = true; break; } } final String basePath = VfsUtilCore.urlToPath(contentEntry.getUrl()) + (mavenStyle ? "/src/test/flex" : "/testSrc"); String path = basePath; int i = 0; while (LocalFileSystem.getInstance().findFileByPath(path) != null) { path = basePath + (++i); } return path; } } return ""; } protected JComponent createCenterPanel() { return myMainPanel; } public JComponent getPreferredFocusedComponent() { return myTestClassNameTextField; } private void createUIComponents() { final Module module = ModuleUtil.findModuleForPsiElement(myContextClass); assert module != null; myPackageCombo = JSReferenceEditor.forPackageName(StringUtil.getPackageName(myContextClass.getQualifiedName()), module.getProject(), null, getTestClassPackageScope(module), RefactoringBundle.message("choose.destination.package")); final Condition<JSClass> filter = jsClass -> { final JSAttributeList attributeList = jsClass.getAttributeList(); return !jsClass.isInterface() && attributeList != null && !attributeList.hasModifier(JSAttributeList.ModifierType.FINAL); }; mySuperClassField = JSReferenceEditor.forClassName("", module.getProject(), null, getSuperClassScope(module), null, filter, JSBundle.message("choose.super.class.title")); final List<JSMemberInfo> memberInfos = new ArrayList<>(); JSMemberInfo.extractClassMembers(myContextClass, memberInfos, new MemberInfoBase.Filter<JSAttributeListOwner>() { public boolean includeMember(final JSAttributeListOwner member) { final JSAttributeList attributeList = member.getAttributeList(); return member instanceof JSFunction && ((JSFunction)member).getKind() != JSFunction.FunctionKind.CONSTRUCTOR && attributeList != null && attributeList.getAccessType() == JSAttributeList.AccessType.PUBLIC; } }); myMemberSelectionPanel = new JSMemberSelectionPanel("Generate test methods for:", memberInfos, null); } protected ValidationInfo doValidate() { if (myCreateTestSourceFolderCheckBox.isVisible() && myCreateTestSourceFolderCheckBox.isSelected()) { final String path = FileUtil.toSystemIndependentName(myTestSourceFolderTextField.getText().trim()); if (path.isEmpty()) return new ValidationInfo("Path is empty", myTestSourceFolderTextField); if (LocalFileSystem.getInstance().findFileByPath(path) != null) { return new ValidationInfo("File or folder already exists", myTestSourceFolderTextField); } boolean underContentRoot = false; for (VirtualFile contentRoot : ModuleRootManager.getInstance(myModule).getContentRoots()) { if (path.startsWith(contentRoot.getPath() + "/")) { underContentRoot = true; break; } } if (!underContentRoot) { return new ValidationInfo("Test source folder must be under module content root", myTestSourceFolderTextField); } } return null; } protected void doOKAction() { final String superClassFqn = mySuperClassField.getText().trim(); final PsiElement element = ActionScriptClassResolver.findClassByQNameStatic(superClassFqn, getSuperClassScope(myModule)); mySuperClass = element instanceof JSClass ? (JSClass)element : null; if (myCreateTestSourceFolderCheckBox.isVisible() && myCreateTestSourceFolderCheckBox.isSelected()) { myTargetDirectory = createTestSourceFolderAndPackage(myModule, myTestSourceFolderTextField.getText().trim(), getPackageName()); } if (myTargetDirectory == null) { myTargetDirectory = JSRefactoringUtil .chooseOrCreateDirectoryForClass(myModule.getProject(), myModule, getTestClassPackageScope(myModule), getPackageName(), getTestClassName(), myExistingTestSourceRoot, ThreeState.YES); } if (myTargetDirectory != null) { if (myCreateTestSourceFolderCheckBox.isVisible()) { PropertiesComponent.getInstance(myModule.getProject()).setValue(CREATE_TEST_SOURCE_FOLDER_KEY, myCreateTestSourceFolderCheckBox.isSelected(), true); } super.doOKAction(); } } @Nullable private static PsiDirectory createTestSourceFolderAndPackage(final Module module, final String srcRootPath, final String packageName) { final String path = FileUtil.toSystemIndependentName(srcRootPath); VirtualFile contentRoot = null; for (VirtualFile root : ModuleRootManager.getInstance(module).getContentRoots()) { if (path.startsWith(root.getPath() + "/")) { contentRoot = root; break; } } if (contentRoot != null) { final ModifiableRootModel model = ModuleRootManager.getInstance(module).getModifiableModel(); try { final VirtualFile finalContentRoot = contentRoot; final VirtualFile folder = ApplicationManager.getApplication().runWriteAction((NullableComputable<VirtualFile>)() -> { try { final VirtualFile srcRoot = VfsUtil.createDirectoryIfMissing(finalContentRoot, path.substring((finalContentRoot.getPath() + "/").length())); final VirtualFile folder1 = packageName.isEmpty() ? srcRoot : VfsUtil.createDirectoryIfMissing(srcRoot, packageName.replace('.', '/')); final ContentEntry contentEntry = MarkRootActionBase.findContentEntry(model, folder1); if (contentEntry != null) { contentEntry.addSourceFolder(srcRoot, true); model.commit(); return folder1; } } catch (IOException ignore) {/*unlucky*/} return null; }); return folder == null ? null : PsiManager.getInstance(module.getProject()).findDirectory(folder); } finally { if (model.isWritable()) { model.dispose(); } } } return null; } @Nullable private static PsiDirectory findExistingTestSourceRoot(final Module module) { PsiDirectory testSourceRoot = null; final ModuleRootManager manager = ModuleRootManager.getInstance(module); for (VirtualFile srcRoot : manager.getSourceRoots(true)) { if (manager.getFileIndex().isInTestSourceContent(srcRoot)) { testSourceRoot = PsiManager.getInstance(module.getProject()).findDirectory(srcRoot); break; } } return testSourceRoot; } private static GlobalSearchScope getSuperClassScope(final Module module) { return GlobalSearchScope.moduleWithDependenciesAndLibrariesScope(module); } private static GlobalSearchScope getTestClassPackageScope(final Module module) { return GlobalSearchScope.moduleWithDependentsScope(module); } public String getTestClassName() { return myTestClassNameTextField.getText().trim(); } public String getPackageName() { return myPackageCombo.getText().trim(); } public PsiDirectory getTargetDirectory() { return myTargetDirectory; } @Nullable public JSClass getSuperClass() { return mySuperClass; } public boolean isGenerateSetUp() { return mySetUpCheckBox.isSelected(); } public boolean isGenerateTearDown() { return myTearDownCheckBox.isSelected(); } public JSMemberInfo[] getSelectedMemberInfos() { return JSMemberInfo.getSelected(myMemberSelectionPanel.getTable().getSelectedMemberInfos(), myContextClass, Conditions.alwaysTrue()); } }