gui-tests-framework: GUI tests framework is forked from Android GUI tests framework; excision of android and gradle dependencies; addition dependencies to community-tests module and main_idea_tests

This commit is contained in:
Sergey Karashevich
2016-10-09 17:59:25 +03:00
parent 86f8185f00
commit 4697dede92
56 changed files with 8320 additions and 0 deletions
+11
View File
@@ -0,0 +1,11 @@
<component name="libraryTable">
<library name="jsr305-1.3.9">
<CLASSES>
<root url="jar://$PROJECT_DIR$/plugins/gradle/lib/jsr305-1.3.9.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$PROJECT_DIR$/plugins/gradle/lib/jsr305-1.3.9.jar!/" />
</SOURCES>
</library>
</component>
+42
View File
@@ -7,5 +7,47 @@
</content>
<orderEntry type="jdk" jdkName="1.8" jdkType="JavaSDK" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module-library" scope="TEST">
<library name="JUnit4">
<CLASSES>
<root url="jar://$APPLICATION_HOME_DIR$/lib/junit-4.12.jar!/" />
<root url="jar://$APPLICATION_HOME_DIR$/lib/hamcrest-core-1.3.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</orderEntry>
<orderEntry type="module" module-name="core-api" scope="TEST" />
<orderEntry type="library" name="fest" level="project" />
<orderEntry type="module" module-name="platform-impl" scope="TEST" />
<orderEntry type="module" module-name="testFramework" scope="TEST" />
<orderEntry type="module-library">
<library>
<CLASSES>
<root url="jar://$MODULE_DIR$/../plugins/gradle/lib/guava-jdk5-17.0.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</orderEntry>
<orderEntry type="module" module-name="java-psi-api" scope="TEST" />
<orderEntry type="module" module-name="bootstrap" scope="TEST" />
<orderEntry type="module" module-name="external-system-rt" scope="TEST" />
<orderEntry type="module" module-name="xdebugger-impl" scope="TEST" />
<orderEntry type="module" module-name="external-system-impl" scope="TEST" />
<orderEntry type="module" module-name="openapi" scope="TEST" />
<orderEntry type="module" module-name="lint-api" scope="TEST" />
<orderEntry type="module" module-name="testRunner" scope="TEST" />
<orderEntry type="module-library">
<library>
<CLASSES>
<root url="jar://$MODULE_DIR$/../plugins/gradle/lib/jsr305-1.3.9.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$MODULE_DIR$/../plugins/gradle/lib/jsr305-1.3.9.jar!/" />
</SOURCES>
</library>
</orderEntry>
</component>
</module>
@@ -0,0 +1,154 @@
/*
* 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;
import com.intellij.tests.gui.fixtures.newProjectWizard.NewProjectWizardFixture;
import com.intellij.tests.gui.framework.GuiTestCase;
import com.intellij.tests.gui.framework.IdeGuiTest;
import org.jetbrains.annotations.NotNull;
import org.junit.Test;
import java.io.IOException;
/**
* Created by karashevich on 27/05/16.
*/
public class NewProjectTest extends GuiTestCase {
@Test @IdeGuiTest
public void testNewProject() throws IOException, InterruptedException {
String myName = "TestProject";
String myDomain = "com.test";
String myPkg = "com.android.test.app";
findWelcomeFrame().createNewProject();
NewProjectWizardFixture newProjectWizard = findNewProjectWizard();
newProjectWizard.selectProjectType("Java");
//newProjectWizard.
Thread.sleep(10000);
//newProjectWizard.clickNext()
//
//newProjectWizard.getConfigureFormFactorStep().selectMinimumSdkApi(MOBILE, myMinSdk);
//newProjectWizard.clickNext();
// Skip "Add Activity" step
newProjectWizard.clickNext();
//newProjectWizard.getChooseOptionsForNewFileStep().enterActivityName(myActivity);ё
//newProjectWizard.clickFinish();
}
@NotNull
NewProjectDescriptor newProject(@NotNull String name) {
return new NewProjectDescriptor(name);
}
private class NewProjectDescriptor {
private String myActivity = "MainActivity";
private String myPkg = "com.android.test.app";
private String myMinSdk = "19";
private String myName = "TestProject";
private String myDomain = "com.android";
private boolean myWaitForSync = true;
private NewProjectDescriptor(@NotNull String name) {
withName(name);
}
/**
* Set a custom package to use in the new project
*/
NewProjectDescriptor withPackageName(@NotNull String pkg) {
myPkg = pkg;
return this;
}
/**
* Set a new project name to use for the new project
*/
NewProjectDescriptor withName(@NotNull String name) {
myName = name;
return this;
}
/**
* Set a custom activity name to use in the new project
*/
NewProjectDescriptor withActivity(@NotNull String activity) {
myActivity = activity;
return this;
}
/**
* Set a custom minimum SDK version to use in the new project
*/
NewProjectDescriptor withMinSdk(@NotNull String minSdk) {
myMinSdk = minSdk;
return this;
}
/**
* Set a custom company domain to enter in the new project wizard
*/
NewProjectDescriptor withCompanyDomain(@NotNull String domain) {
myDomain = domain;
return this;
}
/**
* Picks brief names in order to make the test execute faster (less slow typing in name text fields)
*/
NewProjectDescriptor withBriefNames() {
withActivity("A").withCompanyDomain("C").withName("P").withPackageName("a.b");
return this;
}
/** Turns off the automatic wait-for-sync that normally happens on {@link #create} */
NewProjectDescriptor withoutSync() {
myWaitForSync = false;
return this;
}
/**
* Creates a project fixture for this description
*/
void create() {
findWelcomeFrame().createNewProject();
NewProjectWizardFixture newProjectWizard = findNewProjectWizard();
newProjectWizard.clickNext();
newProjectWizard.clickNext();
// Skip "Add Activity" step
newProjectWizard.clickNext();
newProjectWizard.getChooseOptionsForNewFileStep().enterActivityName(myActivity);
newProjectWizard.clickFinish();
}
}
}
@@ -0,0 +1,68 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* 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.driver;
import com.intellij.ui.SearchTextField;
import org.fest.swing.annotation.RunsInEDT;
import org.fest.swing.core.Robot;
import org.fest.swing.driver.JComponentDriver;
import org.fest.swing.driver.TextDisplayDriver;
import org.fest.swing.edt.GuiQuery;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.regex.Pattern;
import static org.fest.assertions.Assertions.assertThat;
import static org.fest.swing.edt.GuiActionRunner.execute;
public class SearchTextFieldDriver extends JComponentDriver implements TextDisplayDriver<SearchTextField> {
public SearchTextFieldDriver(@NotNull Robot robot) {
super(robot);
}
@Override
@RunsInEDT
public void requireText(@NotNull SearchTextField component, String expected) {
assertThat(textOf(component)).isEqualTo(expected);
}
@Override
@RunsInEDT
public void requireText(@NotNull SearchTextField component, final @NotNull Pattern pattern) {
assertThat(textOf(component)).matches(pattern.pattern());
}
@Override
@RunsInEDT
@Nullable
public String textOf(final @NotNull SearchTextField component) {
return execute(new GuiQuery<String>() {
@Override
protected
@Nullable
String executeInEDT() {
return component.getText();
}
});
}
@RunsInEDT
public void enterText(@NotNull SearchTextField textBox, @NotNull String text) {
focusAndWaitForFocusGain(textBox);
robot.enterText(text);
}
}
@@ -0,0 +1,114 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* 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.fixtures;
import com.intellij.openapi.actionSystem.ActionManager;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.impl.ActionButton;
import com.intellij.openapi.util.Ref;
import org.fest.swing.core.GenericTypeMatcher;
import org.fest.swing.core.Robot;
import org.fest.swing.edt.GuiQuery;
import org.fest.swing.exception.ComponentLookupException;
import org.fest.swing.timing.Condition;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.awt.*;
import java.util.Collection;
import static com.intellij.tests.gui.framework.GuiTests.LONG_TIMEOUT;
import static com.intellij.tests.gui.framework.GuiTests.SHORT_TIMEOUT;
import static com.intellij.util.containers.ContainerUtil.getFirstItem;
import static org.fest.swing.edt.GuiActionRunner.execute;
import static org.fest.swing.timing.Pause.pause;
public class ActionButtonFixture extends JComponentFixture<ActionButtonFixture, ActionButton> {
@NotNull
public static ActionButtonFixture findByActionId(@NotNull final String actionId,
@NotNull final Robot robot,
@NotNull final Container container) {
final Ref<ActionButton> actionButtonRef = new Ref<ActionButton>();
pause(new Condition("Find ActionButton with ID '" + actionId + "'") {
@Override
public boolean test() {
Collection<ActionButton> found = robot.finder().findAll(container, new GenericTypeMatcher<ActionButton>(ActionButton.class) {
@Override
protected boolean isMatching(@NotNull ActionButton button) {
if (button.isVisible()) {
AnAction action = button.getAction();
if (action != null) {
String id = ActionManager.getInstance().getId(action);
return actionId.equals(id);
}
}
return false;
}
});
if (found.size() == 1) {
actionButtonRef.set(getFirstItem(found));
return true;
}
return false;
}
}, SHORT_TIMEOUT);
ActionButton button = actionButtonRef.get();
if (button == null) {
throw new ComponentLookupException("Failed to find ActionButton with ID '" + actionId + "'");
}
return new ActionButtonFixture(robot, button);
}
@NotNull
public ActionButtonFixture waitUntilEnabledAndShowing() {
pause(new Condition("wait for action to be enabled and showing") {
@Override
public boolean test() {
//noinspection ConstantConditions
return execute(new GuiQuery<Boolean>() {
@Nullable
@Override
protected Boolean executeInEDT() throws Throwable {
ActionButton target = target();
if (target.getAction().getTemplatePresentation().isEnabledAndVisible()) {
return target.isShowing() && target.isVisible() && target.isEnabled();
}
return false;
}
});
}
}, LONG_TIMEOUT);
return this;
}
@NotNull
public static ActionButtonFixture findByText(@NotNull final String text, @NotNull Robot robot, @NotNull Container container) {
final ActionButton button = robot.finder().find(container, new GenericTypeMatcher<ActionButton>(ActionButton.class) {
@Override
protected boolean isMatching(@NotNull ActionButton button) {
AnAction action = button.getAction();
return text.equals(action.getTemplatePresentation().getText());
}
});
return new ActionButtonFixture(robot, button);
}
private ActionButtonFixture(@NotNull Robot robot, @NotNull ActionButton target) {
super(ActionButtonFixture.class, robot, target);
}
}
@@ -0,0 +1,73 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* 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.fixtures;
import org.fest.swing.core.Robot;
import com.intellij.openapi.actionSystem.ActionManager;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.util.Ref;
import com.intellij.ui.components.labels.ActionLink;
import org.fest.swing.core.GenericTypeMatcher;
import org.fest.swing.exception.ComponentLookupException;
import org.fest.swing.timing.Condition;
import org.jetbrains.annotations.NotNull;
import java.awt.*;
import java.util.Collection;
import static com.intellij.tests.gui.framework.GuiTests.SHORT_TIMEOUT;
import static com.intellij.util.containers.ContainerUtil.getFirstItem;
import static org.fest.swing.timing.Pause.pause;
public class ActionLinkFixture extends JComponentFixture<ActionLinkFixture, ActionLink> {
@NotNull
public static ActionLinkFixture findByActionId(@NotNull final String actionId,
@NotNull final Robot robot,
@NotNull final Container container) {
final Ref<ActionLink> actionLinkRef = new Ref<ActionLink>();
pause(new Condition("Find ActionLink with ID '" + actionId + "'") {
@Override
public boolean test() {
Collection<ActionLink> found = robot.finder().findAll(container, new GenericTypeMatcher<ActionLink>(ActionLink.class) {
@Override
protected boolean isMatching(@NotNull ActionLink actionLink) {
if (actionLink.isVisible()) {
AnAction action = actionLink.getAction();
String id = ActionManager.getInstance().getId(action);
return actionId.equals(id);
}
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 ID '" + actionId + "'");
}
return new ActionLinkFixture(robot, actionLink);
}
private ActionLinkFixture(@NotNull Robot robot, @NotNull ActionLink target) {
super(ActionLinkFixture.class, robot, target);
}
}
@@ -0,0 +1,158 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* 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.fixtures;
import com.intellij.openapi.actionSystem.ex.ComboBoxAction;
import com.intellij.ui.JBListWithHintProvider;
import com.intellij.ui.popup.PopupFactoryImpl;
import com.intellij.ui.popup.list.ListPopupModel;
import org.fest.swing.core.GenericTypeMatcher;
import org.fest.swing.core.Robot;
import org.fest.swing.edt.GuiQuery;
import org.fest.swing.edt.GuiTask;
import org.fest.swing.fixture.JButtonFixture;
import org.fest.swing.timing.Condition;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
import static com.intellij.tests.gui.framework.GuiTests.SHORT_TIMEOUT;
import static org.fest.swing.edt.GuiActionRunner.execute;
import static org.fest.swing.timing.Pause.pause;
import static org.junit.Assert.*;
public class ComboBoxActionFixture {
@NotNull private Robot myRobot;
@NotNull private JButton myTarget;
private static final Class<?> ourComboBoxButtonClass;
static {
Class<?> temp = null;
try {
temp = ComboBoxActionFixture.class.getClassLoader().loadClass(ComboBoxAction.class.getCanonicalName() + "$ComboBoxButton");
}
catch (ClassNotFoundException e) {
e.printStackTrace();
}
ourComboBoxButtonClass = temp;
}
public static ComboBoxActionFixture findComboBox(@NotNull Robot robot, @NotNull Container root) {
JButton comboBoxButton = robot.finder().find(root, new GenericTypeMatcher<JButton>(JButton.class) {
@Override
protected boolean isMatching(@NotNull JButton component) {
return ourComboBoxButtonClass.isInstance(component);
}
});
return new ComboBoxActionFixture(robot, comboBoxButton);
}
public static ComboBoxActionFixture findComboBoxByText(@NotNull Robot robot, @NotNull Container root, @NotNull final String text) {
JButton comboBoxButton = robot.finder().find(root, new GenericTypeMatcher<JButton>(JButton.class) {
@Override
protected boolean isMatching(@NotNull JButton component) {
return ourComboBoxButtonClass.isInstance(component) && component.getText().equals(text);
}
});
return new ComboBoxActionFixture(robot, comboBoxButton);
}
public ComboBoxActionFixture(@NotNull Robot robot, @NotNull JButton target) {
myRobot = robot;
myTarget = target;
}
public void selectItem(@NotNull String itemName) {
click();
selectItemByText(getPopupList(), itemName);
}
public String getSelectedItemText() {
return execute(new GuiQuery<String>() {
@Nullable
@Override
protected String executeInEDT() throws Throwable {
return myTarget.getText();
}
});
}
private void click() {
final JButtonFixture comboBoxButtonFixture = new JButtonFixture(myRobot, myTarget);
pause(new Condition("Wait until comboBoxButton is enabled") {
@Override
public boolean test() {
//noinspection ConstantConditions
return execute(new GuiQuery<Boolean>() {
@Override
protected Boolean executeInEDT() throws Throwable {
return comboBoxButtonFixture.target().isEnabled();
}
});
}
}, SHORT_TIMEOUT);
comboBoxButtonFixture.click();
}
@NotNull
private JList getPopupList() {
return myRobot.finder().findByType(JBListWithHintProvider.class);
}
private static void selectItemByText(@NotNull final JList list, @NotNull final String text) {
pause(new Condition("Wait until the list is populated.") {
@Override
public boolean test() {
ListPopupModel popupModel = (ListPopupModel)list.getModel();
for (int i = 0; i < popupModel.getSize(); ++i) {
PopupFactoryImpl.ActionItem actionItem = (PopupFactoryImpl.ActionItem)popupModel.get(i);
assertNotNull(actionItem);
if (text.equals(actionItem.getText())) {
return true;
}
}
return false;
}
}, SHORT_TIMEOUT);
final Integer appIndex = execute(new GuiQuery<Integer>() {
@Override
protected Integer executeInEDT() throws Throwable {
ListPopupModel popupModel = (ListPopupModel)list.getModel();
for (int i = 0; i < popupModel.getSize(); ++i) {
PopupFactoryImpl.ActionItem actionItem = (PopupFactoryImpl.ActionItem)popupModel.get(i);
assertNotNull(actionItem);
if (text.equals(actionItem.getText())) {
return i;
}
}
return -1;
}
});
//noinspection ConstantConditions
assertTrue(appIndex >= 0);
execute(new GuiTask() {
@Override
protected void executeInEDT() throws Throwable {
list.setSelectedIndex(appIndex);
}
});
assertEquals(text, ((PopupFactoryImpl.ActionItem)list.getSelectedValue()).getText());
}
}
@@ -0,0 +1,49 @@
/*
* 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.fixtures;
import org.fest.swing.core.Robot;
import org.fest.swing.driver.ComponentDriver;
import org.fest.swing.fixture.AbstractComponentFixture;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.awt.*;
/**
* Created by karashevich on 30/05/16.
*/
public class ComponentFixture<S, C extends Component> extends AbstractComponentFixture<S, C, ComponentDriver> {
public ComponentFixture(@NotNull Class<S> selfType, @NotNull Robot robot, @NotNull Class<? extends C> type) {
super(selfType, robot, type);
}
public ComponentFixture(@NotNull Class<S> selfType, @NotNull Robot robot, @Nullable String name, @NotNull Class<? extends C> type) {
super(selfType, robot, name, type);
}
public ComponentFixture(@NotNull Class<S> selfType, @NotNull Robot robot, @NotNull C target) {
super(selfType, robot, target);
}
@Override
@NotNull
protected ComponentDriver createDriver(@NotNull Robot robot) {
return new ComponentDriver(robot);
}
}
@@ -0,0 +1,60 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* 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.fixtures;
import org.fest.swing.core.Robot;
import org.fest.swing.core.matcher.DialogMatcher;
import org.fest.swing.fixture.DialogFixture;
import org.fest.swing.fixture.JTableCellFixture;
import org.fest.swing.fixture.JTableFixture;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import static com.intellij.tests.gui.framework.GuiTests.SHORT_TIMEOUT;
import static com.intellij.tests.gui.framework.GuiTests.findAndClickOkButton;
import static org.fest.swing.core.matcher.DialogMatcher.withTitle;
import static org.fest.swing.data.TableCell.row;
import static org.fest.swing.finder.WindowFinder.findDialog;
public class ConfigureProjectSubsetDialogFixture {
@NotNull private DialogFixture myDialog;
@NotNull private final JTableFixture myModulesTable;
@NotNull
public static ConfigureProjectSubsetDialogFixture find(@NotNull Robot robot) {
DialogMatcher matcher = withTitle("Select Modules to Include in Project Subset").andShowing();
DialogFixture dialog = findDialog(matcher).withTimeout(SHORT_TIMEOUT.duration()).using(robot);
return new ConfigureProjectSubsetDialogFixture(dialog);
}
private ConfigureProjectSubsetDialogFixture(@NotNull DialogFixture dialog) {
myDialog = dialog;
Robot robot = dialog.robot();
myModulesTable = new JTableFixture(robot, robot.finder().findByType(dialog.target(), JTable.class, true));
}
@NotNull
public ConfigureProjectSubsetDialogFixture selectModule(@NotNull String moduleName, boolean selected) {
JTableCellFixture cell = myModulesTable.cell(moduleName);
myModulesTable.enterValue(row(cell.row()).column(0), String.valueOf(selected));
return this;
}
public void clickOk() {
findAndClickOkButton(myDialog);
}
}
@@ -0,0 +1,24 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* 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.fixtures;
import org.jetbrains.annotations.NotNull;
public class DebugToolWindowFixture extends ExecutionToolWindowFixture {
public DebugToolWindowFixture(@NotNull IdeFrameFixture frameFixture) {
super("Debug", frameFixture);
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,41 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* 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.fixtures;
import com.intellij.ui.EditorNotificationPanel;
import com.intellij.ui.HyperlinkLabel;
import org.fest.reflect.core.Reflection;
import org.fest.swing.core.GenericTypeMatcher;
import org.fest.swing.core.Robot;
import org.jetbrains.annotations.NotNull;
public class EditorNotificationPanelFixture extends JComponentFixture<EditorNotificationPanelFixture, EditorNotificationPanel> {
public EditorNotificationPanelFixture(@NotNull Robot robot, @NotNull EditorNotificationPanel target) {
super(EditorNotificationPanelFixture.class, robot, target);
}
public void performAction(@NotNull final String label) {
HyperlinkLabel link = robot().finder().find(target(), new GenericTypeMatcher<HyperlinkLabel>(HyperlinkLabel.class) {
@Override
protected boolean isMatching(@NotNull HyperlinkLabel hyperlinkLabel) {
// IntelliJ's HyperLinkLabel class does not expose the getText method (it is package private)
return hyperlinkLabel.isShowing() &&
label.equals(Reflection.method("getText").withReturnType(String.class).in(hyperlinkLabel).invoke());
}
});
driver().click(link);
}
}
@@ -0,0 +1,254 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* 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.fixtures;
import com.intellij.execution.impl.ConsoleViewImpl;
import com.intellij.execution.testframework.TestTreeView;
import com.intellij.execution.ui.layout.impl.GridImpl;
import com.intellij.execution.ui.layout.impl.JBRunnerTabs;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.impl.ActionButton;
import com.intellij.openapi.actionSystem.impl.ActionToolbarImpl;
import com.intellij.openapi.ui.ThreeComponentsSplitter;
import com.intellij.ui.content.Content;
import com.intellij.ui.tabs.impl.JBTabsImpl;
import com.intellij.ui.tabs.impl.TabLabel;
import com.intellij.xdebugger.impl.ui.tree.XDebuggerTree;
import com.intellij.xdebugger.impl.ui.tree.nodes.XDebuggerTreeNode;
import org.fest.swing.core.GenericTypeMatcher;
import org.fest.swing.core.Robot;
import org.fest.swing.edt.GuiActionRunner;
import org.fest.swing.edt.GuiTask;
import org.fest.swing.exception.ComponentLookupException;
import org.fest.swing.timing.Condition;
import org.fest.swing.timing.Pause;
import org.fest.swing.timing.Timeout;
import org.fest.swing.util.TextMatcher;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.TestOnly;
import javax.swing.*;
import java.util.List;
import static com.intellij.tests.gui.framework.GuiTests.waitUntilFound;
import static com.intellij.util.ui.UIUtil.findComponentOfType;
import static com.intellij.util.ui.UIUtil.findComponentsOfType;
import static junit.framework.Assert.assertNotNull;
import static org.fest.reflect.core.Reflection.method;
import static org.fest.swing.timing.Pause.pause;
public class ExecutionToolWindowFixture extends ToolWindowFixture {
public static class ContentFixture {
@NotNull private final ExecutionToolWindowFixture myParentToolWindow;
@NotNull private final Robot myRobot;
@NotNull private final Content myContent;
private ContentFixture(@NotNull ExecutionToolWindowFixture parentToolWindow, @NotNull Robot robot, @NotNull Content content) {
myParentToolWindow = parentToolWindow;
myRobot = robot;
myContent = content;
}
public void waitForOutput(@NotNull final TextMatcher matcher, @NotNull Timeout timeout) {
pause(new Condition("LogCat tool window output check for package name.") {
@Override
public boolean test() {
return outputMatches(matcher);
}
}, timeout);
}
/**
* Waits until it grabs the console window and then returns true if its text matches that of {@code matcher}.
* Note: The caller should wrap it in something like a org.fest.swing.timing.Pause.pause to make sure they don't hang forever if the
* console view cannot be found for some reason.
*/
public boolean outputMatches(@NotNull TextMatcher matcher) {
ConsoleViewImpl consoleView;
while ((consoleView = findConsoleView()) == null || consoleView.getEditor() == null) {
// If our handle has been replaced, find it again.
JComponent consoleComponent = getTabComponent("Console");
myRobot.click(consoleComponent);
}
return matcher.isMatching(consoleView.getEditor().getDocument().getText());
}
// Returns the console or null if it is not found.
@Nullable
private ConsoleViewImpl findConsoleView() {
try {
return myRobot.finder().findByType(myParentToolWindow.myToolWindow.getComponent(), ConsoleViewImpl.class, false);
} catch (ComponentLookupException e) {
return null;
}
}
@NotNull
public JComponent getTabComponent(@NotNull final String tabName) {
return getTabContent(myParentToolWindow.myToolWindow.getComponent(), JBRunnerTabs.class, GridImpl.class, tabName);
}
@NotNull
public UnitTestTreeFixture getUnitTestTree() {
return new UnitTestTreeFixture(this, myRobot.finder().findByType(myContent.getComponent(), TestTreeView.class));
}
// Returns the root of the debugger tree or null.
@Nullable
public XDebuggerTreeNode getDebuggerTreeRoot() {
try {
JComponent debuggerComponent = getTabComponent("Debugger");
if (debuggerComponent != null) {
myRobot.click(debuggerComponent);
}
ThreeComponentsSplitter threeComponentsSplitter =
myRobot.finder().findByType(debuggerComponent, ThreeComponentsSplitter.class, false);
JComponent innerComponent = threeComponentsSplitter.getInnerComponent();
assertNotNull(innerComponent);
return myRobot.finder().findByType(innerComponent, XDebuggerTree.class, false).getRoot();
} catch (ComponentLookupException e) {
return null;
}
}
public void clickDebuggerTreeRoot() {
try {
JComponent debuggerComponent = getTabComponent("Debugger");
myRobot.click(debuggerComponent);
} catch (ComponentLookupException e) { }
}
@NotNull
private JComponent getTabContent(@NotNull final JComponent root,
final Class<? extends JBTabsImpl> parentComponentType,
@NotNull final Class<? extends JComponent> tabContentType,
@NotNull final String tabName) {
myParentToolWindow.activate();
myParentToolWindow.waitUntilIsVisible();
TabLabel tabLabel;
if (parentComponentType == null) {
tabLabel = waitUntilFound(myRobot, new GenericTypeMatcher<TabLabel>(TabLabel.class) {
@Override
protected boolean isMatching(@NotNull TabLabel component) {
return component.toString().equals(tabName);
}
});
}
else {
final JComponent parent = myRobot.finder().findByType(root, parentComponentType, false);
tabLabel = waitUntilFound(myRobot, parent, new GenericTypeMatcher<TabLabel>(TabLabel.class) {
@Override
protected boolean isMatching(@NotNull TabLabel component) {
return component.getParent() == parent && component.toString().equals(tabName);
}
});
}
myRobot.click(tabLabel);
return myRobot.finder().findByType(tabContentType);
}
public boolean isExecutionInProgress() {
// Consider that execution is in progress if 'stop' toolbar button is enabled.
for (ActionButton button : getToolbarButtons()) {
if ("com.intellij.execution.actions.StopAction".equals(button.getAction().getClass().getCanonicalName())) {
//noinspection ConstantConditions
return method("isButtonEnabled").withReturnType(boolean.class).in(button).invoke();
}
}
return true;
}
public void rerun() {
for (ActionButton button : getToolbarButtons()) {
if ("com.intellij.execution.runners.FakeRerunAction".equals(button.getAction().getClass().getCanonicalName())) {
myRobot.click(button);
return;
}
}
throw new IllegalStateException("Could not find the Re-run button.");
}
public void rerunFailed() {
for (ActionButton button : getToolbarButtons()) {
if ("com.intellij.execution.junit2.ui.actions.RerunFailedTestsAction".equals(button.getAction().getClass().getCanonicalName())) {
myRobot.click(button);
return;
}
}
throw new IllegalStateException("Could not find the Re-run failed tests button.");
}
public void waitForExecutionToFinish(@NotNull Timeout timeout) {
Pause.pause(new Condition("Wait for execution to finish") {
@Override
public boolean test() {
return !isExecutionInProgress();
}
}, timeout);
}
@TestOnly
public boolean stop() {
for (final ActionButton button : getToolbarButtons()) {
final AnAction action = button.getAction();
if (action != null && action.getClass().getName().equals("com.intellij.execution.actions.StopAction")) {
//noinspection ConstantConditions
boolean enabled = method("isButtonEnabled").withReturnType(boolean.class).in(button).invoke();
if (enabled) {
GuiActionRunner.execute(new GuiTask() {
@Override
protected void executeInEDT() throws Throwable {
button.click();
}
});
return true;
}
return false;
}
}
return false;
}
@NotNull
private List<ActionButton> getToolbarButtons() {
ActionToolbarImpl toolbar = findComponentOfType(myContent.getComponent(), ActionToolbarImpl.class);
assert toolbar != null;
return findComponentsOfType(toolbar, ActionButton.class);
}
} // End class ContentFixture
protected ExecutionToolWindowFixture(@NotNull String toolWindowId, @NotNull IdeFrameFixture ideFrame) {
super(toolWindowId, ideFrame.getProject(), ideFrame.robot());
}
@NotNull
public ContentFixture findContent(@NotNull String tabName) {
Content content = getContent(tabName);
assertNotNull(content);
return new ContentFixture(this, myRobot, content);
}
@NotNull
public ContentFixture findContent(@NotNull TextMatcher tabNameMatcher) {
Content content = getContent(tabNameMatcher);
assertNotNull(content);
return new ContentFixture(this, myRobot, content);
}
}
@@ -0,0 +1,113 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* 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.fixtures;
import com.intellij.openapi.fileChooser.ex.FileChooserDialogImpl;
import com.intellij.openapi.fileChooser.ex.FileSystemTreeImpl;
import com.intellij.openapi.vfs.VirtualFile;
import org.fest.swing.core.GenericTypeMatcher;
import org.fest.swing.core.Robot;
import org.fest.swing.edt.GuiTask;
import org.fest.swing.timing.Condition;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.util.concurrent.atomic.AtomicBoolean;
import static com.intellij.tests.gui.framework.GuiTests.SHORT_TIMEOUT;
import static com.intellij.tests.gui.framework.GuiTests.findAndClickOkButton;
import static org.fest.reflect.core.Reflection.field;
import static org.fest.swing.edt.GuiActionRunner.execute;
import static org.fest.swing.timing.Pause.pause;
import static org.fest.util.Strings.quote;
import static org.junit.Assert.assertNotNull;
public class FileChooserDialogFixture extends IdeaDialogFixture<FileChooserDialogImpl> {
@NotNull
public static FileChooserDialogFixture findOpenProjectDialog(@NotNull Robot robot) {
return findDialog(robot, new GenericTypeMatcher<JDialog>(JDialog.class) {
@Override
protected boolean isMatching(@NotNull JDialog dialog) {
return dialog.isShowing() && "Open File or Project".equals(dialog.getTitle());
}
});
}
@NotNull
public static FileChooserDialogFixture findImportProjectDialog(@NotNull Robot robot) {
return findDialog(robot, new GenericTypeMatcher<JDialog>(JDialog.class) {
@Override
protected boolean isMatching(@NotNull JDialog dialog) {
String title = dialog.getTitle();
return dialog.isShowing() && title != null && title.startsWith("Select") && title.endsWith("Project to Import");
}
});
}
@NotNull
public static FileChooserDialogFixture findDialog(@NotNull Robot robot, @NotNull final GenericTypeMatcher<JDialog> matcher) {
return new FileChooserDialogFixture(robot, find(robot, FileChooserDialogImpl.class, matcher));
}
private FileChooserDialogFixture(@NotNull Robot robot, @NotNull DialogAndWrapper<FileChooserDialogImpl> dialogAndWrapper) {
super(robot, dialogAndWrapper);
}
@NotNull
public FileChooserDialogFixture select(@NotNull final VirtualFile file) {
sleepWithTimeBomb();
final FileSystemTreeImpl fileSystemTree = field("myFileSystemTree").ofType(FileSystemTreeImpl.class)
.in(getDialogWrapper())
.get();
assertNotNull(fileSystemTree);
final AtomicBoolean fileSelected = new AtomicBoolean();
execute(new GuiTask() {
@Override
protected void executeInEDT() throws Throwable {
fileSystemTree.select(file, new Runnable() {
@Override
public void run() {
fileSelected.set(true);
}
});
}
});
pause(new Condition("File " + quote(file.getPath()) + " is selected") {
@Override
public boolean test() {
return fileSelected.get();
}
}, SHORT_TIMEOUT);
return this;
}
private void sleepWithTimeBomb() {
//TODO: WTF?!!
assert System.currentTimeMillis() < 1452600000000L; // 2016-01-12 12:00
try {
Thread.sleep(5000);
}
catch (InterruptedException e) {}
}
@NotNull
public FileChooserDialogFixture clickOk() {
findAndClickOkButton(this);
return this;
}
}
@@ -0,0 +1,196 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* 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.fixtures;
import com.intellij.codeInsight.daemon.impl.DaemonCodeAnalyzerEx;
import com.intellij.codeInsight.daemon.impl.HighlightInfo;
import com.intellij.lang.annotation.HighlightSeverity;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.fileEditor.FileEditor;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.intellij.util.CommonProcessors;
import org.fest.swing.edt.GuiQuery;
import org.fest.swing.timing.Condition;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.io.File;
import java.util.Collection;
import static com.intellij.openapi.vfs.VfsUtilCore.virtualToIoFile;
import static com.intellij.tests.gui.framework.GuiTests.SHORT_TIMEOUT;
import static junit.framework.Assert.assertNotNull;
import static org.fest.assertions.Assertions.assertThat;
import static org.fest.reflect.core.Reflection.method;
import static org.fest.swing.edt.GuiActionRunner.execute;
import static org.fest.swing.timing.Pause.pause;
import static org.fest.util.Strings.quote;
public class FileFixture {
@NotNull private final Project myProject;
@NotNull private final File myPath;
@NotNull private final VirtualFile myVirtualFile;
public FileFixture(@NotNull Project project, @NotNull VirtualFile file) {
myProject = project;
myPath = virtualToIoFile(file);
myVirtualFile = file;
}
@NotNull
public FileFixture requireOpenAndSelected() {
requireVirtualFile();
pause(new Condition("File " + quote(myPath.getPath()) + " to be opened") {
@Override
public boolean test() {
//noinspection ConstantConditions
return execute(new GuiQuery<Boolean>() {
@Override
protected Boolean executeInEDT() throws Throwable {
return isOpenAndSelected();
}
});
}
}, SHORT_TIMEOUT);
return this;
}
private boolean isOpenAndSelected() {
FileEditorManager editorManager = FileEditorManager.getInstance(myProject);
FileEditor selectedEditor = editorManager.getSelectedEditor(myVirtualFile);
if (selectedEditor != null) {
JComponent component = selectedEditor.getComponent();
if (component.isVisible() && component.isShowing()) {
Document document = FileDocumentManager.getInstance().getDocument(myVirtualFile);
if (document != null) {
PsiFile psiFile = PsiDocumentManager.getInstance(myProject).getPsiFile(document);
if (psiFile != null) {
DaemonCodeAnalyzerEx codeAnalyzer = DaemonCodeAnalyzerEx.getInstanceEx(myProject);
//noinspection ConstantConditions
boolean isRunning = method("isRunning").withReturnType(boolean.class).in(codeAnalyzer).invoke();
return !isRunning;
}
}
}
}
return false;
}
@NotNull
public FileFixture waitUntilErrorAnalysisFinishes() {
pause(new Condition("error analysis finishes") {
@Override
public boolean test() {
//noinspection ConstantConditions
return execute(new GuiQuery<Boolean>() {
@Override
protected Boolean executeInEDT() throws Throwable {
return DaemonCodeAnalyzerEx.getInstanceEx(myProject).isErrorAnalyzingFinished(getPsiFile());
}
});
}
}, SHORT_TIMEOUT);
return this;
}
@NotNull
public FileFixture requireCodeAnalysisHighlightCount(@NotNull HighlightSeverity severity, int expected) {
Collection<HighlightInfo> highlightInfos = getHighlightInfos(severity);
assertThat(highlightInfos).hasSize(expected);
return this;
}
@NotNull
public Collection<HighlightInfo> getHighlightInfos(@NotNull final HighlightSeverity severity) {
waitUntilErrorAnalysisFinishes();
final Document document = getNotNullDocument();
Collection<HighlightInfo> highlightInfos = execute(new GuiQuery<Collection<HighlightInfo>>() {
@Override
protected Collection<HighlightInfo> executeInEDT() throws Throwable {
CommonProcessors.CollectProcessor<HighlightInfo> processor = new CommonProcessors.CollectProcessor<HighlightInfo>();
DaemonCodeAnalyzerEx.processHighlights(document, myProject, severity, 0, document.getTextLength(), processor);
return processor.getResults();
}
});
assert highlightInfos != null;
return highlightInfos;
}
@NotNull
private PsiFile getPsiFile() {
final PsiFile psiFile = execute(new GuiQuery<PsiFile>() {
@Override
protected PsiFile executeInEDT() throws Throwable {
return PsiManager.getInstance(myProject).findFile(myVirtualFile);
}
});
assertNotNull("No Psi file found for path " + quote(myVirtualFile.getPath()), psiFile);
return psiFile;
}
@NotNull
public FileFixture waitForCodeAnalysisHighlightCount(@NotNull final HighlightSeverity severity, final int expected) {
final Document document = getNotNullDocument();
pause(new Condition("Waiting for code analysis " + severity + " count to reach " + expected) {
@Override
public boolean test() {
Collection<HighlightInfo> highlightInfos = execute(new GuiQuery<Collection<HighlightInfo>>() {
@Override
protected Collection<HighlightInfo> executeInEDT() throws Throwable {
CommonProcessors.CollectProcessor<HighlightInfo> processor = new CommonProcessors.CollectProcessor<HighlightInfo>();
DaemonCodeAnalyzerEx.processHighlights(document, myProject, severity, 0, document.getTextLength(), processor);
return processor.getResults();
}
});
assertNotNull(highlightInfos);
return highlightInfos.size() == expected;
}
}, SHORT_TIMEOUT);
return this;
}
@NotNull
private Document getNotNullDocument() {
Document document = getDocument(myVirtualFile);
assertNotNull("No Document found for path " + quote(myPath.getPath()), document);
return document;
}
@NotNull
public FileFixture requireVirtualFile() {
assertNotNull("No VirtualFile found for path " + quote(myPath.getPath()), myVirtualFile);
return this;
}
@Nullable
public static Document getDocument(@NotNull final VirtualFile file) {
return execute(new GuiQuery<Document>() {
@Override
protected Document executeInEDT() throws Throwable {
return FileDocumentManager.getInstance().getDocument(file);
}
});
}
}
@@ -0,0 +1,61 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* 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.fixtures;
import com.intellij.find.impl.FindDialog;
import com.intellij.openapi.ui.ComboBox;
import org.fest.swing.core.Robot;
import org.fest.swing.edt.GuiTask;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import static com.intellij.tests.gui.framework.GuiTests.findAndClickButton;
import static org.fest.assertions.Assertions.assertThat;
import static org.fest.swing.edt.GuiActionRunner.execute;
import static org.junit.Assert.assertNotNull;
public class FindDialogFixture extends IdeaDialogFixture<FindDialog> {
@NotNull
public static FindDialogFixture find(@NotNull Robot robot) {
return new FindDialogFixture(robot, find(robot, FindDialog.class));
}
private FindDialogFixture(@NotNull Robot robot, @NotNull DialogAndWrapper<FindDialog> dialogAndWrapper) {
super(robot, dialogAndWrapper);
}
@NotNull
public FindDialogFixture setTextToFind(@NotNull final String text) {
execute(new GuiTask() {
@Override
protected void executeInEDT() throws Throwable {
JComponent c = getDialogWrapper().getPreferredFocusedComponent();
assertThat(c).isInstanceOf(ComboBox.class);
ComboBox input = (ComboBox)c;
assertNotNull(input);
input.setSelectedItem(text);
}
});
return this;
}
@NotNull
public FindDialogFixture clickFind() {
findAndClickButton(this, "Find");
return this;
}
}
@@ -0,0 +1,94 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* 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.fixtures;
import com.intellij.ide.OccurenceNavigatorSupport;
import com.intellij.ui.content.Content;
import com.intellij.ui.treeStructure.Tree;
import com.intellij.usageView.UsageViewManager;
import com.intellij.usages.impl.GroupNode;
import org.fest.swing.edt.GuiQuery;
import org.fest.util.Lists;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.util.List;
import static org.fest.reflect.core.Reflection.field;
import static org.fest.swing.edt.GuiActionRunner.execute;
import static org.junit.Assert.assertNotNull;
public class FindToolWindowFixture {
@NotNull private final IdeFrameFixture myParent;
public FindToolWindowFixture(@NotNull IdeFrameFixture parent) {
myParent = parent;
}
@NotNull
public ContentFixture getSelectedContext() {
return new ContentFixture(myParent);
}
public static class ContentFixture {
@NotNull private final Content myContent;
ContentFixture(@NotNull IdeFrameFixture parent) {
UsageViewManager usageViewManager = UsageViewManager.getInstance(parent.getProject());
myContent = usageViewManager.getSelectedContent();
assertNotNull(myContent);
}
public void findUsagesInGeneratedCodeGroup() {
findUsageGroup("Usages in generated code");
}
public void findUsageGroup(@NotNull final String groupText) {
final Tree tree = getContentsTree();
final List<String> groupNames = Lists.newArrayList();
GroupNode foundGroup = execute(new GuiQuery<GroupNode>() {
@Override
@Nullable
protected GroupNode executeInEDT() throws Throwable {
GroupNode rootNode = (GroupNode)tree.getModel().getRoot();
GroupNode found = null;
for (GroupNode subGroup : rootNode.getSubGroups()) {
String subGroupText = subGroup.getGroup().getText(null);
groupNames.add(subGroupText);
if (groupText.equals(subGroupText)) {
found = subGroup;
}
}
return found;
}
});
String msg = String.format("Failed to find usage group '%1$s' in %2$s", groupText, groupNames);
assertNotNull(msg, foundGroup);
}
@NotNull
private Tree getContentsTree() {
JComponent component = myContent.getComponent();
OccurenceNavigatorSupport navigatorSupport = field("mySupport").ofType(OccurenceNavigatorSupport.class).in(component).get();
assertNotNull(navigatorSupport);
JTree tree = field("myTree").ofType(JTree.class).in(navigatorSupport).get();
assertNotNull(tree);
return (Tree)tree;
}
}
}
@@ -0,0 +1,868 @@
/*
* 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.fixtures;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.intellij.codeInspection.ui.InspectionTree;
import com.intellij.ide.RecentProjectsManager;
import com.intellij.ide.actions.ShowSettingsUtilImpl;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.externalSystem.model.ExternalSystemException;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.options.ShowSettingsUtil;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ContentEntry;
import com.intellij.openapi.roots.ModifiableRootModel;
import com.intellij.openapi.roots.ModuleRootManager;
import com.intellij.openapi.roots.SourceFolder;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.wm.impl.IdeFrameImpl;
import com.intellij.openapi.wm.impl.welcomeScreen.WelcomeFrame;
import com.intellij.ui.EditorNotificationPanel;
import com.intellij.xdebugger.impl.ui.tree.nodes.XDebuggerTreeNode;
import org.fest.swing.core.GenericTypeMatcher;
import org.fest.swing.core.Robot;
import org.fest.swing.core.matcher.JButtonMatcher;
import org.fest.swing.core.matcher.JLabelMatcher;
import org.fest.swing.edt.GuiQuery;
import org.fest.swing.edt.GuiTask;
import org.fest.swing.timing.Condition;
import org.fest.swing.timing.Timeout;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jps.model.module.JpsModuleSourceRootType;
import javax.swing.*;
import javax.swing.tree.TreeNode;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.io.File;
import java.util.Collection;
import java.util.Enumeration;
import java.util.List;
import java.util.Set;
import static com.intellij.ide.impl.ProjectUtil.closeAndDispose;
import static com.intellij.openapi.util.io.FileUtil.*;
import static com.intellij.openapi.util.text.StringUtil.isNotEmpty;
import static com.intellij.openapi.vfs.VfsUtilCore.urlToPath;
import static com.intellij.tests.gui.framework.GuiTests.*;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.fail;
import static org.fest.assertions.Assertions.assertThat;
import static org.fest.swing.edt.GuiActionRunner.execute;
import static org.fest.swing.timing.Pause.pause;
import static org.fest.util.Strings.quote;
import static org.junit.Assert.*;
/**
* Created by karashevich on 30/05/16.
*/
public class IdeFrameFixture extends ComponentFixture<IdeFrameFixture, IdeFrameImpl> {
@NotNull private final File myProjectPath;
private EditorFixture myEditor;
@NotNull
public static IdeFrameFixture find(@NotNull final Robot robot, @NotNull final File projectPath, @Nullable final String projectName) {
final GenericTypeMatcher<IdeFrameImpl> matcher = new GenericTypeMatcher<IdeFrameImpl>(IdeFrameImpl.class) {
@Override
protected boolean isMatching(@NotNull IdeFrameImpl frame) {
Project project = frame.getProject();
if (project != null && toSystemIndependentName(projectPath.getPath()).equals(project.getBasePath())) {
return projectName == null || projectName.equals(project.getName());
}
return false;
}
};
pause(new Condition("IdeFrame " + quote(projectPath.getPath()) + " to show up") {
@Override
public boolean test() {
Collection<IdeFrameImpl> frames = robot.finder().findAll(matcher);
return !frames.isEmpty();
}
}, LONG_TIMEOUT);
IdeFrameImpl ideFrame = robot.finder().find(matcher);
return new IdeFrameFixture(robot, ideFrame, projectPath);
}
public IdeFrameFixture(@NotNull Robot robot, @NotNull IdeFrameImpl target, @NotNull File projectPath) {
super(IdeFrameFixture.class, robot, target);
myProjectPath = projectPath;
final Project project = getProject();
Disposable disposable = new NoOpDisposable();
Disposer.register(project, disposable);
//myGradleProjectEventListener = new GradleProjectEventListener();
//GradleSyncState.subscribe(project, myGradleProjectEventListener);
//PostProjectBuildTasksExecutor.subscribe(project, myGradleProjectEventListener);
}
@NotNull
public File getProjectPath() {
return myProjectPath;
}
@NotNull
public List<String> getModuleNames() {
List<String> names = Lists.newArrayList();
for (Module module : getModuleManager().getModules()) {
names.add(module.getName());
}
return names;
}
@NotNull
public IdeFrameFixture requireModuleCount(int expected) {
Module[] modules = getModuleManager().getModules();
assertThat(modules).as("Module count in project " + quote(getProject().getName())).hasSize(expected);
return this;
}
@NotNull
public Collection<String> getSourceFolderRelativePaths(@NotNull String moduleName, @NotNull final JpsModuleSourceRootType<?> sourceType) {
final Set<String> paths = Sets.newHashSet();
Module module = getModule(moduleName);
final ModuleRootManager moduleRootManager = ModuleRootManager.getInstance(module);
execute(new GuiTask() {
@Override
protected void executeInEDT() throws Throwable {
ModifiableRootModel rootModel = moduleRootManager.getModifiableModel();
try {
for (ContentEntry contentEntry : rootModel.getContentEntries()) {
for (SourceFolder folder : contentEntry.getSourceFolders()) {
JpsModuleSourceRootType<?> rootType = folder.getRootType();
if (rootType.equals(sourceType)) {
String path = urlToPath(folder.getUrl());
String relativePath = getRelativePath(myProjectPath, new File(toSystemDependentName(path)));
paths.add(relativePath);
}
}
}
}
finally {
rootModel.dispose();
}
}
});
return paths;
}
@NotNull
public Module getModule(@NotNull String name) {
Module module = findModule(name);
assertNotNull("Unable to find module with name " + quote(name), module);
return module;
}
@Nullable
public Module findModule(@NotNull String name) {
for (Module module : getModuleManager().getModules()) {
if (name.equals(module.getName())) {
return module;
}
}
return null;
}
@NotNull
private ModuleManager getModuleManager() {
return ModuleManager.getInstance(getProject());
}
@NotNull
public EditorFixture getEditor() {
if (myEditor == null) {
myEditor = new EditorFixture(robot(), this);
}
return myEditor;
}
//@NotNull
//public GradleInvocationResult invokeProjectMake() {
// return invokeProjectMake(null);
//}
//@NotNull
//public GradleInvocationResult invokeProjectMake(@Nullable Runnable executeAfterInvokingMake) {
// myGradleProjectEventListener.reset();
//
// final AtomicReference<GradleInvocationResult> resultRef = new AtomicReference<GradleInvocationResult>();
// AndroidProjectBuildNotifications.subscribe(getProject(), new AndroidProjectBuildNotifications.AndroidProjectBuildListener() {
// @Override
// public void buildComplete(@NotNull AndroidProjectBuildNotifications.BuildContext context) {
// if (context instanceof GradleBuildContext) {
// resultRef.set(((GradleBuildContext)context).getBuildResult());
// }
// }
// });
// selectProjectMakeAction();
//
// if (executeAfterInvokingMake != null) {
// executeAfterInvokingMake.run();
// }
//
// waitForBuildToFinish(COMPILE_JAVA);
//
// GradleInvocationResult result = resultRef.get();
// assertNotNull(result);
//
// return result;
//}
@NotNull
public IdeFrameFixture invokeProjectMakeAndSimulateFailure(@NotNull final String failure) {
Runnable failTask = new Runnable() {
@Override
public void run() {
throw new ExternalSystemException(failure);
}
};
//ApplicationManager.getApplication().putUserData(EXECUTE_BEFORE_PROJECT_BUILD_IN_GUI_TEST_KEY, failTask);
selectProjectMakeAction();
return this;
}
/**
* Finds the Run button in the IDE interface.
*
* @return ActionButtonFixture for the run button.
*/
@NotNull
public ActionButtonFixture findRunApplicationButton() {
return findActionButtonByActionId("Run");
}
public void debugApp(@NotNull String appName) throws ClassNotFoundException {
selectApp(appName);
findActionButtonByActionId("Debug").click();
}
public void runApp(@NotNull String appName) throws ClassNotFoundException {
selectApp(appName);
findActionButtonByActionId("Run").click();
}
@NotNull
public RunToolWindowFixture getRunToolWindow() {
return new RunToolWindowFixture(this);
}
@NotNull
public DebugToolWindowFixture getDebugToolWindow() {
return new DebugToolWindowFixture(this);
}
protected void selectProjectMakeAction() {
invokeMenuPath("Build", "Make Project");
}
/**
* Invokes an action by menu path
*
* @param path the series of menu names, e.g. {@link invokeActionByMenuPath("Build", "Make Project")}
*/
public void invokeMenuPath(@NotNull String... path) {
getMenuFixture().invokeMenuPath(path);
}
/**
* Invokes an action by menu path (where each segment is a regular expression). This is particularly
* useful when the menu items can change dynamically, such as the labels of Undo actions, Run actions,
* etc.
*
* @param path the series of menu name regular expressions, e.g. {@link invokeActionByMenuPath("Build", "Make( Project)?")}
*/
public void invokeMenuPathRegex(@NotNull String... path) {
getMenuFixture().invokeMenuPathRegex(path);
}
@NotNull
private MenuFixture getMenuFixture() {
return new MenuFixture(robot(), target());
}
//@NotNull
//public IdeFrameFixture waitForBuildToFinish(@NotNull final BuildMode buildMode) {
// final Project project = getProject();
// if (buildMode == SOURCE_GEN && !GradleProjectBuilder.getInstance(project).isSourceGenerationEnabled()) {
// return this;
// }
//
// pause(new Condition("Build (" + buildMode + ") for project " + quote(project.getName()) + " to finish'") {
// @Override
// public boolean test() {
// if (buildMode == SOURCE_GEN) {
// PostProjectBuildTasksExecutor tasksExecutor = PostProjectBuildTasksExecutor.getInstance(project);
// if (tasksExecutor.getLastBuildTimestamp() > -1) {
// // This will happen when creating a new project. Source generation happens before the IDE frame is found and build listeners
// // are created. It is fairly safe to assume that source generation happened if we have a timestamp for a "last performed build".
// return true;
// }
// }
// return myGradleProjectEventListener.isBuildFinished(buildMode);
// }
// }, LONG_TIMEOUT);
//
// waitForBackgroundTasksToFinish();
// robot().waitForIdle();
//
// return this;
//}
@NotNull
public FileFixture findExistingFileByRelativePath(@NotNull String relativePath) {
VirtualFile file = findFileByRelativePath(relativePath, true);
return new FileFixture(getProject(), file);
}
@Nullable
@Contract("_, true -> !null")
public VirtualFile findFileByRelativePath(@NotNull String relativePath, boolean requireExists) {
//noinspection Contract
assertFalse("Should use '/' in test relative paths, not File.separator", relativePath.contains("\\"));
Project project = getProject();
VirtualFile file = project.getBaseDir().findFileByRelativePath(relativePath);
if (requireExists) {
//noinspection Contract
assertNotNull("Unable to find file with relative path " + quote(relativePath), file);
}
return file;
}
//@NotNull
//public IdeFrameFixture requestProjectSyncAndExpectFailure() {
// requestProjectSync();
// return waitForGradleProjectSyncToFail();
//}
@NotNull
public IdeFrameFixture requestProjectSyncAndSimulateFailure(@NotNull final String failure) {
Runnable failTask = new Runnable() {
@Override
public void run() {
throw new ExternalSystemException(failure);
}
};
//ApplicationManager.getApplication().putUserData(EXECUTE_BEFORE_PROJECT_SYNC_TASK_IN_GUI_TEST_KEY, failTask);
// When simulating the error, we don't have to wait for sync to happen. Sync never happens because the error is thrown before it (sync)
// is started.
return requestProjectSync();
}
@NotNull
public IdeFrameFixture requestProjectSync() {
//myGradleProjectEventListener.reset();
// We wait until all "Run Configurations" are populated in the toolbar combo-box. Until then the "Project Sync" button is not in its
// final position, and FEST will click the wrong button.
pause(new Condition("Waiting for 'Run Configurations' to be populated") {
@Override
public boolean test() {
RunConfigurationComboBoxFixture runConfigurationComboBox = RunConfigurationComboBoxFixture.find(IdeFrameFixture.this);
return isNotEmpty(runConfigurationComboBox.getText());
}
}, SHORT_TIMEOUT);
waitForBackgroundTasksToFinish();
findGradleSyncAction().waitUntilEnabledAndShowing();
// TODO figure out why in IDEA 15 even though an action is enabled, visible and showing, clicking it (via UI testing infrastructure)
// does not work consistently
return this;
}
@NotNull
private ActionButtonFixture findGradleSyncAction() {
return findActionButtonByActionId("Android.SyncProject");
}
//@NotNull
//public IdeFrameFixture waitForGradleProjectSyncToFail() {
// try {
// waitForGradleProjectSyncToFinish(true);
// fail("Expecting project sync to fail");
// }
// catch (RuntimeException expected) {
// // expected failure.
// }
// return waitForBackgroundTasksToFinish();
//}
//@NotNull
//public IdeFrameFixture waitForGradleProjectSyncToStart() {
// Project project = getProject();
// final GradleSyncState syncState = GradleSyncState.getInstance(project);
// if (!syncState.isSyncInProgress()) {
// pause(new Condition("Syncing project " + quote(project.getName()) + " to finish") {
// @Override
// public boolean test() {
// return myGradleProjectEventListener.isSyncStarted();
// }
// }, SHORT_TIMEOUT);
// }
// return this;
//}
//@NotNull
//public IdeFrameFixture waitForGradleProjectSyncToFinish() {
// waitForGradleProjectSyncToFinish(false);
// return this;
//}
//private void waitForGradleProjectSyncToFinish(final boolean expectSyncFailure) {
// final Project project = getProject();
//
// // ensure GradleInvoker (in-process build) is always enabled.
// AndroidGradleBuildConfiguration buildConfiguration = AndroidGradleBuildConfiguration.getInstance(project);
// buildConfiguration.USE_EXPERIMENTAL_FASTER_BUILD = true;
//
// pause(new Condition("Syncing project " + quote(project.getName()) + " to finish") {
// @Override
// public boolean test() {
// GradleSyncState syncState = GradleSyncState.getInstance(project);
// boolean syncFinished =
// (myGradleProjectEventListener.isSyncFinished() || syncState.isSyncNeeded() != ThreeState.YES) && !syncState.isSyncInProgress();
// if (expectSyncFailure) {
// syncFinished = syncFinished && myGradleProjectEventListener.hasSyncError();
// }
// return syncFinished;
// }
// }, LONG_TIMEOUT);
//
// findGradleSyncAction().waitUntilEnabledAndShowing();
//
// if (myGradleProjectEventListener.hasSyncError()) {
// RuntimeException syncError = myGradleProjectEventListener.getSyncError();
// myGradleProjectEventListener.reset();
// throw syncError;
// }
//
// if (!myGradleProjectEventListener.isSyncSkipped()) {
// waitForBuildToFinish(SOURCE_GEN);
// }
//
// waitForBackgroundTasksToFinish();
//}
@NotNull
public IdeFrameFixture waitForBackgroundTasksToFinish() {
pause(new Condition("Background tasks to finish") {
@Override
public boolean test() {
ProgressManager progressManager = ProgressManager.getInstance();
return !progressManager.hasModalProgressIndicator() &&
!progressManager.hasProgressIndicator() &&
!progressManager.hasUnsafeProgressIndicator();
}
}, LONG_TIMEOUT);
robot().waitForIdle();
return this;
}
@NotNull
private ActionButtonFixture findActionButtonByActionId(String actionId) {
return ActionButtonFixture.findByActionId(actionId, robot(), target());
}
@NotNull
public MessagesToolWindowFixture getMessagesToolWindow() {
return new MessagesToolWindowFixture(getProject(), robot());
}
@NotNull
public EditorNotificationPanelFixture requireEditorNotification(@NotNull final String message) {
final Ref<EditorNotificationPanel> notificationPanelRef = new Ref<EditorNotificationPanel>();
pause(new Condition("Notification with message '" + message + "' shows up") {
@Override
public boolean test() {
EditorNotificationPanel notificationPanel = findNotificationPanel(message);
notificationPanelRef.set(notificationPanel);
return notificationPanel != null;
}
});
EditorNotificationPanel notificationPanel = notificationPanelRef.get();
assertNotNull(notificationPanel);
return new EditorNotificationPanelFixture(robot(), notificationPanel);
}
public void requireNoEditorNotification() {
assertNull(findNotificationPanel(null));
}
/**
* Locates an editor notification with the given main message (unless the message is {@code null}, in which case we assert that there are
* no visible editor notifications. Will fail if the given notification is not found.
*/
@Nullable
private EditorNotificationPanel findNotificationPanel(@Nullable String message) {
Collection<EditorNotificationPanel> panels = robot().finder().findAll(target(), new GenericTypeMatcher<EditorNotificationPanel>(
EditorNotificationPanel.class, true) {
@Override
protected boolean isMatching(@NotNull EditorNotificationPanel panel) {
return panel.isShowing();
}
});
if (message == null) {
if (!panels.isEmpty()) {
List<String> labels = Lists.newArrayList();
for (EditorNotificationPanel panel : panels) {
labels.addAll(getEditorNotificationLabels(panel));
}
fail("Found editor notifications when none were expected" + labels);
}
return null;
}
List<String> labels = Lists.newArrayList();
for (EditorNotificationPanel panel : panels) {
List<String> found = getEditorNotificationLabels(panel);
labels.addAll(found);
for (String label : found) {
if (label.contains(message)) {
return panel;
}
}
}
return null;
}
/**
* Looks up the main label for a given editor notification panel
*/
private List<String> getEditorNotificationLabels(@NotNull EditorNotificationPanel panel) {
final List<String> allText = Lists.newArrayList();
final Collection<JLabel> labels = robot().finder().findAll(panel, JLabelMatcher.any().andShowing());
for (final JLabel label : labels) {
String text = execute(new GuiQuery<String>() {
@Override
@Nullable
protected String executeInEDT() throws Throwable {
return label.getText();
}
});
if (isNotEmpty(text)) {
allText.add(text);
}
}
return allText;
}
@NotNull
public IdeSettingsDialogFixture openIdeSettings() {
// Using invokeLater because we are going to show a *modal* dialog via API (instead of clicking a button, for example.) If we use
// GuiActionRunner the test will hang until the modal dialog is closed.
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
Project project = getProject();
ShowSettingsUtil.getInstance().showSettingsDialog(project, ShowSettingsUtilImpl.getConfigurableGroups(project, true));
}
});
return IdeSettingsDialogFixture.find(robot());
}
@NotNull
public RunConfigurationsDialogFixture invokeRunConfigurationsDialog() {
invokeMenuPath("Run", "Edit Configurations...");
return RunConfigurationsDialogFixture.find(robot());
}
@NotNull
public InspectionsFixture inspectCode() {
invokeMenuPath("Analyze", "Inspect Code...");
//final Ref<FileChooserDialogImpl> wrapperRef = new Ref<FileChooserDialogImpl>();
JDialog dialog = robot().finder().find(new GenericTypeMatcher<JDialog>(JDialog.class) {
@Override
protected boolean isMatching(@NotNull JDialog dialog) {
return "Specify Inspection Scope".equals(dialog.getTitle());
}
});
JButton button = robot().finder().find(dialog, JButtonMatcher.withText("OK").andShowing());
robot().click(button);
final InspectionTree tree = waitUntilFound(robot(), new GenericTypeMatcher<InspectionTree>(InspectionTree.class) {
@Override
protected boolean isMatching(@NotNull InspectionTree component) {
return true;
}
});
return new InspectionsFixture(robot(), getProject(), tree);
}
@NotNull
public ProjectViewFixture getProjectView() {
return new ProjectViewFixture(getProject(), robot());
}
@NotNull
public Project getProject() {
Project project = target().getProject();
assertNotNull(project);
return project;
}
public void closeProject() {
execute(new GuiTask() {
@Override
protected void executeInEDT() throws Throwable {
closeAndDispose(getProject());
RecentProjectsManager.getInstance().updateLastProjectPath();
WelcomeFrame.showIfNoProjectOpened();
}
});
pause(new Condition("Waiting for 'Welcome' page to show up") {
@Override
public boolean test() {
for (Frame frame : Frame.getFrames()) {
if (frame == WelcomeFrame.getInstance() && frame.isShowing()) {
return true;
}
}
return false;
}
});
}
@NotNull
public MessagesFixture findMessageDialog(@NotNull String title) {
return MessagesFixture.findByTitle(robot(), target(), title);
}
@NotNull
public FindDialogFixture invokeFindInPathDialog() {
invokeMenuPath("Edit", "Find", "Find in Path...");
return FindDialogFixture.find(robot());
}
@NotNull
public FindToolWindowFixture getFindToolWindow() {
return new FindToolWindowFixture(this);
}
//@NotNull
//public GradleBuildModelFixture parseBuildFileForModule(@NotNull String moduleName, boolean openInEditor) {
// Module module = getModule(moduleName);
// return parseBuildFile(module, openInEditor);
//}
//
//@NotNull
//public GradleBuildModelFixture parseBuildFile(@NotNull Module module, boolean openInEditor) {
// VirtualFile buildFile = getGradleBuildFile(module);
// assertNotNull(buildFile);
// return parseBuildFile(buildFile, openInEditor);
//}
//
//@NotNull
//public GradleBuildModelFixture parseBuildFile(@NotNull final VirtualFile buildFile, boolean openInEditor) {
// if (openInEditor) {
// getEditor().open(buildFile, Tab.DEFAULT).getCurrentFile();
// }
// final Ref<GradleBuildModel> buildModelRef = new Ref<GradleBuildModel>();
// new ReadAction() {
// @Override
// protected void run(@NotNull Result result) throws Throwable {
// buildModelRef.set(GradleBuildModel.parseBuildFile(buildFile, getProject()));
// }
// }.execute();
// GradleBuildModel buildModel = buildModelRef.get();
// assertNotNull(buildModel);
// return new GradleBuildModelFixture(buildModel);
//}
private static class NoOpDisposable implements Disposable {
@Override
public void dispose() {
}
}
public void selectApp(@NotNull String appName) {
final ActionButtonFixture runButton = findRunApplicationButton();
Container actionToolbarContainer = execute(new GuiQuery<Container>() {
@Override
protected Container executeInEDT() throws Throwable {
return runButton.target().getParent();
}
});
assertNotNull(actionToolbarContainer);
ComboBoxActionFixture comboBoxActionFixture = ComboBoxActionFixture.findComboBox(robot(), actionToolbarContainer);
comboBoxActionFixture.selectItem(appName);
robot().pressAndReleaseKey(KeyEvent.VK_ENTER);
robot().waitForIdle();
}
/////////////////////////////////////////////////////////////////
//// Methods to help control debugging under a test. ///////
/////////////////////////////////////////////////////////////////
public void resumeProgram() {
invokeMenuPathOnRobotIdle(this, "Run", "Resume Program");
}
public void stepOver() {
invokeMenuPathOnRobotIdle(this, "Run", "Step Over");
}
public void stepInto() {
invokeMenuPathOnRobotIdle(this, "Run", "Step Into");
}
public void stepOut() {
invokeMenuPathOnRobotIdle(this, "Run", "Step Out");
}
/**
* Toggles breakpoints at the line numbers in {@code lines} of the source file with basename {@code fileBaseName}. This will work only
* if the fileBasename is unique in the project that's open on Android Studio.
*/
public void toggleBreakPoints(String fileBasename, int[] lines) {
// We open the file twice to bring the editor into focus. Idea 1.15 has this bug where opening a file doesn't automatically bring its
// editor window into focus.
openFile(this, fileBasename);
openFile(this, fileBasename);
for (int line : lines) {
navigateToLine(this, line);
invokeMenuPathOnRobotIdle(this, "Run", "Toggle Line Breakpoint");
}
}
// Recursively prints out the debugger tree rooted at {@code node} into {@code builder} with an indent of
// "{@code level} * {@code numIndentSpaces}" whitespaces. Each node is printed on a separate line. The indent level for every child node
// is 1 more than their parent.
private static void printNode(XDebuggerTreeNode node, StringBuilder builder, int level, int numIndentSpaces) {
int numIndent = level;
if (builder.length() > 0) {
builder.append(System.getProperty("line.separator"));
}
for (int i = 0; i < level * numIndentSpaces; ++i) {
builder.append(' ');
}
builder.append(node.getText().toString());
Enumeration<XDebuggerTreeNode> children = node.children();
while (children.hasMoreElements()) {
printNode(children.nextElement(), builder, level + 1, numIndentSpaces);
}
}
/**
* Prints out the debugger tree rooted at {@code root}.
*/
@NotNull
public static String printDebuggerTree(XDebuggerTreeNode root) {
StringBuilder builder = new StringBuilder();
printNode(root, builder, 0, 2);
return builder.toString();
}
@NotNull
private static String[] debuggerTreeRootToChildrenTexts(XDebuggerTreeNode treeRoot) {
List<? extends TreeNode> children = treeRoot.getChildren();
String[] childrenTexts = new String[children.size()];
int i = 0;
for (TreeNode child : children) {
childrenTexts[i] = ((XDebuggerTreeNode)child).getText().toString();
++i;
}
return childrenTexts;
}
/**
* Returns the subset of {@code expectedPatterns} which do not match any of the children (just the first level children, not recursive) of
* {@code treeRoot} .
*/
@NotNull
public static List<String> getUnmatchedTerminalVariableValues(String[] expectedPatterns, XDebuggerTreeNode treeRoot) {
String[] childrenTexts = debuggerTreeRootToChildrenTexts(treeRoot);
List<String> unmatchedPatterns = Lists.newArrayList();
for (String expectedPattern : expectedPatterns) {
boolean matched = false;
for (String childText : childrenTexts) {
if (childText.matches(expectedPattern)) {
matched = true;
break;
}
}
if (!matched) {
unmatchedPatterns.add(expectedPattern);
}
}
return unmatchedPatterns;
}
/**
* Returns the appropriate pattern to look for a variable named {@code name} with the type {@code type} and value {@code value} appearing
* in the Variables window in Android Studio.
*/
@NotNull
public static String variableToSearchPattern(String name, String type, String value) {
return String.format("%s = \\{%s\\} %s", name, type, value);
}
public boolean verifyVariablesAtBreakpoint(String[] expectedVariablePatterns, String debugConfigName, long tryUntilMillis) {
DebugToolWindowFixture debugToolWindowFixture = new DebugToolWindowFixture(this);
final ExecutionToolWindowFixture.ContentFixture contentFixture = debugToolWindowFixture.findContent(debugConfigName);
contentFixture.clickDebuggerTreeRoot();
// Wait for the debugger tree to appear.
pause(new Condition("Looking for debugger tree.") {
@Override
public boolean test() {
return contentFixture.getDebuggerTreeRoot() != null;
}
}, Timeout.timeout(tryUntilMillis));
// Get the debugger tree and print it.
XDebuggerTreeNode debuggerTreeRoot = contentFixture.getDebuggerTreeRoot();
if (debuggerTreeRoot == null) {
return false;
}
List<String> unmatchedPatterns = getUnmatchedTerminalVariableValues(expectedVariablePatterns, debuggerTreeRoot);
return unmatchedPatterns.isEmpty();
}
}
@@ -0,0 +1,97 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* 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.fixtures;
import com.google.common.collect.Lists;
import com.intellij.openapi.options.Configurable;
import com.intellij.openapi.options.ConfigurableGroup;
import com.intellij.openapi.options.newEditor.SettingsDialog;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.ui.treeStructure.CachingSimpleNode;
import org.fest.swing.core.GenericTypeMatcher;
import org.fest.swing.core.Robot;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
import java.util.List;
import static org.fest.assertions.Assertions.assertThat;
import static org.fest.reflect.core.Reflection.field;
import static org.junit.Assert.assertNotNull;
public class IdeSettingsDialogFixture extends IdeaDialogFixture<SettingsDialog> {
@NotNull
public static IdeSettingsDialogFixture find(@NotNull Robot robot) {
return new IdeSettingsDialogFixture(robot, find(robot, SettingsDialog.class, new GenericTypeMatcher<JDialog>(JDialog.class) {
@Override
protected boolean isMatching(@NotNull JDialog dialog) {
String expectedTitle = SystemInfo.isMac ? "Preferences" : "Settings";
return expectedTitle.equals(dialog.getTitle()) && dialog.isShowing();
}
}));
}
private IdeSettingsDialogFixture(@NotNull Robot robot, @NotNull DialogAndWrapper<SettingsDialog> dialogAndWrapper) {
super(robot, dialogAndWrapper);
}
@NotNull
public List<String> getProjectSettingsNames() {
List<String> names = Lists.newArrayList();
JPanel optionsEditor = field("myEditor").ofType(JPanel.class).in(getDialogWrapper()).get();
assertNotNull(optionsEditor);
List<JComponent> trees = findComponentsOfType(optionsEditor, "com.intellij.openapi.options.newEditor.SettingsTreeView");
assertThat(trees).hasSize(1);
JComponent tree = trees.get(0);
CachingSimpleNode root = field("myRoot").ofType(CachingSimpleNode.class).in(tree).get();
assertNotNull(root);
ConfigurableGroup[] groups = field("myGroups").ofType(ConfigurableGroup[].class).in(root).get();
assertNotNull(groups);
for (ConfigurableGroup current : groups) {
Configurable[] configurables = current.getConfigurables();
for (Configurable configurable : configurables) {
names.add(configurable.getDisplayName());
}
}
return names;
}
@NotNull
private static List<JComponent> findComponentsOfType(@NotNull JComponent parent, @NotNull String typeName) {
List<JComponent> result = Lists.newArrayList();
findComponentsOfType(typeName, result, parent);
return result;
}
private static void findComponentsOfType(@NotNull String typeName, @NotNull List<JComponent> result, @Nullable JComponent parent) {
if (parent == null) {
return;
}
if (parent.getClass().getName().equals(typeName)) {
result.add(parent);
}
for (Component c : parent.getComponents()) {
if (c instanceof JComponent) {
findComponentsOfType(typeName, result, (JComponent)c);
}
}
}
}
@@ -0,0 +1,120 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* 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.fixtures;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.util.Ref;
import org.fest.reflect.exception.ReflectionError;
import org.fest.reflect.reference.TypeRef;
import org.fest.swing.core.GenericTypeMatcher;
import org.fest.swing.core.Robot;
import org.fest.swing.fixture.ContainerFixture;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.lang.ref.WeakReference;
import static com.intellij.tests.gui.framework.GuiTests.findAndClickCancelButton;
import static com.intellij.tests.gui.framework.GuiTests.waitUntilFound;
import static junit.framework.Assert.assertNotNull;
import static org.fest.reflect.core.Reflection.field;
public abstract class IdeaDialogFixture<T extends DialogWrapper> extends ComponentFixture<IdeaDialogFixture, JDialog> implements ContainerFixture<JDialog> {
@NotNull private final T myDialogWrapper;
@Nullable
protected static <T extends DialogWrapper> T getDialogWrapperFrom(@NotNull JDialog dialog, Class<T> dialogWrapperType) {
try {
WeakReference<DialogWrapper> dialogWrapperRef = field("myDialogWrapper").ofType(new TypeRef<WeakReference<DialogWrapper>>() {})
.in(dialog)
.get();
assertNotNull(dialogWrapperRef);
DialogWrapper wrapper = dialogWrapperRef.get();
if (dialogWrapperType.isInstance(wrapper)) {
return dialogWrapperType.cast(wrapper);
}
}
catch (ReflectionError ignored) {
}
return null;
}
public static class DialogAndWrapper<T extends DialogWrapper> {
public final JDialog dialog;
public final T wrapper;
public DialogAndWrapper(@NotNull JDialog dialog, @NotNull T wrapper) {
this.dialog = dialog;
this.wrapper = wrapper;
}
}
@NotNull
public static <T extends DialogWrapper> DialogAndWrapper<T> find(@NotNull Robot robot, @NotNull final Class<T> clz) {
return find(robot, clz, new GenericTypeMatcher<JDialog>(JDialog.class) {
@Override
protected boolean isMatching(@NotNull JDialog component) {
return component.isShowing();
}
});
}
@NotNull
public static <T extends DialogWrapper> DialogAndWrapper<T> find(@NotNull Robot robot, @NotNull final Class<T> clz,
@NotNull final GenericTypeMatcher<JDialog> matcher) {
final Ref<T> wrapperRef = new Ref<T>();
JDialog dialog = waitUntilFound(robot, new GenericTypeMatcher<JDialog>(JDialog.class) {
@Override
protected boolean isMatching(@NotNull JDialog dialog) {
if (matcher.matches(dialog)) {
T wrapper = getDialogWrapperFrom(dialog, clz);
if (wrapper != null) {
wrapperRef.set(wrapper);
return true;
}
}
return false;
}
});
return new DialogAndWrapper<T>(dialog, wrapperRef.get());
}
protected IdeaDialogFixture(@NotNull Robot robot, @NotNull JDialog target, @NotNull T dialogWrapper) {
super(IdeaDialogFixture.class, robot, target);
myDialogWrapper = dialogWrapper;
}
protected IdeaDialogFixture(@NotNull Robot robot, @NotNull DialogAndWrapper<T> dialogAndWrapper) {
this(robot, dialogAndWrapper.dialog, dialogAndWrapper.wrapper);
}
@NotNull
protected T getDialogWrapper() {
return myDialogWrapper;
}
public void clickCancel() {
// Grab focus in case it is not automatically done by the window manager, e.g. 9wm
focus();
findAndClickCancelButton(this);
}
public void close() {
robot().close(target());
}
}
@@ -0,0 +1,69 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* 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.fixtures;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Ref;
import org.fest.swing.core.GenericTypeMatcher;
import org.fest.swing.core.Robot;
import org.fest.swing.core.matcher.JTextComponentMatcher;
import org.fest.swing.fixture.JTextComponentFixture;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import javax.swing.text.JTextComponent;
import static com.intellij.tests.gui.framework.GuiTests.findAndClickOkButton;
import static com.intellij.tests.gui.framework.GuiTests.waitUntilFound;
import static org.junit.Assert.assertNotNull;
public class InputDialogFixture extends IdeaDialogFixture<DialogWrapper> {
@NotNull
public static InputDialogFixture findByTitle(@NotNull Robot robot, @NotNull final String title) {
final Ref<DialogWrapper> wrapperRef = new Ref<DialogWrapper>();
JDialog dialog = waitUntilFound(robot, new GenericTypeMatcher<JDialog>(JDialog.class) {
@Override
protected boolean isMatching(@NotNull JDialog dialog) {
if (!title.equals(dialog.getTitle()) || !dialog.isShowing()) {
return false;
}
DialogWrapper wrapper = getDialogWrapperFrom(dialog, DialogWrapper.class);
if (wrapper != null) {
String typeName = Messages.class.getName() + "$InputDialog";
if (typeName.equals(wrapper.getClass().getName())) {
wrapperRef.set(wrapper);
return true;
}
}
return false;
}
});
return new InputDialogFixture(robot, dialog, wrapperRef.get());
}
public void enterTextAndClickOk(@NotNull String text) {
JTextComponent input = robot().finder().find(target(), JTextComponentMatcher.any());
assertNotNull(input);
JTextComponentFixture inputFixture = new JTextComponentFixture(robot(), input);
inputFixture.enterText(text);
findAndClickOkButton(this);
}
private InputDialogFixture(@NotNull Robot robot, @NotNull JDialog target, @NotNull DialogWrapper dialogWrapper) {
super(robot, target, dialogWrapper);
}
}
@@ -0,0 +1,83 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* 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.fixtures;
import com.google.common.collect.Lists;
import com.intellij.codeInspection.ui.InspectionTree;
import com.intellij.codeInspection.ui.InspectionTreeNode;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.wm.ToolWindowId;
import org.fest.swing.core.Robot;
import org.fest.swing.edt.GuiQuery;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import static org.fest.swing.edt.GuiActionRunner.execute;
/**
* Fixture for the Inspections window in the IDE
*/
public class InspectionsFixture extends ToolWindowFixture {
private final InspectionTree myTree;
public InspectionsFixture(@NotNull Robot robot, @NotNull Project project, InspectionTree tree) {
super(ToolWindowId.INSPECTION, project, robot);
myTree = tree;
}
public String getResults() {
activate();
waitUntilIsVisible();
return execute(new GuiQuery<String>() {
@Override
@Nullable
protected String executeInEDT() throws Throwable {
StringBuilder sb = new StringBuilder();
InspectionsFixture.describe(myTree.getRoot(), sb, 0);
return sb.toString();
}
});
}
public static void describe(@NotNull InspectionTreeNode node, @NotNull StringBuilder sb, int depth) {
for (int i = 0; i < depth; i++) {
sb.append(" ");
}
sb.append(node.toString());
sb.append("\n");
// The exact order of the results sometimes varies so sort the children alphabetically
// instead to ensure stable test output
List<InspectionTreeNode> children = Lists.newArrayListWithExpectedSize(node.getChildCount());
for (int i = 0, n = node.getChildCount(); i < n; i++) {
children.add((InspectionTreeNode)node.getChildAt(i));
}
Collections.sort(children, new Comparator<InspectionTreeNode>() {
@Override
public int compare(InspectionTreeNode node1, InspectionTreeNode node2) {
return node1.toString().compareTo(node2.toString());
}
});
for (InspectionTreeNode child : children) {
describe(child, sb, depth + 1);
}
}
}
@@ -0,0 +1,44 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* 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.fixtures;
import org.fest.swing.core.Robot;
import org.fest.swing.driver.JComponentDriver;
import org.fest.swing.fixture.AbstractJComponentFixture;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
public abstract class JComponentFixture<S, C extends JComponent> extends AbstractJComponentFixture<S, C, JComponentDriver> {
public JComponentFixture(@NotNull Class<S> selfType, @NotNull Robot robot, @NotNull Class<? extends C> type) {
super(selfType, robot, type);
}
public JComponentFixture(@NotNull Class<S> selfType, @NotNull Robot robot, @Nullable String name, @NotNull Class<? extends C> type) {
super(selfType, robot, name, type);
}
public JComponentFixture(@NotNull Class<S> selfType, @NotNull Robot robot, @NotNull C target) {
super(selfType, robot, target);
}
@Override
@NotNull
protected JComponentDriver createDriver(@NotNull Robot robot) {
return new JComponentDriver(robot);
}
}
@@ -0,0 +1,37 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* 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.fixtures;
import com.intellij.openapi.roots.JavadocOrderRootType;
import com.intellij.openapi.roots.libraries.Library;
import org.jetbrains.annotations.NotNull;
import static org.fest.assertions.Assertions.assertThat;
public class LibraryFixture {
@NotNull private final Library myLibrary;
LibraryFixture(@NotNull Library library) {
myLibrary = library;
}
@NotNull
public LibraryFixture requireJavadocUrls(@NotNull String... urls) {
String[] actualUrls = myLibrary.getUrls(JavadocOrderRootType.getInstance());
assertThat(actualUrls).as("Javadoc URLs").containsOnly(urls);
return this;
}
}
@@ -0,0 +1,125 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* 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.fixtures;
import com.google.common.collect.Lists;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.wm.impl.IdeFrameImpl;
import org.fest.swing.core.GenericTypeMatcher;
import org.fest.swing.core.Robot;
import org.fest.swing.timing.Condition;
import org.fest.swing.timing.Pause;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.awt.*;
import java.util.Arrays;
import java.util.List;
import static org.fest.assertions.Assertions.assertThat;
import static org.fest.util.Lists.newArrayList;
import static org.junit.Assert.assertNotNull;
class MenuFixture {
@NotNull private final Robot myRobot;
@NotNull private final IdeFrameImpl myContainer;
MenuFixture(@NotNull Robot robot, @NotNull IdeFrameImpl container) {
myRobot = robot;
myContainer = container;
}
/**
* Invokes an action by menu path
*
* @param path the series of menu names, e.g. {@link invokeActionByMenuPath("Build", "Make Project ")}
*/
void invokeMenuPath(@NotNull String... path) {
JMenuItem menuItem = findActionMenuItem(false, path);
myRobot.click(menuItem);
}
/**
* Invokes an action by menu path (where each segment is a regular expression). This is particularly
* useful when the menu items can change dynamically, such as the labels of Undo actions, Run actions,
* etc.
*
* @param path the series of menu name regular expressions, e.g. {@link invokeActionByMenuPath("Build", "Make( Project)?")}
*/
void invokeMenuPathRegex(@NotNull String... path) {
JMenuItem menuItem = findActionMenuItem(true, path);
myRobot.click(menuItem);
}
@NotNull
private JMenuItem findActionMenuItem(final boolean pathIsRegex, @NotNull String... path) {
assertThat(path).isNotEmpty();
int segmentCount = path.length;
// We keep the list of previously found pop-up menus, so we don't look for menu items in the same pop-up more than once.
List<JPopupMenu> previouslyFoundPopups = Lists.newArrayList();
Container root = myContainer;
for (int i = 0; i < segmentCount; i++) {
final String segment = path[i];
assertNotNull(root);
JMenuItem found = myRobot.finder().find(root, new GenericTypeMatcher<JMenuItem>(JMenuItem.class) {
@Override
protected boolean isMatching(@NotNull JMenuItem menuItem) {
return pathIsRegex ? menuItem.getText().matches(segment) : segment.equals(menuItem.getText());
}
});
if (root instanceof JPopupMenu) {
previouslyFoundPopups.add((JPopupMenu)root);
}
if (i < segmentCount - 1) {
myRobot.click(found);
List<JPopupMenu> showingPopupMenus = findShowingPopupMenus(i + 1);
showingPopupMenus.removeAll(previouslyFoundPopups);
assertThat(showingPopupMenus).hasSize(1);
root = showingPopupMenus.get(0);
continue;
}
return found;
}
throw new AssertionError("Menu item with path " + Arrays.toString(path) + " should have been found already");
}
@NotNull
private List<JPopupMenu> findShowingPopupMenus(final int expectedCount) {
final Ref<List<JPopupMenu>> ref = new Ref<List<JPopupMenu>>();
Pause.pause(new Condition("waiting for " + expectedCount + " JPopupMenus to show up") {
@Override
public boolean test() {
List<JPopupMenu> popupMenus = newArrayList(myRobot.finder().findAll(new GenericTypeMatcher<JPopupMenu>(JPopupMenu.class) {
@Override
protected boolean isMatching(@NotNull JPopupMenu popupMenu) {
return popupMenu.isShowing();
}
}));
boolean allFound = popupMenus.size() == expectedCount;
if (allFound) {
ref.set(popupMenus);
}
return allFound;
}
});
List<JPopupMenu> popupMenus = ref.get();
assertThat(popupMenus).isNotNull().hasSize(expectedCount);
return popupMenus;
}
}
@@ -0,0 +1,72 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* 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.fixtures;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Ref;
import org.fest.swing.core.GenericTypeMatcher;
import org.fest.swing.core.Robot;
import org.fest.swing.edt.GuiQuery;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import static com.google.common.base.Strings.nullToEmpty;
import static com.intellij.tests.gui.framework.GuiTests.waitUntilFound;
import static org.fest.swing.edt.GuiActionRunner.execute;
class MessageDialogFixture extends IdeaDialogFixture<DialogWrapper> implements MessagesFixture.Delegate {
@NotNull
static MessageDialogFixture findByTitle(@NotNull Robot robot, @NotNull final String title) {
final Ref<DialogWrapper> wrapperRef = new Ref<DialogWrapper>();
JDialog dialog = waitUntilFound(robot, new GenericTypeMatcher<JDialog>(JDialog.class) {
@Override
protected boolean isMatching(@NotNull JDialog dialog) {
if (!title.equals(dialog.getTitle()) || !dialog.isShowing()) {
return false;
}
DialogWrapper wrapper = getDialogWrapperFrom(dialog, DialogWrapper.class);
if (wrapper != null) {
String typeName = Messages.class.getName() + "$MessageDialog";
if (typeName.equals(wrapper.getClass().getName())) {
wrapperRef.set(wrapper);
return true;
}
}
return false;
}
});
return new MessageDialogFixture(robot, dialog, wrapperRef.get());
}
private MessageDialogFixture(@NotNull Robot robot, @NotNull JDialog target, @NotNull DialogWrapper dialogWrapper) {
super(robot, target, dialogWrapper);
}
@Override
@NotNull
public String getMessage() {
final JTextPane textPane = robot().finder().findByType(target(), JTextPane.class);
//noinspection ConstantConditions
return execute(new GuiQuery<String>() {
@Override
protected String executeInEDT() throws Throwable {
return nullToEmpty(textPane.getText());
}
});
}
}
@@ -0,0 +1,174 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* 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.fixtures;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.ui.Messages;
import com.intellij.ui.messages.SheetController;
import org.fest.swing.core.GenericTypeMatcher;
import org.fest.swing.fixture.ContainerFixture;
import org.fest.swing.fixture.JPanelFixture;
import org.jdom.Document;
import org.jdom.Element;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
import org.fest.swing.core.Robot;
import static com.google.common.base.Strings.nullToEmpty;
import static com.intellij.openapi.util.JDOMUtil.loadDocument;
import static com.intellij.tests.gui.framework.GuiTests.*;
import static org.fest.assertions.Assertions.assertThat;
import static org.fest.reflect.core.Reflection.field;
import static org.junit.Assert.assertNotNull;
public class MessagesFixture {
@NotNull private final ContainerFixture<? extends Container> myDelegate;
@NotNull
public static MessagesFixture findByTitle(@NotNull Robot robot, @NotNull Container root, @NotNull String title) {
if (Messages.canShowMacSheetPanel()) {
return new MessagesFixture(findMacSheetByTitle(robot, root, title));
}
MessageDialogFixture dialog = MessageDialogFixture.findByTitle(robot, title);
return new MessagesFixture(dialog);
}
private MessagesFixture(@NotNull ContainerFixture<? extends Container> delegate) {
myDelegate = delegate;
}
@NotNull
public MessagesFixture clickOk() {
findAndClickOkButton(myDelegate);
return this;
}
@NotNull
public MessagesFixture clickYes() {
return click("Yes");
}
@NotNull
public MessagesFixture click(@NotNull String text) {
findAndClickButton(myDelegate, text);
return this;
}
@NotNull
public MessagesFixture requireMessageContains(@NotNull String message) {
String actual = ((Delegate)myDelegate).getMessage();
assertThat(actual).contains(message);
return this;
}
public void clickCancel() {
findAndClickCancelButton(myDelegate);
}
@NotNull
static JPanelFixture findMacSheetByTitle(@NotNull Robot robot, @NotNull Container root, @NotNull String title) {
JPanel sheetPanel = waitUntilFound(robot, root, new GenericTypeMatcher<JPanel>(JPanel.class) {
@Override
protected boolean isMatching(@NotNull JPanel panel) {
if (panel.getClass().getName().startsWith(SheetController.class.getName()) && panel.isShowing()) {
SheetController controller = findSheetController(panel);
JPanel sheetPanel = field("mySheetPanel").ofType(JPanel.class).in(controller).get();
if (sheetPanel == panel) {
return true;
}
}
return false;
}
});
String sheetTitle = getTitle(sheetPanel, robot);
assertThat(sheetTitle).as("Sheet title").isEqualTo(title);
return new MacSheetPanelFixture(robot, sheetPanel);
}
@Nullable
private static String getTitle(@NotNull JPanel sheetPanel, @NotNull Robot robot) {
final JEditorPane messageTextPane = getMessageTextPane(sheetPanel);
JEditorPane titleTextPane = robot.finder().find(sheetPanel, new GenericTypeMatcher<JEditorPane>(JEditorPane.class) {
@Override
protected boolean isMatching(@NotNull JEditorPane editorPane) {
return editorPane != messageTextPane;
}
});
return getHtmlBody(titleTextPane.getText());
}
@Nullable
public <T extends JComponent> T find(GenericTypeMatcher<T> matcher) {
return myDelegate.robot().finder().find(myDelegate.target(), matcher);
}
interface Delegate {
@NotNull String getMessage();
}
private static class MacSheetPanelFixture extends JPanelFixture implements Delegate {
public MacSheetPanelFixture(@NotNull Robot robot, @NotNull JPanel target) {
super(robot, target);
}
@Override
@NotNull
public String getMessage() {
JEditorPane messageTextPane = getMessageTextPane(target());
String text = getHtmlBody(messageTextPane.getText());
return nullToEmpty(text);
}
}
@NotNull
private static JEditorPane getMessageTextPane(@NotNull JPanel sheetPanel) {
SheetController sheetController = findSheetController(sheetPanel);
JEditorPane messageTextPane = field("messageTextPane").ofType(JEditorPane.class).in(sheetController).get();
assertNotNull(messageTextPane);
return messageTextPane;
}
@NotNull
private static SheetController findSheetController(@NotNull JPanel sheetPanel) {
SheetController sheetController = field("this$0").ofType(SheetController.class).in(sheetPanel).get();
assertNotNull(sheetController);
return sheetController;
}
@Nullable
private static String getHtmlBody(@NotNull String html) {
try {
Document document = loadDocument(html);
Element rootElement = document.getRootElement();
String sheetTitle = rootElement.getChild("body").getText();
return sheetTitle.replace("\n", "").trim();
}
catch (Throwable e) {
Logger.getInstance(MessagesFixture.class).info("Failed to parse HTML '" + html + "'", e);
}
return null;
}
}
@@ -0,0 +1,375 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* 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.fixtures;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import com.intellij.ide.errorTreeView.*;
import com.intellij.openapi.externalSystem.service.notification.EditableNotificationMessageElement;
import com.intellij.openapi.externalSystem.service.notification.NotificationMessageElement;
import com.intellij.openapi.fileEditor.OpenFileDescriptor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.wm.ToolWindowId;
import com.intellij.pom.Navigatable;
import com.intellij.ui.content.Content;
import org.fest.swing.core.Robot;
import org.fest.swing.edt.GuiQuery;
import org.fest.swing.edt.GuiTask;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.event.HyperlinkEvent;
import javax.swing.tree.TreeCellEditor;
import java.io.File;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static com.intellij.openapi.vfs.VfsUtilCore.virtualToIoFile;
import static javax.swing.event.HyperlinkEvent.EventType.ACTIVATED;
import static junit.framework.Assert.assertNotNull;
import static org.fest.assertions.Assertions.assertThat;
import static org.fest.reflect.core.Reflection.field;
import static org.fest.swing.awt.AWT.visibleCenterOf;
import static org.fest.swing.edt.GuiActionRunner.execute;
import static org.fest.util.Strings.quote;
public class MessagesToolWindowFixture extends ToolWindowFixture {
MessagesToolWindowFixture(@NotNull Project project, @NotNull Robot robot) {
super(ToolWindowId.MESSAGES_WINDOW, project, robot);
}
@NotNull
public ContentFixture getGradleSyncContent() {
Content content = getContent("Gradle Sync");
assertNotNull(content);
return new SyncContentFixture(content);
}
@NotNull
public ContentFixture getGradleBuildContent() {
Content content = getContent("Gradle Build");
assertNotNull(content);
return new BuildContentFixture(content);
}
public abstract static class ContentFixture {
@NotNull private final Content myContent;
private ContentFixture(@NotNull Content content) {
myContent = content;
}
@NotNull
public MessageFixture findMessageContainingText(@NotNull ErrorTreeElementKind kind, @NotNull final String text) {
ErrorTreeElement element = doFindMessage(kind, new MessageMatcher() {
@Override
protected boolean matches(@NotNull String[] lines) {
for (String s : lines) {
if (s.contains(text)) {
return true;
}
}
return false;
}
});
return createFixture(element);
}
@NotNull
public MessageFixture findMessage(@NotNull ErrorTreeElementKind kind, @NotNull MessageMatcher matcher) {
ErrorTreeElement found = doFindMessage(kind, matcher);
return createFixture(found);
}
@NotNull
protected abstract MessageFixture createFixture(@NotNull ErrorTreeElement element);
@NotNull
private ErrorTreeElement doFindMessage(@NotNull final ErrorTreeElementKind kind, @NotNull final MessageMatcher matcher) {
ErrorTreeElement found = execute(new GuiQuery<ErrorTreeElement>() {
@Override
@Nullable
protected ErrorTreeElement executeInEDT() throws Throwable {
NewErrorTreeViewPanel component = (NewErrorTreeViewPanel)myContent.getComponent();
ErrorViewStructure errorView = component.getErrorViewStructure();
Object root = errorView.getRootElement();
return findMessage(errorView, errorView.getChildElements(root), matcher, kind);
}
});
assertNotNull(String.format("Failed to find message of type %1$s and matching text %2$s", kind, matcher.toString()), found);
return found;
}
@Nullable
private static ErrorTreeElement findMessage(@NotNull ErrorViewStructure errorView,
@NotNull ErrorTreeElement[] children,
@NotNull MessageMatcher matcher,
@NotNull ErrorTreeElementKind kind) {
for (ErrorTreeElement child : children) {
if (child instanceof GroupingElement) {
ErrorTreeElement found = findMessage(errorView, errorView.getChildElements(child), matcher, kind);
if (found != null) {
return found;
}
}
if (kind == child.getKind() && matcher.matches(child.getText())) {
return child;
}
}
return null;
}
}
public static abstract class MessageMatcher {
protected abstract boolean matches(@NotNull String[] text);
@NotNull
public static MessageMatcher firstLineStartingWith(@NotNull final String prefix) {
return new MessageMatcher() {
@Override
public boolean matches(@NotNull String[] text) {
assertThat(text).isNotEmpty();
return text[0].startsWith(prefix);
}
@Override
public String toString() {
return "first line starting with " + quote(prefix);
}
};
}
}
public class SyncContentFixture extends ContentFixture {
SyncContentFixture(@NotNull Content content) {
super(content);
}
@Override
@NotNull
protected MessageFixture createFixture(@NotNull ErrorTreeElement element) {
return new SyncMessageFixture(myRobot, element);
}
}
public class BuildContentFixture extends ContentFixture {
BuildContentFixture(@NotNull Content content) {
super(content);
}
@Override
@NotNull
protected MessageFixture createFixture(@NotNull ErrorTreeElement element) {
throw new UnsupportedOperationException();
}
}
public abstract static class MessageFixture {
private static final Pattern ANCHOR_TAG_PATTERN = Pattern.compile("<a href=\"(.*?)\">([^<]+)</a>");
@NotNull protected final Robot myRobot;
@NotNull protected final ErrorTreeElement myTarget;
protected MessageFixture(@NotNull Robot robot, @NotNull ErrorTreeElement target) {
myRobot = robot;
myTarget = target;
}
@NotNull
public abstract HyperlinkFixture findHyperlink(@NotNull String hyperlinkText);
@NotNull
protected String extractUrl(@NotNull String wholeText, @NotNull String hyperlinkText) {
String url = null;
Matcher matcher = ANCHOR_TAG_PATTERN.matcher(wholeText);
while (matcher.find()) {
String anchorText = matcher.group(2);
// Text may be spread across multiple lines. Put everything in one line.
if (anchorText != null) {
anchorText = anchorText.replaceAll("[\\s]+", " ");
if (anchorText.equals(hyperlinkText)) {
url = matcher.group(1);
break;
}
}
}
assertNotNull("Failed to find URL for hyperlink " + quote(hyperlinkText), url);
return url;
}
@NotNull
public MessageFixture requireLocation(@NotNull File filePath, int line) {
doRequireLocation(filePath, line);
return this;
}
protected void doRequireLocation(@NotNull File expectedFilePath, int line) {
assertThat(myTarget).isInstanceOf(NotificationMessageElement.class);
NotificationMessageElement element = (NotificationMessageElement)myTarget;
Navigatable navigatable = element.getNavigatable();
assertThat(navigatable).isInstanceOf(OpenFileDescriptor.class);
OpenFileDescriptor descriptor = (OpenFileDescriptor)navigatable;
File actualFilePath = virtualToIoFile(descriptor.getFile());
assertThat(actualFilePath).isEqualTo(expectedFilePath);
assertThat((descriptor.getLine() + 1)).as("line").isEqualTo(line); // descriptor line is zero-based.
}
@NotNull
public abstract String getText();
}
public static class SyncMessageFixture extends MessageFixture {
SyncMessageFixture(@NotNull Robot robot, @NotNull ErrorTreeElement target) {
super(robot, target);
}
@Override
@NotNull
public HyperlinkFixture findHyperlink(@NotNull String hyperlinkText) {
Pair<JEditorPane, String> cellEditorAndText = getCellEditorAndText();
String url = extractUrl(cellEditorAndText.getSecond(), hyperlinkText);
return new SyncHyperlinkFixture(myRobot, url, cellEditorAndText.getFirst());
}
@Override
@NotNull
public String getText() {
String html = getCellEditorAndText().getSecond();
int startBodyIndex = html.indexOf("<body>");
assertThat(startBodyIndex).isGreaterThanOrEqualTo(0);
int endBodyIndex = html.indexOf("</body>");
assertThat(endBodyIndex).isGreaterThan(startBodyIndex);
String body = html.substring(startBodyIndex + 6 /* 6 = length of '<body>' */, endBodyIndex);
List<String> lines = Splitter.on('\n').omitEmptyStrings().trimResults().splitToList(body);
body = Joiner.on(' ').join(lines);
return body;
}
@NotNull
private Pair<JEditorPane, String> getCellEditorAndText() {
// There is no specific UI component for a hyperlink in the "Messages" window. Instead we have a JEditorPane with HTML. This method
// finds the anchor tags, and matches the text of each of them against the given text. If a matching hyperlink is found, we fire a
// HyperlinkEvent, simulating a click on the actual hyperlink.
assertThat(myTarget).isInstanceOf(EditableNotificationMessageElement.class);
final JEditorPane editorComponent = execute(new GuiQuery<JEditorPane>() {
@Override
protected JEditorPane executeInEDT() throws Throwable {
EditableNotificationMessageElement message = (EditableNotificationMessageElement)myTarget;
TreeCellEditor cellEditor = message.getRightSelfEditor();
return field("editorComponent").ofType(JEditorPane.class).in(cellEditor).get();
}
});
assertNotNull(editorComponent);
String text = execute(new GuiQuery<String>() {
@Override
protected String executeInEDT() throws Throwable {
return editorComponent.getText();
}
});
assertNotNull(text);
return Pair.create(editorComponent, text);
}
}
public abstract static class HyperlinkFixture {
@NotNull protected final Robot myRobot;
@NotNull protected final String myUrl;
protected HyperlinkFixture(@NotNull Robot robot, @NotNull String url) {
myRobot = robot;
myUrl = url;
}
@NotNull
public HyperlinkFixture requireUrl(@NotNull String expected) {
assertThat(myUrl).as("URL").isEqualTo(expected);
return this;
}
@NotNull
public HyperlinkFixture click() {
click(true);
return this;
}
/**
* Simulates a click on the hyperlink. This method returns immediately and does not wait for any UI actions triggered by the click to be
* finished.
*/
public HyperlinkFixture clickAndContinue() {
click(false);
return this;
}
private void click(boolean synchronous) {
if (synchronous) {
execute(new GuiTask() {
@Override
protected void executeInEDT() {
new Runnable() {
@Override
public void run() {
doClick();
}
}.run();
}
});
}
else {
//noinspection SSBasedInspection
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
doClick();
}
});
}
}
protected abstract void doClick();
}
public static class SyncHyperlinkFixture extends HyperlinkFixture {
@NotNull private final JEditorPane myTarget;
SyncHyperlinkFixture(@NotNull Robot robot, @NotNull String url, @NotNull JEditorPane target) {
super(robot, url);
myTarget = target;
}
@Override
protected void doClick() {
// at least move the mouse where the message is, so we can know that something is happening.
myRobot.moveMouse(visibleCenterOf(myTarget));
myTarget.fireHyperlinkUpdate(new HyperlinkEvent(this, ACTIVATED, null, myUrl));
}
}
}
@@ -0,0 +1,282 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* 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.fixtures;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.intellij.ide.projectView.ProjectView;
import com.intellij.ide.projectView.ProjectViewNode;
import com.intellij.ide.projectView.impl.AbstractProjectViewPane;
import com.intellij.ide.projectView.impl.nodes.ExternalLibrariesNode;
import com.intellij.ide.projectView.impl.nodes.NamedLibraryElement;
import com.intellij.ide.projectView.impl.nodes.NamedLibraryElementNode;
import com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode;
import com.intellij.ide.util.treeView.AbstractTreeStructure;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.JavaSdk;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.roots.JdkOrderEntry;
import com.intellij.openapi.roots.LibraryOrSdkOrderEntry;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiDirectory;
import com.intellij.util.ui.tree.TreeUtil;
import org.fest.swing.core.Robot;
import org.fest.swing.edt.GuiActionRunner;
import org.fest.swing.edt.GuiQuery;
import org.fest.swing.edt.GuiTask;
import org.fest.swing.timing.Condition;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.tree.DefaultMutableTreeNode;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import static com.intellij.tests.gui.framework.GuiTests.SHORT_TIMEOUT;
import static org.fest.assertions.Assertions.assertThat;
import static org.fest.reflect.core.Reflection.field;
import static org.fest.swing.timing.Pause.pause;
import static org.junit.Assert.assertNotNull;
public class ProjectViewFixture extends ToolWindowFixture {
ProjectViewFixture(@NotNull Project project, @NotNull Robot robot) {
super("Project", project, robot);
}
@NotNull
public PaneFixture selectProjectPane() {
activate();
final ProjectView projectView = ProjectView.getInstance(myProject);
pause(new Condition("Project view is initialized") {
@Override
public boolean test() {
//noinspection ConstantConditions
return field("isInitialized").ofType(boolean.class).in(projectView).get();
}
}, SHORT_TIMEOUT);
final String id = "ProjectPane";
GuiActionRunner.execute(new GuiTask() {
@Override
protected void executeInEDT() throws Throwable {
projectView.changeView(id);
}
});
return new PaneFixture(projectView.getProjectViewPaneById(id));
}
@NotNull
public PaneFixture selectAndroidPane() {
activate();
final ProjectView projectView = ProjectView.getInstance(myProject);
pause(new Condition("Project view is initialized") {
@Override
public boolean test() {
//noinspection ConstantConditions
return field("isInitialized").ofType(boolean.class).in(projectView).get();
}
}, SHORT_TIMEOUT);
final String id = "AndroidView";
GuiActionRunner.execute(new GuiTask() {
@Override
protected void executeInEDT() throws Throwable {
projectView.changeView(id);
}
});
return new PaneFixture(projectView.getProjectViewPaneById(id));
}
public static class PaneFixture {
@NotNull private final AbstractProjectViewPane myPane;
PaneFixture(@NotNull AbstractProjectViewPane pane) {
myPane = pane;
}
@NotNull
public PaneFixture expand() {
GuiActionRunner.execute(new GuiTask() {
@Override
protected void executeInEDT() throws Throwable {
TreeUtil.expandAll(myPane.getTree());
}
});
return this;
}
@NotNull
private AbstractTreeStructure getTreeStructure() {
final AtomicReference<AbstractTreeStructure> treeStructureRef = new AtomicReference<AbstractTreeStructure>();
pause(new Condition("Tree Structure to be built") {
@Override
public boolean test() {
AbstractTreeStructure treeStructure = GuiActionRunner.execute(new GuiQuery<AbstractTreeStructure>() {
@Override
protected AbstractTreeStructure executeInEDT() throws Throwable {
try {
return myPane.getTreeBuilder().getTreeStructure();
}
catch (NullPointerException e) {
// expected;
}
return null;
}
});
treeStructureRef.set(treeStructure);
return treeStructure != null;
}
}, SHORT_TIMEOUT);
return treeStructureRef.get();
}
@NotNull
public NodeFixture findExternalLibrariesNode() {
final AbstractTreeStructure treeStructure = getTreeStructure();
ExternalLibrariesNode node = GuiActionRunner.execute(new GuiQuery<ExternalLibrariesNode>() {
@Nullable
@Override
protected ExternalLibrariesNode executeInEDT() throws Throwable {
Object[] childElements = treeStructure.getChildElements(treeStructure.getRootElement());
for (Object child : childElements) {
if (child instanceof ExternalLibrariesNode) {
return (ExternalLibrariesNode)child;
}
}
return null;
}
});
if (node != null) {
return new NodeFixture(node, treeStructure);
}
throw new AssertionError("Unable to find 'External Libraries' node");
}
public void selectByPath(@NotNull final String... paths) {
final AbstractTreeStructure treeStructure = getTreeStructure();
final PsiDirectoryNode node = GuiActionRunner.execute(new GuiQuery<PsiDirectoryNode>() {
@Nullable
@Override
protected PsiDirectoryNode executeInEDT() throws Throwable {
Object root = treeStructure.getRootElement();
final List<Object> treePath = Lists.newArrayList(root);
for (String path : paths) {
Object[] childElements = treeStructure.getChildElements(root);
Object newRoot = null;
for (Object child : childElements) {
if (child instanceof PsiDirectoryNode) {
PsiDirectory dir = ((PsiDirectoryNode)child).getValue();
if (dir != null && path.equals(dir.getName())) {
newRoot = child;
treePath.add(newRoot);
break;
}
}
}
if (newRoot != null) {
root = newRoot;
}
else {
return null;
}
}
if (root == treeStructure.getRootElement()) {
return null;
}
myPane.expand(treePath.toArray(), true);
myPane.select(root, ((PsiDirectoryNode)root).getVirtualFile(), true);
return (PsiDirectoryNode)root;
}
});
assertNotNull(node);
pause(new Condition("Node to be selected") {
@Override
public boolean test() {
return node.equals(GuiActionRunner.execute(new GuiQuery<Object>() {
@Override
protected Object executeInEDT() throws Throwable {
DefaultMutableTreeNode selectedNode = myPane.getSelectedNode();
if (selectedNode != null) {
return selectedNode.getUserObject();
}
return null;
}
}));
}
}, SHORT_TIMEOUT);
}
}
public static class NodeFixture {
@NotNull private final ProjectViewNode<?> myNode;
@NotNull private final AbstractTreeStructure myTreeStructure;
NodeFixture(@NotNull ProjectViewNode<?> node, @NotNull AbstractTreeStructure treeStructure) {
myNode = node;
myTreeStructure = treeStructure;
}
@NotNull
public List<NodeFixture> getChildren() {
final List<NodeFixture> children = Lists.newArrayList();
GuiActionRunner.execute(new GuiTask() {
@Override
protected void executeInEDT() throws Throwable {
for (Object child : myTreeStructure.getChildElements(myNode)) {
if (child instanceof ProjectViewNode) {
children.add(new NodeFixture((ProjectViewNode<?>)child, myTreeStructure));
}
}
}
});
return children;
}
public boolean isJdk() {
if (myNode instanceof NamedLibraryElementNode) {
NamedLibraryElement value = ((NamedLibraryElementNode)myNode).getValue();
assertNotNull(value);
LibraryOrSdkOrderEntry orderEntry = value.getOrderEntry();
if (orderEntry instanceof JdkOrderEntry) {
Sdk sdk = ((JdkOrderEntry)orderEntry).getJdk();
return sdk.getSdkType() instanceof JavaSdk;
}
}
return false;
}
@NotNull
public NodeFixture requireDirectory(@NotNull String name) {
assertThat(myNode).isInstanceOf(PsiDirectoryNode.class);
VirtualFile file = myNode.getVirtualFile();
assertNotNull(file);
assertThat(file.getName()).isEqualTo(name);
return this;
}
@Override
public String toString() {
return Strings.nullToEmpty(myNode.getName());
}
}
}
@@ -0,0 +1,138 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* 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.fixtures;
import com.intellij.openapi.actionSystem.impl.SimpleDataContext;
import com.intellij.openapi.util.Ref;
import com.intellij.psi.PsiElement;
import com.intellij.refactoring.RefactoringBundle;
import com.intellij.refactoring.rename.RenameDialog;
import com.intellij.refactoring.rename.RenameHandler;
import com.intellij.ui.EditorTextField;
import org.fest.swing.core.GenericTypeMatcher;
import org.fest.swing.core.Robot;
import org.fest.swing.edt.GuiQuery;
import org.fest.swing.edt.GuiTask;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import static com.google.common.base.Strings.isNullOrEmpty;
import static com.intellij.tests.gui.framework.GuiTests.waitUntilFound;
import static org.fest.reflect.core.Reflection.field;
import static org.fest.swing.edt.GuiActionRunner.execute;
import static org.junit.Assert.assertNotNull;
public class RenameDialogFixture extends IdeaDialogFixture<RenameDialog> {
public RenameDialogFixture(@NotNull Robot robot, @NotNull JDialog target, @NotNull RenameDialog dialogWrapper) {
super(robot, target, dialogWrapper);
}
/**
* Starts 'rename' refactoring for the given data.
* <p/>
* <b>Note:</b> proper way would be to write dedicated 'project view fixture' and emulate user actions like 'expand nodes until
* we find a target one' but that IJ component (project view) is rather complex and it's much easier to start the refactoring
* programmatically.
*
* @param element target PSI element for which 'rename' refactoring should begin
* @param handler rename refactoring handler to use
* @param robot robot to use
* @return a fixture for the 'rename dialog' which occurs when we start 'rename' refactoring for the given data
*/
@NotNull
public static RenameDialogFixture startFor(@NotNull final PsiElement element,
@NotNull final RenameHandler handler,
@NotNull Robot robot)
{
// We use SwingUtilities instead of FEST here because RenameDialog is modal and GuiActionRunner doesn't return until
//noinspection SSBasedInspection
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
handler.invoke(element.getProject(), new PsiElement[] { element }, SimpleDataContext.getProjectContext(element.getProject()));
}
});
final Ref<RenameDialog> ref = new Ref<RenameDialog>();
JDialog dialog = waitUntilFound(robot, new GenericTypeMatcher<JDialog>(JDialog.class) {
@Override
protected boolean isMatching(@NotNull JDialog dialog) {
if (!RefactoringBundle.message("rename.title").equals(dialog.getTitle()) || !dialog.isShowing()) {
return false;
}
RenameDialog renameDialog = getDialogWrapperFrom(dialog, RenameDialog.class);
if (renameDialog == null) {
return false;
}
ref.set(renameDialog);
return true;
}
});
return new RenameDialogFixture(robot, dialog, ref.get());
}
@NotNull
public String getNewName() {
//noinspection ConstantConditions
return execute(new GuiQuery<String>() {
@Override
protected String executeInEDT() throws Throwable {
String text = robot().finder().findByType(target(), EditorTextField.class).getText();
return text == null ? "" : text;
}
});
}
public void setNewName(@NotNull final String newName) {
execute(new GuiTask() {
@Override
protected void executeInEDT() throws Throwable {
robot().finder().findByType(target(), EditorTextField.class).setText(newName);
}
});
}
/**
* Allows to check if a warning exists at the target 'rename dialog'
*
* @param warningText <code>null</code> as a wildcard to match any non-empty warning text;
* non-null text which is evaluated to be a part of the target dialog's warning text
* @return <code>true</code> if the target 'rename dialog' has a warning and given text matches it according to the
* rules described above; <code>false</code> otherwise
*/
public boolean warningExists(@Nullable final String warningText) {
//noinspection ConstantConditions
return execute(new GuiQuery<Boolean>() {
@Override
protected Boolean executeInEDT() throws Throwable {
JComponent errorTextPane = field("myErrorText").ofType(JComponent.class).in(getDialogWrapper()).get();
assertNotNull(errorTextPane);
if (!errorTextPane.isVisible()) {
return false;
}
JLabel errorLabel = field("myLabel").ofType(JLabel.class).in(errorTextPane).get();
assertNotNull(errorLabel);
String text = errorLabel.getText();
if (isNullOrEmpty(text)) {
return false;
}
return warningText == null || text.contains(warningText);
}
});
}
}
@@ -0,0 +1,110 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* 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.fixtures;
import com.android.tools.lint.detector.api.TextFormat;
import com.intellij.refactoring.rename.RenameDialog;
import com.intellij.refactoring.ui.ConflictsDialog;
import com.intellij.ui.EditorTextField;
import org.fest.swing.core.Robot;
import org.fest.swing.core.matcher.JTextComponentMatcher;
import org.fest.swing.edt.GuiActionRunner;
import org.fest.swing.edt.GuiQuery;
import org.fest.swing.edt.GuiTask;
import org.jetbrains.annotations.NotNull;
import javax.swing.text.JTextComponent;
import java.awt.event.KeyEvent;
import java.util.regex.Pattern;
import static com.intellij.tests.gui.framework.GuiTests.findAndClickButton;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
public class RenameRefactoringDialogFixture extends IdeaDialogFixture<RenameDialog> {
@NotNull
public static RenameRefactoringDialogFixture find(@NotNull Robot robot) {
return new RenameRefactoringDialogFixture(robot, find(robot, RenameDialog.class));
}
private RenameRefactoringDialogFixture(@NotNull Robot robot, @NotNull DialogAndWrapper<RenameDialog> dialogAndWrapper) {
super(robot, dialogAndWrapper);
}
@NotNull
public RenameRefactoringDialogFixture setNewName(@NotNull final String newName) {
final EditorTextField field = robot().finder().findByType(target(), EditorTextField.class);
GuiActionRunner.execute(new GuiTask() {
@Override
protected void executeInEDT() throws Throwable {
field.requestFocus();
}
});
robot().pressAndReleaseKey(KeyEvent.VK_BACK_SPACE); // to make sure we don't append to existing item on Linux
robot().enterText(newName);
return this;
}
@NotNull
public RenameRefactoringDialogFixture clickRefactor() {
findAndClickButton(this, "Refactor");
return this;
}
public static class ConflictsDialogFixture extends IdeaDialogFixture<ConflictsDialog> {
protected ConflictsDialogFixture(@NotNull Robot robot, @NotNull DialogAndWrapper<ConflictsDialog> dialogAndWrapper) {
super(robot, dialogAndWrapper);
}
@NotNull
public static ConflictsDialogFixture find(@NotNull Robot robot) {
return new ConflictsDialogFixture(robot, find(robot, ConflictsDialog.class));
}
@NotNull
public ConflictsDialogFixture clickContinue() {
findAndClickButton(this, "Continue");
return this;
}
public String getHtml() {
final JTextComponent component = robot().finder().find(target(), JTextComponentMatcher.any());
return GuiActionRunner.execute(new GuiQuery<String>() {
@Override
protected String executeInEDT() throws Throwable {
return component.getText();
}
});
}
public String getText() {
String html = getHtml();
return TextFormat.HTML.convertTo(html, TextFormat.TEXT).trim();
}
public void requireMessageText(@NotNull String text) {
assertEquals(text, getText());
}
public void requireMessageTextContains(@NotNull String text) {
assertTrue(getText() + " does not contain expected message fragment " + text, getText().contains(text));
}
public void requireMessageTextMatches(@NotNull String regexp) {
assertTrue(getText() + " does not match " + regexp, Pattern.matches(regexp, getText()));
}
}
}
@@ -0,0 +1,59 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* 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.fixtures;
import org.fest.swing.core.Robot;
import org.fest.swing.core.matcher.DialogMatcher;
import org.fest.swing.core.matcher.JLabelMatcher;
import org.fest.swing.driver.JTextComponentDriver;
import org.fest.swing.fixture.ContainerFixture;
import org.jetbrains.annotations.NotNull;
import javax.swing.text.JTextComponent;
import java.awt.*;
import static com.intellij.tests.gui.framework.GuiTests.findAndClickOkButton;
public class ResourceChooserDialogFixture extends ComponentFixture<ResourceChooserDialogFixture, Dialog>
implements ContainerFixture<Dialog> {
@NotNull
public static ResourceChooserDialogFixture findDialog(@NotNull Robot robot) {
Dialog jDialog = robot.finder().find(DialogMatcher.withTitle("Select Resource Directory").andShowing());
return new ResourceChooserDialogFixture(robot, jDialog);
}
private ResourceChooserDialogFixture(@NotNull Robot robot, Dialog target) {
super(ResourceChooserDialogFixture.class, robot, target);
}
public void setDirectoryName(@NotNull String directory) {
Container parent = robot().finder().find(target(), JLabelMatcher.withText("Directory name:")).getParent();
JTextComponent directoryField = robot().finder().findByType(parent, JTextComponent.class, true);
JTextComponentDriver driver = new JTextComponentDriver(robot());
driver.selectAll(directoryField);
driver.setText(directoryField, directory);
}
@NotNull
public ResourceChooserDialogFixture clickOK() {
findAndClickOkButton(this);
return this;
}
}
@@ -0,0 +1,66 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* 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.fixtures;
import com.intellij.openapi.actionSystem.impl.ActionToolbarImpl;
import org.fest.swing.core.ComponentFinder;
import org.fest.swing.core.GenericTypeMatcher;
import org.fest.swing.core.Robot;
import org.fest.swing.edt.GuiQuery;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import static com.intellij.openapi.actionSystem.ActionPlaces.MAIN_TOOLBAR;
import static org.fest.reflect.core.Reflection.field;
import static org.fest.swing.edt.GuiActionRunner.execute;
public class RunConfigurationComboBoxFixture extends JComponentFixture<RunConfigurationComboBoxFixture, JButton> {
@NotNull
static RunConfigurationComboBoxFixture find(@NotNull final IdeFrameFixture parent) {
ComponentFinder finder = parent.robot().finder();
ActionToolbarImpl toolbar = finder.find(parent.target(), new GenericTypeMatcher<ActionToolbarImpl>(ActionToolbarImpl.class) {
@Override
protected boolean isMatching(@NotNull ActionToolbarImpl toolbar) {
String place = field("myPlace").ofType(String.class).in(toolbar).get();
return MAIN_TOOLBAR.equals(place);
}
});
JButton button = finder.find(toolbar, new GenericTypeMatcher<JButton>(JButton.class) {
@Override
protected boolean isMatching(@NotNull JButton button) {
return button.getClass().getSimpleName().equals("ComboBoxButton");
}
});
return new RunConfigurationComboBoxFixture(parent.robot(), button);
}
private RunConfigurationComboBoxFixture(@NotNull Robot robot, @NotNull JButton target) {
super(RunConfigurationComboBoxFixture.class, robot, target);
}
@Nullable
public String getText() {
return execute(new GuiQuery<String>() {
@Override
protected String executeInEDT() throws Throwable {
return target().getText();
}
});
}
}
@@ -0,0 +1,153 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* 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.fixtures;
import com.intellij.execution.configurations.ConfigurationType;
import com.intellij.tests.gui.matcher.ClassNameMatcher;
import com.intellij.ui.components.JBList;
import com.intellij.ui.popup.PopupFactoryImpl.ActionItem;
import com.intellij.ui.treeStructure.Tree;
import org.fest.swing.cell.JTreeCellReader;
import org.fest.swing.core.GenericTypeMatcher;
import org.fest.swing.core.Robot;
import org.fest.swing.core.matcher.DialogMatcher;
import org.fest.swing.core.matcher.JButtonMatcher;
import org.fest.swing.driver.BasicJListCellReader;
import org.fest.swing.edt.GuiActionRunner;
import org.fest.swing.edt.GuiTask;
import org.fest.swing.fixture.JListFixture;
import org.fest.swing.fixture.JTreeFixture;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import static com.intellij.tests.gui.framework.GuiTests.waitUntilFound;
import static org.fest.reflect.core.Reflection.method;
/**
* Controls the Run Configurations dialog
*/
public class RunConfigurationsDialogFixture extends ComponentFixture<RunConfigurationsDialogFixture, JDialog> {
public RunConfigurationsDialogFixture(@NotNull Robot robot, @NotNull JDialog target) {
super(RunConfigurationsDialogFixture.class, robot, target);
}
@NotNull
public static RunConfigurationsDialogFixture find(@NotNull Robot robot) {
JDialog frame = waitUntilFound(robot, new GenericTypeMatcher<JDialog>(JDialog.class) {
@Override
protected boolean isMatching(@NotNull JDialog dialog) {
return "Run/Debug Configurations".equals(dialog.getTitle()) && dialog.isShowing();
}
});
return new RunConfigurationsDialogFixture(robot, frame);
}
@NotNull
private JButton findButtonByText(@NotNull String text) {
return robot().finder().find(target(), JButtonMatcher.withText(text).andShowing());
}
public void save() {
robot().click(findButtonByText("OK"));
}
public JTreeFixture getTreeFixture() {
Tree tree = robot().finder().findByType(target(), Tree.class, true);
JTreeFixture fixture = new JTreeFixture(robot(), tree);
fixture.replaceCellReader(new RunConfigurationsJTreeCellReader());
return fixture;
}
public void select(String path) {
getTreeFixture().selectPath(path);
}
public void useGradleAwareMake() {
JBList stepsList = robot().finder().findByType(target(), JBList.class);
JListFixture stepsFixture = new JListFixture(robot(), stepsList);
// Find ActionToolbarImpl and ActionButtons inside.
if (!stepsFixture.contents()[0].equals("Make")) {
throw new IllegalStateException("There should be only only one make step, 'Make'.");
}
stepsFixture.clickItem(0);
JPanel beforeRunStepsPanel = robot().finder().find(
target(),
ClassNameMatcher.forClass("com.intellij.execution.impl.BeforeRunStepsPanel", JPanel.class, true));
ActionButtonFixture.findByText("Remove", robot(), beforeRunStepsPanel).click();
ActionButtonFixture.findByText("Add", robot(), beforeRunStepsPanel).click();
JBList popupList = robot().finder().find(
target(),
ClassNameMatcher.forClass("com.intellij.ui.popup.list.ListPopupImpl$MyList", JBList.class, true));
click(popupList, "Gradle-aware Make");
Dialog gradleMakeDialog = robot().finder().find(DialogMatcher.withTitle("Select Gradle Task").andShowing());
robot().click(robot().finder().find(gradleMakeDialog, JButtonMatcher.withText("OK")));
}
private void click(final JBList popupList, String text) {
JListFixture popupFixture = new JListFixture(robot(), popupList);
popupFixture.replaceCellReader(new MakeStepsCellReader());
final int index = popupFixture.item(text).index();
// For some reason calling popupFixture.click(...) doesn't work, but this does:
GuiActionRunner.execute(new GuiTask() {
@Override
protected void executeInEDT() throws Throwable {
popupList.setSelectedIndex(index);
}
});
robot().pressAndReleaseKey(KeyEvent.VK_ENTER);
}
/** {@link JTreeCellReader} that works with this particular tree. */
private static class RunConfigurationsJTreeCellReader implements JTreeCellReader {
@Nullable
@Override
public String valueAt(@NotNull JTree tree, Object modelValue) {
Object userObject = method("getUserObject").withReturnType(Object.class).in(modelValue).invoke();
if (userObject == null) {
return null;
}
if (userObject instanceof ConfigurationType) {
return ((ConfigurationType)userObject).getDisplayName();
}
return userObject.toString();
}
}
private static class MakeStepsCellReader extends BasicJListCellReader {
@Nullable
@Override
public String valueAt(@NotNull JList list, int index) {
Object element = list.getModel().getElementAt(index);
if (element instanceof ActionItem) {
return ((ActionItem)element).getText();
}
return super.valueAt(list, index);
}
}
}
@@ -0,0 +1,24 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* 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.fixtures;
import org.jetbrains.annotations.NotNull;
public class RunToolWindowFixture extends ExecutionToolWindowFixture {
public RunToolWindowFixture(@NotNull IdeFrameFixture frameFixture) {
super("Run", frameFixture);
}
}
@@ -0,0 +1,49 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* 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.fixtures;
import com.intellij.tests.gui.driver.SearchTextFieldDriver;
import com.intellij.ui.SearchTextField;
import org.fest.swing.core.Robot;
import org.fest.swing.fixture.AbstractJComponentFixture;
import org.jetbrains.annotations.NotNull;
public class SearchTextFieldFixture extends AbstractJComponentFixture<SearchTextFieldFixture, SearchTextField, SearchTextFieldDriver> {
public SearchTextFieldFixture(@NotNull Robot robot,
@NotNull SearchTextField target) {
super(SearchTextFieldFixture.class, robot, target);
}
@NotNull
public SearchTextFieldFixture enterText(@NotNull String text) {
driver().enterText(target(), text);
return this;
}
@NotNull
public SearchTextFieldFixture requireText(@NotNull String text) {
driver().requireText(target(), text);
return this;
}
@NotNull
@Override
protected SearchTextFieldDriver createDriver(Robot robot) {
return new SearchTextFieldDriver(robot);
}
}
@@ -0,0 +1,70 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* 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.fixtures;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.util.Ref;
import org.fest.swing.core.GenericTypeMatcher;
import org.fest.swing.core.Robot;
import org.fest.swing.fixture.JRadioButtonFixture;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import static com.intellij.tests.gui.framework.GuiTests.findAndClickOkButton;
import static com.intellij.tests.gui.framework.GuiTests.waitUntilFound;
public class SelectRefactoringDialogFixture extends IdeaDialogFixture<DialogWrapper> {
@NotNull
public static SelectRefactoringDialogFixture findByTitle(@NotNull Robot robot) {
final Ref<DialogWrapper> wrapperRef = new Ref<DialogWrapper>();
JDialog dialog = waitUntilFound(robot, new GenericTypeMatcher<JDialog>(JDialog.class) {
@Override
protected boolean isMatching(@NotNull JDialog dialog) {
if (!"Select Refactoring".equals(dialog.getTitle()) || !dialog.isShowing()) {
return false;
}
DialogWrapper wrapper = getDialogWrapperFrom(dialog, DialogWrapper.class);
if (wrapper != null) {
wrapperRef.set(wrapper);
return true;
}
return false;
}
});
return new SelectRefactoringDialogFixture(robot, dialog, wrapperRef.get());
}
public void selectRenameModule() {
JRadioButton renameModuleCheckbox = robot().finder().find(target(), new GenericTypeMatcher<JRadioButton>(JRadioButton.class) {
@Override
protected boolean isMatching(@NotNull JRadioButton checkBox) {
return "Rename module".equals(checkBox.getText());
}
});
JRadioButtonFixture renameModuleRadioButton = new JRadioButtonFixture(robot(), renameModuleCheckbox);
renameModuleRadioButton.select();
}
public void clickOk() {
findAndClickOkButton(this);
}
private SelectRefactoringDialogFixture(@NotNull Robot robot, @NotNull JDialog target, @NotNull DialogWrapper dialogWrapper) {
super(robot, target, dialogWrapper);
}
}
@@ -0,0 +1,48 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* 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.fixtures;
public class SelectSdkDialogFixture {
//extends IdeaDialogFixture<SelectSdkDialog> {
//@NotNull
//public static SelectSdkDialogFixture find(@NotNull Robot robot) {
// return new SelectSdkDialogFixture(robot, find(robot, SelectSdkDialog.class));
//}
//
//private SelectSdkDialogFixture(@NotNull Robot robot, @NotNull DialogAndWrapper<SelectSdkDialog> dialogAndWrapper) {
// super(robot, dialogAndWrapper);
//}
//
//@NotNull
//public SelectSdkDialogFixture setJdkPath(@NotNull final File path) {
// final JLabel label = robot().finder().find(target(), JLabelMatcher.withText("Select Java JDK:").andShowing());
// execute(new GuiTask() {
// @Override
// protected void executeInEDT() throws Throwable {
// Component textField = label.getLabelFor();
// assertThat(textField).isInstanceOf(JTextField.class);
// ((JTextField)textField).setText(path.getPath());
// }
// });
// return this;
//}
//
//@NotNull
//public SelectSdkDialogFixture clickOk() {
// findAndClickOkButton(this);
// return this;
//}
}
@@ -0,0 +1,222 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* 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.fixtures;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.wm.ToolWindow;
import com.intellij.openapi.wm.ToolWindowManager;
import com.intellij.ui.content.Content;
import org.fest.swing.core.Robot;
import org.fest.swing.edt.GuiQuery;
import org.fest.swing.edt.GuiTask;
import org.fest.swing.timing.Condition;
import org.fest.swing.timing.Timeout;
import org.fest.swing.util.TextMatcher;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import static com.intellij.tests.gui.framework.GuiTests.SHORT_TIMEOUT;
import static com.intellij.tests.gui.framework.GuiTests.THIRTY_SEC_TIMEOUT;
import static org.fest.swing.edt.GuiActionRunner.execute;
import static org.fest.swing.timing.Pause.pause;
public abstract class ToolWindowFixture {
@NotNull protected final String myToolWindowId;
@NotNull protected final Project myProject;
@NotNull protected final Robot myRobot;
@NotNull protected final ToolWindow myToolWindow;
protected ToolWindowFixture(@NotNull final String toolWindowId, @NotNull final Project project, @NotNull Robot robot) {
myToolWindowId = toolWindowId;
myProject = project;
final Ref<ToolWindow> toolWindowRef = new Ref<ToolWindow>();
pause(new Condition("Find tool window with ID '" + toolWindowId + "'") {
@Override
public boolean test() {
ToolWindow toolWindow = ToolWindowManager.getInstance(project).getToolWindow(toolWindowId);
toolWindowRef.set(toolWindow);
return toolWindow != null;
}
}, SHORT_TIMEOUT);
myRobot = robot;
myToolWindow = toolWindowRef.get();
}
@Nullable
protected Content getContent(@NotNull final String displayName) {
activateAndWaitUntilIsVisible();
final Ref<Content> contentRef = new Ref<Content>();
pause(new Condition("finding content '" + displayName + "'") {
@Override
public boolean test() {
Content[] contents = getContents();
for (Content content : contents) {
if (displayName.equals(content.getDisplayName())) {
contentRef.set(content);
return true;
}
}
return false;
}
}, SHORT_TIMEOUT);
return contentRef.get();
}
@Nullable
protected Content getContent(@NotNull final String displayName, @NotNull Timeout timeout) {
long now = System.currentTimeMillis();
long budget = timeout.duration();
activateAndWaitUntilIsVisible(Timeout.timeout(budget));
long revisedNow = System.currentTimeMillis();
budget -= (revisedNow - now);
final Ref<Content> contentRef = new Ref<Content>();
pause(new Condition("finding content with display name " + displayName) {
@Override
public boolean test() {
Content[] contents = getContents();
for (Content content : contents) {
if (displayName.equals(content.getDisplayName())) {
contentRef.set(content);
return true;
}
}
return false;
}
}, Timeout.timeout(budget));
return contentRef.get();
}
@Nullable
protected Content getContent(@NotNull final TextMatcher displayNameMatcher) {
return getContent(displayNameMatcher, SHORT_TIMEOUT);
}
@Nullable
protected Content getContent(@NotNull final TextMatcher displayNameMatcher, @NotNull Timeout timeout) {
long now = System.currentTimeMillis();
long budget = timeout.duration();
activateAndWaitUntilIsVisible(Timeout.timeout(budget));
long revisedNow = System.currentTimeMillis();
budget -= (revisedNow - now);
now = revisedNow;
final Ref<Content> contentRef = new Ref<Content>();
pause(new Condition("finding content matching " + displayNameMatcher.formattedValues()) {
@Override
public boolean test() {
Content[] contents = getContents();
for (Content content : contents) {
String displayName = content.getDisplayName();
if (displayNameMatcher.isMatching(displayName)) {
contentRef.set(content);
return true;
}
}
return false;
}
}, Timeout.timeout(budget));
return contentRef.get();
}
private void activateAndWaitUntilIsVisible() {
activateAndWaitUntilIsVisible(SHORT_TIMEOUT);
}
private void activateAndWaitUntilIsVisible(@NotNull Timeout timeout) {
long now = System.currentTimeMillis();
long budget = timeout.duration();
activate();
budget -= System.currentTimeMillis() - now;
waitUntilIsVisible(Timeout.timeout(budget));
}
@NotNull
private Content[] getContents() {
return myToolWindow.getContentManager().getContents();
}
protected boolean isActive() {
//noinspection ConstantConditions
return execute(new GuiQuery<Boolean>() {
@Override
protected Boolean executeInEDT() throws Throwable {
return myToolWindow.isActive();
}
});
}
public void activate() {
if (isActive()) {
return;
}
final Callback callback = new Callback();
execute(new GuiTask() {
@Override
protected void executeInEDT() throws Throwable {
myToolWindow.activate(callback);
}
});
pause(new Condition("Wait for ToolWindow '" + myToolWindowId + "' to be activated") {
@Override
public boolean test() {
return callback.finished;
}
}, SHORT_TIMEOUT);
}
protected void waitUntilIsVisible() {
waitUntilIsVisible(THIRTY_SEC_TIMEOUT);
}
protected void waitUntilIsVisible(@NotNull Timeout timeout) {
pause(new Condition("Wait for ToolWindow '" + myToolWindowId + "' to be visible") {
@Override
public boolean test() {
if (!isActive()) {
activate();
}
return isVisible();
}
}, timeout);
}
private boolean isVisible() {
//noinspection ConstantConditions
return execute(new GuiQuery<Boolean>() {
@Override
protected Boolean executeInEDT() throws Throwable {
if (!myToolWindow.isVisible()) {
return false;
}
JComponent component = myToolWindow.getComponent();
return component.isVisible() && component.isShowing();
}
});
}
private static class Callback implements Runnable {
volatile boolean finished;
@Override
public void run() {
finished = true;
}
}
}
@@ -0,0 +1,81 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* 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.fixtures;
import com.intellij.execution.testframework.AbstractTestProxy;
import com.intellij.execution.testframework.TestFrameworkRunningModel;
import com.intellij.execution.testframework.TestTreeView;
import org.fest.swing.timing.Condition;
import org.fest.swing.timing.Pause;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Fixture for the tree widget, on the left hand side of "Run" window (when running tests).
*/
public class UnitTestTreeFixture {
private ExecutionToolWindowFixture.ContentFixture myContentFixture;
private final TestTreeView myTreeView;
public UnitTestTreeFixture(@NotNull ExecutionToolWindowFixture.ContentFixture contentFixture,
@NotNull TestTreeView treeView) {
myContentFixture = contentFixture;
myTreeView = treeView;
}
@Nullable
public TestFrameworkRunningModel getModel() {
Pause.pause(new Condition("Wait for the test results model.") {
@Override
public boolean test() {
return myTreeView.getData(TestTreeView.MODEL_DATA_KEY.getName()) != null;
}
});
return TestTreeView.MODEL_DATA_KEY.getData(myTreeView);
}
public boolean isAllTestsPassed() {
return getFailingTestsCount() == 0;
}
public int getFailingTestsCount() {
int count = 0;
AbstractTestProxy root = getModel().getRoot();
for (AbstractTestProxy test : root.getAllTests()) {
if (test.isLeaf() && test.isDefect()) {
count++;
}
}
return count;
}
public int getAllTestsCount() {
int count = 0;
AbstractTestProxy root = getModel().getRoot();
for (AbstractTestProxy test : root.getAllTests()) {
if (test.isLeaf()) {
count++;
}
}
return count;
}
@NotNull
public ExecutionToolWindowFixture.ContentFixture getContent() {
return myContentFixture;
}
}
@@ -0,0 +1,62 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* 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.fixtures;
import com.intellij.openapi.wm.impl.welcomeScreen.FlatWelcomeFrame;
import org.fest.swing.core.Robot;
import org.fest.swing.exception.ComponentLookupException;
import org.jetbrains.annotations.NotNull;
import java.awt.*;
public class WelcomeFrameFixture extends ComponentFixture<WelcomeFrameFixture, FlatWelcomeFrame> {
@NotNull
public static WelcomeFrameFixture find(@NotNull Robot robot) {
for (Frame frame : Frame.getFrames()) {
if (frame instanceof FlatWelcomeFrame && frame.isShowing()) {
return new WelcomeFrameFixture(robot, (FlatWelcomeFrame)frame);
}
}
throw new ComponentLookupException("Unable to find 'Welcome' window");
}
private WelcomeFrameFixture(@NotNull Robot robot, @NotNull FlatWelcomeFrame target) {
super(WelcomeFrameFixture.class, robot, target);
}
@NotNull
public WelcomeFrameFixture createNewProject() {
findActionLinkByActionId("WelcomeScreen.CreateNewProject").click();
return this;
}
@NotNull
public WelcomeFrameFixture importProject() {
findActionLinkByActionId("WelcomeScreen.ImportProject").click();
return this;
}
@NotNull
private ActionLinkFixture findActionLinkByActionId(String actionId) {
return ActionLinkFixture.findByActionId(actionId, robot(), target());
}
@NotNull
public MessagesFixture findMessageDialog(@NotNull String title) {
return MessagesFixture.findByTitle(robot(), target(), title);
}
}
@@ -0,0 +1,105 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* 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.fixtures.newProjectWizard;
import com.intellij.tests.gui.fixtures.ComponentFixture;
import org.fest.swing.core.GenericTypeMatcher;
import org.fest.swing.core.Robot;
import org.fest.swing.fixture.ContainerFixture;
import org.fest.swing.fixture.JButtonFixture;
import org.fest.swing.fixture.JLabelFixture;
import org.fest.swing.fixture.JTextComponentFixture;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import javax.swing.text.JTextComponent;
import static com.intellij.tests.gui.framework.GuiTests.*;
/**
* Base class for fixtures which control wizards that extend {@link DynamicWizard}
*/
public abstract class AbstractWizardFixture<S> extends ComponentFixture<S, JDialog> implements ContainerFixture<JDialog> {
public AbstractWizardFixture(@NotNull Class<S> selfType, @NotNull Robot robot, @NotNull JDialog target) {
super(selfType, robot, target);
}
@NotNull
protected JRootPane findStepWithTitle(@NotNull final String title) {
JRootPane rootPane = target().getRootPane();
waitUntilFound(robot(), rootPane, new GenericTypeMatcher<JLabel>(JLabel.class) {
@Override
protected boolean isMatching(@NotNull JLabel label) {
if (!label.isShowing()) {
return false;
}
return title.equals(label.getText());
}
});
return rootPane;
}
@NotNull
public S clickNext() {
findAndClickButton(this, "Next");
return myself();
}
@NotNull
public S clickFinish() {
findAndClickButton(this, "Finish");
return myself();
}
@NotNull
public S clickCancel() {
findAndClickCancelButton(this);
return myself();
}
@NotNull
public JTextComponentFixture findTextField(@NotNull final String labelText) {
return new JTextComponentFixture(robot(), robot().finder().findByLabel(labelText, JTextComponent.class));
}
@NotNull
public JButtonFixture findWizardButton(@NotNull final String text) {
JButton button = robot().finder().find(target(), new GenericTypeMatcher<JButton>(JButton.class) {
@Override
protected boolean isMatching(@NotNull JButton button) {
String buttonText = button.getText();
if (buttonText != null) {
return buttonText.trim().equals(text) && button.isShowing();
}
return false;
}
});
return new JButtonFixture(robot(), button);
}
@NotNull
public JLabelFixture findLabel(@NotNull final String text) {
JLabel label = waitUntilFound(robot(), target(), new GenericTypeMatcher<JLabel>(JLabel.class) {
@Override
protected boolean isMatching(@NotNull JLabel label) {
return text.equals(label.getText().replaceAll("(?i)<.?html>", ""));
}
});
return new JLabelFixture(robot(), label);
}
}
@@ -0,0 +1,60 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* 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.fixtures.newProjectWizard;
import com.intellij.tests.gui.fixtures.JComponentFixture;
import org.fest.swing.core.GenericTypeMatcher;
import org.fest.swing.core.Robot;
import org.fest.swing.driver.JTextComponentDriver;
import org.fest.swing.fixture.JCheckBoxFixture;
import org.fest.swing.fixture.JComboBoxFixture;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
public abstract class AbstractWizardStepFixture<S> extends JComponentFixture<S, JRootPane> {
protected AbstractWizardStepFixture(@NotNull Class<S> selfType, @NotNull Robot robot, @NotNull JRootPane target) {
super(selfType, robot, target);
}
@NotNull
protected JCheckBoxFixture findCheckBoxWithLabel(@NotNull final String label) {
JCheckBox checkBox = robot().finder().find(target(), new GenericTypeMatcher<JCheckBox>(JCheckBox.class) {
@Override
protected boolean isMatching(@NotNull JCheckBox component) {
return label.equals(component.getText());
}
});
return new JCheckBoxFixture(robot(), checkBox);
}
@NotNull
protected JComboBoxFixture findComboBoxWithLabel(@NotNull String label) {
JComboBox comboBox = robot().finder().findByLabel(target(), label, JComboBox.class, true);
return new JComboBoxFixture(robot(), comboBox);
}
@NotNull
protected JTextField findTextFieldWithLabel(@NotNull String label) {
return robot().finder().findByLabel(target(), label, JTextField.class, true);
}
protected void replaceText(@NotNull JTextField textField, @NotNull String text) {
JTextComponentDriver driver = new JTextComponentDriver(robot());
driver.selectAll(textField);
driver.enterText(textField, text);
}
}
@@ -0,0 +1,49 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* 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.fixtures.newProjectWizard;
import org.fest.swing.core.Robot;
import org.fest.swing.edt.GuiQuery;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import static org.fest.swing.edt.GuiActionRunner.execute;
public class ChooseOptionsForNewFileStepFixture extends AbstractWizardStepFixture<ChooseOptionsForNewFileStepFixture> {
protected ChooseOptionsForNewFileStepFixture(@NotNull Robot robot, @NotNull JRootPane target) {
super(ChooseOptionsForNewFileStepFixture.class, robot, target);
}
@NotNull
public ChooseOptionsForNewFileStepFixture enterActivityName(@NotNull String name) {
JTextField textField = robot().finder().findByLabel(target(), "Activity Name:", JTextField.class, true);
replaceText(textField, name);
return this;
}
@NotNull
public String getLayoutName() {
final JTextField textField = robot().finder().findByLabel("Layout Name:", JTextField.class, true);
//noinspection ConstantConditions
return execute(new GuiQuery<String>() {
@Override
protected String executeInEDT() throws Throwable {
return textField.getText();
}
});
}
}
@@ -0,0 +1,62 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* 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.fixtures.newProjectWizard;
import com.intellij.openapi.ui.TextFieldWithBrowseButton;
import org.fest.swing.core.Robot;
import org.fest.swing.edt.GuiQuery;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.io.File;
import static org.fest.assertions.Assertions.assertThat;
import static org.fest.swing.edt.GuiActionRunner.execute;
public class ConfigureAndroidProjectStepFixture extends AbstractWizardStepFixture<ConfigureAndroidProjectStepFixture> {
protected ConfigureAndroidProjectStepFixture(@NotNull Robot robot, @NotNull JRootPane target) {
super(ConfigureAndroidProjectStepFixture.class, robot, target);
}
@NotNull
public ConfigureAndroidProjectStepFixture enterApplicationName(@NotNull String text) {
JTextField textField = findTextFieldWithLabel("Application name:");
replaceText(textField, text);
return this;
}
@NotNull
public ConfigureAndroidProjectStepFixture enterCompanyDomain(@NotNull String text) {
JTextField textField = findTextFieldWithLabel("Company Domain:");
replaceText(textField, text);
return this;
}
@NotNull
public File getLocationInFileSystem() {
final TextFieldWithBrowseButton locationField = robot().finder().findByType(target(), TextFieldWithBrowseButton.class);
//noinspection ConstantConditions
return execute(new GuiQuery<File>() {
@Override
protected File executeInEDT() throws Throwable {
String location = locationField.getText();
assertThat(location).isNotNull().isNotEmpty();
return new File(location);
}
});
}
}
@@ -0,0 +1,65 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* 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.fixtures.newProjectWizard;
import org.fest.swing.core.Robot;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
public class ConfigureFormFactorStepFixture extends AbstractWizardStepFixture<ConfigureFormFactorStepFixture> {
protected ConfigureFormFactorStepFixture(@NotNull Robot robot, @NotNull JRootPane target) {
super(ConfigureFormFactorStepFixture.class, robot, target);
}
//@NotNull
//public ConfigureFormFactorStepFixture selectMinimumSdkApi(@NotNull final FormFactor formFactor, @NotNull final String api) {
// JCheckBox checkBox = robot().finder().find(target(), new GenericTypeMatcher<JCheckBox>(JCheckBox.class) {
// @Override
// protected boolean isMatching(@NotNull JCheckBox checkBox) {
// String text = checkBox.getText();
// // "startsWith" instead of "equals" because the UI may add "(Not installed)" at the end.
// return text != null && text.startsWith(formFactor.toString());
// }
// });
// AbstractButtonDriver buttonDriver = new AbstractButtonDriver(robot());
// buttonDriver.requireEnabled(checkBox);
// buttonDriver.select(checkBox);
//
// final JComboBox comboBox = robot().finder().findByName(target(), formFactor.id + ".minSdk", JComboBox.class);
// //noinspection ConstantConditions
// int itemIndex = execute(new GuiQuery<Integer>() {
// @Override
// protected Integer executeInEDT() throws Throwable {
// BasicJComboBoxCellReader cellReader = new BasicJComboBoxCellReader();
// int itemCount = comboBox.getItemCount();
// for (int i = 0; i < itemCount; i++) {
// String value = cellReader.valueAt(comboBox, i);
// if (value != null && value.startsWith("API " + api + ":")) {
// return i;
// }
// }
// return -1;
// }
// });
// if (itemIndex < 0) {
// throw new LocationUnavailableException("Unable to find SDK " + api + " in " + formFactor + " drop-down");
// }
// JComboBoxDriver comboBoxDriver = new JComboBoxDriver(robot());
// comboBoxDriver.selectItem(comboBox, itemIndex);
// return this;
//}
}
@@ -0,0 +1,76 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* 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.fixtures.newProjectWizard;
import com.intellij.ide.IdeBundle;
import com.intellij.ui.components.JBList;
import org.fest.swing.core.GenericTypeMatcher;
import org.fest.swing.core.Robot;
import org.fest.swing.fixture.JListFixture;
import org.fest.swing.fixture.JTreeFixture;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
public class NewProjectWizardFixture extends AbstractWizardFixture<NewProjectWizardFixture> {
@NotNull
public static NewProjectWizardFixture find(@NotNull Robot robot) {
JDialog dialog = robot.finder().find(new GenericTypeMatcher<JDialog>(JDialog.class) {
@Override
protected boolean isMatching(@NotNull JDialog dialog) {
return IdeBundle.message("title.new.project").equals(dialog.getTitle()) && dialog.isShowing();
}
});
return new NewProjectWizardFixture(robot, dialog);
}
private NewProjectWizardFixture(@NotNull Robot robot, @NotNull JDialog target) {
super(NewProjectWizardFixture.class, robot, target);
}
@NotNull
public NewProjectWizardFixture selectProjectType(String projectTypeName){
JListFixture projectTypeList = new JListFixture(robot(), robot().finder().findByType(JBList.class, true));
projectTypeList.clickItem(projectTypeName);
return this;
}
@NotNull
public NewProjectWizardFixture selectFramework(String frameworkName){
JTreeFixture frameworkTree = new JTreeFixture(robot(), robot().finder().findByType(JTree.class, true));
//frameworkTree.
return this;
}
@NotNull
public ConfigureAndroidProjectStepFixture getConfigureAndroidProjectStep() {
JRootPane rootPane = findStepWithTitle("Configure your new project");
return new ConfigureAndroidProjectStepFixture(robot(), rootPane);
}
@NotNull
public ConfigureFormFactorStepFixture getConfigureFormFactorStep() {
JRootPane rootPane = findStepWithTitle("Select the form factors your app will run on");
return new ConfigureFormFactorStepFixture(robot(), rootPane);
}
@NotNull
public ChooseOptionsForNewFileStepFixture getChooseOptionsForNewFileStep() {
JRootPane rootPane = findStepWithTitle("Customize the Activity");
return new ChooseOptionsForNewFileStepFixture(robot(), rootPane);
}
}
@@ -0,0 +1,403 @@
/*
* 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.framework;
/**
* Created by karashevich on 30/05/16.
*/
import com.intellij.openapi.application.Application;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectManager;
import com.intellij.openapi.project.ex.ProjectManagerEx;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.wm.WindowManager;
import com.intellij.openapi.wm.impl.WindowManagerImpl;
import com.intellij.openapi.wm.impl.welcomeScreen.WelcomeFrame;
import com.intellij.tests.gui.fixtures.IdeFrameFixture;
import com.intellij.tests.gui.fixtures.WelcomeFrameFixture;
import com.intellij.tests.gui.fixtures.newProjectWizard.NewProjectWizardFixture;
import com.intellij.util.net.HttpConfigurable;
import org.fest.swing.core.BasicRobot;
import org.fest.swing.core.Robot;
import org.fest.swing.edt.GuiQuery;
import org.fest.swing.edt.GuiTask;
import org.fest.swing.timing.Condition;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.input.SAXBuilder;
import org.jdom.xpath.XPath;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.junit.After;
import org.junit.Before;
import org.junit.runner.RunWith;
import java.awt.*;
import java.io.File;
import java.io.IOException;
import static com.intellij.ide.impl.ProjectUtil.closeAndDispose;
import static com.intellij.openapi.util.io.FileUtil.*;
import static com.intellij.openapi.util.io.FileUtilRt.delete;
import static com.intellij.openapi.vfs.VfsUtil.findFileByIoFile;
import static com.intellij.tests.gui.framework.GuiTestRunner.canRunGuiTests;
import static com.intellij.tests.gui.framework.GuiTests.*;
import static junit.framework.Assert.assertNotNull;
import static org.fest.assertions.Assertions.assertThat;
import static org.fest.swing.edt.GuiActionRunner.execute;
import static org.fest.swing.timing.Pause.pause;
import static org.fest.util.Strings.quote;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@RunWith(GuiTestRunner.class)
public abstract class GuiTestCase {
protected Robot myRobot;
@SuppressWarnings("UnusedDeclaration") // This field is set via reflection.
private String myTestName;
protected IdeFrameFixture myProjectFrame;
/**
* @return the name of the test method being executed.
*/
protected String getTestName() {
return myTestName;
}
@Before
public void setUp() throws Exception {
if (!canRunGuiTests()) {
// We currently do not support running UI tests in headless environments.
return;
}
Application application = ApplicationManager.getApplication();
assertNotNull(application); // verify that we are using the IDE's ClassLoader.
setUpDefaultProjectCreationLocationPath();
myRobot = BasicRobot.robotWithCurrentAwtHierarchy();
myRobot.settings().delayBetweenEvents(30);
setIdeSettings();
setUpSdks();
// There is a race condition between reloading the configuration file after file deletion detected and the serialization of IDEA model
// we just customized so that modules can't be loaded correctly.
// This is a hack to prevent StoreAwareProjectManager from doing any reloading during test.
ProjectManagerEx.getInstanceEx().blockReloadingProjectOnExternalChanges();
refreshFiles();
}
private static void setIdeSettings() {
// Clear HTTP proxy settings, in case a test changed them.
HttpConfigurable ideSettings = HttpConfigurable.getInstance();
ideSettings.USE_HTTP_PROXY = false;
ideSettings.PROXY_HOST = "";
ideSettings.PROXY_PORT = 80;
// TODO: setUpDefaultGeneralSettings();
}
@After
public void tearDown() {
if (myProjectFrame != null) {
myProjectFrame.waitForBackgroundTasksToFinish();
}
if (myRobot != null) {
myRobot.cleanUpWithoutDisposingWindows();
// We close all modal dialogs left over, because they block the AWT thread and could trigger a deadlock in the next test.
for (Window window : Window.getWindows()) {
if (window.isShowing() && window instanceof Dialog) {
if (((Dialog) window).getModalityType() == Dialog.ModalityType.APPLICATION_MODAL) {
fail("Modal dialog still active: " + window);
myRobot.close(window);
}
}
}
}
ProjectManagerEx.getInstanceEx().unblockReloadingProjectOnExternalChanges();
}
@NotNull
protected WelcomeFrameFixture findWelcomeFrame() {
return WelcomeFrameFixture.find(myRobot);
}
@NotNull
protected NewProjectWizardFixture findNewProjectWizard() {
return NewProjectWizardFixture.find(myRobot);
}
@NotNull
protected IdeFrameFixture findIdeFrame(@NotNull String projectName, @NotNull File projectPath) {
return IdeFrameFixture.find(myRobot, projectPath, projectName);
}
@SuppressWarnings("UnusedDeclaration")
// Called by GuiTestRunner via reflection.
protected void closeAllProjects() {
pause(new Condition("Close all projects") {
@Override
public boolean test() {
final Project[] openProjects = ProjectManager.getInstance().getOpenProjects();
execute(new GuiTask() {
@Override
protected void executeInEDT() throws Throwable {
for (Project project : openProjects) {
assertTrue("Failed to close project " + quote(project.getName()), closeAndDispose(project));
}
}
});
return ProjectManager.getInstance().getOpenProjects().length == 0;
}
}, SHORT_TIMEOUT);
//noinspection ConstantConditions
boolean welcomeFrameShown = execute(new GuiQuery<Boolean>() {
@Override
protected Boolean executeInEDT() throws Throwable {
Project[] openProjects = ProjectManager.getInstance().getOpenProjects();
if (openProjects.length == 0) {
WelcomeFrame.showNow();
WindowManagerImpl windowManager = (WindowManagerImpl)WindowManager.getInstance();
windowManager.disposeRootFrame();
return true;
}
return false;
}
});
if (welcomeFrameShown) {
pause(new Condition("'Welcome' frame to show up") {
@Override
public boolean test() {
for (Frame frame : Frame.getFrames()) {
if (frame == WelcomeFrame.getInstance() && frame.isShowing()) {
return true;
}
}
return false;
}
}, SHORT_TIMEOUT);
}
}
@NotNull
protected IdeFrameFixture importSimpleApplication() throws IOException {
return importProjectAndWaitForProjectSyncToFinish("SimpleApplication");
}
@NotNull
protected IdeFrameFixture importMultiModule() throws IOException {
return importProjectAndWaitForProjectSyncToFinish("MultiModule");
}
@NotNull
protected IdeFrameFixture importProjectAndWaitForProjectSyncToFinish(@NotNull String projectDirName) throws IOException {
return importProjectAndWaitForProjectSyncToFinish(projectDirName, null);
}
@NotNull
protected IdeFrameFixture importProjectAndWaitForProjectSyncToFinish(@NotNull String projectDirName, @Nullable String gradleVersion)
throws IOException {
File projectPath = setUpProject(projectDirName, false);
VirtualFile toSelect = findFileByIoFile(projectPath, false);
assertNotNull(toSelect);
doImportProject(toSelect);
IdeFrameFixture projectFrame = findIdeFrame(projectPath);
//projectFrame.waitForGradleProjectSyncToFinish();
return projectFrame;
}
@NotNull
protected File importProject(@NotNull String projectDirName) throws IOException {
File projectPath = setUpProject(projectDirName, false);
VirtualFile toSelect = findFileByIoFile(projectPath, false);
assertNotNull(toSelect);
doImportProject(toSelect);
return projectPath;
}
private static void doImportProject(@NotNull final VirtualFile projectDir) {
execute(new GuiTask() {
@Override
protected void executeInEDT() throws Throwable {
}
});
}
/**
* Sets up a project before using it in a UI test:
* <ul>
* <li>Makes a copy of the project in testData/guiTests/newProjects (deletes any existing copy of the project first.) This copy is
* the one the test will use.</li>
* <li>Creates a Gradle wrapper for the test project.</li>
* <li>Updates the version of the Android Gradle plug-in used by the project, if applicable</li>
* <li>Creates a local.properties file pointing to the Android SDK path specified by the system property (or environment variable)
* 'ADT_TEST_SDK_PATH'</li>
* <li>Copies over missing files to the .idea directory (if the project will be opened, instead of imported.)</li>
* <li>Deletes .idea directory, .iml files and build directories, if the project will be imported.</li>
* <p/>
* </ul>
*
* @param projectDirName the name of the project's root directory. Tests are located in testData/guiTests.
* @param forOpen indicates whether the project will be opened by the IDE, or imported.
* @param updateAndroidPluginVersion indicates if the latest supported version of the Android Gradle plug-in should be set in the
* project.
* @param gradleVersion the Gradle version to use in the wrapper. If {@code null} is passed, this method will use the latest supported
* version of Gradle.
* @return the path of project's root directory (the copy of the project, not the original one.)
* @throws IOException if an unexpected I/O error occurs.
*/
@NotNull
private File setUpProject(@NotNull String projectDirName,
boolean forOpen) throws IOException {
File projectPath = copyProjectBeforeOpening(projectDirName);
updateLocalProperties(projectPath);
if (forOpen) {
File toDotIdea = new File(projectPath, Project.DIRECTORY_STORE_FOLDER);
ensureExists(toDotIdea);
File fromDotIdea = new File(getTestProjectsRootDirPath(), join("commonFiles", Project.DIRECTORY_STORE_FOLDER));
assertThat(fromDotIdea).isDirectory();
for (File from : notNullize(fromDotIdea.listFiles())) {
if (from.isDirectory()) {
File destination = new File(toDotIdea, from.getName());
if (!destination.isDirectory()) {
copyDirContent(from, destination);
}
continue;
}
File to = new File(toDotIdea, from.getName());
if (!to.isFile()) {
copy(from, to);
}
}
}
else {
cleanUpProjectForImport(projectPath);
}
return projectPath;
}
@NotNull
protected File copyProjectBeforeOpening(@NotNull String projectDirName) throws IOException {
File masterProjectPath = getMasterProjectDirPath(projectDirName);
File projectPath = getTestProjectDirPath(projectDirName);
if (projectPath.isDirectory()) {
delete(projectPath);
System.out.println(String.format("Deleted project path '%1$s'", projectPath.getPath()));
}
copyDir(masterProjectPath, projectPath);
System.out.println(String.format("Copied project '%1$s' to path '%2$s'", projectDirName, projectPath.getPath()));
return projectPath;
}
protected void updateLocalProperties(File projectPath) throws IOException {
//File androidHomePath = IdeSdks.getAndroidSdkPath();
//assertNotNull(androidHomePath);
//
//LocalProperties localProperties = new LocalProperties(projectPath);
//localProperties.setAndroidSdkPath(androidHomePath);
//localProperties.save();
}
@NotNull
protected File getMasterProjectDirPath(@NotNull String projectDirName) {
return new File(getTestProjectsRootDirPath(), projectDirName);
}
@NotNull
protected File getTestProjectDirPath(@NotNull String projectDirName) {
return new File(getProjectCreationDirPath(), projectDirName);
}
protected void cleanUpProjectForImport(@NotNull File projectPath) {
File dotIdeaFolderPath = new File(projectPath, Project.DIRECTORY_STORE_FOLDER);
if (dotIdeaFolderPath.isDirectory()) {
File modulesXmlFilePath = new File(dotIdeaFolderPath, "modules.xml");
if (modulesXmlFilePath.isFile()) {
SAXBuilder saxBuilder = new SAXBuilder();
try {
Document document = saxBuilder.build(modulesXmlFilePath);
XPath xpath = XPath.newInstance("//*[@fileurl]");
//noinspection unchecked
java.util.List<Element> modules = xpath.selectNodes(document);
int urlPrefixSize = "file://$PROJECT_DIR$/".length();
for (Element module : modules) {
String fileUrl = module.getAttributeValue("fileurl");
if (!StringUtil.isEmpty(fileUrl)) {
String relativePath = toSystemDependentName(fileUrl.substring(urlPrefixSize));
File imlFilePath = new File(projectPath, relativePath);
if (imlFilePath.isFile()) {
delete(imlFilePath);
}
// It is likely that each module has a "build" folder. Delete it as well.
File buildFilePath = new File(imlFilePath.getParentFile(), "build");
if (buildFilePath.isDirectory()) {
delete(buildFilePath);
}
}
}
}
catch (Throwable ignored) {
// if something goes wrong, just ignore. Most likely it won't affect project import in any way.
}
}
delete(dotIdeaFolderPath);
}
}
@NotNull
protected IdeFrameFixture findIdeFrame(@NotNull File projectPath) {
return IdeFrameFixture.find(myRobot, projectPath, null);
}
protected void refreshFiles() {
execute(new GuiTask() {
@Override
protected void executeInEDT() throws Throwable {
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run() {
LocalFileSystem.getInstance().refresh(false /* synchronous */);
}
});
}
});
}
}
@@ -0,0 +1,188 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* 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.framework;
import com.google.common.collect.Maps;
import com.intellij.openapi.projectRoots.JavaSdk;
import com.intellij.openapi.projectRoots.JavaSdkVersion;
import com.intellij.openapi.projectRoots.Sdk;
import org.fest.reflect.reference.TypeRef;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.reflect.Method;
import java.util.Map;
import static org.fest.reflect.core.Reflection.method;
import static org.junit.Assert.assertNotNull;
/**
* Collects configuration information from a UI test method's {@link IdeGuiTest} and {@link IdeGuiTestSetup} annotations and applies it
* to the test before execution, using the the IDE's {@code ClassLoader} (which is the {@code ClassLoader} used by UI tests, to be able to
* access IDE's services, state and components.)
*/
class GuiTestConfigurator {
private static final String CLOSE_PROJECT_BEFORE_EXECUTION_KEY = "closeProjectBeforeExecution";
private static final String RETRY_COUNT_KEY = "retryCount";
private static final String RUN_WITH_MINIMUM_JDK_VERSION_KEY = "runWithMinimumJdkVersion";
private static final String SKIP_SOURCE_GENERATION_ON_SYNC_KEY = "skipSourceGenerationOnSync";
private static final String TAKE_SCREENSHOT_ON_TEST_FAILURE_KEY = "takeScreenshotOnTestFailure";
@NotNull private final String myTestName;
@NotNull private final Object myTest;
@NotNull private final ClassLoader myClassLoader;
@Nullable private final Object myMinimumJdkVersion;
private final boolean myCloseProjectBeforeExecution;
private final int myRetryCount;
private final boolean mySkipSourceGenerationOnSync;
private final boolean myTakeScreenshotOnTestFailure;
@NotNull
static GuiTestConfigurator createNew(@NotNull Method testMethod, @NotNull Object test) throws Throwable {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
Class<?> target = classLoader.loadClass(GuiTestConfigurator.class.getCanonicalName());
Map<String, Object> testConfig = method("extractTestConfiguration").withReturnType(new TypeRef<Map<String, Object>>() {})
.withParameterTypes(Method.class)
.in(target)
.invoke(testMethod);
assertNotNull(testConfig);
return new GuiTestConfigurator(testConfig, testMethod.getName(), test, classLoader);
}
// Invoked using reflection and the IDE's ClassLoader.
@NotNull
private static Map<String, Object> extractTestConfiguration(@NotNull Method testMethod) {
Map<String, Object> config = Maps.newHashMap();
IdeGuiTest guiTest = testMethod.getAnnotation(IdeGuiTest.class);
if (guiTest != null) {
config.put(CLOSE_PROJECT_BEFORE_EXECUTION_KEY, guiTest.closeProjectBeforeExecution());
config.put(RUN_WITH_MINIMUM_JDK_VERSION_KEY, guiTest.runWithMinimumJdkVersion());
config.put(RETRY_COUNT_KEY, guiTest.retryCount());
}
IdeGuiTestSetup guiTestSetup = testMethod.getDeclaringClass().getAnnotation(IdeGuiTestSetup.class);
if (guiTestSetup != null) {
config.put(SKIP_SOURCE_GENERATION_ON_SYNC_KEY, guiTestSetup.skipSourceGenerationOnSync());
config.put(TAKE_SCREENSHOT_ON_TEST_FAILURE_KEY, guiTestSetup.takeScreenshotOnTestFailure());
}
return config;
}
private GuiTestConfigurator(@NotNull Map<String, Object> configuration,
@NotNull String testName,
@NotNull Object test,
@NotNull ClassLoader classLoader) {
myCloseProjectBeforeExecution = getBooleanValue(CLOSE_PROJECT_BEFORE_EXECUTION_KEY, configuration, true);
myMinimumJdkVersion = getValue(RUN_WITH_MINIMUM_JDK_VERSION_KEY, configuration, JavaSdkVersion.class);
myRetryCount = getIntValue(RETRY_COUNT_KEY, configuration, 0);
mySkipSourceGenerationOnSync = getBooleanValue(SKIP_SOURCE_GENERATION_ON_SYNC_KEY, configuration, false);
myTakeScreenshotOnTestFailure = getBooleanValue(TAKE_SCREENSHOT_ON_TEST_FAILURE_KEY, configuration, true);
myTestName = testName;
myTest = test;
myClassLoader = classLoader;
}
private static boolean getBooleanValue(@NotNull String key, @NotNull Map<String, Object> configuration, boolean defaultValue) {
Object value = configuration.get(key);
if (value instanceof Boolean) {
return ((Boolean)value);
}
return defaultValue;
}
@Nullable
private static Object getValue(@NotNull String key, @NotNull Map<String, Object> configuration, @NotNull Class<?> type) {
Object value = configuration.get(key);
if (value != null && value.getClass().getCanonicalName().equals(type.getCanonicalName())) {
return value;
}
return null;
}
private static int getIntValue(@NotNull String key, @NotNull Map<String, Object> configuration, int defaultValue) {
Object value = configuration.get(key);
if (value instanceof Integer) {
return ((Integer)value);
}
return defaultValue;
}
void executeSetupTasks() throws Throwable {
closeAllProjects();
skipSourceGenerationOnSync();
}
private void closeAllProjects() {
if (myCloseProjectBeforeExecution) {
method("closeAllProjects").in(myTest).invoke();
}
}
private void skipSourceGenerationOnSync() throws Throwable {
if (mySkipSourceGenerationOnSync) {
Class<?> target = loadMyClassWithTestClassLoader();
method("doSkipSourceGenerationOnSync").in(target).invoke();
}
}
// Invoked using reflection and the IDE's ClassLoader.
private static void doSkipSourceGenerationOnSync() {
System.out.println("Skipping source generation on project sync.");
}
boolean shouldSkipTest() throws Throwable {
if (myMinimumJdkVersion != null) {
Class<?> target = loadMyClassWithTestClassLoader();
Class<?> javaSdkVersionClass = myClassLoader.loadClass(JavaSdkVersion.class.getCanonicalName());
Boolean hasRequiredJdk = method("hasRequiredJdk").withReturnType(boolean.class)
.withParameterTypes(javaSdkVersionClass)
.in(target)
.invoke(myMinimumJdkVersion);
assertNotNull(hasRequiredJdk);
if (!hasRequiredJdk) {
String jdkVersion = method("getDescription").withReturnType(String.class).in(myMinimumJdkVersion).invoke();
System.out.println(String.format("Skipping test '%1$s'. It needs JDK %2$s or newer.", myTestName, jdkVersion));
return true;
}
}
return false;
}
// Invoked using reflection and the IDE's ClassLoader.
private static boolean hasRequiredJdk(@NotNull JavaSdkVersion jdkVersion) {
//Sdk jdk = IdeSdks.getJdk();
Sdk jdk = null;
assertNotNull("Expecting to have a JDK", jdk);
JavaSdkVersion currentVersion = JavaSdk.getInstance().getVersion(jdk);
return currentVersion != null && currentVersion.isAtLeast(jdkVersion);
}
boolean shouldTakeScreenshotOnFailure() {
return myTakeScreenshotOnTestFailure;
}
int getRetryCount() {
return myRetryCount;
}
@NotNull
private Class<?> loadMyClassWithTestClassLoader() throws ClassNotFoundException {
return myClassLoader.loadClass(GuiTestConfigurator.class.getCanonicalName());
}
}
@@ -0,0 +1,180 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* 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.framework;
import com.google.common.base.Strings;
import org.fest.swing.image.ScreenshotTaker;
import org.jetbrains.annotations.Nullable;
import org.junit.After;
import org.junit.Before;
import org.junit.internal.AssumptionViolatedException;
import org.junit.internal.runners.model.ReflectiveCallable;
import org.junit.internal.runners.statements.Fail;
import org.junit.internal.runners.statements.RunAfters;
import org.junit.internal.runners.statements.RunBefores;
import org.junit.runner.notification.Failure;
import org.junit.runner.notification.RunNotifier;
import org.junit.runners.BlockJUnit4ClassRunner;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.InitializationError;
import org.junit.runners.model.Statement;
import org.junit.runners.model.TestClass;
import java.awt.*;
import java.lang.management.GarbageCollectorMXBean;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import static org.junit.Assert.assertNotNull;
public class GuiTestRunner extends BlockJUnit4ClassRunner {
private TestClass myTestClass;
private final List<GarbageCollectorMXBean> myGarbageCollectorMXBeans = ManagementFactory.getGarbageCollectorMXBeans();
private final MemoryMXBean myMemoryMXBean = ManagementFactory.getMemoryMXBean();
@Nullable private final ScreenshotTaker myScreenshotTaker;
public GuiTestRunner(Class<?> testClass) throws InitializationError {
super(testClass);
myScreenshotTaker = canRunGuiTests() ? new ScreenshotTaker() : null;
// UI_TEST_MODE is set whenever we run UI tests on top of a Studio build. In that case, we
// assume the classpath has been properly configured. Otherwise, if we're running from the
// IDE or an Ant build, we need to check we have access to the classpath of community-main.
if (Strings.isNullOrEmpty(System.getenv("UI_TEST_MODE"))) {
//try {
// A random class which is reachable from module community-main's classpath but not
// module android's classpath.
//Class.forName("git4idea.repo.GitConfig", false, testClass.getClassLoader());
//don't check for git module here
}
//catch (ClassNotFoundException e) {
// throw new InitializationError("Invalid test run configuration. Edit your test configuration and make sure that " +
// "\"Use classpath of module\" is set to \"community-main\", *NOT* \"android\"!");
//}
//}
}
@Override
protected void runChild(final FrameworkMethod method, RunNotifier notifier) {
if (!canRunGuiTests()) {
notifier.fireTestAssumptionFailed(new Failure(describeChild(method), new AssumptionViolatedException("Headless environment")));
System.out.println(String.format("Skipping test '%1$s'. UI tests cannot run in a headless environment.", method.getName()));
} else if (MethodInvoker.doesIdeHaveFatalErrors()) {
notifier.fireTestIgnored(describeChild(method)); // TODO: can we restart the IDE at this point, instead of giving up?
System.out.println(String.format("Skipping test '%1$s': a fatal error has occurred in the IDE", method.getName()));
notifier.pleaseStop();
} else {
printPerfStats();
printTimestamp();
super.runChild(method, notifier);
printTimestamp();
printPerfStats();
}
}
private void printTimestamp() {
final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS");
System.out.println(dateFormat.format(new Date()));
}
private void printPerfStats() {
long gcCount = 0, gcTime = 0;
for (GarbageCollectorMXBean garbageCollectorMXBean : myGarbageCollectorMXBeans) {
gcCount += garbageCollectorMXBean.getCollectionCount();
gcTime += garbageCollectorMXBean.getCollectionTime();
}
System.out.printf("%d garbage collections; cumulative %d ms%n", gcCount, gcTime);
myMemoryMXBean.gc();
System.out.printf("heap: %s%n", myMemoryMXBean.getHeapMemoryUsage());
System.out.printf("non-heap: %s%n", myMemoryMXBean.getNonHeapMemoryUsage());
}
@Override
protected Statement methodBlock(FrameworkMethod method) {
FrameworkMethod newMethod;
try {
loadClassesWithIdeClassLoader();
Method methodFromClassLoader = myTestClass.getJavaClass().getMethod(method.getName());
newMethod = new FrameworkMethod(methodFromClassLoader);
}
catch (Exception e) {
return new Fail(e);
}
Object test;
try {
test = new ReflectiveCallable() {
@Override
protected Object runReflectiveCall() throws Throwable {
return createTest();
}
}.run();
}
catch (Throwable e) {
return new Fail(e);
}
Statement statement = methodInvoker(newMethod, test);
List<FrameworkMethod> beforeMethods = myTestClass.getAnnotatedMethods(Before.class);
if (!beforeMethods.isEmpty()) {
statement = new RunBefores(statement, beforeMethods, test);
}
List<FrameworkMethod> afterMethods = myTestClass.getAnnotatedMethods(After.class);
if (!afterMethods.isEmpty()) {
statement = new RunAfters(statement, afterMethods, test);
}
return statement;
}
public static boolean canRunGuiTests() {
return !GraphicsEnvironment.isHeadless();
}
private void loadClassesWithIdeClassLoader() throws Exception {
ClassLoader ideClassLoader = IdeTestApplication.getInstance().getIdeClassLoader();
Thread.currentThread().setContextClassLoader(ideClassLoader);
Class<?> testClass = getTestClass().getJavaClass();
myTestClass = new TestClass(ideClassLoader.loadClass(testClass.getName()));
}
@Override
protected Object createTest() throws Exception {
return myTestClass != null ? myTestClass.getJavaClass().newInstance() : super.createTest();
}
@Override
protected Statement methodInvoker(final FrameworkMethod method, Object test) {
try {
assertNotNull(myScreenshotTaker);
return new MethodInvoker(method, test, myScreenshotTaker);
}
catch (Throwable e) {
return new Fail(e);
}
}
}
@@ -0,0 +1,562 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* 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.framework;
import com.google.common.collect.Lists;
import org.fest.swing.core.Robot;
import com.intellij.diagnostic.AbstractMessage;
import com.intellij.diagnostic.MessagePool;
import com.intellij.ide.GeneralSettings;
import com.intellij.ide.RecentProjectsManager;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ex.PathManagerEx;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectManager;
import com.intellij.openapi.project.ProjectManagerAdapter;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.wm.IdeFrame;
import com.intellij.openapi.wm.impl.IdeFrameImpl;
import com.intellij.tests.gui.fixtures.IdeFrameFixture;
import com.intellij.ui.components.JBList;
import com.intellij.ui.popup.PopupFactoryImpl;
import com.intellij.ui.popup.list.ListPopupModel;
import org.fest.swing.core.BasicRobot;
import org.fest.swing.core.ComponentFinder;
import org.fest.swing.core.GenericTypeMatcher;
import org.fest.swing.edt.GuiActionRunner;
import org.fest.swing.edt.GuiQuery;
import org.fest.swing.edt.GuiTask;
import org.fest.swing.fixture.ContainerFixture;
import org.fest.swing.fixture.JListFixture;
import org.fest.swing.timing.Condition;
import org.fest.swing.timing.Pause;
import org.fest.swing.timing.Timeout;
import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import static com.google.common.base.Joiner.on;
import static com.google.common.io.Files.createTempDir;
import static com.intellij.openapi.projectRoots.JdkUtil.checkForJdk;
import static com.intellij.openapi.util.text.StringUtil.isNotEmpty;
import static com.intellij.util.containers.ContainerUtil.getFirstItem;
import static java.util.concurrent.TimeUnit.MINUTES;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.fest.swing.edt.GuiActionRunner.execute;
import static org.fest.swing.finder.WindowFinder.findFrame;
import static org.fest.swing.timing.Pause.pause;
import static org.fest.swing.timing.Timeout.timeout;
import static org.fest.util.Strings.isNullOrEmpty;
import static org.fest.util.Strings.quote;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
public final class GuiTests {
public static final Timeout THIRTY_SEC_TIMEOUT = timeout(30, SECONDS);
public static final Timeout SHORT_TIMEOUT = timeout(2, MINUTES);
public static final Timeout LONG_TIMEOUT = timeout(5, MINUTES);
public static final String GUI_TESTS_RUNNING_IN_SUITE_PROPERTY = "gui.tests.running.in.suite";
/** Environment variable pointing to the JDK to be used for tests */
public static final String JDK_HOME_FOR_TESTS = "JDK_HOME_FOR_TESTS";
private static final EventQueue SYSTEM_EVENT_QUEUE = Toolkit.getDefaultToolkit().getSystemEventQueue();
private static final File TMP_PROJECT_ROOT = createTempProjectCreationDir();
// Called by MethodInvoker via reflection
@SuppressWarnings("unused")
public static void failIfIdeHasFatalErrors() {
final MessagePool messagePool = MessagePool.getInstance();
List<AbstractMessage> fatalErrors = messagePool.getFatalErrors(true, true);
int fatalErrorCount = fatalErrors.size();
for (int i = 0; i < fatalErrorCount; i++) {
System.err.println("** Fatal Error " + (i + 1) + " of " + fatalErrorCount);
AbstractMessage error = fatalErrors.get(i);
System.err.println("* Message: ");
System.err.println(error.getMessage());
String additionalInfo = error.getAdditionalInfo();
if (isNotEmpty(additionalInfo)) {
System.err.println("* Additional Info: ");
System.err.println(additionalInfo);
}
String throwableText = error.getThrowableText();
if (isNotEmpty(throwableText)) {
System.err.println("* Throwable: ");
System.err.println(throwableText);
}
System.err.println();
}
if (fatalErrorCount > 0) {
throw new AssertionError(fatalErrorCount + " fatal errors found. Stopping test execution.");
}
}
// Called by MethodInvoker via reflection
@SuppressWarnings("unused")
public static boolean doesIdeHaveFatalErrors() {
final MessagePool messagePool = MessagePool.getInstance();
List<AbstractMessage> fatalErrors = messagePool.getFatalErrors(true, true);
return !fatalErrors.isEmpty();
}
// Called by IdeTestApplication via reflection.
@SuppressWarnings("unused")
public static void setUpDefaultGeneralSettings() {
//setGuiTestingMode(true);
GeneralSettings.getInstance().setShowTipsOnStartup(false);
setUpDefaultProjectCreationLocationPath();
setUpSdks();
}
public static void setUpSdks() {
String jdkHome = getSystemPropertyOrEnvironmentVariable(JDK_HOME_FOR_TESTS);
if (isNullOrEmpty(jdkHome) || !checkForJdk(jdkHome)) {
fail("Please specify the path to a valid JDK using system property " + JDK_HOME_FOR_TESTS);
}
final File jdkPath = new File(jdkHome);
execute(new GuiTask() {
@Override
protected void executeInEDT() throws Throwable {
//File currentAndroidSdkPath = IdeSdks.getAndroidSdkPath();
//File currentJdkPath = IdeSdks.getJdkPath();
}
});
}
@Nullable
public static File getGradleHomePath() {
return getFilePathProperty("supported.gradle.home.path", "the path of a local Gradle 2.2.1 distribution", true);
}
@Nullable
public static File getUnsupportedGradleHome() {
return getGradleHomeFromSystemProperty("unsupported.gradle.home.path", "2.1");
}
@Nullable
public static File getGradleHomeFromSystemProperty(@NotNull String propertyName, @NotNull String gradleVersion) {
String description = "the path of a Gradle " + gradleVersion + " distribution";
return getFilePathProperty(propertyName, description, true);
}
@Nullable
public static File getFilePathProperty(@NotNull String propertyName,
@NotNull String description,
boolean isDirectory) {
String pathValue = System.getProperty(propertyName);
if (!isNullOrEmpty(pathValue)) {
File path = new File(pathValue);
if (isDirectory && path.isDirectory() || !isDirectory && path.isFile()) {
return path;
}
}
System.out.println("Please specify " + description + ", using system property " + quote(propertyName));
return null;
}
public static void setUpDefaultProjectCreationLocationPath() {
RecentProjectsManager.getInstance().setLastProjectCreationLocation(getProjectCreationDirPath().getPath());
}
// Called by IdeTestApplication via reflection.
@SuppressWarnings("UnusedDeclaration")
public static void waitForIdeToStart() {
GuiActionRunner.executeInEDT(false);
Robot robot = null;
try {
robot = BasicRobot.robotWithCurrentAwtHierarchy();
final MyProjectManagerListener listener = new MyProjectManagerListener();
findFrame(new GenericTypeMatcher<Frame>(Frame.class) {
@Override
protected boolean isMatching(@NotNull Frame frame) {
if (frame instanceof IdeFrame) {
if (frame instanceof IdeFrameImpl) {
listener.myActive = true;
ProjectManager.getInstance().addProjectManagerListener(listener);
}
return true;
}
return false;
}
}).withTimeout(LONG_TIMEOUT.duration()).using(robot);
// We know the IDE event queue was pushed in front of the AWT queue. Some JDKs will leave a dummy event in the AWT queue, which
// we attempt to clear here. All other events, including those posted by the Robot, will go through the IDE event queue.
try {
if (SYSTEM_EVENT_QUEUE.peekEvent() != null) {
SYSTEM_EVENT_QUEUE.getNextEvent();
}
} catch (InterruptedException ex ) {
// Ignored.
}
if (listener.myActive) {
pause(new Condition("Project to be opened") {
@Override
public boolean test() {
boolean notified = listener.myNotified;
if (notified) {
ProgressManager progressManager = ProgressManager.getInstance();
boolean isIdle = !progressManager.hasModalProgressIndicator() &&
!progressManager.hasProgressIndicator() &&
!progressManager.hasUnsafeProgressIndicator();
if (isIdle) {
ProjectManager.getInstance().removeProjectManagerListener(listener);
}
return isIdle;
}
return false;
}
}, LONG_TIMEOUT);
}
}
finally {
GuiActionRunner.executeInEDT(true);
if (robot != null) {
robot.cleanUpWithoutDisposingWindows();
}
}
}
@NotNull
public static File getProjectCreationDirPath() {
return TMP_PROJECT_ROOT;
}
@NotNull
public static File createTempProjectCreationDir() {
try {
// The temporary location might contain symlinks, such as /var@ -> /private/var on MacOS.
// EditorFixture seems to require a canonical path when opening the file.
return createTempDir().getCanonicalFile();
}
catch (IOException ex) {
// For now, keep the original behavior and point inside the source tree.
ex.printStackTrace();
return new File(getTestProjectsRootDirPath(), "newProjects");
}
}
@NotNull
public static File getTestProjectsRootDirPath() {
final String path = PathManagerEx.getTestDataPath(GuiTestCase.class);
return new File(path);
//String testDataPath = AndroidTestBase.getTestDataPath();
//assertNotNull(testDataPath);
//assertThat(testDataPath).isNotEmpty();
//testDataPath = toCanonicalPath(toSystemDependentName(testDataPath));
//return new File(testDataPath, "guiTests");
}
private GuiTests() {
}
public static void deleteFile(@Nullable final VirtualFile file) {
// File deletion must happen on UI thread under write lock
if (file != null) {
execute(new GuiTask() {
@Override
protected void executeInEDT() throws Throwable {
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run() {
try {
file.delete(this);
}
catch (IOException e) {
// ignored
}
}
});
}
});
}
}
/** Waits until an IDE popup is shown (and returns it */
public static JBList waitForPopup(@NotNull Robot robot) {
return waitUntilFound(robot, null, new GenericTypeMatcher<JBList>(JBList.class) {
@Override
protected boolean isMatching(@NotNull JBList list) {
ListModel model = list.getModel();
return model instanceof ListPopupModel;
}
});
}
/**
* Clicks an IntelliJ/Studio popup menu item with the given label prefix
*
* @param labelPrefix the target menu item label prefix
* @param component a component in the same window that the popup menu is associated with
* @param robot the robot to drive it with
*/
public static void clickPopupMenuItem(@NotNull String labelPrefix, @NotNull Component component, @NotNull Robot robot) {
clickPopupMenuItemMatching(new PrefixMatcher(labelPrefix), component, robot);
}
public static void clickPopupMenuItemMatching(@NotNull Matcher<String> labelMatcher, @NotNull Component component, @NotNull Robot robot) {
// IntelliJ doesn't seem to use a normal JPopupMenu, so this won't work:
// JPopupMenu menu = myRobot.findActivePopupMenu();
// Instead, it uses a JList (technically a JBList), which is placed somewhere
// under the root pane.
Container root = getRootContainer(component);
// First find the JBList which holds the popup. There could be other JBLists in the hierarchy,
// so limit it to one that is actually used as a popup, as identified by its model being a ListPopupModel:
assertNotNull(root);
JBList list = robot.finder().find(root, new GenericTypeMatcher<JBList>(JBList.class) {
@Override
protected boolean isMatching(@NotNull JBList list) {
ListModel model = list.getModel();
return model instanceof ListPopupModel;
}
});
// We can't use the normal JListFixture method to click by label since the ListModel items are
// ActionItems whose toString does not reflect the text, so search through the model items instead:
ListPopupModel model = (ListPopupModel)list.getModel();
List<String> items = Lists.newArrayList();
for (int i = 0; i < model.getSize(); i++) {
Object elementAt = model.getElementAt(i);
if (elementAt instanceof PopupFactoryImpl.ActionItem) {
PopupFactoryImpl.ActionItem item = (PopupFactoryImpl.ActionItem)elementAt;
String s = item.getText();
if (labelMatcher.matches(s)) {
new JListFixture(robot, list).clickItem(i);
return;
}
items.add(s);
} else { // For example package private class IntentionActionWithTextCaching used in quickfix popups
String s = elementAt.toString();
if (labelMatcher.matches(s)) {
new JListFixture(robot, list).clickItem(i);
return;
}
items.add(s);
}
}
if (items.isEmpty()) {
fail("Could not find any menu items in popup");
}
fail("Did not find menu item '" + labelMatcher + "' among " + on(", ").join(items));
}
/** Returns the root container containing the given component */
@Nullable
public static Container getRootContainer(@NotNull final Component component) {
return execute(new GuiQuery<Container>() {
@Override
@Nullable
protected Container executeInEDT() throws Throwable {
return (Container)SwingUtilities.getRoot(component);
}
});
}
public static void findAndClickOkButton(@NotNull ContainerFixture<? extends Container> container) {
findAndClickButton(container, "OK");
}
public static void findAndClickCancelButton(@NotNull ContainerFixture<? extends Container> container) {
findAndClickButton(container, "Cancel");
}
public static void findAndClickButton(@NotNull ContainerFixture<? extends Container> container, @NotNull final String text) {
Robot robot = container.robot();
JButton button = findButton(container, text, robot);
robot.click(button);
}
public static void findAndClickButtonWhenEnabled(@NotNull ContainerFixture<? extends Container> container, @NotNull final String text) {
Robot robot = container.robot();
final JButton button = findButton(container, text, robot);
pause(new Condition("Wait for button " + text + " to be enabled.") {
@Override
public boolean test() {
return button.isEnabled() && button.isVisible() && button.isShowing();
}
}, SHORT_TIMEOUT);
robot.click(button);
}
public static void invokeMenuPathOnRobotIdle(IdeFrameFixture projectFrame, String... path) {
projectFrame.robot().waitForIdle();
projectFrame.invokeMenuPath(path);
}
/**
* Opens the file with basename {@code fileBasename}
*/
public static void openFile(IdeFrameFixture projectFrame, String fileBasename) {
invokeMenuPathOnRobotIdle(projectFrame, "Navigate", "File...");
projectFrame.robot().waitForIdle();
typeText("multifunction-jni.c", projectFrame.robot(), 30);
projectFrame.robot().pressAndReleaseKey(KeyEvent.VK_ENTER);
}
/**
* Navigates to line number {@code lineNum} of the currently active editor window.
*/
public static void navigateToLine(IdeFrameFixture projectFrame, int lineNum) {
invokeMenuPathOnRobotIdle(projectFrame, "Navigate", "Line...");
projectFrame.robot().enterText(Integer.toString(lineNum));
projectFrame.robot().waitForIdle();
projectFrame.robot().pressAndReleaseKey(KeyEvent.VK_ENTER);
}
private static void typeText(String text, Robot robot, long delayAfterEachCharacterMillis) {
robot.waitForIdle();
for (int i = 0; i < text.length(); ++i) {
robot.type(text.charAt(i));
Pause.pause(delayAfterEachCharacterMillis, TimeUnit.MILLISECONDS);
}
}
@NotNull
private static JButton findButton(@NotNull ContainerFixture<? extends Container> container, @NotNull final String text, Robot robot) {
return robot.finder().find(container.target(), new GenericTypeMatcher<JButton>(JButton.class) {
@Override
protected boolean isMatching(@NotNull JButton button) {
String buttonText = button.getText();
if (buttonText != null) {
return buttonText.trim().equals(text) && button.isShowing();
}
return false;
}
});
}
/** Returns a full path to the GUI data directory in the user's AOSP source tree, if known, or null */
//@Nullable
//public static File getTestDataDir() {
// File aosp = getAospSourceDir();
// return aosp != null ? new File(aosp, RELATIVE_DATA_PATH) : null;
//}
/** Waits for a first component which passes the given matcher to become visible */
@NotNull
public static <T extends Component> T waitUntilFound(@NotNull final Robot robot, @NotNull final GenericTypeMatcher<T> matcher) {
return waitUntilFound(robot, null, matcher);
}
public static void skip(@NotNull String testName) {
System.out.println("Skipping test '" + testName + "'");
}
/** Waits for a first component which passes the given matcher under the given root to become visible. */
@NotNull
public static <T extends Component> T waitUntilFound(@NotNull final Robot robot,
@Nullable final Container root,
@NotNull final GenericTypeMatcher<T> matcher) {
final AtomicReference<T> reference = new AtomicReference<T>();
pause(new Condition("Find component using " + matcher.toString()) {
@Override
public boolean test() {
ComponentFinder finder = robot.finder();
Collection<T> allFound = root != null ? finder.findAll(root, matcher) : finder.findAll(matcher);
boolean found = allFound.size() == 1;
if (found) {
reference.set(getFirstItem(allFound));
}
else if (allFound.size() > 1) {
// Only allow a single component to be found, otherwise you can get some really confusing
// test failures; the matcher should pick a specific enough instance
fail("Found more than one " + matcher.supportedType().getSimpleName() + " which matches the criteria: " + allFound);
}
return found;
}
}, SHORT_TIMEOUT);
return reference.get();
}
/** Waits until no components match the given criteria under the given root */
public static <T extends Component> void waitUntilGone(@NotNull final Robot robot,
@NotNull final Container root,
@NotNull final GenericTypeMatcher<T> matcher) {
pause(new Condition("Find component using " + matcher.toString()) {
@Override
public boolean test() {
Collection<T> allFound = robot.finder().findAll(root, matcher);
return allFound.isEmpty();
}
}, SHORT_TIMEOUT);
}
@Nullable
public static String getSystemPropertyOrEnvironmentVariable(@NotNull String name) {
String s = System.getProperty(name);
return s == null ? System.getenv(name) : s;
}
private static class MyProjectManagerListener extends ProjectManagerAdapter {
boolean myActive;
boolean myNotified;
@Override
public void projectOpened(Project project) {
myNotified = true;
}
}
private static class PrefixMatcher extends BaseMatcher<String> {
private final String prefix;
public PrefixMatcher(String prefix) {
this.prefix = prefix;
}
@Override
public boolean matches(Object item) {
return item instanceof String && ((String)item).startsWith(prefix);
}
@Override
public void describeTo(Description description) {
description.appendText("with prefix '" + prefix +"'");
}
}
}
@@ -0,0 +1,33 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* 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.framework;
import com.intellij.openapi.projectRoots.JavaSdkVersion;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import static com.intellij.openapi.projectRoots.JavaSdkVersion.JDK_1_6;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface IdeGuiTest {
boolean closeProjectBeforeExecution() default true;
int retryCount() default 0;
JavaSdkVersion runWithMinimumJdkVersion() default JDK_1_6;
}
@@ -0,0 +1,31 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* 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.framework;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Settings to be applied when executing every test method in a class.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface IdeGuiTestSetup {
boolean skipSourceGenerationOnSync() default false;
boolean takeScreenshotOnTestFailure() default true;
}
@@ -0,0 +1,274 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* 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.framework;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.intellij.ide.BootstrapClassLoaderUtil;
import com.intellij.ide.WindowsCommandLineProcessor;
import com.intellij.ide.startup.StartupActionScriptManager;
import com.intellij.idea.Main;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.Application;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.PathManager;
import com.intellij.openapi.application.ex.ApplicationEx;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.util.SystemProperties;
import com.intellij.util.lang.UrlClassLoader;
import com.intellij.util.text.StringTokenizer;
import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Pattern;
import static com.intellij.openapi.application.PathManager.PROPERTY_CONFIG_PATH;
import static com.intellij.openapi.util.io.FileUtil.*;
import static com.intellij.openapi.util.text.StringUtil.isNotEmpty;
import static com.intellij.tests.gui.framework.GuiTests.getProjectCreationDirPath;
import static com.intellij.util.ArrayUtil.EMPTY_STRING_ARRAY;
import static com.intellij.util.ui.UIUtil.initDefaultLAF;
import static org.fest.assertions.Assertions.assertThat;
import static org.fest.reflect.core.Reflection.method;
import static org.junit.Assert.assertNotNull;
public class IdeTestApplication implements Disposable {
private static final Logger LOG = Logger.getInstance(IdeTestApplication.class);
private static final String PROPERTY_IGNORE_CLASSPATH = "ignore.classpath";
private static final String PROPERTY_ALLOW_BOOTSTRAP_RESOURCES = "idea.allow.bootstrap.resources";
private static final String PROPERTY_ADDITIONAL_CLASSPATH = "idea.additional.classpath";
private static IdeTestApplication ourInstance;
@NotNull private final ClassLoader myIdeClassLoader;
@NotNull
public static synchronized IdeTestApplication getInstance() throws Exception {
//System.setProperty(PLATFORM_PREFIX_KEY, "AndroidStudio");
//System.setProperty(PLATFORM_PREFIX_KEY, "idea");
File configDirPath = getConfigDirPath();
System.setProperty(PROPERTY_CONFIG_PATH, configDirPath.getPath());
// Force Swing FileChooser on Mac (instead of native one) to be able to use FEST to drive it.
System.setProperty("native.mac.file.chooser.enabled", "false");
if (!isLoaded()) {
ourInstance = new IdeTestApplication();
recreateDirectory(configDirPath);
File newProjectsRootDirPath = getProjectCreationDirPath();
recreateDirectory(newProjectsRootDirPath);
ClassLoader ideClassLoader = ourInstance.getIdeClassLoader();
Class<?> clazz = ideClassLoader.loadClass(GuiTests.class.getCanonicalName());
method("waitForIdeToStart").in(clazz).invoke();
method("setUpDefaultGeneralSettings").in(clazz).invoke();
}
return ourInstance;
}
@NotNull
private static File getConfigDirPath() throws IOException {
File dirPath = new File(getGuiTestRootDirPath(), "config");
ensureExists(dirPath);
return dirPath;
}
@NotNull
public static File getFailedTestScreenshotDirPath() throws IOException {
File dirPath = new File(getGuiTestRootDirPath(), "failures");
ensureExists(dirPath);
return dirPath;
}
@NotNull
private static File getGuiTestRootDirPath() throws IOException {
String guiTestRootDirPathProperty = System.getProperty("gui.tests.root.dir.path");
if (isNotEmpty(guiTestRootDirPathProperty)) {
File rootDirPath = new File(guiTestRootDirPathProperty);
if (rootDirPath.isDirectory()) {
return rootDirPath;
}
}
String homeDirPath = toSystemDependentName(PathManager.getHomePath());
assertThat(homeDirPath).isNotEmpty();
File rootDirPath = new File(homeDirPath, join("androidStudio", "gui-tests"));
ensureExists(rootDirPath);
return rootDirPath;
}
private static void recreateDirectory(@NotNull File path) throws IOException {
delete(path);
ensureExists(path);
}
private IdeTestApplication() throws Exception {
String[] args = EMPTY_STRING_ARRAY;
LOG.assertTrue(ourInstance == null, "Only one instance allowed.");
ourInstance = this;
pluginManagerStart(args);
mainMain();
myIdeClassLoader = createClassLoader();
//myIdeClassLoader = BootstrapClassLoaderUtil.initClassLoader(false);
WindowsCommandLineProcessor.ourMirrorClass = Class.forName(WindowsCommandLineProcessor.class.getName(), true, myIdeClassLoader);
// We turn on "GUI Testing Mode" right away, even before loading the IDE.
//Class<?> androidPluginClass = Class.forName("org.jetbrains.android.AndroidPlugin", true, myIdeClassLoader);
//method("setGuiTestingMode").withParameterTypes(boolean.class).in(androidPluginClass).invoke(true);
Class<?> classUtilCoreClass = Class.forName("com.intellij.ide.ClassUtilCore", true, myIdeClassLoader);
method("clearJarURLCache").in(classUtilCoreClass).invoke();
Class<?> pluginManagerClass = Class.forName("com.intellij.ide.plugins.PluginManager", true, myIdeClassLoader);
method("start").withParameterTypes(String.class, String.class, String[].class)
.in(pluginManagerClass)
.invoke("com.intellij.idea.MainImpl", "start", args);
}
// This method replaces BootstrapClassLoaderUtil.initClassLoader. The reason behind it is that when running UI tests the ClassLoader
// containing the URLs for the plugin jars is loaded by a different ClassLoader and it gets ignored. The result is test failing because
// classes like AndroidPlugin cannot be found.
@NotNull
private static ClassLoader createClassLoader() throws MalformedURLException, URISyntaxException {
Collection<URL> classpath = Sets.newLinkedHashSet();
addIdeaLibraries(classpath);
addAdditionalClassPath(classpath);
UrlClassLoader.Builder builder = UrlClassLoader.build()
.urls(filterClassPath(Lists.newArrayList(classpath)))
.parent(IdeTestApplication.class.getClassLoader())
.allowLock(false)
.usePersistentClasspathIndexForLocalClassDirectories();
if (SystemProperties.getBooleanProperty(PROPERTY_ALLOW_BOOTSTRAP_RESOURCES, true)) {
builder.allowBootstrapResources();
}
UrlClassLoader newClassLoader = builder.get();
// prepare plugins
try {
StartupActionScriptManager.executeActionScript();
}
catch (IOException e) {
Main.showMessage("Plugin Installation Error", e);
}
Thread.currentThread().setContextClassLoader(newClassLoader);
return newClassLoader;
}
private static void addIdeaLibraries(@NotNull Collection<URL> classpath) throws MalformedURLException {
Class<BootstrapClassLoaderUtil> aClass = BootstrapClassLoaderUtil.class;
String selfRoot = PathManager.getResourceRoot(aClass, "/" + aClass.getName().replace('.', '/') + ".class");
assertNotNull(selfRoot);
URL selfRootUrl = new File(selfRoot).getAbsoluteFile().toURI().toURL();
classpath.add(selfRootUrl);
File libFolder = new File(PathManager.getLibPath());
addLibraries(classpath, libFolder, selfRootUrl);
addLibraries(classpath, new File(libFolder, "ext"), selfRootUrl);
addLibraries(classpath, new File(libFolder, "ant/lib"), selfRootUrl);
}
private static void addLibraries(@NotNull Collection<URL> classPath, @NotNull File fromDir, @NotNull URL selfRootUrl)
throws MalformedURLException {
for (File file : notNullize(fromDir.listFiles())) {
if (isJarOrZip(file)) {
URL url = file.toURI().toURL();
if (!selfRootUrl.equals(url)) {
classPath.add(url);
}
}
}
}
private static void addAdditionalClassPath(@NotNull Collection<URL> classpath) throws MalformedURLException {
StringTokenizer tokenizer = new StringTokenizer(System.getProperty(PROPERTY_ADDITIONAL_CLASSPATH, ""), File.pathSeparator, false);
while (tokenizer.hasMoreTokens()) {
String pathItem = tokenizer.nextToken();
classpath.add(new File(pathItem).toURI().toURL());
}
}
private static List<URL> filterClassPath(@NotNull List<URL> classpath) {
String ignoreProperty = System.getProperty(PROPERTY_IGNORE_CLASSPATH);
if (ignoreProperty != null) {
Pattern pattern = Pattern.compile(ignoreProperty);
for (Iterator<URL> i = classpath.iterator(); i.hasNext(); ) {
String url = i.next().toExternalForm();
if (pattern.matcher(url).matches()) {
i.remove();
}
}
}
return classpath;
}
private static void pluginManagerStart(@NotNull String[] args) {
// Duplicates what PluginManager#start does.
Main.setFlags(args);
initDefaultLAF();
}
private static void mainMain() {
// Duplicates what Main#main does.
method("installPatch").in(Main.class).invoke();
}
@NotNull
public ClassLoader getIdeClassLoader() {
return myIdeClassLoader;
}
@Override
public void dispose() {
disposeInstance();
}
public static synchronized void disposeInstance() {
if (!isLoaded()) {
return;
}
Application application = ApplicationManager.getApplication();
if (application != null) {
if (application instanceof ApplicationEx) {
((ApplicationEx)application).exit(true, true);
}
else {
application.exit();
}
}
ourInstance = null;
}
public static synchronized boolean isLoaded() {
return ourInstance != null;
}
}
@@ -0,0 +1,151 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* 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.framework;
import org.fest.swing.image.ScreenshotTaker;
import org.jetbrains.annotations.NotNull;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.Statement;
import java.io.File;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.GregorianCalendar;
import static com.intellij.tests.gui.framework.GuiTestRunner.canRunGuiTests;
import static com.intellij.tests.gui.framework.IdeTestApplication.getFailedTestScreenshotDirPath;
import static org.fest.reflect.core.Reflection.field;
import static org.fest.reflect.core.Reflection.method;
public class MethodInvoker extends Statement {
@NotNull private final GuiTestConfigurator myTestConfigurator;
@NotNull private final FrameworkMethod myTestMethod;
@NotNull private final Object myTest;
@NotNull private final ScreenshotTaker myScreenshotTaker;
MethodInvoker(@NotNull FrameworkMethod testMethod, @NotNull Object test, @NotNull ScreenshotTaker screenshotTaker) throws Throwable {
myTestConfigurator = GuiTestConfigurator.createNew(testMethod.getMethod(), test);
myTestMethod = testMethod;
myTest = test;
myScreenshotTaker = screenshotTaker;
}
@Override
public void evaluate() throws Throwable {
//if (myTestConfigurator.shouldSkipTest()) {
// Message already printed in console.
//return;
//}
String testFqn = getTestFqn();
if (doesIdeHaveFatalErrors()) {
// Fatal errors were caused by previous test. Skipping this test.
System.out.println(String.format("Skipping test '%1$s': a fatal error has occurred in the IDE", testFqn));
return;
}
System.out.println(String.format("Executing test '%1$s'", testFqn));
int retryCount = myTestConfigurator.getRetryCount();
for (int i = 0; i <= retryCount; i++) {
if (i > 0) {
System.out.println(String.format("Retrying execution of test '%1$s'", testFqn));
}
try {
runTest(i);
break; // no need to retry.
}
catch (Throwable throwable) {
if (retryCount == i) {
throw throwable; // Last run, throw any exceptions caught.
}
else {
throwable.printStackTrace();
failIfIdeHasFatalErrors();
}
}
}
failIfIdeHasFatalErrors();
}
public static boolean doesIdeHaveFatalErrors() {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
try {
Class<?> guiTestsType = Class.forName(GuiTests.class.getCanonicalName(), true, classLoader);
//noinspection ConstantConditions
return method("doesIdeHaveFatalErrors").withReturnType(boolean.class).in(guiTestsType).invoke();
} catch (ClassNotFoundException ex) {
// ignore exception
return true;
}
}
private static void failIfIdeHasFatalErrors() throws ClassNotFoundException {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
Class<?> guiTestsType = Class.forName(GuiTests.class.getCanonicalName(), true, classLoader);
method("failIfIdeHasFatalErrors").in(guiTestsType).invoke();
}
private void runTest(int executionIndex) throws Throwable {
myTestConfigurator.executeSetupTasks();
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
Class<?> guiTestCaseType = Class.forName(GuiTestCase.class.getCanonicalName(), true, classLoader);
if (guiTestCaseType.isInstance(myTest)) {
if (!canRunGuiTests()) {
// We don't run tests in headless environment.
return;
}
field("myTestName").ofType(String.class).in(myTest).set(myTestMethod.getName());
}
try {
myTestMethod.invokeExplosively(myTest);
}
catch (Throwable e) {
e.printStackTrace();
takeScreenshot(executionIndex);
throw e;
}
}
@NotNull
private String getTestFqn() {
return myTestMethod.getMethod().getDeclaringClass() + "#" + myTestMethod.getName();
}
private void takeScreenshot(int executionIndex) {
if (myTestConfigurator.shouldTakeScreenshotOnFailure()) {
Method method = myTestMethod.getMethod();
String fileNamePrefix = method.getDeclaringClass().getSimpleName() + "." + (executionIndex + 1) + "." + method.getName();
String extension = ".png";
try {
File rootDir = getFailedTestScreenshotDirPath();
File screenshotFilePath = new File(rootDir, fileNamePrefix + extension);
if (screenshotFilePath.isFile()) {
SimpleDateFormat format = new SimpleDateFormat("MM-dd-yyyy.HH:mm:ss");
String now = format.format(new GregorianCalendar().getTime());
screenshotFilePath = new File(rootDir, fileNamePrefix + "." + now + extension);
}
myScreenshotTaker.saveDesktopAsPng(screenshotFilePath.getPath());
System.out.println("Screenshot of failed test taken and stored at " + screenshotFilePath.getPath());
}
catch (Throwable ignored) {
System.out.println("Failed to take screenshot. Cause: " + ignored.getMessage());
}
}
}
}
@@ -0,0 +1,52 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* 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.matcher;
import org.fest.swing.core.GenericTypeMatcher;
import org.jetbrains.annotations.NotNull;
import java.awt.*;
/**
* Matcher that checks the name of the actual implementation. This is useful when matching
* against non-public IJ classes.
*/
public class ClassNameMatcher<T extends Component> extends GenericTypeMatcher<T> {
private final String myClassName;
private ClassNameMatcher(String className, Class<T> supportedType) {
super(supportedType);
myClassName = className;
}
private ClassNameMatcher(String className, Class<T> supportedType, boolean requireShowing) {
super(supportedType, requireShowing);
myClassName = className;
}
@Override
protected boolean isMatching(@NotNull T component) {
return myClassName.equals(component.getClass().getName());
}
public static <T extends Component> ClassNameMatcher<T> forClass(String className, Class<T> supportedType) {
return new ClassNameMatcher<T>(className, supportedType);
}
public static <T extends Component> ClassNameMatcher<T> forClass(String className, Class<T> supportedType, boolean requireShowing) {
return new ClassNameMatcher<T>(className, supportedType, requireShowing);
}
}