diff --git a/community-tests/community-tests.iml b/community-tests/community-tests.iml index 2eb0f6cc6f74..04445d253ad3 100644 --- a/community-tests/community-tests.iml +++ b/community-tests/community-tests.iml @@ -3,24 +3,15 @@ - + + - - - - - - - - - - - + - - + + @@ -30,14 +21,14 @@ - - - - - - - - + + + + + + + + @@ -49,7 +40,11 @@ - - + + + + + + \ No newline at end of file diff --git a/community-tests/src/META-INF/community-tests.xml b/community-tests/src/META-INF/community-tests.xml new file mode 100644 index 000000000000..4ceadd7837e2 --- /dev/null +++ b/community-tests/src/META-INF/community-tests.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/community-tests/src/com/intellij/tests/gui/JavaEEProjectTest.java b/community-tests/src/com/intellij/tests/gui/JavaEEProjectTest.java index 2f2221c66dcc..9e7df1f9ddf0 100644 --- a/community-tests/src/com/intellij/tests/gui/JavaEEProjectTest.java +++ b/community-tests/src/com/intellij/tests/gui/JavaEEProjectTest.java @@ -21,6 +21,7 @@ import com.intellij.openapi.project.DumbService; import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ProjectBundle; import com.intellij.openapi.project.ProjectUtil; +import com.intellij.tests.gui.fixtures.ActionLinkFixture; import com.intellij.tests.gui.fixtures.ProjectViewFixture; import com.intellij.tests.gui.fixtures.ToolWindowFixture; import com.intellij.tests.gui.fixtures.newProjectWizard.NewProjectWizardFixture; diff --git a/community-tests/src/com/intellij/tests/gui/JavaProjectTest.kt b/community-tests/src/com/intellij/tests/gui/JavaProjectTest.kt index 2dce0ef14924..f3a23d3f5b80 100644 --- a/community-tests/src/com/intellij/tests/gui/JavaProjectTest.kt +++ b/community-tests/src/com/intellij/tests/gui/JavaProjectTest.kt @@ -15,15 +15,16 @@ */ package com.intellij.tests.gui +import com.intellij.tests.gui.fixtures.ActionLinkFixture import com.intellij.tests.gui.fixtures.DialogFixtures.CreateDialogFixture import com.intellij.tests.gui.fixtures.EditorFixture import com.intellij.tests.gui.fixtures.JBPopupFixture -import com.intellij.tests.gui.framework.GuiTestCase -import com.intellij.tests.gui.framework.TestGroup -import com.intellij.tests.gui.framework.createNewProject +import com.intellij.tests.gui.framework.* +import com.intellij.ui.components.JBList import org.junit.Ignore import org.junit.Test import java.io.File +import javax.swing.JTextField import javax.swing.text.JTextComponent /** @@ -42,7 +43,8 @@ class JavaProjectTest: GuiTestCase() { val newProjectWizard = findNewProjectWizard() with(newProjectWizard){ setupJdk() - selectProjectType("Java") +// selectProjectType("Java") + clickListItem("Java", robot(), this.target()) selectFramework("JavaEE Persistence") clickNext() setProjectName(projectName); diff --git a/community-tests/src/com/intellij/tests/gui/fixtures/ActionLinkFixture.java b/community-tests/src/com/intellij/tests/gui/fixtures/ActionLinkFixture.java index 24bbda785a64..bc4643517ccb 100755 --- a/community-tests/src/com/intellij/tests/gui/fixtures/ActionLinkFixture.java +++ b/community-tests/src/com/intellij/tests/gui/fixtures/ActionLinkFixture.java @@ -33,6 +33,7 @@ import static com.intellij.util.containers.ContainerUtil.getFirstItem; import static org.fest.swing.timing.Pause.pause; public class ActionLinkFixture extends JComponentFixture { + @NotNull public static ActionLinkFixture findByActionId(@NotNull final String actionId, @NotNull final Robot robot, @@ -67,6 +68,39 @@ public class ActionLinkFixture extends JComponentFixture actionLinkRef = new Ref(); + pause(new Condition("Find ActionLink with name '" + actionName + "'") { + @Override + public boolean test() { + Collection found = robot.finder().findAll(container, new GenericTypeMatcher(ActionLink.class) { + @Override + protected boolean isMatching(@NotNull ActionLink actionLink) { + if (actionLink.isVisible()) { + return actionLink.getText().equals(actionName); + } + return false; + } + }); + if (found.size() == 1) { + actionLinkRef.set(getFirstItem(found)); + return true; + } + return false; + } + }, SHORT_TIMEOUT); + + ActionLink actionLink = actionLinkRef.get(); + if (actionLink == null) { + throw new ComponentLookupException("Failed to find ActionLink with name '" + actionName + "'"); + } + return new ActionLinkFixture(robot, actionLink); + } + + private ActionLinkFixture(@NotNull Robot robot, @NotNull ActionLink target) { super(ActionLinkFixture.class, robot, target); } diff --git a/community-tests/src/com/intellij/tests/gui/framework/GuiTests.java b/community-tests/src/com/intellij/tests/gui/framework/GuiTests.java index 0003c3479471..b5ee7c75f877 100644 --- a/community-tests/src/com/intellij/tests/gui/framework/GuiTests.java +++ b/community-tests/src/com/intellij/tests/gui/framework/GuiTests.java @@ -41,9 +41,7 @@ import com.intellij.ui.popup.PopupFactoryImpl; import com.intellij.ui.popup.list.ListPopupModel; import com.intellij.util.JdkBundle; import com.intellij.util.ui.EdtInvocationManager; -import org.fest.swing.core.BasicRobot; -import org.fest.swing.core.ComponentFinder; -import org.fest.swing.core.GenericTypeMatcher; +import org.fest.swing.core.*; import org.fest.swing.core.Robot; import org.fest.swing.edt.GuiActionRunner; import org.fest.swing.edt.GuiQuery; @@ -609,6 +607,18 @@ public final class GuiTests { } } + @Nullable + public static JLabel findBoundedLabel(@NotNull Container container, @NotNull JTextField textField, @NotNull Robot robot) { + Collection labels = robot.finder().findAll(container, new GenericTypeMatcher(JLabel.class) { + @Override + protected boolean isMatching(@Nonnull JLabel label) { + return label.getLabelFor().equals(textField); + } + }); + if (labels != null && !labels.isEmpty()) return labels.iterator().next(); + else return null; + } + @NotNull public static JButton findButton(@NotNull ContainerFixture container, @NotNull final String text, Robot robot) { return robot.finder().find(container.target(), new GenericTypeMatcher(JButton.class) { @@ -741,4 +751,6 @@ public final class GuiTests { String homeSubPath = SystemInfo.isMac ? "/Contents/Home" : ""; return jdkBundle.getLocation().getAbsolutePath() + homeSubPath; } + + } diff --git a/community-tests/src/com/intellij/tests/gui/framework/ScriptingAPI.kt b/community-tests/src/com/intellij/tests/gui/framework/ScriptingAPI.kt index 96151752dcd9..292c099cc8b7 100644 --- a/community-tests/src/com/intellij/tests/gui/framework/ScriptingAPI.kt +++ b/community-tests/src/com/intellij/tests/gui/framework/ScriptingAPI.kt @@ -2,9 +2,12 @@ package com.intellij.tests.gui.framework import com.intellij.openapi.ui.JBPopupMenu import com.intellij.tests.gui.fixtures.WelcomeFrameFixture +import com.intellij.ui.components.JBList import org.fest.swing.core.Robot +import org.fest.swing.fixture.JListFixture import org.fest.swing.timing.Condition import org.fest.swing.timing.Pause +import java.awt.Container /** We presume that IDEA is already started, shows "Welcome dialog" and we could create a new project @@ -22,4 +25,9 @@ fun pause(time: Long, root: Robot) { } }, time) +} + +fun clickListItem(itemName: String, robot: Robot, parentContainer: Container) { + val listFixture = JListFixture(robot, robot.finder().findByType(parentContainer, JBList::class.java, true)) + listFixture.clickItem(itemName) } \ No newline at end of file diff --git a/community-tests/src/com/intellij/tests/gui/script/ScriptRecorder.kt b/community-tests/src/com/intellij/tests/gui/script/ScriptRecorder.kt new file mode 100644 index 000000000000..dd9fc9eb4a33 --- /dev/null +++ b/community-tests/src/com/intellij/tests/gui/script/ScriptRecorder.kt @@ -0,0 +1,157 @@ +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 + * + * 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.intellij.tests.gui.script + +import com.intellij.internal.inspector.UiDropperActionExtension +import com.intellij.openapi.actionSystem.AnAction +import java.awt.Component +import javax.swing.* + +/** + * Created by jetbrains on 18/08/16. + */ + + +/** + * ScriptRecorder needs to create GUI Tests quickly. For recording UI actions like click, text entering, checkbox marking we using UI dropper. + */ +class ScriptRecorder() : AnAction(), UiDropperActionExtension { + + override fun getAnAction(): com.intellij.openapi.actionSystem.AnAction? { + return this + } + + var currentContextComponent: Component? = null + private var currentContext = Contexts() + + + override fun actionPerformed(e: com.intellij.openapi.actionSystem.AnActionEvent?) { + //get action type for script: click, enter text, mark checkbox + val component = e!!.getDataContext().getData("Component") as Component + checkContext(component) + clickCmp(component, e) + } + + //registering custom actions for a UI Dropper by ctrl + alt + click + fun registerUiDropperAction() { + com.intellij.internal.inspector.UiDropperAction.setClickAction(this) + } + + fun recordAction() { + + } + + fun clickCmp(cmp: Component, e: com.intellij.openapi.actionSystem.AnActionEvent) { + when (cmp) { + is JButton -> write(Templates.findAndClickButton(cmp.text)) + is com.intellij.ui.components.labels.ActionLink -> write(Templates.clickActionLink(cmp.text)) + is JTextField -> { + write(Templates.findJTextField()) + } + is com.intellij.ui.components.JBList -> { + val itemName = e.dataContext.getData("ItemName") as String + if (isPopupList(cmp)) + write(Templates.clickPopupItem(itemName)) + else + write(Templates.clickListItem(itemName)) + } + } + } + + private fun isPopupList(cmp: Component) = cmp.javaClass.name.toLowerCase().contains("listpopup") + + fun checkContext(cmp: Component) { + cmp as JComponent + if (isPopupList(cmp)) return //dont' change context for a popup menu + if (currentContextComponent != null && !cmp.rootPane.equals(currentContextComponent)) { + write(currentContext.closeContext()) + } + if (currentContextComponent == null || !cmp.rootPane.equals(currentContextComponent)) { + currentContextComponent = cmp.rootPane + when (cmp.rootPane.parent) { + is JDialog -> { + if ((cmp.rootPane.parent as JDialog).title.equals(com.intellij.ide.IdeBundle.message("title.new.project"))) + write(currentContext.projectWizardContextStart()) + else + write(currentContext.dialogContextStart((cmp.rootPane.parent as JDialog).title)) + } + is com.intellij.openapi.wm.impl.welcomeScreen.FlatWelcomeFrame -> write(currentContext.welcomeFrameStart()) + is JFrame -> println("is JFrame") + } + + } + } + + fun write(str: String) { + println(str) + } + +} + +//TEMPLATES +private object Templates { + fun findDialog(name: String, title: String?) = "val ${name} = DialogFixture.find(robot(), \"${title}\")" + fun withDialog(name: String) = "with (${name}){" + fun findProjectWizard(name: String) = "val ${name} = findNewProjectWizard()" + fun withProjectWizard(name: String) = "with (${name}){" + fun findWelcomeFrame(name: String) = "val ${name} = findWelcomeFrame()" + fun withWelcomeFrame(name: String) = "with (${name}){" + + fun findAndClickButton(text: String) = "GuiTests.findAndClickButton(this, \"${text}\")" + fun clickActionLink(text: String) = "ActionLinkFixture.findActionLinkByName(\"${text}\", robot(), this.target()).click()" + + fun clickPopupItem(itemName: String) = "GuiTests.clickPopupMenuItem(\"${itemName}\", this.target(), robot())" + fun clickListItem(name: String) = "clickListItem(\"${name}\", robot(), this.target())" + + fun findJTextField() = "val textField = myRobot.finder().findByType(JTextField::class.java)" + fun findJTextFieldByLabel(labelText: String) = "val textField = findTextField(\"${labelText}\").click()" +} + + +private class Contexts() { + + enum class Type {DIALOG, WELCOME_FRAME, PROJECT_WIZARD, IDE_FRAME} + + var dialogCount = 0 + var currentContextType: Type? = null + + fun dialogContextStart(title: String): String { + currentContextType = Type.DIALOG + val name = "dialog${dialogCount++}" + val findDialog = Templates.findDialog(name, title) + val withDialog = Templates.withDialog(name) + return findDialog + "\n" + withDialog + } + + fun projectWizardContextStart(): String { + currentContextType = Type.PROJECT_WIZARD + val name = "projectWizard" + val findProjectWizard = Templates.findProjectWizard(name) + val withProjectWizard = Templates.withProjectWizard(name) + return findProjectWizard + "\n" + withProjectWizard + } + + fun welcomeFrameStart(): String { + currentContextType = Type.WELCOME_FRAME + val name = "welcomeFrame" + val findWelcomeFrame = Templates.findWelcomeFrame(name) + var withWelcomeFrame = Templates.withWelcomeFrame(name) + return findWelcomeFrame + "\n" + withWelcomeFrame + } + + fun closeContext() = "}" + +} diff --git a/platform/platform-impl/src/com/intellij/internal/inspector/UiDropperAction.java b/platform/platform-impl/src/com/intellij/internal/inspector/UiDropperAction.java index fe351d52c205..a4f549ed15da 100644 --- a/platform/platform-impl/src/com/intellij/internal/inspector/UiDropperAction.java +++ b/platform/platform-impl/src/com/intellij/internal/inspector/UiDropperAction.java @@ -23,27 +23,29 @@ import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.actionSystem.impl.SimpleDataContext; import com.intellij.openapi.extensions.Extensions; -import com.intellij.openapi.keymap.impl.keyGestures.GestureActionEvent; import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.roots.ui.configuration.actions.IconWithTextAction; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.ui.StripeTable; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.wm.IdeGlassPane; +import com.intellij.openapi.wm.impl.IdeGlassPaneImpl; import com.intellij.ui.*; import com.intellij.ui.components.JBList; import com.intellij.ui.components.JBScrollPane; import com.intellij.ui.components.JBTextField; +import com.intellij.ui.popup.PopupFactoryImpl; import com.intellij.ui.speedSearch.SpeedSearchUtil; import com.intellij.util.ExceptionUtil; import com.intellij.util.Function; import com.intellij.util.ObjectUtils; import com.intellij.util.ReflectionUtil; import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.containers.HashMap; import com.intellij.util.containers.WeakKeyWeakValueHashMap; import com.intellij.util.ui.*; import com.intellij.util.ui.tree.TreeUtil; -import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -63,15 +65,16 @@ import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.TreeModel; import javax.swing.tree.TreePath; -import javax.xml.crypto.Data; import java.awt.*; import java.awt.event.*; import java.awt.geom.RoundRectangle2D; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; -import java.util.*; +import java.util.Arrays; import java.util.List; +import java.util.Map; +import java.util.Vector; import static java.awt.event.MouseEvent.BUTTON1; import static java.awt.event.MouseEvent.MOUSE_CLICKED; @@ -125,7 +128,7 @@ public class UiDropperAction extends ToggleAction implements DumbAware { NotificationType.INFORMATION); } } - + private static class InspectorWindow extends JDialog { private InspectorTable myInspectorTable; private Component myComponent; @@ -426,6 +429,14 @@ public class UiDropperAction extends ToggleAction implements DumbAware { setFont(UIUtil.getLabelFont()); } + public MyLabel(String text, JComponent glassPane, int width) { + super(text); + myText = text; + myGlasspane = glassPane; + setForeground(Gray._230); + setFont(new Font(UIUtil.getLabelFont().getName(), Font.PLAIN, 6)); + } + public void dispose() { if (myGlasspane != null) myGlasspane.remove(this); } @@ -1019,9 +1030,15 @@ public class UiDropperAction extends ToggleAction implements DumbAware { JRootPane rootPane = SwingUtilities.getRootPane(jbList); JComponent glassPane = (JComponent)rootPane.getGlassPane(); + if (!(glassPane instanceof IdeGlassPane)) rootPane.setGlassPane(new IdeGlassPaneImpl(rootPane)); myHighlightComponent = new HighlightComponent(new JBColor(JBColor.GREEN, JBColor.RED), glassPane, jbList); - myLabel = new MyLabel("JBList Cell: " + elementAt.getClass().getName() + " (\"" + elementAt.toString() + "\")", glassPane); + if (elementAt instanceof PopupFactoryImpl.ActionItem) { + myLabel = new MyLabel(elementAt.getClass().getName() + " (\"" + elementAt.toString() + "\")", glassPane, cellBounds.width); + } else { + myLabel = new MyLabel("JBList Cell: " + elementAt.getClass().getName() + " (\"" + elementAt.toString() + "\")", glassPane); + } + Point jbListLocationOnRootPane = SwingUtilities.convertPoint(jbList, new Point(0, 0), jbList.getRootPane()); myHighlightComponent.setBounds(jbListLocationOnRootPane.x + cellBounds.x, jbListLocationOnRootPane.y + cellBounds.y, cellBounds.width, cellBounds.height); @@ -1065,6 +1082,22 @@ public class UiDropperAction extends ToggleAction implements DumbAware { glassPane.repaint(); } + @Nullable + private static String getCellText(@NotNull JBList jbList, @NotNull Point pointOnList){ + int index = jbList.locationToIndex(pointOnList); + Rectangle cellBounds = jbList.getCellBounds(index, index); + if (cellBounds.contains(pointOnList)) { + Object elementAt = jbList.getModel().getElementAt(index); + if (elementAt instanceof PopupFactoryImpl.ActionItem) { + return ((PopupFactoryImpl.ActionItem)elementAt).getText(); + } + else { + return elementAt.toString(); + } + } + return null; + } + private void cleanupHighlighting() { if (myHighlightComponent != null) { myHighlightComponent.dispose(); @@ -1166,7 +1199,11 @@ public class UiDropperAction extends ToggleAction implements DumbAware { //user click if (me.getClickCount() == 1 && me.getID() == MOUSE_CLICKED && me.getButton() == BUTTON1) { if (clickAction != null) { - DataContext dataContext = SimpleDataContext.getSimpleContext("Component", component); + Map dataMap = new HashMap<>(); + dataMap.put("Component", component); + if (component instanceof JBList) + dataMap.put("ItemName", getCellText((JBList)component, mousePoint)); + DataContext dataContext = SimpleDataContext.getSimpleContext(dataMap, null); AnActionEvent event = AnActionEvent.createFromDataContext(UI_DROPPER_PLACE, null, dataContext); clickAction.actionPerformed(event); return; diff --git a/platform/platform-resources/src/META-INF/PlatformLangPlugin.xml b/platform/platform-resources/src/META-INF/PlatformLangPlugin.xml index 7b5ef05e85d4..3c8db98c5dd1 100644 --- a/platform/platform-resources/src/META-INF/PlatformLangPlugin.xml +++ b/platform/platform-resources/src/META-INF/PlatformLangPlugin.xml @@ -100,6 +100,9 @@ + + + diff --git a/platform/platform-resources/src/META-INF/PlatformPlugin.xml b/platform/platform-resources/src/META-INF/PlatformPlugin.xml index fd5b7a1f99d7..14eff28ca9c6 100644 --- a/platform/platform-resources/src/META-INF/PlatformPlugin.xml +++ b/platform/platform-resources/src/META-INF/PlatformPlugin.xml @@ -13,6 +13,9 @@ + + + diff --git a/resources/src/idea/RichPlatformPlugin.xml b/resources/src/idea/RichPlatformPlugin.xml index f5e91239caeb..fe061cae2744 100644 --- a/resources/src/idea/RichPlatformPlugin.xml +++ b/resources/src/idea/RichPlatformPlugin.xml @@ -396,6 +396,9 @@ + + +